aboutsummaryrefslogtreecommitdiffstats
path: root/python/flask
diff options
context:
space:
mode:
authorJames Taylor <user234683@users.noreply.github.com>2019-06-16 16:16:03 -0700
committerJames Taylor <user234683@users.noreply.github.com>2019-06-16 16:16:03 -0700
commit2db58930a6f8c955c4d437657bd07e2939a705f2 (patch)
treeb1d388bd4adc1d3134d255cd0c4d8746d7b2468b /python/flask
parent9f93b9429c77e631972186049fbc7518e2cf5d4b (diff)
downloadyt-local-2db58930a6f8c955c4d437657bd07e2939a705f2.tar.lz
yt-local-2db58930a6f8c955c4d437657bd07e2939a705f2.tar.xz
yt-local-2db58930a6f8c955c4d437657bd07e2939a705f2.zip
Convert watch page to flask framework
Diffstat (limited to 'python/flask')
-rw-r--r--python/flask/__init__.py49
-rw-r--r--python/flask/__main__.py14
-rw-r--r--python/flask/_compat.py101
-rw-r--r--python/flask/app.py2334
-rw-r--r--python/flask/blueprints.py447
-rw-r--r--python/flask/cli.py910
-rw-r--r--python/flask/config.py269
-rw-r--r--python/flask/ctx.py457
-rw-r--r--python/flask/debughelpers.py168
-rw-r--r--python/flask/globals.py61
-rw-r--r--python/flask/helpers.py1051
-rw-r--r--python/flask/json/__init__.py357
-rw-r--r--python/flask/json/tag.py300
-rw-r--r--python/flask/logging.py78
-rw-r--r--python/flask/sessions.py385
-rw-r--r--python/flask/signals.py57
-rw-r--r--python/flask/templating.py150
-rw-r--r--python/flask/testing.py246
-rw-r--r--python/flask/views.py158
-rw-r--r--python/flask/wrappers.py216
20 files changed, 7808 insertions, 0 deletions
diff --git a/python/flask/__init__.py b/python/flask/__init__.py
new file mode 100644
index 0000000..59f0fff
--- /dev/null
+++ b/python/flask/__init__.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+"""
+ flask
+ ~~~~~
+
+ A microframework based on Werkzeug. It's extensively documented
+ and follows best practice patterns.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+__version__ = '1.0.3'
+
+# utilities we import from Werkzeug and Jinja2 that are unused
+# in the module but are exported as public interface.
+from werkzeug.exceptions import abort
+from werkzeug.utils import redirect
+from jinja2 import Markup, escape
+
+from .app import Flask, Request, Response
+from .config import Config
+from .helpers import url_for, flash, send_file, send_from_directory, \
+ get_flashed_messages, get_template_attribute, make_response, safe_join, \
+ stream_with_context
+from .globals import current_app, g, request, session, _request_ctx_stack, \
+ _app_ctx_stack
+from .ctx import has_request_context, has_app_context, \
+ after_this_request, copy_current_request_context
+from .blueprints import Blueprint
+from .templating import render_template, render_template_string
+
+# the signals
+from .signals import signals_available, template_rendered, request_started, \
+ request_finished, got_request_exception, request_tearing_down, \
+ appcontext_tearing_down, appcontext_pushed, \
+ appcontext_popped, message_flashed, before_render_template
+
+# We're not exposing the actual json module but a convenient wrapper around
+# it.
+from . import json
+
+# This was the only thing that Flask used to export at one point and it had
+# a more generic name.
+jsonify = json.jsonify
+
+# backwards compat, goes away in 1.0
+from .sessions import SecureCookieSession as Session
+json_available = True
diff --git a/python/flask/__main__.py b/python/flask/__main__.py
new file mode 100644
index 0000000..4aee654
--- /dev/null
+++ b/python/flask/__main__.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.__main__
+ ~~~~~~~~~~~~~~
+
+ Alias for flask.run for the command line.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+if __name__ == '__main__':
+ from .cli import main
+ main(as_module=True)
diff --git a/python/flask/_compat.py b/python/flask/_compat.py
new file mode 100644
index 0000000..dfbaae9
--- /dev/null
+++ b/python/flask/_compat.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+"""
+ flask._compat
+ ~~~~~~~~~~~~~
+
+ Some py2/py3 compatibility support based on a stripped down
+ version of six so we don't have to depend on a specific version
+ of it.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import sys
+
+PY2 = sys.version_info[0] == 2
+_identity = lambda x: x
+
+
+if not PY2:
+ text_type = str
+ string_types = (str,)
+ integer_types = (int,)
+
+ iterkeys = lambda d: iter(d.keys())
+ itervalues = lambda d: iter(d.values())
+ iteritems = lambda d: iter(d.items())
+
+ from inspect import getfullargspec as getargspec
+ from io import StringIO
+ import collections.abc as collections_abc
+
+ def reraise(tp, value, tb=None):
+ if value.__traceback__ is not tb:
+ raise value.with_traceback(tb)
+ raise value
+
+ implements_to_string = _identity
+
+else:
+ text_type = unicode
+ string_types = (str, unicode)
+ integer_types = (int, long)
+
+ iterkeys = lambda d: d.iterkeys()
+ itervalues = lambda d: d.itervalues()
+ iteritems = lambda d: d.iteritems()
+
+ from inspect import getargspec
+ from cStringIO import StringIO
+ import collections as collections_abc
+
+ exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
+
+ def implements_to_string(cls):
+ cls.__unicode__ = cls.__str__
+ cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
+ return cls
+
+
+def with_metaclass(meta, *bases):
+ """Create a base class with a metaclass."""
+ # This requires a bit of explanation: the basic idea is to make a
+ # dummy metaclass for one level of class instantiation that replaces
+ # itself with the actual metaclass.
+ class metaclass(type):
+ def __new__(cls, name, this_bases, d):
+ return meta(name, bases, d)
+ return type.__new__(metaclass, 'temporary_class', (), {})
+
+
+# Certain versions of pypy have a bug where clearing the exception stack
+# breaks the __exit__ function in a very peculiar way. The second level of
+# exception blocks is necessary because pypy seems to forget to check if an
+# exception happened until the next bytecode instruction?
+#
+# Relevant PyPy bugfix commit:
+# https://bitbucket.org/pypy/pypy/commits/77ecf91c635a287e88e60d8ddb0f4e9df4003301
+# According to ronan on #pypy IRC, it is released in PyPy2 2.3 and later
+# versions.
+#
+# Ubuntu 14.04 has PyPy 2.2.1, which does exhibit this bug.
+BROKEN_PYPY_CTXMGR_EXIT = False
+if hasattr(sys, 'pypy_version_info'):
+ class _Mgr(object):
+ def __enter__(self):
+ return self
+ def __exit__(self, *args):
+ if hasattr(sys, 'exc_clear'):
+ # Python 3 (PyPy3) doesn't have exc_clear
+ sys.exc_clear()
+ try:
+ try:
+ with _Mgr():
+ raise AssertionError()
+ except:
+ raise
+ except TypeError:
+ BROKEN_PYPY_CTXMGR_EXIT = True
+ except AssertionError:
+ pass
diff --git a/python/flask/app.py b/python/flask/app.py
new file mode 100644
index 0000000..c570a95
--- /dev/null
+++ b/python/flask/app.py
@@ -0,0 +1,2334 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.app
+ ~~~~~~~~~
+
+ This module implements the central WSGI application object.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+import sys
+import warnings
+from datetime import timedelta
+from functools import update_wrapper
+from itertools import chain
+from threading import Lock
+
+from werkzeug.datastructures import Headers, ImmutableDict
+from werkzeug.exceptions import BadRequest, BadRequestKeyError, HTTPException, \
+ InternalServerError, MethodNotAllowed, default_exceptions
+from werkzeug.routing import BuildError, Map, RequestRedirect, \
+ RoutingException, Rule
+
+from . import cli, json
+from ._compat import integer_types, reraise, string_types, text_type
+from .config import Config, ConfigAttribute
+from .ctx import AppContext, RequestContext, _AppCtxGlobals
+from .globals import _request_ctx_stack, g, request, session
+from .helpers import (
+ _PackageBoundObject,
+ _endpoint_from_view_func, find_package, get_env, get_debug_flag,
+ get_flashed_messages, locked_cached_property, url_for, get_load_dotenv
+)
+from .logging import create_logger
+from .sessions import SecureCookieSessionInterface
+from .signals import appcontext_tearing_down, got_request_exception, \
+ request_finished, request_started, request_tearing_down
+from .templating import DispatchingJinjaLoader, Environment, \
+ _default_template_ctx_processor
+from .wrappers import Request, Response
+
+# 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.
+
+ .. versionadded:: 1.0
+ The ``host_matching`` and ``static_host`` parameters were added.
+
+ .. versionadded:: 1.0
+ The ``subdomain_matching`` parameter was added. Subdomain
+ matching needs to be enabled manually now. Setting
+ :data:`SERVER_NAME` does not implicitly enable it.
+
+ :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 static_host: the host to use when adding the static route.
+ Defaults to None. Required when using ``host_matching=True``
+ with a ``static_folder`` configured.
+ :param host_matching: set ``url_map.host_matching`` attribute.
+ Defaults to False.
+ :param subdomain_matching: consider the subdomain relative to
+ :data:`SERVER_NAME` when matching routes. Defaults to False.
+ :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
+
+ #: 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 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 test 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
+ #: :data:`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 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({
+ 'ENV': None,
+ 'DEBUG': None,
+ 'TESTING': False,
+ 'PROPAGATE_EXCEPTIONS': None,
+ 'PRESERVE_CONTEXT_ON_EXCEPTION': None,
+ 'SECRET_KEY': None,
+ 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
+ 'USE_X_SENDFILE': False,
+ 'SERVER_NAME': None,
+ 'APPLICATION_ROOT': '/',
+ 'SESSION_COOKIE_NAME': 'session',
+ 'SESSION_COOKIE_DOMAIN': None,
+ 'SESSION_COOKIE_PATH': None,
+ 'SESSION_COOKIE_HTTPONLY': True,
+ 'SESSION_COOKIE_SECURE': False,
+ 'SESSION_COOKIE_SAMESITE': None,
+ 'SESSION_REFRESH_EACH_REQUEST': True,
+ 'MAX_CONTENT_LENGTH': None,
+ 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
+ 'TRAP_BAD_REQUEST_ERRORS': None,
+ 'TRAP_HTTP_EXCEPTIONS': False,
+ 'EXPLAIN_TEMPLATE_LOADING': False,
+ 'PREFERRED_URL_SCHEME': 'http',
+ 'JSON_AS_ASCII': True,
+ 'JSON_SORT_KEYS': True,
+ 'JSONIFY_PRETTYPRINT_REGULAR': False,
+ 'JSONIFY_MIMETYPE': 'application/json',
+ 'TEMPLATES_AUTO_RELOAD': None,
+ 'MAX_COOKIE_SIZE': 4093,
+ })
+
+ #: 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 :class:`~click.testing.CliRunner` subclass, by default
+ #: :class:`~flask.testing.FlaskCliRunner` that is used by
+ #: :meth:`test_cli_runner`. Its ``__init__`` method should take a
+ #: Flask app object as the first argument.
+ #:
+ #: .. versionadded:: 1.0
+ test_cli_runner_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()
+
+ # TODO remove the next three attrs when Sphinx :inherited-members: works
+ # https://github.com/sphinx-doc/sphinx/issues/741
+
+ #: The name of the package or module that this app belongs to. Do not
+ #: change this once it is set by the constructor.
+ import_name = None
+
+ #: Location of the template files to be added to the template lookup.
+ #: ``None`` if templates should not be added.
+ template_folder = None
+
+ #: Absolute path to the package on the filesystem. Used to look up
+ #: resources contained in the package.
+ root_path = None
+
+ def __init__(
+ self,
+ import_name,
+ static_url_path=None,
+ static_folder='static',
+ static_host=None,
+ host_matching=False,
+ subdomain_matching=False,
+ 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_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)
+
+ #: A dictionary of all view functions registered. The keys will
+ #: be function names which are also used to generate URLs and
+ #: the values are the function objects themselves.
+ #: To register a view function, use the :meth:`route` decorator.
+ self.view_functions = {}
+
+ #: A dictionary of all registered error handlers. The key is ``None``
+ #: for error handlers active on the application, otherwise the key is
+ #: the name of the blueprint. Each key points to another dictionary
+ #: where the key is the status code of the http exception. The
+ #: special key ``None`` points to a list of tuples where the first item
+ #: is the class for the instance check and the second the error handler
+ #: function.
+ #:
+ #: To register an error handler, use the :meth:`errorhandler`
+ #: decorator.
+ self.error_handler_spec = {}
+
+ #: A list of functions that are called when :meth:`url_for` raises a
+ #: :exc:`~werkzeug.routing.BuildError`. Each function registered here
+ #: is called with `error`, `endpoint` and `values`. If a function
+ #: 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 will be called at the
+ #: beginning of each request. The key of the dictionary is the name of
+ #: the blueprint this function is active for, or ``None`` for all
+ #: requests. To register a function, use the :meth:`before_request`
+ #: decorator.
+ self.before_request_funcs = {}
+
+ #: A list of functions that will be called at the beginning of the
+ #: first request to this instance. To register a function, use the
+ #: :meth:`before_first_request` decorator.
+ #:
+ #: .. 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 are called before the
+ #: :attr:`before_request_funcs` functions. The key of the dictionary is
+ #: the name of the blueprint this function is active for, or ``None``
+ #: for all requests. To register a function, use
+ #: :meth:`url_value_preprocessor`.
+ #:
+ #: .. versionadded:: 0.7
+ self.url_value_preprocessors = {}
+
+ #: A dictionary with lists of functions that can be used as URL value
+ #: preprocessors. The key ``None`` here is used for application wide
+ #: callbacks, otherwise the key is the name of the blueprint.
+ #: Each of these functions has the chance to modify the dictionary
+ #: of URL values before they are used as the keyword arguments of the
+ #: view function. For each function registered this one should also
+ #: provide a :meth:`url_defaults` function that adds the parameters
+ #: automatically again that were removed that way.
+ #:
+ #: .. versionadded:: 0.7
+ self.url_default_functions = {}
+
+ #: A dictionary with list of functions that are called without argument
+ #: to populate the template context. The key of the dictionary is the
+ #: name of the blueprint this function is active for, ``None`` for all
+ #: requests. Each returns a dictionary that the template context is
+ #: updated with. To register a function here, use the
+ #: :meth:`context_processor` decorator.
+ self.template_context_processors = {
+ None: [_default_template_ctx_processor]
+ }
+
+ #: A list of shell context processor functions that should be run
+ #: when a shell context is created.
+ #:
+ #: .. 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()
+
+ self.url_map.host_matching = host_matching
+ self.subdomain_matching = subdomain_matching
+
+ # tracks internally if the application already handled at least one
+ # request.
+ self._got_first_request = False
+ self._before_request_lock = Lock()
+
+ # Add a static route using the provided static_url_path, static_host,
+ # and static_folder if there is a configured static_folder.
+ # Note we do this without checking if static_folder exists.
+ # For one, it might be created while the server is running (e.g. during
+ # development). Also, Google App Engine stores static files somewhere
+ if self.has_static_folder:
+ assert bool(static_host) == host_matching, 'Invalid static_host/host_matching combination'
+ self.add_url_rule(
+ self.static_url_path + '/<path:filename>',
+ endpoint='static',
+ host=static_host,
+ 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)
+
+ @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
+
+ @locked_cached_property
+ def logger(self):
+ """The ``'flask.app'`` logger, a standard Python
+ :class:`~logging.Logger`.
+
+ In debug mode, the logger's :attr:`~logging.Logger.level` will be set
+ to :data:`~logging.DEBUG`.
+
+ If there are no handlers configured, a default handler will be added.
+ See :ref:`logging` for more information.
+
+ .. versionchanged:: 1.0
+ Behavior was simplified. The logger is always named
+ ``flask.app``. The level is only set during configuration, it
+ doesn't check ``app.debug`` each time. Only one format is used,
+ not different ones depending on ``app.debug``. No handlers are
+ removed, and a handler is only added if no handlers are already
+ configured.
+
+ .. versionadded:: 0.3
+ """
+ return create_logger(self)
+
+ @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
+ defaults = dict(self.default_config)
+ defaults['ENV'] = get_env()
+ defaults['DEBUG'] = get_debug_flag()
+ return self.config_class(root_path, defaults)
+
+ 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 _get_templates_auto_reload(self):
+ """Reload templates when they are changed. Used by
+ :meth:`create_jinja_environment`.
+
+ This attribute can be configured with :data:`TEMPLATES_AUTO_RELOAD`. If
+ not set, it will be enabled in debug mode.
+
+ .. versionadded:: 1.0
+ This property was added but the underlying config and behavior
+ already existed.
+ """
+ rv = self.config['TEMPLATES_AUTO_RELOAD']
+ return rv if rv is not None else self.debug
+
+ def _set_templates_auto_reload(self, value):
+ self.config['TEMPLATES_AUTO_RELOAD'] = value
+
+ templates_auto_reload = property(
+ _get_templates_auto_reload, _set_templates_auto_reload
+ )
+ del _get_templates_auto_reload, _set_templates_auto_reload
+
+ 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:
+ options['auto_reload'] = self.templates_auto_reload
+
+ 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 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
+
+ #: What environment the app is running in. Flask and extensions may
+ #: enable behaviors based on the environment, such as enabling debug
+ #: mode. This maps to the :data:`ENV` config key. This is set by the
+ #: :envvar:`FLASK_ENV` environment variable and may not behave as
+ #: expected if set in code.
+ #:
+ #: **Do not enable development when deploying in production.**
+ #:
+ #: Default: ``'production'``
+ env = ConfigAttribute('ENV')
+
+ def _get_debug(self):
+ return self.config['DEBUG']
+
+ def _set_debug(self, value):
+ self.config['DEBUG'] = value
+ self.jinja_env.auto_reload = self.templates_auto_reload
+
+ #: Whether debug mode is enabled. When using ``flask run`` to start
+ #: the development server, an interactive debugger will be shown for
+ #: unhandled exceptions, and the server will be reloaded when code
+ #: changes. This maps to the :data:`DEBUG` config key. This is
+ #: enabled when :attr:`env` is ``'development'`` and is overridden
+ #: by the ``FLASK_DEBUG`` environment variable. It may not behave as
+ #: expected if set in code.
+ #:
+ #: **Do not enable debug mode when deploying in production.**
+ #:
+ #: Default: ``True`` if :attr:`env` is ``'development'``, or
+ #: ``False`` otherwise.
+ debug = property(_get_debug, _set_debug)
+ del _get_debug, _set_debug
+
+ def run(self, host=None, port=None, debug=None,
+ load_dotenv=True, **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.
+
+ :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'`` or the host in the ``SERVER_NAME`` config variable
+ if present.
+ :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 load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`
+ files to set environment variables. Will also change the working
+ directory to the directory containing the first file found.
+ :param options: the options to be forwarded to the underlying Werkzeug
+ server. See :func:`werkzeug.serving.run_simple` for more
+ information.
+
+ .. versionchanged:: 1.0
+ If installed, python-dotenv will be used to load environment
+ variables from :file:`.env` and :file:`.flaskenv` files.
+
+ If set, the :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG`
+ environment variables will override :attr:`env` and
+ :attr:`debug`.
+
+ Threaded mode is enabled by default.
+
+ .. versionchanged:: 0.10
+ The default port is now picked from the ``SERVER_NAME``
+ variable.
+ """
+ # Change this into a no-op if the server is invoked from the
+ # command line. Have a look at cli.py for more information.
+ if os.environ.get('FLASK_RUN_FROM_CLI') == 'true':
+ from .debughelpers import explain_ignored_app_run
+ explain_ignored_app_run()
+ return
+
+ if get_load_dotenv(load_dotenv):
+ cli.load_dotenv()
+
+ # if set, let env vars override previous values
+ if 'FLASK_ENV' in os.environ:
+ self.env = get_env()
+ self.debug = get_debug_flag()
+ elif 'FLASK_DEBUG' in os.environ:
+ self.debug = get_debug_flag()
+
+ # debug passed to method overrides all other sources
+ if debug is not None:
+ self.debug = bool(debug)
+
+ _host = '127.0.0.1'
+ _port = 5000
+ server_name = self.config.get('SERVER_NAME')
+ sn_host, sn_port = None, None
+
+ if server_name:
+ sn_host, _, sn_port = server_name.partition(':')
+
+ host = host or sn_host or _host
+ port = int(port or sn_port or _port)
+
+ options.setdefault('use_reloader', self.debug)
+ options.setdefault('use_debugger', self.debug)
+ options.setdefault('threaded', True)
+
+ cli.show_server_banner(self.env, self.debug, self.name, False)
+
+ from werkzeug.serving import run_simple
+
+ 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 test_cli_runner(self, **kwargs):
+ """Create a CLI runner for testing CLI commands.
+ See :ref:`testing-cli`.
+
+ Returns an instance of :attr:`test_cli_runner_class`, by default
+ :class:`~flask.testing.FlaskCliRunner`. The Flask app object is
+ passed as the first argument.
+
+ .. versionadded:: 1.0
+ """
+ cls = self.test_cli_runner_class
+
+ if cls is None:
+ from flask.testing import FlaskCliRunner as cls
+
+ return cls(self, **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`.
+
+ .. deprecated: 1.0
+ Will be removed in 1.1. Use ``session_interface.open_session``
+ instead.
+
+ :param request: an instance of :attr:`request_class`.
+ """
+
+ warnings.warn(DeprecationWarning(
+ '"open_session" is deprecated and will be removed in 1.1. Use'
+ ' "session_interface.open_session" instead.'
+ ))
+ 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`.
+
+ .. deprecated: 1.0
+ Will be removed in 1.1. Use ``session_interface.save_session``
+ instead.
+
+ :param session: the session to be saved (a
+ :class:`~werkzeug.contrib.securecookie.SecureCookie`
+ object)
+ :param response: an instance of :attr:`response_class`
+ """
+
+ warnings.warn(DeprecationWarning(
+ '"save_session" is deprecated and will be removed in 1.1. Use'
+ ' "session_interface.save_session" instead.'
+ ))
+ 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`.
+
+ .. deprecated: 1.0
+ Will be removed in 1.1. Use ``session_interface.make_null_session``
+ instead.
+
+ .. versionadded:: 0.7
+ """
+
+ warnings.warn(DeprecationWarning(
+ '"make_null_session" is deprecated and will be removed in 1.1. Use'
+ ' "session_interface.make_null_session" instead.'
+ ))
+ return self.session_interface.make_null_session(self)
+
+ @setupmethod
+ def register_blueprint(self, blueprint, **options):
+ """Register a :class:`~flask.Blueprint` on the application. Keyword
+ arguments passed to this method will override the defaults set on the
+ blueprint.
+
+ Calls the blueprint's :meth:`~flask.Blueprint.register` method after
+ recording the blueprint in the application's :attr:`blueprints`.
+
+ :param blueprint: The blueprint to register.
+ :param url_prefix: Blueprint routes will be prefixed with this.
+ :param subdomain: Blueprint routes will match on this subdomain.
+ :param url_defaults: Blueprint routes will use these default values for
+ view arguments.
+ :param options: Additional keyword arguments are passed to
+ :class:`~flask.blueprints.BlueprintSetupState`. They can be
+ accessed in :meth:`~flask.Blueprint.record` callbacks.
+
+ .. versionadded:: 0.7
+ """
+ first_registration = False
+
+ if blueprint.name in self.blueprints:
+ assert self.blueprints[blueprint.name] is blueprint, (
+ 'A name collision occurred between blueprints %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,
+ provide_automatic_options=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 provide_automatic_options: controls whether the ``OPTIONS``
+ method should be added automatically. This can also be controlled
+ by setting the ``view_func.provide_automatic_options = False``
+ before adding the rule.
+ :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.
+ if provide_automatic_options is None:
+ 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):
+ """Register a function to handle errors by code or exception class.
+
+ A decorator that is used to register a function given an
+ error code. Example::
+
+ @app.errorhandler(404)
+ def page_not_found(error):
+ return 'This page does not exist', 404
+
+ You can also register handlers for arbitrary exceptions::
+
+ @app.errorhandler(DatabaseError)
+ def special_exception_handler(error):
+ return 'Database connection failed', 500
+
+ .. versionadded:: 0.7
+ Use :meth:`register_error_handler` instead of modifying
+ :attr:`error_handler_spec` directly, for application wide error
+ handlers.
+
+ .. versionadded:: 0.7
+ One can now additionally also register custom exception types
+ that do not necessarily have to be a subclass of the
+ :class:`~werkzeug.exceptions.HTTPException` class.
+
+ :param code_or_exception: the code as integer for the handler, or
+ an arbitrary exception
+ """
+ def decorator(f):
+ self._register_error_handler(None, code_or_exception, f)
+ return f
+ return decorator
+
+ @setupmethod
+ def register_error_handler(self, code_or_exception, f):
+ """Alternative error attach function to the :meth:`errorhandler`
+ decorator that is more straightforward to use for non decorator
+ usage.
+
+ .. versionadded:: 0.7
+ """
+ self._register_error_handler(None, code_or_exception, f)
+
+ @setupmethod
+ def _register_error_handler(self, key, code_or_exception, f):
+ """
+ :type key: None|str
+ :type code_or_exception: int|T<=Exception
+ :type f: callable
+ """
+ if isinstance(code_or_exception, HTTPException): # old broken behavior
+ raise ValueError(
+ 'Tried to register a handler for an exception instance {0!r}.'
+ ' Handlers can only be registered for exception classes or'
+ ' HTTP error codes.'.format(code_or_exception)
+ )
+
+ try:
+ exc_class, code = self._get_exc_class_and_code(code_or_exception)
+ except KeyError:
+ raise KeyError(
+ "'{0}' is not a recognized HTTP error code. Use a subclass of"
+ " HTTPException with that code instead.".format(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.
+
+ For example, this can be used to open a database connection, or to load
+ the logged in user from the session.
+
+ The function will be called without any arguments. If it returns a
+ non-None value, the value is handled as if it was the return value from
+ the view, and further request handling is stopped.
+ """
+ self.before_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def before_first_request(self, f):
+ """Registers a function to be run before the first request to this
+ 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 an exception it will
+ be passed an error object.
+
+ The return values of teardown functions are ignored.
+
+ .. admonition:: Debug Note
+
+ In debug mode Flask will not tear down a request on an exception
+ immediately. Instead it will keep it alive so that the interactive
+ debugger can still access it. This behavior can be controlled
+ by the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable.
+ """
+ self.teardown_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def teardown_appcontext(self, f):
+ """Registers a function to be called when the application context
+ 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 unhandled exception
+ it will be passed an error object. If an :meth:`errorhandler` is
+ registered, it will handle the exception and the teardown will not
+ receive it.
+
+ 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):
+ """Register a URL value preprocessor function for all view
+ functions in the application. These functions will be called before the
+ :meth:`before_request` functions.
+
+ The function can modify the values captured from the matched url before
+ they are passed to the view. For example, this can be used to pop a
+ common language code value and place it in ``g`` rather than pass it to
+ every view.
+
+ The function is passed the endpoint name and values dict. The return
+ value is ignored.
+ """
+ self.url_value_preprocessors.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def url_defaults(self, f):
+ """Callback function for URL defaults for all view functions of the
+ application. It's called with the endpoint and values and should
+ update the values passed in place.
+ """
+ self.url_default_functions.setdefault(None, []).append(f)
+ return f
+
+ def _find_error_handler(self, e):
+ """Return a registered error handler for an exception in this order:
+ blueprint handler for a specific code, app handler for a specific code,
+ blueprint handler for an exception class, app handler for an exception
+ class, or ``None`` if a suitable handler is not found.
+ """
+ exc_class, code = self._get_exc_class_and_code(type(e))
+
+ for name, c in (
+ (request.blueprint, code), (None, code),
+ (request.blueprint, None), (None, None)
+ ):
+ handler_map = self.error_handler_spec.setdefault(name, {}).get(c)
+
+ if not handler_map:
+ continue
+
+ for cls in exc_class.__mro__:
+ handler = handler_map.get(cls)
+
+ if handler is not None:
+ return handler
+
+ 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.
+
+ .. versionchanged:: 1.0.3
+ ``RoutingException``, used internally for actions such as
+ slash redirects during routing, is not passed to error
+ handlers.
+
+ .. versionchanged:: 1.0
+ Exceptions are looked up by code *and* by MRO, so
+ ``HTTPExcpetion`` subclasses can be handled with a catch-all
+ handler for the base ``HTTPException``.
+
+ .. 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
+
+ # RoutingExceptions are used internally to trigger routing
+ # actions, such as slash redirects raising RequestRedirect. They
+ # are not raised or handled in user code.
+ if isinstance(e, RoutingException):
+ 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.
+
+ .. versionchanged:: 1.0
+ Bad request errors are not trapped by default in debug mode.
+
+ .. versionadded:: 0.8
+ """
+ if self.config['TRAP_HTTP_EXCEPTIONS']:
+ return True
+
+ trap_bad_request = self.config['TRAP_BAD_REQUEST_ERRORS']
+
+ # if unset, trap key errors in debug mode
+ if (
+ trap_bad_request is None and self.debug
+ and isinstance(e, BadRequestKeyError)
+ ):
+ return True
+
+ if trap_bad_request:
+ 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 is :class:`~werkzeug
+ .exceptions.HTTPException` which is forwarded to the
+ :meth:`handle_http_exception` method. This function will either
+ return a response value or reraise the exception with the same
+ traceback.
+
+ .. versionchanged:: 1.0
+ Key errors raised from request data like ``form`` show the
+ bad key in debug mode rather than a generic bad request
+ message.
+
+ .. 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, BadRequestKeyError):
+ if self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"]:
+ # Werkzeug < 0.15 doesn't add the KeyError to the 400
+ # message, add it in manually.
+ description = e.get_description()
+
+ if e.args[0] not in description:
+ e.description = "KeyError: '{}'".format(*e.args)
+ else:
+ # Werkzeug >= 0.15 does add it, remove it in production
+ e.args = ()
+
+ 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):
+ """Convert the return value from a view function to an instance of
+ :attr:`response_class`.
+
+ :param rv: the return value from the view function. The view function
+ must return a response. Returning ``None``, or the view ending
+ without returning, is not allowed. The following types are allowed
+ for ``view_rv``:
+
+ ``str`` (``unicode`` in Python 2)
+ A response object is created with the string encoded to UTF-8
+ as the body.
+
+ ``bytes`` (``str`` in Python 2)
+ A response object is created with the bytes as the body.
+
+ ``tuple``
+ Either ``(body, status, headers)``, ``(body, status)``, or
+ ``(body, headers)``, where ``body`` is any of the other types
+ allowed here, ``status`` is a string or an integer, and
+ ``headers`` is a dictionary or a list of ``(key, value)``
+ tuples. If ``body`` is a :attr:`response_class` instance,
+ ``status`` overwrites the exiting value and ``headers`` are
+ extended.
+
+ :attr:`response_class`
+ The object is returned unchanged.
+
+ other :class:`~werkzeug.wrappers.Response` class
+ The object is coerced to :attr:`response_class`.
+
+ :func:`callable`
+ The function is called as a WSGI application. The result is
+ used to create a response object.
+
+ .. versionchanged:: 0.9
+ Previously a tuple was interpreted as the arguments for the
+ response object.
+ """
+
+ status = headers = None
+
+ # unpack tuple returns
+ if isinstance(rv, tuple):
+ len_rv = len(rv)
+
+ # a 3-tuple is unpacked directly
+ if len_rv == 3:
+ rv, status, headers = rv
+ # decide if a 2-tuple has status or headers
+ elif len_rv == 2:
+ if isinstance(rv[1], (Headers, dict, tuple, list)):
+ rv, headers = rv
+ else:
+ rv, status = rv
+ # other sized tuples are not allowed
+ else:
+ raise TypeError(
+ 'The view function did not return a valid response tuple.'
+ ' The tuple must have the form (body, status, headers),'
+ ' (body, status), or (body, headers).'
+ )
+
+ # the body must not be None
+ if rv is None:
+ raise TypeError(
+ 'The view function did not return a valid response. The'
+ ' function either returned None or ended without a return'
+ ' statement.'
+ )
+
+ # make sure the body is an instance of the response class
+ if not isinstance(rv, self.response_class):
+ if isinstance(rv, (text_type, bytes, bytearray)):
+ # let the response class set the status and headers instead of
+ # waiting to do it manually, so that the class can handle any
+ # special logic
+ rv = self.response_class(rv, status=status, headers=headers)
+ status = headers = None
+ else:
+ # evaluate a WSGI callable, or coerce a different response
+ # class to the correct type
+ try:
+ rv = self.response_class.force_type(rv, request.environ)
+ except TypeError as e:
+ new_error = TypeError(
+ '{e}\nThe view function did not return a valid'
+ ' response. The return type must be a string, tuple,'
+ ' Response instance, or WSGI callable, but it was a'
+ ' {rv.__class__.__name__}.'.format(e=e, rv=rv)
+ )
+ reraise(TypeError, new_error, sys.exc_info()[2])
+
+ # prefer the status if it was provided
+ if status is not None:
+ if isinstance(status, (text_type, bytes, bytearray)):
+ rv.status = status
+ else:
+ rv.status_code = status
+
+ # extend existing headers with provided 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.
+
+ .. versionchanged:: 1.0
+ :data:`SERVER_NAME` no longer implicitly enables subdomain
+ matching. Use :attr:`subdomain_matching` instead.
+ """
+ if request is not None:
+ # If subdomain matching is disabled (the default), use the
+ # default subdomain in all cases. This should be the default
+ # in Werkzeug but it currently does not have that feature.
+ subdomain = ((self.url_map.default_subdomain or None)
+ if not self.subdomain_matching else None)
+ return self.url_map.bind_to_environ(
+ request.environ,
+ server_name=self.config['SERVER_NAME'],
+ subdomain=subdomain)
+ # 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'],
+ 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 request is dispatched. Calls
+ :attr:`url_value_preprocessors` registered with the app and the
+ current blueprint (if any). Then calls :attr:`before_request_funcs`
+ registered with the app and the blueprint.
+
+ If any :meth:`before_request` handler returns a non-None value, the
+ value is handled as if it was the return value from the view, and
+ further request handling is stopped.
+ """
+
+ 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.session_interface.save_session(self, ctx.session, response)
+ return response
+
+ def do_teardown_request(self, exc=_sentinel):
+ """Called after the request is dispatched and the response is
+ returned, right before the request context is popped.
+
+ This calls all functions decorated with
+ :meth:`teardown_request`, and :meth:`Blueprint.teardown_request`
+ if a blueprint handled the request. Finally, the
+ :data:`request_tearing_down` signal is sent.
+
+ This is called by
+ :meth:`RequestContext.pop() <flask.ctx.RequestContext.pop>`,
+ which may be delayed during testing to maintain access to
+ resources.
+
+ :param exc: An unhandled exception raised while dispatching the
+ request. Detected from the current exception information if
+ not passed. Passed to each teardown function.
+
+ .. versionchanged:: 0.9
+ Added the ``exc`` argument.
+ """
+ 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 right before the application context is popped.
+
+ When handling a request, the application context is popped
+ after the request context. See :meth:`do_teardown_request`.
+
+ This calls all functions decorated with
+ :meth:`teardown_appcontext`. Then the
+ :data:`appcontext_tearing_down` signal is sent.
+
+ This is called by
+ :meth:`AppContext.pop() <flask.ctx.AppContext.pop>`.
+
+ .. 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):
+ """Create an :class:`~flask.ctx.AppContext`. Use as a ``with``
+ block to push the context, which will make :data:`current_app`
+ point at this application.
+
+ An application context is automatically pushed by
+ :meth:`RequestContext.push() <flask.ctx.RequestContext.push>`
+ when handling a request, and when running a CLI command. Use
+ this to manually create a context outside of these situations.
+
+ ::
+
+ with app.app_context():
+ init_db()
+
+ See :doc:`/appcontext`.
+
+ .. versionadded:: 0.9
+ """
+ return AppContext(self)
+
+ def request_context(self, environ):
+ """Create a :class:`~flask.ctx.RequestContext` representing a
+ WSGI environment. Use a ``with`` block to push the context,
+ which will make :data:`request` point at this request.
+
+ See :doc:`/reqcontext`.
+
+ Typically you should not call this from your own code. A request
+ context is automatically pushed by the :meth:`wsgi_app` when
+ handling a request. Use :meth:`test_request_context` to create
+ an environment and context instead of this method.
+
+ :param environ: a WSGI environment
+ """
+ return RequestContext(self, environ)
+
+ def test_request_context(self, *args, **kwargs):
+ """Create a :class:`~flask.ctx.RequestContext` for a WSGI
+ environment created from the given values. This is mostly useful
+ during testing, where you may want to run a function that uses
+ request data without dispatching a full request.
+
+ See :doc:`/reqcontext`.
+
+ Use a ``with`` block to push the context, which will make
+ :data:`request` point at the request for the created
+ environment. ::
+
+ with test_request_context(...):
+ generate_report()
+
+ When using the shell, it may be easier to push and pop the
+ context manually to avoid indentation. ::
+
+ ctx = app.test_request_context(...)
+ ctx.push()
+ ...
+ ctx.pop()
+
+ Takes the same arguments as Werkzeug's
+ :class:`~werkzeug.test.EnvironBuilder`, with some defaults from
+ the application. See the linked Werkzeug docs for most of the
+ available arguments. Flask-specific behavior is listed here.
+
+ :param path: URL path being requested.
+ :param base_url: Base URL where the app is being served, which
+ ``path`` is relative to. If not given, built from
+ :data:`PREFERRED_URL_SCHEME`, ``subdomain``,
+ :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`.
+ :param subdomain: Subdomain name to append to
+ :data:`SERVER_NAME`.
+ :param url_scheme: Scheme to use instead of
+ :data:`PREFERRED_URL_SCHEME`.
+ :param data: The request body, either as a string or a dict of
+ form keys and values.
+ :param json: If given, this is serialized as JSON and passed as
+ ``data``. Also defaults ``content_type`` to
+ ``application/json``.
+ :param args: other positional arguments passed to
+ :class:`~werkzeug.test.EnvironBuilder`.
+ :param kwargs: other keyword arguments passed to
+ :class:`~werkzeug.test.EnvironBuilder`.
+ """
+ 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
+ :meth:`__call__` so that middlewares can be applied without
+ losing a reference to the app object. 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
+ Teardown events for the request and app contexts are called
+ even if an unhandled error occurs. Other events may not be
+ called depending on when an error occurs during dispatch.
+ 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)
+ error = None
+ try:
+ try:
+ ctx.push()
+ response = self.full_dispatch_request()
+ except Exception as e:
+ error = e
+ response = self.handle_exception(e)
+ except:
+ error = sys.exc_info()[1]
+ raise
+ return response(environ, start_response)
+ finally:
+ if self.should_ignore_error(error):
+ error = None
+ ctx.auto_pop(error)
+
+ def __call__(self, environ, start_response):
+ """The WSGI server calls the Flask application object as the
+ WSGI application. This calls :meth:`wsgi_app` which can be
+ wrapped to applying middleware."""
+ return self.wsgi_app(environ, start_response)
+
+ def __repr__(self):
+ return '<%s %r>' % (
+ self.__class__.__name__,
+ self.name,
+ )
diff --git a/python/flask/blueprints.py b/python/flask/blueprints.py
new file mode 100644
index 0000000..c2158fe
--- /dev/null
+++ b/python/flask/blueprints.py
@@ -0,0 +1,447 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.blueprints
+ ~~~~~~~~~~~~~~~~
+
+ Blueprints are the recommended way to implement larger or more
+ pluggable applications in Flask 0.7 and later.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+from functools import update_wrapper
+
+from .helpers import _PackageBoundObject, _endpoint_from_view_func
+
+
+class BlueprintSetupState(object):
+ """Temporary holder object for registering a blueprint with the
+ application. An instance of this class is created by the
+ :meth:`~flask.Blueprint.make_setup_state` method and later passed
+ to all register callback functions.
+ """
+
+ def __init__(self, blueprint, app, options, first_registration):
+ #: a reference to the current application
+ self.app = app
+
+ #: a reference to the blueprint that created this setup state.
+ self.blueprint = blueprint
+
+ #: a dictionary with all options that were passed to the
+ #: :meth:`~flask.Flask.register_blueprint` method.
+ self.options = options
+
+ #: as blueprints can be registered multiple times with the
+ #: application and not everything wants to be registered
+ #: multiple times on it, this attribute can be used to figure
+ #: out if the blueprint was registered in the past already.
+ self.first_registration = first_registration
+
+ subdomain = self.options.get('subdomain')
+ if subdomain is None:
+ subdomain = self.blueprint.subdomain
+
+ #: The subdomain that the blueprint should be active for, ``None``
+ #: otherwise.
+ self.subdomain = subdomain
+
+ url_prefix = self.options.get('url_prefix')
+ if url_prefix is None:
+ url_prefix = self.blueprint.url_prefix
+ #: The prefix that should be used for all URLs defined on the
+ #: blueprint.
+ self.url_prefix = url_prefix
+
+ #: A dictionary with URL defaults that is added to each and every
+ #: URL that was defined with the blueprint.
+ self.url_defaults = dict(self.blueprint.url_values_defaults)
+ self.url_defaults.update(self.options.get('url_defaults', ()))
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """A helper method to register a rule (and optionally a view function)
+ to the application. The endpoint is automatically prefixed with the
+ blueprint's name.
+ """
+ if self.url_prefix is not None:
+ if rule:
+ rule = '/'.join((
+ self.url_prefix.rstrip('/'), rule.lstrip('/')))
+ else:
+ rule = self.url_prefix
+ options.setdefault('subdomain', self.subdomain)
+ if endpoint is None:
+ endpoint = _endpoint_from_view_func(view_func)
+ defaults = self.url_defaults
+ if 'defaults' in options:
+ defaults = dict(defaults, **options.pop('defaults'))
+ self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
+ view_func, defaults=defaults, **options)
+
+
+class Blueprint(_PackageBoundObject):
+ """Represents a blueprint. A blueprint is an object that records
+ functions that will be called with the
+ :class:`~flask.blueprints.BlueprintSetupState` later to register functions
+ or other things on the main application. See :ref:`blueprints` for more
+ information.
+
+ .. versionadded:: 0.7
+ """
+
+ warn_on_modifications = False
+ _got_registered_once = False
+
+ #: Blueprint local JSON decoder class to use.
+ #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_encoder`.
+ json_encoder = None
+ #: Blueprint local JSON decoder class to use.
+ #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_decoder`.
+ json_decoder = None
+
+ # TODO remove the next three attrs when Sphinx :inherited-members: works
+ # https://github.com/sphinx-doc/sphinx/issues/741
+
+ #: The name of the package or module that this app belongs to. Do not
+ #: change this once it is set by the constructor.
+ import_name = None
+
+ #: Location of the template files to be added to the template lookup.
+ #: ``None`` if templates should not be added.
+ template_folder = None
+
+ #: Absolute path to the package on the filesystem. Used to look up
+ #: resources contained in the package.
+ root_path = None
+
+ def __init__(self, name, import_name, static_folder=None,
+ static_url_path=None, template_folder=None,
+ url_prefix=None, subdomain=None, url_defaults=None,
+ root_path=None):
+ _PackageBoundObject.__init__(self, import_name, template_folder,
+ root_path=root_path)
+ self.name = name
+ self.url_prefix = url_prefix
+ self.subdomain = subdomain
+ self.static_folder = static_folder
+ self.static_url_path = static_url_path
+ self.deferred_functions = []
+ if url_defaults is None:
+ url_defaults = {}
+ self.url_values_defaults = url_defaults
+
+ def record(self, func):
+ """Registers a function that is called when the blueprint is
+ registered on the application. This function is called with the
+ state as argument as returned by the :meth:`make_setup_state`
+ method.
+ """
+ if self._got_registered_once and self.warn_on_modifications:
+ from warnings import warn
+ warn(Warning('The blueprint was already registered once '
+ 'but is getting modified now. These changes '
+ 'will not show up.'))
+ self.deferred_functions.append(func)
+
+ def record_once(self, func):
+ """Works like :meth:`record` but wraps the function in another
+ function that will ensure the function is only called once. If the
+ blueprint is registered a second time on the application, the
+ function passed is not called.
+ """
+ def wrapper(state):
+ if state.first_registration:
+ func(state)
+ return self.record(update_wrapper(wrapper, func))
+
+ def make_setup_state(self, app, options, first_registration=False):
+ """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
+ object that is later passed to the register callback functions.
+ Subclasses can override this to return a subclass of the setup state.
+ """
+ return BlueprintSetupState(self, app, options, first_registration)
+
+ def register(self, app, options, first_registration=False):
+ """Called by :meth:`Flask.register_blueprint` to register all views
+ and callbacks registered on the blueprint with the application. Creates
+ a :class:`.BlueprintSetupState` and calls each :meth:`record` callback
+ with it.
+
+ :param app: The application this blueprint is being registered with.
+ :param options: Keyword arguments forwarded from
+ :meth:`~Flask.register_blueprint`.
+ :param first_registration: Whether this is the first time this
+ blueprint has been registered on the application.
+ """
+ self._got_registered_once = True
+ state = self.make_setup_state(app, options, first_registration)
+
+ if self.has_static_folder:
+ state.add_url_rule(
+ self.static_url_path + '/<path:filename>',
+ view_func=self.send_static_file, endpoint='static'
+ )
+
+ for deferred in self.deferred_functions:
+ deferred(state)
+
+ def route(self, rule, **options):
+ """Like :meth:`Flask.route` but for a blueprint. The endpoint for the
+ :func:`url_for` function is prefixed with the name of the blueprint.
+ """
+ def decorator(f):
+ endpoint = options.pop("endpoint", f.__name__)
+ self.add_url_rule(rule, endpoint, f, **options)
+ return f
+ return decorator
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
+ the :func:`url_for` function is prefixed with the name of the blueprint.
+ """
+ if endpoint:
+ assert '.' not in endpoint, "Blueprint endpoints should not contain dots"
+ if view_func and hasattr(view_func, '__name__'):
+ assert '.' not in view_func.__name__, "Blueprint view function name should not contain dots"
+ self.record(lambda s:
+ s.add_url_rule(rule, endpoint, view_func, **options))
+
+ def endpoint(self, endpoint):
+ """Like :meth:`Flask.endpoint` but for a blueprint. This does not
+ prefix the endpoint with the blueprint name, this has to be done
+ explicitly by the user of this method. If the endpoint is prefixed
+ with a `.` it will be registered to the current blueprint, otherwise
+ it's an application independent endpoint.
+ """
+ def decorator(f):
+ def register_endpoint(state):
+ state.app.view_functions[endpoint] = f
+ self.record_once(register_endpoint)
+ return f
+ return decorator
+
+ def app_template_filter(self, name=None):
+ """Register a custom template filter, available application wide. Like
+ :meth:`Flask.template_filter` but for a blueprint.
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.add_app_template_filter(f, name=name)
+ return f
+ return decorator
+
+ def add_app_template_filter(self, f, name=None):
+ """Register a custom template filter, available application wide. Like
+ :meth:`Flask.add_template_filter` but for a blueprint. Works exactly
+ like the :meth:`app_template_filter` decorator.
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ def register_template(state):
+ state.app.jinja_env.filters[name or f.__name__] = f
+ self.record_once(register_template)
+
+ def app_template_test(self, name=None):
+ """Register a custom template test, available application wide. Like
+ :meth:`Flask.template_test` but for a blueprint.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the test, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.add_app_template_test(f, name=name)
+ return f
+ return decorator
+
+ def add_app_template_test(self, f, name=None):
+ """Register a custom template test, available application wide. Like
+ :meth:`Flask.add_template_test` but for a blueprint. Works exactly
+ like the :meth:`app_template_test` decorator.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the test, otherwise the
+ function name will be used.
+ """
+ def register_template(state):
+ state.app.jinja_env.tests[name or f.__name__] = f
+ self.record_once(register_template)
+
+ def app_template_global(self, name=None):
+ """Register a custom template global, available application wide. Like
+ :meth:`Flask.template_global` but for a blueprint.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the global, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.add_app_template_global(f, name=name)
+ return f
+ return decorator
+
+ def add_app_template_global(self, f, name=None):
+ """Register a custom template global, available application wide. Like
+ :meth:`Flask.add_template_global` but for a blueprint. Works exactly
+ like the :meth:`app_template_global` decorator.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the global, otherwise the
+ function name will be used.
+ """
+ def register_template(state):
+ state.app.jinja_env.globals[name or f.__name__] = f
+ self.record_once(register_template)
+
+ def before_request(self, f):
+ """Like :meth:`Flask.before_request` but for a blueprint. This function
+ is only executed before each request that is handled by a function of
+ that blueprint.
+ """
+ self.record_once(lambda s: s.app.before_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def before_app_request(self, f):
+ """Like :meth:`Flask.before_request`. Such a function is executed
+ before each request, even if outside of a blueprint.
+ """
+ self.record_once(lambda s: s.app.before_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def before_app_first_request(self, f):
+ """Like :meth:`Flask.before_first_request`. Such a function is
+ executed before the first request to the application.
+ """
+ self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
+ return f
+
+ def after_request(self, f):
+ """Like :meth:`Flask.after_request` but for a blueprint. This function
+ is only executed after each request that is handled by a function of
+ that blueprint.
+ """
+ self.record_once(lambda s: s.app.after_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def after_app_request(self, f):
+ """Like :meth:`Flask.after_request` but for a blueprint. Such a function
+ is executed after each request, even if outside of the blueprint.
+ """
+ self.record_once(lambda s: s.app.after_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def teardown_request(self, f):
+ """Like :meth:`Flask.teardown_request` but for a blueprint. This
+ function is only executed when tearing down requests handled by a
+ function of that blueprint. Teardown request functions are executed
+ when the request context is popped, even when no actual request was
+ performed.
+ """
+ self.record_once(lambda s: s.app.teardown_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def teardown_app_request(self, f):
+ """Like :meth:`Flask.teardown_request` but for a blueprint. Such a
+ function is executed when tearing down each request, even if outside of
+ the blueprint.
+ """
+ self.record_once(lambda s: s.app.teardown_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def context_processor(self, f):
+ """Like :meth:`Flask.context_processor` but for a blueprint. This
+ function is only executed for requests handled by a blueprint.
+ """
+ self.record_once(lambda s: s.app.template_context_processors
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def app_context_processor(self, f):
+ """Like :meth:`Flask.context_processor` but for a blueprint. Such a
+ function is executed each request, even if outside of the blueprint.
+ """
+ self.record_once(lambda s: s.app.template_context_processors
+ .setdefault(None, []).append(f))
+ return f
+
+ def app_errorhandler(self, code):
+ """Like :meth:`Flask.errorhandler` but for a blueprint. This
+ handler is used for all requests, even if outside of the blueprint.
+ """
+ def decorator(f):
+ self.record_once(lambda s: s.app.errorhandler(code)(f))
+ return f
+ return decorator
+
+ def url_value_preprocessor(self, f):
+ """Registers a function as URL value preprocessor for this
+ blueprint. It's called before the view functions are called and
+ can modify the url values provided.
+ """
+ self.record_once(lambda s: s.app.url_value_preprocessors
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def url_defaults(self, f):
+ """Callback function for URL defaults for this blueprint. It's called
+ with the endpoint and values and should update the values passed
+ in place.
+ """
+ self.record_once(lambda s: s.app.url_default_functions
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def app_url_value_preprocessor(self, f):
+ """Same as :meth:`url_value_preprocessor` but application wide.
+ """
+ self.record_once(lambda s: s.app.url_value_preprocessors
+ .setdefault(None, []).append(f))
+ return f
+
+ def app_url_defaults(self, f):
+ """Same as :meth:`url_defaults` but application wide.
+ """
+ self.record_once(lambda s: s.app.url_default_functions
+ .setdefault(None, []).append(f))
+ return f
+
+ def errorhandler(self, code_or_exception):
+ """Registers an error handler that becomes active for this blueprint
+ only. Please be aware that routing does not happen local to a
+ blueprint so an error handler for 404 usually is not handled by
+ a blueprint unless it is caused inside a view function. Another
+ special case is the 500 internal server error which is always looked
+ up from the application.
+
+ Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator
+ of the :class:`~flask.Flask` object.
+ """
+ def decorator(f):
+ self.record_once(lambda s: s.app._register_error_handler(
+ self.name, code_or_exception, f))
+ return f
+ return decorator
+
+ def register_error_handler(self, code_or_exception, f):
+ """Non-decorator version of the :meth:`errorhandler` error attach
+ function, akin to the :meth:`~flask.Flask.register_error_handler`
+ application-wide function of the :class:`~flask.Flask` object but
+ for error handlers limited to this blueprint.
+
+ .. versionadded:: 0.11
+ """
+ self.record_once(lambda s: s.app._register_error_handler(
+ self.name, code_or_exception, f))
diff --git a/python/flask/cli.py b/python/flask/cli.py
new file mode 100644
index 0000000..3eb93b3
--- /dev/null
+++ b/python/flask/cli.py
@@ -0,0 +1,910 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.cli
+ ~~~~~~~~~
+
+ A simple command line application to run flask apps.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import print_function
+
+import ast
+import inspect
+import os
+import platform
+import re
+import ssl
+import sys
+import traceback
+from functools import update_wrapper
+from operator import attrgetter
+from threading import Lock, Thread
+
+import click
+from werkzeug.utils import import_string
+
+from . import __version__
+from ._compat import getargspec, iteritems, reraise, text_type
+from .globals import current_app
+from .helpers import get_debug_flag, get_env, get_load_dotenv
+
+try:
+ import dotenv
+except ImportError:
+ dotenv = None
+
+
+class NoAppException(click.UsageError):
+ """Raised if an application cannot be found or loaded."""
+
+
+def find_best_app(script_info, module):
+ """Given a module instance this tries to find the best possible
+ application in the module or raises an exception.
+ """
+ from . import Flask
+
+ # Search for the most common names first.
+ for attr_name in ('app', 'application'):
+ app = getattr(module, attr_name, None)
+
+ if isinstance(app, Flask):
+ return app
+
+ # Otherwise find the only object that is a Flask instance.
+ matches = [
+ v for k, v in iteritems(module.__dict__) if isinstance(v, Flask)
+ ]
+
+ if len(matches) == 1:
+ return matches[0]
+ elif len(matches) > 1:
+ raise NoAppException(
+ 'Detected multiple Flask applications in module "{module}". Use '
+ '"FLASK_APP={module}:name" to specify the correct '
+ 'one.'.format(module=module.__name__)
+ )
+
+ # Search for app factory functions.
+ for attr_name in ('create_app', 'make_app'):
+ app_factory = getattr(module, attr_name, None)
+
+ if inspect.isfunction(app_factory):
+ try:
+ app = call_factory(script_info, app_factory)
+
+ if isinstance(app, Flask):
+ return app
+ except TypeError:
+ if not _called_with_wrong_args(app_factory):
+ raise
+ raise NoAppException(
+ 'Detected factory "{factory}" in module "{module}", but '
+ 'could not call it without arguments. Use '
+ '"FLASK_APP=\'{module}:{factory}(args)\'" to specify '
+ 'arguments.'.format(
+ factory=attr_name, module=module.__name__
+ )
+ )
+
+ raise NoAppException(
+ 'Failed to find Flask application or factory in module "{module}". '
+ 'Use "FLASK_APP={module}:name to specify one.'.format(
+ module=module.__name__
+ )
+ )
+
+
+def call_factory(script_info, app_factory, arguments=()):
+ """Takes an app factory, a ``script_info` object and optionally a tuple
+ of arguments. Checks for the existence of a script_info argument and calls
+ the app_factory depending on that and the arguments provided.
+ """
+ args_spec = getargspec(app_factory)
+ arg_names = args_spec.args
+ arg_defaults = args_spec.defaults
+
+ if 'script_info' in arg_names:
+ return app_factory(*arguments, script_info=script_info)
+ elif arguments:
+ return app_factory(*arguments)
+ elif not arguments and len(arg_names) == 1 and arg_defaults is None:
+ return app_factory(script_info)
+
+ return app_factory()
+
+
+def _called_with_wrong_args(factory):
+ """Check whether calling a function raised a ``TypeError`` because
+ the call failed or because something in the factory raised the
+ error.
+
+ :param factory: the factory function that was called
+ :return: true if the call failed
+ """
+ tb = sys.exc_info()[2]
+
+ try:
+ while tb is not None:
+ if tb.tb_frame.f_code is factory.__code__:
+ # in the factory, it was called successfully
+ return False
+
+ tb = tb.tb_next
+
+ # didn't reach the factory
+ return True
+ finally:
+ del tb
+
+
+def find_app_by_string(script_info, module, app_name):
+ """Checks if the given string is a variable name or a function. If it is a
+ function, it checks for specified arguments and whether it takes a
+ ``script_info`` argument and calls the function with the appropriate
+ arguments.
+ """
+ from flask import Flask
+ match = re.match(r'^ *([^ ()]+) *(?:\((.*?) *,? *\))? *$', app_name)
+
+ if not match:
+ raise NoAppException(
+ '"{name}" is not a valid variable name or function '
+ 'expression.'.format(name=app_name)
+ )
+
+ name, args = match.groups()
+
+ try:
+ attr = getattr(module, name)
+ except AttributeError as e:
+ raise NoAppException(e.args[0])
+
+ if inspect.isfunction(attr):
+ if args:
+ try:
+ args = ast.literal_eval('({args},)'.format(args=args))
+ except (ValueError, SyntaxError)as e:
+ raise NoAppException(
+ 'Could not parse the arguments in '
+ '"{app_name}".'.format(e=e, app_name=app_name)
+ )
+ else:
+ args = ()
+
+ try:
+ app = call_factory(script_info, attr, args)
+ except TypeError as e:
+ if not _called_with_wrong_args(attr):
+ raise
+
+ raise NoAppException(
+ '{e}\nThe factory "{app_name}" in module "{module}" could not '
+ 'be called with the specified arguments.'.format(
+ e=e, app_name=app_name, module=module.__name__
+ )
+ )
+ else:
+ app = attr
+
+ if isinstance(app, Flask):
+ return app
+
+ raise NoAppException(
+ 'A valid Flask application was not obtained from '
+ '"{module}:{app_name}".'.format(
+ module=module.__name__, app_name=app_name
+ )
+ )
+
+
+def prepare_import(path):
+ """Given a filename this will try to calculate the python path, add it
+ to the search path and return the actual module name that is expected.
+ """
+ path = os.path.realpath(path)
+
+ if os.path.splitext(path)[1] == '.py':
+ path = os.path.splitext(path)[0]
+
+ if os.path.basename(path) == '__init__':
+ path = os.path.dirname(path)
+
+ module_name = []
+
+ # move up until outside package structure (no __init__.py)
+ while True:
+ path, name = os.path.split(path)
+ module_name.append(name)
+
+ if not os.path.exists(os.path.join(path, '__init__.py')):
+ break
+
+ if sys.path[0] != path:
+ sys.path.insert(0, path)
+
+ return '.'.join(module_name[::-1])
+
+
+def locate_app(script_info, module_name, app_name, raise_if_not_found=True):
+ __traceback_hide__ = True
+
+ try:
+ __import__(module_name)
+ except ImportError:
+ # Reraise the ImportError if it occurred within the imported module.
+ # Determine this by checking whether the trace has a depth > 1.
+ if sys.exc_info()[-1].tb_next:
+ raise NoAppException(
+ 'While importing "{name}", an ImportError was raised:'
+ '\n\n{tb}'.format(name=module_name, tb=traceback.format_exc())
+ )
+ elif raise_if_not_found:
+ raise NoAppException(
+ 'Could not import "{name}".'.format(name=module_name)
+ )
+ else:
+ return
+
+ module = sys.modules[module_name]
+
+ if app_name is None:
+ return find_best_app(script_info, module)
+ else:
+ return find_app_by_string(script_info, module, app_name)
+
+
+def get_version(ctx, param, value):
+ if not value or ctx.resilient_parsing:
+ return
+ import werkzeug
+ message = (
+ 'Python %(python)s\n'
+ 'Flask %(flask)s\n'
+ 'Werkzeug %(werkzeug)s'
+ )
+ click.echo(message % {
+ 'python': platform.python_version(),
+ 'flask': __version__,
+ 'werkzeug': werkzeug.__version__,
+ }, color=ctx.color)
+ ctx.exit()
+
+
+version_option = click.Option(
+ ['--version'],
+ help='Show the flask version',
+ expose_value=False,
+ callback=get_version,
+ is_flag=True,
+ is_eager=True
+)
+
+
+class DispatchingApp(object):
+ """Special application that dispatches to a Flask application which
+ is imported by name in a background thread. If an error happens
+ it is recorded and shown as part of the WSGI handling which in case
+ of the Werkzeug debugger means that it shows up in the browser.
+ """
+
+ def __init__(self, loader, use_eager_loading=False):
+ self.loader = loader
+ self._app = None
+ self._lock = Lock()
+ self._bg_loading_exc_info = None
+ if use_eager_loading:
+ self._load_unlocked()
+ else:
+ self._load_in_background()
+
+ def _load_in_background(self):
+ def _load_app():
+ __traceback_hide__ = True
+ with self._lock:
+ try:
+ self._load_unlocked()
+ except Exception:
+ self._bg_loading_exc_info = sys.exc_info()
+ t = Thread(target=_load_app, args=())
+ t.start()
+
+ def _flush_bg_loading_exception(self):
+ __traceback_hide__ = True
+ exc_info = self._bg_loading_exc_info
+ if exc_info is not None:
+ self._bg_loading_exc_info = None
+ reraise(*exc_info)
+
+ def _load_unlocked(self):
+ __traceback_hide__ = True
+ self._app = rv = self.loader()
+ self._bg_loading_exc_info = None
+ return rv
+
+ def __call__(self, environ, start_response):
+ __traceback_hide__ = True
+ if self._app is not None:
+ return self._app(environ, start_response)
+ self._flush_bg_loading_exception()
+ with self._lock:
+ if self._app is not None:
+ rv = self._app
+ else:
+ rv = self._load_unlocked()
+ return rv(environ, start_response)
+
+
+class ScriptInfo(object):
+ """Helper object to deal with Flask applications. This is usually not
+ necessary to interface with as it's used internally in the dispatching
+ to click. In future versions of Flask this object will most likely play
+ a bigger role. Typically it's created automatically by the
+ :class:`FlaskGroup` but you can also manually create it and pass it
+ onwards as click object.
+ """
+
+ def __init__(self, app_import_path=None, create_app=None,
+ set_debug_flag=True):
+ #: Optionally the import path for the Flask application.
+ self.app_import_path = app_import_path or os.environ.get('FLASK_APP')
+ #: Optionally a function that is passed the script info to create
+ #: the instance of the application.
+ self.create_app = create_app
+ #: A dictionary with arbitrary data that can be associated with
+ #: this script info.
+ self.data = {}
+ self.set_debug_flag = set_debug_flag
+ self._loaded_app = None
+
+ def load_app(self):
+ """Loads the Flask app (if not yet loaded) and returns it. Calling
+ this multiple times will just result in the already loaded app to
+ be returned.
+ """
+ __traceback_hide__ = True
+
+ if self._loaded_app is not None:
+ return self._loaded_app
+
+ app = None
+
+ if self.create_app is not None:
+ app = call_factory(self, self.create_app)
+ else:
+ if self.app_import_path:
+ path, name = (re.split(r':(?![\\/])', self.app_import_path, 1) + [None])[:2]
+ import_name = prepare_import(path)
+ app = locate_app(self, import_name, name)
+ else:
+ for path in ('wsgi.py', 'app.py'):
+ import_name = prepare_import(path)
+ app = locate_app(self, import_name, None,
+ raise_if_not_found=False)
+
+ if app:
+ break
+
+ if not app:
+ raise NoAppException(
+ 'Could not locate a Flask application. You did not provide '
+ 'the "FLASK_APP" environment variable, and a "wsgi.py" or '
+ '"app.py" module was not found in the current directory.'
+ )
+
+ if self.set_debug_flag:
+ # Update the app's debug flag through the descriptor so that
+ # other values repopulate as well.
+ app.debug = get_debug_flag()
+
+ self._loaded_app = app
+ return app
+
+
+pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True)
+
+
+def with_appcontext(f):
+ """Wraps a callback so that it's guaranteed to be executed with the
+ script's application context. If callbacks are registered directly
+ to the ``app.cli`` object then they are wrapped with this function
+ by default unless it's disabled.
+ """
+ @click.pass_context
+ def decorator(__ctx, *args, **kwargs):
+ with __ctx.ensure_object(ScriptInfo).load_app().app_context():
+ return __ctx.invoke(f, *args, **kwargs)
+ return update_wrapper(decorator, f)
+
+
+class AppGroup(click.Group):
+ """This works similar to a regular click :class:`~click.Group` but it
+ changes the behavior of the :meth:`command` decorator so that it
+ automatically wraps the functions in :func:`with_appcontext`.
+
+ Not to be confused with :class:`FlaskGroup`.
+ """
+
+ def command(self, *args, **kwargs):
+ """This works exactly like the method of the same name on a regular
+ :class:`click.Group` but it wraps callbacks in :func:`with_appcontext`
+ unless it's disabled by passing ``with_appcontext=False``.
+ """
+ wrap_for_ctx = kwargs.pop('with_appcontext', True)
+ def decorator(f):
+ if wrap_for_ctx:
+ f = with_appcontext(f)
+ return click.Group.command(self, *args, **kwargs)(f)
+ return decorator
+
+ def group(self, *args, **kwargs):
+ """This works exactly like the method of the same name on a regular
+ :class:`click.Group` but it defaults the group class to
+ :class:`AppGroup`.
+ """
+ kwargs.setdefault('cls', AppGroup)
+ return click.Group.group(self, *args, **kwargs)
+
+
+class FlaskGroup(AppGroup):
+ """Special subclass of the :class:`AppGroup` group that supports
+ loading more commands from the configured Flask app. Normally a
+ developer does not have to interface with this class but there are
+ some very advanced use cases for which it makes sense to create an
+ instance of this.
+
+ For information as of why this is useful see :ref:`custom-scripts`.
+
+ :param add_default_commands: if this is True then the default run and
+ shell commands wil be added.
+ :param add_version_option: adds the ``--version`` option.
+ :param create_app: an optional callback that is passed the script info and
+ returns the loaded app.
+ :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`
+ files to set environment variables. Will also change the working
+ directory to the directory containing the first file found.
+ :param set_debug_flag: Set the app's debug flag based on the active
+ environment
+
+ .. versionchanged:: 1.0
+ If installed, python-dotenv will be used to load environment variables
+ from :file:`.env` and :file:`.flaskenv` files.
+ """
+
+ def __init__(self, add_default_commands=True, create_app=None,
+ add_version_option=True, load_dotenv=True,
+ set_debug_flag=True, **extra):
+ params = list(extra.pop('params', None) or ())
+
+ if add_version_option:
+ params.append(version_option)
+
+ AppGroup.__init__(self, params=params, **extra)
+ self.create_app = create_app
+ self.load_dotenv = load_dotenv
+ self.set_debug_flag = set_debug_flag
+
+ if add_default_commands:
+ self.add_command(run_command)
+ self.add_command(shell_command)
+ self.add_command(routes_command)
+
+ self._loaded_plugin_commands = False
+
+ def _load_plugin_commands(self):
+ if self._loaded_plugin_commands:
+ return
+ try:
+ import pkg_resources
+ except ImportError:
+ self._loaded_plugin_commands = True
+ return
+
+ for ep in pkg_resources.iter_entry_points('flask.commands'):
+ self.add_command(ep.load(), ep.name)
+ self._loaded_plugin_commands = True
+
+ def get_command(self, ctx, name):
+ self._load_plugin_commands()
+
+ # We load built-in commands first as these should always be the
+ # same no matter what the app does. If the app does want to
+ # override this it needs to make a custom instance of this group
+ # and not attach the default commands.
+ #
+ # This also means that the script stays functional in case the
+ # application completely fails.
+ rv = AppGroup.get_command(self, ctx, name)
+ if rv is not None:
+ return rv
+
+ info = ctx.ensure_object(ScriptInfo)
+ try:
+ rv = info.load_app().cli.get_command(ctx, name)
+ if rv is not None:
+ return rv
+ except NoAppException:
+ pass
+
+ def list_commands(self, ctx):
+ self._load_plugin_commands()
+
+ # The commands available is the list of both the application (if
+ # available) plus the builtin commands.
+ rv = set(click.Group.list_commands(self, ctx))
+ info = ctx.ensure_object(ScriptInfo)
+ try:
+ rv.update(info.load_app().cli.list_commands(ctx))
+ except Exception:
+ # Here we intentionally swallow all exceptions as we don't
+ # want the help page to break if the app does not exist.
+ # If someone attempts to use the command we try to create
+ # the app again and this will give us the error.
+ # However, we will not do so silently because that would confuse
+ # users.
+ traceback.print_exc()
+ return sorted(rv)
+
+ def main(self, *args, **kwargs):
+ # Set a global flag that indicates that we were invoked from the
+ # command line interface. This is detected by Flask.run to make the
+ # call into a no-op. This is necessary to avoid ugly errors when the
+ # script that is loaded here also attempts to start a server.
+ os.environ['FLASK_RUN_FROM_CLI'] = 'true'
+
+ if get_load_dotenv(self.load_dotenv):
+ load_dotenv()
+
+ obj = kwargs.get('obj')
+
+ if obj is None:
+ obj = ScriptInfo(create_app=self.create_app,
+ set_debug_flag=self.set_debug_flag)
+
+ kwargs['obj'] = obj
+ kwargs.setdefault('auto_envvar_prefix', 'FLASK')
+ return super(FlaskGroup, self).main(*args, **kwargs)
+
+
+def _path_is_ancestor(path, other):
+ """Take ``other`` and remove the length of ``path`` from it. Then join it
+ to ``path``. If it is the original value, ``path`` is an ancestor of
+ ``other``."""
+ return os.path.join(path, other[len(path):].lstrip(os.sep)) == other
+
+
+def load_dotenv(path=None):
+ """Load "dotenv" files in order of precedence to set environment variables.
+
+ If an env var is already set it is not overwritten, so earlier files in the
+ list are preferred over later files.
+
+ Changes the current working directory to the location of the first file
+ found, with the assumption that it is in the top level project directory
+ and will be where the Python path should import local packages from.
+
+ This is a no-op if `python-dotenv`_ is not installed.
+
+ .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
+
+ :param path: Load the file at this location instead of searching.
+ :return: ``True`` if a file was loaded.
+
+ .. versionadded:: 1.0
+ """
+ if dotenv is None:
+ if path or os.path.exists('.env') or os.path.exists('.flaskenv'):
+ click.secho(
+ ' * Tip: There are .env files present.'
+ ' Do "pip install python-dotenv" to use them.',
+ fg='yellow')
+ return
+
+ if path is not None:
+ return dotenv.load_dotenv(path)
+
+ new_dir = None
+
+ for name in ('.env', '.flaskenv'):
+ path = dotenv.find_dotenv(name, usecwd=True)
+
+ if not path:
+ continue
+
+ if new_dir is None:
+ new_dir = os.path.dirname(path)
+
+ dotenv.load_dotenv(path)
+
+ if new_dir and os.getcwd() != new_dir:
+ os.chdir(new_dir)
+
+ return new_dir is not None # at least one file was located and loaded
+
+
+def show_server_banner(env, debug, app_import_path, eager_loading):
+ """Show extra startup messages the first time the server is run,
+ ignoring the reloader.
+ """
+ if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
+ return
+
+ if app_import_path is not None:
+ message = ' * Serving Flask app "{0}"'.format(app_import_path)
+
+ if not eager_loading:
+ message += ' (lazy loading)'
+
+ click.echo(message)
+
+ click.echo(' * Environment: {0}'.format(env))
+
+ if env == 'production':
+ click.secho(
+ ' WARNING: This is a development server. '
+ 'Do not use it in a production deployment.', fg='red')
+ click.secho(' Use a production WSGI server instead.', dim=True)
+
+ if debug is not None:
+ click.echo(' * Debug mode: {0}'.format('on' if debug else 'off'))
+
+
+class CertParamType(click.ParamType):
+ """Click option type for the ``--cert`` option. Allows either an
+ existing file, the string ``'adhoc'``, or an import for a
+ :class:`~ssl.SSLContext` object.
+ """
+
+ name = 'path'
+
+ def __init__(self):
+ self.path_type = click.Path(
+ exists=True, dir_okay=False, resolve_path=True)
+
+ def convert(self, value, param, ctx):
+ try:
+ return self.path_type(value, param, ctx)
+ except click.BadParameter:
+ value = click.STRING(value, param, ctx).lower()
+
+ if value == 'adhoc':
+ try:
+ import OpenSSL
+ except ImportError:
+ raise click.BadParameter(
+ 'Using ad-hoc certificates requires pyOpenSSL.',
+ ctx, param)
+
+ return value
+
+ obj = import_string(value, silent=True)
+
+ if sys.version_info < (2, 7, 9):
+ if obj:
+ return obj
+ else:
+ if isinstance(obj, ssl.SSLContext):
+ return obj
+
+ raise
+
+
+def _validate_key(ctx, param, value):
+ """The ``--key`` option must be specified when ``--cert`` is a file.
+ Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed.
+ """
+ cert = ctx.params.get('cert')
+ is_adhoc = cert == 'adhoc'
+
+ if sys.version_info < (2, 7, 9):
+ is_context = cert and not isinstance(cert, (text_type, bytes))
+ else:
+ is_context = isinstance(cert, ssl.SSLContext)
+
+ if value is not None:
+ if is_adhoc:
+ raise click.BadParameter(
+ 'When "--cert" is "adhoc", "--key" is not used.',
+ ctx, param)
+
+ if is_context:
+ raise click.BadParameter(
+ 'When "--cert" is an SSLContext object, "--key is not used.',
+ ctx, param)
+
+ if not cert:
+ raise click.BadParameter(
+ '"--cert" must also be specified.',
+ ctx, param)
+
+ ctx.params['cert'] = cert, value
+
+ else:
+ if cert and not (is_adhoc or is_context):
+ raise click.BadParameter(
+ 'Required when using "--cert".',
+ ctx, param)
+
+ return value
+
+
+@click.command('run', short_help='Run a development server.')
+@click.option('--host', '-h', default='127.0.0.1',
+ help='The interface to bind to.')
+@click.option('--port', '-p', default=5000,
+ help='The port to bind to.')
+@click.option('--cert', type=CertParamType(),
+ help='Specify a certificate file to use HTTPS.')
+@click.option('--key',
+ type=click.Path(exists=True, dir_okay=False, resolve_path=True),
+ callback=_validate_key, expose_value=False,
+ help='The key file to use when specifying a certificate.')
+@click.option('--reload/--no-reload', default=None,
+ help='Enable or disable the reloader. By default the reloader '
+ 'is active if debug is enabled.')
+@click.option('--debugger/--no-debugger', default=None,
+ help='Enable or disable the debugger. By default the debugger '
+ 'is active if debug is enabled.')
+@click.option('--eager-loading/--lazy-loader', default=None,
+ help='Enable or disable eager loading. By default eager '
+ 'loading is enabled if the reloader is disabled.')
+@click.option('--with-threads/--without-threads', default=True,
+ help='Enable or disable multithreading.')
+@pass_script_info
+def run_command(info, host, port, reload, debugger, eager_loading,
+ with_threads, cert):
+ """Run a local development server.
+
+ This server is for development purposes only. It does not provide
+ the stability, security, or performance of production WSGI servers.
+
+ The reloader and debugger are enabled by default if
+ FLASK_ENV=development or FLASK_DEBUG=1.
+ """
+ debug = get_debug_flag()
+
+ if reload is None:
+ reload = debug
+
+ if debugger is None:
+ debugger = debug
+
+ if eager_loading is None:
+ eager_loading = not reload
+
+ show_server_banner(get_env(), debug, info.app_import_path, eager_loading)
+ app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)
+
+ from werkzeug.serving import run_simple
+ run_simple(host, port, app, use_reloader=reload, use_debugger=debugger,
+ threaded=with_threads, ssl_context=cert)
+
+
+@click.command('shell', short_help='Run a shell in the app context.')
+@with_appcontext
+def shell_command():
+ """Run an interactive Python shell in the context of a given
+ Flask application. The application will populate the default
+ namespace of this shell according to it's configuration.
+
+ This is useful for executing small snippets of management code
+ without having to manually configure the application.
+ """
+ import code
+ from flask.globals import _app_ctx_stack
+ app = _app_ctx_stack.top.app
+ banner = 'Python %s on %s\nApp: %s [%s]\nInstance: %s' % (
+ sys.version,
+ sys.platform,
+ app.import_name,
+ app.env,
+ app.instance_path,
+ )
+ ctx = {}
+
+ # Support the regular Python interpreter startup script if someone
+ # is using it.
+ startup = os.environ.get('PYTHONSTARTUP')
+ if startup and os.path.isfile(startup):
+ with open(startup, 'r') as f:
+ eval(compile(f.read(), startup, 'exec'), ctx)
+
+ ctx.update(app.make_shell_context())
+
+ code.interact(banner=banner, local=ctx)
+
+
+@click.command('routes', short_help='Show the routes for the app.')
+@click.option(
+ '--sort', '-s',
+ type=click.Choice(('endpoint', 'methods', 'rule', 'match')),
+ default='endpoint',
+ help=(
+ 'Method to sort routes by. "match" is the order that Flask will match '
+ 'routes when dispatching a request.'
+ )
+)
+@click.option(
+ '--all-methods',
+ is_flag=True,
+ help="Show HEAD and OPTIONS methods."
+)
+@with_appcontext
+def routes_command(sort, all_methods):
+ """Show all registered routes with endpoints and methods."""
+
+ rules = list(current_app.url_map.iter_rules())
+ if not rules:
+ click.echo('No routes were registered.')
+ return
+
+ ignored_methods = set(() if all_methods else ('HEAD', 'OPTIONS'))
+
+ if sort in ('endpoint', 'rule'):
+ rules = sorted(rules, key=attrgetter(sort))
+ elif sort == 'methods':
+ rules = sorted(rules, key=lambda rule: sorted(rule.methods))
+
+ rule_methods = [
+ ', '.join(sorted(rule.methods - ignored_methods)) for rule in rules
+ ]
+
+ headers = ('Endpoint', 'Methods', 'Rule')
+ widths = (
+ max(len(rule.endpoint) for rule in rules),
+ max(len(methods) for methods in rule_methods),
+ max(len(rule.rule) for rule in rules),
+ )
+ widths = [max(len(h), w) for h, w in zip(headers, widths)]
+ row = '{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}'.format(*widths)
+
+ click.echo(row.format(*headers).strip())
+ click.echo(row.format(*('-' * width for width in widths)))
+
+ for rule, methods in zip(rules, rule_methods):
+ click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip())
+
+
+cli = FlaskGroup(help="""\
+A general utility script for Flask applications.
+
+Provides commands from Flask, extensions, and the application. Loads the
+application defined in the FLASK_APP environment variable, or from a wsgi.py
+file. Setting the FLASK_ENV environment variable to 'development' will enable
+debug mode.
+
+\b
+ {prefix}{cmd} FLASK_APP=hello.py
+ {prefix}{cmd} FLASK_ENV=development
+ {prefix}flask run
+""".format(
+ cmd='export' if os.name == 'posix' else 'set',
+ prefix='$ ' if os.name == 'posix' else '> '
+))
+
+
+def main(as_module=False):
+ args = sys.argv[1:]
+
+ if as_module:
+ this_module = 'flask'
+
+ if sys.version_info < (2, 7):
+ this_module += '.cli'
+
+ name = 'python -m ' + this_module
+
+ # Python rewrites "python -m flask" to the path to the file in argv.
+ # Restore the original command so that the reloader works.
+ sys.argv = ['-m', this_module] + args
+ else:
+ name = None
+
+ cli.main(args=args, prog_name=name)
+
+
+if __name__ == '__main__':
+ main(as_module=True)
diff --git a/python/flask/config.py b/python/flask/config.py
new file mode 100644
index 0000000..a5475ed
--- /dev/null
+++ b/python/flask/config.py
@@ -0,0 +1,269 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.config
+ ~~~~~~~~~~~~
+
+ Implements the configuration related objects.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+import types
+import errno
+
+from werkzeug.utils import import_string
+from ._compat import string_types, iteritems
+from . import json
+
+
+class ConfigAttribute(object):
+ """Makes an attribute forward to the config"""
+
+ def __init__(self, name, get_converter=None):
+ self.__name__ = name
+ self.get_converter = get_converter
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ rv = obj.config[self.__name__]
+ if self.get_converter is not None:
+ rv = self.get_converter(rv)
+ return rv
+
+ def __set__(self, obj, value):
+ obj.config[self.__name__] = value
+
+
+class Config(dict):
+ """Works exactly like a dict but provides ways to fill it from files
+ or special dictionaries. There are two common patterns to populate the
+ config.
+
+ Either you can fill the config from a config file::
+
+ app.config.from_pyfile('yourconfig.cfg')
+
+ Or alternatively you can define the configuration options in the
+ module that calls :meth:`from_object` or provide an import path to
+ a module that should be loaded. It is also possible to tell it to
+ use the same module and with that provide the configuration values
+ just before the call::
+
+ DEBUG = True
+ SECRET_KEY = 'development key'
+ app.config.from_object(__name__)
+
+ In both cases (loading from any Python file or loading from modules),
+ only uppercase keys are added to the config. This makes it possible to use
+ lowercase values in the config file for temporary values that are not added
+ to the config or to define the config keys in the same file that implements
+ the application.
+
+ Probably the most interesting way to load configurations is from an
+ environment variable pointing to a file::
+
+ app.config.from_envvar('YOURAPPLICATION_SETTINGS')
+
+ In this case before launching the application you have to set this
+ environment variable to the file you want to use. On Linux and OS X
+ use the export statement::
+
+ export YOURAPPLICATION_SETTINGS='/path/to/config/file'
+
+ On windows use `set` instead.
+
+ :param root_path: path to which files are read relative from. When the
+ config object is created by the application, this is
+ the application's :attr:`~flask.Flask.root_path`.
+ :param defaults: an optional dictionary of default values
+ """
+
+ def __init__(self, root_path, defaults=None):
+ dict.__init__(self, defaults or {})
+ self.root_path = root_path
+
+ def from_envvar(self, variable_name, silent=False):
+ """Loads a configuration from an environment variable pointing to
+ a configuration file. This is basically just a shortcut with nicer
+ error messages for this line of code::
+
+ app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
+
+ :param variable_name: name of the environment variable
+ :param silent: set to ``True`` if you want silent failure for missing
+ files.
+ :return: bool. ``True`` if able to load config, ``False`` otherwise.
+ """
+ rv = os.environ.get(variable_name)
+ if not rv:
+ if silent:
+ return False
+ raise RuntimeError('The environment variable %r is not set '
+ 'and as such configuration could not be '
+ 'loaded. Set this variable and make it '
+ 'point to a configuration file' %
+ variable_name)
+ return self.from_pyfile(rv, silent=silent)
+
+ def from_pyfile(self, filename, silent=False):
+ """Updates the values in the config from a Python file. This function
+ behaves as if the file was imported as module with the
+ :meth:`from_object` function.
+
+ :param filename: the filename of the config. This can either be an
+ absolute filename or a filename relative to the
+ root path.
+ :param silent: set to ``True`` if you want silent failure for missing
+ files.
+
+ .. versionadded:: 0.7
+ `silent` parameter.
+ """
+ filename = os.path.join(self.root_path, filename)
+ d = types.ModuleType('config')
+ d.__file__ = filename
+ try:
+ with open(filename, mode='rb') as config_file:
+ exec(compile(config_file.read(), filename, 'exec'), d.__dict__)
+ except IOError as e:
+ if silent and e.errno in (
+ errno.ENOENT, errno.EISDIR, errno.ENOTDIR
+ ):
+ return False
+ e.strerror = 'Unable to load configuration file (%s)' % e.strerror
+ raise
+ self.from_object(d)
+ return True
+
+ def from_object(self, obj):
+ """Updates the values from the given object. An object can be of one
+ of the following two types:
+
+ - a string: in this case the object with that name will be imported
+ - an actual object reference: that object is used directly
+
+ Objects are usually either modules or classes. :meth:`from_object`
+ loads only the uppercase attributes of the module/class. A ``dict``
+ object will not work with :meth:`from_object` because the keys of a
+ ``dict`` are not attributes of the ``dict`` class.
+
+ Example of module-based configuration::
+
+ app.config.from_object('yourapplication.default_config')
+ from yourapplication import default_config
+ app.config.from_object(default_config)
+
+ Nothing is done to the object before loading. If the object is a
+ class and has ``@property`` attributes, it needs to be
+ instantiated before being passed to this method.
+
+ You should not use this function to load the actual configuration but
+ rather configuration defaults. The actual config should be loaded
+ with :meth:`from_pyfile` and ideally from a location not within the
+ package because the package might be installed system wide.
+
+ See :ref:`config-dev-prod` for an example of class-based configuration
+ using :meth:`from_object`.
+
+ :param obj: an import name or object
+ """
+ if isinstance(obj, string_types):
+ obj = import_string(obj)
+ for key in dir(obj):
+ if key.isupper():
+ self[key] = getattr(obj, key)
+
+ def from_json(self, filename, silent=False):
+ """Updates the values in the config from a JSON file. This function
+ behaves as if the JSON object was a dictionary and passed to the
+ :meth:`from_mapping` function.
+
+ :param filename: the filename of the JSON file. This can either be an
+ absolute filename or a filename relative to the
+ root path.
+ :param silent: set to ``True`` if you want silent failure for missing
+ files.
+
+ .. versionadded:: 0.11
+ """
+ filename = os.path.join(self.root_path, filename)
+
+ try:
+ with open(filename) as json_file:
+ obj = json.loads(json_file.read())
+ except IOError as e:
+ if silent and e.errno in (errno.ENOENT, errno.EISDIR):
+ return False
+ e.strerror = 'Unable to load configuration file (%s)' % e.strerror
+ raise
+ return self.from_mapping(obj)
+
+ def from_mapping(self, *mapping, **kwargs):
+ """Updates the config like :meth:`update` ignoring items with non-upper
+ keys.
+
+ .. versionadded:: 0.11
+ """
+ mappings = []
+ if len(mapping) == 1:
+ if hasattr(mapping[0], 'items'):
+ mappings.append(mapping[0].items())
+ else:
+ mappings.append(mapping[0])
+ elif len(mapping) > 1:
+ raise TypeError(
+ 'expected at most 1 positional argument, got %d' % len(mapping)
+ )
+ mappings.append(kwargs.items())
+ for mapping in mappings:
+ for (key, value) in mapping:
+ if key.isupper():
+ self[key] = value
+ return True
+
+ def get_namespace(self, namespace, lowercase=True, trim_namespace=True):
+ """Returns a dictionary containing a subset of configuration options
+ that match the specified namespace/prefix. Example usage::
+
+ app.config['IMAGE_STORE_TYPE'] = 'fs'
+ app.config['IMAGE_STORE_PATH'] = '/var/app/images'
+ app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com'
+ image_store_config = app.config.get_namespace('IMAGE_STORE_')
+
+ The resulting dictionary `image_store_config` would look like::
+
+ {
+ 'type': 'fs',
+ 'path': '/var/app/images',
+ 'base_url': 'http://img.website.com'
+ }
+
+ This is often useful when configuration options map directly to
+ keyword arguments in functions or class constructors.
+
+ :param namespace: a configuration namespace
+ :param lowercase: a flag indicating if the keys of the resulting
+ dictionary should be lowercase
+ :param trim_namespace: a flag indicating if the keys of the resulting
+ dictionary should not include the namespace
+
+ .. versionadded:: 0.11
+ """
+ rv = {}
+ for k, v in iteritems(self):
+ if not k.startswith(namespace):
+ continue
+ if trim_namespace:
+ key = k[len(namespace):]
+ else:
+ key = k
+ if lowercase:
+ key = key.lower()
+ rv[key] = v
+ return rv
+
+ def __repr__(self):
+ return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
diff --git a/python/flask/ctx.py b/python/flask/ctx.py
new file mode 100644
index 0000000..ec8e787
--- /dev/null
+++ b/python/flask/ctx.py
@@ -0,0 +1,457 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.ctx
+ ~~~~~~~~~
+
+ Implements the objects required to keep the context.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import sys
+from functools import update_wrapper
+
+from werkzeug.exceptions import HTTPException
+
+from .globals import _request_ctx_stack, _app_ctx_stack
+from .signals import appcontext_pushed, appcontext_popped
+from ._compat import BROKEN_PYPY_CTXMGR_EXIT, reraise
+
+
+# a singleton sentinel value for parameter defaults
+_sentinel = object()
+
+
+class _AppCtxGlobals(object):
+ """A plain object. Used as a namespace for storing data during an
+ application context.
+
+ Creating an app context automatically creates this object, which is
+ made available as the :data:`g` proxy.
+
+ .. describe:: 'key' in g
+
+ Check whether an attribute is present.
+
+ .. versionadded:: 0.10
+
+ .. describe:: iter(g)
+
+ Return an iterator over the attribute names.
+
+ .. versionadded:: 0.10
+ """
+
+ def get(self, name, default=None):
+ """Get an attribute by name, or a default value. Like
+ :meth:`dict.get`.
+
+ :param name: Name of attribute to get.
+ :param default: Value to return if the attribute is not present.
+
+ .. versionadded:: 0.10
+ """
+ return self.__dict__.get(name, default)
+
+ def pop(self, name, default=_sentinel):
+ """Get and remove an attribute by name. Like :meth:`dict.pop`.
+
+ :param name: Name of attribute to pop.
+ :param default: Value to return if the attribute is not present,
+ instead of raise a ``KeyError``.
+
+ .. versionadded:: 0.11
+ """
+ if default is _sentinel:
+ return self.__dict__.pop(name)
+ else:
+ return self.__dict__.pop(name, default)
+
+ def setdefault(self, name, default=None):
+ """Get the value of an attribute if it is present, otherwise
+ set and return a default value. Like :meth:`dict.setdefault`.
+
+ :param name: Name of attribute to get.
+ :param: default: Value to set and return if the attribute is not
+ present.
+
+ .. versionadded:: 0.11
+ """
+ return self.__dict__.setdefault(name, default)
+
+ def __contains__(self, item):
+ return item in self.__dict__
+
+ def __iter__(self):
+ return iter(self.__dict__)
+
+ def __repr__(self):
+ top = _app_ctx_stack.top
+ if top is not None:
+ return '<flask.g of %r>' % top.app.name
+ return object.__repr__(self)
+
+
+def after_this_request(f):
+ """Executes a function after this request. This is useful to modify
+ response objects. The function is passed the response object and has
+ to return the same or a new one.
+
+ Example::
+
+ @app.route('/')
+ def index():
+ @after_this_request
+ def add_header(response):
+ response.headers['X-Foo'] = 'Parachute'
+ return response
+ return 'Hello World!'
+
+ This is more useful if a function other than the view function wants to
+ modify a response. For instance think of a decorator that wants to add
+ some headers without converting the return value into a response object.
+
+ .. versionadded:: 0.9
+ """
+ _request_ctx_stack.top._after_request_functions.append(f)
+ return f
+
+
+def copy_current_request_context(f):
+ """A helper function that decorates a function to retain the current
+ request context. This is useful when working with greenlets. The moment
+ the function is decorated a copy of the request context is created and
+ then pushed when the function is called.
+
+ Example::
+
+ import gevent
+ from flask import copy_current_request_context
+
+ @app.route('/')
+ def index():
+ @copy_current_request_context
+ def do_some_work():
+ # do some work here, it can access flask.request like you
+ # would otherwise in the view function.
+ ...
+ gevent.spawn(do_some_work)
+ return 'Regular response'
+
+ .. versionadded:: 0.10
+ """
+ top = _request_ctx_stack.top
+ if top is None:
+ raise RuntimeError('This decorator can only be used at local scopes '
+ 'when a request context is on the stack. For instance within '
+ 'view functions.')
+ reqctx = top.copy()
+ def wrapper(*args, **kwargs):
+ with reqctx:
+ return f(*args, **kwargs)
+ return update_wrapper(wrapper, f)
+
+
+def has_request_context():
+ """If you have code that wants to test if a request context is there or
+ not this function can be used. For instance, you may want to take advantage
+ of request information if the request object is available, but fail
+ silently if it is unavailable.
+
+ ::
+
+ class User(db.Model):
+
+ def __init__(self, username, remote_addr=None):
+ self.username = username
+ if remote_addr is None and has_request_context():
+ remote_addr = request.remote_addr
+ self.remote_addr = remote_addr
+
+ Alternatively you can also just test any of the context bound objects
+ (such as :class:`request` or :class:`g`) for truthness::
+
+ class User(db.Model):
+
+ def __init__(self, username, remote_addr=None):
+ self.username = username
+ if remote_addr is None and request:
+ remote_addr = request.remote_addr
+ self.remote_addr = remote_addr
+
+ .. versionadded:: 0.7
+ """
+ return _request_ctx_stack.top is not None
+
+
+def has_app_context():
+ """Works like :func:`has_request_context` but for the application
+ context. You can also just do a boolean check on the
+ :data:`current_app` object instead.
+
+ .. versionadded:: 0.9
+ """
+ return _app_ctx_stack.top is not None
+
+
+class AppContext(object):
+ """The application context binds an application object implicitly
+ to the current thread or greenlet, similar to how the
+ :class:`RequestContext` binds request information. The application
+ context is also implicitly created if a request context is created
+ but the application is not on top of the individual application
+ context.
+ """
+
+ def __init__(self, app):
+ self.app = app
+ self.url_adapter = app.create_url_adapter(None)
+ self.g = app.app_ctx_globals_class()
+
+ # Like request context, app contexts can be pushed multiple times
+ # but there a basic "refcount" is enough to track them.
+ self._refcnt = 0
+
+ def push(self):
+ """Binds the app context to the current context."""
+ self._refcnt += 1
+ if hasattr(sys, 'exc_clear'):
+ sys.exc_clear()
+ _app_ctx_stack.push(self)
+ appcontext_pushed.send(self.app)
+
+ def pop(self, exc=_sentinel):
+ """Pops the app context."""
+ try:
+ self._refcnt -= 1
+ if self._refcnt <= 0:
+ if exc is _sentinel:
+ exc = sys.exc_info()[1]
+ self.app.do_teardown_appcontext(exc)
+ finally:
+ rv = _app_ctx_stack.pop()
+ assert rv is self, 'Popped wrong app context. (%r instead of %r)' \
+ % (rv, self)
+ appcontext_popped.send(self.app)
+
+ def __enter__(self):
+ self.push()
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ self.pop(exc_value)
+
+ if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:
+ reraise(exc_type, exc_value, tb)
+
+
+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.
+
+ Do not attempt to use this class directly, instead use
+ :meth:`~flask.Flask.test_request_context` and
+ :meth:`~flask.Flask.request_context` to create this object.
+
+ When the request context is popped, it will evaluate all the
+ functions registered on the application for teardown execution
+ (:meth:`~flask.Flask.teardown_request`).
+
+ The request context is automatically popped at the end of the request
+ for you. In debug mode the request context is kept around if
+ exceptions happen so that interactive debuggers have a chance to
+ introspect the data. With 0.4 this can also be forced for requests
+ that did not fail and outside of ``DEBUG`` mode. By setting
+ ``'flask._preserve_context'`` to ``True`` on the WSGI environment the
+ context will not pop itself at the end of the request. This is used by
+ the :meth:`~flask.Flask.test_client` for example to implement the
+ deferred cleanup functionality.
+
+ You might find this helpful for unittests where you need the
+ information from the context local around for a little longer. Make
+ sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in
+ that situation, otherwise your unittests will leak memory.
+ """
+
+ def __init__(self, app, environ, request=None):
+ self.app = app
+ if request is None:
+ request = app.request_class(environ)
+ self.request = request
+ self.url_adapter = app.create_url_adapter(self.request)
+ self.flashes = None
+ self.session = None
+
+ # Request contexts can be pushed multiple times and interleaved with
+ # other request contexts. Now only if the last level is popped we
+ # get rid of them. Additionally if an application context is missing
+ # one is created implicitly so for each level we add this information
+ self._implicit_app_ctx_stack = []
+
+ # indicator if the context was preserved. Next time another context
+ # is pushed the preserved context is popped.
+ self.preserved = False
+
+ # remembers the exception for pop if there is one in case the context
+ # preservation kicks in.
+ self._preserved_exc = None
+
+ # Functions that should be executed after the request on the response
+ # object. These will be called before the regular "after_request"
+ # functions.
+ self._after_request_functions = []
+
+ self.match_request()
+
+ def _get_g(self):
+ return _app_ctx_stack.top.g
+ def _set_g(self, value):
+ _app_ctx_stack.top.g = value
+ g = property(_get_g, _set_g)
+ del _get_g, _set_g
+
+ def copy(self):
+ """Creates a copy of this request context with the same request object.
+ This can be used to move a request context to a different greenlet.
+ Because the actual request object is the same this cannot be used to
+ move a request context to a different thread unless access to the
+ request object is locked.
+
+ .. versionadded:: 0.10
+ """
+ return self.__class__(self.app,
+ environ=self.request.environ,
+ request=self.request
+ )
+
+ def match_request(self):
+ """Can be overridden by a subclass to hook into the matching
+ of the request.
+ """
+ try:
+ url_rule, self.request.view_args = \
+ self.url_adapter.match(return_rule=True)
+ self.request.url_rule = url_rule
+ except HTTPException as e:
+ self.request.routing_exception = e
+
+ def push(self):
+ """Binds the request context to the current context."""
+ # If an exception occurs in debug mode or if context preservation is
+ # activated under exception situations exactly one context stays
+ # on the stack. The rationale is that you want to access that
+ # information under debug situations. However if someone forgets to
+ # pop that context again we want to make sure that on the next push
+ # it's invalidated, otherwise we run at risk that something leaks
+ # memory. This is usually only a problem in test suite since this
+ # functionality is not active in production environments.
+ top = _request_ctx_stack.top
+ if top is not None and top.preserved:
+ top.pop(top._preserved_exc)
+
+ # Before we push the request context we have to ensure that there
+ # is an application context.
+ app_ctx = _app_ctx_stack.top
+ if app_ctx is None or app_ctx.app != self.app:
+ app_ctx = self.app.app_context()
+ app_ctx.push()
+ self._implicit_app_ctx_stack.append(app_ctx)
+ else:
+ self._implicit_app_ctx_stack.append(None)
+
+ if hasattr(sys, 'exc_clear'):
+ sys.exc_clear()
+
+ _request_ctx_stack.push(self)
+
+ # Open the session at the moment that the request context is available.
+ # This allows a custom open_session method to use the request context.
+ # Only open a new session if this is the first time the request was
+ # pushed, otherwise stream_with_context loses the session.
+ if self.session is None:
+ session_interface = self.app.session_interface
+ self.session = session_interface.open_session(
+ self.app, self.request
+ )
+
+ if self.session is None:
+ self.session = session_interface.make_null_session(self.app)
+
+ def pop(self, exc=_sentinel):
+ """Pops the request context and unbinds it by doing that. This will
+ also trigger the execution of functions registered by the
+ :meth:`~flask.Flask.teardown_request` decorator.
+
+ .. versionchanged:: 0.9
+ Added the `exc` argument.
+ """
+ app_ctx = self._implicit_app_ctx_stack.pop()
+
+ try:
+ clear_request = False
+ if not self._implicit_app_ctx_stack:
+ self.preserved = False
+ self._preserved_exc = None
+ if exc is _sentinel:
+ exc = sys.exc_info()[1]
+ self.app.do_teardown_request(exc)
+
+ # If this interpreter supports clearing the exception information
+ # we do that now. This will only go into effect on Python 2.x,
+ # on 3.x it disappears automatically at the end of the exception
+ # stack.
+ if hasattr(sys, 'exc_clear'):
+ sys.exc_clear()
+
+ request_close = getattr(self.request, 'close', None)
+ if request_close is not None:
+ request_close()
+ clear_request = True
+ finally:
+ rv = _request_ctx_stack.pop()
+
+ # get rid of circular dependencies at the end of the request
+ # so that we don't require the GC to be active.
+ if clear_request:
+ rv.request.environ['werkzeug.request'] = None
+
+ # Get rid of the app as well if necessary.
+ if app_ctx is not None:
+ app_ctx.pop(exc)
+
+ assert rv is self, 'Popped wrong request context. ' \
+ '(%r instead of %r)' % (rv, self)
+
+ def auto_pop(self, exc):
+ if self.request.environ.get('flask._preserve_context') or \
+ (exc is not None and self.app.preserve_context_on_exception):
+ self.preserved = True
+ self._preserved_exc = exc
+ else:
+ self.pop(exc)
+
+ def __enter__(self):
+ self.push()
+ return 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. Furthermore
+ # the context can be force kept alive for the test client.
+ # See flask.testing for how this works.
+ self.auto_pop(exc_value)
+
+ if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:
+ reraise(exc_type, exc_value, tb)
+
+ def __repr__(self):
+ return '<%s \'%s\' [%s] of %s>' % (
+ self.__class__.__name__,
+ self.request.url,
+ self.request.method,
+ self.app.name,
+ )
diff --git a/python/flask/debughelpers.py b/python/flask/debughelpers.py
new file mode 100644
index 0000000..e9765f2
--- /dev/null
+++ b/python/flask/debughelpers.py
@@ -0,0 +1,168 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.debughelpers
+ ~~~~~~~~~~~~~~~~~~
+
+ Various helpers to make the development experience better.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+from warnings import warn
+
+from ._compat import implements_to_string, text_type
+from .app import Flask
+from .blueprints import Blueprint
+from .globals import _request_ctx_stack
+
+
+class UnexpectedUnicodeError(AssertionError, UnicodeError):
+ """Raised in places where we want some better error reporting for
+ unexpected unicode or binary data.
+ """
+
+
+@implements_to_string
+class DebugFilesKeyError(KeyError, AssertionError):
+ """Raised from request.files during debugging. The idea is that it can
+ provide a better error message than just a generic KeyError/BadRequest.
+ """
+
+ def __init__(self, request, key):
+ form_matches = request.form.getlist(key)
+ buf = ['You tried to access the file "%s" in the request.files '
+ 'dictionary but it does not exist. The mimetype for the request '
+ 'is "%s" instead of "multipart/form-data" which means that no '
+ 'file contents were transmitted. To fix this error you should '
+ 'provide enctype="multipart/form-data" in your form.' %
+ (key, request.mimetype)]
+ if form_matches:
+ buf.append('\n\nThe browser instead transmitted some file names. '
+ 'This was submitted: %s' % ', '.join('"%s"' % x
+ for x in form_matches))
+ self.msg = ''.join(buf)
+
+ def __str__(self):
+ return self.msg
+
+
+class FormDataRoutingRedirect(AssertionError):
+ """This exception is raised by Flask in debug mode if it detects a
+ redirect caused by the routing system when the request method is not
+ GET, HEAD or OPTIONS. Reasoning: form data will be dropped.
+ """
+
+ def __init__(self, request):
+ exc = request.routing_exception
+ buf = ['A request was sent to this URL (%s) but a redirect was '
+ 'issued automatically by the routing system to "%s".'
+ % (request.url, exc.new_url)]
+
+ # In case just a slash was appended we can be extra helpful
+ if request.base_url + '/' == exc.new_url.split('?')[0]:
+ buf.append(' The URL was defined with a trailing slash so '
+ 'Flask will automatically redirect to the URL '
+ 'with the trailing slash if it was accessed '
+ 'without one.')
+
+ buf.append(' Make sure to directly send your %s-request to this URL '
+ 'since we can\'t make browsers or HTTP clients redirect '
+ 'with form data reliably or without user interaction.' %
+ request.method)
+ buf.append('\n\nNote: this exception is only raised in debug mode')
+ AssertionError.__init__(self, ''.join(buf).encode('utf-8'))
+
+
+def attach_enctype_error_multidict(request):
+ """Since Flask 0.8 we're monkeypatching the files object in case a
+ request is detected that does not use multipart form data but the files
+ object is accessed.
+ """
+ oldcls = request.files.__class__
+ class newcls(oldcls):
+ def __getitem__(self, key):
+ try:
+ return oldcls.__getitem__(self, key)
+ except KeyError:
+ if key not in request.form:
+ raise
+ raise DebugFilesKeyError(request, key)
+ newcls.__name__ = oldcls.__name__
+ newcls.__module__ = oldcls.__module__
+ request.files.__class__ = newcls
+
+
+def _dump_loader_info(loader):
+ yield 'class: %s.%s' % (type(loader).__module__, type(loader).__name__)
+ for key, value in sorted(loader.__dict__.items()):
+ if key.startswith('_'):
+ continue
+ if isinstance(value, (tuple, list)):
+ if not all(isinstance(x, (str, text_type)) for x in value):
+ continue
+ yield '%s:' % key
+ for item in value:
+ yield ' - %s' % item
+ continue
+ elif not isinstance(value, (str, text_type, int, float, bool)):
+ continue
+ yield '%s: %r' % (key, value)
+
+
+def explain_template_loading_attempts(app, template, attempts):
+ """This should help developers understand what failed"""
+ info = ['Locating template "%s":' % template]
+ total_found = 0
+ blueprint = None
+ reqctx = _request_ctx_stack.top
+ if reqctx is not None and reqctx.request.blueprint is not None:
+ blueprint = reqctx.request.blueprint
+
+ for idx, (loader, srcobj, triple) in enumerate(attempts):
+ if isinstance(srcobj, Flask):
+ src_info = 'application "%s"' % srcobj.import_name
+ elif isinstance(srcobj, Blueprint):
+ src_info = 'blueprint "%s" (%s)' % (srcobj.name,
+ srcobj.import_name)
+ else:
+ src_info = repr(srcobj)
+
+ info.append('% 5d: trying loader of %s' % (
+ idx + 1, src_info))
+
+ for line in _dump_loader_info(loader):
+ info.append(' %s' % line)
+
+ if triple is None:
+ detail = 'no match'
+ else:
+ detail = 'found (%r)' % (triple[1] or '<string>')
+ total_found += 1
+ info.append(' -> %s' % detail)
+
+ seems_fishy = False
+ if total_found == 0:
+ info.append('Error: the template could not be found.')
+ seems_fishy = True
+ elif total_found > 1:
+ info.append('Warning: multiple loaders returned a match for the template.')
+ seems_fishy = True
+
+ if blueprint is not None and seems_fishy:
+ info.append(' The template was looked up from an endpoint that '
+ 'belongs to the blueprint "%s".' % blueprint)
+ info.append(' Maybe you did not place a template in the right folder?')
+ info.append(' See http://flask.pocoo.org/docs/blueprints/#templates')
+
+ app.logger.info('\n'.join(info))
+
+
+def explain_ignored_app_run():
+ if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
+ warn(Warning('Silently ignoring app.run() because the '
+ 'application is run from the flask command line '
+ 'executable. Consider putting app.run() behind an '
+ 'if __name__ == "__main__" guard to silence this '
+ 'warning.'), stacklevel=3)
diff --git a/python/flask/globals.py b/python/flask/globals.py
new file mode 100644
index 0000000..7d50a6f
--- /dev/null
+++ b/python/flask/globals.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.globals
+ ~~~~~~~~~~~~~
+
+ Defines all the global objects that are proxies to the current
+ active context.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from functools import partial
+from werkzeug.local import LocalStack, LocalProxy
+
+
+_request_ctx_err_msg = '''\
+Working outside of request context.
+
+This typically means that you attempted to use functionality that needed
+an active HTTP request. Consult the documentation on testing for
+information about how to avoid this problem.\
+'''
+_app_ctx_err_msg = '''\
+Working outside of application context.
+
+This typically means that you attempted to use functionality that needed
+to interface with the current application object in some way. To solve
+this, set up an application context with app.app_context(). See the
+documentation for more information.\
+'''
+
+
+def _lookup_req_object(name):
+ top = _request_ctx_stack.top
+ if top is None:
+ raise RuntimeError(_request_ctx_err_msg)
+ return getattr(top, name)
+
+
+def _lookup_app_object(name):
+ top = _app_ctx_stack.top
+ if top is None:
+ raise RuntimeError(_app_ctx_err_msg)
+ return getattr(top, name)
+
+
+def _find_app():
+ top = _app_ctx_stack.top
+ if top is None:
+ raise RuntimeError(_app_ctx_err_msg)
+ return top.app
+
+
+# context locals
+_request_ctx_stack = LocalStack()
+_app_ctx_stack = LocalStack()
+current_app = LocalProxy(_find_app)
+request = LocalProxy(partial(_lookup_req_object, 'request'))
+session = LocalProxy(partial(_lookup_req_object, 'session'))
+g = LocalProxy(partial(_lookup_app_object, 'g'))
diff --git a/python/flask/helpers.py b/python/flask/helpers.py
new file mode 100644
index 0000000..158edc5
--- /dev/null
+++ b/python/flask/helpers.py
@@ -0,0 +1,1051 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.helpers
+ ~~~~~~~~~~~~~
+
+ Implements various helpers.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+import socket
+import sys
+import pkgutil
+import posixpath
+import mimetypes
+from time import time
+from zlib import adler32
+from threading import RLock
+import unicodedata
+from werkzeug.routing import BuildError
+from functools import update_wrapper
+
+from werkzeug.urls import url_quote
+from werkzeug.datastructures import Headers, Range
+from werkzeug.exceptions import BadRequest, NotFound, \
+ RequestedRangeNotSatisfiable
+
+from werkzeug.wsgi import wrap_file
+from jinja2 import FileSystemLoader
+
+from .signals import message_flashed
+from .globals import session, _request_ctx_stack, _app_ctx_stack, \
+ current_app, request
+from ._compat import string_types, text_type, PY2
+
+# sentinel
+_missing = object()
+
+
+# what separators does this operating system provide that are not a slash?
+# this is used by the send_from_directory function to ensure that nobody is
+# able to access files from outside the filesystem.
+_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
+ if sep not in (None, '/'))
+
+
+def get_env():
+ """Get the environment the app is running in, indicated by the
+ :envvar:`FLASK_ENV` environment variable. The default is
+ ``'production'``.
+ """
+ return os.environ.get('FLASK_ENV') or 'production'
+
+
+def get_debug_flag():
+ """Get whether debug mode should be enabled for the app, indicated
+ by the :envvar:`FLASK_DEBUG` environment variable. The default is
+ ``True`` if :func:`.get_env` returns ``'development'``, or ``False``
+ otherwise.
+ """
+ val = os.environ.get('FLASK_DEBUG')
+
+ if not val:
+ return get_env() == 'development'
+
+ return val.lower() not in ('0', 'false', 'no')
+
+
+def get_load_dotenv(default=True):
+ """Get whether the user has disabled loading dotenv files by setting
+ :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the
+ files.
+
+ :param default: What to return if the env var isn't set.
+ """
+ val = os.environ.get('FLASK_SKIP_DOTENV')
+
+ if not val:
+ return default
+
+ return val.lower() in ('0', 'false', 'no')
+
+
+def _endpoint_from_view_func(view_func):
+ """Internal helper that returns the default endpoint for a given
+ function. This always is the function name.
+ """
+ assert view_func is not None, 'expected view func if endpoint ' \
+ 'is not provided.'
+ return view_func.__name__
+
+
+def stream_with_context(generator_or_function):
+ """Request contexts disappear when the response is started on the server.
+ This is done for efficiency reasons and to make it less likely to encounter
+ memory leaks with badly written WSGI middlewares. The downside is that if
+ you are using streamed responses, the generator cannot access request bound
+ information any more.
+
+ This function however can help you keep the context around for longer::
+
+ from flask import stream_with_context, request, Response
+
+ @app.route('/stream')
+ def streamed_response():
+ @stream_with_context
+ def generate():
+ yield 'Hello '
+ yield request.args['name']
+ yield '!'
+ return Response(generate())
+
+ Alternatively it can also be used around a specific generator::
+
+ from flask import stream_with_context, request, Response
+
+ @app.route('/stream')
+ def streamed_response():
+ def generate():
+ yield 'Hello '
+ yield request.args['name']
+ yield '!'
+ return Response(stream_with_context(generate()))
+
+ .. versionadded:: 0.9
+ """
+ try:
+ gen = iter(generator_or_function)
+ except TypeError:
+ def decorator(*args, **kwargs):
+ gen = generator_or_function(*args, **kwargs)
+ return stream_with_context(gen)
+ return update_wrapper(decorator, generator_or_function)
+
+ def generator():
+ ctx = _request_ctx_stack.top
+ if ctx is None:
+ raise RuntimeError('Attempted to stream with context but '
+ 'there was no context in the first place to keep around.')
+ with ctx:
+ # Dummy sentinel. Has to be inside the context block or we're
+ # not actually keeping the context around.
+ yield None
+
+ # The try/finally is here so that if someone passes a WSGI level
+ # iterator in we're still running the cleanup logic. Generators
+ # don't need that because they are closed on their destruction
+ # automatically.
+ try:
+ for item in gen:
+ yield item
+ finally:
+ if hasattr(gen, 'close'):
+ gen.close()
+
+ # The trick is to start the generator. Then the code execution runs until
+ # the first dummy None is yielded at which point the context was already
+ # pushed. This item is discarded. Then when the iteration continues the
+ # real generator is executed.
+ wrapped_g = generator()
+ next(wrapped_g)
+ return wrapped_g
+
+
+def make_response(*args):
+ """Sometimes it is necessary to set additional headers in a view. Because
+ views do not have to return response objects but can return a value that
+ is converted into a response object by Flask itself, it becomes tricky to
+ add headers to it. This function can be called instead of using a return
+ and you will get a response object which you can use to attach headers.
+
+ If view looked like this and you want to add a new header::
+
+ def index():
+ return render_template('index.html', foo=42)
+
+ You can now do something like this::
+
+ def index():
+ response = make_response(render_template('index.html', foo=42))
+ response.headers['X-Parachutes'] = 'parachutes are cool'
+ return response
+
+ This function accepts the very same arguments you can return from a
+ view function. This for example creates a response with a 404 error
+ code::
+
+ response = make_response(render_template('not_found.html'), 404)
+
+ The other use case of this function is to force the return value of a
+ view function into a response which is helpful with view
+ decorators::
+
+ response = make_response(view_function())
+ response.headers['X-Parachutes'] = 'parachutes are cool'
+
+ Internally this function does the following things:
+
+ - if no arguments are passed, it creates a new response argument
+ - if one argument is passed, :meth:`flask.Flask.make_response`
+ is invoked with it.
+ - if more than one argument is passed, the arguments are passed
+ to the :meth:`flask.Flask.make_response` function as tuple.
+
+ .. versionadded:: 0.6
+ """
+ if not args:
+ return current_app.response_class()
+ if len(args) == 1:
+ args = args[0]
+ return current_app.make_response(args)
+
+
+def url_for(endpoint, **values):
+ """Generates a URL to the given endpoint with the method provided.
+
+ Variable arguments that are unknown to the target endpoint are appended
+ to the generated URL as query arguments. If the value of a query argument
+ is ``None``, the whole pair is skipped. In case blueprints are active
+ you can shortcut references to the same blueprint by prefixing the
+ local endpoint with a dot (``.``).
+
+ This will reference the index function local to the current blueprint::
+
+ url_for('.index')
+
+ For more information, head over to the :ref:`Quickstart <url-building>`.
+
+ To integrate applications, :class:`Flask` has a hook to intercept URL build
+ errors through :attr:`Flask.url_build_error_handlers`. The `url_for`
+ function results in a :exc:`~werkzeug.routing.BuildError` when the current
+ app does not have a URL for the given endpoint and values. When it does, the
+ :data:`~flask.current_app` calls its :attr:`~Flask.url_build_error_handlers` if
+ it is not ``None``, which can return a string to use as the result of
+ `url_for` (instead of `url_for`'s default to raise the
+ :exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception.
+ An example::
+
+ def external_url_handler(error, endpoint, values):
+ "Looks up an external URL when `url_for` cannot build a URL."
+ # This is an example of hooking the build_error_handler.
+ # Here, lookup_url is some utility function you've built
+ # which looks up the endpoint in some external URL registry.
+ url = lookup_url(endpoint, **values)
+ if url is None:
+ # External lookup did not have a URL.
+ # Re-raise the BuildError, in context of original traceback.
+ exc_type, exc_value, tb = sys.exc_info()
+ if exc_value is error:
+ raise exc_type, exc_value, tb
+ else:
+ raise error
+ # url_for will use this result, instead of raising BuildError.
+ return url
+
+ app.url_build_error_handlers.append(external_url_handler)
+
+ Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and
+ `endpoint` and `values` are the arguments passed into `url_for`. Note
+ that this is for building URLs outside the current application, and not for
+ handling 404 NotFound errors.
+
+ .. versionadded:: 0.10
+ The `_scheme` parameter was added.
+
+ .. versionadded:: 0.9
+ The `_anchor` and `_method` parameters were added.
+
+ .. versionadded:: 0.9
+ Calls :meth:`Flask.handle_build_error` on
+ :exc:`~werkzeug.routing.BuildError`.
+
+ :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. Server
+ address can be changed via ``SERVER_NAME`` configuration variable which
+ falls back to the `Host` header, then to the IP and port of the request.
+ :param _scheme: a string specifying the desired URL scheme. The `_external`
+ parameter must be set to ``True`` or a :exc:`ValueError` is raised. The default
+ behavior uses the same scheme as the current request, or
+ ``PREFERRED_URL_SCHEME`` from the :ref:`app configuration <config>` if no
+ request context is available. As of Werkzeug 0.10, this also can be set
+ to an empty string to build protocol-relative URLs.
+ :param _anchor: if provided this is added as anchor to the URL.
+ :param _method: if provided this explicitly specifies an HTTP method.
+ """
+ appctx = _app_ctx_stack.top
+ reqctx = _request_ctx_stack.top
+
+ if appctx is None:
+ raise RuntimeError(
+ 'Attempted to generate a URL without the application context being'
+ ' pushed. This has to be executed when application context is'
+ ' available.'
+ )
+
+ # If request specific information is available we have some extra
+ # features that support "relative" URLs.
+ if reqctx is not None:
+ url_adapter = reqctx.url_adapter
+ blueprint_name = request.blueprint
+
+ if endpoint[:1] == '.':
+ if blueprint_name is not None:
+ endpoint = blueprint_name + endpoint
+ else:
+ endpoint = endpoint[1:]
+
+ external = values.pop('_external', False)
+
+ # Otherwise go with the url adapter from the appctx and make
+ # the URLs external by default.
+ else:
+ url_adapter = appctx.url_adapter
+
+ if url_adapter is None:
+ raise RuntimeError(
+ 'Application was not able to create a URL adapter for request'
+ ' independent URL generation. You might be able to fix this by'
+ ' setting the SERVER_NAME config variable.'
+ )
+
+ external = values.pop('_external', True)
+
+ anchor = values.pop('_anchor', None)
+ method = values.pop('_method', None)
+ scheme = values.pop('_scheme', None)
+ appctx.app.inject_url_defaults(endpoint, values)
+
+ # This is not the best way to deal with this but currently the
+ # underlying Werkzeug router does not support overriding the scheme on
+ # a per build call basis.
+ old_scheme = None
+ if scheme is not None:
+ if not external:
+ raise ValueError('When specifying _scheme, _external must be True')
+ old_scheme = url_adapter.url_scheme
+ url_adapter.url_scheme = scheme
+
+ try:
+ try:
+ rv = url_adapter.build(endpoint, values, method=method,
+ force_external=external)
+ finally:
+ if old_scheme is not None:
+ url_adapter.url_scheme = old_scheme
+ except BuildError as error:
+ # We need to inject the values again so that the app callback can
+ # deal with that sort of stuff.
+ values['_external'] = external
+ values['_anchor'] = anchor
+ values['_method'] = method
+ values['_scheme'] = scheme
+ return appctx.app.handle_url_build_error(error, endpoint, values)
+
+ if anchor is not None:
+ rv += '#' + url_quote(anchor)
+ return rv
+
+
+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 :file:`_cider.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('_cider.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 access
+ """
+ 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.3
+ `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.
+ """
+ # Original implementation:
+ #
+ # session.setdefault('_flashes', []).append((category, message))
+ #
+ # This assumed that changes made to mutable structures in the session are
+ # always in sync with the session object, which is not true for session
+ # implementations that use external storage for keeping their keys/values.
+ flashes = session.get('_flashes', [])
+ flashes.append((category, message))
+ session['_flashes'] = flashes
+ message_flashed.send(current_app._get_current_object(),
+ message=message, category=category)
+
+
+def get_flashed_messages(with_categories=False, category_filter=[]):
+ """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.
+
+ Filter the flashed messages to one or more categories by providing those
+ categories in `category_filter`. This allows rendering categories in
+ separate html blocks. The `with_categories` and `category_filter`
+ arguments are distinct:
+
+ * `with_categories` controls whether categories are returned with message
+ text (``True`` gives a tuple, where ``False`` gives just the message text).
+ * `category_filter` filters the messages down to only those matching the
+ provided categories.
+
+ See :ref:`message-flashing-pattern` for examples.
+
+ .. versionchanged:: 0.3
+ `with_categories` parameter added.
+
+ .. versionchanged:: 0.9
+ `category_filter` parameter added.
+
+ :param with_categories: set to ``True`` to also receive categories.
+ :param category_filter: whitelist of categories to limit return values
+ """
+ flashes = _request_ctx_stack.top.flashes
+ if flashes is None:
+ _request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \
+ if '_flashes' in session else []
+ if category_filter:
+ flashes = list(filter(lambda f: f[0] in category_filter, flashes))
+ if not with_categories:
+ return [x[1] for x in flashes]
+ return flashes
+
+
+def send_file(filename_or_fp, mimetype=None, as_attachment=False,
+ attachment_filename=None, add_etags=True,
+ cache_timeout=None, conditional=False, last_modified=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 send certain files as attachment (HTML for instance). The mimetype
+ guessing requires a `filename` or an `attachment_filename` to be
+ provided.
+
+ ETags will also be attached automatically if a `filename` is provided. You
+ can turn this off by setting `add_etags=False`.
+
+ If `conditional=True` and `filename` is provided, this method will try to
+ upgrade the response stream to support range requests. This will allow
+ the request to be answered with partial content response.
+
+ Please never pass filenames to this function from user sources;
+ you should use :func:`send_from_directory` instead.
+
+ .. versionadded:: 0.2
+
+ .. versionadded:: 0.5
+ The `add_etags`, `cache_timeout` and `conditional` parameters were
+ added. The default behavior is now to attach etags.
+
+ .. versionchanged:: 0.7
+ mimetype guessing and etag support for file objects was
+ deprecated because it was unreliable. Pass a filename if you are
+ able to, otherwise attach an etag yourself. This functionality
+ will be removed in Flask 1.0
+
+ .. versionchanged:: 0.9
+ cache_timeout pulls its default from application config, when None.
+
+ .. versionchanged:: 0.12
+ The filename is no longer automatically inferred from file objects. If
+ you want to use automatic mimetype and etag support, pass a filepath via
+ `filename_or_fp` or `attachment_filename`.
+
+ .. versionchanged:: 0.12
+ The `attachment_filename` is preferred over `filename` for MIME-type
+ detection.
+
+ .. versionchanged:: 1.0
+ UTF-8 filenames, as specified in `RFC 2231`_, are supported.
+
+ .. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4
+
+ .. versionchanged:: 1.0.3
+ Filenames are encoded with ASCII instead of Latin-1 for broader
+ compatibility with WSGI servers.
+
+ :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. Make sure that the
+ file pointer is positioned at the start of data to
+ send before calling :func:`send_file`.
+ :param mimetype: the mimetype of the file if provided. If a file path is
+ given, auto detection happens as fallback, otherwise an
+ error will be raised.
+ :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.
+ :param add_etags: set to ``False`` to disable attaching of etags.
+ :param conditional: set to ``True`` to enable conditional responses.
+
+ :param cache_timeout: the timeout in seconds for the headers. When ``None``
+ (default), this value is set by
+ :meth:`~Flask.get_send_file_max_age` of
+ :data:`~flask.current_app`.
+ :param last_modified: set the ``Last-Modified`` header to this value,
+ a :class:`~datetime.datetime` or timestamp.
+ If a file was passed, this overrides its mtime.
+ """
+ mtime = None
+ fsize = None
+ if isinstance(filename_or_fp, string_types):
+ filename = filename_or_fp
+ if not os.path.isabs(filename):
+ filename = os.path.join(current_app.root_path, filename)
+ file = None
+ if attachment_filename is None:
+ attachment_filename = os.path.basename(filename)
+ else:
+ file = filename_or_fp
+ filename = None
+
+ if mimetype is None:
+ if attachment_filename is not None:
+ mimetype = mimetypes.guess_type(attachment_filename)[0] \
+ or 'application/octet-stream'
+
+ if mimetype is None:
+ raise ValueError(
+ 'Unable to infer MIME-type because no filename is available. '
+ 'Please set either `attachment_filename`, pass a filepath to '
+ '`filename_or_fp` or set your own MIME-type via `mimetype`.'
+ )
+
+ headers = Headers()
+ if as_attachment:
+ if attachment_filename is None:
+ raise TypeError('filename unavailable, required for '
+ 'sending as attachment')
+
+ if not isinstance(attachment_filename, text_type):
+ attachment_filename = attachment_filename.decode('utf-8')
+
+ try:
+ attachment_filename = attachment_filename.encode('ascii')
+ except UnicodeEncodeError:
+ filenames = {
+ 'filename': unicodedata.normalize(
+ 'NFKD', attachment_filename).encode('ascii', 'ignore'),
+ 'filename*': "UTF-8''%s" % url_quote(attachment_filename),
+ }
+ else:
+ filenames = {'filename': attachment_filename}
+
+ headers.add('Content-Disposition', 'attachment', **filenames)
+
+ if current_app.use_x_sendfile and filename:
+ if file is not None:
+ file.close()
+ headers['X-Sendfile'] = filename
+ fsize = os.path.getsize(filename)
+ headers['Content-Length'] = fsize
+ data = None
+ else:
+ if file is None:
+ file = open(filename, 'rb')
+ mtime = os.path.getmtime(filename)
+ fsize = os.path.getsize(filename)
+ headers['Content-Length'] = fsize
+ data = wrap_file(request.environ, file)
+
+ rv = current_app.response_class(data, mimetype=mimetype, headers=headers,
+ direct_passthrough=True)
+
+ if last_modified is not None:
+ rv.last_modified = last_modified
+ elif mtime is not None:
+ rv.last_modified = mtime
+
+ rv.cache_control.public = True
+ if cache_timeout is None:
+ cache_timeout = current_app.get_send_file_max_age(filename)
+ if cache_timeout is not None:
+ rv.cache_control.max_age = cache_timeout
+ rv.expires = int(time() + cache_timeout)
+
+ if add_etags and filename is not None:
+ from warnings import warn
+
+ try:
+ rv.set_etag('%s-%s-%s' % (
+ os.path.getmtime(filename),
+ os.path.getsize(filename),
+ adler32(
+ filename.encode('utf-8') if isinstance(filename, text_type)
+ else filename
+ ) & 0xffffffff
+ ))
+ except OSError:
+ warn('Access %s failed, maybe it does not exist, so ignore etags in '
+ 'headers' % filename, stacklevel=2)
+
+ if conditional:
+ try:
+ rv = rv.make_conditional(request, accept_ranges=True,
+ complete_length=fsize)
+ except RequestedRangeNotSatisfiable:
+ if file is not None:
+ file.close()
+ raise
+ # make sure we don't send x-sendfile for servers that
+ # ignore the 304 status code for x-sendfile.
+ if rv.status_code == 304:
+ rv.headers.pop('x-sendfile', None)
+ return rv
+
+
+def safe_join(directory, *pathnames):
+ """Safely join `directory` and zero or more untrusted `pathnames`
+ components.
+
+ Example usage::
+
+ @app.route('/wiki/<path:filename>')
+ def wiki_page(filename):
+ filename = safe_join(app.config['WIKI_FOLDER'], filename)
+ with open(filename, 'rb') as fd:
+ content = fd.read() # Read and process the file content...
+
+ :param directory: the trusted base directory.
+ :param pathnames: the untrusted pathnames relative to that directory.
+ :raises: :class:`~werkzeug.exceptions.NotFound` if one or more passed
+ paths fall out of its boundaries.
+ """
+
+ parts = [directory]
+
+ for filename in pathnames:
+ if filename != '':
+ filename = posixpath.normpath(filename)
+
+ if (
+ any(sep in filename for sep in _os_alt_seps)
+ or os.path.isabs(filename)
+ or filename == '..'
+ or filename.startswith('../')
+ ):
+ raise NotFound()
+
+ parts.append(filename)
+
+ return posixpath.join(*parts)
+
+
+def send_from_directory(directory, filename, **options):
+ """Send a file from a given directory with :func:`send_file`. This
+ is a secure way to quickly expose static files from an upload folder
+ or something similar.
+
+ Example usage::
+
+ @app.route('/uploads/<path:filename>')
+ def download_file(filename):
+ return send_from_directory(app.config['UPLOAD_FOLDER'],
+ filename, as_attachment=True)
+
+ .. admonition:: Sending files and Performance
+
+ It is strongly recommended to activate either ``X-Sendfile`` support in
+ your webserver or (if no authentication happens) to tell the webserver
+ to serve files for the given path on its own without calling into the
+ web application for improved performance.
+
+ .. versionadded:: 0.5
+
+ :param directory: the directory where all the files are stored.
+ :param filename: the filename relative to that directory to
+ download.
+ :param options: optional keyword arguments that are directly
+ forwarded to :func:`send_file`.
+ """
+ filename = safe_join(directory, filename)
+ if not os.path.isabs(filename):
+ filename = os.path.join(current_app.root_path, filename)
+ try:
+ if not os.path.isfile(filename):
+ raise NotFound()
+ except (TypeError, ValueError):
+ raise BadRequest()
+ options.setdefault('conditional', True)
+ return send_file(filename, **options)
+
+
+def get_root_path(import_name):
+ """Returns the path to a package or cwd if that cannot be found. This
+ returns the path of a package or the folder that contains a module.
+
+ Not to be confused with the package path returned by :func:`find_package`.
+ """
+ # Module already imported and has a file attribute. Use that first.
+ mod = sys.modules.get(import_name)
+ if mod is not None and hasattr(mod, '__file__'):
+ return os.path.dirname(os.path.abspath(mod.__file__))
+
+ # Next attempt: check the loader.
+ loader = pkgutil.get_loader(import_name)
+
+ # Loader does not exist or we're referring to an unloaded main module
+ # or a main module without path (interactive sessions), go with the
+ # current working directory.
+ if loader is None or import_name == '__main__':
+ return os.getcwd()
+
+ # For .egg, zipimporter does not have get_filename until Python 2.7.
+ # Some other loaders might exhibit the same behavior.
+ if hasattr(loader, 'get_filename'):
+ filepath = loader.get_filename(import_name)
+ else:
+ # Fall back to imports.
+ __import__(import_name)
+ mod = sys.modules[import_name]
+ filepath = getattr(mod, '__file__', None)
+
+ # If we don't have a filepath it might be because we are a
+ # namespace package. In this case we pick the root path from the
+ # first module that is contained in our package.
+ if filepath is None:
+ raise RuntimeError('No root path can be found for the provided '
+ 'module "%s". This can happen because the '
+ 'module came from an import hook that does '
+ 'not provide file name information or because '
+ 'it\'s a namespace package. In this case '
+ 'the root path needs to be explicitly '
+ 'provided.' % import_name)
+
+ # filepath is import_name.py for a module, or __init__.py for a package.
+ return os.path.dirname(os.path.abspath(filepath))
+
+
+def _matching_loader_thinks_module_is_package(loader, mod_name):
+ """Given the loader that loaded a module and the module this function
+ attempts to figure out if the given module is actually a package.
+ """
+ # If the loader can tell us if something is a package, we can
+ # directly ask the loader.
+ if hasattr(loader, 'is_package'):
+ return loader.is_package(mod_name)
+ # importlib's namespace loaders do not have this functionality but
+ # all the modules it loads are packages, so we can take advantage of
+ # this information.
+ elif (loader.__class__.__module__ == '_frozen_importlib' and
+ loader.__class__.__name__ == 'NamespaceLoader'):
+ return True
+ # Otherwise we need to fail with an error that explains what went
+ # wrong.
+ raise AttributeError(
+ ('%s.is_package() method is missing but is required by Flask of '
+ 'PEP 302 import hooks. If you do not use import hooks and '
+ 'you encounter this error please file a bug against Flask.') %
+ loader.__class__.__name__)
+
+
+def find_package(import_name):
+ """Finds a package and returns the prefix (or None if the package is
+ not installed) as well as the folder that contains the package or
+ module as a tuple. The package path returned is the module that would
+ have to be added to the pythonpath in order to make it possible to
+ import the module. The prefix is the path below which a UNIX like
+ folder structure exists (lib, share etc.).
+ """
+ root_mod_name = import_name.split('.')[0]
+ loader = pkgutil.get_loader(root_mod_name)
+ if loader is None or import_name == '__main__':
+ # import name is not found, or interactive/main module
+ package_path = os.getcwd()
+ else:
+ # For .egg, zipimporter does not have get_filename until Python 2.7.
+ if hasattr(loader, 'get_filename'):
+ filename = loader.get_filename(root_mod_name)
+ elif hasattr(loader, 'archive'):
+ # zipimporter's loader.archive points to the .egg or .zip
+ # archive filename is dropped in call to dirname below.
+ filename = loader.archive
+ else:
+ # At least one loader is missing both get_filename and archive:
+ # Google App Engine's HardenedModulesHook
+ #
+ # Fall back to imports.
+ __import__(import_name)
+ filename = sys.modules[import_name].__file__
+ package_path = os.path.abspath(os.path.dirname(filename))
+
+ # In case the root module is a package we need to chop of the
+ # rightmost part. This needs to go through a helper function
+ # because of python 3.3 namespace packages.
+ if _matching_loader_thinks_module_is_package(
+ loader, root_mod_name):
+ package_path = os.path.dirname(package_path)
+
+ site_parent, site_folder = os.path.split(package_path)
+ py_prefix = os.path.abspath(sys.prefix)
+ if package_path.startswith(py_prefix):
+ return py_prefix, package_path
+ elif site_folder.lower() == 'site-packages':
+ parent, folder = os.path.split(site_parent)
+ # Windows like installations
+ if folder.lower() == 'lib':
+ base_dir = parent
+ # UNIX like installations
+ elif os.path.basename(parent).lower() == 'lib':
+ base_dir = os.path.dirname(parent)
+ else:
+ base_dir = site_parent
+ return base_dir, package_path
+ return None, package_path
+
+
+class locked_cached_property(object):
+ """A decorator that converts a function into a lazy property. The
+ function wrapped is called the first time to retrieve the result
+ and then that calculated result is used the next time you access
+ the value. Works like the one in Werkzeug but has a lock for
+ thread safety.
+ """
+
+ def __init__(self, func, name=None, doc=None):
+ self.__name__ = name or func.__name__
+ self.__module__ = func.__module__
+ self.__doc__ = doc or func.__doc__
+ self.func = func
+ self.lock = RLock()
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ with self.lock:
+ value = obj.__dict__.get(self.__name__, _missing)
+ if value is _missing:
+ value = self.func(obj)
+ obj.__dict__[self.__name__] = value
+ return value
+
+
+class _PackageBoundObject(object):
+ #: The name of the package or module that this app belongs to. Do not
+ #: change this once it is set by the constructor.
+ import_name = None
+
+ #: Location of the template files to be added to the template lookup.
+ #: ``None`` if templates should not be added.
+ template_folder = None
+
+ #: Absolute path to the package on the filesystem. Used to look up
+ #: resources contained in the package.
+ root_path = None
+
+ def __init__(self, import_name, template_folder=None, root_path=None):
+ self.import_name = import_name
+ self.template_folder = template_folder
+
+ if root_path is None:
+ root_path = get_root_path(self.import_name)
+
+ self.root_path = root_path
+ self._static_folder = None
+ self._static_url_path = None
+
+ def _get_static_folder(self):
+ if self._static_folder is not None:
+ return os.path.join(self.root_path, self._static_folder)
+
+ def _set_static_folder(self, value):
+ self._static_folder = value
+
+ static_folder = property(
+ _get_static_folder, _set_static_folder,
+ doc='The absolute path to the configured static folder.'
+ )
+ del _get_static_folder, _set_static_folder
+
+ def _get_static_url_path(self):
+ if self._static_url_path is not None:
+ return self._static_url_path
+
+ if self.static_folder is not None:
+ return '/' + os.path.basename(self.static_folder)
+
+ def _set_static_url_path(self, value):
+ self._static_url_path = value
+
+ static_url_path = property(
+ _get_static_url_path, _set_static_url_path,
+ doc='The URL prefix that the static route will be registered for.'
+ )
+ del _get_static_url_path, _set_static_url_path
+
+ @property
+ def has_static_folder(self):
+ """This is ``True`` if the package bound object's container has a
+ folder for static files.
+
+ .. versionadded:: 0.5
+ """
+ return self.static_folder is not None
+
+ @locked_cached_property
+ def jinja_loader(self):
+ """The Jinja loader for this package bound object.
+
+ .. versionadded:: 0.5
+ """
+ if self.template_folder is not None:
+ return FileSystemLoader(os.path.join(self.root_path,
+ self.template_folder))
+
+ def get_send_file_max_age(self, filename):
+ """Provides default cache_timeout for the :func:`send_file` functions.
+
+ By default, this function returns ``SEND_FILE_MAX_AGE_DEFAULT`` from
+ the configuration of :data:`~flask.current_app`.
+
+ Static file functions such as :func:`send_from_directory` use this
+ function, and :func:`send_file` calls this function on
+ :data:`~flask.current_app` when the given cache_timeout is ``None``. If a
+ cache_timeout is given in :func:`send_file`, that timeout is used;
+ otherwise, this method is called.
+
+ This allows subclasses to change the behavior when sending files based
+ on the filename. For example, to set the cache timeout for .js files
+ to 60 seconds::
+
+ class MyFlask(flask.Flask):
+ def get_send_file_max_age(self, name):
+ if name.lower().endswith('.js'):
+ return 60
+ return flask.Flask.get_send_file_max_age(self, name)
+
+ .. versionadded:: 0.9
+ """
+ return total_seconds(current_app.send_file_max_age_default)
+
+ def send_static_file(self, filename):
+ """Function used internally to send static files from the static
+ folder to the browser.
+
+ .. versionadded:: 0.5
+ """
+ if not self.has_static_folder:
+ raise RuntimeError('No static folder for this object')
+ # Ensure get_send_file_max_age is called in all cases.
+ # Here, we ensure get_send_file_max_age is called for Blueprints.
+ cache_timeout = self.get_send_file_max_age(filename)
+ return send_from_directory(self.static_folder, filename,
+ cache_timeout=cache_timeout)
+
+ def open_resource(self, resource, mode='rb'):
+ """Opens a resource from the application's resource folder. To see
+ how this works, consider the following folder structure::
+
+ /myapplication.py
+ /schema.sql
+ /static
+ /style.css
+ /templates
+ /layout.html
+ /index.html
+
+ If you want to open the :file:`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.
+ :param mode: resource file opening mode, default is 'rb'.
+ """
+ if mode not in ('r', 'rb'):
+ raise ValueError('Resources can only be opened for reading')
+ return open(os.path.join(self.root_path, resource), mode)
+
+
+def total_seconds(td):
+ """Returns the total seconds from a timedelta object.
+
+ :param timedelta td: the timedelta to be converted in seconds
+
+ :returns: number of seconds
+ :rtype: int
+ """
+ return td.days * 60 * 60 * 24 + td.seconds
+
+
+def is_ip(value):
+ """Determine if the given string is an IP address.
+
+ Python 2 on Windows doesn't provide ``inet_pton``, so this only
+ checks IPv4 addresses in that environment.
+
+ :param value: value to check
+ :type value: str
+
+ :return: True if string is an IP address
+ :rtype: bool
+ """
+ if PY2 and os.name == 'nt':
+ try:
+ socket.inet_aton(value)
+ return True
+ except socket.error:
+ return False
+
+ for family in (socket.AF_INET, socket.AF_INET6):
+ try:
+ socket.inet_pton(family, value)
+ except socket.error:
+ pass
+ else:
+ return True
+
+ return False
diff --git a/python/flask/json/__init__.py b/python/flask/json/__init__.py
new file mode 100644
index 0000000..c24286c
--- /dev/null
+++ b/python/flask/json/__init__.py
@@ -0,0 +1,357 @@
+# -*- coding: utf-8 -*-
+"""
+flask.json
+~~~~~~~~~~
+
+:copyright: © 2010 by the Pallets team.
+:license: BSD, see LICENSE for more details.
+"""
+import codecs
+import io
+import uuid
+from datetime import date, datetime
+from flask.globals import current_app, request
+from flask._compat import text_type, PY2
+
+from werkzeug.http import http_date
+from jinja2 import Markup
+
+# Use the same json implementation as itsdangerous on which we
+# depend anyways.
+from itsdangerous import json as _json
+
+
+# Figure out if simplejson escapes slashes. This behavior was changed
+# from one version to another without reason.
+_slash_escape = '\\/' not in _json.dumps('/')
+
+
+__all__ = ['dump', 'dumps', 'load', 'loads', 'htmlsafe_dump',
+ 'htmlsafe_dumps', 'JSONDecoder', 'JSONEncoder',
+ 'jsonify']
+
+
+def _wrap_reader_for_text(fp, encoding):
+ if isinstance(fp.read(0), bytes):
+ fp = io.TextIOWrapper(io.BufferedReader(fp), encoding)
+ return fp
+
+
+def _wrap_writer_for_text(fp, encoding):
+ try:
+ fp.write('')
+ except TypeError:
+ fp = io.TextIOWrapper(fp, encoding)
+ return fp
+
+
+class JSONEncoder(_json.JSONEncoder):
+ """The default Flask JSON encoder. This one extends the default simplejson
+ encoder by also supporting ``datetime`` objects, ``UUID`` as well as
+ ``Markup`` objects which are serialized as RFC 822 datetime strings (same
+ as the HTTP date format). In order to support more data types override the
+ :meth:`default` method.
+ """
+
+ def default(self, o):
+ """Implement this method in a subclass such that it returns a
+ serializable object for ``o``, or calls the base implementation (to
+ raise a :exc:`TypeError`).
+
+ For example, to support arbitrary iterators, you could implement
+ default like this::
+
+ def default(self, o):
+ try:
+ iterable = iter(o)
+ except TypeError:
+ pass
+ else:
+ return list(iterable)
+ return JSONEncoder.default(self, o)
+ """
+ if isinstance(o, datetime):
+ return http_date(o.utctimetuple())
+ if isinstance(o, date):
+ return http_date(o.timetuple())
+ if isinstance(o, uuid.UUID):
+ return str(o)
+ if hasattr(o, '__html__'):
+ return text_type(o.__html__())
+ return _json.JSONEncoder.default(self, o)
+
+
+class JSONDecoder(_json.JSONDecoder):
+ """The default JSON decoder. This one does not change the behavior from
+ the default simplejson decoder. Consult the :mod:`json` documentation
+ for more information. This decoder is not only used for the load
+ functions of this module but also :attr:`~flask.Request`.
+ """
+
+
+def _dump_arg_defaults(kwargs, app=None):
+ """Inject default arguments for dump functions."""
+ if app is None:
+ app = current_app
+
+ if app:
+ bp = app.blueprints.get(request.blueprint) if request else None
+ kwargs.setdefault(
+ 'cls', bp.json_encoder if bp and bp.json_encoder else app.json_encoder
+ )
+
+ if not app.config['JSON_AS_ASCII']:
+ kwargs.setdefault('ensure_ascii', False)
+
+ kwargs.setdefault('sort_keys', app.config['JSON_SORT_KEYS'])
+ else:
+ kwargs.setdefault('sort_keys', True)
+ kwargs.setdefault('cls', JSONEncoder)
+
+
+def _load_arg_defaults(kwargs, app=None):
+ """Inject default arguments for load functions."""
+ if app is None:
+ app = current_app
+
+ if app:
+ bp = app.blueprints.get(request.blueprint) if request else None
+ kwargs.setdefault(
+ 'cls',
+ bp.json_decoder if bp and bp.json_decoder
+ else app.json_decoder
+ )
+ else:
+ kwargs.setdefault('cls', JSONDecoder)
+
+
+def detect_encoding(data):
+ """Detect which UTF codec was used to encode the given bytes.
+
+ The latest JSON standard (:rfc:`8259`) suggests that only UTF-8 is
+ accepted. Older documents allowed 8, 16, or 32. 16 and 32 can be big
+ or little endian. Some editors or libraries may prepend a BOM.
+
+ :param data: Bytes in unknown UTF encoding.
+ :return: UTF encoding name
+ """
+ head = data[:4]
+
+ if head[:3] == codecs.BOM_UTF8:
+ return 'utf-8-sig'
+
+ if b'\x00' not in head:
+ return 'utf-8'
+
+ if head in (codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE):
+ return 'utf-32'
+
+ if head[:2] in (codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE):
+ return 'utf-16'
+
+ if len(head) == 4:
+ if head[:3] == b'\x00\x00\x00':
+ return 'utf-32-be'
+
+ if head[::2] == b'\x00\x00':
+ return 'utf-16-be'
+
+ if head[1:] == b'\x00\x00\x00':
+ return 'utf-32-le'
+
+ if head[1::2] == b'\x00\x00':
+ return 'utf-16-le'
+
+ if len(head) == 2:
+ return 'utf-16-be' if head.startswith(b'\x00') else 'utf-16-le'
+
+ return 'utf-8'
+
+
+def dumps(obj, app=None, **kwargs):
+ """Serialize ``obj`` to a JSON-formatted string. If there is an
+ app context pushed, use the current app's configured encoder
+ (:attr:`~flask.Flask.json_encoder`), or fall back to the default
+ :class:`JSONEncoder`.
+
+ Takes the same arguments as the built-in :func:`json.dumps`, and
+ does some extra configuration based on the application. If the
+ simplejson package is installed, it is preferred.
+
+ :param obj: Object to serialize to JSON.
+ :param app: App instance to use to configure the JSON encoder.
+ Uses ``current_app`` if not given, and falls back to the default
+ encoder when not in an app context.
+ :param kwargs: Extra arguments passed to :func:`json.dumps`.
+
+ .. versionchanged:: 1.0.3
+
+ ``app`` can be passed directly, rather than requiring an app
+ context for configuration.
+ """
+ _dump_arg_defaults(kwargs, app=app)
+ encoding = kwargs.pop('encoding', None)
+ rv = _json.dumps(obj, **kwargs)
+ if encoding is not None and isinstance(rv, text_type):
+ rv = rv.encode(encoding)
+ return rv
+
+
+def dump(obj, fp, app=None, **kwargs):
+ """Like :func:`dumps` but writes into a file object."""
+ _dump_arg_defaults(kwargs, app=app)
+ encoding = kwargs.pop('encoding', None)
+ if encoding is not None:
+ fp = _wrap_writer_for_text(fp, encoding)
+ _json.dump(obj, fp, **kwargs)
+
+
+def loads(s, app=None, **kwargs):
+ """Deserialize an object from a JSON-formatted string ``s``. If
+ there is an app context pushed, use the current app's configured
+ decoder (:attr:`~flask.Flask.json_decoder`), or fall back to the
+ default :class:`JSONDecoder`.
+
+ Takes the same arguments as the built-in :func:`json.loads`, and
+ does some extra configuration based on the application. If the
+ simplejson package is installed, it is preferred.
+
+ :param s: JSON string to deserialize.
+ :param app: App instance to use to configure the JSON decoder.
+ Uses ``current_app`` if not given, and falls back to the default
+ encoder when not in an app context.
+ :param kwargs: Extra arguments passed to :func:`json.dumps`.
+
+ .. versionchanged:: 1.0.3
+
+ ``app`` can be passed directly, rather than requiring an app
+ context for configuration.
+ """
+ _load_arg_defaults(kwargs, app=app)
+ if isinstance(s, bytes):
+ encoding = kwargs.pop('encoding', None)
+ if encoding is None:
+ encoding = detect_encoding(s)
+ s = s.decode(encoding)
+ return _json.loads(s, **kwargs)
+
+
+def load(fp, app=None, **kwargs):
+ """Like :func:`loads` but reads from a file object."""
+ _load_arg_defaults(kwargs, app=app)
+ if not PY2:
+ fp = _wrap_reader_for_text(fp, kwargs.pop('encoding', None) or 'utf-8')
+ return _json.load(fp, **kwargs)
+
+
+def htmlsafe_dumps(obj, **kwargs):
+ """Works exactly like :func:`dumps` but is safe for use in ``<script>``
+ tags. It accepts the same arguments and returns a JSON string. Note that
+ this is available in templates through the ``|tojson`` filter which will
+ also mark the result as safe. Due to how this function escapes certain
+ characters this is safe even if used outside of ``<script>`` tags.
+
+ The following characters are escaped in strings:
+
+ - ``<``
+ - ``>``
+ - ``&``
+ - ``'``
+
+ This makes it safe to embed such strings in any place in HTML with the
+ notable exception of double quoted attributes. In that case single
+ quote your attributes or HTML escape it in addition.
+
+ .. versionchanged:: 0.10
+ This function's return value is now always safe for HTML usage, even
+ if outside of script tags or if used in XHTML. This rule does not
+ hold true when using this function in HTML attributes that are double
+ quoted. Always single quote attributes if you use the ``|tojson``
+ filter. Alternatively use ``|tojson|forceescape``.
+ """
+ rv = dumps(obj, **kwargs) \
+ .replace(u'<', u'\\u003c') \
+ .replace(u'>', u'\\u003e') \
+ .replace(u'&', u'\\u0026') \
+ .replace(u"'", u'\\u0027')
+ if not _slash_escape:
+ rv = rv.replace('\\/', '/')
+ return rv
+
+
+def htmlsafe_dump(obj, fp, **kwargs):
+ """Like :func:`htmlsafe_dumps` but writes into a file object."""
+ fp.write(text_type(htmlsafe_dumps(obj, **kwargs)))
+
+
+def jsonify(*args, **kwargs):
+ """This function wraps :func:`dumps` to add a few enhancements that make
+ life easier. It turns the JSON output into a :class:`~flask.Response`
+ object with the :mimetype:`application/json` mimetype. For convenience, it
+ also converts multiple arguments into an array or multiple keyword arguments
+ into a dict. This means that both ``jsonify(1,2,3)`` and
+ ``jsonify([1,2,3])`` serialize to ``[1,2,3]``.
+
+ For clarity, the JSON serialization behavior has the following differences
+ from :func:`dumps`:
+
+ 1. Single argument: Passed straight through to :func:`dumps`.
+ 2. Multiple arguments: Converted to an array before being passed to
+ :func:`dumps`.
+ 3. Multiple keyword arguments: Converted to a dict before being passed to
+ :func:`dumps`.
+ 4. Both args and kwargs: Behavior undefined and will throw an exception.
+
+ Example usage::
+
+ from flask import jsonify
+
+ @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
+ }
+
+
+ .. versionchanged:: 0.11
+ Added support for serializing top-level arrays. This introduces a
+ security risk in ancient browsers. See :ref:`json-security` for details.
+
+ This function's response will be pretty printed if the
+ ``JSONIFY_PRETTYPRINT_REGULAR`` config parameter is set to True or the
+ Flask app is running in debug mode. Compressed (not pretty) formatting
+ currently means no indents and no spaces after separators.
+
+ .. versionadded:: 0.2
+ """
+
+ indent = None
+ separators = (',', ':')
+
+ if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] or current_app.debug:
+ indent = 2
+ separators = (', ', ': ')
+
+ if args and kwargs:
+ raise TypeError('jsonify() behavior undefined when passed both args and kwargs')
+ elif len(args) == 1: # single args are passed directly to dumps()
+ data = args[0]
+ else:
+ data = args or kwargs
+
+ return current_app.response_class(
+ dumps(data, indent=indent, separators=separators) + '\n',
+ mimetype=current_app.config['JSONIFY_MIMETYPE']
+ )
+
+
+def tojson_filter(obj, **kwargs):
+ return Markup(htmlsafe_dumps(obj, **kwargs))
diff --git a/python/flask/json/tag.py b/python/flask/json/tag.py
new file mode 100644
index 0000000..11c966c
--- /dev/null
+++ b/python/flask/json/tag.py
@@ -0,0 +1,300 @@
+# -*- coding: utf-8 -*-
+"""
+Tagged JSON
+~~~~~~~~~~~
+
+A compact representation for lossless serialization of non-standard JSON types.
+:class:`~flask.sessions.SecureCookieSessionInterface` uses this to serialize
+the session data, but it may be useful in other places. It can be extended to
+support other types.
+
+.. autoclass:: TaggedJSONSerializer
+ :members:
+
+.. autoclass:: JSONTag
+ :members:
+
+Let's seen an example that adds support for :class:`~collections.OrderedDict`.
+Dicts don't have an order in Python or JSON, so to handle this we will dump
+the items as a list of ``[key, value]`` pairs. Subclass :class:`JSONTag` and
+give it the new key ``' od'`` to identify the type. The session serializer
+processes dicts first, so insert the new tag at the front of the order since
+``OrderedDict`` must be processed before ``dict``. ::
+
+ from flask.json.tag import JSONTag
+
+ class TagOrderedDict(JSONTag):
+ __slots__ = ('serializer',)
+ key = ' od'
+
+ def check(self, value):
+ return isinstance(value, OrderedDict)
+
+ def to_json(self, value):
+ return [[k, self.serializer.tag(v)] for k, v in iteritems(value)]
+
+ def to_python(self, value):
+ return OrderedDict(value)
+
+ app.session_interface.serializer.register(TagOrderedDict, index=0)
+
+:copyright: © 2010 by the Pallets team.
+:license: BSD, see LICENSE for more details.
+"""
+
+from base64 import b64decode, b64encode
+from datetime import datetime
+from uuid import UUID
+
+from jinja2 import Markup
+from werkzeug.http import http_date, parse_date
+
+from flask._compat import iteritems, text_type
+from flask.json import dumps, loads
+
+
+class JSONTag(object):
+ """Base class for defining type tags for :class:`TaggedJSONSerializer`."""
+
+ __slots__ = ('serializer',)
+
+ #: The tag to mark the serialized object with. If ``None``, this tag is
+ #: only used as an intermediate step during tagging.
+ key = None
+
+ def __init__(self, serializer):
+ """Create a tagger for the given serializer."""
+ self.serializer = serializer
+
+ def check(self, value):
+ """Check if the given value should be tagged by this tag."""
+ raise NotImplementedError
+
+ def to_json(self, value):
+ """Convert the Python object to an object that is a valid JSON type.
+ The tag will be added later."""
+ raise NotImplementedError
+
+ def to_python(self, value):
+ """Convert the JSON representation back to the correct type. The tag
+ will already be removed."""
+ raise NotImplementedError
+
+ def tag(self, value):
+ """Convert the value to a valid JSON type and add the tag structure
+ around it."""
+ return {self.key: self.to_json(value)}
+
+
+class TagDict(JSONTag):
+ """Tag for 1-item dicts whose only key matches a registered tag.
+
+ Internally, the dict key is suffixed with `__`, and the suffix is removed
+ when deserializing.
+ """
+
+ __slots__ = ()
+ key = ' di'
+
+ def check(self, value):
+ return (
+ isinstance(value, dict)
+ and len(value) == 1
+ and next(iter(value)) in self.serializer.tags
+ )
+
+ def to_json(self, value):
+ key = next(iter(value))
+ return {key + '__': self.serializer.tag(value[key])}
+
+ def to_python(self, value):
+ key = next(iter(value))
+ return {key[:-2]: value[key]}
+
+
+class PassDict(JSONTag):
+ __slots__ = ()
+
+ def check(self, value):
+ return isinstance(value, dict)
+
+ def to_json(self, value):
+ # JSON objects may only have string keys, so don't bother tagging the
+ # key here.
+ return dict((k, self.serializer.tag(v)) for k, v in iteritems(value))
+
+ tag = to_json
+
+
+class TagTuple(JSONTag):
+ __slots__ = ()
+ key = ' t'
+
+ def check(self, value):
+ return isinstance(value, tuple)
+
+ def to_json(self, value):
+ return [self.serializer.tag(item) for item in value]
+
+ def to_python(self, value):
+ return tuple(value)
+
+
+class PassList(JSONTag):
+ __slots__ = ()
+
+ def check(self, value):
+ return isinstance(value, list)
+
+ def to_json(self, value):
+ return [self.serializer.tag(item) for item in value]
+
+ tag = to_json
+
+
+class TagBytes(JSONTag):
+ __slots__ = ()
+ key = ' b'
+
+ def check(self, value):
+ return isinstance(value, bytes)
+
+ def to_json(self, value):
+ return b64encode(value).decode('ascii')
+
+ def to_python(self, value):
+ return b64decode(value)
+
+
+class TagMarkup(JSONTag):
+ """Serialize anything matching the :class:`~flask.Markup` API by
+ having a ``__html__`` method to the result of that method. Always
+ deserializes to an instance of :class:`~flask.Markup`."""
+
+ __slots__ = ()
+ key = ' m'
+
+ def check(self, value):
+ return callable(getattr(value, '__html__', None))
+
+ def to_json(self, value):
+ return text_type(value.__html__())
+
+ def to_python(self, value):
+ return Markup(value)
+
+
+class TagUUID(JSONTag):
+ __slots__ = ()
+ key = ' u'
+
+ def check(self, value):
+ return isinstance(value, UUID)
+
+ def to_json(self, value):
+ return value.hex
+
+ def to_python(self, value):
+ return UUID(value)
+
+
+class TagDateTime(JSONTag):
+ __slots__ = ()
+ key = ' d'
+
+ def check(self, value):
+ return isinstance(value, datetime)
+
+ def to_json(self, value):
+ return http_date(value)
+
+ def to_python(self, value):
+ return parse_date(value)
+
+
+class TaggedJSONSerializer(object):
+ """Serializer that uses a tag system to compactly represent objects that
+ are not JSON types. Passed as the intermediate serializer to
+ :class:`itsdangerous.Serializer`.
+
+ The following extra types are supported:
+
+ * :class:`dict`
+ * :class:`tuple`
+ * :class:`bytes`
+ * :class:`~flask.Markup`
+ * :class:`~uuid.UUID`
+ * :class:`~datetime.datetime`
+ """
+
+ __slots__ = ('tags', 'order')
+
+ #: Tag classes to bind when creating the serializer. Other tags can be
+ #: added later using :meth:`~register`.
+ default_tags = [
+ TagDict, PassDict, TagTuple, PassList, TagBytes, TagMarkup, TagUUID,
+ TagDateTime,
+ ]
+
+ def __init__(self):
+ self.tags = {}
+ self.order = []
+
+ for cls in self.default_tags:
+ self.register(cls)
+
+ def register(self, tag_class, force=False, index=None):
+ """Register a new tag with this serializer.
+
+ :param tag_class: tag class to register. Will be instantiated with this
+ serializer instance.
+ :param force: overwrite an existing tag. If false (default), a
+ :exc:`KeyError` is raised.
+ :param index: index to insert the new tag in the tag order. Useful when
+ the new tag is a special case of an existing tag. If ``None``
+ (default), the tag is appended to the end of the order.
+
+ :raise KeyError: if the tag key is already registered and ``force`` is
+ not true.
+ """
+ tag = tag_class(self)
+ key = tag.key
+
+ if key is not None:
+ if not force and key in self.tags:
+ raise KeyError("Tag '{0}' is already registered.".format(key))
+
+ self.tags[key] = tag
+
+ if index is None:
+ self.order.append(tag)
+ else:
+ self.order.insert(index, tag)
+
+ def tag(self, value):
+ """Convert a value to a tagged representation if necessary."""
+ for tag in self.order:
+ if tag.check(value):
+ return tag.tag(value)
+
+ return value
+
+ def untag(self, value):
+ """Convert a tagged representation back to the original type."""
+ if len(value) != 1:
+ return value
+
+ key = next(iter(value))
+
+ if key not in self.tags:
+ return value
+
+ return self.tags[key].to_python(value[key])
+
+ def dumps(self, value):
+ """Tag the value and dump it to a compact JSON string."""
+ return dumps(self.tag(value), separators=(',', ':'))
+
+ def loads(self, value):
+ """Load data from a JSON string and deserialized any tagged objects."""
+ return loads(value, object_hook=self.untag)
diff --git a/python/flask/logging.py b/python/flask/logging.py
new file mode 100644
index 0000000..389c2c2
--- /dev/null
+++ b/python/flask/logging.py
@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+"""
+flask.logging
+~~~~~~~~~~~~~
+
+:copyright: © 2010 by the Pallets team.
+:license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import absolute_import
+
+import logging
+import sys
+
+from werkzeug.local import LocalProxy
+
+from .globals import request
+
+
+@LocalProxy
+def wsgi_errors_stream():
+ """Find the most appropriate error stream for the application. If a request
+ is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``.
+
+ If you configure your own :class:`logging.StreamHandler`, you may want to
+ use this for the stream. If you are using file or dict configuration and
+ can't import this directly, you can refer to it as
+ ``ext://flask.logging.wsgi_errors_stream``.
+ """
+ return request.environ['wsgi.errors'] if request else sys.stderr
+
+
+def has_level_handler(logger):
+ """Check if there is a handler in the logging chain that will handle the
+ given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`.
+ """
+ level = logger.getEffectiveLevel()
+ current = logger
+
+ while current:
+ if any(handler.level <= level for handler in current.handlers):
+ return True
+
+ if not current.propagate:
+ break
+
+ current = current.parent
+
+ return False
+
+
+#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format
+#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``.
+default_handler = logging.StreamHandler(wsgi_errors_stream)
+default_handler.setFormatter(logging.Formatter(
+ '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
+))
+
+
+def create_logger(app):
+ """Get the ``'flask.app'`` logger and configure it if needed.
+
+ When :attr:`~flask.Flask.debug` is enabled, set the logger level to
+ :data:`logging.DEBUG` if it is not set.
+
+ If there is no handler for the logger's effective level, add a
+ :class:`~logging.StreamHandler` for
+ :func:`~flask.logging.wsgi_errors_stream` with a basic format.
+ """
+ logger = logging.getLogger('flask.app')
+
+ if app.debug and logger.level == logging.NOTSET:
+ logger.setLevel(logging.DEBUG)
+
+ if not has_level_handler(logger):
+ logger.addHandler(default_handler)
+
+ return logger
diff --git a/python/flask/sessions.py b/python/flask/sessions.py
new file mode 100644
index 0000000..c8b7d4e
--- /dev/null
+++ b/python/flask/sessions.py
@@ -0,0 +1,385 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.sessions
+ ~~~~~~~~~~~~~~
+
+ Implements cookie based sessions based on itsdangerous.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import hashlib
+import warnings
+from datetime import datetime
+
+from itsdangerous import BadSignature, URLSafeTimedSerializer
+from werkzeug.datastructures import CallbackDict
+
+from flask._compat import collections_abc
+from flask.helpers import is_ip, total_seconds
+from flask.json.tag import TaggedJSONSerializer
+
+
+class SessionMixin(collections_abc.MutableMapping):
+ """Expands a basic dictionary with session attributes."""
+
+ @property
+ def permanent(self):
+ """This reflects the ``'_permanent'`` key in the dict."""
+ return self.get('_permanent', False)
+
+ @permanent.setter
+ def permanent(self, value):
+ self['_permanent'] = bool(value)
+
+ #: Some implementations can detect whether a session is newly
+ #: created, but that is not guaranteed. Use with caution. The mixin
+ # default is hard-coded ``False``.
+ new = False
+
+ #: Some implementations can detect changes to the session and set
+ #: this when that happens. The mixin default is hard coded to
+ #: ``True``.
+ modified = True
+
+ #: Some implementations can detect when session data is read or
+ #: written and set this when that happens. The mixin default is hard
+ #: coded to ``True``.
+ accessed = True
+
+
+class SecureCookieSession(CallbackDict, SessionMixin):
+ """Base class for sessions based on signed cookies.
+
+ This session backend will set the :attr:`modified` and
+ :attr:`accessed` attributes. It cannot reliably track whether a
+ session is new (vs. empty), so :attr:`new` remains hard coded to
+ ``False``.
+ """
+
+ #: When data is changed, this is set to ``True``. Only the session
+ #: dictionary itself is tracked; if the session contains mutable
+ #: data (for example a nested dict) then this must be set to
+ #: ``True`` manually when modifying that data. The session cookie
+ #: will only be written to the response if this is ``True``.
+ modified = False
+
+ #: When data is read or written, this is set to ``True``. Used by
+ # :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie``
+ #: header, which allows caching proxies to cache different pages for
+ #: different users.
+ accessed = False
+
+ def __init__(self, initial=None):
+ def on_update(self):
+ self.modified = True
+ self.accessed = True
+
+ super(SecureCookieSession, self).__init__(initial, on_update)
+
+ def __getitem__(self, key):
+ self.accessed = True
+ return super(SecureCookieSession, self).__getitem__(key)
+
+ def get(self, key, default=None):
+ self.accessed = True
+ return super(SecureCookieSession, self).get(key, default)
+
+ def setdefault(self, key, default=None):
+ self.accessed = True
+ return super(SecureCookieSession, self).setdefault(key, default)
+
+
+class NullSession(SecureCookieSession):
+ """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 SessionInterface(object):
+ """The basic interface you have to implement in order to replace the
+ default session interface which uses werkzeug's securecookie
+ implementation. The only methods you have to implement are
+ :meth:`open_session` and :meth:`save_session`, the others have
+ useful defaults which you don't need to change.
+
+ The session object returned by the :meth:`open_session` method has to
+ provide a dictionary like interface plus the properties and methods
+ from the :class:`SessionMixin`. We recommend just subclassing a dict
+ and adding that mixin::
+
+ class Session(dict, SessionMixin):
+ pass
+
+ If :meth:`open_session` returns ``None`` Flask will call into
+ :meth:`make_null_session` to create a session that acts as replacement
+ if the session support cannot work because some requirement is not
+ fulfilled. The default :class:`NullSession` class that is created
+ will complain that the secret key was not set.
+
+ To replace the session interface on an application all you have to do
+ is to assign :attr:`flask.Flask.session_interface`::
+
+ app = Flask(__name__)
+ app.session_interface = MySessionInterface()
+
+ .. versionadded:: 0.8
+ """
+
+ #: :meth:`make_null_session` will look here for the class that should
+ #: be created when a null session is requested. Likewise the
+ #: :meth:`is_null_session` method will perform a typecheck against
+ #: this type.
+ null_session_class = NullSession
+
+ #: A flag that indicates if the session interface is pickle based.
+ #: This can be used by Flask extensions to make a decision in regards
+ #: to how to deal with the session object.
+ #:
+ #: .. versionadded:: 0.10
+ pickle_based = False
+
+ def make_null_session(self, app):
+ """Creates a null session which acts as a replacement object if the
+ real session support could not be loaded due to a configuration
+ error. This mainly aids the user experience because the job of the
+ null session is to still support lookup without complaining but
+ modifications are answered with a helpful error message of what
+ failed.
+
+ This creates an instance of :attr:`null_session_class` by default.
+ """
+ return self.null_session_class()
+
+ def is_null_session(self, obj):
+ """Checks if a given object is a null session. Null sessions are
+ not asked to be saved.
+
+ This checks if the object is an instance of :attr:`null_session_class`
+ by default.
+ """
+ return isinstance(obj, self.null_session_class)
+
+ def get_cookie_domain(self, app):
+ """Returns the domain that should be set for the session cookie.
+
+ Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwise
+ falls back to detecting the domain based on ``SERVER_NAME``.
+
+ Once detected (or if not set at all), ``SESSION_COOKIE_DOMAIN`` is
+ updated to avoid re-running the logic.
+ """
+
+ rv = app.config['SESSION_COOKIE_DOMAIN']
+
+ # set explicitly, or cached from SERVER_NAME detection
+ # if False, return None
+ if rv is not None:
+ return rv if rv else None
+
+ rv = app.config['SERVER_NAME']
+
+ # server name not set, cache False to return none next time
+ if not rv:
+ app.config['SESSION_COOKIE_DOMAIN'] = False
+ return None
+
+ # chop off the port which is usually not supported by browsers
+ # remove any leading '.' since we'll add that later
+ rv = rv.rsplit(':', 1)[0].lstrip('.')
+
+ if '.' not in rv:
+ # Chrome doesn't allow names without a '.'
+ # this should only come up with localhost
+ # hack around this by not setting the name, and show a warning
+ warnings.warn(
+ '"{rv}" is not a valid cookie domain, it must contain a ".".'
+ ' Add an entry to your hosts file, for example'
+ ' "{rv}.localdomain", and use that instead.'.format(rv=rv)
+ )
+ app.config['SESSION_COOKIE_DOMAIN'] = False
+ return None
+
+ ip = is_ip(rv)
+
+ if ip:
+ warnings.warn(
+ 'The session cookie domain is an IP address. This may not work'
+ ' as intended in some browsers. Add an entry to your hosts'
+ ' file, for example "localhost.localdomain", and use that'
+ ' instead.'
+ )
+
+ # if this is not an ip and app is mounted at the root, allow subdomain
+ # matching by adding a '.' prefix
+ if self.get_cookie_path(app) == '/' and not ip:
+ rv = '.' + rv
+
+ app.config['SESSION_COOKIE_DOMAIN'] = rv
+ return rv
+
+ def get_cookie_path(self, app):
+ """Returns the path for which the cookie should be valid. The
+ default implementation uses the value from the ``SESSION_COOKIE_PATH``
+ config var if it's set, and falls back to ``APPLICATION_ROOT`` or
+ uses ``/`` if it's ``None``.
+ """
+ return app.config['SESSION_COOKIE_PATH'] \
+ or app.config['APPLICATION_ROOT']
+
+ def get_cookie_httponly(self, app):
+ """Returns True if the session cookie should be httponly. This
+ currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
+ config var.
+ """
+ return app.config['SESSION_COOKIE_HTTPONLY']
+
+ def get_cookie_secure(self, app):
+ """Returns True if the cookie should be secure. This currently
+ just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
+ """
+ return app.config['SESSION_COOKIE_SECURE']
+
+ def get_cookie_samesite(self, app):
+ """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the
+ ``SameSite`` attribute. This currently just returns the value of
+ the :data:`SESSION_COOKIE_SAMESITE` setting.
+ """
+ return app.config['SESSION_COOKIE_SAMESITE']
+
+ def get_expiration_time(self, app, session):
+ """A helper method that returns an expiration date for the session
+ or ``None`` if the session is linked to the browser session. The
+ default implementation returns now + the permanent session
+ lifetime configured on the application.
+ """
+ if session.permanent:
+ return datetime.utcnow() + app.permanent_session_lifetime
+
+ def should_set_cookie(self, app, session):
+ """Used by session backends to determine if a ``Set-Cookie`` header
+ should be set for this session cookie for this response. If the session
+ has been modified, the cookie is set. If the session is permanent and
+ the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is
+ always set.
+
+ This check is usually skipped if the session was deleted.
+
+ .. versionadded:: 0.11
+ """
+
+ return session.modified or (
+ session.permanent and app.config['SESSION_REFRESH_EACH_REQUEST']
+ )
+
+ def open_session(self, app, request):
+ """This method has to be implemented and must either return ``None``
+ in case the loading failed because of a configuration error or an
+ instance of a session object which implements a dictionary like
+ interface + the methods and attributes on :class:`SessionMixin`.
+ """
+ raise NotImplementedError()
+
+ def save_session(self, app, session, response):
+ """This is called for actual sessions returned by :meth:`open_session`
+ at the end of the request. This is still called during a request
+ context so if you absolutely need access to the request you can do
+ that.
+ """
+ raise NotImplementedError()
+
+
+session_json_serializer = TaggedJSONSerializer()
+
+
+class SecureCookieSessionInterface(SessionInterface):
+ """The default session interface that stores sessions in signed cookies
+ through the :mod:`itsdangerous` module.
+ """
+ #: the salt that should be applied on top of the secret key for the
+ #: signing of cookie based sessions.
+ salt = 'cookie-session'
+ #: the hash function to use for the signature. The default is sha1
+ digest_method = staticmethod(hashlib.sha1)
+ #: the name of the itsdangerous supported key derivation. The default
+ #: is hmac.
+ key_derivation = 'hmac'
+ #: A python serializer for the payload. The default is a compact
+ #: JSON derived serializer with support for some extra Python types
+ #: such as datetime objects or tuples.
+ serializer = session_json_serializer
+ session_class = SecureCookieSession
+
+ def get_signing_serializer(self, app):
+ if not app.secret_key:
+ return None
+ signer_kwargs = dict(
+ key_derivation=self.key_derivation,
+ digest_method=self.digest_method
+ )
+ return URLSafeTimedSerializer(app.secret_key, salt=self.salt,
+ serializer=self.serializer,
+ signer_kwargs=signer_kwargs)
+
+ def open_session(self, app, request):
+ s = self.get_signing_serializer(app)
+ if s is None:
+ return None
+ val = request.cookies.get(app.session_cookie_name)
+ if not val:
+ return self.session_class()
+ max_age = total_seconds(app.permanent_session_lifetime)
+ try:
+ data = s.loads(val, max_age=max_age)
+ return self.session_class(data)
+ except BadSignature:
+ return self.session_class()
+
+ def save_session(self, app, session, response):
+ domain = self.get_cookie_domain(app)
+ path = self.get_cookie_path(app)
+
+ # If the session is modified to be empty, remove the cookie.
+ # If the session is empty, return without setting the cookie.
+ if not session:
+ if session.modified:
+ response.delete_cookie(
+ app.session_cookie_name,
+ domain=domain,
+ path=path
+ )
+
+ return
+
+ # Add a "Vary: Cookie" header if the session was accessed at all.
+ if session.accessed:
+ response.vary.add('Cookie')
+
+ if not self.should_set_cookie(app, session):
+ return
+
+ httponly = self.get_cookie_httponly(app)
+ secure = self.get_cookie_secure(app)
+ samesite = self.get_cookie_samesite(app)
+ expires = self.get_expiration_time(app, session)
+ val = self.get_signing_serializer(app).dumps(dict(session))
+ response.set_cookie(
+ app.session_cookie_name,
+ val,
+ expires=expires,
+ httponly=httponly,
+ domain=domain,
+ path=path,
+ secure=secure,
+ samesite=samesite
+ )
diff --git a/python/flask/signals.py b/python/flask/signals.py
new file mode 100644
index 0000000..18f2630
--- /dev/null
+++ b/python/flask/signals.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.signals
+ ~~~~~~~~~~~~~
+
+ Implements signals based on blinker if available, otherwise
+ falls silently back to a noop.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+signals_available = False
+try:
+ from blinker import Namespace
+ signals_available = True
+except ImportError:
+ class Namespace(object):
+ def signal(self, name, doc=None):
+ return _FakeSignal(name, doc)
+
+ class _FakeSignal(object):
+ """If blinker is unavailable, create a fake class with the same
+ interface that allows sending of signals but will fail with an
+ error on anything else. Instead of doing anything on send, it
+ will just ignore the arguments and do nothing instead.
+ """
+
+ def __init__(self, name, doc=None):
+ self.name = name
+ self.__doc__ = doc
+ def _fail(self, *args, **kwargs):
+ raise RuntimeError('signalling support is unavailable '
+ 'because the blinker library is '
+ 'not installed.')
+ send = lambda *a, **kw: None
+ connect = disconnect = has_receivers_for = receivers_for = \
+ temporarily_connected_to = connected_to = _fail
+ del _fail
+
+# The namespace for code signals. If you are not Flask code, do
+# not put signals in here. Create your own namespace instead.
+_signals = Namespace()
+
+
+# Core signals. For usage examples grep the source code or consult
+# the API documentation in docs/api.rst as well as docs/signals.rst
+template_rendered = _signals.signal('template-rendered')
+before_render_template = _signals.signal('before-render-template')
+request_started = _signals.signal('request-started')
+request_finished = _signals.signal('request-finished')
+request_tearing_down = _signals.signal('request-tearing-down')
+got_request_exception = _signals.signal('got-request-exception')
+appcontext_tearing_down = _signals.signal('appcontext-tearing-down')
+appcontext_pushed = _signals.signal('appcontext-pushed')
+appcontext_popped = _signals.signal('appcontext-popped')
+message_flashed = _signals.signal('message-flashed')
diff --git a/python/flask/templating.py b/python/flask/templating.py
new file mode 100644
index 0000000..0240200
--- /dev/null
+++ b/python/flask/templating.py
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.templating
+ ~~~~~~~~~~~~~~~~
+
+ Implements the bridge to Jinja2.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from jinja2 import BaseLoader, Environment as BaseEnvironment, \
+ TemplateNotFound
+
+from .globals import _request_ctx_stack, _app_ctx_stack
+from .signals import template_rendered, before_render_template
+
+
+def _default_template_ctx_processor():
+ """Default template context processor. Injects `request`,
+ `session` and `g`.
+ """
+ reqctx = _request_ctx_stack.top
+ appctx = _app_ctx_stack.top
+ rv = {}
+ if appctx is not None:
+ rv['g'] = appctx.g
+ if reqctx is not None:
+ rv['request'] = reqctx.request
+ rv['session'] = reqctx.session
+ return rv
+
+
+class Environment(BaseEnvironment):
+ """Works like a regular Jinja2 environment but has some additional
+ knowledge of how Flask's blueprint works so that it can prepend the
+ name of the blueprint to referenced templates if necessary.
+ """
+
+ def __init__(self, app, **options):
+ if 'loader' not in options:
+ options['loader'] = app.create_global_jinja_loader()
+ BaseEnvironment.__init__(self, **options)
+ self.app = app
+
+
+class DispatchingJinjaLoader(BaseLoader):
+ """A loader that looks for templates in the application and all
+ the blueprint folders.
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def get_source(self, environment, template):
+ if self.app.config['EXPLAIN_TEMPLATE_LOADING']:
+ return self._get_source_explained(environment, template)
+ return self._get_source_fast(environment, template)
+
+ def _get_source_explained(self, environment, template):
+ attempts = []
+ trv = None
+
+ for srcobj, loader in self._iter_loaders(template):
+ try:
+ rv = loader.get_source(environment, template)
+ if trv is None:
+ trv = rv
+ except TemplateNotFound:
+ rv = None
+ attempts.append((loader, srcobj, rv))
+
+ from .debughelpers import explain_template_loading_attempts
+ explain_template_loading_attempts(self.app, template, attempts)
+
+ if trv is not None:
+ return trv
+ raise TemplateNotFound(template)
+
+ def _get_source_fast(self, environment, template):
+ for srcobj, loader in self._iter_loaders(template):
+ try:
+ return loader.get_source(environment, template)
+ except TemplateNotFound:
+ continue
+ raise TemplateNotFound(template)
+
+ def _iter_loaders(self, template):
+ loader = self.app.jinja_loader
+ if loader is not None:
+ yield self.app, loader
+
+ for blueprint in self.app.iter_blueprints():
+ loader = blueprint.jinja_loader
+ if loader is not None:
+ yield blueprint, loader
+
+ def list_templates(self):
+ result = set()
+ loader = self.app.jinja_loader
+ if loader is not None:
+ result.update(loader.list_templates())
+
+ for blueprint in self.app.iter_blueprints():
+ loader = blueprint.jinja_loader
+ if loader is not None:
+ for template in loader.list_templates():
+ result.add(template)
+
+ return list(result)
+
+
+def _render(template, context, app):
+ """Renders the template and fires the signal"""
+
+ before_render_template.send(app, template=template, context=context)
+ rv = template.render(context)
+ template_rendered.send(app, template=template, context=context)
+ return rv
+
+
+def render_template(template_name_or_list, **context):
+ """Renders a template from the template folder with the given
+ context.
+
+ :param template_name_or_list: the name of the template to be
+ rendered, or an iterable with template names
+ the first one existing will be rendered
+ :param context: the variables that should be available in the
+ context of the template.
+ """
+ ctx = _app_ctx_stack.top
+ ctx.app.update_template_context(context)
+ return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list),
+ context, ctx.app)
+
+
+def render_template_string(source, **context):
+ """Renders a template from the given template source string
+ with the given context. Template variables will be autoescaped.
+
+ :param source: the source code of the template to be
+ rendered
+ :param context: the variables that should be available in the
+ context of the template.
+ """
+ ctx = _app_ctx_stack.top
+ ctx.app.update_template_context(context)
+ return _render(ctx.app.jinja_env.from_string(source),
+ context, ctx.app)
diff --git a/python/flask/testing.py b/python/flask/testing.py
new file mode 100644
index 0000000..114c5cc
--- /dev/null
+++ b/python/flask/testing.py
@@ -0,0 +1,246 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testing
+ ~~~~~~~~~~~~~
+
+ Implements test support helpers. This module is lazily imported
+ and usually not used in production environments.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import werkzeug
+from contextlib import contextmanager
+
+from click.testing import CliRunner
+from flask.cli import ScriptInfo
+from werkzeug.test import Client, EnvironBuilder
+from flask import _request_ctx_stack
+from flask.json import dumps as json_dumps
+from werkzeug.urls import url_parse
+
+
+def make_test_environ_builder(
+ app, path='/', base_url=None, subdomain=None, url_scheme=None,
+ *args, **kwargs
+):
+ """Create a :class:`~werkzeug.test.EnvironBuilder`, taking some
+ defaults from the application.
+
+ :param app: The Flask application to configure the environment from.
+ :param path: URL path being requested.
+ :param base_url: Base URL where the app is being served, which
+ ``path`` is relative to. If not given, built from
+ :data:`PREFERRED_URL_SCHEME`, ``subdomain``,
+ :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`.
+ :param subdomain: Subdomain name to append to :data:`SERVER_NAME`.
+ :param url_scheme: Scheme to use instead of
+ :data:`PREFERRED_URL_SCHEME`.
+ :param json: If given, this is serialized as JSON and passed as
+ ``data``. Also defaults ``content_type`` to
+ ``application/json``.
+ :param args: other positional arguments passed to
+ :class:`~werkzeug.test.EnvironBuilder`.
+ :param kwargs: other keyword arguments passed to
+ :class:`~werkzeug.test.EnvironBuilder`.
+ """
+
+ assert (
+ not (base_url or subdomain or url_scheme)
+ or (base_url is not None) != bool(subdomain or url_scheme)
+ ), 'Cannot pass "subdomain" or "url_scheme" with "base_url".'
+
+ if base_url is None:
+ http_host = app.config.get('SERVER_NAME') or 'localhost'
+ app_root = app.config['APPLICATION_ROOT']
+
+ if subdomain:
+ http_host = '{0}.{1}'.format(subdomain, http_host)
+
+ if url_scheme is None:
+ url_scheme = app.config['PREFERRED_URL_SCHEME']
+
+ url = url_parse(path)
+ base_url = '{scheme}://{netloc}/{path}'.format(
+ scheme=url.scheme or url_scheme,
+ netloc=url.netloc or http_host,
+ path=app_root.lstrip('/')
+ )
+ path = url.path
+
+ if url.query:
+ sep = b'?' if isinstance(url.query, bytes) else '?'
+ path += sep + url.query
+
+ # TODO use EnvironBuilder.json_dumps once we require Werkzeug 0.15
+ if 'json' in kwargs:
+ assert 'data' not in kwargs, "Client cannot provide both 'json' and 'data'."
+ kwargs['data'] = json_dumps(kwargs.pop('json'), app=app)
+
+ if 'content_type' not in kwargs:
+ kwargs['content_type'] = 'application/json'
+
+ return EnvironBuilder(path, base_url, *args, **kwargs)
+
+
+class FlaskClient(Client):
+ """Works like a regular Werkzeug test client but has some knowledge about
+ how Flask works to defer the cleanup of the request context stack to the
+ end of a ``with`` body when used in a ``with`` statement. For general
+ information about how to use this class refer to
+ :class:`werkzeug.test.Client`.
+
+ .. versionchanged:: 0.12
+ `app.test_client()` includes preset default environment, which can be
+ set after instantiation of the `app.test_client()` object in
+ `client.environ_base`.
+
+ Basic usage is outlined in the :ref:`testing` chapter.
+ """
+
+ preserve_context = False
+
+ def __init__(self, *args, **kwargs):
+ super(FlaskClient, self).__init__(*args, **kwargs)
+ self.environ_base = {
+ "REMOTE_ADDR": "127.0.0.1",
+ "HTTP_USER_AGENT": "werkzeug/" + werkzeug.__version__
+ }
+
+ @contextmanager
+ def session_transaction(self, *args, **kwargs):
+ """When used in combination with a ``with`` statement this opens a
+ session transaction. This can be used to modify the session that
+ the test client uses. Once the ``with`` block is left the session is
+ stored back.
+
+ ::
+
+ with client.session_transaction() as session:
+ session['value'] = 42
+
+ Internally this is implemented by going through a temporary test
+ request context and since session handling could depend on
+ request variables this function accepts the same arguments as
+ :meth:`~flask.Flask.test_request_context` which are directly
+ passed through.
+ """
+ if self.cookie_jar is None:
+ raise RuntimeError('Session transactions only make sense '
+ 'with cookies enabled.')
+ app = self.application
+ environ_overrides = kwargs.setdefault('environ_overrides', {})
+ self.cookie_jar.inject_wsgi(environ_overrides)
+ outer_reqctx = _request_ctx_stack.top
+ with app.test_request_context(*args, **kwargs) as c:
+ session_interface = app.session_interface
+ sess = session_interface.open_session(app, c.request)
+ if sess is None:
+ raise RuntimeError('Session backend did not open a session. '
+ 'Check the configuration')
+
+ # Since we have to open a new request context for the session
+ # handling we want to make sure that we hide out own context
+ # from the caller. By pushing the original request context
+ # (or None) on top of this and popping it we get exactly that
+ # behavior. It's important to not use the push and pop
+ # methods of the actual request context object since that would
+ # mean that cleanup handlers are called
+ _request_ctx_stack.push(outer_reqctx)
+ try:
+ yield sess
+ finally:
+ _request_ctx_stack.pop()
+
+ resp = app.response_class()
+ if not session_interface.is_null_session(sess):
+ session_interface.save_session(app, sess, resp)
+ headers = resp.get_wsgi_headers(c.request.environ)
+ self.cookie_jar.extract_wsgi(c.request.environ, headers)
+
+ def open(self, *args, **kwargs):
+ as_tuple = kwargs.pop('as_tuple', False)
+ buffered = kwargs.pop('buffered', False)
+ follow_redirects = kwargs.pop('follow_redirects', False)
+
+ if (
+ not kwargs and len(args) == 1
+ and isinstance(args[0], (EnvironBuilder, dict))
+ ):
+ environ = self.environ_base.copy()
+
+ if isinstance(args[0], EnvironBuilder):
+ environ.update(args[0].get_environ())
+ else:
+ environ.update(args[0])
+
+ environ['flask._preserve_context'] = self.preserve_context
+ else:
+ kwargs.setdefault('environ_overrides', {}) \
+ ['flask._preserve_context'] = self.preserve_context
+ kwargs.setdefault('environ_base', self.environ_base)
+ builder = make_test_environ_builder(
+ self.application, *args, **kwargs
+ )
+
+ try:
+ environ = builder.get_environ()
+ finally:
+ builder.close()
+
+ return Client.open(
+ self, environ,
+ as_tuple=as_tuple,
+ buffered=buffered,
+ follow_redirects=follow_redirects
+ )
+
+ def __enter__(self):
+ if self.preserve_context:
+ raise RuntimeError('Cannot nest client invocations')
+ self.preserve_context = True
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ self.preserve_context = False
+
+ # on exit we want to clean up earlier. Normally the request context
+ # stays preserved until the next request in the same thread comes
+ # in. See RequestGlobals.push() for the general behavior.
+ top = _request_ctx_stack.top
+ if top is not None and top.preserved:
+ top.pop()
+
+
+class FlaskCliRunner(CliRunner):
+ """A :class:`~click.testing.CliRunner` for testing a Flask app's
+ CLI commands. Typically created using
+ :meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`.
+ """
+ def __init__(self, app, **kwargs):
+ self.app = app
+ super(FlaskCliRunner, self).__init__(**kwargs)
+
+ def invoke(self, cli=None, args=None, **kwargs):
+ """Invokes a CLI command in an isolated environment. See
+ :meth:`CliRunner.invoke <click.testing.CliRunner.invoke>` for
+ full method documentation. See :ref:`testing-cli` for examples.
+
+ If the ``obj`` argument is not given, passes an instance of
+ :class:`~flask.cli.ScriptInfo` that knows how to load the Flask
+ app being tested.
+
+ :param cli: Command object to invoke. Default is the app's
+ :attr:`~flask.app.Flask.cli` group.
+ :param args: List of strings to invoke the command with.
+
+ :return: a :class:`~click.testing.Result` object.
+ """
+ if cli is None:
+ cli = self.app.cli
+
+ if 'obj' not in kwargs:
+ kwargs['obj'] = ScriptInfo(create_app=lambda: self.app)
+
+ return super(FlaskCliRunner, self).invoke(cli, args, **kwargs)
diff --git a/python/flask/views.py b/python/flask/views.py
new file mode 100644
index 0000000..1f2c997
--- /dev/null
+++ b/python/flask/views.py
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.views
+ ~~~~~~~~~~~
+
+ This module provides class-based views inspired by the ones in Django.
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from .globals import request
+from ._compat import with_metaclass
+
+
+http_method_funcs = frozenset(['get', 'post', 'head', 'options',
+ 'delete', 'put', 'trace', 'patch'])
+
+
+class View(object):
+ """Alternative way to use view functions. A subclass has to implement
+ :meth:`dispatch_request` which is called with the view arguments from
+ the URL routing system. If :attr:`methods` is provided the methods
+ do not have to be passed to the :meth:`~flask.Flask.add_url_rule`
+ method explicitly::
+
+ class MyView(View):
+ methods = ['GET']
+
+ def dispatch_request(self, name):
+ return 'Hello %s!' % name
+
+ app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))
+
+ When you want to decorate a pluggable view you will have to either do that
+ when the view function is created (by wrapping the return value of
+ :meth:`as_view`) or you can use the :attr:`decorators` attribute::
+
+ class SecretView(View):
+ methods = ['GET']
+ decorators = [superuser_required]
+
+ def dispatch_request(self):
+ ...
+
+ The decorators stored in the decorators list are applied one after another
+ when the view function is created. Note that you can *not* use the class
+ based decorators since those would decorate the view class and not the
+ generated view function!
+ """
+
+ #: A list of methods this view can handle.
+ methods = None
+
+ #: Setting this disables or force-enables the automatic options handling.
+ provide_automatic_options = None
+
+ #: The canonical way to decorate class-based views is to decorate the
+ #: return value of as_view(). However since this moves parts of the
+ #: logic from the class declaration to the place where it's hooked
+ #: into the routing system.
+ #:
+ #: You can place one or more decorators in this list and whenever the
+ #: view function is created the result is automatically decorated.
+ #:
+ #: .. versionadded:: 0.8
+ decorators = ()
+
+ def dispatch_request(self):
+ """Subclasses have to override this method to implement the
+ actual view function code. This method is called with all
+ the arguments from the URL rule.
+ """
+ raise NotImplementedError()
+
+ @classmethod
+ def as_view(cls, name, *class_args, **class_kwargs):
+ """Converts the class into an actual view function that can be used
+ with the routing system. Internally this generates a function on the
+ fly which will instantiate the :class:`View` on each request and call
+ the :meth:`dispatch_request` method on it.
+
+ The arguments passed to :meth:`as_view` are forwarded to the
+ constructor of the class.
+ """
+ def view(*args, **kwargs):
+ self = view.view_class(*class_args, **class_kwargs)
+ return self.dispatch_request(*args, **kwargs)
+
+ if cls.decorators:
+ view.__name__ = name
+ view.__module__ = cls.__module__
+ for decorator in cls.decorators:
+ view = decorator(view)
+
+ # We attach the view class to the view function for two reasons:
+ # first of all it allows us to easily figure out what class-based
+ # view this thing came from, secondly it's also used for instantiating
+ # the view class so you can actually replace it with something else
+ # for testing purposes and debugging.
+ view.view_class = cls
+ view.__name__ = name
+ view.__doc__ = cls.__doc__
+ view.__module__ = cls.__module__
+ view.methods = cls.methods
+ view.provide_automatic_options = cls.provide_automatic_options
+ return view
+
+
+class MethodViewType(type):
+ """Metaclass for :class:`MethodView` that determines what methods the view
+ defines.
+ """
+
+ def __init__(cls, name, bases, d):
+ super(MethodViewType, cls).__init__(name, bases, d)
+
+ if 'methods' not in d:
+ methods = set()
+
+ for key in http_method_funcs:
+ if hasattr(cls, key):
+ methods.add(key.upper())
+
+ # If we have no method at all in there we don't want to add a
+ # method list. This is for instance the case for the base class
+ # or another subclass of a base method view that does not introduce
+ # new methods.
+ if methods:
+ cls.methods = methods
+
+
+class MethodView(with_metaclass(MethodViewType, View)):
+ """A class-based view that dispatches request methods to the corresponding
+ class methods. For example, if you implement a ``get`` method, it will be
+ used to handle ``GET`` requests. ::
+
+ class CounterAPI(MethodView):
+ def get(self):
+ return session.get('counter', 0)
+
+ def post(self):
+ session['counter'] = session.get('counter', 0) + 1
+ return 'OK'
+
+ app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
+ """
+
+ def dispatch_request(self, *args, **kwargs):
+ meth = getattr(self, request.method.lower(), None)
+
+ # If the request method is HEAD and we don't have a handler for it
+ # retry with GET.
+ if meth is None and request.method == 'HEAD':
+ meth = getattr(self, 'get', None)
+
+ assert meth is not None, 'Unimplemented method %r' % request.method
+ return meth(*args, **kwargs)
diff --git a/python/flask/wrappers.py b/python/flask/wrappers.py
new file mode 100644
index 0000000..12eff2c
--- /dev/null
+++ b/python/flask/wrappers.py
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.wrappers
+ ~~~~~~~~~~~~~~
+
+ Implements the WSGI wrappers (request and response).
+
+ :copyright: © 2010 by the Pallets team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug.exceptions import BadRequest
+from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase
+
+from flask import json
+from flask.globals import current_app
+
+
+class JSONMixin(object):
+ """Common mixin for both request and response objects to provide JSON
+ parsing capabilities.
+
+ .. versionadded:: 1.0
+ """
+
+ _cached_json = (Ellipsis, Ellipsis)
+
+ @property
+ def is_json(self):
+ """Check if the mimetype indicates JSON data, either
+ :mimetype:`application/json` or :mimetype:`application/*+json`.
+
+ .. versionadded:: 0.11
+ """
+ mt = self.mimetype
+ return (
+ mt == 'application/json'
+ or (mt.startswith('application/')) and mt.endswith('+json')
+ )
+
+ @property
+ def json(self):
+ """This will contain the parsed JSON data if the mimetype indicates
+ JSON (:mimetype:`application/json`, see :meth:`is_json`), otherwise it
+ will be ``None``.
+ """
+ return self.get_json()
+
+ def _get_data_for_json(self, cache):
+ return self.get_data(cache=cache)
+
+ def get_json(self, force=False, silent=False, cache=True):
+ """Parse and return the data as JSON. If the mimetype does not
+ indicate JSON (:mimetype:`application/json`, see
+ :meth:`is_json`), this returns ``None`` unless ``force`` is
+ true. If parsing fails, :meth:`on_json_loading_failed` is called
+ and its return value is used as the return value.
+
+ :param force: Ignore the mimetype and always try to parse JSON.
+ :param silent: Silence parsing errors and return ``None``
+ instead.
+ :param cache: Store the parsed JSON to return for subsequent
+ calls.
+ """
+ if cache and self._cached_json[silent] is not Ellipsis:
+ return self._cached_json[silent]
+
+ if not (force or self.is_json):
+ return None
+
+ data = self._get_data_for_json(cache=cache)
+
+ try:
+ rv = json.loads(data)
+ except ValueError as e:
+ if silent:
+ rv = None
+ if cache:
+ normal_rv, _ = self._cached_json
+ self._cached_json = (normal_rv, rv)
+ else:
+ rv = self.on_json_loading_failed(e)
+ if cache:
+ _, silent_rv = self._cached_json
+ self._cached_json = (rv, silent_rv)
+ else:
+ if cache:
+ self._cached_json = (rv, rv)
+
+ return rv
+
+ def on_json_loading_failed(self, e):
+ """Called if :meth:`get_json` parsing fails and isn't silenced. If
+ this method returns a value, it is used as the return value for
+ :meth:`get_json`. The default implementation raises a
+ :class:`BadRequest` exception.
+
+ .. versionchanged:: 0.10
+ Raise a :exc:`BadRequest` error instead of returning an error
+ message as JSON. If you want that behavior you can add it by
+ subclassing.
+
+ .. versionadded:: 0.8
+ """
+ if current_app is not None and current_app.debug:
+ raise BadRequest('Failed to decode JSON object: {0}'.format(e))
+
+ raise BadRequest()
+
+
+class Request(RequestBase, JSONMixin):
+ """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.
+
+ The request object is a :class:`~werkzeug.wrappers.Request` subclass and
+ provides all of the attributes Werkzeug defines plus a few Flask
+ specific ones.
+ """
+
+ #: The internal URL rule that matched the request. This can be
+ #: useful to inspect which methods are allowed for the URL from
+ #: a before/after handler (``request.url_rule.methods``) etc.
+ #: Though if the request's method was invalid for the URL rule,
+ #: the valid list is available in ``routing_exception.valid_methods``
+ #: instead (an attribute of the Werkzeug exception :exc:`~werkzeug.exceptions.MethodNotAllowed`)
+ #: because the request was never internally bound.
+ #:
+ #: .. versionadded:: 0.6
+ url_rule = None
+
+ #: A dict of view arguments that matched the request. If an exception
+ #: happened when matching, this will be ``None``.
+ view_args = None
+
+ #: If matching the URL failed, this is the exception that will be
+ #: raised / was raised as part of the request handling. This is
+ #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
+ #: something similar.
+ routing_exception = None
+
+ @property
+ def max_content_length(self):
+ """Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""
+ if current_app:
+ return current_app.config['MAX_CONTENT_LENGTH']
+
+ @property
+ def endpoint(self):
+ """The endpoint that matched the request. This in combination with
+ :attr:`view_args` can be used to reconstruct the same or a
+ modified URL. If an exception happened when matching, this will
+ be ``None``.
+ """
+ if self.url_rule is not None:
+ return self.url_rule.endpoint
+
+ @property
+ def blueprint(self):
+ """The name of the current blueprint"""
+ if self.url_rule and '.' in self.url_rule.endpoint:
+ return self.url_rule.endpoint.rsplit('.', 1)[0]
+
+ def _load_form_data(self):
+ RequestBase._load_form_data(self)
+
+ # In debug mode we're replacing the files multidict with an ad-hoc
+ # subclass that raises a different error for key errors.
+ if (
+ current_app
+ and current_app.debug
+ and self.mimetype != 'multipart/form-data'
+ and not self.files
+ ):
+ from .debughelpers import attach_enctype_error_multidict
+ attach_enctype_error_multidict(self)
+
+
+class Response(ResponseBase, JSONMixin):
+ """The response object that is used by default in Flask. Works like the
+ response object from Werkzeug but is set to have an 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.
+
+ .. versionchanged:: 1.0
+ JSON support is added to the response, like the request. This is useful
+ when testing to get the test client response data as JSON.
+
+ .. versionchanged:: 1.0
+
+ Added :attr:`max_cookie_size`.
+ """
+
+ default_mimetype = 'text/html'
+
+ def _get_data_for_json(self, cache):
+ return self.get_data()
+
+ @property
+ def max_cookie_size(self):
+ """Read-only view of the :data:`MAX_COOKIE_SIZE` config key.
+
+ See :attr:`~werkzeug.wrappers.BaseResponse.max_cookie_size` in
+ Werkzeug's docs.
+ """
+ if current_app:
+ return current_app.config['MAX_COOKIE_SIZE']
+
+ # return Werkzeug's default when not in an app context
+ return super(Response, self).max_cookie_size