Merge pull request #901 from lixxu/master
add name option for route building
This commit is contained in:
commit
158da0927a
|
@ -239,3 +239,65 @@ def handler(request):
|
|||
|
||||
app.blueprint(bp)
|
||||
```
|
||||
|
||||
## User defined route name
|
||||
|
||||
You can pass `name` to change the route name to avoid using the default name (`handler.__name__`).
|
||||
|
||||
```python
|
||||
|
||||
app = Sanic('test_named_route')
|
||||
|
||||
@app.get('/get', name='get_handler')
|
||||
def handler(request):
|
||||
return text('OK')
|
||||
|
||||
# then you need use `app.url_for('get_handler')`
|
||||
# instead of # `app.url_for('handler')`
|
||||
|
||||
# It also works for blueprints
|
||||
bp = Blueprint('test_named_bp')
|
||||
|
||||
@bp.get('/bp/get', name='get_handler')
|
||||
def handler(request):
|
||||
return text('OK')
|
||||
|
||||
app.blueprint(bp)
|
||||
|
||||
# then you need use `app.url_for('test_named_bp.get_handler')`
|
||||
# instead of `app.url_for('test_named_bp.handler')`
|
||||
|
||||
# different names can be used for same url with different methods
|
||||
|
||||
@app.get('/test', name='route_test')
|
||||
def handler(request):
|
||||
return text('OK')
|
||||
|
||||
@app.post('/test', name='route_post')
|
||||
def handler2(request):
|
||||
return text('OK POST')
|
||||
|
||||
@app.put('/test', name='route_put')
|
||||
def handler3(request):
|
||||
return text('OK PUT')
|
||||
|
||||
# below url are the same, you can use any of them
|
||||
# '/test'
|
||||
app.url_for('route_test')
|
||||
# app.url_for('route_post')
|
||||
# app.url_for('route_put')
|
||||
|
||||
# for same handler name with different methods
|
||||
# you need specify the name (it's url_for issue)
|
||||
@app.get('/get')
|
||||
def handler(request):
|
||||
return text('OK')
|
||||
|
||||
@app.post('/post', name='post_handler')
|
||||
def handler(request):
|
||||
return text('OK')
|
||||
|
||||
# then
|
||||
# app.url_for('handler') == '/get'
|
||||
# app.url_for('post_handler') == '/post'
|
||||
```
|
||||
|
|
62
sanic/app.py
62
sanic/app.py
|
@ -112,7 +112,7 @@ class Sanic:
|
|||
|
||||
# Decorator
|
||||
def route(self, uri, methods=frozenset({'GET'}), host=None,
|
||||
strict_slashes=None, stream=False, version=None):
|
||||
strict_slashes=None, stream=False, version=None, name=None):
|
||||
"""Decorate a function to be registered as a route
|
||||
|
||||
:param uri: path of the URL
|
||||
|
@ -120,6 +120,8 @@ class Sanic:
|
|||
:param host:
|
||||
:param strict_slashes:
|
||||
:param stream:
|
||||
:param version:
|
||||
:param name: user defined route name for url_for
|
||||
:return: decorated function
|
||||
"""
|
||||
|
||||
|
@ -139,48 +141,56 @@ class Sanic:
|
|||
handler.is_stream = stream
|
||||
self.router.add(uri=uri, methods=methods, handler=handler,
|
||||
host=host, strict_slashes=strict_slashes,
|
||||
version=version)
|
||||
version=version, name=name)
|
||||
return handler
|
||||
|
||||
return response
|
||||
|
||||
# Shorthand method decorators
|
||||
def get(self, uri, host=None, strict_slashes=None, version=None):
|
||||
def get(self, uri, host=None, strict_slashes=None, version=None,
|
||||
name=None):
|
||||
return self.route(uri, methods=frozenset({"GET"}), host=host,
|
||||
strict_slashes=strict_slashes, version=version)
|
||||
strict_slashes=strict_slashes, version=version,
|
||||
name=name)
|
||||
|
||||
def post(self, uri, host=None, strict_slashes=None, stream=False,
|
||||
version=None):
|
||||
version=None, name=None):
|
||||
return self.route(uri, methods=frozenset({"POST"}), host=host,
|
||||
strict_slashes=strict_slashes, stream=stream,
|
||||
version=version)
|
||||
version=version, name=name)
|
||||
|
||||
def put(self, uri, host=None, strict_slashes=None, stream=False,
|
||||
version=None):
|
||||
version=None, name=None):
|
||||
return self.route(uri, methods=frozenset({"PUT"}), host=host,
|
||||
strict_slashes=strict_slashes, stream=stream,
|
||||
version=version)
|
||||
version=version, name=name)
|
||||
|
||||
def head(self, uri, host=None, strict_slashes=None, version=None):
|
||||
def head(self, uri, host=None, strict_slashes=None, version=None,
|
||||
name=None):
|
||||
return self.route(uri, methods=frozenset({"HEAD"}), host=host,
|
||||
strict_slashes=strict_slashes, version=version)
|
||||
strict_slashes=strict_slashes, version=version,
|
||||
name=name)
|
||||
|
||||
def options(self, uri, host=None, strict_slashes=None, version=None):
|
||||
def options(self, uri, host=None, strict_slashes=None, version=None,
|
||||
name=None):
|
||||
return self.route(uri, methods=frozenset({"OPTIONS"}), host=host,
|
||||
strict_slashes=strict_slashes, version=version)
|
||||
strict_slashes=strict_slashes, version=version,
|
||||
name=name)
|
||||
|
||||
def patch(self, uri, host=None, strict_slashes=None, stream=False,
|
||||
version=None):
|
||||
version=None, name=None):
|
||||
return self.route(uri, methods=frozenset({"PATCH"}), host=host,
|
||||
strict_slashes=strict_slashes, stream=stream,
|
||||
version=version)
|
||||
version=version, name=name)
|
||||
|
||||
def delete(self, uri, host=None, strict_slashes=None, version=None):
|
||||
def delete(self, uri, host=None, strict_slashes=None, version=None,
|
||||
name=None):
|
||||
return self.route(uri, methods=frozenset({"DELETE"}), host=host,
|
||||
strict_slashes=strict_slashes, version=version)
|
||||
strict_slashes=strict_slashes, version=version,
|
||||
name=name)
|
||||
|
||||
def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None,
|
||||
strict_slashes=None, version=None):
|
||||
strict_slashes=None, version=None, name=None):
|
||||
"""A helper method to register class instance or
|
||||
functions as a handler to the application url
|
||||
routes.
|
||||
|
@ -190,6 +200,9 @@ class Sanic:
|
|||
:param methods: list or tuple of methods allowed, these are overridden
|
||||
if using a HTTPMethodView
|
||||
:param host:
|
||||
:param strict_slashes:
|
||||
:param version:
|
||||
:param name: user defined route name for url_for
|
||||
:return: function or class instance
|
||||
"""
|
||||
stream = False
|
||||
|
@ -217,12 +230,12 @@ class Sanic:
|
|||
|
||||
self.route(uri=uri, methods=methods, host=host,
|
||||
strict_slashes=strict_slashes, stream=stream,
|
||||
version=version)(handler)
|
||||
version=version, name=name)(handler)
|
||||
return handler
|
||||
|
||||
# Decorator
|
||||
def websocket(self, uri, host=None, strict_slashes=None,
|
||||
subprotocols=None):
|
||||
subprotocols=None, name=None):
|
||||
"""Decorate a function to be registered as a websocket route
|
||||
:param uri: path of the URL
|
||||
:param subprotocols: optional list of strings with the supported
|
||||
|
@ -265,19 +278,19 @@ class Sanic:
|
|||
|
||||
self.router.add(uri=uri, handler=websocket_handler,
|
||||
methods=frozenset({'GET'}), host=host,
|
||||
strict_slashes=strict_slashes)
|
||||
strict_slashes=strict_slashes, name=name)
|
||||
return handler
|
||||
|
||||
return response
|
||||
|
||||
def add_websocket_route(self, handler, uri, host=None,
|
||||
strict_slashes=None):
|
||||
strict_slashes=None, name=None):
|
||||
"""A helper method to register a function as a websocket route."""
|
||||
if strict_slashes is None:
|
||||
strict_slashes = self.strict_slashes
|
||||
|
||||
return self.websocket(uri, host=host,
|
||||
strict_slashes=strict_slashes)(handler)
|
||||
return self.websocket(uri, host=host, strict_slashes=strict_slashes,
|
||||
name=name)(handler)
|
||||
|
||||
def enable_websocket(self, enable=True):
|
||||
"""Enable or disable the support for websocket.
|
||||
|
@ -400,8 +413,7 @@ class Sanic:
|
|||
uri, route = self.router.find_route_by_view_name(view_name)
|
||||
|
||||
if not uri or not route:
|
||||
raise URLBuildError(
|
||||
'Endpoint with name `{}` was not found'.format(
|
||||
raise URLBuildError('Endpoint with name `{}` was not found'.format(
|
||||
view_name))
|
||||
|
||||
if uri != '/' and uri.endswith('/'):
|
||||
|
|
|
@ -5,7 +5,7 @@ from sanic.views import CompositionView
|
|||
|
||||
FutureRoute = namedtuple('Route',
|
||||
['handler', 'uri', 'methods', 'host',
|
||||
'strict_slashes', 'stream', 'version'])
|
||||
'strict_slashes', 'stream', 'version', 'name'])
|
||||
FutureListener = namedtuple('Listener', ['handler', 'uri', 'methods', 'host'])
|
||||
FutureMiddleware = namedtuple('Route', ['middleware', 'args', 'kwargs'])
|
||||
FutureException = namedtuple('Route', ['handler', 'args', 'kwargs'])
|
||||
|
@ -53,13 +53,13 @@ class Blueprint:
|
|||
|
||||
version = future.version or self.version
|
||||
|
||||
app.route(
|
||||
uri=uri[1:] if uri.startswith('//') else uri,
|
||||
app.route(uri=uri[1:] if uri.startswith('//') else uri,
|
||||
methods=future.methods,
|
||||
host=future.host or self.host,
|
||||
strict_slashes=future.strict_slashes,
|
||||
stream=future.stream,
|
||||
version=version
|
||||
version=version,
|
||||
name=future.name,
|
||||
)(future.handler)
|
||||
|
||||
for future in self.websocket_routes:
|
||||
|
@ -68,10 +68,10 @@ class Blueprint:
|
|||
future.handler.__blueprintname__ = self.name
|
||||
# Prepend the blueprint URI prefix if available
|
||||
uri = url_prefix + future.uri if url_prefix else future.uri
|
||||
app.websocket(
|
||||
uri=uri,
|
||||
app.websocket(uri=uri,
|
||||
host=future.host or self.host,
|
||||
strict_slashes=future.strict_slashes
|
||||
strict_slashes=future.strict_slashes,
|
||||
name=future.name,
|
||||
)(future.handler)
|
||||
|
||||
# Middleware
|
||||
|
@ -100,7 +100,7 @@ class Blueprint:
|
|||
app.listener(event)(listener)
|
||||
|
||||
def route(self, uri, methods=frozenset({'GET'}), host=None,
|
||||
strict_slashes=None, stream=False, version=None):
|
||||
strict_slashes=None, stream=False, version=None, name=None):
|
||||
"""Create a blueprint route from a decorated function.
|
||||
|
||||
:param uri: endpoint at which the route will be accessible.
|
||||
|
@ -111,19 +111,24 @@ class Blueprint:
|
|||
|
||||
def decorator(handler):
|
||||
route = FutureRoute(
|
||||
handler, uri, methods, host, strict_slashes, stream, version)
|
||||
handler, uri, methods, host, strict_slashes, stream, version,
|
||||
name)
|
||||
self.routes.append(route)
|
||||
return handler
|
||||
return decorator
|
||||
|
||||
def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None,
|
||||
strict_slashes=None, version=None):
|
||||
strict_slashes=None, version=None, name=None):
|
||||
"""Create a blueprint route from a function.
|
||||
|
||||
:param handler: function for handling uri requests. Accepts function,
|
||||
or class instance with a view_class method.
|
||||
:param uri: endpoint at which the route will be accessible.
|
||||
:param methods: list of acceptable HTTP methods.
|
||||
:param host:
|
||||
:param strict_slashes:
|
||||
:param version:
|
||||
:param name: user defined route name for url_for
|
||||
:return: function or class instance
|
||||
"""
|
||||
# Handle HTTPMethodView differently
|
||||
|
@ -142,10 +147,12 @@ class Blueprint:
|
|||
methods = handler.handlers.keys()
|
||||
|
||||
self.route(uri=uri, methods=methods, host=host,
|
||||
strict_slashes=strict_slashes, version=version)(handler)
|
||||
strict_slashes=strict_slashes, version=version,
|
||||
name=name)(handler)
|
||||
return handler
|
||||
|
||||
def websocket(self, uri, host=None, strict_slashes=None, version=None):
|
||||
def websocket(self, uri, host=None, strict_slashes=None, version=None,
|
||||
name=None):
|
||||
"""Create a blueprint websocket route from a decorated function.
|
||||
|
||||
:param uri: endpoint at which the route will be accessible.
|
||||
|
@ -155,12 +162,13 @@ class Blueprint:
|
|||
|
||||
def decorator(handler):
|
||||
route = FutureRoute(handler, uri, [], host, strict_slashes,
|
||||
False, version)
|
||||
False, version, name)
|
||||
self.websocket_routes.append(route)
|
||||
return handler
|
||||
return decorator
|
||||
|
||||
def add_websocket_route(self, handler, uri, host=None, version=None):
|
||||
def add_websocket_route(self, handler, uri, host=None, version=None,
|
||||
name=None):
|
||||
"""Create a blueprint websocket route from a function.
|
||||
|
||||
:param handler: function for handling uri requests. Accepts function,
|
||||
|
@ -168,7 +176,7 @@ class Blueprint:
|
|||
:param uri: endpoint at which the route will be accessible.
|
||||
:return: function or class instance
|
||||
"""
|
||||
self.websocket(uri=uri, host=host, version=version)(handler)
|
||||
self.websocket(uri=uri, host=host, version=version, name=name)(handler)
|
||||
return handler
|
||||
|
||||
def listener(self, event):
|
||||
|
@ -214,36 +222,44 @@ class Blueprint:
|
|||
self.statics.append(static)
|
||||
|
||||
# Shorthand method decorators
|
||||
def get(self, uri, host=None, strict_slashes=None, version=None):
|
||||
def get(self, uri, host=None, strict_slashes=None, version=None,
|
||||
name=None):
|
||||
return self.route(uri, methods=["GET"], host=host,
|
||||
strict_slashes=strict_slashes, version=version)
|
||||
strict_slashes=strict_slashes, version=version,
|
||||
name=name)
|
||||
|
||||
def post(self, uri, host=None, strict_slashes=None, stream=False,
|
||||
version=None):
|
||||
version=None, name=None):
|
||||
return self.route(uri, methods=["POST"], host=host,
|
||||
strict_slashes=strict_slashes, stream=stream,
|
||||
version=version)
|
||||
version=version, name=name)
|
||||
|
||||
def put(self, uri, host=None, strict_slashes=None, stream=False,
|
||||
version=None):
|
||||
version=None, name=None):
|
||||
return self.route(uri, methods=["PUT"], host=host,
|
||||
strict_slashes=strict_slashes, stream=stream,
|
||||
version=version)
|
||||
version=version, name=name)
|
||||
|
||||
def head(self, uri, host=None, strict_slashes=None, version=None):
|
||||
def head(self, uri, host=None, strict_slashes=None, version=None,
|
||||
name=None):
|
||||
return self.route(uri, methods=["HEAD"], host=host,
|
||||
strict_slashes=strict_slashes, version=version)
|
||||
strict_slashes=strict_slashes, version=version,
|
||||
name=name)
|
||||
|
||||
def options(self, uri, host=None, strict_slashes=None, version=None):
|
||||
def options(self, uri, host=None, strict_slashes=None, version=None,
|
||||
name=None):
|
||||
return self.route(uri, methods=["OPTIONS"], host=host,
|
||||
strict_slashes=strict_slashes, version=version)
|
||||
strict_slashes=strict_slashes, version=version,
|
||||
name=name)
|
||||
|
||||
def patch(self, uri, host=None, strict_slashes=None, stream=False,
|
||||
version=None):
|
||||
version=None, name=None):
|
||||
return self.route(uri, methods=["PATCH"], host=host,
|
||||
strict_slashes=strict_slashes, stream=stream,
|
||||
version=version)
|
||||
version=version, name=name)
|
||||
|
||||
def delete(self, uri, host=None, strict_slashes=None, version=None):
|
||||
def delete(self, uri, host=None, strict_slashes=None, version=None,
|
||||
name=None):
|
||||
return self.route(uri, methods=["DELETE"], host=host,
|
||||
strict_slashes=strict_slashes, version=version)
|
||||
strict_slashes=strict_slashes, version=version,
|
||||
name=name)
|
||||
|
|
|
@ -67,6 +67,7 @@ class Router:
|
|||
|
||||
def __init__(self):
|
||||
self.routes_all = {}
|
||||
self.routes_names = {}
|
||||
self.routes_static = {}
|
||||
self.routes_dynamic = defaultdict(list)
|
||||
self.routes_always_check = []
|
||||
|
@ -99,7 +100,7 @@ class Router:
|
|||
return name, _type, pattern
|
||||
|
||||
def add(self, uri, methods, handler, host=None, strict_slashes=False,
|
||||
version=None):
|
||||
version=None, name=None):
|
||||
"""Add a handler to the route list
|
||||
|
||||
:param uri: path to match
|
||||
|
@ -118,29 +119,28 @@ class Router:
|
|||
else:
|
||||
uri = "/".join(["/v{}".format(str(version)), uri])
|
||||
# add regular version
|
||||
self._add(uri, methods, handler, host)
|
||||
self._add(uri, methods, handler, host, name)
|
||||
|
||||
if strict_slashes:
|
||||
return
|
||||
|
||||
# Add versions with and without trailing /
|
||||
slash_is_missing = (
|
||||
not uri[-1] == '/'
|
||||
and not self.routes_all.get(uri + '/', False)
|
||||
not uri[-1] == '/' and not self.routes_all.get(uri + '/', False)
|
||||
)
|
||||
without_slash_is_missing = (
|
||||
uri[-1] == '/'
|
||||
and not self.routes_all.get(uri[:-1], False)
|
||||
and not uri == '/'
|
||||
uri[-1] == '/' and not
|
||||
self.routes_all.get(uri[:-1], False) and not
|
||||
uri == '/'
|
||||
)
|
||||
# add version with trailing slash
|
||||
if slash_is_missing:
|
||||
self._add(uri + '/', methods, handler, host)
|
||||
self._add(uri + '/', methods, handler, host, name)
|
||||
# add version without trailing slash
|
||||
elif without_slash_is_missing:
|
||||
self._add(uri[:-1], methods, handler, host)
|
||||
self._add(uri[:-1], methods, handler, host, name)
|
||||
|
||||
def _add(self, uri, methods, handler, host=None):
|
||||
def _add(self, uri, methods, handler, host=None, name=None):
|
||||
"""Add a handler to the route list
|
||||
|
||||
:param uri: path to match
|
||||
|
@ -161,7 +161,7 @@ class Router:
|
|||
"host strings, not {!r}".format(host))
|
||||
|
||||
for host_ in host:
|
||||
self.add(uri, methods, handler, host_)
|
||||
self.add(uri, methods, handler, host_, name)
|
||||
return
|
||||
|
||||
# Dict for faster lookups of if method allowed
|
||||
|
@ -229,22 +229,26 @@ class Router:
|
|||
else:
|
||||
route = self.routes_all.get(uri)
|
||||
|
||||
if route:
|
||||
route = merge_route(route, methods, handler)
|
||||
else:
|
||||
# prefix the handler name with the blueprint name
|
||||
# if available
|
||||
if hasattr(handler, '__blueprintname__'):
|
||||
handler_name = '{}.{}'.format(
|
||||
handler.__blueprintname__, handler.__name__)
|
||||
handler.__blueprintname__, name or handler.__name__)
|
||||
else:
|
||||
handler_name = getattr(handler, '__name__', None)
|
||||
handler_name = name or getattr(handler, '__name__', None)
|
||||
|
||||
if route:
|
||||
route = merge_route(route, methods, handler)
|
||||
else:
|
||||
route = Route(
|
||||
handler=handler, methods=methods, pattern=pattern,
|
||||
parameters=parameters, name=handler_name, uri=uri)
|
||||
|
||||
self.routes_all[uri] = route
|
||||
pairs = self.routes_names.get(handler_name)
|
||||
if not (pairs and (pairs[0] + '/' == uri or uri + '/' == pairs[0])):
|
||||
self.routes_names[handler_name] = (uri, route)
|
||||
|
||||
if properties['unhashable']:
|
||||
self.routes_always_check.append(route)
|
||||
elif parameters:
|
||||
|
@ -265,6 +269,11 @@ class Router:
|
|||
uri = host + uri
|
||||
try:
|
||||
route = self.routes_all.pop(uri)
|
||||
for handler_name, pairs in self.routes_names.items():
|
||||
if pairs[0] == uri:
|
||||
self.routes_names.pop(handler_name)
|
||||
break
|
||||
|
||||
except KeyError:
|
||||
raise RouteDoesNotExist("Route was not registered: {}".format(uri))
|
||||
|
||||
|
@ -289,11 +298,7 @@ class Router:
|
|||
if not view_name:
|
||||
return (None, None)
|
||||
|
||||
for uri, route in self.routes_all.items():
|
||||
if route.name == view_name:
|
||||
return uri, route
|
||||
|
||||
return (None, None)
|
||||
return self.routes_names.get(view_name, (None, None))
|
||||
|
||||
def get(self, request):
|
||||
"""Get a request handler based on the URL of the request, or raises an
|
||||
|
|
388
tests/test_named_routes.py
Normal file
388
tests/test_named_routes.py
Normal file
|
@ -0,0 +1,388 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import asyncio
|
||||
import pytest
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.blueprints import Blueprint
|
||||
from sanic.response import text
|
||||
from sanic.exceptions import URLBuildError
|
||||
from sanic.constants import HTTP_METHODS
|
||||
|
||||
|
||||
# ------------------------------------------------------------ #
|
||||
# UTF-8
|
||||
# ------------------------------------------------------------ #
|
||||
|
||||
@pytest.mark.parametrize('method', HTTP_METHODS)
|
||||
def test_versioned_named_routes_get(method):
|
||||
app = Sanic('test_shorhand_routes_get')
|
||||
bp = Blueprint('test_bp', url_prefix='/bp')
|
||||
|
||||
method = method.lower()
|
||||
route_name = 'route_{}'.format(method)
|
||||
route_name2 = 'route2_{}'.format(method)
|
||||
|
||||
func = getattr(app, method)
|
||||
if callable(func):
|
||||
@func('/{}'.format(method), version=1, name=route_name)
|
||||
def handler(request):
|
||||
return text('OK')
|
||||
else:
|
||||
print(func)
|
||||
raise
|
||||
|
||||
func = getattr(bp, method)
|
||||
if callable(func):
|
||||
@func('/{}'.format(method), version=1, name=route_name2)
|
||||
def handler2(request):
|
||||
return text('OK')
|
||||
|
||||
else:
|
||||
print(func)
|
||||
raise
|
||||
|
||||
app.blueprint(bp)
|
||||
|
||||
assert app.router.routes_all['/v1/{}'.format(method)].name == route_name
|
||||
|
||||
route = app.router.routes_all['/v1/bp/{}'.format(method)]
|
||||
assert route.name == 'test_bp.{}'.format(route_name2)
|
||||
|
||||
assert app.url_for(route_name) == '/v1/{}'.format(method)
|
||||
url = app.url_for('test_bp.{}'.format(route_name2))
|
||||
assert url == '/v1/bp/{}'.format(method)
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_shorthand_default_routes_get():
|
||||
app = Sanic('test_shorhand_routes_get')
|
||||
|
||||
@app.get('/get')
|
||||
def handler(request):
|
||||
return text('OK')
|
||||
|
||||
assert app.router.routes_all['/get'].name == 'handler'
|
||||
assert app.url_for('handler') == '/get'
|
||||
|
||||
|
||||
def test_shorthand_named_routes_get():
|
||||
app = Sanic('test_shorhand_routes_get')
|
||||
bp = Blueprint('test_bp', url_prefix='/bp')
|
||||
|
||||
@app.get('/get', name='route_get')
|
||||
def handler(request):
|
||||
return text('OK')
|
||||
|
||||
@bp.get('/get', name='route_bp')
|
||||
def handler2(request):
|
||||
return text('Blueprint')
|
||||
|
||||
app.blueprint(bp)
|
||||
|
||||
assert app.router.routes_all['/get'].name == 'route_get'
|
||||
assert app.url_for('route_get') == '/get'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
assert app.router.routes_all['/bp/get'].name == 'test_bp.route_bp'
|
||||
assert app.url_for('test_bp.route_bp') == '/bp/get'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('test_bp.handler2')
|
||||
|
||||
|
||||
def test_shorthand_named_routes_post():
|
||||
app = Sanic('test_shorhand_routes_post')
|
||||
|
||||
@app.post('/post', name='route_name')
|
||||
def handler(request):
|
||||
return text('OK')
|
||||
|
||||
assert app.router.routes_all['/post'].name == 'route_name'
|
||||
assert app.url_for('route_name') == '/post'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_shorthand_named_routes_put():
|
||||
app = Sanic('test_shorhand_routes_put')
|
||||
|
||||
@app.put('/put', name='route_put')
|
||||
def handler(request):
|
||||
assert request.stream is None
|
||||
return text('OK')
|
||||
|
||||
assert app.is_request_stream is False
|
||||
assert app.router.routes_all['/put'].name == 'route_put'
|
||||
assert app.url_for('route_put') == '/put'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_shorthand_named_routes_delete():
|
||||
app = Sanic('test_shorhand_routes_delete')
|
||||
|
||||
@app.delete('/delete', name='route_delete')
|
||||
def handler(request):
|
||||
assert request.stream is None
|
||||
return text('OK')
|
||||
|
||||
assert app.is_request_stream is False
|
||||
assert app.router.routes_all['/delete'].name == 'route_delete'
|
||||
assert app.url_for('route_delete') == '/delete'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_shorthand_named_routes_patch():
|
||||
app = Sanic('test_shorhand_routes_patch')
|
||||
|
||||
@app.patch('/patch', name='route_patch')
|
||||
def handler(request):
|
||||
assert request.stream is None
|
||||
return text('OK')
|
||||
|
||||
assert app.is_request_stream is False
|
||||
assert app.router.routes_all['/patch'].name == 'route_patch'
|
||||
assert app.url_for('route_patch') == '/patch'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_shorthand_named_routes_head():
|
||||
app = Sanic('test_shorhand_routes_head')
|
||||
|
||||
@app.head('/head', name='route_head')
|
||||
def handler(request):
|
||||
assert request.stream is None
|
||||
return text('OK')
|
||||
|
||||
assert app.is_request_stream is False
|
||||
assert app.router.routes_all['/head'].name == 'route_head'
|
||||
assert app.url_for('route_head') == '/head'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_shorthand_named_routes_options():
|
||||
app = Sanic('test_shorhand_routes_options')
|
||||
|
||||
@app.options('/options', name='route_options')
|
||||
def handler(request):
|
||||
assert request.stream is None
|
||||
return text('OK')
|
||||
|
||||
assert app.is_request_stream is False
|
||||
assert app.router.routes_all['/options'].name == 'route_options'
|
||||
assert app.url_for('route_options') == '/options'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_named_static_routes():
|
||||
app = Sanic('test_dynamic_route')
|
||||
|
||||
@app.route('/test', name='route_test')
|
||||
async def handler1(request):
|
||||
return text('OK1')
|
||||
|
||||
@app.route('/pizazz', name='route_pizazz')
|
||||
async def handler2(request):
|
||||
return text('OK2')
|
||||
|
||||
assert app.router.routes_all['/test'].name == 'route_test'
|
||||
assert app.router.routes_static['/test'].name == 'route_test'
|
||||
assert app.url_for('route_test') == '/test'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler1')
|
||||
|
||||
assert app.router.routes_all['/pizazz'].name == 'route_pizazz'
|
||||
assert app.router.routes_static['/pizazz'].name == 'route_pizazz'
|
||||
assert app.url_for('route_pizazz') == '/pizazz'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler2')
|
||||
|
||||
|
||||
def test_named_dynamic_route():
|
||||
app = Sanic('test_dynamic_route')
|
||||
|
||||
results = []
|
||||
|
||||
@app.route('/folder/<name>', name='route_dynamic')
|
||||
async def handler(request, name):
|
||||
results.append(name)
|
||||
return text('OK')
|
||||
|
||||
assert app.router.routes_all['/folder/<name>'].name == 'route_dynamic'
|
||||
assert app.url_for('route_dynamic', name='test') == '/folder/test'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_dynamic_named_route_regex():
|
||||
app = Sanic('test_dynamic_route_regex')
|
||||
|
||||
@app.route('/folder/<folder_id:[A-Za-z0-9]{0,4}>', name='route_re')
|
||||
async def handler(request, folder_id):
|
||||
return text('OK')
|
||||
|
||||
route = app.router.routes_all['/folder/<folder_id:[A-Za-z0-9]{0,4}>']
|
||||
assert route.name == 'route_re'
|
||||
assert app.url_for('route_re', folder_id='test') == '/folder/test'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_dynamic_named_route_path():
|
||||
app = Sanic('test_dynamic_route_path')
|
||||
|
||||
@app.route('/<path:path>/info', name='route_dynamic_path')
|
||||
async def handler(request, path):
|
||||
return text('OK')
|
||||
|
||||
route = app.router.routes_all['/<path:path>/info']
|
||||
assert route.name == 'route_dynamic_path'
|
||||
assert app.url_for('route_dynamic_path', path='path/1') == '/path/1/info'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_dynamic_named_route_unhashable():
|
||||
app = Sanic('test_dynamic_route_unhashable')
|
||||
|
||||
@app.route('/folder/<unhashable:[A-Za-z0-9/]+>/end/',
|
||||
name='route_unhashable')
|
||||
async def handler(request, unhashable):
|
||||
return text('OK')
|
||||
|
||||
route = app.router.routes_all['/folder/<unhashable:[A-Za-z0-9/]+>/end/']
|
||||
assert route.name == 'route_unhashable'
|
||||
url = app.url_for('route_unhashable', unhashable='test/asdf')
|
||||
assert url == '/folder/test/asdf/end'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_websocket_named_route():
|
||||
app = Sanic('test_websocket_route')
|
||||
ev = asyncio.Event()
|
||||
|
||||
@app.websocket('/ws', name='route_ws')
|
||||
async def handler(request, ws):
|
||||
assert ws.subprotocol is None
|
||||
ev.set()
|
||||
|
||||
assert app.router.routes_all['/ws'].name == 'route_ws'
|
||||
assert app.url_for('route_ws') == '/ws'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_websocket_named_route_with_subprotocols():
|
||||
app = Sanic('test_websocket_route')
|
||||
results = []
|
||||
|
||||
@app.websocket('/ws', subprotocols=['foo', 'bar'], name='route_ws')
|
||||
async def handler(request, ws):
|
||||
results.append(ws.subprotocol)
|
||||
|
||||
assert app.router.routes_all['/ws'].name == 'route_ws'
|
||||
assert app.url_for('route_ws') == '/ws'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_static_add_named_route():
|
||||
app = Sanic('test_static_add_route')
|
||||
|
||||
async def handler1(request):
|
||||
return text('OK1')
|
||||
|
||||
async def handler2(request):
|
||||
return text('OK2')
|
||||
|
||||
app.add_route(handler1, '/test', name='route_test')
|
||||
app.add_route(handler2, '/test2', name='route_test2')
|
||||
|
||||
assert app.router.routes_all['/test'].name == 'route_test'
|
||||
assert app.router.routes_static['/test'].name == 'route_test'
|
||||
assert app.url_for('route_test') == '/test'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler1')
|
||||
|
||||
assert app.router.routes_all['/test2'].name == 'route_test2'
|
||||
assert app.router.routes_static['/test2'].name == 'route_test2'
|
||||
assert app.url_for('route_test2') == '/test2'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler2')
|
||||
|
||||
|
||||
def test_dynamic_add_named_route():
|
||||
app = Sanic('test_dynamic_add_route')
|
||||
|
||||
results = []
|
||||
|
||||
async def handler(request, name):
|
||||
results.append(name)
|
||||
return text('OK')
|
||||
|
||||
app.add_route(handler, '/folder/<name>', name='route_dynamic')
|
||||
assert app.router.routes_all['/folder/<name>'].name == 'route_dynamic'
|
||||
assert app.url_for('route_dynamic', name='test') == '/folder/test'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_dynamic_add_named_route_unhashable():
|
||||
app = Sanic('test_dynamic_add_route_unhashable')
|
||||
|
||||
async def handler(request, unhashable):
|
||||
return text('OK')
|
||||
|
||||
app.add_route(handler, '/folder/<unhashable:[A-Za-z0-9/]+>/end/',
|
||||
name='route_unhashable')
|
||||
route = app.router.routes_all['/folder/<unhashable:[A-Za-z0-9/]+>/end/']
|
||||
assert route.name == 'route_unhashable'
|
||||
url = app.url_for('route_unhashable', unhashable='folder1')
|
||||
assert url == '/folder/folder1/end'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler')
|
||||
|
||||
|
||||
def test_overload_routes():
|
||||
app = Sanic('test_dynamic_route')
|
||||
|
||||
@app.route('/overload', methods=['GET'], name='route_first')
|
||||
async def handler1(request):
|
||||
return text('OK1')
|
||||
|
||||
@app.route('/overload', methods=['POST', 'PUT'], name='route_second')
|
||||
async def handler1(request):
|
||||
return text('OK2')
|
||||
|
||||
request, response = app.test_client.get(app.url_for('route_first'))
|
||||
assert response.text == 'OK1'
|
||||
|
||||
request, response = app.test_client.post(app.url_for('route_first'))
|
||||
assert response.text == 'OK2'
|
||||
|
||||
request, response = app.test_client.put(app.url_for('route_first'))
|
||||
assert response.text == 'OK2'
|
||||
|
||||
request, response = app.test_client.get(app.url_for('route_second'))
|
||||
assert response.text == 'OK1'
|
||||
|
||||
request, response = app.test_client.post(app.url_for('route_second'))
|
||||
assert response.text == 'OK2'
|
||||
|
||||
request, response = app.test_client.put(app.url_for('route_second'))
|
||||
assert response.text == 'OK2'
|
||||
|
||||
assert app.router.routes_all['/overload'].name == 'route_first'
|
||||
with pytest.raises(URLBuildError):
|
||||
app.url_for('handler1')
|
||||
|
||||
assert app.url_for('route_first') == '/overload'
|
||||
assert app.url_for('route_second') == app.url_for('route_first')
|
Loading…
Reference in New Issue
Block a user