Merge branch 'master' into figome-multiple-inheritance

This commit is contained in:
David Lord 2017-04-21 10:33:29 -07:00
commit 0d9d3d8f92
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
115 changed files with 1453 additions and 740 deletions

View file

@ -10,7 +10,7 @@
:license: BSD, see LICENSE for more details.
"""
__version__ = '0.11.2-dev'
__version__ = '0.13-dev'
# utilities we import from Werkzeug and Jinja2 that are unused
# in the module but are exported as public interface.
@ -40,7 +40,7 @@ from .signals import signals_available, template_rendered, request_started, \
# it.
from . import json
# This was the only thing that flask used to export at one point and it had
# This was the only thing that Flask used to export at one point and it had
# a more generic name.
jsonify = json.jsonify

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 deque
from werkzeug.datastructures import ImmutableDict
from werkzeug.routing import Map, Rule, RequestRedirect, BuildError
@ -124,6 +123,9 @@ class Flask(_PackageBoundObject):
.. 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
@ -131,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
@ -315,7 +324,7 @@ class Flask(_PackageBoundObject):
'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,
})
@ -338,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,
@ -392,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}
@ -519,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
@ -814,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.
@ -825,25 +839,31 @@ 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)
options.setdefault('use_debugger', self.debug)
options.setdefault('passthrough_errors', True)
try:
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
@ -877,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 ....')
@ -960,7 +980,7 @@ class Flask(_PackageBoundObject):
return iter(self._blueprint_order)
@setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
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.
@ -1000,6 +1020,10 @@ class Flask(_PackageBoundObject):
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
@ -1029,8 +1053,9 @@ class Flask(_PackageBoundObject):
# starting with Flask 0.8 the view_func object can disable and
# force-enable the automatic options handling.
provide_automatic_options = getattr(view_func,
'provide_automatic_options', None)
if provide_automatic_options is None:
provide_automatic_options = getattr(view_func,
'provide_automatic_options', None)
if provide_automatic_options is None:
if 'OPTIONS' not in methods:
@ -1116,7 +1141,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)
@ -1154,7 +1179,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)
@ -1348,7 +1374,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.
@ -1437,24 +1463,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, {})
@ -1556,7 +1571,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`
@ -1624,9 +1639,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):
@ -1973,7 +2009,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):

View file

@ -11,6 +11,7 @@
import os
import sys
import traceback
from threading import Lock, Thread
from functools import update_wrapper
@ -86,7 +87,21 @@ def locate_app(app_id):
module = app_id
app_obj = None
__import__(module)
try:
__import__(module)
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:
stack_trace = traceback.format_exc()
raise NoAppException('There was an error trying to import'
' the app (%s):\n%s' % (module, stack_trace))
else:
raise NoAppException('The file/path provided (%s) does not appear'
' to exist. Please verify the path is '
'correct. If app is not on PYTHONPATH, '
'ensure the extension is .py' % module)
mod = sys.modules[module]
if app_obj is None:
app = find_best_app(mod)
@ -125,9 +140,9 @@ version_option = click.Option(['--version'],
is_flag=True, is_eager=True)
class DispatchingApp(object):
"""Special application that dispatches to a flask application which
"""Special application that dispatches to a Flask application which
is imported by name in a background thread. If an error happens
it is is recorded and shows as part of the WSGI handling which in case
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.
"""
@ -356,7 +371,9 @@ class FlaskGroup(AppGroup):
# 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.
pass
# However, we will not do so silently because that would confuse
# users.
traceback.print_exc()
return sorted(rv)
def main(self, *args, **kwargs):
@ -400,6 +417,13 @@ def run_command(info, host, port, reload, debugger, eager_loading,
"""
from werkzeug.serving import run_simple
# Set a global flag that indicates that we were invoked from the
# command line interface provided server command. 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_SERVER'] = '1'
debug = get_debug_flag()
if reload is None:
reload = bool(debug)
@ -423,8 +447,7 @@ def run_command(info, host, port, reload, debugger, eager_loading,
print(' * Forcing debug mode %s' % (debug and 'on' or 'off'))
run_simple(host, port, app, use_reloader=reload,
use_debugger=debugger, threaded=with_threads,
passthrough_errors=True)
use_debugger=debugger, threaded=with_threads)
@click.command('shell', short_help='Runs a shell in the app context.')
@ -464,7 +487,7 @@ def shell_command():
cli = FlaskGroup(help="""\
This shell command acts as general utility script for Flask applications.
It loads the application configured (either through the FLASK_APP environment
It loads the application configured (through the FLASK_APP environment
variable) and then provides commands either provided by the application or
Flask itself.

View file

@ -126,7 +126,7 @@ class Config(dict):
d = types.ModuleType('config')
d.__file__ = filename
try:
with open(filename) as config_file:
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):

View file

@ -8,6 +8,9 @@
:copyright: (c) 2015 by Armin Ronacher.
: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
@ -153,3 +156,12 @@ def explain_template_loading_attempts(app, template, attempts):
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)

