Skip to content

Commit

Permalink
Merge pull request #94 from AspenWeb/clean-up
Browse files Browse the repository at this point in the history
  • Loading branch information
Changaco authored Jun 1, 2021
2 parents 41aa6e0 + f1ddc66 commit 9d24755
Show file tree
Hide file tree
Showing 50 changed files with 442 additions and 1,035 deletions.
19 changes: 12 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
dist: xenial
dist: focal
sudo: false
branches:
only:
- master
language: python
python:
- 3.5
- 3.6
- 3.7
- 3.8
install: travis_retry pip install tox-travis
jobs:
include:
- python: 3.6
env: TOXENV=py36
- python: 3.7
env: TOXENV=py37
- python: 3.8
env: TOXENV=py38
- python: 3.9
env: TOXENV=py39
install: travis_retry pip install tox
script: tox
notifications:
email: false
Expand Down
1 change: 0 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
include COPYRIGHT
include ez_setup.py
include version.txt
include aspen/request_processor/mime.types
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ Installation, Testing, and License

$ pip install aspen

We test against 64-bit Python 2.7 and Python 3.6 on Linux: |travis|
We test against 64-bit Python 3.6, 3.7, 3.8 and 3.9 on Linux: |travis|

And we test against 32- and 64-bit Python 2.7 on Windows: |appveyor|
And we test against 32- and 64-bit Python 3.6, 3.7, 3.8 and 3.9 on Windows: |appveyor|

``aspen`` is MIT-licensed.

Expand Down
19 changes: 4 additions & 15 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
environment:
matrix:
- PYTHON: "C:\\Python37"
PYTHON_VERSION: "3.7.x"
PYTHON_ARCH: "32"

- PYTHON: "C:\\Python37-x64"
PYTHON_VERSION: "3.7.x"
PYTHON_ARCH: "64"

- PYTHON: "C:\\Python38"
PYTHON_VERSION: "3.8.x"
PYTHON_ARCH: "32"

- PYTHON: "C:\\Python38-x64"
PYTHON_VERSION: "3.8.x"
PYTHON_ARCH: "64"
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
PYTHON: "C:\\Python39"
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
PYTHON: "C:\\Python39-x64"

install:
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
Expand Down
7 changes: 0 additions & 7 deletions aspen/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +0,0 @@
"""Importing this package will upgrade the `backslashreplace` error handling
scheme in the global `codecs` registry to support handling decoding errors as
well as encoding errors.
"""
from . import backcompat

backcompat.upgrade_backslashreplace()
21 changes: 0 additions & 21 deletions aspen/backcompat.py

This file was deleted.

1 change: 1 addition & 0 deletions aspen/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def __str__(self):
self.node2.__class__.__name__, self.node2.fspath, self.node2.type,
)


class WildcardCollision(Exception):
"""Raised if a filesystem path contains multiple wildcards with the same name.
Expand Down
24 changes: 7 additions & 17 deletions aspen/http/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@
from .mapping import Mapping


def _decode(o, errors='strict'):
return o.decode('utf8', errors=errors) if isinstance(o, bytes) else o


def path_decode(bs):
return _decode(unquote(bs))


class PathPart(str):
"""Represents a segment of a URL path.
Expand Down Expand Up @@ -52,52 +44,50 @@ def extract_rfc2396_params(path):
for component in pathsegs:
parts = component.split(';')
params = Mapping()
segment = path_decode(parts[0])
segment = unquote(parts[0])
for p in parts[1:]:
if '=' in p:
k, v = p.split('=', 1)
else:
k, v = p, ''
params.add(path_decode(k), path_decode(v))
params.add(unquote(k), unquote(v))
segments_with_params.append(PathPart(segment, params))
return segments_with_params


def split_path_no_params(path):
"""This splits a path into parts on "/" only (no split on ";" or ",").
"""
return [PathPart(path_decode(s)) for s in path.lstrip('/').split('/')]
return [PathPart(unquote(s)) for s in path.lstrip('/').split('/')]


class Path(Mapping):
"""Represent the path of a resource.
Attributes:
raw: the unparsed form of the path - :class:`bytes`
raw: the unparsed form of the path - :class:`str`
decoded: the decoded form of the path - :class:`str`
parts: the segments of the path - :class:`list` of :class:`PathPart` objects
"""

def __init__(self, raw, split_path=extract_rfc2396_params):
self.raw = raw
self.decoded = path_decode(raw)
self.decoded = unquote(raw)
self.parts = split_path(raw)


class Querystring(Mapping):
"""Represent an HTTP querystring.
Attributes:
raw: the unparsed form of the querystring - :class:`bytes`
raw: the unparsed form of the querystring - :class:`str`
decoded: the decoded form of the querystring - :class:`str`
"""

