mirror of https://github.com/pallets/flask.git
				
				
				
			extract common Flask/Blueprint API to Scaffold base class
Co-authored-by: Chris Nguyen <chrisngyn99@gmail.com>
This commit is contained in:
		
							parent
							
								
									216d97c21a
								
							
						
					
					
						commit
						b146a13f63
					
				|  | @ -37,6 +37,11 @@ Unreleased | |||
|     For example, this allows setting the ``Content-Type`` for | ||||
|     ``jsonify()``. Use ``response.headers.extend()`` if extending is | ||||
|     desired. :issue:`3628` | ||||
| -   The ``Scaffold`` class provides a common API for the ``Flask`` and | ||||
|     ``Blueprint`` classes. ``Blueprint`` information is stored in | ||||
|     attributes just like ``Flask``, rather than opaque lambda functions. | ||||
|     This is intended to improve consistency and maintainability. | ||||
|     :issue:`3215` | ||||
| 
 | ||||
| 
 | ||||
| Version 1.1.x | ||||
|  |  | |||
							
								
								
									
										367
									
								
								src/flask/app.py
								
								
								
								
							
							
						
						
									
										367
									
								
								src/flask/app.py
								
								
								
								
							|  | @ -1,7 +1,6 @@ | |||
| import os | ||||
| import sys | ||||
| from datetime import timedelta | ||||
| from functools import update_wrapper | ||||
| from itertools import chain | ||||
| from threading import Lock | ||||
| 
 | ||||
|  | @ -9,7 +8,6 @@ from werkzeug.datastructures import Headers | |||
| from werkzeug.datastructures import ImmutableDict | ||||
| from werkzeug.exceptions import BadRequest | ||||
| from werkzeug.exceptions import BadRequestKeyError | ||||
| from werkzeug.exceptions import default_exceptions | ||||
| from werkzeug.exceptions import HTTPException | ||||
| from werkzeug.exceptions import InternalServerError | ||||
| from werkzeug.routing import BuildError | ||||
|  | @ -30,8 +28,6 @@ from .globals import _request_ctx_stack | |||
| from .globals import g | ||||
| from .globals import request | ||||
| from .globals import session | ||||
| from .helpers import _endpoint_from_view_func | ||||
| from .helpers import _PackageBoundObject | ||||
| from .helpers import find_package | ||||
| from .helpers import get_debug_flag | ||||
| from .helpers import get_env | ||||
|  | @ -41,21 +37,21 @@ from .helpers import locked_cached_property | |||
| from .helpers import url_for | ||||
| from .json import jsonify | ||||
| from .logging import create_logger | ||||
| from .scaffold import _endpoint_from_view_func | ||||
| from .scaffold import _sentinel | ||||
| from .scaffold import Scaffold | ||||
| from .scaffold import setupmethod | ||||
| from .sessions import SecureCookieSessionInterface | ||||
| from .signals import appcontext_tearing_down | ||||
| from .signals import got_request_exception | ||||
| from .signals import request_finished | ||||
| from .signals import request_started | ||||
| from .signals import request_tearing_down | ||||
| from .templating import _default_template_ctx_processor | ||||
| from .templating import DispatchingJinjaLoader | ||||
| from .templating import Environment | ||||
| from .wrappers import Request | ||||
| from .wrappers import Response | ||||
| 
 | ||||
| # a singleton sentinel value for parameter defaults | ||||
| _sentinel = object() | ||||
| 
 | ||||
| 
 | ||||
| def _make_timedelta(value): | ||||
|     if not isinstance(value, timedelta): | ||||
|  | @ -63,28 +59,7 @@ def _make_timedelta(value): | |||
|     return value | ||||
| 
 | ||||
| 
 | ||||
| def setupmethod(f): | ||||
|     """Wraps a method so that it performs a check in debug mode if the | ||||
|     first request was already handled. | ||||
|     """ | ||||
| 
 | ||||
|     def wrapper_func(self, *args, **kwargs): | ||||
|         if self.debug and self._got_first_request: | ||||
|             raise AssertionError( | ||||
|                 "A setup function was called after the " | ||||
|                 "first request was handled.  This usually indicates a bug " | ||||
|                 "in the application where a module was not imported " | ||||
|                 "and decorators or other functionality was called too late.\n" | ||||
|                 "To fix this make sure to import all your view modules, " | ||||
|                 "database models and everything related at a central place " | ||||
|                 "before the application starts serving requests." | ||||
|             ) | ||||
|         return f(self, *args, **kwargs) | ||||
| 
 | ||||
|     return update_wrapper(wrapper_func, f) | ||||
| 
 | ||||
| 
 | ||||
