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.
834 lines
34 KiB
834 lines
34 KiB
# -*- coding: utf-8 -*- |
|
""" |
|
flask.app |
|
~~~~~~~~~ |
|
|
|
This module implements the central WSGI application object. |
|
|
|
:copyright: (c) 2010 by Armin Ronacher. |
|
:license: BSD, see LICENSE for more details. |
|
""" |
|
|
|
import os |
|
from threading import Lock |
|
from datetime import timedelta, datetime |
|
from itertools import chain |
|
|
|
from jinja2 import Environment |
|
|
|
from werkzeug import ImmutableDict, create_environ |
|
from werkzeug.routing import Map, Rule |
|
from werkzeug.exceptions import HTTPException, InternalServerError, NotFound |
|
|
|
from flask.helpers import _PackageBoundObject, url_for, get_flashed_messages, \ |
|
_tojson_filter |
|
from flask.wrappers import Request, Response |
|
from flask.config import ConfigAttribute, Config |
|
from flask.ctx import _default_template_ctx_processor, _RequestContext |
|
from flask.globals import _request_ctx_stack, request |
|
from flask.session import Session, _NullSession |
|
from flask.module import _ModuleSetupState |
|
from flask.templating import _DispatchingJinjaLoader |
|
|
|
# a lock used for logger initialization |
|
_logger_lock = Lock() |
|
|
|
|
|
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 `__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 `__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 what |
|
belongs to your application. This name is used to find resources |
|
on the file system, 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 `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 `yourapplicaiton.app` and not |
|
`yourapplication.views.frontend`) |
|
""" |
|
|
|
#: 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 |
|
|
|
#: Path for the static files. If you don't want to use static files |
|
#: you can set this value to `None` in which case no URL rule is added |
|
#: and the development server will no longer serve any static files. |
|
#: |
|
#: This is the default used for application and modules unless a |
|
#: different value is passed to the constructor. |
|
static_path = '/static' |
|
|
|
#: 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 ocurrs 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 flask. 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. |
|
#: |
|
#: 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') |
|
|
|
#: 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 logging format used for the debug logger. This is only used when |
|
#: the application is in debug mode, otherwise the attached logging |
|
#: handler does the formatting. |
|
#: |
|
#: .. versionadded:: 0.3 |
|
debug_log_format = ( |
|
'-' * 80 + '\n' + |
|
'%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n' + |
|
'%(message)s\n' + |
|
'-' * 80 |
|
) |
|
|
|
#: 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': False, |
|
'TESTING': False, |
|
'SECRET_KEY': None, |
|
'SESSION_COOKIE_NAME': 'session', |
|
'PERMANENT_SESSION_LIFETIME': timedelta(days=31), |
|
'USE_X_SENDFILE': False, |
|
'LOGGER_NAME': None, |
|
'SERVER_NAME': None |
|
}) |
|
|
|
def __init__(self, import_name, static_path=None): |
|
_PackageBoundObject.__init__(self, import_name) |
|
if static_path is not None: |
|
self.static_path = static_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 = Config(self.root_path, self.default_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 = {} |
|
|
|
#: A dictionary of all registered error handlers. The key is |
|
#: be the error code as integer, the value the function that |
|
#: should handle that error. |
|
#: To register a error handler, use the :meth:`errorhandler` |
|
#: decorator. |
|
self.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 module 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 dictionary with lists of functions that should be called after |
|
#: each request. The key of the dictionary is the name of the module |
|
#: 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.after_request_funcs = {} |
|
|
|
#: A dictionary with list of functions that are called without argument |
|
#: to populate the template context. They key of the dictionary is the |
|
#: name of the module 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] |
|
} |
|
|
|
#: all the loaded modules in a dictionary by name. |
|
#: |
|
#: .. versionadded:: 0.5 |
|
self.modules = {} |
|
|
|
#: 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 import BaseConverter |
|
#: |
|
#: class ListConverter(BaseConverter): |
|
#: def to_python(self, value): |
|
#: return value.split(',') |
|
#: def to_url(self, values): |
|
#: return ','.join(BaseConverter.to_url(value) |
|
#: for value in values) |
|
#: |
|
#: app = Flask(__name__) |
|
#: app.url_map.converters['list'] = ListConverter |
|
self.url_map = Map() |
|
|
|
# if there is a static folder, register it for the application. |
|
if self.has_static_folder: |
|
self.add_url_rule(self.static_path + '/<filename>', |
|
endpoint='static', |
|
view_func=self.send_static_file) |
|
|
|
#: The Jinja2 environment. It is created from the |
|
#: :attr:`jinja_options`. |
|
self.jinja_env = self.create_jinja_environment() |
|
self.init_jinja_globals() |
|
|
|
@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 ocurred (%d apples)', 42) |
|
app.logger.error('An error occoured') |
|
|
|
.. 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 logging import getLogger, StreamHandler, Formatter, \ |
|
Logger, DEBUG |
|
class DebugLogger(Logger): |
|
def getEffectiveLevel(x): |
|
return DEBUG if self.debug else Logger.getEffectiveLevel(x) |
|
class DebugHandler(StreamHandler): |
|
def emit(x, record): |
|
StreamHandler.emit(x, record) if self.debug else None |
|
handler = DebugHandler() |
|
handler.setLevel(DEBUG) |
|
handler.setFormatter(Formatter(self.debug_log_format)) |
|
logger = getLogger(self.logger_name) |
|
logger.__class__ = DebugLogger |
|
logger.addHandler(handler) |
|
self._logger = logger |
|
return logger |
|
|
|
def create_jinja_environment(self): |
|
"""Creates the Jinja2 environment based on :attr:`jinja_options` |
|
and :meth:`create_jinja_loader`. |
|
|
|
.. versionadded:: 0.5 |
|
""" |
|
options = dict(self.jinja_options) |
|
if 'autoescape' not in options: |
|
options['autoescape'] = self.select_jinja_autoescape |
|
return Environment(loader=_DispatchingJinjaLoader(self), **options) |
|
|
|
def init_jinja_globals(self): |
|
"""Called directly after the environment was created to inject |
|
some defaults (like `url_for`, `get_flashed_messages` and the |
|
`tojson` filter. |
|
|
|
.. versionadded:: 0.5 |
|
""" |
|
self.jinja_env.globals.update( |
|
url_for=url_for, |
|
get_flashed_messages=get_flashed_messages |
|
) |
|
self.jinja_env.filters['tojson'] = _tojson_filter |
|
|
|
def select_jinja_autoescape(self, filename): |
|
"""Returns `True` if autoescaping should be active for the given |
|
template name. |
|
|
|
.. versionadded:: 0.5 |
|
""" |
|
if filename is None: |
|
return False |
|
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 and g into the template context. |
|
|
|
:param context: the context as a dictionary that is updated in place |
|
to add extra variables. |
|
""" |
|
funcs = self.template_context_processors[None] |
|
mod = _request_ctx_stack.top.request.module |
|
if mod is not None and mod in self.template_context_processors: |
|
funcs = chain(funcs, self.template_context_processors[mod]) |
|
for func in funcs: |
|
context.update(func()) |
|
|
|
def run(self, host='127.0.0.1', port=5000, **options): |
|
"""Runs the application on a local development server. If the |
|
:attr:`debug` flag is set the server will automatically reload |
|
for code changes and show a debugger in case an exception happened. |
|
|
|
.. admonition:: Keep in Mind |
|
|
|
Flask will supress 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 ahve 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. |
|
|
|
:param host: the hostname to listen on. set this to ``'0.0.0.0'`` |
|
to have the server available externally as well. |
|
:param port: the port of the webserver |
|
:param options: the options to be forwarded to the underlying |
|
Werkzeug server. See :func:`werkzeug.run_simple` |
|
for more information. |
|
""" |
|
from werkzeug import run_simple |
|
if 'debug' in options: |
|
self.debug = options.pop('debug') |
|
options.setdefault('use_reloader', self.debug) |
|
options.setdefault('use_debugger', self.debug) |
|
return run_simple(host, port, self, **options) |
|
|
|
def test_client(self): |
|
"""Creates a test client for this application. For information |
|
about unit testing head over to :ref:`testing`. |
|
|
|
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' |
|
|
|
.. versionchanged:: 0.4 |
|
added support for `with` block usage for the client. |
|
""" |
|
from werkzeug import Client |
|
class FlaskClient(Client): |
|
preserve_context = context_preserved = False |
|
def open(self, *args, **kwargs): |
|
if self.context_preserved: |
|
_request_ctx_stack.pop() |
|
self.context_preserved = False |
|
kwargs.setdefault('environ_overrides', {}) \ |
|
['flask._preserve_context'] = self.preserve_context |
|
old = _request_ctx_stack.top |
|
try: |
|
return Client.open(self, *args, **kwargs) |
|
finally: |
|
self.context_preserved = _request_ctx_stack.top is not old |
|
def __enter__(self): |
|
self.preserve_context = True |
|
return self |
|
def __exit__(self, exc_type, exc_value, tb): |
|
self.preserve_context = False |
|
if self.context_preserved: |
|
_request_ctx_stack.pop() |
|
return FlaskClient(self, self.response_class, use_cookies=True) |
|
|
|
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. |
|
|
|
:param request: an instance of :attr:`request_class`. |
|
""" |
|
key = self.secret_key |
|
if key is not None: |
|
return Session.load_cookie(request, self.session_cookie_name, |
|
secret_key=key) |
|
|
|
def save_session(self, session, response): |
|
"""Saves the session if it needs updates. For the default |
|
implementation, check :meth:`open_session`. |
|
|
|
:param session: the session to be saved (a |
|
:class:`~werkzeug.contrib.securecookie.SecureCookie` |
|
object) |
|
:param response: an instance of :attr:`response_class` |
|
""" |
|
expires = None |
|
if session.permanent: |
|
expires = datetime.utcnow() + self.permanent_session_lifetime |
|
session.save_cookie(response, self.session_cookie_name, |
|
expires=expires, httponly=True) |
|
|
|
def register_module(self, module, **options): |
|
"""Registers a module with this application. The keyword argument |
|
of this function are the same as the ones for the constructor of the |
|
:class:`Module` class and will override the values of the module if |
|
provided. |
|
""" |
|
options.setdefault('url_prefix', module.url_prefix) |
|
state = _ModuleSetupState(self, **options) |
|
for func in module._register_events: |
|
func(state) |
|
|
|
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 |
|
|
|
.. versionchanged:: 0.2 |
|
`view_func` parameter added. |
|
|
|
: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 |
|
""" |
|
if endpoint is None: |
|
assert view_func is not None, 'expected view func if endpoint ' \ |
|
'is not provided.' |
|
endpoint = view_func.__name__ |
|
options['endpoint'] = endpoint |
|
options.setdefault('methods', ('GET',)) |
|
self.url_map.add(Rule(rule, **options)) |
|
if view_func is not None: |
|
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. Example:: |
|
|
|
@app.route('/') |
|
def index(): |
|
return 'Hello World' |
|
|
|
Variables parts in the route can be specified with angular |
|
brackets (``/user/<username>``). By default a variable part |
|
in the URL accepts any string without a slash however a different |
|
converter can be specified as well by using ``<converter:name>``. |
|
|
|
Variable parts are passed to the view function as keyword |
|
arguments. |
|
|
|
The following converters are possible: |
|
|
|
=========== =========================================== |
|
`int` accepts integers |
|
`float` like `int` but for floating point values |
|
`path` like the default but also accepts slashes |
|
=========== =========================================== |
|
|
|
Here some examples:: |
|
|
|
@app.route('/') |
|
def index(): |
|
pass |
|
|
|
@app.route('/<username>') |
|
def show_user(username): |
|
pass |
|
|
|
@app.route('/post/<int:post_id>') |
|
def show_post(post_id): |
|
pass |
|
|
|
An important detail to keep in mind is how Flask deals with trailing |
|
slashes. The idea is to keep each URL unique so the following rules |
|
apply: |
|
|
|
1. If a rule ends with a slash and is requested without a slash |
|
by the user, the user is automatically redirected to the same |
|
page with a trailing slash attached. |
|
2. If a rule does not end with a trailing slash and the user request |
|
the page with a trailing slash, a 404 not found is raised. |
|
|
|
This is consistent with how web servers deal with static files. This |
|
also makes it possible to use relative link targets safely. |
|
|
|
The :meth:`route` decorator accepts a couple of other arguments |
|
as well: |
|
|
|
:param rule: the URL rule as string |
|
:param methods: a list of methods this rule should be limited |
|
to (``GET``, ``POST`` etc.). By default a rule |
|
just listens for ``GET`` (and implicitly ``HEAD``). |
|
:param subdomain: specifies the rule for the subdomain in case |
|
subdomain matching is in use. |
|
:param strict_slashes: can be used to disable the strict slashes |
|
setting for this rule. See above. |
|
:param options: other options to be forwarded to the underlying |
|
:class:`~werkzeug.routing.Rule` object. |
|
""" |
|
def decorator(f): |
|
self.add_url_rule(rule, None, f, **options) |
|
return f |
|
return decorator |
|
|
|
def errorhandler(self, code): |
|
"""A decorator that is used to register a function give a given |
|
error code. Example:: |
|
|
|
@app.errorhandler(404) |
|
def page_not_found(error): |
|
return 'This page does not exist', 404 |
|
|
|
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_handlers[404] = page_not_found |
|
|
|
:param code: the code as integer for the handler |
|
""" |
|
def decorator(f): |
|
self.error_handlers[code] = f |
|
return f |
|
return decorator |
|
|
|
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.jinja_env.filters[name or f.__name__] = f |
|
return f |
|
return decorator |
|
|
|
def before_request(self, f): |
|
"""Registers a function to run before each request.""" |
|
self.before_request_funcs.setdefault(None, []).append(f) |
|
return f |
|
|
|
def after_request(self, f): |
|
"""Register a function to be run after each request.""" |
|
self.after_request_funcs.setdefault(None, []).append(f) |
|
return f |
|
|
|
def context_processor(self, f): |
|
"""Registers a template context processor function.""" |
|
self.template_context_processors[None].append(f) |
|
return f |
|
|
|
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 |
|
""" |
|
handler = self.error_handlers.get(e.code) |
|
if handler is None: |
|
return e |
|
return handler(e) |
|
|
|
def handle_exception(self, e): |
|
"""Default exception handling that kicks in when an exception |
|
occours that is not catched. 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 |
|
""" |
|
handler = self.error_handlers.get(500) |
|
if self.debug: |
|
raise |
|
self.logger.exception('Exception on %s [%s]' % ( |
|
request.path, |
|
request.method |
|
)) |
|
if handler is None: |
|
return InternalServerError() |
|
return handler(e) |
|
|
|
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`. |
|
""" |
|
req = _request_ctx_stack.top.request |
|
try: |
|
if req.routing_exception is not None: |
|
raise req.routing_exception |
|
return self.view_functions[req.endpoint](**req.view_args) |
|
except HTTPException, e: |
|
return self.handle_http_exception(e) |
|
|
|
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 |
|
:class:`tuple` the response object is created with the |
|
contents of the tuple as arguments |
|
a WSGI function the function is called as WSGI application |
|
and buffered as response object |
|
======================= =========================================== |
|
|
|
:param rv: the return value from the view function |
|
""" |
|
if rv is None: |
|
raise ValueError('View function did not return a response') |
|
if isinstance(rv, self.response_class): |
|
return rv |
|
if isinstance(rv, basestring): |
|
return self.response_class(rv) |
|
if isinstance(rv, tuple): |
|
return self.response_class(*rv) |
|
return self.response_class.force_type(rv, request.environ) |
|
|
|
def preprocess_request(self): |
|
"""Called before the actual request dispatching and will |
|
call every as :meth:`before_request` decorated function. |
|
If any of these function returns a value it's handled as |
|
if it was the return value from the view and further |
|
request handling is stopped. |
|
""" |
|
funcs = self.before_request_funcs.get(None, ()) |
|
mod = request.module |
|
if mod and mod in self.before_request_funcs: |
|
funcs = chain(funcs, self.before_request_funcs[mod]) |
|
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. |
|
|
|
: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 |
|
mod = ctx.request.module |
|
if not isinstance(ctx.session, _NullSession): |
|
self.save_session(ctx.session, response) |
|
funcs = () |
|
if mod and mod in self.after_request_funcs: |
|
funcs = chain(funcs, self.after_request_funcs[mod]) |
|
if None in self.after_request_funcs: |
|
funcs = chain(funcs, self.after_request_funcs[None]) |
|
for handler in funcs: |
|
response = handler(response) |
|
return response |
|
|
|
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.4 |
|
The :meth:`after_request` functions are now called even if an |
|
error handler took over request processing. This ensures that |
|
even if an exception happens database have the chance to |
|
properly close the connection. |
|
|
|
: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 |
|
""" |
|
with self.request_context(environ): |
|
try: |
|
rv = self.preprocess_request() |
|
if rv is None: |
|
rv = self.dispatch_request() |
|
response = self.make_response(rv) |
|
except Exception, e: |
|
response = self.make_response(self.handle_exception(e)) |
|
try: |
|
response = self.process_response(response) |
|
except Exception, e: |
|
response = self.make_response(self.handle_exception(e)) |
|
return response(environ, start_response) |
|
|
|
def request_context(self, environ): |
|
"""Creates a request context 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() |
|
|
|
The big advantage of this approach is that you can use it without |
|
the try/finally statement in a shell for interactive testing: |
|
|
|
>>> ctx = app.test_request_context() |
|
>>> ctx.bind() |
|
>>> request.path |
|
u'/' |
|
>>> ctx.unbind() |
|
|
|
.. 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 |
|
:func:`werkzeug.create_environ` for more information, this |
|
function accepts the same arguments). |
|
""" |
|
return self.request_context(create_environ(*args, **kwargs)) |
|
|
|
def __call__(self, environ, start_response): |
|
"""Shortcut for :attr:`wsgi_app`.""" |
|
return self.wsgi_app(environ, start_response) |
|
|
|
|