Skip to content

Commit

Permalink
Add path_pattern property to routes and resources
Browse files Browse the repository at this point in the history
The path_pattern is the raw path used to add a new route. For example,
/foo/bar/{name:\d+}.

 - Add path_pattern property to AbstractRoute and AbstractResource
 - Add path pattern implementation to PlainResource, DynamicResource,
 PrefixResource, StaticResource, ResourceRoute and SystemRoute.
 - Add tests

Closes aio-libs#2968
  • Loading branch information
pposca committed May 11, 2018
1 parent 9541960 commit 1f449e1
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGES/2968.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add path_pattern property to routes and resources
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ Paul Colomiets
Paulus Schoutsen
Pavel Kamaev
Pawel Miech
Pepe Osca
Philipp A.
Pieter van Beek
Rafael Viotti
Expand Down
43 changes: 43 additions & 0 deletions aiohttp/web_urldispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ def __init__(self, *, name=None):
def name(self):
return self._name

@property
@abc.abstractmethod
def path_pattern(self):
"""Exposes the route's path_pattern.
For example '/foo/bar/{name:\d+}'
"""

@abc.abstractmethod # pragma: no branch
def url_for(self, **kwargs):
"""Construct url for resource with additional params."""
Expand Down Expand Up @@ -131,6 +140,15 @@ def handler(self):
def name(self):
"""Optional route's name, always equals to resource's name."""

@property
@abc.abstractmethod
def path_pattern(self):
"""Exposes the route's path_patter.
For example '/foo/bar/{name:\d+}'
"""

@property
def resource(self):
return self._resource
Expand Down Expand Up @@ -300,6 +318,10 @@ def __init__(self, path, *, name=None):
assert not path or path.startswith('/')
self._path = path

@property
def path_pattern(self):
return self._path

def freeze(self):
if not self._path:
self._path = '/'
Expand Down Expand Up @@ -343,6 +365,7 @@ def __init__(self, path, *, name=None):
super().__init__(name=name)
pattern = ''
formatter = ''
self._path_pattern = path
for part in ROUTE_RE.split(path):
match = self.DYN.fullmatch(part)
if match:
Expand Down Expand Up @@ -373,6 +396,10 @@ def __init__(self, path, *, name=None):
self._pattern = compiled
self._formatter = formatter

@property
def path_pattern(self):
return self._path_pattern

def add_prefix(self, prefix):
assert prefix.startswith('/')
assert not prefix.endswith('/')
Expand Down Expand Up @@ -414,6 +441,10 @@ def __init__(self, prefix, *, name=None):
super().__init__(name=name)
self._prefix = URL.build(path=prefix).raw_path

@property
def path_pattern(self):
return self._prefix

def add_prefix(self, prefix):
assert prefix.startswith('/')
assert not prefix.endswith('/')
Expand Down Expand Up @@ -457,6 +488,10 @@ def __init__(self, prefix, directory, *, name=None,
'HEAD': ResourceRoute('HEAD', self._handle, self,
expect_handler=expect_handler)}

@property
def path_pattern(self):
return self._prefix + str(self._directory)

def url_for(self, *, filename, append_version=None):
if append_version is None:
append_version = self._append_version
Expand Down Expand Up @@ -669,6 +704,10 @@ def __repr__(self):
def name(self):
return self._resource.name

@property
def path_pattern(self):
return self._resource.path_pattern

def url_for(self, *args, **kwargs):
"""Construct url for route with additional params."""
return self._resource.url_for(*args, **kwargs)
Expand All @@ -690,6 +729,10 @@ def url_for(self, *args, **kwargs):
def name(self):
return None

@property
def path_pattern(self):
return None

def get_info(self):
return {'http_exception': self._http_exception}

Expand Down
58 changes: 57 additions & 1 deletion tests/test_urldispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
from aiohttp.test_utils import make_mocked_request
from aiohttp.web import HTTPMethodNotAllowed, HTTPNotFound, Response
from aiohttp.web_urldispatcher import (PATH_SEP, AbstractResource,
ResourceRoute, SystemRoute, View,
DynamicResource, PlainResource,
ResourceRoute, StaticResource,
SystemRoute, View,
_default_expect_handler)


Expand Down Expand Up @@ -1112,3 +1114,57 @@ def handler(request):

with pytest.warns(DeprecationWarning):
router.add_route('GET', '/handler', handler)


def test_plain_resource_path_pattern():
path_pattern = '/plain/path'
res = PlainResource(path=path_pattern)
assert res.path_pattern == path_pattern


def test_dynamic_resource_path_pattern():
path_pattern = [
'/get/{name}',
'/get/{num:^\d+}',
r'/handler/{to:\d+}',
r'/{one}/{two:.+}',
]
for path in path_pattern:
res = DynamicResource(path=path)
assert res.path_pattern == path


def test_static_resource_path_pattern():
prefix = '/prefix'
directory = str(os.path.dirname(aiohttp.__file__))
path_pattern = prefix + directory
res = StaticResource(prefix=prefix, directory=directory)
assert res.path_pattern == path_pattern


def test_prefixed_subapp_resource_path_pattern(app, loop):
path_pattern = '/prefix'
subapp = web.Application()
res = subapp.add_subapp(path_pattern, subapp)
assert res.path_pattern == path_pattern


def test_resource_route_path_pattern(router):
path_pattern = '/plain'
route = router.add_route('GET', path_pattern, make_handler())
assert route.path_pattern == path_pattern

path_pattern = '/variable/{name}'
route = router.add_route('GET', path_pattern, make_handler())
assert route.path_pattern == path_pattern

prefix = '/prefix'
directory = str(os.path.dirname(aiohttp.__file__))
path_pattern = prefix + directory
route = router.add_static(prefix, directory)
assert route.path_pattern == path_pattern


def test_system_route_path_pattern():
route = SystemRoute(BaseException())
assert route.path_pattern is None

0 comments on commit 1f449e1

Please sign in to comment.