| class Flask(_PackageBoundObject): | ||||
| class Flask(Scaffold): | ||||
|     """The flask object implements a WSGI application and acts as the central | ||||
|     object.  It is passed the name of the module or package of the | ||||
|     application.  Once it is created it will act as a central registry for | ||||
|  | @ -394,13 +369,14 @@ class Flask(_PackageBoundObject): | |||
|         instance_relative_config=False, | ||||
|         root_path=None, | ||||
|     ): | ||||
|         _PackageBoundObject.__init__( | ||||
|             self, import_name, template_folder=template_folder, root_path=root_path | ||||
|         super().__init__( | ||||
|             import_name=import_name, | ||||
|             static_folder=static_folder, | ||||
|             static_url_path=static_url_path, | ||||
|             template_folder=template_folder, | ||||
|             root_path=root_path, | ||||
|         ) | ||||
| 
 | ||||
|         self.static_url_path = static_url_path | ||||
|         self.static_folder = static_folder | ||||
| 
 | ||||
|         if instance_path is None: | ||||
|             instance_path = self.auto_find_instance_path() | ||||
|         elif not os.path.isabs(instance_path): | ||||
|  | @ -419,24 +395,6 @@ class Flask(_PackageBoundObject): | |||
|         #: to load a config from files. | ||||
|         self.config = self.make_config(instance_relative_config) | ||||
| 
 | ||||
|         #: A dictionary of all view functions registered.  The keys will | ||||
|         #: be function names which are also used to generate URLs and | ||||
|         #: the values are the function objects themselves. | ||||
|         #: To register a view function, use the :meth:`route` decorator. | ||||
|         self.view_functions = {} | ||||
| 
 | ||||
|         #: A dictionary of all registered error handlers.  The key is ``None`` | ||||
|         #: for error handlers active on the application, otherwise the key is | ||||
|         #: the name of the blueprint.  Each key points to another dictionary | ||||
|         #: where the key is the status code of the http exception.  The | ||||
|         #: special key ``None`` points to a list of tuples where the first item | ||||
|         #: is the class for the instance check and the second the error handler | ||||
|         #: function. | ||||
|         #: | ||||
|         #: To register an error handler, use the :meth:`errorhandler` | ||||
|         #: decorator. | ||||
|         self.error_handler_spec = {} | ||||
| 
 | ||||
|         #: A list of functions that are called when :meth:`url_for` raises a | ||||
|         #: :exc:`~werkzeug.routing.BuildError`.  Each function registered here | ||||
|         #: is called with `error`, `endpoint` and `values`.  If a function | ||||
|  | @ -446,13 +404,6 @@ class Flask(_PackageBoundObject): | |||
|         #: .. versionadded:: 0.9 | ||||
|         self.url_build_error_handlers = [] | ||||
| 
 | ||||
|         #: A dictionary with lists of functions that will be called at the | ||||
|         #: beginning of each request. The key of the dictionary is the name of | ||||
|         #: the blueprint this function is active for, or ``None`` for all | ||||
|         #: requests. To register a function, use the :meth:`before_request` | ||||
|         #: decorator. | ||||
|         self.before_request_funcs = {} | ||||
| 
 | ||||
|         #: A list of functions that will be called at the beginning of the | ||||
|         #: first request to this instance. To register a function, use the | ||||
|         #: :meth:`before_first_request` decorator. | ||||
|  | @ -460,25 +411,6 @@ class Flask(_PackageBoundObject): | |||
|         #: .. versionadded:: 0.8 | ||||
|         self.before_first_request_funcs = [] | ||||
| 
 | ||||
|         #: A dictionary with lists of functions that should be called after | ||||
|         #: each request.  The key of the dictionary is the name of the blueprint | ||||
|         #: this function is active for, ``None`` for all requests.  This can for | ||||
|         #: example be used to close database connections. To register a function | ||||
|         #: here, use the :meth:`after_request` decorator. | ||||
|         self.after_request_funcs = {} | ||||
| 
 | ||||
|         #: A dictionary with lists of functions that are called after | ||||
|         #: each request, even if an exception has occurred. The key of the | ||||
|         #: dictionary is the name of the blueprint this function is active for, | ||||
|         #: ``None`` for all requests. These functions are not allowed to modify | ||||
|         #: the request, and their return values are ignored. If an exception | ||||
|         #: occurred while processing the request, it gets passed to each | ||||
|         #: teardown_request function. To register a function here, use the | ||||
|         #: :meth:`teardown_request` decorator. | ||||
|         #: | ||||
|         #: .. versionadded:: 0.7 | ||||
|         self.teardown_request_funcs = {} | ||||
| 
 | ||||
|         #: A list of functions that are called when the application context | ||||
|         #: is destroyed.  Since the application context is also torn down | ||||
|         #: if the request ends this is the place to store code that disconnects | ||||
|  | @ -487,35 +419,6 @@ class Flask(_PackageBoundObject): | |||
|         #: .. versionadded:: 0.9 | ||||
|         self.teardown_appcontext_funcs = [] | ||||
| 
 | ||||
|         #: A dictionary with lists of functions that are called before the | ||||
|         #: :attr:`before_request_funcs` functions. The key of the dictionary is | ||||
|         #: the name of the blueprint this function is active for, or ``None`` | ||||
|         #: for all requests. To register a function, use | ||||
|         #: :meth:`url_value_preprocessor`. | ||||
|         #: | ||||
|         #: .. versionadded:: 0.7 | ||||
|         self.url_value_preprocessors = {} | ||||
| 
 | ||||
|         #: A dictionary with lists of functions that can be used as URL value | ||||
|         #: preprocessors.  The key ``None`` here is used for application wide | ||||
|         #: callbacks, otherwise the key is the name of the blueprint. | ||||
|         #: Each of these functions has the chance to modify the dictionary | ||||
|         #: of URL values before they are used as the keyword arguments of the | ||||
|         #: view function.  For each function registered this one should also | ||||
|         #: provide a :meth:`url_defaults` function that adds the parameters | ||||
|         #: automatically again that were removed that way. | ||||
|         #: | ||||
|         #: .. versionadded:: 0.7 | ||||
|         self.url_default_functions = {} | ||||
| 
 | ||||
|         #: A dictionary with list of functions that are called without argument | ||||
|         #: to populate the template context.  The key of the dictionary is the | ||||
|         #: name of the blueprint this function is active for, ``None`` for all | ||||
|         #: requests.  Each returns a dictionary that the template context is | ||||
|         #: updated with.  To register a function here, use the | ||||
|         #: :meth:`context_processor` decorator. | ||||
|         self.template_context_processors = {None: [_default_template_ctx_processor]} | ||||
| 
 | ||||
|         #: A list of shell context processor functions that should be run | ||||
|         #: when a shell context is created. | ||||
|         #: | ||||
|  | @ -586,6 +489,9 @@ class Flask(_PackageBoundObject): | |||
|         # the app's commands to another CLI tool. | ||||
|         self.cli.name = self.name | ||||
| 
 | ||||
|     def _is_setup_finished(self): | ||||
|         return self.debug and self._got_first_request | ||||
| 
 | ||||
|     @locked_cached_property | ||||
|     def name(self): | ||||
|         """The name of the application.  This is usually the import name | ||||
|  | @ -1206,152 +1112,6 @@ class Flask(_PackageBoundObject): | |||
|                 ) | ||||
|             self.view_functions[endpoint] = view_func | ||||
| 
 | ||||
|     def route(self, rule, **options): | ||||
|         """A decorator that is used to register a view function for a | ||||
|         given URL rule.  This does the same thing as :meth:`add_url_rule` | ||||
|         but is intended for decorator usage:: | ||||
| 
 | ||||
|             @app.route('/') | ||||
|             def index(): | ||||
|                 return 'Hello World' | ||||
| 
 | ||||
|         For more information refer to :ref:`url-route-registrations`. | ||||
| 
 | ||||
|         :param rule: the URL rule as string | ||||
|         :param endpoint: the endpoint for the registered URL rule.  Flask | ||||
|                          itself assumes the name of the view function as | ||||
|                          endpoint | ||||
|         :param options: the options to be forwarded to the underlying | ||||
|                         :class:`~werkzeug.routing.Rule` object.  A change | ||||
|                         to Werkzeug is handling of method options.  methods | ||||
|                         is a list of methods this rule should be limited | ||||
|                         to (``GET``, ``POST`` etc.).  By default a rule | ||||
|                         just listens for ``GET`` (and implicitly ``HEAD``). | ||||
|                         Starting with Flask 0.6, ``OPTIONS`` is implicitly | ||||
|                         added and handled by the standard request handling. | ||||
|         """ | ||||
| 
 | ||||
