ranchimallflo-api/py3.7/lib/python3.7/site-packages/quart/blueprints.py

789 lines
27 KiB
Python

from functools import update_wrapper
from json import JSONDecoder, JSONEncoder
from typing import Callable, Iterable, List, Optional, Type, TYPE_CHECKING, Union
from .static import PackageStatic
if TYPE_CHECKING:
from .app import Quart # noqa
DeferedSetupFunction = Callable[['BlueprintSetupState'], Callable]
class Blueprint(PackageStatic):
"""A blueprint is a collection of application properties.
The application properties include routes, error handlers, and
before and after request functions. It is useful to produce
modular code as it allows the properties to be defined in a
blueprint thereby defering the addition of these properties to the
app.
Attributes:
json_decoder: The decoder to use for routes in this blueprint,
the default, None, indicates that the app encoder should be
used.
json_encoder: The encoder to use for routes in this blueprint,
the default, None, indicates that the app encoder should be
used.
url_prefix: An additional prefix to every route rule in the
blueprint.
"""
json_decoder: Optional[JSONDecoder] = None
json_encoder: Optional[JSONEncoder] = None
def __init__(
self,
name: str,
import_name: str,
static_folder: Optional[str]=None,
static_url_path: Optional[str]=None,
template_folder: Optional[str]=None,
url_prefix: Optional[str]=None,
subdomain: Optional[str]=None,
root_path: Optional[str]=None,
) -> None:
super().__init__(import_name, template_folder, root_path, static_folder, static_url_path)
self.name = name
self.url_prefix = url_prefix
self.deferred_functions: List[DeferedSetupFunction] = []
self.subdomain = subdomain
def route(
self,
path: str,
methods: Optional[List[str]]=None,
endpoint: Optional[str]=None,
defaults: Optional[dict]=None,
host: Optional[str]=None,
subdomain: Optional[str]=None,
*,
provide_automatic_options: Optional[bool]=None,
strict_slashes: bool=True,
) -> Callable:
"""Add a route to the blueprint.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.route`. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.route('/')
def route():
...
"""
def decorator(func: Callable) -> Callable:
self.add_url_rule(
path, endpoint, func, methods, defaults=defaults, host=host, subdomain=subdomain,
provide_automatic_options=provide_automatic_options, strict_slashes=strict_slashes,
)
return func
return decorator
def add_url_rule(
self,
path: str,
endpoint: Optional[str]=None,
view_func: Optional[Callable]=None,
methods: Optional[Iterable[str]]=None,
defaults: Optional[dict]=None,
host: Optional[str]=None,
subdomain: Optional[str]=None,
*,
provide_automatic_options: Optional[bool]=None,
is_websocket: bool=False,
strict_slashes: bool=True,
) -> None:
"""Add a route/url rule to the blueprint.
This is designed to be used on the blueprint directly, and
has the same arguments as
:meth:`~quart.Quart.add_url_rule`. An example usage,
.. code-block:: python
def route():
...
blueprint = Blueprint(__name__)
blueprint.add_url_rule('/', route)
"""
endpoint = endpoint or view_func.__name__
if '.' in endpoint:
raise ValueError('Blueprint endpoints should not contain periods')
self.record(
lambda state: state.add_url_rule(
path, endpoint, view_func, methods, defaults, host, self.subdomain,
provide_automatic_options=provide_automatic_options, is_websocket=is_websocket,
strict_slashes=strict_slashes,
),
)
def websocket(
self,
path: str,
endpoint: Optional[str]=None,
defaults: Optional[dict]=None,
host: Optional[str]=None,
subdomain: Optional[str]=None,
*,
strict_slashes: bool=True,
) -> Callable:
"""Add a websocket to the blueprint.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.websocket`. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.websocket('/')
async def route():
...
"""
def decorator(func: Callable) -> Callable:
self.add_websocket(
path, endpoint, func, defaults=defaults, host=host, subdomain=subdomain,
strict_slashes=strict_slashes,
)
return func
return decorator
def add_websocket(
self,
path: str,
endpoint: Optional[str]=None,
view_func: Optional[Callable]=None,
defaults: Optional[dict]=None,
host: Optional[str]=None,
subdomain: Optional[str]=None,
*,
strict_slashes: bool=True,
) -> None:
"""Add a websocket rule to the blueprint.
This is designed to be used on the blueprint directly, and
has the same arguments as
:meth:`~quart.Quart.add_websocket`. An example usage,
.. code-block:: python
def route():
...
blueprint = Blueprint(__name__)
blueprint.add_websocket('/', route)
"""
return self.add_url_rule(
path, endpoint, view_func, {'GET'}, defaults=defaults, host=host, subdomain=subdomain,
provide_automatic_options=False, is_websocket=True, strict_slashes=strict_slashes,
)
def endpoint(self, endpoint: str) -> Callable:
"""Add an endpoint to the blueprint.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.endpoint`. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.endpoint('index')
def index():
...
"""
def decorator(func: Callable) -> Callable:
self.record_once(lambda state: state.register_endpoint(endpoint, func))
return func
return decorator
def app_template_filter(self, name: Optional[str]=None) -> Callable:
"""Add an application wide template filter.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.template_filter`. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.app_template_filter()
def filter(value):
...
"""
def decorator(func: Callable) -> Callable:
self.add_app_template_filter(func, name=name)
return func
return decorator
def add_app_template_filter(self, func: Callable, name: Optional[str]=None) -> None:
"""Add an application wide template filter.
This is designed to be used on the blueprint directly, and
has the same arguments as
:meth:`~quart.Quart.add_template_filter`. An example usage,
.. code-block:: python
def filter():
...
blueprint = Blueprint(__name__)
blueprint.add_app_template_filter(filter)
"""
self.record_once(lambda state: state.register_template_filter(func, name))
def app_template_test(self, name: Optional[str]=None) -> Callable:
"""Add an application wide template test.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.template_test`. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.app_template_test()
def test(value):
...
"""
def decorator(func: Callable) -> Callable:
self.add_app_template_test(func, name=name)
return func
return decorator
def add_app_template_test(self, func: Callable, name: Optional[str]=None) -> None:
"""Add an application wide template test.
This is designed to be used on the blueprint directly, and
has the same arguments as
:meth:`~quart.Quart.add_template_test`. An example usage,
.. code-block:: python
def test():
...
blueprint = Blueprint(__name__)
blueprint.add_app_template_test(test)
"""
self.record_once(lambda state: state.register_template_test(func, name))
def app_template_global(self, name: Optional[str]=None) -> Callable:
"""Add an application wide template global.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.template_global`. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.app_template_global()
def global(value):
...
"""
def decorator(func: Callable) -> Callable:
self.add_app_template_global(func, name=name)
return func
return decorator
def add_app_template_global(self, func: Callable, name: Optional[str]=None) -> None:
"""Add an application wide template global.
This is designed to be used on the blueprint directly, and
has the same arguments as
:meth:`~quart.Quart.add_template_global`. An example usage,
.. code-block:: python
def global():
...
blueprint = Blueprint(__name__)
blueprint.add_app_template_global(global)
"""
self.record_once(lambda state: state.register_template_global(func, name))
def before_request(self, func: Callable) -> Callable:
"""Add a before request function to the Blueprint.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.before_request`. It applies only to requests that
are routed to an endpoint in this blueprint. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.before_request
def before():
...
"""
self.record_once(lambda state: state.app.before_request(func, self.name))
return func
def before_websocket(self, func: Callable) -> Callable:
"""Add a before request websocket to the Blueprint.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.before_websocket`. It applies only to requests that
are routed to an endpoint in this blueprint. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.before_websocket
def before():
...
"""
self.record_once(lambda state: state.app.before_websocket(func, self.name))
return func
def before_app_request(self, func: Callable) -> Callable:
"""Add a before request function to the app.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.before_request`. It applies to all requests to the
app this blueprint is registered on. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.before_app_request
def before():
...
"""
self.record_once(lambda state: state.app.before_request(func))
return func
def before_app_websocket(self, func: Callable) -> Callable:
"""Add a before request websocket to the App.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.before_websocket`. It applies to all requests to the
app this blueprint is registered on. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.before_app_websocket
def before():
...
"""
self.record_once(lambda state: state.app.before_websocket(func))
return func
def before_app_first_request(self, func: Callable) -> Callable:
"""Add a before request first function to the app.
This is designed to be used as a decorator, and has the same
arguments as :meth:`~quart.Quart.before_first_request`. It is
triggered before the first request to the app this blueprint
is registered on. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.before_app_first_request
def before_first():
...
"""
self.record_once(lambda state: state.app.before_first_request(func))
return func
def after_request(self, func: Callable) -> Callable:
"""Add an after request function to the Blueprint.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.after_request`. It applies only to requests that
are routed to an endpoint in this blueprint. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.after_request
def after():
...
"""
self.record_once(lambda state: state.app.after_request(func, self.name))
return func
def after_websocket(self, func: Callable) -> Callable:
"""Add an after websocket function to the Blueprint.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.after_websocket`. It applies only to requests that
are routed to an endpoint in this blueprint. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.after_websocket
def after():
...
"""
self.record_once(lambda state: state.app.after_websocket(func, self.name))
return func
def after_app_request(self, func: Callable) -> Callable:
"""Add a after request function to the app.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.after_request`. It applies to all requests to the
app this blueprint is registered on. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.after_app_request
def after():
...
"""
self.record_once(lambda state: state.app.after_request(func))
return func
def after_app_websocket(self, func: Callable) -> Callable:
"""Add an after websocket function to the App.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.after_websocket`. It applies to all requests to the
ppe this blueprint is registerd on. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.after_app_websocket
def after():
...
"""
self.record_once(lambda state: state.app.after_websocket(func))
return func
def teardown_request(self, func: Callable) -> Callable:
"""Add a teardown request function to the Blueprint.
This is designed to be used as a decorator, and has the same arguments
as :meth:`~quart.Quart.teardown_request`. It applies only to requests that
are routed to an endpoint in this blueprint. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.teardown_request
def teardown():
...
"""
self.record_once(lambda state: state.app.teardown_request(func, self.name))
return func
def teardown_websocket(self, func: Callable) -> Callable:
"""Add a teardown websocket function to the Blueprint.
This is designed to be used as a decorator, and has the same
arguments as :meth:`~quart.Quart.teardown_websocket`. It
applies only to requests that are routed to an endpoint in
this blueprint. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.teardown_websocket
def teardown():
...
"""
self.record_once(lambda state: state.app.teardown_websocket(func, self.name))
return func
def teardown_app_request(self, func: Callable) -> Callable:
"""Add a teardown request function to the app.
This is designed to be used as a decorator, and has the same
arguments as :meth:`~quart.Quart.teardown_request`. It applies
to all requests to the app this blueprint is registered on. An
example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.teardown_app_request
def teardown():
...
"""
self.record_once(lambda state: state.app.teardown_request(func))
return func
def errorhandler(self, error: Union[Type[Exception], int]) -> Callable:
"""Add an error handler function to the Blueprint.
This is designed to be used as a decorator, and has the same
arguments as :meth:`~quart.Quart.errorhandler`. It applies
only to errors that originate in routes in this blueprint. An
example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.errorhandler(404)
def not_found():
...
"""
def decorator(func: Callable) -> Callable:
self.register_error_handler(error, func)
return func
return decorator
def app_errorhandler(self, error: Union[Type[Exception], int]) -> Callable:
"""Add an error handler function to the App.
This is designed to be used as a decorator, and has the same
arguments as :meth:`~quart.Quart.errorhandler`. It applies
only to all errors. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.app_errorhandler(404)
def not_found():
...
"""
def decorator(func: Callable) -> Callable:
self.record_once(lambda state: state.app.register_error_handler(error, func))
return func
return decorator
def register_error_handler(self, error: Union[Type[Exception], int], func: Callable) -> None:
"""Add an error handler function to the blueprint.
This is designed to be used on the blueprint directly, and
has the same arguments as
:meth:`~quart.Quart.register_error_handler`. An example usage,
.. code-block:: python
def not_found():
...
blueprint = Blueprint(__name__)
blueprint.register_error_handler(404, not_found)
"""
self.record_once(lambda state: state.app.register_error_handler(error, func, self.name))
def context_processor(self, func: Callable) -> Callable:
"""Add a context processor function to this blueprint.
This is designed to be used as a decorator, and has the same
arguments as :meth:`~quart.Quart.context_processor`. This will
add context to all templates rendered in this blueprint's
routes. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.context_processor
def processor():
...
"""
self.record_once(lambda state: state.app.context_processor(func, self.name))
return func
def app_context_processor(self, func: Callable) -> Callable:
"""Add a context processor function to the app.
This is designed to be used as a decorator, and has the same
arguments as :meth:`~quart.Quart.context_processor`. This will
add context to all templates rendered. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.app_context_processor
def processor():
...
"""
self.record_once(lambda state: state.app.context_processor(func))
return func
def url_value_preprocessor(self, func: Callable) -> Callable:
"""Add a url value preprocessor.
This is designed to be used as a decorator, and has the same
arguments as :meth:`~quart.Quart.url_value_preprocessor`. This
will apply to urls in this blueprint. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.url_value_preprocessor
def processor(endpoint, view_args):
...
"""
self.record_once(lambda state: state.app.url_value_preprocessor(func, self.name))
return func
def app_url_value_preprocessor(self, func: Callable) -> Callable:
"""Add a url value preprocessor.
This is designed to be used as a decorator, and has the same
arguments as
:meth:`~quart.Quart.app_url_value_preprocessor`. This will
apply to all URLs. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.app_url_value_preprocessor
def processor(endpoint, view_args):
...
"""
self.record_once(lambda state: state.app.url_value_preprocessor(func))
return func
def url_defaults(self, func: Callable) -> Callable:
"""Add a url default preprocessor.
This is designed to be used as a decorator, and has the same
arguments as :meth:`~quart.Quart.url_defaults`. This will
apply to urls in this blueprint. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.url_defaults
def default(endpoint, values):
...
"""
self.record_once(lambda state: state.app.url_defaults(func, self.name))
return func
def app_url_defaults(self, func: Callable) -> Callable:
"""Add a url default preprocessor.
This is designed to be used as a decorator, and has the same
arguments as :meth:`~quart.Quart.url_defaults`. This will
apply to all urls. An example usage,
.. code-block:: python
blueprint = Blueprint(__name__)
@blueprint.app_url_defaults
def default(endpoint, values):
...
"""
self.record_once(lambda state: state.app.url_defaults(func))
return func
def record(self, func: DeferedSetupFunction) -> None:
"""Used to register a deferred action."""
self.deferred_functions.append(func)
def record_once(self, func: DeferedSetupFunction) -> None:
"""Used to register a deferred action that happens only once."""
def wrapper(state: 'BlueprintSetupState') -> None:
if state.first_registration:
func(state)
self.record(update_wrapper(wrapper, func))
def register(
self,
app: 'Quart',
first_registration: bool,
*,
url_prefix: Optional[str]=None,
) -> None:
"""Register this blueprint on the app given."""
state = self.make_setup_state(app, first_registration, url_prefix=url_prefix)
if self.has_static_folder:
state.add_url_rule(
self.static_url_path + '/<path:filename>',
view_func=self.send_static_file, endpoint='static',
)
for func in self.deferred_functions:
func(state)
def make_setup_state(
self,
app: 'Quart',
first_registration: bool,
*,
url_prefix: Optional[str]=None,
) -> 'BlueprintSetupState':
"""Return a blueprint setup state instance.
Arguments:
first_registration: True if this is the first registration
of this blueprint on the app.
url_prefix: An optional prefix to all rules
"""
return BlueprintSetupState(self, app, first_registration, url_prefix=url_prefix)
class BlueprintSetupState:
"""This setups the blueprint on the app.
When used it can apply the deferred functions on the Blueprint to
the app. Override if you wish for blueprints to have be registered
in different ways.
Attributes:
first_registration: True if this is the first registration
of this blueprint on the app.
"""
def __init__(
self,
blueprint: Blueprint,
app: 'Quart',
first_registration: bool,
*,
url_prefix: Optional[str]=None,
) -> None:
self.blueprint = blueprint
self.app = app
self.url_prefix = url_prefix or self.blueprint.url_prefix
self.first_registration = first_registration
def add_url_rule(
self,
path: str,
endpoint: Optional[str]=None,
view_func: Optional[Callable]=None,
methods: Optional[Iterable[str]]=None,
defaults: Optional[dict]=None,
host: Optional[str]=None,
subdomain: Optional[str]=None,
*,
provide_automatic_options: Optional[bool]=None,
is_websocket: bool=False,
strict_slashes: bool=True,
) -> None:
if self.url_prefix is not None:
path = f"{self.url_prefix}{path}"
endpoint = f"{self.blueprint.name}.{endpoint}"
self.app.add_url_rule(
path, endpoint, view_func, methods, defaults, host=host, subdomain=subdomain,
provide_automatic_options=provide_automatic_options, is_websocket=is_websocket,
strict_slashes=strict_slashes,
)
def register_endpoint(self, endpoint: str, func: Callable) -> None:
self.app.view_functions[endpoint] = func
def register_template_filter(self, func: Callable, name: Optional[str]) -> None:
self.app.add_template_filter(func, name)
def register_template_test(self, func: Callable, name: Optional[str]) -> None:
self.app.add_template_test(func, name)
def register_template_global(self, func: Callable, name: Optional[str]) -> None:
self.app.add_template_global(func, name)