-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Route definitions #2004
Route definitions #2004
Changes from 21 commits
a2fbd23
62f653a
2ce063f
727ea50
daa31ec
0c23ebf
6cab8b8
8b44cde
1d5492c
3eb5137
a5c24a0
910cf1c
36d4fc0
de48fda
234ed0e
2f5bb08
d158513
b8ad2f4
7391a69
341da53
ec0630a
9c1845c
a9cbbb4
e2bc869
58686cb
875ae8e
7c54e36
48203d3
68f7225
aaab3ed
c10f4e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,8 @@ | |
import os | ||
import re | ||
import warnings | ||
from collections.abc import Container, Iterable, Sized | ||
from collections import namedtuple | ||
from collections.abc import Container, Iterable, Sequence, Sized | ||
from functools import wraps | ||
from pathlib import Path | ||
from types import MappingProxyType | ||
|
@@ -28,12 +29,25 @@ | |
__all__ = ('UrlDispatcher', 'UrlMappingMatchInfo', | ||
'AbstractResource', 'Resource', 'PlainResource', 'DynamicResource', | ||
'AbstractRoute', 'ResourceRoute', | ||
'StaticResource', 'View') | ||
'StaticResource', 'View', 'RoutesDef', | ||
'head', 'get', 'post', 'patch', 'put', 'delete', 'route') | ||
|
||
HTTP_METHOD_RE = re.compile(r"^[0-9A-Za-z!#\$%&'\*\+\-\.\^_`\|~]+$") | ||
PATH_SEP = re.escape('/') | ||
|
||
|
||
class RouteDef(namedtuple('_RouteDef', 'method, path, handler, kwargs')): | ||
# TODO: add __repr__ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I said I still think the distinction is matter. |
||
|
||
def register(self, router): | ||
if self.method in hdrs.METH_ALL: | ||
reg = getattr(router, 'add_'+self.method.lower()) | ||
reg(self.path, self.handler, **self.kwargs) | ||
else: | ||
router.add_route(self.method, self.path, self.handler, | ||
**self.kwargs) | ||
|
||
|
||
class AbstractResource(Sized, Iterable): | ||
|
||
def __init__(self, *, name=None): | ||
|
@@ -894,3 +908,85 @@ def freeze(self): | |
super().freeze() | ||
for resource in self._resources: | ||
resource.freeze() | ||
|
||
def add_routes(self, routes): | ||
"""Append routes to route table. | ||
|
||
Parameter should be a sequence of RouteDef objects. | ||
""" | ||
# TODO: add_table maybe? | ||
for route in routes: | ||
route.register(self) | ||
|
||
|
||
def route(method, path, handler, **kwargs): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think after refactoring this function is not required There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is: user might want to provide custom HTTP method in very rare cases |
||
return RouteDef(method, path, handler, kwargs) | ||
|
||
|
||
def head(path, handler, **kwargs): | ||
return route(hdrs.METH_HEAD, path, handler, **kwargs) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the code gets more readable when we do not use abbreviations. Can I open a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be good in general but for this particular case it doesn't make sense I think. |
||
|
||
|
||
def get(path, handler, *, name=None, allow_head=True, **kwargs): | ||
return route(hdrs.METH_GET, path, handler, | ||
allow_head=allow_head, **kwargs) | ||
|
||
|
||
def post(path, handler, **kwargs): | ||
return route(hdrs.METH_POST, path, handler, **kwargs) | ||
|
||
|
||
def put(path, handler, **kwargs): | ||
return route(hdrs.METH_PUT, path, handler, **kwargs) | ||
|
||
|
||
def patch(path, handler, **kwargs): | ||
return route(hdrs.METH_PATCH, path, handler, **kwargs) | ||
|
||
|
||
def delete(path, handler, **kwargs): | ||
return route(hdrs.METH_DELETE, path, handler, **kwargs) | ||
|
||
|
||
class RoutesDef(Sequence): | ||
"""Route definition table""" | ||
def __init__(self): | ||
self._items = [] | ||
|
||
# TODO: add __repr__ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for suggestion for route definitions should be sorted by insertion order. |
||
|
||
def __getitem__(self, index): | ||
return self._items[index] | ||
|
||
def __iter__(self): | ||
return iter(self._items) | ||
|
||
def __len__(self): | ||
return len(self._items) | ||
|
||
def __contains__(self, item): | ||
return item in self._items | ||
|
||
def route(self, method, path, **kwargs): | ||
def inner(handler): | ||
self._items.append(RouteDef(method, path, handler, kwargs)) | ||
return handler | ||
return inner | ||
|
||
def head(self, path, **kwargs): | ||
return self.route(hdrs.METH_HEAD, path, **kwargs) | ||
|
||
def get(self, path, **kwargs): | ||
return self.route(hdrs.METH_GET, path, **kwargs) | ||
|
||
def post(self, path, **kwargs): | ||
return self.route(hdrs.METH_POST, path, **kwargs) | ||
|
||
def put(self, path, **kwargs): | ||
return self.route(hdrs.METH_PUT, path, **kwargs) | ||
|
||
def patch(self, path, **kwargs): | ||
return self.route(hdrs.METH_PATCH, path, **kwargs) | ||
|
||
def delete(self, path, **kwargs): | ||
return self.route(hdrs.METH_DELETE, path, **kwargs) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
#!/usr/bin/env python3 | ||
"""Example for aiohttp.web basic server | ||
with decorator definition for routes | ||
""" | ||
|
||
import asyncio | ||
import textwrap | ||
|
||
from aiohttp import web | ||
|
||
|
||
routes = web.RoutesDef() | ||
|
||
|
||
@routes.get('/') | ||
async def intro(request): | ||
txt = textwrap.dedent("""\ | ||
Type {url}/hello/John {url}/simple or {url}/change_body | ||
in browser url bar | ||
""").format(url='127.0.0.1:8080') | ||
binary = txt.encode('utf8') | ||
resp = web.StreamResponse() | ||
resp.content_length = len(binary) | ||
resp.content_type = 'text/plain' | ||
await resp.prepare(request) | ||
resp.write(binary) | ||
return resp | ||
|
||
|
||
@routes.get('/simple') | ||
async def simple(request): | ||
return web.Response(text="Simple answer") | ||
|
||
|
||
@routes.get('/change_body') | ||
async def change_body(request): | ||
resp = web.Response() | ||
resp.body = b"Body changed" | ||
resp.content_type = 'text/plain' | ||
return resp | ||
|
||
|
||
@routes.get('/hello') | ||
async def hello(request): | ||
resp = web.StreamResponse() | ||
name = request.match_info.get('name', 'Anonymous') | ||
answer = ('Hello, ' + name).encode('utf8') | ||
resp.content_length = len(answer) | ||
resp.content_type = 'text/plain' | ||
await resp.prepare(request) | ||
resp.write(answer) | ||
await resp.write_eof() | ||
return resp | ||
|
||
|
||
async def init(): | ||
app = web.Application() | ||
app.router.add_routes(routes) | ||
return app | ||
|
||
loop = asyncio.get_event_loop() | ||
app = loop.run_until_complete(init()) | ||
web.run_app(app) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#!/usr/bin/env python3 | ||
"""Example for aiohttp.web basic server | ||
with table definition for routes | ||
""" | ||
|
||
import asyncio | ||
import textwrap | ||
|
||
from aiohttp import web | ||
|
||
|
||
async def intro(request): | ||
txt = textwrap.dedent("""\ | ||
Type {url}/hello/John {url}/simple or {url}/change_body | ||
in browser url bar | ||
""").format(url='127.0.0.1:8080') | ||
binary = txt.encode('utf8') | ||
resp = web.StreamResponse() | ||
resp.content_length = len(binary) | ||
resp.content_type = 'text/plain' | ||
await resp.prepare(request) | ||
resp.write(binary) | ||
return resp | ||
|
||
|
||
async def simple(request): | ||
return web.Response(text="Simple answer") | ||
|
||
|
||
async def change_body(request): | ||
resp = web.Response() | ||
resp.body = b"Body changed" | ||
resp.content_type = 'text/plain' | ||
return resp | ||
|
||
|
||
async def hello(request): | ||
resp = web.StreamResponse() | ||
name = request.match_info.get('name', 'Anonymous') | ||
answer = ('Hello, ' + name).encode('utf8') | ||
resp.content_length = len(answer) | ||
resp.content_type = 'text/plain' | ||
await resp.prepare(request) | ||
resp.write(answer) | ||
await resp.write_eof() | ||
return resp | ||
|
||
|
||
async def init(): | ||
app = web.Application() | ||
app.router.add_routes([ | ||
web.get('/', intro), | ||
web.get('/simple', simple), | ||
web.get('/change_body', change_body), | ||
web.get('/hello/{name}', hello), | ||
web.get('/hello', hello), | ||
]) | ||
return app | ||
|
||
loop = asyncio.get_event_loop() | ||
app = loop.run_until_complete(init()) | ||
web.run_app(app) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not only
Route
?What's the meaning of
Def
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we already use
Route
name for routes added byapp.router.add_route()
call.RouteDef
is a scratch for non-added-yet route definition