|         def decorator(f): | ||||
|             endpoint = options.pop("endpoint", None) | ||||
|             self.add_url_rule(rule, endpoint, f, **options) | ||||
|             return f | ||||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def endpoint(self, endpoint): | ||||
|         """A decorator to register a function as an endpoint. | ||||
|         Example:: | ||||
| 
 | ||||
|             @app.endpoint('example.endpoint') | ||||
|             def example(): | ||||
|                 return "example" | ||||
| 
 | ||||
|         :param endpoint: the name of the endpoint | ||||
|         """ | ||||
| 
 | ||||
|         def decorator(f): | ||||
|             self.view_functions[endpoint] = f | ||||
|             return f | ||||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def _get_exc_class_and_code(exc_class_or_code): | ||||
|         """Get the exception class being handled. For HTTP status codes | ||||
|         or ``HTTPException`` subclasses, return both the exception and | ||||
|         status code. | ||||
| 
 | ||||
|         :param exc_class_or_code: Any exception class, or an HTTP status | ||||
|             code as an integer. | ||||
|         """ | ||||
|         if isinstance(exc_class_or_code, int): | ||||
|             exc_class = default_exceptions[exc_class_or_code] | ||||
|         else: | ||||
|             exc_class = exc_class_or_code | ||||
| 
 | ||||
|         assert issubclass( | ||||
|             exc_class, Exception | ||||
|         ), "Custom exceptions must be subclasses of Exception." | ||||
| 
 | ||||
|         if issubclass(exc_class, HTTPException): | ||||
|             return exc_class, exc_class.code | ||||
|         else: | ||||
|             return exc_class, None | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def errorhandler(self, code_or_exception): | ||||
|         """Register a function to handle errors by code or exception class. | ||||
| 
 | ||||
|         A decorator that is used to register a function given an | ||||
|         error code.  Example:: | ||||
| 
 | ||||
|             @app.errorhandler(404) | ||||
|             def page_not_found(error): | ||||
|                 return 'This page does not exist', 404 | ||||
| 
 | ||||
|         You can also register handlers for arbitrary exceptions:: | ||||
| 
 | ||||
|             @app.errorhandler(DatabaseError) | ||||
|             def special_exception_handler(error): | ||||
|                 return 'Database connection failed', 500 | ||||
| 
 | ||||
|         .. versionadded:: 0.7 | ||||
|             Use :meth:`register_error_handler` instead of modifying | ||||
|             :attr:`error_handler_spec` directly, for application wide error | ||||
|             handlers. | ||||
| 
 | ||||
|         .. versionadded:: 0.7 | ||||
|            One can now additionally also register custom exception types | ||||
|            that do not necessarily have to be a subclass of the | ||||
|            :class:`~werkzeug.exceptions.HTTPException` class. | ||||
| 
 | ||||
|         :param code_or_exception: the code as integer for the handler, or | ||||
|                                   an arbitrary exception | ||||
|         """ | ||||
| 
 | ||||
|         def decorator(f): | ||||
|             self._register_error_handler(None, code_or_exception, f) | ||||
|             return f | ||||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def register_error_handler(self, code_or_exception, f): | ||||
|         """Alternative error attach function to the :meth:`errorhandler` | ||||
|         decorator that is more straightforward to use for non decorator | ||||
|         usage. | ||||
| 
 | ||||
|         .. versionadded:: 0.7 | ||||
|         """ | ||||
|         self._register_error_handler(None, code_or_exception, f) | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def _register_error_handler(self, key, code_or_exception, f): | ||||
|         """ | ||||
|         :type key: None|str | ||||
|         :type code_or_exception: int|T<=Exception | ||||
|         :type f: callable | ||||
|         """ | ||||
|         if isinstance(code_or_exception, HTTPException):  # old broken behavior | ||||
|             raise ValueError( | ||||
|                 "Tried to register a handler for an exception instance" | ||||
|                 f" {code_or_exception!r}. Handlers can only be" | ||||
|                 " registered for exception classes or HTTP error codes." | ||||
|             ) | ||||
| 
 | ||||
|         try: | ||||
|             exc_class, code = self._get_exc_class_and_code(code_or_exception) | ||||
|         except KeyError: | ||||
|             raise KeyError( | ||||
|                 f"'{code_or_exception}' is not a recognized HTTP error" | ||||
|                 " code. Use a subclass of HTTPException with that code" | ||||
|                 " instead." | ||||
|             ) | ||||
| 
 | ||||
|         handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {}) | ||||
|         handlers[exc_class] = f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def template_filter(self, name=None): | ||||
|         """A decorator that is used to register custom template filter. | ||||
|  | @ -1455,20 +1215,6 @@ class Flask(_PackageBoundObject): | |||
|         """ | ||||
|         self.jinja_env.globals[name or f.__name__] = f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def before_request(self, f): | ||||
|         """Registers a function to run before each request. | ||||
| 
 | ||||
|         For example, this can be used to open a database connection, or to load | ||||
|         the logged in user from the session. | ||||
| 
 | ||||
|         The function will be called without any arguments. If it returns a | ||||
|         non-None value, the value is handled as if it was the return value from | ||||
|         the view, and further request handling is stopped. | ||||
|         """ | ||||
|         self.before_request_funcs.setdefault(None, []).append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def before_first_request(self, f): | ||||
|         """Registers a function to be run before the first request to this | ||||
|  | @ -1482,59 +1228,6 @@ class Flask(_PackageBoundObject): | |||
|         self.before_first_request_funcs.append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def after_request(self, f): | ||||
|         """Register a function to be run after each request. | ||||
| 
 | ||||
|         Your function must take one parameter, an instance of | ||||
|         :attr:`response_class` and return a new response object or the | ||||
|         same (see :meth:`process_response`). | ||||
| 
 | ||||
|         As of Flask 0.7 this function might not be executed at the end of the | ||||
|         request in case an unhandled exception occurred. | ||||
|         """ | ||||
|         self.after_request_funcs.setdefault(None, []).append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def teardown_request(self, f): | ||||
|         """Register a function to be run at the end of each request, | ||||
|         regardless of whether there was an exception or not.  These functions | ||||
|         are executed when the request context is popped, even if not an | ||||
|         actual request was performed. | ||||
| 
 | ||||
|         Example:: | ||||
| 
 | ||||
|             ctx = app.test_request_context() | ||||
|             ctx.push() | ||||
|             ... | ||||
|             ctx.pop() | ||||
| 
 | ||||
