|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
|
|
flask
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
A microframework based on Werkzeug. It's extensively documented
|
|
|
|
and follows best practice patterns.
|
|
|
|
|
|
|
|
:copyright: (c) 2010 by Armin Ronacher.
|
|
|
|
:license: BSD, see LICENSE for more details.
|
|
|
|
"""
|
|
|
|
from __future__ import with_statement
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import mimetypes
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
|
|
|
from itertools import chain
|
|
|
|
from jinja2 import Environment, PackageLoader, FileSystemLoader
|
|
|
|
from werkzeug import Request as RequestBase, Response as ResponseBase, \
|
|
|
|
LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \
|
|
|
|
ImmutableDict, cached_property, wrap_file, Headers
|
|
|
|
from werkzeug.routing import Map, Rule
|
|
|
|
from werkzeug.exceptions import HTTPException
|
|
|
|
from werkzeug.contrib.securecookie import SecureCookie
|
|
|
|
|
|
|
|
# try to load the best simplejson implementation available. If JSON
|
|
|
|
# is not installed, we add a failing class.
|
|
|
|
json_available = True
|
|
|
|
try:
|
|
|
|
import simplejson as json
|
|
|
|
except ImportError:
|
|
|
|
try:
|
|
|
|
import json
|
|
|
|
except ImportError:
|
|
|
|
json_available = False
|
|
|
|
|
|
|
|
# utilities we import from Werkzeug and Jinja2 that are unused
|
|
|
|
# in the module but are exported as public interface.
|
|
|
|
from werkzeug import abort, redirect
|
|
|
|
from jinja2 import Markup, escape
|
|
|
|
|
|
|
|
# use pkg_resource if that works, otherwise fall back to cwd. The
|
|
|
|
# current working directory is generally not reliable with the notable
|
|
|
|
# exception of google appengine.
|
|
|
|
try:
|
|
|
|
import pkg_resources
|
|
|
|
pkg_resources.resource_stream
|
|
|
|
except (ImportError, AttributeError):
|
|
|
|
pkg_resources = None
|
|
|
|
|
|
|
|
|
|
|
|
class Request(RequestBase):
|
|
|
|
"""The request object used by default in flask. Remembers the
|
|
|
|
matched endpoint and view arguments.
|
|
|
|
|
|
|
|
It is what ends up as :class:`~flask.request`. If you want to replace
|
|
|
|
the request object used you can subclass this and set
|
|
|
|
:attr:`~flask.Flask.request_class` to your subclass.
|
|
|
|
"""
|
|
|
|
|
|
|
|
endpoint = view_args = routing_exception = None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def module(self):
|
|
|
|
"""The name of the current module"""
|
|
|
|
if self.endpoint and '.' in self.endpoint:
|
|
|
|
return self.endpoint.rsplit('.', 1)[0]
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def json(self):
|
|
|
|
"""If the mimetype is `application/json` this will contain the
|
|
|
|
parsed JSON data.
|
|
|
|
"""
|
|
|
|
if __debug__:
|
|
|
|
_assert_have_json()
|
|
|
|
if self.mimetype == 'application/json':
|
|
|
|
return json.loads(self.data)
|
|
|
|
|
|
|
|
|
|
|
|
class Response(ResponseBase):
|
|
|
|
"""The response object that is used by default in flask. Works like the
|
|
|
|
response object from Werkzeug but is set to have a HTML mimetype by
|
|
|
|
default. Quite often you don't have to create this object yourself because
|
|
|
|
:meth:`~flask.Flask.make_response` will take care of that for you.
|
|
|
|
|
|
|
|
If you want to replace the response object used you can subclass this and
|
|
|
|
set :attr:`~flask.Flask.response_class` to your subclass.
|
|
|
|
"""
|
|
|
|
default_mimetype = 'text/html'
|
|
|
|
|
|
|
|
|
|
|
|
class _RequestGlobals(object):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Session(SecureCookie):
|
|
|
|
"""Expands the session with support for switching between permanent
|
|
|
|
and non-permanent sessions.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def _get_permanent(self):
|
|
|
|
return self.get('_permanent', False)
|
|
|
|
|
|
|
|
def _set_permanent(self, value):
|
|
|
|
self['_permanent'] = bool(value)
|
|
|
|
|
|
|
|
permanent = property(_get_permanent, _set_permanent)
|
|
|
|
del _get_permanent, _set_permanent
|
|
|
|
|
|
|
|
|
|
|
|
class _NullSession(Session):
|
|
|
|
"""Class used to generate nicer error messages if sessions are not
|
|
|
|
available. Will still allow read-only access to the empty session
|
|
|
|
but fail on setting.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def _fail(self, *args, **kwargs):
|
|
|
|
raise RuntimeError('the session is unavailable because no secret '
|
|
|
|
'key was set. Set the secret_key on the '
|
|
|
|
'application to something unique and secret')
|
|
|
|
__setitem__ = __delitem__ = clear = pop = popitem = \
|
|
|
|
update = setdefault = _fail
|
|
|
|
del _fail
|
|
|
|
|
|
|
|
|
|
|
|
class _RequestContext(object):
|
|
|
|
"""The request context contains all request relevant information. It is
|
|
|
|
created at the beginning of the request and pushed to the
|
|
|
|
`_request_ctx_stack` and removed at the end of it. It will create the
|
|
|
|
URL adapter and request object for the WSGI environment provided.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, app, environ):
|
|
|
|
self.app = app
|
|
|
|
self.url_adapter = app.url_map.bind_to_environ(environ)
|
|
|
|
self.request = app.request_class(environ)
|
|
|
|
self.session = app.open_session(self.request)
|
|
|
|
if self.session is None:
|
|
|
|
self.session = _NullSession()
|
|
|
|
self.g = _RequestGlobals()
|
|
|
|
self.flashes = None
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.request.endpoint, self.request.view_args = \
|
|
|
|
self.url_adapter.match()
|
|
|
|
except HTTPException, e:
|
|
|
|
self.request.routing_exception = e
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
_request_ctx_stack.push(self)
|
|
|
|
|
|
|
|
def __exit__(self, exc_type, exc_value, tb):
|
|
|
|
# do not pop the request stack if we are in debug mode and an
|
|
|
|
# exception happened. This will allow the debugger to still
|
|
|
|
# access the request object in the interactive shell.
|
|
|
|
if tb is None or not self.app.debug:
|
|
|
|
_request_ctx_stack.pop()
|
|
|
|
|
|
|
|
|
|
|
|
def url_for(endpoint, **values):
|
|
|
|
"""Generates a URL to the given endpoint with the method provided.
|
|
|
|
The endpoint is relative to the active module if modules are in use.
|
|
|
|
|
|
|
|
Here some examples:
|
|
|
|
|
|
|
|
==================== ======================= =============================
|
|
|
|
Active Module Target Endpoint Target Function
|
|
|
|
==================== ======================= =============================
|
|
|
|
`None` ``'index'`` `index` of the application
|
|
|
|
`None` ``'.index'`` `index` of the application
|
|
|
|
``'admin'`` ``'index'`` `index` of the `admin` module
|
|
|
|
any ``'.index'`` `index` of the application
|
|
|
|
any ``'admin.index'`` `index` of the `admin` module
|
|
|
|
==================== ======================= =============================
|
|
|
|
|
|
|
|
Variable arguments that are unknown to the target endpoint are appended
|
|
|
|
to the generated URL as query arguments.
|
|
|
|
|
|
|
|
For more information, head over to the :ref:`Quickstart <url-building>`.
|
|
|
|
|
|
|
|
:param endpoint: the endpoint of the URL (name of the function)
|
|
|
|
:param values: the variable arguments of the URL rule
|
|
|
|
:param _external: if set to `True`, an absolute URL is generated.
|
|
|
|
"""
|
|
|
|
ctx = _request_ctx_stack.top
|
|
|
|
if '.' not in endpoint:
|
|
|
|
mod = ctx.request.module
|
|
|
|
if mod is not None:
|
|
|
|
endpoint = mod + '.' + endpoint
|
|
|
|
elif endpoint.startswith('.'):
|
|
|
|
endpoint = endpoint[1:]
|
|
|
|
external = values.pop('_external', False)
|
|
|
|
return ctx.url_adapter.build(endpoint, values, force_external=external)
|
|
|
|
|
|
|
|
|
|
|
|
def get_template_attribute(template_name, attribute):
|
|
|
|
"""Loads a macro (or variable) a template exports. This can be used to
|
|
|
|
invoke a macro from within Python code. If you for example have a
|
|
|
|
template named `_foo.html` with the following contents:
|
|
|
|
|
|
|
|
.. sourcecode:: html+jinja
|
|
|
|
|
|
|
|
{% macro hello(name) %}Hello {{ name }}!{% endmacro %}
|
|
|
|
|
|
|
|
You can access this from Python code like this::
|
|
|
|
|
|
|
|
hello = get_template_attribute('_foo.html', 'hello')
|
|
|
|
return hello('World')
|
|
|
|
|
|
|
|
.. versionadded:: 0.2
|
|
|
|
|
|
|
|
:param template_name: the name of the template
|
|
|
|
:param attribute: the name of the variable of macro to acccess
|
|
|
|
"""
|
|
|
|
return getattr(current_app.jinja_env.get_template(template_name).module,
|
|
|
|
attribute)
|
|
|
|
|
|
|
|
|
|
|
|
def flash(message, category='message'):
|
|
|
|
"""Flashes a message to the next request. In order to remove the
|
|
|
|
flashed message from the session and to display it to the user,
|
|
|
|
the template has to call :func:`get_flashed_messages`.
|
|
|
|
|
|
|
|
.. versionchanged: 0.5
|
|
|
|
`category` parameter added.
|
|
|
|
|
|
|
|
:param message: the message to be flashed.
|
|
|
|
:param category: the category for the message. The following values
|
|
|
|
are recommended: ``'message'`` for any kind of message,
|
|
|
|
``'error'`` for errors, ``'info'`` for information
|
|
|
|
messages and ``'warning'`` for warnings. However any
|
|
|
|
kind of string can be used as category.
|
|
|
|
"""
|
|
|
|
session.setdefault('_flashes', []).append((category, message))
|
|
|
|
|
|
|
|
|
|
|
|
def get_flashed_messages(with_categories=False):
|
|
|
|
"""Pulls all flashed messages from the session and returns them.
|
|
|
|
Further calls in the same request to the function will return
|
|
|
|
the same messages. By default just the messages are returned,
|
|
|
|
but when `with_categories` is set to `True`, the return value will
|
|
|
|
be a list of tuples in the form ``(category, message)`` instead.
|
|
|
|
|
|
|
|
Example usage:
|
|
|
|
|
|
|
|
.. sourcecode:: html+jinja
|
|
|
|
|
|
|
|
{% for category, msg in get_flashed_messages(with_categories=true) %}
|
|
|
|
<p class=flash-{{ category }}>{{ msg }}
|
|
|
|
{% endfor %}
|
|
|
|
|
|
|
|
.. versionchanged:: 0.5
|
|
|
|
`with_categories` parameter added.
|
|
|
|
|
|
|
|
:param with_categories: set to `True` to also receive categories.
|
|
|
|
"""
|
|
|
|
flashes = _request_ctx_stack.top.flashes
|
|
|
|
if flashes is None:
|
|
|
|
_request_ctx_stack.top.flashes = flashes = session.pop('_flashes', [])
|
|
|
|
if not with_categories:
|
|
|
|
return [x[1] for x in flashes]
|
|
|
|
return flashes
|
|
|
|
|
|
|
|
|
|
|
|
def jsonify(*args, **kwargs):
|
|
|
|
"""Creates a :class:`~flask.Response` with the JSON representation of
|
|
|
|
the given arguments with an `application/json` mimetype. The arguments
|
|
|
|
to this function are the same as to the :class:`dict` constructor.
|
|
|
|
|
|
|
|
Example usage::
|
|
|
|
|
|
|
|
@app.route('/_get_current_user')
|
|
|
|
def get_current_user():
|
|
|
|
return jsonify(username=g.user.username,
|
|
|
|
email=g.user.email,
|
|
|
|
id=g.user.id)
|
|
|
|
|
|
|
|
This will send a JSON response like this to the browser::
|
|
|
|
|
|
|
|
{
|
|
|
|
"username": "admin",
|
|
|
|
"email": "admin@localhost",
|
|
|
|
"id": 42
|
|
|
|
}
|
|
|
|
|
|
|
|
This requires Python 2.6 or an installed version of simplejson.
|
|
|
|
|
|
|
|
.. versionadded:: 0.2
|
|
|
|
"""
|
|
|
|
if __debug__:
|
|
|
|
_assert_have_json()
|
|
|
|
return current_app.response_class(json.dumps(dict(*args, **kwargs),
|
|
|
|
indent=None if request.is_xhr else 2), mimetype='application/json')
|
|
|
|
|
|
|
|
|
|
|
|
def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|
|
|
attachment_filename=None):
|
|
|
|
"""Sends the contents of a file to the client. This will use the
|
|
|
|
most efficient method available and configured. By default it will
|
|
|
|
try to use the WSGI server's file_wrapper support. Alternatively
|
|
|
|
you can set the application's :attr:`~Flask.use_x_sendfile` attribute
|
|
|
|
to ``True`` to directly emit an `X-Sendfile` header. This however
|
|
|
|
requires support of the underlying webserver for `X-Sendfile`.
|
|
|
|
|
|
|
|
By default it will try to guess the mimetype for you, but you can
|
|
|
|
also explicitly provide one. For extra security you probably want
|
|
|
|
to sent certain files as attachment (HTML for instance).
|
|
|
|
|
|
|
|
Please never pass filenames to this function from user sources without
|
|
|
|
checking them first. Something like this is usually sufficient to
|
|
|
|
avoid security problems::
|
|
|
|
|
|
|
|
if '..' in filename or filename.startswith('/'):
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
.. versionadded:: 0.2
|
|
|
|
|
|
|
|
:param filename_or_fp: the filename of the file to send. This is
|
|
|
|
relative to the :attr:`~Flask.root_path` if a
|
|
|
|
relative path is specified.
|
|
|
|
Alternatively a file object might be provided
|
|
|
|
in which case `X-Sendfile` might not work and
|
|
|
|
fall back to the traditional method.
|
|
|
|
:param mimetype: the mimetype of the file if provided, otherwise
|
|
|
|
auto detection happens.
|
|
|
|
:param as_attachment: set to `True` if you want to send this file with
|
|
|
|
a ``Content-Disposition: attachment`` header.
|
|
|
|
:param attachment_filename: the filename for the attachment if it
|
|
|
|
differs from the file's filename.
|
|
|
|
"""
|
|
|
|
if isinstance(filename_or_fp, basestring):
|
|
|
|
filename = filename_or_fp
|
|
|
|
file = None
|
|
|
|
else:
|
|
|
|
file = filename_or_fp
|
|
|
|
filename = getattr(file, 'name', None)
|
|
|
|
if filename is not None:
|
|
|
|
filename = os.path.join(current_app.root_path, filename)
|
|
|
|
if mimetype is None and (filename or attachment_filename):
|
|
|
|
mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
|
|
|
|
if mimetype is None:
|
|
|
|
mimetype = 'application/octet-stream'
|
|
|
|
|
|
|
|
headers = Headers()
|
|
|
|
if as_attachment:
|
|
|
|
if attachment_filename is None:
|
|
|
|
if filename is None:
|
|
|
|
raise TypeError('filename unavailable, required for '
|
|
|
|
'sending as attachment')
|
|
|
|
attachment_filename = os.path.basename(filename)
|
|
|
|
headers.add('Content-Disposition', 'attachment',
|
|
|
|
filename=attachment_filename)
|
|
|
|
|
|
|
|
if current_app.use_x_sendfile and filename:
|
|
|
|
if file is not None:
|
|
|
|
file.close()
|
|
|
|
headers['X-Sendfile'] = filename
|
|
|
|
data = None
|
|
|
|
else:
|
|
|
|
if file is None:
|
|
|
|
file = open(filename, 'rb')
|
|
|
|
data = wrap_file(request.environ, file)
|
|
|
|
|
|
|
|
return Response(data, mimetype=mimetype, headers=headers,
|
|
|
|
direct_passthrough=True)
|
|
|
|
|
|
|
|
|
|
|
|
def render_template(template_name, **context):
|
|
|
|
"""Renders a template from the template folder with the given
|
|
|
|
context.
|
|
|
|
|
|
|
|
:param template_name: the name of the template to be rendered
|
|
|
|
:param context: the variables that should be available in the
|
|
|
|
context of the template.
|
|
|
|
"""
|
|
|
|
current_app.update_template_context(context)
|
|
|
|
return current_app.jinja_env.get_template(template_name).render(context)
|
|
|
|
|
|
|
|
|
|
|
|
def render_template_string(source, **context):
|
|
|
|
"""Renders a template from the given template source string
|
|
|
|
with the given context.
|
|
|
|
|
|
|
|
:param template_name: the sourcecode of the template to be
|
|
|
|
rendered
|
|
|
|
:param context: the variables that should be available in the
|
|
|
|
context of the template.
|
|
|
|
"""
|
|
|
|
current_app.update_template_context(context)
|
|
|
|
return current_app.jinja_env.from_string(source).render(context)
|
|
|
|
|
|
|
|
|
|
|
|
def _default_template_ctx_processor():
|
|
|
|
"""Default template context processor. Injects `request`,
|
|
|
|
`session` and `g`.
|
|
|
|
"""
|
|
|
|
reqctx = _request_ctx_stack.top
|
|
|
|
return dict(
|
|
|
|
request=reqctx.request,
|
|
|
|
session=reqctx.session,
|
|
|
|
g=reqctx.g
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def _assert_have_json():
|
|
|
|
"""Helper function that fails if JSON is unavailable."""
|
|
|
|
if not json_available:
|
|
|
|
raise RuntimeError('simplejson not installed')
|
|
|
|
|
|
|
|
|
|
|
|
def _get_package_path(name):
|
|
|
|
"""Returns the path to a package or cwd if that cannot be found."""
|
|
|
|
try:
|
|
|
|
return os.path.abspath(os.path.dirname(sys.modules[name].__file__))
|
|
|
|
except (KeyError, AttributeError):
|
|
|
|
return os.getcwd()
|
|
|
|
|
|
|
|
|
|
|
|
# figure out if simplejson escapes slashes. This behaviour was changed
|
|
|
|
# from one version to another without reason.
|
|
|
|
if not json_available or '\\/' not in json.dumps('/'):
|
|
|
|
|
|
|
|
def _tojson_filter(*args, **kwargs):
|
|
|
|
if __debug__:
|
|
|
|
_assert_have_json()
|
|
|
|
return json.dumps(*args, **kwargs).replace('/', '\\/')
|
|
|
|
else:
|
|
|
|
_tojson_filter = json.dumps
|
|
|
|
|
|
|
|
|
|
|
|
class _PackageBoundObject(object):
|
|
|
|
|
|
|
|
def __init__(self, import_name):
|
|
|
|
#: the name of the package or module. Do not change this once
|
|
|
|
#: it was set by the constructor.
|
|
|
|
self.import_name = import_name
|
|
|
|
|
|
|
|
#: where is the app root located?
|
|
|
|
self.root_path = _get_package_path(self.import_name)
|
|
|
|
|
|
|
|
def open_resource(self, resource):
|
|
|
|
"""Opens a resource from the application's resource folder. To see
|
|
|
|
how this works, consider the following folder structure::
|
|
|
|
|
|
|
|
/myapplication.py
|
|
|
|
/schemal.sql
|
|
|
|
/static
|
|
|
|
/style.css
|
|
|
|
/template
|
|
|
|
/layout.html
|
|
|
|
/index.html
|
|
|
|
|
|
|
|
If you want to open the `schema.sql` file you would do the
|
|
|
|
following::
|
|
|
|
|
|
|
|
with app.open_resource('schema.sql') as f:
|
|
|
|
contents = f.read()
|
|
|
|
do_something_with(contents)
|
|
|
|
|
|
|
|
:param resource: the name of the resource. To access resources within
|
|
|
|
subfolders use forward slashes as separator.
|
|
|
|
"""
|
|
|
|
if pkg_resources is None:
|
|
|
|
return open(os.path.join(self.root_path, resource), 'rb')
|
|
|
|
return pkg_resources.resource_stream(self.import_name, resource)
|
|
|
|
|
|
|
|
|
|
|
|
class _ModuleSetupState(object):
|
|
|
|
|
|
|
|
def __init__(self, app, url_prefix=None):
|
|
|
|
self.app = app
|
|
|
|
self.url_prefix = url_prefix
|
|
|
|
|
|
|
|
|
|
|
|
class Module(_PackageBoundObject):
|
|
|
|
"""Container object that enables pluggable applications. A module can
|
|
|
|
be used to organize larger applications. They represent blueprints that,
|
|
|
|
in combination with a :class:`Flask` object are used to create a large
|
|
|
|
application.
|
|
|
|
|
|
|
|
A module is like an application bound to an `import_name`. Multiple
|
|
|
|
modules can share the same import names, but in that case a `name` has
|
|
|
|
to be provided to keep them apart. If different import names are used,
|
|
|
|
the rightmost part of the import name is used as name.
|
|
|
|
|
|
|
|
Here an example structure for a larger appliation::
|
|
|
|
|
|
|
|
/myapplication
|
|
|
|
/__init__.py
|
|
|
|
/views
|
|
|
|
/__init__.py
|
|
|
|
/admin.py
|
|
|
|
/frontend.py
|
|
|
|
|
|
|
|
The `myapplication/__init__.py` can look like this::
|
|
|
|
|
|
|
|
from flask import Flask
|
|
|
|
from myapplication.views.admin import admin
|
|
|
|
from myapplication.views.frontend import frontend
|
|
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
app.register_module(admin, url_prefix='/admin')
|
|
|
|
app.register_module(frontend)
|
|
|
|
|
|
|
|
And here an example view module (`myapplication/views/admin.py`)::
|
|
|
|
|
|
|
|
from flask import Module
|
|
|
|
|
|
|
|
admin = Module(__name__)
|
|
|
|
|
|
|
|
@admin.route('/')
|
|
|
|
def index():
|
|
|
|
pass
|
|
|
|
|
|
|
|
@admin.route('/login')
|
|
|
|
def login():
|
|
|
|
pass
|
|
|
|
|
|
|
|
For a gentle introduction into modules, checkout the
|
|
|
|
:ref:`working-with-modules` section.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, import_name, name=None, url_prefix=None):
|
|
|
|
if name is None:
|
|
|
|
assert '.' in import_name, 'name required if package name ' \
|
|
|
|
'does not point to a submodule'
|
|
|
|
name = import_name.rsplit('.', 1)[1]
|
|
|
|
_PackageBoundObject.__init__(self, import_name)
|
|
|
|
self.name = name
|
|
|
|
self.url_prefix = url_prefix
|
|
|
|
self._register_events = []
|
|
|
|
|
|
|
|
def route(self, rule, **options):
|
|
|
|
"""Like :meth:`Flask.route` but for a module. The endpoint for the
|
|
|
|
:func:`url_for` function is prefixed with the name of the module.
|
|
|
|
"""
|
|
|
|
def decorator(f):
|
|
|
|
self.add_url_rule(rule, f.__name__, f, **options)
|
|
|
|
return f
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
def add_url_rule(self, rule, endpoint, view_func=None, **options):
|
|
|
|
"""Like :meth:`Flask.add_url_rule` but for a module. The endpoint for
|
|
|
|
the :func:`url_for` function is prefixed with the name of the module.
|
|
|
|
"""
|
|
|
|
def register_rule(state):
|
|
|
|
the_rule = rule
|
|
|
|
if state.url_prefix:
|
|
|
|
the_rule = state.url_prefix + rule
|
|
|
|
state.app.add_url_rule(the_rule, '%s.%s' % (self.name, endpoint),
|
|
|
|
view_func, **options)
|
|
|
|
self._record(register_rule)
|
|
|
|
|
|
|
|
def before_request(self, f):
|
|
|
|
"""Like :meth:`Flask.before_request` but for a module. This function
|
|
|
|
is only executed before each request that is handled by a function of
|
|
|
|
that module.
|
|
|
|
"""
|
|
|
|
self._record(lambda s: s.app.before_request_funcs
|
|
|
|
.setdefault(self.name, []).append(f))
|
|
|
|
return f
|
|
|
|
|
|
|
|
def before_app_request(self, f):
|
|
|
|
"""Like :meth:`Flask.before_request`. Such a function is executed
|
|
|
|
before each request, even if outside of a module.
|
|
|
|
"""
|
|
|
|
self._record(lambda s: s.app.before_request_funcs
|
|
|
|
.setdefault(None, []).append(f))
|
|
|
|
return f
|
|
|
|
|
|
|
|
def after_request(self, f):
|
|
|
|
"""Like :meth:`Flask.after_request` but for a module. This function
|
|
|
|
is only executed after each request that is handled by a function of
|
|
|
|
that module.
|
|
|
|
"""
|
|
|
|
self._record(lambda s: s.app.after_request_funcs
|
|
|
|
.setdefault(self.name, []).append(f))
|
|
|
|
return f
|
|
|
|
|
|
|
|
def after_app_request(self, f):
|
|
|
|
"""Like :meth:`Flask.after_request` but for a module. Such a function
|
|
|
|
is executed after each request, even if outside of the module.
|
|
|
|
"""
|
|
|
|
self._record(lambda s: s.app.after_request_funcs
|
|
|
|
.setdefault(None, []).append(f))
|
|
|
|
return f
|
|
|
|
|
|
|
|
def context_processor(self, f):
|
|
|
|
"""Like :meth:`Flask.context_processor` but for a module. This
|
|
|
|
function is only executed for requests handled by a module.
|
|
|
|
"""
|
|
|
|
self._record(lambda s: s.app.template_context_processors
|
|
|
|
.setdefault(self.name, []).append(f))
|
|
|
|
return f
|
|
|
|
|
|
|
|
def app_context_processor(self, f):
|
|
|
|
"""Like :meth:`Flask.context_processor` but for a module. Such a
|
|
|
|
function is executed each request, even if outside of the module.
|
|
|
|
"""
|
|
|
|
self._record(lambda s: s.app.template_context_processors
|
|
|
|
.setdefault(None, []).append(f))
|
|
|
|
return f
|
|
|
|
|
|
|
|
def _record(self, func):
|
|
|
|
self._register_events.append(func)
|
|
|
|
|
|
|
|
|
|
|
|
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__)
|
|
|
|
"""
|
|
|
|
|
|
|
|
#: 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.
|
|
|
|
static_path = '/static'
|
|
|
|
|
|
|
|
#: 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.
|
|
|
|
secret_key = None
|
|
|
|
|
|
|
|
#: The secure cookie uses this for the name of the session cookie
|
|
|
|
session_cookie_name = 'session'
|
|
|
|
|
|
|
|
#: 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.
|
|
|
|
permanent_session_lifetime = timedelta(days=31)
|
|
|
|
|
|
|
|
#: 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
|
|
|
|
use_x_sendfile = False
|
|
|
|
|
|
|
|
#: options that are passed directly to the Jinja2 environment
|
|
|
|
jinja_options = ImmutableDict(
|
|
|
|
autoescape=True,
|
|
|
|
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
|
|
|
|
)
|
|
|
|
|
|
|
|
def __init__(self, import_name):
|
|
|
|
_PackageBoundObject.__init__(self, import_name)
|
|
|
|
|
|
|
|
#: 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.
|
|
|
|
self.debug = False
|
|
|
|
|
|
|
|
#: 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]
|
|
|
|
}
|
|
|
|
|
|
|
|
#: 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 self.static_path is not None:
|
|
|
|
self.add_url_rule(self.static_path + '/<filename>',
|
|
|
|
build_only=True, endpoint='static')
|
|
|
|
if pkg_resources is not None:
|
|
|
|
target = (self.import_name, 'static')
|
|
|
|
else:
|
|
|
|
target = os.path.join(self.root_path, 'static')
|
|
|
|
self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
|
|
|
|
self.static_path: target
|
|
|
|
})
|
|
|
|
|
|
|
|
#: the Jinja2 environment. It is created from the
|
|
|
|
#: :attr:`jinja_options` and the loader that is returned
|
|
|
|
#: by the :meth:`create_jinja_loader` function.
|
|
|
|
self.jinja_env = Environment(loader=self.create_jinja_loader(),
|
|
|
|
**self.jinja_options)
|
|
|
|
self.jinja_env.globals.update(
|
|
|
|
url_for=url_for,
|
|
|
|
get_flashed_messages=get_flashed_messages
|
|
|
|
)
|
|
|
|
self.jinja_env.filters['tojson'] = _tojson_filter
|
|
|
|
|
|
|
|
def create_jinja_loader(self):
|
|
|
|
"""Creates the Jinja loader. By default just a package loader for
|
|
|
|
the configured package is returned that looks up templates in the
|
|
|
|
`templates` folder. To add other loaders it's possible to
|
|
|
|
override this method.
|
|
|
|
"""
|
|
|
|
if pkg_resources is None:
|
|
|
|
return FileSystemLoader(os.path.join(self.root_path, 'templates'))
|
|
|
|
return PackageLoader(self.import_name)
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
: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`.
|
|
|
|
"""
|
|
|
|
from werkzeug import Client
|
|
|
|
return Client(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 subdoain 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():
|
|
|
|
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():
|
|
|
|
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 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:
|
|
|
|
handler = self.error_handlers.get(e.code)
|
|
|
|
if handler is None:
|
|
|
|
return e
|
|
|
|
return handler(e)
|
|
|
|
except Exception, e:
|
|
|
|
handler = self.error_handlers.get(500)
|
|
|
|
if self.debug or handler is None:
|
|
|
|
raise
|
|
|
|
return handler(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`:
|
|
|
|
|
|
|
|
======================= ===========================================
|
|
|
|
: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.
|
|
|
|
|
|
|
|
: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):
|
|
|
|
rv = self.preprocess_request()
|
|
|
|
if rv is None:
|
|
|
|
rv = self.dispatch_request()
|
|
|
|
response = self.make_response(rv)
|
|
|
|
response = self.process_response(response)
|
|
|
|
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)
|
|
|
|
|
|
|
|
: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)
|
|
|
|
|
|
|
|
|
|
|
|
# context locals
|
|
|
|
_request_ctx_stack = LocalStack()
|
|
|
|
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
|
|
|
|
request = LocalProxy(lambda: _request_ctx_stack.top.request)
|
|
|
|
session = LocalProxy(lambda: _request_ctx_stack.top.session)
|
|
|
|
g = LocalProxy(lambda: _request_ctx_stack.top.g)
|