View file

@ -17,6 +17,7 @@ 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
@ -25,8 +26,9 @@ try:
except ImportError:
from urlparse import quote as url_quote
from werkzeug.datastructures import Headers
from werkzeug.exceptions import BadRequest, NotFound
from werkzeug.datastructures import Headers, Range
from werkzeug.exceptions import BadRequest, NotFound, \
RequestedRangeNotSatisfiable
# this was moved in 0.7
try:
@ -39,7 +41,7 @@ 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
from ._compat import string_types, text_type
# sentinel
@ -57,7 +59,7 @@ def get_debug_flag(default=None):
val = os.environ.get('FLASK_DEBUG')
if not val:
return default
return val not in ('0', 'false', 'no')
return val.lower() not in ('0', 'false', 'no')
def _endpoint_from_view_func(view_func):
@ -329,6 +331,7 @@ def url_for(endpoint, **values):
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:
@ -437,7 +440,18 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
to ``True`` to directly emit an ``X-Sendfile`` header. This however
requires support of the underlying webserver for ``X-Sendfile``.
You must explicitly provide the mimetype for the filename or file object.
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.
@ -458,11 +472,20 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
cache_timeout pulls its default from application config, when None.
.. versionchanged:: 0.12
mimetype guessing and etag support removed for file objects.
If no mimetype or attachment_filename is provided, application/octet-stream
will be used.
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`.
:param filename_or_fp: the filename of the file to send in `latin-1`.
.. versionchanged:: 0.12
The `attachment_filename` is preferred over `filename` for MIME-type
detection.
.. versionchanged:: 0.13
UTF-8 filenames, as specified in `RFC 2231`_, are supported.
.. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4
: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
@ -470,8 +493,9 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
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, otherwise
auto detection happens.
: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
@ -488,42 +512,62 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
If a file was passed, this overrides its mtime.
"""
mtime = None
fsize = None
if isinstance(filename_or_fp, string_types):
filename = filename_or_fp
file = None
else:
file = filename_or_fp
filename = getattr(file, 'name', None)
if filename is not None:
if not os.path.isabs(filename):
filename = os.path.join(current_app.root_path, filename)
if mimetype is None and (filename or attachment_filename):
mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
file = None
if attachment_filename is None:
attachment_filename = os.path.basename(filename)
else:
file = filename_or_fp
filename = None
if mimetype is None:
mimetype = 'application/octet-stream'
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:
if filename is None:
raise TypeError('filename unavailable, required for '
'sending as attachment')
attachment_filename = os.path.basename(filename)
headers.add('Content-Disposition', 'attachment',
filename=attachment_filename)
raise TypeError('filename unavailable, required for '
'sending as attachment')
try:
attachment_filename = attachment_filename.encode('latin-1')
except UnicodeEncodeError:
filenames = {
'filename': unicodedata.normalize(
'NFKD', attachment_filename).encode('latin-1', '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
headers['Content-Length'] = os.path.getsize(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)
headers['Content-Length'] = os.path.getsize(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,
@ -541,7 +585,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
rv.cache_control.max_age = cache_timeout
rv.expires = int(time() + cache_timeout)
if add_etags and filename is not None and file is None:
if add_etags and filename is not None:
from warnings import warn
try:
@ -557,12 +601,22 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
warn('Access %s failed, maybe it does not exist, so ignore etags in '
'headers' % filename, stacklevel=2)
if conditional:
if conditional:
if callable(getattr(Range, 'to_content_range_header', None)):
# Werkzeug supports Range Requests
# Remove this test when support for Werkzeug <0.12 is dropped
try:
rv = rv.make_conditional(request, accept_ranges=True,
complete_length=fsize)
except RequestedRangeNotSatisfiable:
file.close()
raise
else:
rv = rv.make_conditional(request)
# 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)
# 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

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""
flask.jsonimpl
~~~~~~~~~~~~~~
flask.json
~~~~~~~~~~
Implementation helpers for the JSON support in Flask.
@ -236,11 +236,10 @@ def jsonify(*args, **kwargs):
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 it was not requested
with ``X-Requested-With: XMLHttpRequest`` to simplify debugging unless
the ``JSONIFY_PRETTYPRINT_REGULAR`` config parameter is set to false.
Compressed (not pretty) formatting currently means no indents and no
spaces after separators.
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
"""
@ -248,7 +247,7 @@ def jsonify(*args, **kwargs):
indent = None
separators = (',', ':')
if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] and not request.is_xhr:
if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] or current_app.debug:
indent = 2
separators = (', ', ': ')

View file

@ -87,4 +87,8 @@ def create_logger(app):
logger.__class__ = DebugLogger
logger.addHandler(debug_handler)
logger.addHandler(prod_handler)
# Disable propagation by default
logger.propagate = False
return logger

View file

@ -84,21 +84,25 @@ class TaggedJSONSerializer(object):
def dumps(self, value):
return json.dumps(_tag(value), separators=(',', ':'))
LOADS_MAP = {
' t': tuple,
' u': uuid.UUID,
' b': b64decode,
' m': Markup,
' d': parse_date,
}
def loads(self, value):
def object_hook(obj):
if len(obj) != 1:
return obj
the_key, the_value = next(iteritems(obj))
if the_key == ' t':
return tuple(the_value)
elif the_key == ' u':
return uuid.UUID(the_value)
elif the_key == ' b':
return b64decode(the_value)
elif the_key == ' m':
return Markup(the_value)
elif the_key == ' d':
return parse_date(the_value)
# Check the key for a corresponding function
return_function = self.LOADS_MAP.get(the_key)
if return_function:
# Pass the value to the function
return return_function(the_value)
# Didn't find a function for this object
return obj
return json.loads(value, object_hook=object_hook)
@ -168,7 +172,7 @@ class SessionInterface(object):
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
#: This can be used by Flask extensions to make a decision in regards
#: to how to deal with the session object.
#:
#: .. versionadded:: 0.10

View file

@ -37,7 +37,7 @@ except ImportError:
temporarily_connected_to = connected_to = _fail
del _fail
# The namespace for code signals. If you are not flask code, do
# The namespace for code signals. If you are not Flask code, do
# not put signals in here. Create your own namespace instead.
_signals = Namespace()

View file

@ -10,6 +10,7 @@
:license: BSD, see LICENSE for more details.
"""
import werkzeug
from contextlib import contextmanager
from werkzeug.test import Client, EnvironBuilder
from flask import _request_ctx_stack
@ -43,11 +44,23 @@ class FlaskClient(Client):
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
@ -101,6 +114,7 @@ class FlaskClient(Client):
def open(self, *args, **kwargs):
kwargs.setdefault('environ_overrides', {}) \
['flask._preserve_context'] = self.preserve_context
kwargs.setdefault('environ_base', self.environ_base)
as_tuple = kwargs.pop('as_tuple', False)
buffered = kwargs.pop('buffered', False)

View file

@ -127,7 +127,7 @@ class MethodViewType(type):
class MethodView(with_metaclass(MethodViewType, View)):
"""Like a regular class-based view but that dispatches requests to
particular methods. For instance if you implement a method called
:meth:`get` it means you will response to ``'GET'`` requests and
:meth:`get` it means it will respond to ``'GET'`` requests and
the :meth:`dispatch_request` implementation will automatically
forward your request to that. Also :attr:`options` is set for you
automatically::

View file

@ -137,7 +137,8 @@ class Request(RequestBase):
on the request.
"""
rv = getattr(self, '_cached_json', _missing)
if rv is not _missing:
# We return cached JSON only when the cache is enabled.
if cache and rv is not _missing:
return rv
if not (force or self.is_json):