|         When ``ctx.pop()`` is executed in the above example, the teardown | ||||
|         functions are called just before the request context moves from the | ||||
|         stack of active contexts.  This becomes relevant if you are using | ||||
|         such constructs in tests. | ||||
| 
 | ||||
|         Generally teardown functions must take every necessary step to avoid | ||||
|         that they will fail.  If they do execute code that might fail they | ||||
|         will have to surround the execution of these code by try/except | ||||
|         statements and log occurring errors. | ||||
| 
 | ||||
|         When a teardown function was called because of an exception it will | ||||
|         be passed an error object. | ||||
| 
 | ||||
|         The return values of teardown functions are ignored. | ||||
| 
 | ||||
|         .. admonition:: Debug Note | ||||
| 
 | ||||
|            In debug mode Flask will not tear down a request on an exception | ||||
|            immediately.  Instead it will keep it alive so that the interactive | ||||
|            debugger can still access it.  This behavior can be controlled | ||||
|            by the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. | ||||
|         """ | ||||
|         self.teardown_request_funcs.setdefault(None, []).append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def teardown_appcontext(self, f): | ||||
|         """Registers a function to be called when the application context | ||||
|  | @ -1568,12 +1261,6 @@ class Flask(_PackageBoundObject): | |||
|         self.teardown_appcontext_funcs.append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def context_processor(self, f): | ||||
|         """Registers a template context processor function.""" | ||||
|         self.template_context_processors[None].append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def shell_context_processor(self, f): | ||||
|         """Registers a shell context processor function. | ||||
|  | @ -1583,32 +1270,6 @@ class Flask(_PackageBoundObject): | |||
|         self.shell_context_processors.append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def url_value_preprocessor(self, f): | ||||
|         """Register a URL value preprocessor function for all view | ||||
|         functions in the application. These functions will be called before the | ||||
|         :meth:`before_request` functions. | ||||
| 
 | ||||
|         The function can modify the values captured from the matched url before | ||||
|         they are passed to the view. For example, this can be used to pop a | ||||
|         common language code value and place it in ``g`` rather than pass it to | ||||
|         every view. | ||||
| 
 | ||||
|         The function is passed the endpoint name and values dict. The return | ||||
|         value is ignored. | ||||
|         """ | ||||
|         self.url_value_preprocessors.setdefault(None, []).append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def url_defaults(self, f): | ||||
|         """Callback function for URL defaults for all view functions of the | ||||
|         application.  It's called with the endpoint and values and should | ||||
|         update the values passed in place. | ||||
|         """ | ||||
|         self.url_default_functions.setdefault(None, []).append(f) | ||||
|         return f | ||||
| 
 | ||||
|     def _find_error_handler(self, e): | ||||
|         """Return a registered error handler for an exception in this order: | ||||
|         blueprint handler for a specific code, app handler for a specific code, | ||||
|  |  | |||
|  | @ -1,10 +1,8 @@ | |||
| from functools import update_wrapper | ||||
| 
 | ||||
| from .helpers import _endpoint_from_view_func | ||||
| from .helpers import _PackageBoundObject | ||||
| 
 | ||||
| # a singleton sentinel value for parameter defaults | ||||
| _sentinel = object() | ||||
| from .scaffold import _endpoint_from_view_func | ||||
| from .scaffold import _sentinel | ||||
| from .scaffold import Scaffold | ||||
| 
 | ||||
| 
 | ||||
| class BlueprintSetupState: | ||||
|  | @ -76,7 +74,7 @@ class BlueprintSetupState: | |||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| class Blueprint(_PackageBoundObject): | ||||
| class Blueprint(Scaffold): | ||||
|     """Represents a blueprint, a collection of routes and other | ||||
|     app-related functions that can be registered on a real application | ||||
|     later. | ||||
|  | @ -167,20 +165,25 @@ class Blueprint(_PackageBoundObject): | |||
|         root_path=None, | ||||
|         cli_group=_sentinel, | ||||
|     ): | ||||
|         _PackageBoundObject.__init__( | ||||
|             self, import_name, template_folder, root_path=root_path | ||||
|         super().__init__( | ||||
|             import_name=import_name, | ||||
|             static_folder=static_folder, | ||||
|             static_url_path=static_url_path, | ||||
|             template_folder=template_folder, | ||||
|             root_path=root_path, | ||||
|         ) | ||||
|         self.name = name | ||||
|         self.url_prefix = url_prefix | ||||
|         self.subdomain = subdomain | ||||
|         self.static_folder = static_folder | ||||
|         self.static_url_path = static_url_path | ||||
|         self.deferred_functions = [] | ||||
|         if url_defaults is None: | ||||
|             url_defaults = {} | ||||
|         self.url_values_defaults = url_defaults | ||||
|         self.cli_group = cli_group | ||||
| 
 | ||||
|     def _is_setup_finished(self): | ||||
|         return self.warn_on_modifications and self._got_registered_once | ||||
| 
 | ||||
|     def record(self, func): | ||||
|         """Registers a function that is called when the blueprint is | ||||
|         registered on the application.  This function is called with the | ||||
|  | @ -241,6 +244,36 @@ class Blueprint(_PackageBoundObject): | |||
|                 endpoint="static", | ||||
|             ) | ||||
| 
 | ||||
|         # Merge app and self dictionaries. | ||||
|         def merge_dict_lists(self_dict, app_dict): | ||||
|             """Merges self_dict into app_dict. Replaces None keys with self.name. | ||||
|             Values of dict must be lists. | ||||
|             """ | ||||
|             for key, values in self_dict.items(): | ||||
|                 key = self.name if key is None else f"{self.name}.{key}" | ||||
|                 app_dict.setdefault(key, []).extend(values) | ||||
| 
 | ||||
|         def merge_dict_nested(self_dict, app_dict): | ||||
|             """Merges self_dict into app_dict. Replaces None keys with self.name. | ||||
|             Values of dict must be dict. | ||||
|             """ | ||||
|             for key, value in self_dict.items(): | ||||
|                 key = self.name if key is None else f"{self.name}.{key}" | ||||
|                 app_dict[key] = value | ||||
| 
 | ||||
|         app.view_functions.update(self.view_functions) | ||||
| 
 | ||||
|         merge_dict_lists(self.before_request_funcs, app.before_request_funcs) | ||||
|         merge_dict_lists(self.after_request_funcs, app.after_request_funcs) | ||||
|         merge_dict_lists(self.teardown_request_funcs, app.teardown_request_funcs) | ||||
|         merge_dict_lists(self.url_default_functions, app.url_default_functions) | ||||
|         merge_dict_lists(self.url_value_preprocessors, app.url_value_preprocessors) | ||||
|         merge_dict_lists( | ||||
|             self.template_context_processors, app.template_context_processors | ||||
|         ) | ||||
| 
 | ||||
