add name option for route building
This commit is contained in:
parent
826f1b4713
commit
eab809d410
|
@ -239,3 +239,31 @@ def handler(request):
|
||||||
|
|
||||||
app.blueprint(bp)
|
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')`
|
||||||
|
```
|
||||||
|
|
62
sanic/app.py
62
sanic/app.py
|
@ -112,7 +112,7 @@ class Sanic:
|
||||||
|
|
||||||
# Decorator
|
# Decorator
|
||||||
def route(self, uri, methods=frozenset({'GET'}), host=None,
|
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
|
"""Decorate a function to be registered as a route
|
||||||
|
|
||||||
:param uri: path of the URL
|
:param uri: path of the URL
|
||||||
|
@ -120,6 +120,8 @@ class Sanic:
|
||||||
:param host:
|
:param host:
|
||||||
:param strict_slashes:
|
:param strict_slashes:
|
||||||
:param stream:
|
:param stream:
|
||||||
|
:param version:
|
||||||
|
:param name: user defined route name for url_for
|
||||||
:return: decorated function
|
:return: decorated function
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -139,48 +141,56 @@ class Sanic:
|
||||||
handler.is_stream = stream
|
handler.is_stream = stream
|
||||||
self.router.add(uri=uri, methods=methods, handler=handler,
|
self.router.add(uri=uri, methods=methods, handler=handler,
|
||||||
host=host, strict_slashes=strict_slashes,
|
host=host, strict_slashes=strict_slashes,
|
||||||
version=version)
|
version=version, name=name)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# Shorthand method decorators
|
# 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,
|
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,
|
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,
|
return self.route(uri, methods=frozenset({"POST"}), host=host,
|
||||||
strict_slashes=strict_slashes, stream=stream,
|
strict_slashes=strict_slashes, stream=stream,
|
||||||
version=version)
|
version=version, name=name)
|
||||||
|
|
||||||
def put(self, uri, host=None, strict_slashes=None, stream=False,
|
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,
|
return self.route(uri, methods=frozenset({"PUT"}), host=host,
|
||||||
strict_slashes=strict_slashes, stream=stream,
|
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,
|
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,
|
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,
|
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,
|
return self.route(uri, methods=frozenset({"PATCH"}), host=host,
|
||||||
strict_slashes=strict_slashes, stream=stream,
|
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,
|
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,
|
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
|
"""A helper method to register class instance or
|
||||||
functions as a handler to the application url
|
functions as a handler to the application url
|
||||||
routes.
|
routes.
|
||||||
|
@ -190,6 +200,9 @@ class Sanic:
|
||||||
:param methods: list or tuple of methods allowed, these are overridden
|
:param methods: list or tuple of methods allowed, these are overridden
|
||||||
if using a HTTPMethodView
|
if using a HTTPMethodView
|
||||||
:param host:
|
:param host:
|
||||||
|
:param strict_slashes:
|
||||||
|
:param version:
|
||||||
|
:param name: user defined route name for url_for
|
||||||
:return: function or class instance
|
:return: function or class instance
|
||||||
"""
|
"""
|
||||||
stream = False
|
stream = False
|
||||||
|
@ -217,12 +230,12 @@ class Sanic:
|
||||||
|
|
||||||
self.route(uri=uri, methods=methods, host=host,
|
self.route(uri=uri, methods=methods, host=host,
|
||||||
strict_slashes=strict_slashes, stream=stream,
|
strict_slashes=strict_slashes, stream=stream,
|
||||||
version=version)(handler)
|
version=version, name=name)(handler)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
# Decorator
|
# Decorator
|
||||||
def websocket(self, uri, host=None, strict_slashes=None,
|
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
|
"""Decorate a function to be registered as a websocket route
|
||||||
:param uri: path of the URL
|
:param uri: path of the URL
|
||||||
:param subprotocols: optional list of strings with the supported
|
:param subprotocols: optional list of strings with the supported
|
||||||
|
@ -265,19 +278,19 @@ class Sanic:
|
||||||
|
|
||||||
self.router.add(uri=uri, handler=websocket_handler,
|
self.router.add(uri=uri, handler=websocket_handler,
|
||||||
methods=frozenset({'GET'}), host=host,
|
methods=frozenset({'GET'}), host=host,
|
||||||
strict_slashes=strict_slashes)
|
strict_slashes=strict_slashes, name=name)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def add_websocket_route(self, handler, uri, host=None,
|
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."""
|
"""A helper method to register a function as a websocket route."""
|
||||||
if strict_slashes is None:
|
if strict_slashes is None:
|
||||||
strict_slashes = self.strict_slashes
|
strict_slashes = self.strict_slashes
|
||||||
|
|
||||||
return self.websocket(uri, host=host,
|
return self.websocket(uri, host=host, strict_slashes=strict_slashes,
|
||||||
strict_slashes=strict_slashes)(handler)
|
name=name)(handler)
|
||||||
|
|
||||||
def enable_websocket(self, enable=True):
|
def enable_websocket(self, enable=True):
|
||||||
"""Enable or disable the support for websocket.
|
"""Enable or disable the support for websocket.
|
||||||
|
@ -400,8 +413,7 @@ class Sanic:
|
||||||
uri, route = self.router.find_route_by_view_name(view_name)
|
uri, route = self.router.find_route_by_view_name(view_name)
|
||||||
|
|
||||||
if not uri or not route:
|
if not uri or not route:
|
||||||
raise URLBuildError(
|
raise URLBuildError('Endpoint with name `{}` was not found'.format(
|
||||||
'Endpoint with name `{}` was not found'.format(
|
|
||||||
view_name))
|
view_name))
|
||||||
|
|
||||||
if uri != '/' and uri.endswith('/'):
|
if uri != '/' and uri.endswith('/'):
|
||||||
|
|
|
@ -5,7 +5,7 @@ from sanic.views import CompositionView
|
||||||
|
|
||||||
FutureRoute = namedtuple('Route',
|
FutureRoute = namedtuple('Route',
|
||||||
['handler', 'uri', 'methods', 'host',
|
['handler', 'uri', 'methods', 'host',
|
||||||
'strict_slashes', 'stream', 'version'])
|
'strict_slashes', 'stream', 'version', 'name'])
|
||||||
FutureListener = namedtuple('Listener', ['handler', 'uri', 'methods', 'host'])
|
FutureListener = namedtuple('Listener', ['handler', 'uri', 'methods', 'host'])
|
||||||
FutureMiddleware = namedtuple('Route', ['middleware', 'args', 'kwargs'])
|
FutureMiddleware = namedtuple('Route', ['middleware', 'args', 'kwargs'])
|
||||||
FutureException = namedtuple('Route', ['handler', 'args', 'kwargs'])
|
FutureException = namedtuple('Route', ['handler', 'args', 'kwargs'])
|
||||||
|
@ -53,13 +53,13 @@ class Blueprint:
|
||||||
|
|
||||||
version = future.version or self.version
|
version = future.version or self.version
|
||||||
|
|
||||||
app.route(
|
app.route(uri=uri[1:] if uri.startswith('//') else uri,
|
||||||
uri=uri[1:] if uri.startswith('//') else uri,
|
|
||||||
methods=future.methods,
|
methods=future.methods,
|
||||||
host=future.host or self.host,
|
host=future.host or self.host,
|
||||||
strict_slashes=future.strict_slashes,
|
strict_slashes=future.strict_slashes,
|
||||||
stream=future.stream,
|
stream=future.stream,
|
||||||
version=version
|
version=version,
|
||||||
|
name=future.name,
|
||||||
)(future.handler)
|
)(future.handler)
|
||||||
|
|
||||||
for future in self.websocket_routes:
|
for future in self.websocket_routes:
|
||||||
|
@ -68,10 +68,10 @@ class Blueprint:
|
||||||
future.handler.__blueprintname__ = self.name
|
future.handler.__blueprintname__ = self.name
|
||||||
# Prepend the blueprint URI prefix if available
|
# Prepend the blueprint URI prefix if available
|
||||||
uri = url_prefix + future.uri if url_prefix else future.uri
|
uri = url_prefix + future.uri if url_prefix else future.uri
|
||||||
app.websocket(
|
app.websocket(uri=uri,
|
||||||
uri=uri,
|
|
||||||
host=future.host or self.host,
|
host=future.host or self.host,
|
||||||
strict_slashes=future.strict_slashes
|
strict_slashes=future.strict_slashes,
|
||||||
|
name=future.name,
|
||||||
)(future.handler)
|
)(future.handler)
|
||||||
|
|
||||||
# Middleware
|
# Middleware
|
||||||
|
@ -100,7 +100,7 @@ class Blueprint:
|
||||||
app.listener(event)(listener)
|
app.listener(event)(listener)
|
||||||
|
|
||||||
def route(self, uri, methods=frozenset({'GET'}), host=None,
|
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.
|
"""Create a blueprint route from a decorated function.
|
||||||
|
|
||||||
:param uri: endpoint at which the route will be accessible.
|
:param uri: endpoint at which the route will be accessible.
|
||||||
|
@ -111,19 +111,24 @@ class Blueprint:
|
||||||
|
|
||||||
def decorator(handler):
|
def decorator(handler):
|
||||||
route = FutureRoute(
|
route = FutureRoute(
|
||||||
handler, uri, methods, host, strict_slashes, stream, version)
|
handler, uri, methods, host, strict_slashes, stream, version,
|
||||||
|
name)
|
||||||
self.routes.append(route)
|
self.routes.append(route)
|
||||||
return handler
|
return handler
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def add_route(self, handler, uri, methods=frozenset({'GET'}), host=None,
|
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.
|
"""Create a blueprint route from a function.
|
||||||
|
|
||||||
:param handler: function for handling uri requests. Accepts function,
|
:param handler: function for handling uri requests. Accepts function,
|
||||||
or class instance with a view_class method.
|
or class instance with a view_class method.
|
||||||
:param uri: endpoint at which the route will be accessible.
|
:param uri: endpoint at which the route will be accessible.
|
||||||
:param methods: list of acceptable HTTP methods.
|
: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
|
:return: function or class instance
|
||||||
"""
|
"""
|
||||||
# Handle HTTPMethodView differently
|
# Handle HTTPMethodView differently
|
||||||
|
@ -142,10 +147,12 @@ class Blueprint:
|
||||||
methods = handler.handlers.keys()
|
methods = handler.handlers.keys()
|
||||||
|
|
||||||
self.route(uri=uri, methods=methods, host=host,
|
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
|
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.
|
"""Create a blueprint websocket route from a decorated function.
|
||||||
|
|
||||||
:param uri: endpoint at which the route will be accessible.
|
:param uri: endpoint at which the route will be accessible.
|
||||||
|
@ -155,12 +162,13 @@ class Blueprint:
|
||||||
|
|
||||||
def decorator(handler):
|
def decorator(handler):
|
||||||
route = FutureRoute(handler, uri, [], host, strict_slashes,
|
route = FutureRoute(handler, uri, [], host, strict_slashes,
|
||||||
False, version)
|
False, version, name)
|
||||||
self.websocket_routes.append(route)
|
self.websocket_routes.append(route)
|
||||||
return handler
|
return handler
|
||||||
return decorator
|
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.
|
"""Create a blueprint websocket route from a function.
|
||||||
|
|
||||||
:param handler: function for handling uri requests. Accepts 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.
|
:param uri: endpoint at which the route will be accessible.
|
||||||
:return: function or class instance
|
: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
|
return handler
|
||||||
|
|
||||||
def listener(self, event):
|
def listener(self, event):
|
||||||
|
@ -214,36 +222,44 @@ class Blueprint:
|
||||||
self.statics.append(static)
|
self.statics.append(static)
|
||||||
|
|
||||||
# Shorthand method decorators
|
# 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,
|
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,
|
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,
|
return self.route(uri, methods=["POST"], host=host,
|
||||||
strict_slashes=strict_slashes, stream=stream,
|
strict_slashes=strict_slashes, stream=stream,
|
||||||
version=version)
|
version=version, name=name)
|
||||||
|
|
||||||
def put(self, uri, host=None, strict_slashes=None, stream=False,
|
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,
|
return self.route(uri, methods=["PUT"], host=host,
|
||||||
strict_slashes=strict_slashes, stream=stream,
|
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,
|
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,
|
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,
|
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,
|
return self.route(uri, methods=["PATCH"], host=host,
|
||||||
strict_slashes=strict_slashes, stream=stream,
|
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,
|
return self.route(uri, methods=["DELETE"], host=host,
|
||||||
strict_slashes=strict_slashes, version=version)
|
strict_slashes=strict_slashes, version=version,
|
||||||
|
name=name)
|
||||||
|
|
|
@ -99,7 +99,7 @@ class Router:
|
||||||
return name, _type, pattern
|
return name, _type, pattern
|
||||||
|
|
||||||
def add(self, uri, methods, handler, host=None, strict_slashes=False,
|
def add(self, uri, methods, handler, host=None, strict_slashes=False,
|
||||||
version=None):
|
version=None, name=None):
|
||||||
"""Add a handler to the route list
|
"""Add a handler to the route list
|
||||||
|
|
||||||
:param uri: path to match
|
:param uri: path to match
|
||||||
|
@ -118,7 +118,7 @@ class Router:
|
||||||
else:
|
else:
|
||||||
uri = "/".join(["/v{}".format(str(version)), uri])
|
uri = "/".join(["/v{}".format(str(version)), uri])
|
||||||
# add regular version
|
# add regular version
|
||||||
self._add(uri, methods, handler, host)
|
self._add(uri, methods, handler, host, name)
|
||||||
|
|
||||||
if strict_slashes:
|
if strict_slashes:
|
||||||
return
|
return
|
||||||
|
@ -135,12 +135,12 @@ class Router:
|
||||||
)
|
)
|
||||||
# add version with trailing slash
|
# add version with trailing slash
|
||||||
if slash_is_missing:
|
if slash_is_missing:
|
||||||
self._add(uri + '/', methods, handler, host)
|
self._add(uri + '/', methods, handler, host, name)
|
||||||
# add version without trailing slash
|
# add version without trailing slash
|
||||||
elif without_slash_is_missing:
|
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
|
"""Add a handler to the route list
|
||||||
|
|
||||||
:param uri: path to match
|
:param uri: path to match
|
||||||
|
@ -161,7 +161,7 @@ class Router:
|
||||||
"host strings, not {!r}".format(host))
|
"host strings, not {!r}".format(host))
|
||||||
|
|
||||||
for host_ in host:
|
for host_ in host:
|
||||||
self.add(uri, methods, handler, host_)
|
self.add(uri, methods, handler, host_, name)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Dict for faster lookups of if method allowed
|
# Dict for faster lookups of if method allowed
|
||||||
|
@ -236,9 +236,9 @@ class Router:
|
||||||
# if available
|
# if available
|
||||||
if hasattr(handler, '__blueprintname__'):
|
if hasattr(handler, '__blueprintname__'):
|
||||||
handler_name = '{}.{}'.format(
|
handler_name = '{}.{}'.format(
|
||||||
handler.__blueprintname__, handler.__name__)
|
handler.__blueprintname__, name or handler.__name__)
|
||||||
else:
|
else:
|
||||||
handler_name = getattr(handler, '__name__', None)
|
handler_name = name or getattr(handler, '__name__', None)
|
||||||
|
|
||||||
route = Route(
|
route = Route(
|
||||||
handler=handler, methods=methods, pattern=pattern,
|
handler=handler, methods=methods, pattern=pattern,
|
||||||
|
|
391
tests/test_named_routes.py
Normal file
391
tests/test_named_routes.py
Normal file
|
@ -0,0 +1,391 @@
|
||||||
|
#!/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.router import RouteExists, RouteDoesNotExist
|
||||||
|
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 handler2(request):
|
||||||
|
return text('OK2')
|
||||||
|
|
||||||
|
@app.route('/overload2', methods=['POST', 'PUT'], name='route_third')
|
||||||
|
async def handler3(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'
|
||||||
|
|
||||||
|
assert app.router.routes_all['/overload'].name == 'route_first'
|
||||||
|
with pytest.raises(URLBuildError):
|
||||||
|
app.url_for('handler1')
|
||||||
|
|
||||||
|
with pytest.raises(URLBuildError):
|
||||||
|
app.url_for('handler2')
|
||||||
|
|
||||||
|
with pytest.raises(URLBuildError):
|
||||||
|
app.url_for('route_second')
|
||||||
|
|
||||||
|
assert app.url_for('route_third') == '/overload2'
|
||||||
|
with pytest.raises(URLBuildError):
|
||||||
|
app.url_for('handler3')
|
Loading…
Reference in New Issue
Block a user