Restructured blueprint class

Blueprints currently queue functions to be called, which are simple, yet
hard to inspect.  These changes allow tools to be built that analyze
blueprints more easily.
This commit is contained in:
Channel Cat 2017-01-29 17:39:55 -08:00 committed by Kyle Blöm
parent 2a4c74619b
commit 206e55a911

View File

@ -1,59 +1,11 @@
from collections import defaultdict from collections import defaultdict, namedtuple
class BlueprintSetup: FutureRoute = namedtuple('Route', ['handler', 'uri', 'methods', 'host'])
""" FutureListener = namedtuple('Listener', ['handler', 'uri', 'methods', 'host'])
Creates a blueprint state like object. FutureMiddleware = namedtuple('Route', ['middleware', 'args', 'kwargs'])
""" FutureException = namedtuple('Route', ['handler', 'args', 'kwargs'])
FutureStatic = namedtuple('Route', ['uri', 'file_or_directory', 'args', 'kwargs'])
def __init__(self, blueprint, app, options):
self.app = app
self.blueprint = blueprint
self.options = options
url_prefix = self.options.get('url_prefix')
if url_prefix is None:
url_prefix = self.blueprint.url_prefix
#: The prefix that should be used for all URLs defined on the
#: blueprint.
self.url_prefix = url_prefix
def add_route(self, handler, uri, methods, host=None):
"""
A helper method to register a handler to the application url routes.
"""
if self.url_prefix:
uri = self.url_prefix + uri
if host is None:
host = self.blueprint.host
self.app.route(uri=uri, methods=methods, host=host)(handler)
def add_exception(self, handler, *args, **kwargs):
"""
Registers exceptions to sanic.
"""
self.app.exception(*args, **kwargs)(handler)
def add_static(self, uri, file_or_directory, *args, **kwargs):
"""
Registers static files to sanic.
"""
if self.url_prefix:
uri = self.url_prefix + uri
self.app.static(uri, file_or_directory, *args, **kwargs)
def add_middleware(self, middleware, *args, **kwargs):
"""
Registers middleware to sanic.
"""
if args or kwargs:
self.app.middleware(*args, **kwargs)(middleware)
else:
self.app.middleware(middleware)
class Blueprint: class Blueprint:
@ -65,30 +17,47 @@ class Blueprint:
""" """
self.name = name self.name = name
self.url_prefix = url_prefix self.url_prefix = url_prefix
self.deferred_functions = []
self.listeners = defaultdict(list)
self.host = host self.host = host
def record(self, func): self.routes = []
""" self.exceptions = []
Registers a callback function that is invoked when the blueprint is self.listeners = defaultdict(list)
registered on the application. self.middlewares = []
""" self.statics = []
self.deferred_functions.append(func)
def make_setup_state(self, app, options):
"""
Returns a new BlueprintSetup object
"""
return BlueprintSetup(self, app, options)
def register(self, app, options): def register(self, app, options):
""" """
Registers the blueprint to the sanic app. Registers the blueprint to the sanic app.
""" """
state = self.make_setup_state(app, options)
for deferred in self.deferred_functions: url_prefix = options.get('url_prefix', self.url_prefix)
deferred(state)
# Routes
for future in self.routes:
# Prepend the blueprint URI prefix if available
uri = url_prefix + future.uri if url_prefix else future.uri
app.route(
uri=uri,
methods=future.methods,
host=future.host or self.host
)(future.handler)
# Middleware
for future in self.middlewares:
if future.args or future.kwargs:
app.middleware(*future.args, **future.kwargs)(future.middleware)
else:
app.middleware(future.middleware)
# Exceptions
for future in self.exceptions:
app.exception(*future.args, **future.kwargs)(future.handler)
# Static Files
for future in self.statics:
# Prepend the blueprint URI prefix if available
uri = url_prefix + future.uri if url_prefix else future.uri
app.static(uri, future.file_or_directory, *future.args, **future.kwargs)
def route(self, uri, methods=frozenset({'GET'}), host=None): def route(self, uri, methods=frozenset({'GET'}), host=None):
""" """
@ -97,7 +66,8 @@ class Blueprint:
:param methods: List of acceptable HTTP methods. :param methods: List of acceptable HTTP methods.
""" """
def decorator(handler): def decorator(handler):
self.record(lambda s: s.add_route(handler, uri, methods, host)) route = FutureRoute(handler, uri, methods, host)
self.routes.append(route)
return handler return handler
return decorator return decorator
@ -108,7 +78,8 @@ class Blueprint:
: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.
""" """
self.record(lambda s: s.add_route(handler, uri, methods, host)) route = FutureRoute(handler, uri, methods, host)
self.routes.append(route)
return handler return handler
def listener(self, event): def listener(self, event):
@ -125,10 +96,10 @@ class Blueprint:
""" """
Creates a blueprint middleware from a decorated function. Creates a blueprint middleware from a decorated function.
""" """
def register_middleware(middleware): def register_middleware(_middleware):
self.record( future_middleware = FutureMiddleware(_middleware, args, kwargs)
lambda s: s.add_middleware(middleware, *args, **kwargs)) self.middlewares.append(future_middleware)
return middleware return _middleware
# Detect which way this was called, @middleware or @middleware('AT') # Detect which way this was called, @middleware or @middleware('AT')
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]): if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
@ -143,7 +114,8 @@ class Blueprint:
Creates a blueprint exception from a decorated function. Creates a blueprint exception from a decorated function.
""" """
def decorator(handler): def decorator(handler):
self.record(lambda s: s.add_exception(handler, *args, **kwargs)) exception = FutureException(handler, args, kwargs)
self.exceptions.append(exception)
return handler return handler
return decorator return decorator
@ -153,5 +125,5 @@ class Blueprint:
:param uri: Endpoint at which the route will be accessible. :param uri: Endpoint at which the route will be accessible.
:param file_or_directory: Static asset. :param file_or_directory: Static asset.
""" """
self.record( static = FutureStatic(uri, file_or_directory, args, kwargs)
lambda s: s.add_static(uri, file_or_directory, *args, **kwargs)) self.statics.append(static)