Merge branch 'master' into jrmccarthy-master

This commit is contained in:
David Lord 2017-04-21 07:03:46 -07:00
commit 8ad4f476aa
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
159 changed files with 3160 additions and 1954 deletions

View file

@ -14,7 +14,6 @@ from threading import Lock
from datetime import timedelta
from itertools import chain
from functools import update_wrapper
from collections import Mapping, deque
from werkzeug.datastructures import ImmutableDict
from werkzeug.routing import Map, Rule, RequestRedirect, BuildError
@ -22,7 +21,8 @@ from werkzeug.exceptions import HTTPException, InternalServerError, \
MethodNotAllowed, BadRequest, default_exceptions
from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
locked_cached_property, _endpoint_from_view_func, find_package
locked_cached_property, _endpoint_from_view_func, find_package, \
get_debug_flag
from . import json, cli
from .wrappers import Request, Response
from .config import ConfigAttribute, Config
@ -33,7 +33,7 @@ from .templating import DispatchingJinjaLoader, Environment, \
_default_template_ctx_processor
from .signals import request_started, request_finished, got_request_exception, \
request_tearing_down, appcontext_tearing_down
from ._compat import reraise, string_types, text_type, integer_types, iterkeys
from ._compat import reraise, string_types, text_type, integer_types
# a lock used for logger initialization
_logger_lock = Lock()
@ -120,9 +120,12 @@ class Flask(_PackageBoundObject):
The `instance_path` and `instance_relative_config` parameters were
added.
.. versionadded:: 1.0
.. versionadded:: 0.11
The `root_path` parameter was added.
.. versionadded:: 0.13
The `host_matching` and `static_host` parameters were added.
:param import_name: the name of the application package
:param static_url_path: can be used to specify a different path for the
static files on the web. Defaults to the name
@ -130,6 +133,13 @@ class Flask(_PackageBoundObject):
: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.
folder in the root path of the application. Defaults
to None.
:param host_matching: sets the app's ``url_map.host_matching`` to the given
given value. Defaults to False.
: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 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
@ -159,7 +169,7 @@ class Flask(_PackageBoundObject):
#: The class that is used for the Jinja environment.
#:
#: .. versionadded:: 1.0
#: .. versionadded:: 0.11
jinja_environment = Environment
#: The class that is used for the :data:`~flask.g` instance.
@ -168,7 +178,7 @@ class Flask(_PackageBoundObject):
#:
#: 1. Store arbitrary attributes on flask.g.
#: 2. Add a property for lazy per-request database connectors.
#: 3. Return None instead of AttributeError on expected attributes.
#: 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
@ -198,7 +208,7 @@ class Flask(_PackageBoundObject):
#: 1. Default values for certain config options.
#: 2. Access to config values through attributes in addition to keys.
#:
#: .. versionadded:: 1.0
#: .. versionadded:: 0.11
config_class = Config
#: The debug flag. Set this to ``True`` to enable debugging of the
@ -246,6 +256,16 @@ class Flask(_PackageBoundObject):
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.
@ -279,7 +299,7 @@ class Flask(_PackageBoundObject):
#: Default configuration parameters.
default_config = ImmutableDict({
'DEBUG': False,
'DEBUG': get_debug_flag(default=False),
'TESTING': False,
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
@ -297,14 +317,15 @@ class Flask(_PackageBoundObject):
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': 12 * 60 * 60, # 12 hours
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_PRETTYPRINT_REGULAR': False,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
})
@ -326,7 +347,8 @@ class Flask(_PackageBoundObject):
session_interface = SecureCookieSessionInterface()
def __init__(self, import_name, static_path=None, static_url_path=None,
static_folder='static', template_folder='templates',
static_folder='static', static_host=None,
host_matching=False, template_folder='templates',
instance_path=None, instance_relative_config=False,
root_path=None):
_PackageBoundObject.__init__(self, import_name,
@ -380,7 +402,7 @@ class Flask(_PackageBoundObject):
#: is the class for the instance check and the second the error handler
#: function.
#:
#: To register a error handler, use the :meth:`errorhandler`
#: To register an error handler, use the :meth:`errorhandler`
#: decorator.
self.error_handler_spec = {None: self._error_handlers}
@ -411,9 +433,8 @@ class Flask(_PackageBoundObject):
#: 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 open database connections or getting hold of the
#: currently logged in user. To register a function here, use the
#: :meth:`after_request` decorator.
#: 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
@ -471,7 +492,7 @@ class Flask(_PackageBoundObject):
#: A list of shell context processor functions that should be run
#: when a shell context is created.
#:
#: .. versionadded:: 1.0
#: .. versionadded:: 0.11
self.shell_context_processors = []
#: all the attached blueprints in a dictionary by name. Blueprints
@ -508,26 +529,29 @@ class Flask(_PackageBoundObject):
#: def to_python(self, value):
#: return value.split(',')
#: def to_url(self, values):
#: return ','.join(BaseConverter.to_url(value)
#: 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
# tracks internally if the application already handled at least one
# request.
self._got_first_request = False
self._before_request_lock = Lock()
# register the static folder for the application. Do that even
# if the folder does not exist. First of all it might be created
# while the server is running (usually happens during development)
# but also because google appengine stores static files somewhere
# else when mapped with the .yml file.
# 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',
endpoint='static', host=static_host,
view_func=self.send_static_file)
#: The click command line context for this application. Commands
@ -536,7 +560,7 @@ class Flask(_PackageBoundObject):
#: provided by Flask itself and can be overridden.
#:
#: This is an instance of a :class:`click.Group` object.
self.cli = cli.AppGroup(self)
self.cli = cli.AppGroup(self.name)
def _get_error_handlers(self):
from warnings import warn
@ -673,7 +697,7 @@ class Flask(_PackageBoundObject):
this function to customize the behavior.
.. versionadded:: 0.5
.. versionchanged:: 1.0
.. versionchanged:: 0.11
``Environment.auto_reload`` set in accordance with
``TEMPLATES_AUTO_RELOAD`` configuration option.
"""
@ -762,7 +786,7 @@ class Flask(_PackageBoundObject):
application. This runs all the registered shell context
processors.
.. versionadded:: 1.0
.. versionadded:: 0.11
"""
rv = {'app': self, 'g': g}
for processor in self.shell_context_processors:
@ -770,8 +794,13 @@ class Flask(_PackageBoundObject):
return rv
def run(self, host=None, port=None, debug=None, **options):
"""Runs the application on a local development server. If the
:attr:`debug` flag is set the server will automatically reload
"""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
@ -781,8 +810,7 @@ class Flask(_PackageBoundObject):
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 ``runserver``
support.
be using the :command:`flask` command line script's ``run`` support.
.. admonition:: Keep in Mind
@ -799,7 +827,8 @@ class Flask(_PackageBoundObject):
: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'``.
``'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.
@ -810,15 +839,22 @@ class Flask(_PackageBoundObject):
:func:`werkzeug.serving.run_simple` for more
information.
"""
# 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_SERVER') == '1':
from .debughelpers import explain_ignored_app_run
explain_ignored_app_run()
return
from werkzeug.serving import run_simple
if host is None:
host = '127.0.0.1'
if port is None:
server_name = self.config['SERVER_NAME']
if server_name and ':' in server_name:
port = int(server_name.rsplit(':', 1)[1])
else:
port = 5000
_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)
if debug is not None:
self.debug = bool(debug)
options.setdefault('use_reloader', self.debug)
@ -827,7 +863,7 @@ class Flask(_PackageBoundObject):
run_simple(host, port, self, **options)
finally:
# reset the first request information if the development server
# resetted normally. This makes it possible to restart the 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
@ -861,9 +897,9 @@ class Flask(_PackageBoundObject):
from flask.testing import FlaskClient
class CustomClient(FlaskClient):
def __init__(self, authentication=None, *args, **kwargs):
FlaskClient.__init__(*args, **kwargs)
self._authentication = authentication
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 ....')
@ -878,7 +914,7 @@ class Flask(_PackageBoundObject):
to override the client to be used by setting the
:attr:`test_client_class` attribute.
.. versionchanged:: 1.0
.. versionchanged:: 0.11
Added `**kwargs` to support passing additional keyword arguments to
the constructor of :attr:`test_client_class`.
"""
@ -939,7 +975,7 @@ class Flask(_PackageBoundObject):
def iter_blueprints(self):
"""Iterates over all blueprints by the order they were registered.
.. versionadded:: 1.0
.. versionadded:: 0.11
"""
return iter(self._blueprint_order)
@ -1106,7 +1142,7 @@ class Flask(_PackageBoundObject):
@setupmethod
def errorhandler(self, code_or_exception):
"""A decorator that is used to register a function give a given
"""A decorator that is used to register a function given an
error code. Example::
@app.errorhandler(404)
@ -1144,7 +1180,8 @@ class Flask(_PackageBoundObject):
that do not necessarily have to be a subclass of the
:class:`~werkzeug.exceptions.HTTPException` class.
:param code: the code as integer for the handler
: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)
@ -1338,7 +1375,7 @@ class Flask(_PackageBoundObject):
will have to surround the execution of these code by try/except
statements and log occurring errors.
When a teardown function was called because of a exception it will
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.
@ -1394,7 +1431,7 @@ class Flask(_PackageBoundObject):
def shell_context_processor(self, f):
"""Registers a shell context processor function.
.. versionadded:: 1.0
.. versionadded:: 0.11
"""
self.shell_context_processors.append(f)
return f
@ -1427,24 +1464,13 @@ class Flask(_PackageBoundObject):
def find_handler(handler_map):
if not handler_map:
return
queue = deque(exc_class.__mro__)
# Protect from geniuses who might create circular references in
# __mro__
done = set()
while queue:
cls = queue.popleft()
if cls in done:
continue
done.add(cls)
for cls in exc_class.__mro__:
handler = handler_map.get(cls)
if handler is not None:
# cache for next time exc_class is raised
handler_map[exc_class] = handler
return handler
queue.extend(cls.__mro__)
# try blueprint handlers
handler = find_handler(self.error_handler_spec
.get(request.blueprint, {})
@ -1546,7 +1572,7 @@ class Flask(_PackageBoundObject):
self.log_exception((exc_type, exc_value, tb))
if handler is None:
return InternalServerError()
return handler(e)
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`
@ -1614,9 +1640,30 @@ class Flask(_PackageBoundObject):
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)
response = self.process_response(response)
request_finished.send(self, response=response)
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):
@ -1794,7 +1841,7 @@ class Flask(_PackageBoundObject):
if it was the return value from the view and further
request handling is stopped.
This also triggers the :meth:`url_value_processor` functions before
This also triggers the :meth:`url_value_preprocessor` functions before
the actual :meth:`before_request` functions are called.
"""
bp = _request_ctx_stack.top.request.blueprint
@ -1963,7 +2010,10 @@ class Flask(_PackageBoundObject):
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.make_response(self.handle_exception(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):