|         merge_dict_nested(self.error_handler_spec, app.error_handler_spec) | ||||
| 
 | ||||
|         for deferred in self.deferred_functions: | ||||
|             deferred(state) | ||||
| 
 | ||||
|  | @ -258,18 +291,6 @@ class Blueprint(_PackageBoundObject): | |||
|             self.cli.name = cli_resolved_group | ||||
|             app.cli.add_command(self.cli) | ||||
| 
 | ||||
|     def route(self, rule, **options): | ||||
|         """Like :meth:`Flask.route` but for a blueprint.  The endpoint for the | ||||
|         :func:`url_for` function is prefixed with the name of the blueprint. | ||||
|         """ | ||||
| 
 | ||||
|         def decorator(f): | ||||
|             endpoint = options.pop("endpoint", f.__name__) | ||||
|             self.add_url_rule(rule, endpoint, f, **options) | ||||
|             return f | ||||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     def add_url_rule(self, rule, endpoint=None, view_func=None, **options): | ||||
|         """Like :meth:`Flask.add_url_rule` but for a blueprint.  The endpoint for | ||||
|         the :func:`url_for` function is prefixed with the name of the blueprint. | ||||
|  | @ -282,23 +303,6 @@ class Blueprint(_PackageBoundObject): | |||
|             ), "Blueprint view function name should not contain dots" | ||||
|         self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options)) | ||||
| 
 | ||||
|     def endpoint(self, endpoint): | ||||
|         """Like :meth:`Flask.endpoint` but for a blueprint.  This does not | ||||
|         prefix the endpoint with the blueprint name, this has to be done | ||||
|         explicitly by the user of this method.  If the endpoint is prefixed | ||||
|         with a `.` it will be registered to the current blueprint, otherwise | ||||
|         it's an application independent endpoint. | ||||
|         """ | ||||
| 
 | ||||
|         def decorator(f): | ||||
|             def register_endpoint(state): | ||||
|                 state.app.view_functions[endpoint] = f | ||||
| 
 | ||||
|             self.record_once(register_endpoint) | ||||
|             return f | ||||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     def app_template_filter(self, name=None): | ||||
|         """Register a custom template filter, available application wide.  Like | ||||
|         :meth:`Flask.template_filter` but for a blueprint. | ||||
|  | @ -391,16 +395,6 @@ class Blueprint(_PackageBoundObject): | |||
| 
 | ||||
|         self.record_once(register_template) | ||||
| 
 | ||||
|     def before_request(self, f): | ||||
|         """Like :meth:`Flask.before_request` but for a blueprint.  This function | ||||
|         is only executed before each request that is handled by a function of | ||||
|         that blueprint. | ||||
|         """ | ||||
|         self.record_once( | ||||
|             lambda s: s.app.before_request_funcs.setdefault(self.name, []).append(f) | ||||
|         ) | ||||
|         return f | ||||
| 
 | ||||
|     def before_app_request(self, f): | ||||
|         """Like :meth:`Flask.before_request`.  Such a function is executed | ||||
|         before each request, even if outside of a blueprint. | ||||
|  | @ -417,16 +411,6 @@ class Blueprint(_PackageBoundObject): | |||
|         self.record_once(lambda s: s.app.before_first_request_funcs.append(f)) | ||||
|         return f | ||||
| 
 | ||||
|     def after_request(self, f): | ||||
|         """Like :meth:`Flask.after_request` but for a blueprint.  This function | ||||
|         is only executed after each request that is handled by a function of | ||||
|         that blueprint. | ||||
|         """ | ||||
|         self.record_once( | ||||
|             lambda s: s.app.after_request_funcs.setdefault(self.name, []).append(f) | ||||
|         ) | ||||
|         return f | ||||
| 
 | ||||
|     def after_app_request(self, f): | ||||
|         """Like :meth:`Flask.after_request` but for a blueprint.  Such a function | ||||
|         is executed after each request, even if outside of the blueprint. | ||||
|  | @ -436,18 +420,6 @@ class Blueprint(_PackageBoundObject): | |||
|         ) | ||||
|         return f | ||||
| 
 | ||||
|     def teardown_request(self, f): | ||||
|         """Like :meth:`Flask.teardown_request` but for a blueprint.  This | ||||
|         function is only executed when tearing down requests handled by a | ||||
|         function of that blueprint.  Teardown request functions are executed | ||||
|         when the request context is popped, even when no actual request was | ||||
|         performed. | ||||
|         """ | ||||
|         self.record_once( | ||||
|             lambda s: s.app.teardown_request_funcs.setdefault(self.name, []).append(f) | ||||
|         ) | ||||
|         return f | ||||
| 
 | ||||
|     def teardown_app_request(self, f): | ||||
|         """Like :meth:`Flask.teardown_request` but for a blueprint.  Such a | ||||
|         function is executed when tearing down each request, even if outside of | ||||
|  | @ -458,17 +430,6 @@ class Blueprint(_PackageBoundObject): | |||
|         ) | ||||
|         return f | ||||
| 
 | ||||
|     def context_processor(self, f): | ||||
|         """Like :meth:`Flask.context_processor` but for a blueprint.  This | ||||
|         function is only executed for requests handled by a blueprint. | ||||
|         """ | ||||
|         self.record_once( | ||||
|             lambda s: s.app.template_context_processors.setdefault( | ||||
|                 self.name, [] | ||||
|             ).append(f) | ||||
|         ) | ||||
|         return f | ||||
| 
 | ||||
|     def app_context_processor(self, f): | ||||
|         """Like :meth:`Flask.context_processor` but for a blueprint.  Such a | ||||
|         function is executed each request, even if outside of the blueprint. | ||||
|  | @ -489,26 +450,6 @@ class Blueprint(_PackageBoundObject): | |||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     def url_value_preprocessor(self, f): | ||||
|         """Registers a function as URL value preprocessor for this | ||||
|         blueprint.  It's called before the view functions are called and | ||||
|         can modify the url values provided. | ||||
|         """ | ||||
|         self.record_once( | ||||
|             lambda s: s.app.url_value_preprocessors.setdefault(self.name, []).append(f) | ||||
|         ) | ||||
|         return f | ||||
| 
 | ||||
|     def url_defaults(self, f): | ||||
|         """Callback function for URL defaults for this blueprint.  It's called | ||||
|         with the endpoint and values and should update the values passed | ||||
|         in place. | ||||
|         """ | ||||
|         self.record_once( | ||||
|             lambda s: s.app.url_default_functions.setdefault(self.name, []).append(f) | ||||
|         ) | ||||
|         return f | ||||
| 
 | ||||