def __init__(self, raw, errors='replace'):
"""Takes a string of type application/x-www-form-urlencoded.
"""
# urllib needs bytestrings in py2 and unicode strings in py3

self.decoded = _decode(unquote_plus(raw), errors=errors)
self.decoded = unquote_plus(raw)
self.raw = raw
Mapping.__init__(self, parse_qs(
raw, errors=errors, keep_blank_values=True, strict_parsing=False
Expand Down
17 changes: 14 additions & 3 deletions aspen/http/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,28 @@ def open_resource(request_processor, resource_path):
to create and delete symlinks inside the resource directories whenever they
want, but it makes the attack more difficult and detectable.
"""
return open(check_resource_path(request_processor, resource_path), 'rb')


def check_resource_path(request_processor, resource_path):
"""Return the “real” path of a file (i.e. a path without symlinks).
:raises AttemptedBreakout:
if the :obj:`resource_path` points to a file that isn't inside any of
the known
:attr:`~aspen.request_processor.DefaultConfiguration.resource_directories`
"""
real_path = os.path.realpath(resource_path)
is_outside = all(
not _is_subpath(real_path, resource_dir)
for resource_dir in request_processor.resource_directories
)
if is_outside:
raise AttemptedBreakout(resource_path, real_path)
return open(real_path, 'rb')
return real_path


class Static(object):
class Static:
"""Model a static HTTP resource.
"""

Expand Down Expand Up @@ -76,7 +87,7 @@ def render(self, *ignored):
return output


class Dynamic(object):
class Dynamic:
"""Model a dynamic HTTP resource.
"""

Expand Down
2 changes: 1 addition & 1 deletion aspen/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


@auto_repr
class Output(object):
class Output:
"""The result of rendering a resource.
"""

Expand Down
6 changes: 1 addition & 5 deletions aspen/renderers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
# for backwards compatibility with aspen-renderer modules
from .simplates.renderers import Factory, Renderer

Factory, Renderer # make pyflakes happy

import warnings
warnings.warn('aspen.renderers is deprecated and will be removed in a future version. '
'Please use aspen.simplates.renderers instead.', FutureWarning)
Factory, Renderer # make flake8 happy
13 changes: 3 additions & 10 deletions aspen/request_processor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
]


class RequestProcessor(object):
class RequestProcessor:
"""A core request processor designed for integration into a host framework.
The ``kwargs`` are for configuration, see :class:`DefaultConfiguration`
Expand All @@ -44,7 +44,6 @@ def __init__(self, **kwargs):

# XXX register codecs here


# Configure from defaults and kwargs.
# ===================================

Expand All @@ -55,7 +54,6 @@ def __init__(self, **kwargs):
else:
self.__dict__[name] = copy(default)


# Set some attributes.
# ====================

Expand Down Expand Up @@ -111,7 +109,7 @@ def safe_getcwd(errorstr):
Simplate.default_renderers_by_media_type = defaultdict(lambda: self.renderer_default)
Simplate.default_renderers_by_media_type[self.media_type_json] = 'json_dump'

initial_context = { 'request_processor': self }
initial_context = {'request_processor': self}
Simplate.defaults = SimplateDefaults(
Simplate.default_renderers_by_media_type,
Simplate.renderer_factories,
Expand Down Expand Up @@ -145,7 +143,6 @@ def safe_getcwd(errorstr):
# create the resources cache
self.resources = Resources(self)


def dispatch(self, path):
"""Call the dispatcher and inject the path variables into the given path object.
Expand All @@ -161,7 +158,6 @@ def dispatch(self, path):
path[k] = v
return dispatch_result


def process(self, path, querystring, accept_header, context):
"""Process a request.
Expand Down Expand Up @@ -192,21 +188,18 @@ def process(self, path, querystring, accept_header, context):

return dispatch_result, None, None


def is_dynamic(self, fspath):
"""Given a filesystem path, return a boolean.
"""
return self.get_resource_class(fspath) is not Static


def get_resource_class(self, fspath):
"""Given a filesystem path, return a resource class.
"""
parts = fspath.split('.')
extension = parts[-1] if len(parts) > 1 else None
return self.dynamic_classes_by_file_extension.get(extension, Static)


def guess_media_type(self, filename):
"""Guess the media type of a file by looking at its extension.
Expand All @@ -222,7 +215,7 @@ def guess_media_type(self, filename):
return media_type


class DefaultConfiguration(object):
class DefaultConfiguration:
"""Default configuration values.
"""

Expand Down
Loading

0 comments on commit 9d24755

Please sign in to comment.