This commit is contained in:
Suby Raman
2017-02-02 16:24:16 -05:00
parent 48aa51b739
commit d614823013
4 changed files with 107 additions and 20 deletions

View File

@@ -4,8 +4,7 @@ from functools import lru_cache
from .exceptions import NotFound, InvalidUsage
from .views import CompositionView
Route = namedtuple(
'Route',
Route = namedtuple('Route',
['handler', 'methods', 'pattern', 'parameters', 'name'])
Parameter = namedtuple('Parameter', ['name', 'cast'])
@@ -70,6 +69,28 @@ class Router:
self.routes_always_check = []
self.hosts = None
def __str__(self):
"""
The typical user inspecting the router will likely want to see
the routes available. Provide a simple representation.
"""
def _route_to_str(uri, route):
out = 'name={0.name}, methods={0.methods}, URI={1}>\n'.format(
route, uri)
if route.handler.__doc__:
out += '{}\n'.format(route.handler.__doc__)
out += '\n'
return out
out = ''
for uri, route in self.routes_all.items():
out += _route_to_str(uri, route)
return out
def parse_parameter_string(self, parameter_string):
"""
Parse a parameter string into its constituent name, type, and pattern
@@ -130,11 +151,16 @@ class Router:
properties = {"unhashable": None}
def add_parameter(match):
# We could receive NAME or NAME:PATTERN
name = match.group(1)
name, _type, pattern = self.parse_parameter_string(name)
pattern = 'string'
if ':' in name:
name, pattern = name.split(':', 1)
parameter = Parameter(
name=name, cast=_type)
default = (str, pattern)
# Pull from pre-configured types
_type, pattern = REGEX_TYPES.get(pattern, default)
parameter = Parameter(name=name, cast=_type)
parameters.append(parameter)
# Mark the whole route as unhashable if it has the hash key in it
@@ -146,7 +172,7 @@ class Router:
return '({})'.format(pattern)
pattern_string = re.sub(self.parameter_pattern, add_parameter, uri)
pattern_string = re.sub(r'<(.+?)>', add_parameter, uri)
pattern = re.compile(r'^{}$'.format(pattern_string))
def merge_route(route, methods, handler):

View File

@@ -17,6 +17,7 @@ from .response import HTTPResponse
from .router import Router
from .server import serve, serve_multiple, HttpProtocol
from .static import register as static_register
from .views import CompositionView
class Sanic:
@@ -120,7 +121,16 @@ class Sanic:
"""
# Handle HTTPMethodView differently
if hasattr(handler, 'view_class'):
methods = frozenset(HTTP_METHODS)
methods = set()
for method in HTTP_METHODS:
if getattr(handler.view_class, method.lower(), None):
methods.add(method)
# handle composition view differently
if isinstance(handler, CompositionView):
methods = handler.handlers.keys()
self.route(uri=uri, methods=methods, host=host)(handler)
return handler

View File

@@ -1,4 +1,5 @@
from .exceptions import InvalidUsage
from .constants import HTTP_METHODS
class HTTPMethodView:
@@ -40,11 +41,7 @@ class HTTPMethodView:
def dispatch_request(self, request, *args, **kwargs):
handler = getattr(self, request.method.lower(), None)
if handler:
return handler(request, *args, **kwargs)
raise InvalidUsage(
'Method {} not allowed for URL {}'.format(
request.method, request.url), status_code=405)
return handler(request, *args, **kwargs)
@classmethod
def as_view(cls, *class_args, **class_kwargs):
@@ -89,15 +86,15 @@ class CompositionView:
def add(self, methods, handler):
for method in methods:
if method not in HTTP_METHODS:
raise InvalidUsage(
'{} is not a valid HTTP method.'.format(method))
if method in self.handlers:
raise KeyError(
'Method {} already is registered.'.format(method))
raise InvalidUsage(
'Method {} is already registered.'.format(method))
self.handlers[method] = handler
def __call__(self, request, *args, **kwargs):
handler = self.handlers.get(request.method.upper(), None)
if handler is None:
raise InvalidUsage(
'Method {} not allowed for URL {}'.format(
request.method, request.url), status_code=405)
handler = self.handlers[request.method.upper()]
return handler(request, *args, **kwargs)