|     def app_url_value_preprocessor(self, f): | ||||
|         """Same as :meth:`url_value_preprocessor` but application wide. | ||||
|         """ | ||||
|  | @ -524,35 +465,3 @@ class Blueprint(_PackageBoundObject): | |||
|             lambda s: s.app.url_default_functions.setdefault(None, []).append(f) | ||||
|         ) | ||||
|         return f | ||||
| 
 | ||||
|     def errorhandler(self, code_or_exception): | ||||
|         """Registers an error handler that becomes active for this blueprint | ||||
|         only.  Please be aware that routing does not happen local to a | ||||
|         blueprint so an error handler for 404 usually is not handled by | ||||
|         a blueprint unless it is caused inside a view function.  Another | ||||
|         special case is the 500 internal server error which is always looked | ||||
|         up from the application. | ||||
| 
 | ||||
|         Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator | ||||
|         of the :class:`~flask.Flask` object. | ||||
|         """ | ||||
| 
 | ||||
|         def decorator(f): | ||||
|             self.record_once( | ||||
|                 lambda s: s.app._register_error_handler(self.name, code_or_exception, f) | ||||
|             ) | ||||
|             return f | ||||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     def register_error_handler(self, code_or_exception, f): | ||||
|         """Non-decorator version of the :meth:`errorhandler` error attach | ||||
|         function, akin to the :meth:`~flask.Flask.register_error_handler` | ||||
|         application-wide function of the :class:`~flask.Flask` object but | ||||
|         for error handlers limited to this blueprint. | ||||
| 
 | ||||
|         .. versionadded:: 0.11 | ||||
|         """ | ||||
|         self.record_once( | ||||
|             lambda s: s.app._register_error_handler(self.name, code_or_exception, f) | ||||
|         ) | ||||
|  |  | |||
|  | @ -76,14 +76,6 @@ def get_load_dotenv(default=True): | |||
|     return val.lower() in ("0", "false", "no") | ||||
| 
 | ||||
| 
 | ||||
| def _endpoint_from_view_func(view_func): | ||||
|     """Internal helper that returns the default endpoint for a given | ||||
|     function.  This always is the function name. | ||||
|     """ | ||||
|     assert view_func is not None, "expected view func if endpoint is not provided." | ||||
|     return view_func.__name__ | ||||
| 
 | ||||
| 
 | ||||
| def stream_with_context(generator_or_function): | ||||
|     """Request contexts disappear when the response is started on the server. | ||||
|     This is done for efficiency reasons and to make it less likely to encounter | ||||
|  |  | |||
|  | @ -0,0 +1,411 @@ | |||
| from functools import update_wrapper | ||||
| 
 | ||||
| from werkzeug.exceptions import default_exceptions | ||||
| from werkzeug.exceptions import HTTPException | ||||
| 
 | ||||
| from .helpers import _PackageBoundObject | ||||
| from .templating import _default_template_ctx_processor | ||||
| 
 | ||||
| # a singleton sentinel value for parameter defaults | ||||
| _sentinel = object() | ||||
| 
 | ||||
| 
 | ||||
| def setupmethod(f): | ||||
|     """Wraps a method so that it performs a check in debug mode if the | ||||
|     first request was already handled. | ||||
|     """ | ||||
| 
 | ||||
|     def wrapper_func(self, *args, **kwargs): | ||||
|         if self._is_setup_finished(): | ||||
|             raise AssertionError( | ||||
|                 "A setup function was called after the " | ||||
|                 "first request was handled.  This usually indicates a bug " | ||||
|                 "in the application where a module was not imported " | ||||
|                 "and decorators or other functionality was called too late.\n" | ||||
|                 "To fix this make sure to import all your view modules, " | ||||
|                 "database models and everything related at a central place " | ||||
|                 "before the application starts serving requests." | ||||
|             ) | ||||
|         return f(self, *args, **kwargs) | ||||
| 
 | ||||
|     return update_wrapper(wrapper_func, f) | ||||
| 
 | ||||
| 
 | ||||
| class Scaffold(_PackageBoundObject): | ||||
|     """A common base for class Flask and class Blueprint. | ||||
|     """ | ||||
| 
 | ||||
|     #: Skeleton local JSON decoder class to use. | ||||
|     #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_encoder`. | ||||
|     json_encoder = None | ||||
| 
 | ||||
|     #: Skeleton local JSON decoder class to use. | ||||
|     #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_decoder`. | ||||
|     json_decoder = None | ||||
| 
 | ||||
|     #: The name of the package or module that this app belongs to. Do not | ||||
|     #: change this once it is set by the constructor. | ||||
|     import_name = None | ||||
| 
 | ||||
|     #: Location of the template files to be added to the template lookup. | ||||
|     #: ``None`` if templates should not be added. | ||||
|     template_folder = None | ||||
| 
 | ||||
|     #: Absolute path to the package on the filesystem. Used to look up | ||||
|     #: resources contained in the package. | ||||
|     root_path = None | ||||
| 
 | ||||
|     def __init__( | ||||
|         self, | ||||
|         import_name, | ||||
|         static_folder="static", | ||||
|         static_url_path=None, | ||||
|         template_folder=None, | ||||
|         root_path=None, | ||||
|     ): | ||||
|         super().__init__( | ||||
|             import_name=import_name, | ||||
|             template_folder=template_folder, | ||||
|             root_path=root_path, | ||||
|         ) | ||||
|         self.static_folder = static_folder | ||||
|         self.static_url_path = static_url_path | ||||
| 
 | ||||
|         #: A dictionary of all view functions registered.  The keys will | ||||
|         #: be function names which are also used to generate URLs and | ||||
|         #: the values are the function objects themselves. | ||||
|         #: To register a view function, use the :meth:`route` decorator. | ||||
|         self.view_functions = {} | ||||
| 
 | ||||
|         #: A dictionary of all registered error handlers.  The key is ``None`` | ||||
|         #: for error handlers active on the application, otherwise the key is | ||||
|         #: the name of the blueprint.  Each key points to another dictionary | ||||
|         #: where the key is the status code of the http exception.  The | ||||
|         #: special key ``None`` points to a list of tuples where the first item | ||||
|         #: is the class for the instance check and the second the error handler | ||||
|         #: function. | ||||
|         #: | ||||
|         #: To register an error handler, use the :meth:`errorhandler` | ||||
|         #: decorator. | ||||
|         self.error_handler_spec = {} | ||||
| 
 | ||||
|         #: A dictionary with lists of functions that will be called at the | ||||
|         #: beginning of each request. The key of the dictionary is the name of | ||||
|         #: the blueprint this function is active for, or ``None`` for all | ||||
|         #: requests. To register a function, use the :meth:`before_request` | ||||
|         #: decorator. | ||||
|         self.before_request_funcs = {} | ||||
| 
 | ||||
|         #: A dictionary with lists of functions that should be called after | ||||
|         #: each request.  The key of the dictionary is the name of the blueprint | ||||
|         #: this function is active for, ``None`` for all requests.  This can for | ||||
|         #: example be used to close database connections. To register a function | ||||
|         #: here, use the :meth:`after_request` decorator. | ||||
|         self.after_request_funcs = {} | ||||
| 
 | ||||
|         #: A dictionary with lists of functions that are called after | ||||
|         #: each request, even if an exception has occurred. The key of the | ||||
|         #: dictionary is the name of the blueprint this function is active for, | ||||
|         #: ``None`` for all requests. These functions are not allowed to modify | ||||
|         #: the request, and their return values are ignored. If an exception | ||||
|         #: occurred while processing the request, it gets passed to each | ||||
|         #: teardown_request function. To register a function here, use the | ||||
|         #: :meth:`teardown_request` decorator. | ||||
|         #: | ||||
|         #: .. versionadded:: 0.7 | ||||
|         self.teardown_request_funcs = {} | ||||
| 
 | ||||
|         #: A dictionary with list of functions that are called without argument | ||||
|         #: to populate the template context.  The key of the dictionary is the | ||||
|         #: name of the blueprint this function is active for, ``None`` for all | ||||
|         #: requests.  Each returns a dictionary that the template context is | ||||
|         #: updated with.  To register a function here, use the | ||||
|         #: :meth:`context_processor` decorator. | ||||
|         self.template_context_processors = {None: [_default_template_ctx_processor]} | ||||
| 
 | ||||
|         #: A dictionary with lists of functions that are called before the | ||||
|         #: :attr:`before_request_funcs` functions. The key of the dictionary is | ||||
|         #: the name of the blueprint this function is active for, or ``None`` | ||||
|         #: for all requests. To register a function, use | ||||
|         #: :meth:`url_value_preprocessor`. | ||||
|         #: | ||||
|         #: .. versionadded:: 0.7 | ||||
|         self.url_value_preprocessors = {} | ||||
| 
 | ||||
|         #: A dictionary with lists of functions that can be used as URL value | ||||
|         #: preprocessors.  The key ``None`` here is used for application wide | ||||
|         #: callbacks, otherwise the key is the name of the blueprint. | ||||
|         #: Each of these functions has the chance to modify the dictionary | ||||
|         #: of URL values before they are used as the keyword arguments of the | ||||
|         #: view function.  For each function registered this one should also | ||||
|         #: provide a :meth:`url_defaults` function that adds the parameters | ||||
|         #: automatically again that were removed that way. | ||||
|         #: | ||||
|         #: .. versionadded:: 0.7 | ||||
|         self.url_default_functions = {} | ||||
| 
 | ||||
|     def _is_setup_finished(self): | ||||
|         raise NotImplementedError | ||||
| 
 | ||||
|     def route(self, rule, **options): | ||||
|         """A decorator that is used to register a view function for a | ||||
|         given URL rule.  This does the same thing as :meth:`add_url_rule` | ||||
|         but is intended for decorator usage:: | ||||
| 
 | ||||
