mirror of https://github.com/mitsuhiko/flask.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2000 lines
81 KiB
2000 lines
81 KiB
# -*- coding: utf-8 -*- |
|
""" |
|
flask.app |
|
~~~~~~~~~ |
|
|
|
This module implements the central WSGI application object. |
|
|
|
:copyright: (c) 2015 by Armin Ronacher. |
|
:license: BSD, see LICENSE for more details. |
|
""" |
|
import os |
|
import sys |
|
from threading import Lock |
|
from datetime import timedelta |
|
from itertools import chain |
|
from functools import update_wrapper |
|
|
|
from werkzeug.datastructures import ImmutableDict |
|
from werkzeug.routing import Map, Rule, RequestRedirect, BuildError |
|
from werkzeug.exceptions import HTTPException, InternalServerError, \ |
|
MethodNotAllowed, BadRequest, default_exceptions |
|
|
|
from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \ |
|
locked_cached_property, _endpoint_from_view_func, find_package, \ |
|
get_debug_flag |
|
from . import json, cli |
|
from .wrappers import Request, Response |
|
from .config import ConfigAttribute, Config |
|
from .ctx import RequestContext, AppContext, _AppCtxGlobals |
|
from .globals import _request_ctx_stack, request, session, g |
|
from .sessions import SecureCookieSessionInterface |
|
from .templating import DispatchingJinjaLoader, Environment, \ |
|
_default_template_ctx_processor |
|
from .signals import request_started, request_finished, got_request_exception, \ |
|
request_tearing_down, appcontext_tearing_down |
|
from ._compat import reraise, string_types, text_type, integer_types |
|
|
|
# a lock used for logger initialization |
|
_logger_lock = Lock() |
|
|
|
# a singleton sentinel value for parameter defaults |
|
_sentinel = object() |
|
|
|
|
|
def _make_timedelta(value): |
|
if not isinstance(value, timedelta): |
|
return timedelta(seconds=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): |
|
"""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 |
|
the view functions, the URL rules, template configuration and much more. |
|
|
|
The name of the package is used to resolve resources from inside the |
|
package or the folder the module is contained in depending on if the |
|
package parameter resolves to an actual python package (a folder with |
|
an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file). |
|
|
|
For more information about resource loading, see :func:`open_resource`. |
|
|
|
Usually you create a :class:`Flask` instance in your main module or |
|
in the :file:`__init__.py` file of your package like this:: |
|
|
|
from flask import Flask |
|
app = Flask(__name__) |
|
|
|
.. admonition:: About the First Parameter |
|
|
|
The idea of the first parameter is to give Flask an idea of what |
|
belongs to your application. This name is used to find resources |
|
on the filesystem, can be used by extensions to improve debugging |
|
information and a lot more. |
|
|
|
So it's important what you provide there. If you are using a single |
|
module, `__name__` is always the correct value. If you however are |
|
using a package, it's usually recommended to hardcode the name of |
|
your package there. |
|
|
|
For example if your application is defined in :file:`yourapplication/app.py` |
|
you should create it with one of the two versions below:: |
|
|
|
app = Flask('yourapplication') |
|
app = Flask(__name__.split('.')[0]) |
|
|
|
Why is that? The application will work even with `__name__`, thanks |
|
to how resources are looked up. However it will make debugging more |
|
painful. Certain extensions can make assumptions based on the |
|
import name of your application. For example the Flask-SQLAlchemy |
|
extension will look for the code in your application that triggered |
|
an SQL query in debug mode. If the import name is not properly set |
|
up, that debugging information is lost. (For example it would only |
|
pick up SQL queries in `yourapplication.app` and not |
|
`yourapplication.views.frontend`) |
|
|
|
.. versionadded:: 0.7 |
|
The `static_url_path`, `static_folder`, and `template_folder` |
|
parameters were added. |
|
|
|
.. versionadded:: 0.8 |
|
The `instance_path` and `instance_relative_config` parameters were |
|
added. |
|
|
|
.. versionadded:: 0.11 |
|
The `root_path` parameter was added. |
|
|
|
:param import_name: the name of the application package |
|
:param static_url_path: can be used to specify a different path for the |
|
static files on the web. Defaults to the name |
|
of the `static_folder` folder. |
|
:param static_folder: the folder with static files that should be served |
|
at `static_url_path`. Defaults to the ``'static'`` |
|
folder in the root path of the application. |
|
:param template_folder: the folder that contains the templates that should |
|
be used by the application. Defaults to |
|
``'templates'`` folder in the root path of the |
|
application. |
|
:param instance_path: An alternative instance path for the application. |
|
By default the folder ``'instance'`` next to the |
|
package or module is assumed to be the instance |
|
path. |
|
:param instance_relative_config: if set to ``True`` relative filenames |
|
for loading the config are assumed to |
|
be relative to the instance path instead |
|
of the application root. |
|
:param root_path: Flask by default will automatically calculate the path |
|
to the root of the application. In certain situations |
|
this cannot be achieved (for instance if the package |
|
is a Python 3 namespace package) and needs to be |
|
manually defined. |
|
""" |
|
|
|
#: The class that is used for request objects. See :class:`~flask.Request` |
|
#: for more information. |
|
request_class = Request |
|
|
|
#: The class that is used for response objects. See |
|
#: :class:`~flask.Response` for more information. |
|
response_class = Response |
|
|
|
#: The class that is used for the Jinja environment. |
|
#: |
|
#: .. versionadded:: 0.11 |
|
jinja_environment = Environment |
|
|
|
#: The class that is used for the :data:`~flask.g` instance. |
|
#: |
|
#: Example use cases for a custom class: |
|
#: |
|
#: 1. Store arbitrary attributes on flask.g. |
|
#: 2. Add a property for lazy per-request database connectors. |
|
#: 3. Return None instead of AttributeError on unexpected attributes. |
|
#: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g. |
|
#: |
|
#: In Flask 0.9 this property was called `request_globals_class` but it |
|
#: was changed in 0.10 to :attr:`app_ctx_globals_class` because the |
|
#: flask.g object is now application context scoped. |
|
#: |
|
#: .. versionadded:: 0.10 |
|
app_ctx_globals_class = _AppCtxGlobals |
|
|
|
# Backwards compatibility support |
|
def _get_request_globals_class(self): |
|
return self.app_ctx_globals_class |
|
def _set_request_globals_class(self, value): |
|
from warnings import warn |
|
warn(DeprecationWarning('request_globals_class attribute is now ' |
|
'called app_ctx_globals_class')) |
|
self.app_ctx_globals_class = value |
|
request_globals_class = property(_get_request_globals_class, |
|
_set_request_globals_class) |
|
del _get_request_globals_class, _set_request_globals_class |
|
|
|
#: The class that is used for the ``config`` attribute of this app. |
|
#: Defaults to :class:`~flask.Config`. |
|
#: |
|
#: Example use cases for a custom class: |
|
#: |
|
#: 1. Default values for certain config options. |
|
#: 2. Access to config values through attributes in addition to keys. |
|
#: |
|
#: .. versionadded:: 0.11 |
|
config_class = Config |
|
|
|
#: The debug flag. Set this to ``True`` to enable debugging of the |
|
#: application. In debug mode the debugger will kick in when an unhandled |
|
#: exception occurs and the integrated server will automatically reload |
|
#: the application if changes in the code are detected. |
|
#: |
|
#: This attribute can also be configured from the config with the ``DEBUG`` |
|
#: configuration key. Defaults to ``False``. |
|
debug = ConfigAttribute('DEBUG') |
|
|
|
#: The testing flag. Set this to ``True`` to enable the test mode of |
|
#: Flask extensions (and in the future probably also Flask itself). |
|
#: For example this might activate unittest helpers that have an |
|
#: additional runtime cost which should not be enabled by default. |
|
#: |
|
#: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the |
|
#: default it's implicitly enabled. |
|
#: |
|
#: This attribute can also be configured from the config with the |
|
#: ``TESTING`` configuration key. Defaults to ``False``. |
|
testing = ConfigAttribute('TESTING') |
|
|
|
#: If a secret key is set, cryptographic components can use this to |
|
#: sign cookies and other things. Set this to a complex random value |
|
#: when you want to use the secure cookie for instance. |
|
#: |
|
#: This attribute can also be configured from the config with the |
|
#: ``SECRET_KEY`` configuration key. Defaults to ``None``. |
|
secret_key = ConfigAttribute('SECRET_KEY') |
|
|
|
#: The secure cookie uses this for the name of the session cookie. |
|
#: |
|
#: This attribute can also be configured from the config with the |
|
#: ``SESSION_COOKIE_NAME`` configuration key. Defaults to ``'session'`` |
|
session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME') |
|
|
|
#: A :class:`~datetime.timedelta` which is used to set the expiration |
|
#: date of a permanent session. The default is 31 days which makes a |
|
#: permanent session survive for roughly one month. |
|
#: |
|
#: This attribute can also be configured from the config with the |
|
#: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to |
|
#: ``timedelta(days=31)`` |
|
permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME', |
|
get_converter=_make_timedelta) |
|
|
|
#: A :class:`~datetime.timedelta` which is used as default cache_timeout |
|
#: for the :func:`send_file` functions. The default is 12 hours. |
|
#: |
|
#: This attribute can also be configured from the config with the |
|
#: ``SEND_FILE_MAX_AGE_DEFAULT`` configuration key. This configuration |
|
#: variable can also be set with an integer value used as seconds. |
|
#: Defaults to ``timedelta(hours=12)`` |
|
send_file_max_age_default = ConfigAttribute('SEND_FILE_MAX_AGE_DEFAULT', |
|
get_converter=_make_timedelta) |
|
|
|
#: Enable this if you want to use the X-Sendfile feature. Keep in |
|
#: mind that the server has to support this. This only affects files |
|
#: sent with the :func:`send_file` method. |
|
#: |
|
#: .. versionadded:: 0.2 |
|
#: |
|
#: This attribute can also be configured from the config with the |
|
#: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``. |
|
use_x_sendfile = ConfigAttribute('USE_X_SENDFILE') |
|
|
|
#: The name of the logger to use. By default the logger name is the |
|
#: package name passed to the constructor. |
|
#: |
|
#: .. versionadded:: 0.4 |
|
logger_name = ConfigAttribute('LOGGER_NAME') |
|
|
|
#: The JSON encoder class to use. Defaults to :class:`~flask.json.JSONEncoder`. |
|
#: |
|
#: .. versionadded:: 0.10 |
|
json_encoder = json.JSONEncoder |
|
|
|
#: The JSON decoder class to use. Defaults to :class:`~flask.json.JSONDecoder`. |
|
#: |
|
#: .. versionadded:: 0.10 |
|
json_decoder = json.JSONDecoder |
|
|
|
#: Options that are passed directly to the Jinja2 environment. |
|
jinja_options = ImmutableDict( |
|
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'] |
|
) |
|
|
|
#: Default configuration parameters. |
|
default_config = ImmutableDict({ |
|
'DEBUG': get_debug_flag(default=False), |
|
'TESTING': False, |
|
'PROPAGATE_EXCEPTIONS': None, |
|
'PRESERVE_CONTEXT_ON_EXCEPTION': None, |
|
'SECRET_KEY': None, |
|
'PERMANENT_SESSION_LIFETIME': timedelta(days=31), |
|
'USE_X_SENDFILE': False, |
|
'LOGGER_NAME': None, |
|
'LOGGER_HANDLER_POLICY': 'always', |
|
'SERVER_NAME': None, |
|
'APPLICATION_ROOT': None, |
|
'SESSION_COOKIE_NAME': 'session', |
|
'SESSION_COOKIE_DOMAIN': None, |
|
'SESSION_COOKIE_PATH': None, |
|
'SESSION_COOKIE_HTTPONLY': True, |
|
'SESSION_COOKIE_SECURE': False, |
|
'SESSION_REFRESH_EACH_REQUEST': True, |
|
'MAX_CONTENT_LENGTH': None, |
|
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), |
|
'TRAP_BAD_REQUEST_ERRORS': False, |
|
'TRAP_HTTP_EXCEPTIONS': False, |
|
'EXPLAIN_TEMPLATE_LOADING': False, |
|
'PREFERRED_URL_SCHEME': 'http', |
|
'JSON_AS_ASCII': True, |
|
'JSON_SORT_KEYS': True, |
|
'JSONIFY_PRETTYPRINT_REGULAR': True, |
|
'JSONIFY_MIMETYPE': 'application/json', |
|
'TEMPLATES_AUTO_RELOAD': None, |
|
}) |
|
|
|
#: The rule object to use for URL rules created. This is used by |
|
#: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`. |
|
#: |
|
#: .. versionadded:: 0.7 |
|
url_rule_class = Rule |
|
|
|
#: the test client that is used with when `test_client` is used. |
|
#: |
|
#: .. versionadded:: 0.7 |
|
test_client_class = None |
|
|
|
#: the session interface to use. By default an instance of |
|
#: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. |
|
#: |
|
#: .. versionadded:: 0.8 |
|
session_interface = SecureCookieSessionInterface() |
|
|
|
def __init__(self, import_name, static_path=None, static_url_path=None, |
|
static_folder='static', template_folder='templates', |
|
instance_path=None, instance_relative_config=False, |
|
root_path=None): |
|
_PackageBoundObject.__init__(self, import_name, |
|
template_folder=template_folder, |
|
root_path=root_path) |
|
if static_path is not None: |
|
from warnings import warn |
|
warn(DeprecationWarning('static_path is now called ' |
|
'static_url_path'), stacklevel=2) |
|
static_url_path = static_path |
|
|
|
if static_url_path is not None: |
|
self.static_url_path = static_url_path |
|
if static_folder is not None: |
|
self.static_folder = static_folder |
|
if instance_path is None: |
|
instance_path = self.auto_find_instance_path() |
|
elif not os.path.isabs(instance_path): |
|
raise ValueError('If an instance path is provided it must be ' |
|
'absolute. A relative path was given instead.') |
|
|
|
#: Holds the path to the instance folder. |
|
#: |
|
#: .. versionadded:: 0.8 |
|
self.instance_path = instance_path |
|
|
|
#: The configuration dictionary as :class:`Config`. This behaves |
|
#: exactly like a regular dictionary but supports additional methods |
|
#: to load a config from files. |
|
self.config = self.make_config(instance_relative_config) |
|
|
|
# Prepare the deferred setup of the logger. |
|
self._logger = None |
|
self.logger_name = self.import_name |
|
|
|
#: 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 = {} |
|
|
|
# support for the now deprecated `error_handlers` attribute. The |
|
# :attr:`error_handler_spec` shall be used now. |
|
self._error_handlers = {} |
|
|
|
#: 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 a error handler, use the :meth:`errorhandler` |
|
#: decorator. |
|
self.error_handler_spec = {None: self._error_handlers} |
|
|
|
#: 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 |
|
#: returns ``None`` or raises a :exc:`BuildError` the next function is |
|
#: tried. |
|
#: |
|
#: .. versionadded:: 0.9 |
|
self.url_build_error_handlers = [] |
|
|
|
#: A dictionary with lists of functions that should be called at the |
|
#: beginning of the 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 open database connections or |
|
#: getting hold of the currently logged in user. To register a |
|
#: function here, use the :meth:`before_request` decorator. |
|
self.before_request_funcs = {} |
|
|
|
#: A lists of functions that should be called at the beginning of the |
|
#: first request to this instance. To register a function here, use |
|
#: the :meth:`before_first_request` decorator. |
|
#: |
|
#: .. 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 |
|
#: from databases. |
|
#: |
|
#: .. versionadded:: 0.9 |
|
self.teardown_appcontext_funcs = [] |
|
|
|
#: A dictionary with lists of functions that can be used as URL |
|
#: value processor functions. Whenever a URL is built these functions |
|
#: are called to modify the dictionary of values in place. 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 |
|
#: |
|
#: .. 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. |
|
#: |
|
#: .. versionadded:: 0.11 |
|
self.shell_context_processors = [] |
|
|
|
#: all the attached blueprints in a dictionary by name. Blueprints |
|
#: can be attached multiple times so this dictionary does not tell |
|
#: you how often they got attached. |
|
#: |
|
#: .. versionadded:: 0.7 |
|
self.blueprints = {} |
|
self._blueprint_order = [] |
|
|
|
#: a place where extensions can store application specific state. For |
|
#: example this is where an extension could store database engines and |
|
#: similar things. For backwards compatibility extensions should register |
|
#: themselves like this:: |
|
#: |
|
#: if not hasattr(app, 'extensions'): |
|
#: app.extensions = {} |
|
#: app.extensions['extensionname'] = SomeObject() |
|
#: |
|
#: The key must match the name of the extension module. For example in |
|
#: case of a "Flask-Foo" extension in `flask_foo`, the key would be |
|
#: ``'foo'``. |
|
#: |
|
#: .. versionadded:: 0.7 |
|
self.extensions = {} |
|
|
|
#: The :class:`~werkzeug.routing.Map` for this instance. You can use |
|
#: this to change the routing converters after the class was created |
|
#: but before any routes are connected. Example:: |
|
#: |
|
#: from werkzeug.routing import BaseConverter |
|
#: |
|
#: class ListConverter(BaseConverter): |
|
#: def to_python(self, value): |
|
#: return value.split(',') |
|
#: def to_url(self, values): |
|
#: return ','.join(super(ListConverter, self).to_url(value) |
|
#: for value in values) |
|
#: |
|
#: app = Flask(__name__) |
|
#: app.url_map.converters['list'] = ListConverter |
|
self.url_map = Map() |
|
|
|
# tracks internally if the application already handled at least one |
|
# request. |
|
self._got_first_request = False |
|
self._before_request_lock = Lock() |
|
|
|
# register the static folder for the application. Do that even |
|
# if the folder does not exist. First of all it might be created |
|
# while the server is running (usually happens during development) |
|
# but also because google appengine stores static files somewhere |
|
# else when mapped with the .yml file. |
|
if self.has_static_folder: |
|
self.add_url_rule(self.static_url_path + '/<path:filename>', |
|
endpoint='static', |
|
view_func=self.send_static_file) |
|
|
|
#: The click command line context for this application. Commands |
|
#: registered here show up in the :command:`flask` command once the |
|
#: application has been discovered. The default commands are |
|
#: provided by Flask itself and can be overridden. |
|
#: |
|
#: This is an instance of a :class:`click.Group` object. |
|
self.cli = cli.AppGroup(self.name) |
|
|
|
def _get_error_handlers(self): |
|
from warnings import warn |
|
warn(DeprecationWarning('error_handlers is deprecated, use the ' |
|
'new error_handler_spec attribute instead.'), stacklevel=1) |
|
return self._error_handlers |
|
def _set_error_handlers(self, value): |
|
self._error_handlers = value |
|
self.error_handler_spec[None] = value |
|
error_handlers = property(_get_error_handlers, _set_error_handlers) |
|
del _get_error_handlers, _set_error_handlers |
|
|
|
@locked_cached_property |
|
def name(self): |
|
"""The name of the application. This is usually the import name |
|
with the difference that it's guessed from the run file if the |
|
import name is main. This name is used as a display name when |
|
Flask needs the name of the application. It can be set and overridden |
|
to change the value. |
|
|
|
.. versionadded:: 0.8 |
|
""" |
|
if self.import_name == '__main__': |
|
fn = getattr(sys.modules['__main__'], '__file__', None) |
|
if fn is None: |
|
return '__main__' |
|
return os.path.splitext(os.path.basename(fn))[0] |
|
return self.import_name |
|
|
|
@property |
|
def propagate_exceptions(self): |
|
"""Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration |
|
value in case it's set, otherwise a sensible default is returned. |
|
|
|
.. versionadded:: 0.7 |
|
""" |
|
rv = self.config['PROPAGATE_EXCEPTIONS'] |
|
if rv is not None: |
|
return rv |
|
return self.testing or self.debug |
|
|
|
@property |
|
def preserve_context_on_exception(self): |
|
"""Returns the value of the ``PRESERVE_CONTEXT_ON_EXCEPTION`` |
|
configuration value in case it's set, otherwise a sensible default |
|
is returned. |
|
|
|
.. versionadded:: 0.7 |
|
""" |
|
rv = self.config['PRESERVE_CONTEXT_ON_EXCEPTION'] |
|
if rv is not None: |
|
return rv |
|
return self.debug |
|
|
|
@property |
|
def logger(self): |
|
"""A :class:`logging.Logger` object for this application. The |
|
default configuration is to log to stderr if the application is |
|
in debug mode. This logger can be used to (surprise) log messages. |
|
Here some examples:: |
|
|
|
app.logger.debug('A value for debugging') |
|
app.logger.warning('A warning occurred (%d apples)', 42) |
|
app.logger.error('An error occurred') |
|
|
|
.. versionadded:: 0.3 |
|
""" |
|
if self._logger and self._logger.name == self.logger_name: |
|
return self._logger |
|
with _logger_lock: |
|
if self._logger and self._logger.name == self.logger_name: |
|
return self._logger |
|
from flask.logging import create_logger |
|
self._logger = rv = create_logger(self) |
|
return rv |
|
|
|
@locked_cached_property |
|
def jinja_env(self): |
|
"""The Jinja2 environment used to load templates.""" |
|
return self.create_jinja_environment() |
|
|
|
@property |
|
def got_first_request(self): |
|
"""This attribute is set to ``True`` if the application started |
|
handling the first request. |
|
|
|
.. versionadded:: 0.8 |
|
""" |
|
return self._got_first_request |
|
|
|
def make_config(self, instance_relative=False): |
|
"""Used to create the config attribute by the Flask constructor. |
|
The `instance_relative` parameter is passed in from the constructor |
|
of Flask (there named `instance_relative_config`) and indicates if |
|
the config should be relative to the instance path or the root path |
|
of the application. |
|
|
|
.. versionadded:: 0.8 |
|
""" |
|
root_path = self.root_path |
|
if instance_relative: |
|
root_path = self.instance_path |
|
return self.config_class(root_path, self.default_config) |
|
|
|
def auto_find_instance_path(self): |
|
"""Tries to locate the instance path if it was not provided to the |
|
constructor of the application class. It will basically calculate |
|
the path to a folder named ``instance`` next to your main file or |
|
the package. |
|
|
|
.. versionadded:: 0.8 |
|
""" |
|
prefix, package_path = find_package(self.import_name) |
|
if prefix is None: |
|
return os.path.join(package_path, 'instance') |
|
return os.path.join(prefix, 'var', self.name + '-instance') |
|
|
|
def open_instance_resource(self, resource, mode='rb'): |
|
"""Opens a resource from the application's instance folder |
|
(:attr:`instance_path`). Otherwise works like |
|
:meth:`open_resource`. Instance resources can also be opened for |
|
writing. |
|
|
|
:param resource: the name of the resource. To access resources within |
|
subfolders use forward slashes as separator. |
|
:param mode: resource file opening mode, default is 'rb'. |
|
""" |
|
return open(os.path.join(self.instance_path, resource), mode) |
|
|
|
def create_jinja_environment(self): |
|
"""Creates the Jinja2 environment based on :attr:`jinja_options` |
|
and :meth:`select_jinja_autoescape`. Since 0.7 this also adds |
|
the Jinja2 globals and filters after initialization. Override |
|
this function to customize the behavior. |
|
|
|
.. versionadded:: 0.5 |
|
.. versionchanged:: 0.11 |
|
``Environment.auto_reload`` set in accordance with |
|
``TEMPLATES_AUTO_RELOAD`` configuration option. |
|
""" |
|
options = dict(self.jinja_options) |
|
if 'autoescape' not in options: |
|
options['autoescape'] = self.select_jinja_autoescape |
|
if 'auto_reload' not in options: |
|
if self.config['TEMPLATES_AUTO_RELOAD'] is not None: |
|
options['auto_reload'] = self.config['TEMPLATES_AUTO_RELOAD'] |
|
else: |
|
options['auto_reload'] = self.debug |
|
rv = self.jinja_environment(self, **options) |
|
rv.globals.update( |
|
url_for=url_for, |
|
get_flashed_messages=get_flashed_messages, |
|
config=self.config, |
|
# request, session and g are normally added with the |
|
# context processor for efficiency reasons but for imported |
|
# templates we also want the proxies in there. |
|
request=request, |
|
session=session, |
|
g=g |
|
) |
|
rv.filters['tojson'] = json.tojson_filter |
|
return rv |
|
|
|
def create_global_jinja_loader(self): |
|
"""Creates the loader for the Jinja2 environment. Can be used to |
|
override just the loader and keeping the rest unchanged. It's |
|
discouraged to override this function. Instead one should override |
|
the :meth:`jinja_loader` function instead. |
|
|
|
The global loader dispatches between the loaders of the application |
|
and the individual blueprints. |
|
|
|
.. versionadded:: 0.7 |
|
""" |
|
return DispatchingJinjaLoader(self) |
|
|
|
def init_jinja_globals(self): |
|
"""Deprecated. Used to initialize the Jinja2 globals. |
|
|
|
.. versionadded:: 0.5 |
|
.. versionchanged:: 0.7 |
|
This method is deprecated with 0.7. Override |
|
:meth:`create_jinja_environment` instead. |
|
""" |
|
|
|
def select_jinja_autoescape(self, filename): |
|
"""Returns ``True`` if autoescaping should be active for the given |
|
template name. If no template name is given, returns `True`. |
|
|
|
.. versionadded:: 0.5 |
|
""" |
|
if filename is None: |
|
return True |
|
return filename.endswith(('.html', '.htm', '.xml', '.xhtml')) |
|
|
|
def update_template_context(self, context): |
|
"""Update the template context with some commonly used variables. |
|
This injects request, session, config and g into the template |
|
context as well as everything template context processors want |
|
to inject. Note that the as of Flask 0.6, the original values |
|
in the context will not be overridden if a context processor |
|
decides to return a value with the same key. |
|
|
|
:param context: the context as a dictionary that is updated in place |
|
to add extra variables. |
|
""" |
|
funcs = self.template_context_processors[None] |
|
reqctx = _request_ctx_stack.top |
|
if reqctx is not None: |
|
bp = reqctx.request.blueprint |
|
if bp is not None and bp in self.template_context_processors: |
|
funcs = chain(funcs, self.template_context_processors[bp]) |
|
orig_ctx = context.copy() |
|
for func in funcs: |
|
context.update(func()) |
|
# make sure the original values win. This makes it possible to |
|
# easier add new variables in context processors without breaking |
|
# existing views. |
|
context.update(orig_ctx) |
|
|
|
def make_shell_context(self): |
|
"""Returns the shell context for an interactive shell for this |
|
application. This runs all the registered shell context |
|
processors. |
|
|
|
.. versionadded:: 0.11 |
|
""" |
|
rv = {'app': self, 'g': g} |
|
for processor in self.shell_context_processors: |
|
rv.update(processor()) |
|
return rv |
|
|
|
def run(self, host=None, port=None, debug=None, **options): |
|
"""Runs the application on a local development server. |
|
|
|
Do not use ``run()`` in a production setting. It is not intended to |
|
meet security and performance requirements for a production server. |
|
Instead, see :ref:`deployment` for WSGI server recommendations. |
|
|
|
If the :attr:`debug` flag is set the server will automatically reload |
|
for code changes and show a debugger in case an exception happened. |
|
|
|
If you want to run the application in debug mode, but disable the |
|
code execution on the interactive debugger, you can pass |
|
``use_evalex=False`` as parameter. This will keep the debugger's |
|
traceback screen active, but disable code execution. |
|
|
|
It is not recommended to use this function for development with |
|
automatic reloading as this is badly supported. Instead you should |
|
be using the :command:`flask` command line script's ``run`` support. |
|
|
|
.. admonition:: Keep in Mind |
|
|
|
Flask will suppress any server error with a generic error page |
|
unless it is in debug mode. As such to enable just the |
|
interactive debugger without the code reloading, you have to |
|
invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``. |
|
Setting ``use_debugger`` to ``True`` without being in debug mode |
|
won't catch any exceptions because there won't be any to |
|
catch. |
|
|
|
.. versionchanged:: 0.10 |
|
The default port is now picked from the ``SERVER_NAME`` variable. |
|
|
|
:param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to |
|
have the server available externally as well. Defaults to |
|
``'127.0.0.1'``. |
|
:param port: the port of the webserver. Defaults to ``5000`` or the |
|
port defined in the ``SERVER_NAME`` config variable if |
|
present. |
|
:param debug: if given, enable or disable debug mode. |
|
See :attr:`debug`. |
|
:param options: the options to be forwarded to the underlying |
|
Werkzeug server. See |
|
:func:`werkzeug.serving.run_simple` for more |
|
information. |
|
""" |
|
from werkzeug.serving import run_simple |
|
if host is None: |
|
host = '127.0.0.1' |
|
if port is None: |
|
server_name = self.config['SERVER_NAME'] |
|
if server_name and ':' in server_name: |
|
port = int(server_name.rsplit(':', 1)[1]) |
|
else: |
|
port = 5000 |
|
if debug is not None: |
|
self.debug = bool(debug) |
|
options.setdefault('use_reloader', self.debug) |
|
options.setdefault('use_debugger', self.debug) |
|
try: |
|
run_simple(host, port, self, **options) |
|
finally: |
|
# reset the first request information if the development server |
|
# reset normally. This makes it possible to restart the server |
|
# without reloader and that stuff from an interactive shell. |
|
self._got_first_request = False |
|
|
|
def test_client(self, use_cookies=True, **kwargs): |
|
"""Creates a test client for this application. For information |
|
about unit testing head over to :ref:`testing`. |
|
|
|
Note that if you are testing for assertions or exceptions in your |
|
application code, you must set ``app.testing = True`` in order for the |
|
exceptions to propagate to the test client. Otherwise, the exception |
|
will be handled by the application (not visible to the test client) and |
|
the only indication of an AssertionError or other exception will be a |
|
500 status code response to the test client. See the :attr:`testing` |
|
attribute. For example:: |
|
|
|
app.testing = True |
|
client = app.test_client() |
|
|
|
The test client can be used in a ``with`` block to defer the closing down |
|
of the context until the end of the ``with`` block. This is useful if |
|
you want to access the context locals for testing:: |
|
|
|
with app.test_client() as c: |
|
rv = c.get('/?vodka=42') |
|
assert request.args['vodka'] == '42' |
|
|
|
Additionally, you may pass optional keyword arguments that will then |
|
be passed to the application's :attr:`test_client_class` constructor. |
|
For example:: |
|
|
|
from flask.testing import FlaskClient |
|
|
|
class CustomClient(FlaskClient): |
|
def __init__(self, *args, **kwargs): |
|
self._authentication = kwargs.pop("authentication") |
|
super(CustomClient,self).__init__( *args, **kwargs) |
|
|
|
app.test_client_class = CustomClient |
|
client = app.test_client(authentication='Basic ....') |
|
|
|
See :class:`~flask.testing.FlaskClient` for more information. |
|
|
|
.. versionchanged:: 0.4 |
|
added support for ``with`` block usage for the client. |
|
|
|
.. versionadded:: 0.7 |
|
The `use_cookies` parameter was added as well as the ability |
|
to override the client to be used by setting the |
|
:attr:`test_client_class` attribute. |
|
|
|
.. versionchanged:: 0.11 |
|
Added `**kwargs` to support passing additional keyword arguments to |
|
the constructor of :attr:`test_client_class`. |
|
""" |
|
cls = self.test_client_class |
|
if cls is None: |
|
from flask.testing import FlaskClient as cls |
|
return cls(self, self.response_class, use_cookies=use_cookies, **kwargs) |
|
|
|
def open_session(self, request): |
|
"""Creates or opens a new session. Default implementation stores all |
|
session data in a signed cookie. This requires that the |
|
:attr:`secret_key` is set. Instead of overriding this method |
|
we recommend replacing the :class:`session_interface`. |
|
|
|
:param request: an instance of :attr:`request_class`. |
|
""" |
|
return self.session_interface.open_session(self, request) |
|
|
|
def save_session(self, session, response): |
|
"""Saves the session if it needs updates. For the default |
|
implementation, check :meth:`open_session`. Instead of overriding this |
|
method we recommend replacing the :class:`session_interface`. |
|
|
|
:param session: the session to be saved (a |
|
:class:`~werkzeug.contrib.securecookie.SecureCookie` |
|
object) |
|
:param response: an instance of :attr:`response_class` |
|
""" |
|
return self.session_interface.save_session(self, session, response) |
|
|
|
def make_null_session(self): |
|
"""Creates a new instance of a missing session. Instead of overriding |
|
this method we recommend replacing the :class:`session_interface`. |
|
|
|
.. versionadded:: 0.7 |
|
""" |
|
return self.session_interface.make_null_session(self) |
|
|
|
@setupmethod |
|
def register_blueprint(self, blueprint, **options): |
|
"""Registers a blueprint on the application. |
|
|
|
.. versionadded:: 0.7 |
|
""" |
|
first_registration = False |
|
if blueprint.name in self.blueprints: |
|
assert self.blueprints[blueprint.name] is blueprint, \ |
|
'A blueprint\'s name collision occurred between %r and ' \ |
|
'%r. Both share the same name "%s". Blueprints that ' \ |
|
'are created on the fly need unique names.' % \ |
|
(blueprint, self.blueprints[blueprint.name], blueprint.name) |
|
else: |
|
self.blueprints[blueprint.name] = blueprint |
|
self._blueprint_order.append(blueprint) |
|
first_registration = True |
|
blueprint.register(self, options, first_registration) |
|
|
|
def iter_blueprints(self): |
|
"""Iterates over all blueprints by the order they were registered. |
|
|
|
.. versionadded:: 0.11 |
|
""" |
|
return iter(self._blueprint_order) |
|
|
|
@setupmethod |
|
def add_url_rule(self, rule, endpoint=None, view_func=None, **options): |
|
"""Connects a URL rule. Works exactly like the :meth:`route` |
|
decorator. If a view_func is provided it will be registered with the |
|
endpoint. |
|
|
|
Basically this example:: |
|
|
|
@app.route('/') |
|
def index(): |
|
pass |
|
|
|
Is equivalent to the following:: |
|
|
|
def index(): |
|
pass |
|
app.add_url_rule('/', 'index', index) |
|
|
|
If the view_func is not provided you will need to connect the endpoint |
|
to a view function like so:: |
|
|
|
app.view_functions['index'] = index |
|
|
|
Internally :meth:`route` invokes :meth:`add_url_rule` so if you want |
|
to customize the behavior via subclassing you only need to change |
|
this method. |
|
|
|
For more information refer to :ref:`url-route-registrations`. |
|
|
|
.. versionchanged:: 0.2 |
|
`view_func` parameter added. |
|
|
|
.. versionchanged:: 0.6 |
|
``OPTIONS`` is added automatically as method. |
|
|
|
: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 view_func: the function to call when serving a request to the |
|
provided 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. |
|
""" |
|
if endpoint is None: |
|
endpoint = _endpoint_from_view_func(view_func) |
|
options['endpoint'] = endpoint |
|
methods = options.pop('methods', None) |
|
|
|
# if the methods are not given and the view_func object knows its |
|
# methods we can use that instead. If neither exists, we go with |
|
# a tuple of only ``GET`` as default. |
|
if methods is None: |
|
methods = getattr(view_func, 'methods', None) or ('GET',) |
|
if isinstance(methods, string_types): |
|
raise TypeError('Allowed methods have to be iterables of strings, ' |
|
'for example: @app.route(..., methods=["POST"])') |
|
methods = set(item.upper() for item in methods) |
|
|
|
# Methods that should always be added |
|
required_methods = set(getattr(view_func, 'required_methods', ())) |
|
|
|
# starting with Flask 0.8 the view_func object can disable and |
|
# force-enable the automatic options handling. |
|
provide_automatic_options = getattr(view_func, |
|
'provide_automatic_options', None) |
|
|
|
if provide_automatic_options is None: |
|
if 'OPTIONS' not in methods: |
|
provide_automatic_options = True |
|
required_methods.add('OPTIONS') |
|
else: |
|
provide_automatic_options = False |
|
|
|
# Add the required methods now. |
|
methods |= required_methods |
|
|
|
rule = self.url_rule_class(rule, methods=methods, **options) |
|
rule.provide_automatic_options = provide_automatic_options |
|
|
|
self.url_map.add(rule) |
|
if view_func is not None: |
|
old_func = self.view_functions.get(endpoint) |
|
if old_func is not None and old_func != view_func: |
|
raise AssertionError('View function mapping is overwriting an ' |
|
'existing endpoint function: %s' % endpoint) |
|
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): |
|
"""Ensure that we register only exceptions as handler keys""" |
|
if isinstance(exc_class_or_code, integer_types): |
|
exc_class = default_exceptions[exc_class_or_code] |
|
else: |
|
exc_class = exc_class_or_code |
|
|
|
assert issubclass(exc_class, Exception) |
|
|
|
if issubclass(exc_class, HTTPException): |
|
return exc_class, exc_class.code |
|
else: |
|
return exc_class, None |
|
|
|
@setupmethod |
|
def errorhandler(self, code_or_exception): |
|
"""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 |
|
|
|
You can also register a function as error handler without using |
|
the :meth:`errorhandler` decorator. The following example is |
|
equivalent to the one above:: |
|
|
|
def page_not_found(error): |
|
return 'This page does not exist', 404 |
|
app.error_handler_spec[None][404] = page_not_found |
|
|
|
Setting error handlers via assignments to :attr:`error_handler_spec` |
|
however is discouraged as it requires fiddling with nested dictionaries |
|
and the special case for arbitrary exception types. |
|
|
|
The first ``None`` refers to the active blueprint. If the error |
|
handler should be application wide ``None`` shall be used. |
|
|
|
.. 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 |
|
|
|
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 {0!r}. ' |
|
'Handlers can only be registered for exception classes or HTTP error codes.' |
|
.format(code_or_exception)) |
|
|
|
exc_class, code = self._get_exc_class_and_code(code_or_exception) |
|
|
|
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. |
|
You can specify a name for the filter, otherwise the function |
|
name will be used. Example:: |
|
|
|
@app.template_filter() |
|
def reverse(s): |
|
return s[::-1] |
|
|
|
:param name: the optional name of the filter, otherwise the |
|
function name will be used. |
|
""" |
|
def decorator(f): |
|
self.add_template_filter(f, name=name) |
|
return f |
|
return decorator |
|
|
|
@setupmethod |
|
def add_template_filter(self, f, name=None): |
|
"""Register a custom template filter. Works exactly like the |
|
:meth:`template_filter` decorator. |
|
|
|
:param name: the optional name of the filter, otherwise the |
|
function name will be used. |
|
""" |
|
self.jinja_env.filters[name or f.__name__] = f |
|
|
|
@setupmethod |
|
def template_test(self, name=None): |
|
"""A decorator that is used to register custom template test. |
|
You can specify a name for the test, otherwise the function |
|
name will be used. Example:: |
|
|
|
@app.template_test() |
|
def is_prime(n): |
|
if n == 2: |
|
return True |
|
for i in range(2, int(math.ceil(math.sqrt(n))) + 1): |
|
if n % i == 0: |
|
return False |
|
return True |
|
|
|
.. versionadded:: 0.10 |
|
|
|
:param name: the optional name of the test, otherwise the |
|
function name will be used. |
|
""" |
|
def decorator(f): |
|
self.add_template_test(f, name=name) |
|
return f |
|
return decorator |
|
|
|
@setupmethod |
|
def add_template_test(self, f, name=None): |
|
"""Register a custom template test. Works exactly like the |
|
:meth:`template_test` decorator. |
|
|
|
.. versionadded:: 0.10 |
|
|
|
:param name: the optional name of the test, otherwise the |
|
function name will be used. |
|
""" |
|
self.jinja_env.tests[name or f.__name__] = f |
|
|
|
@setupmethod |
|
def template_global(self, name=None): |
|
"""A decorator that is used to register a custom template global function. |
|
You can specify a name for the global function, otherwise the function |
|
name will be used. Example:: |
|
|
|
@app.template_global() |
|
def double(n): |
|
return 2 * n |
|
|
|
.. versionadded:: 0.10 |
|
|
|
:param name: the optional name of the global function, otherwise the |
|
function name will be used. |
|
""" |
|
def decorator(f): |
|
self.add_template_global(f, name=name) |
|
return f |
|
return decorator |
|
|
|
@setupmethod |
|
def add_template_global(self, f, name=None): |
|
"""Register a custom template global function. Works exactly like the |
|
:meth:`template_global` decorator. |
|
|
|
.. versionadded:: 0.10 |
|
|
|
:param name: the optional name of the global function, otherwise the |
|
function name will be used. |
|
""" |
|
self.jinja_env.globals[name or f.__name__] = f |
|
|
|
@setupmethod |
|
def before_request(self, f): |
|
"""Registers a function to run before each request. |
|
|
|
The function will be called without any arguments. |
|
If the function returns a non-None value, it's 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 |
|
instance of the application. |
|
|
|
The function will be called without any arguments and its return |
|
value is ignored. |
|
|
|
.. versionadded:: 0.8 |
|
""" |
|
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 a 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 |
|
ends. These functions are typically also called when the request |
|
context is popped. |
|
|
|
Example:: |
|
|
|
ctx = app.app_context() |
|
ctx.push() |
|
... |
|
ctx.pop() |
|
|
|
When ``ctx.pop()`` is executed in the above example, the teardown |
|
functions are called just before the app context moves from the |
|
stack of active contexts. This becomes relevant if you are using |
|
such constructs in tests. |
|
|
|
Since a request context typically also manages an application |
|
context it would also be called when you pop a request context. |
|
|
|
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. |
|
|
|
.. versionadded:: 0.9 |
|
""" |
|
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. |
|
|
|
.. versionadded:: 0.11 |
|
""" |
|
self.shell_context_processors.append(f) |
|
return f |
|
|
|
@setupmethod |
|
def url_value_preprocessor(self, f): |
|
"""Registers a function as URL value preprocessor for all view |
|
functions of the application. It's called before the view functions |
|
are called and can modify the url values provided. |
|
""" |
|
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): |
|
"""Finds a registered error handler for the request’s blueprint. |
|
Otherwise falls back to the app, returns None if not a suitable |
|
handler is found. |
|
""" |
|
exc_class, code = self._get_exc_class_and_code(type(e)) |
|
|
|
def find_handler(handler_map): |
|
if not handler_map: |
|
return |
|
for cls in exc_class.__mro__: |
|
handler = handler_map.get(cls) |
|
if handler is not None: |
|
# cache for next time exc_class is raised |
|
handler_map[exc_class] = handler |
|
return handler |
|
|
|
# try blueprint handlers |
|
handler = find_handler(self.error_handler_spec |
|
.get(request.blueprint, {}) |
|
.get(code)) |
|
if handler is not None: |
|
return handler |
|
|
|
# fall back to app handlers |
|
return find_handler(self.error_handler_spec[None].get(code)) |
|
|
|
def handle_http_exception(self, e): |
|
"""Handles an HTTP exception. By default this will invoke the |
|
registered error handlers and fall back to returning the |
|
exception as response. |
|
|
|
.. versionadded:: 0.3 |
|
""" |
|
# Proxy exceptions don't have error codes. We want to always return |
|
# those unchanged as errors |
|
if e.code is None: |
|
return e |
|
|
|
handler = self._find_error_handler(e) |
|
if handler is None: |
|
return e |
|
return handler(e) |
|
|
|
def trap_http_exception(self, e): |
|
"""Checks if an HTTP exception should be trapped or not. By default |
|
this will return ``False`` for all exceptions except for a bad request |
|
key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It |
|
also returns ``True`` if ``TRAP_HTTP_EXCEPTIONS`` is set to ``True``. |
|
|
|
This is called for all HTTP exceptions raised by a view function. |
|
If it returns ``True`` for any exception the error handler for this |
|
exception is not called and it shows up as regular exception in the |
|
traceback. This is helpful for debugging implicitly raised HTTP |
|
exceptions. |
|
|
|
.. versionadded:: 0.8 |
|
""" |
|
if self.config['TRAP_HTTP_EXCEPTIONS']: |
|
return True |
|
if self.config['TRAP_BAD_REQUEST_ERRORS']: |
|
return isinstance(e, BadRequest) |
|
return False |
|
|
|
def handle_user_exception(self, e): |
|
"""This method is called whenever an exception occurs that should be |
|
handled. A special case are |
|
:class:`~werkzeug.exception.HTTPException`\s which are forwarded by |
|
this function to the :meth:`handle_http_exception` method. This |
|
function will either return a response value or reraise the |
|
exception with the same traceback. |
|
|
|
.. versionadded:: 0.7 |
|
""" |
|
exc_type, exc_value, tb = sys.exc_info() |
|
assert exc_value is e |
|
|
|
# ensure not to trash sys.exc_info() at that point in case someone |
|
# wants the traceback preserved in handle_http_exception. Of course |
|
# we cannot prevent users from trashing it themselves in a custom |
|
# trap_http_exception method so that's their fault then. |
|
|
|
if isinstance(e, HTTPException) and not self.trap_http_exception(e): |
|
return self.handle_http_exception(e) |
|
|
|
handler = self._find_error_handler(e) |
|
|
|
if handler is None: |
|
reraise(exc_type, exc_value, tb) |
|
return handler(e) |
|
|
|
def handle_exception(self, e): |
|
"""Default exception handling that kicks in when an exception |
|
occurs that is not caught. In debug mode the exception will |
|
be re-raised immediately, otherwise it is logged and the handler |
|
for a 500 internal server error is used. If no such handler |
|
exists, a default 500 internal server error message is displayed. |
|
|
|
.. versionadded:: 0.3 |
|
""" |
|
exc_type, exc_value, tb = sys.exc_info() |
|
|
|
got_request_exception.send(self, exception=e) |
|
handler = self._find_error_handler(InternalServerError()) |
|
|
|
if self.propagate_exceptions: |
|
# if we want to repropagate the exception, we can attempt to |
|
# raise it with the whole traceback in case we can do that |
|
# (the function was actually called from the except part) |
|
# otherwise, we just raise the error again |
|
if exc_value is e: |
|
reraise(exc_type, exc_value, tb) |
|
else: |
|
raise e |
|
|
|
self.log_exception((exc_type, exc_value, tb)) |
|
if handler is None: |
|
return InternalServerError() |
|
return self.finalize_request(handler(e), from_error_handler=True) |
|
|
|
def log_exception(self, exc_info): |
|
"""Logs an exception. This is called by :meth:`handle_exception` |
|
if debugging is disabled and right before the handler is called. |
|
The default implementation logs the exception as error on the |
|
:attr:`logger`. |
|
|
|
.. versionadded:: 0.8 |
|
""" |
|
self.logger.error('Exception on %s [%s]' % ( |
|
request.path, |
|
request.method |
|
), exc_info=exc_info) |
|
|
|
def raise_routing_exception(self, request): |
|
"""Exceptions that are recording during routing are reraised with |
|
this method. During debug we are not reraising redirect requests |
|
for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising |
|
a different error instead to help debug situations. |
|
|
|
:internal: |
|
""" |
|
if not self.debug \ |
|
or not isinstance(request.routing_exception, RequestRedirect) \ |
|
or request.method in ('GET', 'HEAD', 'OPTIONS'): |
|
raise request.routing_exception |
|
|
|
from .debughelpers import FormDataRoutingRedirect |
|
raise FormDataRoutingRedirect(request) |
|
|
|
def dispatch_request(self): |
|
"""Does the request dispatching. Matches the URL and returns the |
|
return value of the view or error handler. This does not have to |
|
be a response object. In order to convert the return value to a |
|
proper response object, call :func:`make_response`. |
|
|
|
.. versionchanged:: 0.7 |
|
This no longer does the exception handling, this code was |
|
moved to the new :meth:`full_dispatch_request`. |
|
""" |
|
req = _request_ctx_stack.top.request |
|
if req.routing_exception is not None: |
|
self.raise_routing_exception(req) |
|
rule = req.url_rule |
|
# if we provide automatic options for this URL and the |
|
# request came with the OPTIONS method, reply automatically |
|
if getattr(rule, 'provide_automatic_options', False) \ |
|
and req.method == 'OPTIONS': |
|
return self.make_default_options_response() |
|
# otherwise dispatch to the handler for that endpoint |
|
return self.view_functions[rule.endpoint](**req.view_args) |
|
|
|
def full_dispatch_request(self): |
|
"""Dispatches the request and on top of that performs request |
|
pre and postprocessing as well as HTTP exception catching and |
|
error handling. |
|
|
|
.. versionadded:: 0.7 |
|
""" |
|
self.try_trigger_before_first_request_functions() |
|
try: |
|
request_started.send(self) |
|
rv = self.preprocess_request() |
|
if rv is None: |
|
rv = self.dispatch_request() |
|
except Exception as e: |
|
rv = self.handle_user_exception(e) |
|
return self.finalize_request(rv) |
|
|
|
def finalize_request(self, rv, from_error_handler=False): |
|
"""Given the return value from a view function this finalizes |
|
the request by converting it into a response and invoking the |
|
postprocessing functions. This is invoked for both normal |
|
request dispatching as well as error handlers. |
|
|
|
Because this means that it might be called as a result of a |
|
failure a special safe mode is available which can be enabled |
|
with the `from_error_handler` flag. If enabled, failures in |
|
response processing will be logged and otherwise ignored. |
|
|
|
:internal: |
|
""" |
|
response = self.make_response(rv) |
|
try: |
|
response = self.process_response(response) |
|
request_finished.send(self, response=response) |
|
except Exception: |
|
if not from_error_handler: |
|
raise |
|
self.logger.exception('Request finalizing failed with an ' |
|
'error while handling an error') |
|
return response |
|
|
|
def try_trigger_before_first_request_functions(self): |
|
"""Called before each request and will ensure that it triggers |
|
the :attr:`before_first_request_funcs` and only exactly once per |
|
application instance (which means process usually). |
|
|
|
:internal: |
|
""" |
|
if self._got_first_request: |
|
return |
|
with self._before_request_lock: |
|
if self._got_first_request: |
|
return |
|
for func in self.before_first_request_funcs: |
|
func() |
|
self._got_first_request = True |
|
|
|
def make_default_options_response(self): |
|
"""This method is called to create the default ``OPTIONS`` response. |
|
This can be changed through subclassing to change the default |
|
behavior of ``OPTIONS`` responses. |
|
|
|
.. versionadded:: 0.7 |
|
""" |
|
adapter = _request_ctx_stack.top.url_adapter |
|
if hasattr(adapter, 'allowed_methods'): |
|
methods = adapter.allowed_methods() |
|
else: |
|
# fallback for Werkzeug < 0.7 |
|
methods = [] |
|
try: |
|
adapter.match(method='--') |
|
except MethodNotAllowed as e: |
|
methods = e.valid_methods |
|
except HTTPException as e: |
|
pass |
|
rv = self.response_class() |
|
rv.allow.update(methods) |
|
return rv |
|
|
|
def should_ignore_error(self, error): |
|
"""This is called to figure out if an error should be ignored |
|
or not as far as the teardown system is concerned. If this |
|
function returns ``True`` then the teardown handlers will not be |
|
passed the error. |
|
|
|
.. versionadded:: 0.10 |
|
""" |
|
return False |
|
|
|
def make_response(self, rv): |
|
"""Converts the return value from a view function to a real |
|
response object that is an instance of :attr:`response_class`. |
|
|
|
The following types are allowed for `rv`: |
|
|
|
.. tabularcolumns:: |p{3.5cm}|p{9.5cm}| |
|
|
|
======================= =========================================== |
|
:attr:`response_class` the object is returned unchanged |
|
:class:`str` a response object is created with the |
|
string as body |
|
:class:`unicode` a response object is created with the |
|
string encoded to utf-8 as body |
|
a WSGI function the function is called as WSGI application |
|
and buffered as response object |
|
:class:`tuple` A tuple in the form ``(response, status, |
|
headers)`` or ``(response, headers)`` |
|
where `response` is any of the |
|
types defined here, `status` is a string |
|
or an integer and `headers` is a list or |
|
a dictionary with header values. |
|
======================= =========================================== |
|
|
|
:param rv: the return value from the view function |
|
|
|
.. versionchanged:: 0.9 |
|
Previously a tuple was interpreted as the arguments for the |
|
response object. |
|
""" |
|
status_or_headers = headers = None |
|
if isinstance(rv, tuple): |
|
rv, status_or_headers, headers = rv + (None,) * (3 - len(rv)) |
|
|
|
if rv is None: |
|
raise ValueError('View function did not return a response') |
|
|
|
if isinstance(status_or_headers, (dict, list)): |
|
headers, status_or_headers = status_or_headers, None |
|
|
|
if not isinstance(rv, self.response_class): |
|
# When we create a response object directly, we let the constructor |
|
# set the headers and status. We do this because there can be |
|
# some extra logic involved when creating these objects with |
|
# specific values (like default content type selection). |
|
if isinstance(rv, (text_type, bytes, bytearray)): |
|
rv = self.response_class(rv, headers=headers, |
|
status=status_or_headers) |
|
headers = status_or_headers = None |
|
else: |
|
rv = self.response_class.force_type(rv, request.environ) |
|
|
|
if status_or_headers is not None: |
|
if isinstance(status_or_headers, string_types): |
|
rv.status = status_or_headers |
|
else: |
|
rv.status_code = status_or_headers |
|
if headers: |
|
rv.headers.extend(headers) |
|
|
|
return rv |
|
|
|
def create_url_adapter(self, request): |
|
"""Creates a URL adapter for the given request. The URL adapter |
|
is created at a point where the request context is not yet set up |
|
so the request is passed explicitly. |
|
|
|
.. versionadded:: 0.6 |
|
|
|
.. versionchanged:: 0.9 |
|
This can now also be called without a request object when the |
|
URL adapter is created for the application context. |
|
""" |
|
if request is not None: |
|
return self.url_map.bind_to_environ(request.environ, |
|
server_name=self.config['SERVER_NAME']) |
|
# We need at the very least the server name to be set for this |
|
# to work. |
|
if self.config['SERVER_NAME'] is not None: |
|
return self.url_map.bind( |
|
self.config['SERVER_NAME'], |
|
script_name=self.config['APPLICATION_ROOT'] or '/', |
|
url_scheme=self.config['PREFERRED_URL_SCHEME']) |
|
|
|
def inject_url_defaults(self, endpoint, values): |
|
"""Injects the URL defaults for the given endpoint directly into |
|
the values dictionary passed. This is used internally and |
|
automatically called on URL building. |
|
|
|
.. versionadded:: 0.7 |
|
""" |
|
funcs = self.url_default_functions.get(None, ()) |
|
if '.' in endpoint: |
|
bp = endpoint.rsplit('.', 1)[0] |
|
funcs = chain(funcs, self.url_default_functions.get(bp, ())) |
|
for func in funcs: |
|
func(endpoint, values) |
|
|
|
def handle_url_build_error(self, error, endpoint, values): |
|
"""Handle :class:`~werkzeug.routing.BuildError` on :meth:`url_for`. |
|
""" |
|
exc_type, exc_value, tb = sys.exc_info() |
|
for handler in self.url_build_error_handlers: |
|
try: |
|
rv = handler(error, endpoint, values) |
|
if rv is not None: |
|
return rv |
|
except BuildError as e: |
|
# make error available outside except block (py3) |
|
error = e |
|
|
|
# At this point we want to reraise the exception. If the error is |
|
# still the same one we can reraise it with the original traceback, |
|
# otherwise we raise it from here. |
|
if error is exc_value: |
|
reraise(exc_type, exc_value, tb) |
|
raise error |
|
|
|
def preprocess_request(self): |
|
"""Called before the actual request dispatching and will |
|
call each :meth:`before_request` decorated function, passing no |
|
arguments. |
|
If any of these functions returns a value, it's handled as |
|
if it was the return value from the view and further |
|
request handling is stopped. |
|
|
|
This also triggers the :meth:`url_value_preprocessor` functions before |
|
the actual :meth:`before_request` functions are called. |
|
""" |
|
bp = _request_ctx_stack.top.request.blueprint |
|
|
|
funcs = self.url_value_preprocessors.get(None, ()) |
|
if bp is not None and bp in self.url_value_preprocessors: |
|
funcs = chain(funcs, self.url_value_preprocessors[bp]) |
|
for func in funcs: |
|
func(request.endpoint, request.view_args) |
|
|
|
funcs = self.before_request_funcs.get(None, ()) |
|
if bp is not None and bp in self.before_request_funcs: |
|
funcs = chain(funcs, self.before_request_funcs[bp]) |
|
for func in funcs: |
|
rv = func() |
|
if rv is not None: |
|
return rv |
|
|
|
def process_response(self, response): |
|
"""Can be overridden in order to modify the response object |
|
before it's sent to the WSGI server. By default this will |
|
call all the :meth:`after_request` decorated functions. |
|
|
|
.. versionchanged:: 0.5 |
|
As of Flask 0.5 the functions registered for after request |
|
execution are called in reverse order of registration. |
|
|
|
:param response: a :attr:`response_class` object. |
|
:return: a new response object or the same, has to be an |
|
instance of :attr:`response_class`. |
|
""" |
|
ctx = _request_ctx_stack.top |
|
bp = ctx.request.blueprint |
|
funcs = ctx._after_request_functions |
|
if bp is not None and bp in self.after_request_funcs: |
|
funcs = chain(funcs, reversed(self.after_request_funcs[bp])) |
|
if None in self.after_request_funcs: |
|
funcs = chain(funcs, reversed(self.after_request_funcs[None])) |
|
for handler in funcs: |
|
response = handler(response) |
|
if not self.session_interface.is_null_session(ctx.session): |
|
self.save_session(ctx.session, response) |
|
return response |
|
|
|
def do_teardown_request(self, exc=_sentinel): |
|
"""Called after the actual request dispatching and will |
|
call every as :meth:`teardown_request` decorated function. This is |
|
not actually called by the :class:`Flask` object itself but is always |
|
triggered when the request context is popped. That way we have a |
|
tighter control over certain resources under testing environments. |
|
|
|
.. versionchanged:: 0.9 |
|
Added the `exc` argument. Previously this was always using the |
|
current exception information. |
|
""" |
|
if exc is _sentinel: |
|
exc = sys.exc_info()[1] |
|
funcs = reversed(self.teardown_request_funcs.get(None, ())) |
|
bp = _request_ctx_stack.top.request.blueprint |
|
if bp is not None and bp in self.teardown_request_funcs: |
|
funcs = chain(funcs, reversed(self.teardown_request_funcs[bp])) |
|
for func in funcs: |
|
func(exc) |
|
request_tearing_down.send(self, exc=exc) |
|
|
|
def do_teardown_appcontext(self, exc=_sentinel): |
|
"""Called when an application context is popped. This works pretty |
|
much the same as :meth:`do_teardown_request` but for the application |
|
context. |
|
|
|
.. versionadded:: 0.9 |
|
""" |
|
if exc is _sentinel: |
|
exc = sys.exc_info()[1] |
|
for func in reversed(self.teardown_appcontext_funcs): |
|
func(exc) |
|
appcontext_tearing_down.send(self, exc=exc) |
|
|
|
def app_context(self): |
|
"""Binds the application only. For as long as the application is bound |
|
to the current context the :data:`flask.current_app` points to that |
|
application. An application context is automatically created when a |
|
request context is pushed if necessary. |
|
|
|
Example usage:: |
|
|
|
with app.app_context(): |
|
... |
|
|
|
.. versionadded:: 0.9 |
|
""" |
|
return AppContext(self) |
|
|
|
def request_context(self, environ): |
|
"""Creates a :class:`~flask.ctx.RequestContext` from the given |
|
environment and binds it to the current context. This must be used in |
|
combination with the ``with`` statement because the request is only bound |
|
to the current context for the duration of the ``with`` block. |
|
|
|
Example usage:: |
|
|
|
with app.request_context(environ): |
|
do_something_with(request) |
|
|
|
The object returned can also be used without the ``with`` statement |
|
which is useful for working in the shell. The example above is |
|
doing exactly the same as this code:: |
|
|
|
ctx = app.request_context(environ) |
|
ctx.push() |
|
try: |
|
do_something_with(request) |
|
finally: |
|
ctx.pop() |
|
|
|
.. versionchanged:: 0.3 |
|
Added support for non-with statement usage and ``with`` statement |
|
is now passed the ctx object. |
|
|
|
:param environ: a WSGI environment |
|
""" |
|
return RequestContext(self, environ) |
|
|
|
def test_request_context(self, *args, **kwargs): |
|
"""Creates a WSGI environment from the given values (see |
|
:class:`werkzeug.test.EnvironBuilder` for more information, this |
|
function accepts the same arguments). |
|
""" |
|
from flask.testing import make_test_environ_builder |
|
builder = make_test_environ_builder(self, *args, **kwargs) |
|
try: |
|
return self.request_context(builder.get_environ()) |
|
finally: |
|
builder.close() |
|
|
|
def wsgi_app(self, environ, start_response): |
|
"""The actual WSGI application. This is not implemented in |
|
`__call__` so that middlewares can be applied without losing a |
|
reference to the class. So instead of doing this:: |
|
|
|
app = MyMiddleware(app) |
|
|
|
It's a better idea to do this instead:: |
|
|
|
app.wsgi_app = MyMiddleware(app.wsgi_app) |
|
|
|
Then you still have the original application object around and |
|
can continue to call methods on it. |
|
|
|
.. versionchanged:: 0.7 |
|
The behavior of the before and after request callbacks was changed |
|
under error conditions and a new callback was added that will |
|
always execute at the end of the request, independent on if an |
|
error occurred or not. See :ref:`callbacks-and-errors`. |
|
|
|
:param environ: a WSGI environment |
|
:param start_response: a callable accepting a status code, |
|
a list of headers and an optional |
|
exception context to start the response |
|
""" |
|
ctx = self.request_context(environ) |
|
ctx.push() |
|
error = None |
|
try: |
|
try: |
|
response = self.full_dispatch_request() |
|
except Exception as e: |
|
error = e |
|
response = self.handle_exception(e) |
|
return response(environ, start_response) |
|
finally: |
|
if self.should_ignore_error(error): |
|
error = None |
|
ctx.auto_pop(error) |
|
|
|
def __call__(self, environ, start_response): |
|
"""Shortcut for :attr:`wsgi_app`.""" |
|
return self.wsgi_app(environ, start_response) |
|
|
|
def __repr__(self): |
|
return '<%s %r>' % ( |
|
self.__class__.__name__, |
|
self.name, |
|
)
|
|
|