|             @app.route('/') | ||||
|             def index(): | ||||
|                 return 'Hello World' | ||||
| 
 | ||||
|         For more information refer to :ref:`url-route-registrations`. | ||||
| 
 | ||||
|         :param rule: the URL rule as string | ||||
|         :param endpoint: the endpoint for the registered URL rule.  Flask | ||||
|                          itself assumes the name of the view function as | ||||
|                          endpoint | ||||
|         :param options: the options to be forwarded to the underlying | ||||
|                         :class:`~werkzeug.routing.Rule` object.  A change | ||||
|                         to Werkzeug is handling of method options.  methods | ||||
|                         is a list of methods this rule should be limited | ||||
|                         to (``GET``, ``POST`` etc.).  By default a rule | ||||
|                         just listens for ``GET`` (and implicitly ``HEAD``). | ||||
|                         Starting with Flask 0.6, ``OPTIONS`` is implicitly | ||||
|                         added and handled by the standard request handling. | ||||
|         """ | ||||
| 
 | ||||
|         def decorator(f): | ||||
|             endpoint = options.pop("endpoint", None) | ||||
|             self.add_url_rule(rule, endpoint, f, **options) | ||||
|             return f | ||||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def add_url_rule( | ||||
|         self, | ||||
|         rule, | ||||
|         endpoint=None, | ||||
|         view_func=None, | ||||
|         provide_automatic_options=None, | ||||
|         **options, | ||||
|     ): | ||||
|         raise NotImplementedError | ||||
| 
 | ||||
|     def endpoint(self, endpoint): | ||||
|         """A decorator to register a function as an endpoint. | ||||
|         Example:: | ||||
| 
 | ||||
|             @app.endpoint('example.endpoint') | ||||
|             def example(): | ||||
|                 return "example" | ||||
| 
 | ||||
|         :param endpoint: the name of the endpoint | ||||
|         """ | ||||
| 
 | ||||
|         def decorator(f): | ||||
|             self.view_functions[endpoint] = f | ||||
|             return f | ||||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def before_request(self, f): | ||||
|         """Registers a function to run before each request. | ||||
| 
 | ||||
|         For example, this can be used to open a database connection, or to load | ||||
|         the logged in user from the session. | ||||
| 
 | ||||
|         The function will be called without any arguments. If it returns a | ||||
|         non-None value, the value is handled as if it was the return value from | ||||
|         the view, and further request handling is stopped. | ||||
|         """ | ||||
|         self.before_request_funcs.setdefault(None, []).append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def after_request(self, f): | ||||
|         """Register a function to be run after each request. | ||||
| 
 | ||||
|         Your function must take one parameter, an instance of | ||||
|         :attr:`response_class` and return a new response object or the | ||||
|         same (see :meth:`process_response`). | ||||
| 
 | ||||
|         As of Flask 0.7 this function might not be executed at the end of the | ||||
|         request in case an unhandled exception occurred. | ||||
|         """ | ||||
|         self.after_request_funcs.setdefault(None, []).append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def teardown_request(self, f): | ||||
|         """Register a function to be run at the end of each request, | ||||
|         regardless of whether there was an exception or not.  These functions | ||||
|         are executed when the request context is popped, even if not an | ||||
|         actual request was performed. | ||||
| 
 | ||||
|         Example:: | ||||
| 
 | ||||
|             ctx = app.test_request_context() | ||||
|             ctx.push() | ||||
|             ... | ||||
|             ctx.pop() | ||||
| 
 | ||||
|         When ``ctx.pop()`` is executed in the above example, the teardown | ||||
|         functions are called just before the request context moves from the | ||||
|         stack of active contexts.  This becomes relevant if you are using | ||||
|         such constructs in tests. | ||||
| 
 | ||||
|         Generally teardown functions must take every necessary step to avoid | ||||
|         that they will fail.  If they do execute code that might fail they | ||||
|         will have to surround the execution of these code by try/except | ||||
|         statements and log occurring errors. | ||||
| 
 | ||||
|         When a teardown function was called because of an exception it will | ||||
|         be passed an error object. | ||||
| 
 | ||||
|         The return values of teardown functions are ignored. | ||||
| 
 | ||||
|         .. admonition:: Debug Note | ||||
| 
 | ||||
|            In debug mode Flask will not tear down a request on an exception | ||||
|            immediately.  Instead it will keep it alive so that the interactive | ||||
|            debugger can still access it.  This behavior can be controlled | ||||
|            by the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. | ||||
|         """ | ||||
|         self.teardown_request_funcs.setdefault(None, []).append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def context_processor(self, f): | ||||
|         """Registers a template context processor function.""" | ||||
|         self.template_context_processors[None].append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def url_value_preprocessor(self, f): | ||||
|         """Register a URL value preprocessor function for all view | ||||
|         functions in the application. These functions will be called before the | ||||
|         :meth:`before_request` functions. | ||||
| 
 | ||||
|         The function can modify the values captured from the matched url before | ||||
|         they are passed to the view. For example, this can be used to pop a | ||||
|         common language code value and place it in ``g`` rather than pass it to | ||||
|         every view. | ||||
| 
 | ||||
|         The function is passed the endpoint name and values dict. The return | ||||
|         value is ignored. | ||||
|         """ | ||||
|         self.url_value_preprocessors.setdefault(None, []).append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def url_defaults(self, f): | ||||
|         """Callback function for URL defaults for all view functions of the | ||||
|         application.  It's called with the endpoint and values and should | ||||
|         update the values passed in place. | ||||
|         """ | ||||
|         self.url_default_functions.setdefault(None, []).append(f) | ||||
|         return f | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def errorhandler(self, code_or_exception): | ||||
|         """Register a function to handle errors by code or exception class. | ||||
| 
 | ||||
|         A decorator that is used to register a function given an | ||||
|         error code.  Example:: | ||||
| 
 | ||||
|             @app.errorhandler(404) | ||||
|             def page_not_found(error): | ||||
|                 return 'This page does not exist', 404 | ||||
| 
 | ||||
|         You can also register handlers for arbitrary exceptions:: | ||||
| 
 | ||||
|             @app.errorhandler(DatabaseError) | ||||
|             def special_exception_handler(error): | ||||
|                 return 'Database connection failed', 500 | ||||
| 
 | ||||
|         .. versionadded:: 0.7 | ||||
|             Use :meth:`register_error_handler` instead of modifying | ||||
|             :attr:`error_handler_spec` directly, for application wide error | ||||
|             handlers. | ||||
| 
 | ||||
|         .. versionadded:: 0.7 | ||||
|            One can now additionally also register custom exception types | ||||
|            that do not necessarily have to be a subclass of the | ||||
|            :class:`~werkzeug.exceptions.HTTPException` class. | ||||
| 
 | ||||
|         :param code_or_exception: the code as integer for the handler, or | ||||
|                                   an arbitrary exception | ||||
|         """ | ||||
| 
 | ||||
|         def decorator(f): | ||||
|             self._register_error_handler(None, code_or_exception, f) | ||||
|             return f | ||||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def register_error_handler(self, code_or_exception, f): | ||||
|         """Alternative error attach function to the :meth:`errorhandler` | ||||
|         decorator that is more straightforward to use for non decorator | ||||
|         usage. | ||||
| 
 | ||||
|         .. versionadded:: 0.7 | ||||
|         """ | ||||
|         self._register_error_handler(None, code_or_exception, f) | ||||
| 
 | ||||
|     @setupmethod | ||||
|     def _register_error_handler(self, key, code_or_exception, f): | ||||
|         """ | ||||
|         :type key: None|str | ||||
|         :type code_or_exception: int|T<=Exception | ||||
|         :type f: callable | ||||
|         """ | ||||
|         if isinstance(code_or_exception, HTTPException):  # old broken behavior | ||||
|             raise ValueError( | ||||
|                 "Tried to register a handler for an exception instance" | ||||
|                 f" {code_or_exception!r}. Handlers can only be" | ||||
|                 " registered for exception classes or HTTP error codes." | ||||
|             ) | ||||
| 
 | ||||
|         try: | ||||
|             exc_class, code = self._get_exc_class_and_code(code_or_exception) | ||||
|         except KeyError: | ||||
|             raise KeyError( | ||||
|                 f"'{code_or_exception}' is not a recognized HTTP error" | ||||
|                 " code. Use a subclass of HTTPException with that code" | ||||
|                 " instead." | ||||
|             ) | ||||
| 
 | ||||
|         handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {}) | ||||
|         handlers[exc_class] = f | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def _get_exc_class_and_code(exc_class_or_code): | ||||
|         """Get the exception class being handled. For HTTP status codes | ||||
|         or ``HTTPException`` subclasses, return both the exception and | ||||
|         status code. | ||||
| 
 | ||||
|         :param exc_class_or_code: Any exception class, or an HTTP status | ||||
|             code as an integer. | ||||
|         """ | ||||
|         if isinstance(exc_class_or_code, int): | ||||
|             exc_class = default_exceptions[exc_class_or_code] | ||||
|         else: | ||||
|             exc_class = exc_class_or_code | ||||
| 
 | ||||
|         assert issubclass( | ||||
|             exc_class, Exception | ||||
|         ), "Custom exceptions must be subclasses of Exception." | ||||
| 
 | ||||
|         if issubclass(exc_class, HTTPException): | ||||
|             return exc_class, exc_class.code | ||||
|         else: | ||||
|             return exc_class, None | ||||
| 
 | ||||
| 
 | ||||
| def _endpoint_from_view_func(view_func): | ||||
|     """Internal helper that returns the default endpoint for a given | ||||
|     function.  This always is the function name. | ||||
|     """ | ||||
|     assert view_func is not None, "expected view func if endpoint is not provided." | ||||
|     return view_func.__name__ | ||||
		Loading…
	
		Reference in New Issue