Flask update
This commit is contained in:
+11
-4
@@ -10,25 +10,32 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
__version__ = '0.8-dev'
|
||||
|
||||
# utilities we import from Werkzeug and Jinja2 that are unused
|
||||
# in the module but are exported as public interface.
|
||||
from werkzeug import abort, redirect
|
||||
from 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, jsonify, json_available, flash, \
|
||||
send_file, send_from_directory, get_flashed_messages, \
|
||||
get_template_attribute, make_response
|
||||
get_template_attribute, make_response, safe_join
|
||||
from .globals import current_app, g, request, session, _request_ctx_stack
|
||||
from .ctx import has_request_context
|
||||
from .module import Module
|
||||
from .blueprints import Blueprint
|
||||
from .templating import render_template, render_template_string
|
||||
from .session import Session
|
||||
|
||||
# the signals
|
||||
from .signals import signals_available, template_rendered, request_started, \
|
||||
request_finished, got_request_exception
|
||||
request_finished, got_request_exception, request_tearing_down
|
||||
|
||||
# only import json if it's available
|
||||
if json_available:
|
||||
from .helpers import json
|
||||
|
||||
# backwards compat, goes away in 1.0
|
||||
from .sessions import SecureCookieSession as Session
|
||||
|
||||
+726
-155
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,318 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.blueprints
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Blueprints are the recommended way to implement larger or more
|
||||
pluggable applications in Flask 0.7 and later.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
: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 blurprint 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:
|
||||
rule = self.url_prefix + rule
|
||||
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.blueprint.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
|
||||
|
||||
def __init__(self, name, import_name, static_folder=None,
|
||||
static_url_path=None, template_folder=None,
|
||||
url_prefix=None, subdomain=None, url_defaults=None):
|
||||
_PackageBoundObject.__init__(self, import_name, template_folder)
|
||||
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 = []
|
||||
self.view_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 a blueprint
|
||||
on the application. This can be overridden to customize the register
|
||||
behavior. Keyword arguments from
|
||||
:func:`~flask.Flask.register_blueprint` are directly forwarded to this
|
||||
method in the `options` dictionary.
|
||||
"""
|
||||
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):
|
||||
self.add_url_rule(rule, f.__name__, 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.
|
||||
"""
|
||||
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 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_preprocessor
|
||||
.setdefault(self.name, []).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
|
||||
+15
-7
@@ -13,9 +13,9 @@ from __future__ import with_statement
|
||||
|
||||
import imp
|
||||
import os
|
||||
import sys
|
||||
import errno
|
||||
|
||||
from werkzeug import import_string
|
||||
from werkzeug.utils import import_string
|
||||
|
||||
|
||||
class ConfigAttribute(object):
|
||||
@@ -83,13 +83,13 @@ class Config(dict):
|
||||
|
||||
def from_envvar(self, variable_name, silent=False):
|
||||
"""Loads a configuration from an environment variable pointing to
|
||||
a configuration file. This basically is just a shortcut with nicer
|
||||
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 failing for missing
|
||||
:param silent: set to `True` if you want silent failure for missing
|
||||
files.
|
||||
:return: bool. `True` if able to load config, `False` otherwise.
|
||||
"""
|
||||
@@ -105,7 +105,7 @@ class Config(dict):
|
||||
self.from_pyfile(rv)
|
||||
return True
|
||||
|
||||
def from_pyfile(self, filename):
|
||||
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.
|
||||
@@ -113,6 +113,11 @@ class Config(dict):
|
||||
: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 = imp.new_module('config')
|
||||
@@ -120,9 +125,12 @@ class Config(dict):
|
||||
try:
|
||||
execfile(filename, d.__dict__)
|
||||
except IOError, e:
|
||||
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
|
||||
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
|
||||
@@ -133,8 +141,8 @@ class Config(dict):
|
||||
|
||||
Objects are usually either modules or classes.
|
||||
|
||||
Just the uppercase variables in that object are stored in the config
|
||||
after lowercasing. Example usage::
|
||||
Just the uppercase variables in that object are stored in the config.
|
||||
Example usage::
|
||||
|
||||
app.config.from_object('yourapplication.default_config')
|
||||
from yourapplication import default_config
|
||||
|
||||
+88
-8
@@ -12,30 +12,99 @@
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
from .globals import _request_ctx_stack
|
||||
from .session import _NullSession
|
||||
from .module import blueprint_is_module
|
||||
|
||||
|
||||
class _RequestGlobals(object):
|
||||
pass
|
||||
|
||||
|
||||
class _RequestContext(object):
|
||||
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 if you want to take advantage
|
||||
of request information is it's available but fail silently if the request
|
||||
object 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
|
||||
|
||||
|
||||
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):
|
||||
self.app = app
|
||||
self.request = app.request_class(environ)
|
||||
self.url_adapter = app.create_url_adapter(self.request)
|
||||
self.session = app.open_session(self.request)
|
||||
if self.session is None:
|
||||
self.session = _NullSession()
|
||||
self.g = _RequestGlobals()
|
||||
self.flashes = None
|
||||
self.session = None
|
||||
|
||||
self.match_request()
|
||||
|
||||
# Support for deprecated functionality. This is doing away with
|
||||
# Flask 1.0
|
||||
blueprint = self.request.blueprint
|
||||
if blueprint is not None:
|
||||
# better safe than sorry, we don't want to break code that
|
||||
# already worked
|
||||
bp = app.blueprints.get(blueprint)
|
||||
if bp is not None and blueprint_is_module(bp):
|
||||
self.request._is_old_module = True
|
||||
|
||||
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)
|
||||
@@ -44,11 +113,22 @@ class _RequestContext(object):
|
||||
self.request.routing_exception = e
|
||||
|
||||
def push(self):
|
||||
"""Binds the request context."""
|
||||
"""Binds the request context to the current context."""
|
||||
_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 (e.g. flask-sqlalchemy).
|
||||
self.session = self.app.open_session(self.request)
|
||||
if self.session is None:
|
||||
self.session = self.app.make_null_session()
|
||||
|
||||
def pop(self):
|
||||
"""Pops the request context."""
|
||||
"""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.
|
||||
"""
|
||||
self.app.do_teardown_request()
|
||||
_request_ctx_stack.pop()
|
||||
|
||||
def __enter__(self):
|
||||
@@ -62,5 +142,5 @@ class _RequestContext(object):
|
||||
# the context can be force kept alive for the test client.
|
||||
# See flask.testing for how this works.
|
||||
if not self.request.environ.get('flask._preserve_context') and \
|
||||
(tb is None or not self.app.debug):
|
||||
(tb is None or not self.app.preserve_context_on_exception):
|
||||
self.pop()
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.debughelpers
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Various helpers to make the development experience better.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
||||
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).encode('utf-8')
|
||||
|
||||
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.' % 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, e:
|
||||
if key not in request.form:
|
||||
raise
|
||||
raise DebugFilesKeyError(request, key)
|
||||
newcls.__name__ = oldcls.__name__
|
||||
newcls.__module__ = oldcls.__module__
|
||||
request.files.__class__ = newcls
|
||||
@@ -11,7 +11,7 @@
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
from werkzeug import LocalStack, LocalProxy
|
||||
from werkzeug.local import LocalStack, LocalProxy
|
||||
|
||||
def _lookup_object(name):
|
||||
top = _request_ctx_stack.top
|
||||
|
||||
+187
-44
@@ -9,12 +9,15 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import os
|
||||
import sys
|
||||
import posixpath
|
||||
import mimetypes
|
||||
from time import time
|
||||
from zlib import adler32
|
||||
from threading import RLock
|
||||
|
||||
# try to load the best simplejson implementation available. If JSON
|
||||
# is not installed, we add a failing class.
|
||||
@@ -33,9 +36,15 @@ except ImportError:
|
||||
json_available = False
|
||||
|
||||
|
||||
from werkzeug import Headers, wrap_file, cached_property
|
||||
from werkzeug.datastructures import Headers
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
# this was moved in 0.7
|
||||
try:
|
||||
from werkzeug.wsgi import wrap_file
|
||||
except ImportError:
|
||||
from werkzeug.utils import wrap_file
|
||||
|
||||
from jinja2 import FileSystemLoader
|
||||
|
||||
from .globals import session, _request_ctx_stack, current_app, request
|
||||
@@ -58,6 +67,10 @@ else:
|
||||
_tojson_filter = json.dumps
|
||||
|
||||
|
||||
# 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.
|
||||
@@ -151,22 +164,16 @@ def make_response(*args):
|
||||
|
||||
def url_for(endpoint, **values):
|
||||
"""Generates a URL to the given endpoint with the method provided.
|
||||
The endpoint is relative to the active module if modules are in use.
|
||||
|
||||
Here are some examples:
|
||||
|
||||
==================== ======================= =============================
|
||||
Active Module Target Endpoint Target Function
|
||||
==================== ======================= =============================
|
||||
`None` ``'index'`` `index` of the application
|
||||
`None` ``'.index'`` `index` of the application
|
||||
``'admin'`` ``'index'`` `index` of the `admin` module
|
||||
any ``'.index'`` `index` of the application
|
||||
any ``'admin.index'`` `index` of the `admin` module
|
||||
==================== ======================= =============================
|
||||
|
||||
Variable arguments that are unknown to the target endpoint are appended
|
||||
to the generated URL as query arguments.
|
||||
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>`.
|
||||
|
||||
@@ -175,13 +182,22 @@ def url_for(endpoint, **values):
|
||||
:param _external: if set to `True`, an absolute URL is generated.
|
||||
"""
|
||||
ctx = _request_ctx_stack.top
|
||||
if '.' not in endpoint:
|
||||
mod = ctx.request.module
|
||||
if mod is not None:
|
||||
endpoint = mod + '.' + endpoint
|
||||
elif endpoint.startswith('.'):
|
||||
endpoint = endpoint[1:]
|
||||
blueprint_name = request.blueprint
|
||||
if not ctx.request._is_old_module:
|
||||
if endpoint[:1] == '.':
|
||||
if blueprint_name is not None:
|
||||
endpoint = blueprint_name + endpoint
|
||||
else:
|
||||
endpoint = endpoint[1:]
|
||||
else:
|
||||
# TODO: get rid of this deprecated functionality in 1.0
|
||||
if '.' not in endpoint:
|
||||
if blueprint_name is not None:
|
||||
endpoint = blueprint_name + '.' + endpoint
|
||||
elif endpoint.startswith('.'):
|
||||
endpoint = endpoint[1:]
|
||||
external = values.pop('_external', False)
|
||||
ctx.app.inject_url_defaults(endpoint, values)
|
||||
return ctx.url_adapter.build(endpoint, values, force_external=external)
|
||||
|
||||
|
||||
@@ -248,7 +264,8 @@ def get_flashed_messages(with_categories=False):
|
||||
"""
|
||||
flashes = _request_ctx_stack.top.flashes
|
||||
if flashes is None:
|
||||
_request_ctx_stack.top.flashes = flashes = session.pop('_flashes', [])
|
||||
_request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \
|
||||
if '_flashes' in session else []
|
||||
if not with_categories:
|
||||
return [x[1] for x in flashes]
|
||||
return flashes
|
||||
@@ -321,7 +338,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
||||
if not attachment_filename and not mimetype \
|
||||
and isinstance(filename, basestring):
|
||||
warn(DeprecationWarning('The filename support for file objects '
|
||||
'passed to send_file is not deprecated. Pass an '
|
||||
'passed to send_file is now deprecated. Pass an '
|
||||
'attach_filename if you want mimetypes to be guessed.'),
|
||||
stacklevel=2)
|
||||
if add_etags:
|
||||
@@ -377,7 +394,10 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
||||
rv.set_etag('flask-%s-%s-%s' % (
|
||||
os.path.getmtime(filename),
|
||||
os.path.getsize(filename),
|
||||
adler32(filename) & 0xffffffff
|
||||
adler32(
|
||||
filename.encode('utf8') if isinstance(filename, unicode)
|
||||
else filename
|
||||
) & 0xffffffff
|
||||
))
|
||||
if conditional:
|
||||
rv = rv.make_conditional(request)
|
||||
@@ -388,6 +408,31 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
||||
return rv
|
||||
|
||||
|
||||
def safe_join(directory, filename):
|
||||
"""Safely join `directory` and `filename`.
|
||||
|
||||
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 base directory.
|
||||
:param filename: the untrusted filename relative to that directory.
|
||||
:raises: :class:`~werkzeug.exceptions.NotFound` if the resulting path
|
||||
would fall out of `directory`.
|
||||
"""
|
||||
filename = posixpath.normpath(filename)
|
||||
for sep in _os_alt_seps:
|
||||
if sep in filename:
|
||||
raise NotFound()
|
||||
if os.path.isabs(filename) or filename.startswith('../'):
|
||||
raise NotFound()
|
||||
return os.path.join(directory, filename)
|
||||
|
||||
|
||||
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
|
||||
@@ -415,35 +460,128 @@ def send_from_directory(directory, filename, **options):
|
||||
:param options: optional keyword arguments that are directly
|
||||
forwarded to :func:`send_file`.
|
||||
"""
|
||||
filename = posixpath.normpath(filename)
|
||||
for sep in _os_alt_seps:
|
||||
if sep in filename:
|
||||
raise NotFound()
|
||||
if os.path.isabs(filename) or filename.startswith('../'):
|
||||
raise NotFound()
|
||||
filename = os.path.join(directory, filename)
|
||||
filename = safe_join(directory, filename)
|
||||
if not os.path.isfile(filename):
|
||||
raise NotFound()
|
||||
return send_file(filename, conditional=True, **options)
|
||||
|
||||
|
||||
def _get_package_path(name):
|
||||
"""Returns the path to a package or cwd if that cannot be found."""
|
||||
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`.
|
||||
"""
|
||||
__import__(import_name)
|
||||
try:
|
||||
return os.path.abspath(os.path.dirname(sys.modules[name].__file__))
|
||||
except (KeyError, AttributeError):
|
||||
directory = os.path.dirname(sys.modules[import_name].__file__)
|
||||
return os.path.abspath(directory)
|
||||
except AttributeError:
|
||||
return os.getcwd()
|
||||
|
||||
|
||||
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.).
|
||||
"""
|
||||
__import__(import_name)
|
||||
root_mod = sys.modules[import_name.split('.')[0]]
|
||||
package_path = getattr(root_mod, '__file__', None)
|
||||
if package_path is None:
|
||||
package_path = os.getcwd()
|
||||
else:
|
||||
package_path = os.path.abspath(os.path.dirname(package_path))
|
||||
if hasattr(root_mod, '__path__'):
|
||||
package_path = os.path.dirname(package_path)
|
||||
|
||||
# leave the egg wrapper folder or the actual .egg on the filesystem
|
||||
test_package_path = package_path
|
||||
if os.path.basename(test_package_path).endswith('.egg'):
|
||||
test_package_path = os.path.dirname(test_package_path)
|
||||
|
||||
site_parent, site_folder = os.path.split(test_package_path)
|
||||
py_prefix = os.path.abspath(sys.prefix)
|
||||
if test_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):
|
||||
|
||||
def __init__(self, import_name):
|
||||
def __init__(self, import_name, template_folder=None):
|
||||
#: The name of the package or module. Do not change this once
|
||||
#: it was set by the constructor.
|
||||
self.import_name = import_name
|
||||
|
||||
#: location of the templates. `None` if templates should not be
|
||||
#: exposed.
|
||||
self.template_folder = template_folder
|
||||
|
||||
#: Where is the app root located?
|
||||
self.root_path = _get_package_path(self.import_name)
|
||||
self.root_path = get_root_path(self.import_name)
|
||||
|
||||
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)
|
||||
del _get_static_folder, _set_static_folder
|
||||
|
||||
def _get_static_url_path(self):
|
||||
if self._static_url_path is None:
|
||||
if self.static_folder is None:
|
||||
return None
|
||||
return '/' + os.path.basename(self.static_folder)
|
||||
return self._static_url_path
|
||||
def _set_static_url_path(self, value):
|
||||
self._static_url_path = value
|
||||
static_url_path = property(_get_static_url_path, _set_static_url_path)
|
||||
del _get_static_url_path, _set_static_url_path
|
||||
|
||||
@property
|
||||
def has_static_folder(self):
|
||||
@@ -452,15 +590,17 @@ class _PackageBoundObject(object):
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
return os.path.isdir(os.path.join(self.root_path, 'static'))
|
||||
return self.static_folder is not None
|
||||
|
||||
@cached_property
|
||||
@locked_cached_property
|
||||
def jinja_loader(self):
|
||||
"""The Jinja loader for this package bound object.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
return FileSystemLoader(os.path.join(self.root_path, 'templates'))
|
||||
if self.template_folder is not None:
|
||||
return FileSystemLoader(os.path.join(self.root_path,
|
||||
self.template_folder))
|
||||
|
||||
def send_static_file(self, filename):
|
||||
"""Function used internally to send static files from the static
|
||||
@@ -468,10 +608,11 @@ class _PackageBoundObject(object):
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
return send_from_directory(os.path.join(self.root_path, 'static'),
|
||||
filename)
|
||||
if not self.has_static_folder:
|
||||
raise RuntimeError('No static folder for this object')
|
||||
return send_from_directory(self.static_folder, filename)
|
||||
|
||||
def open_resource(self, resource):
|
||||
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::
|
||||
|
||||
@@ -493,4 +634,6 @@ class _PackageBoundObject(object):
|
||||
:param resource: the name of the resource. To access resources within
|
||||
subfolders use forward slashes as separator.
|
||||
"""
|
||||
return open(os.path.join(self.root_path, resource), '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)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from logging import getLogger, StreamHandler, Formatter, Logger, DEBUG
|
||||
from logging import getLogger, StreamHandler, Formatter, getLoggerClass, DEBUG
|
||||
|
||||
|
||||
def create_logger(app):
|
||||
@@ -21,6 +21,7 @@ def create_logger(app):
|
||||
function also removes all attached handlers in case there was a
|
||||
logger with the log name before.
|
||||
"""
|
||||
Logger = getLoggerClass()
|
||||
|
||||
class DebugLogger(Logger):
|
||||
def getEffectiveLevel(x):
|
||||
|
||||
+17
-205
@@ -9,109 +9,24 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from .helpers import _PackageBoundObject, _endpoint_from_view_func
|
||||
import os
|
||||
|
||||
from .blueprints import Blueprint
|
||||
|
||||
|
||||
def _register_module(module, static_path):
|
||||
"""Internal helper function that returns a function for recording
|
||||
that registers the `send_static_file` function for the module on
|
||||
the application if necessary. It also registers the module on
|
||||
the application.
|
||||
"""
|
||||
def _register(state):
|
||||
state.app.modules[module.name] = module
|
||||
# do not register the rule if the static folder of the
|
||||
# module is the same as the one from the application.
|
||||
if state.app.root_path == module.root_path:
|
||||
return
|
||||
path = static_path
|
||||
if path is None:
|
||||
path = state.app.static_path
|
||||
if state.url_prefix:
|
||||
path = state.url_prefix + path
|
||||
state.app.add_url_rule(path + '/<path:filename>',
|
||||
endpoint='%s.static' % module.name,
|
||||
view_func=module.send_static_file,
|
||||
subdomain=state.subdomain)
|
||||
return _register
|
||||
def blueprint_is_module(bp):
|
||||
"""Used to figure out if something is actually a module"""
|
||||
return isinstance(bp, Module)
|
||||
|
||||
|
||||
class _ModuleSetupState(object):
|
||||
class Module(Blueprint):
|
||||
"""Deprecated module support. Until Flask 0.6 modules were a different
|
||||
name of the concept now available as blueprints in Flask. They are
|
||||
essentially doing the same but have some bad semantics for templates and
|
||||
static files that were fixed with blueprints.
|
||||
|
||||
def __init__(self, app, url_prefix=None, subdomain=None):
|
||||
self.app = app
|
||||
self.url_prefix = url_prefix
|
||||
self.subdomain = subdomain
|
||||
|
||||
|
||||
class Module(_PackageBoundObject):
|
||||
"""Container object that enables pluggable applications. A module can
|
||||
be used to organize larger applications. They represent blueprints that,
|
||||
in combination with a :class:`Flask` object are used to create a large
|
||||
application.
|
||||
|
||||
A module is like an application bound to an `import_name`. Multiple
|
||||
modules can share the same import names, but in that case a `name` has
|
||||
to be provided to keep them apart. If different import names are used,
|
||||
the rightmost part of the import name is used as name.
|
||||
|
||||
Here's an example structure for a larger application::
|
||||
|
||||
/myapplication
|
||||
/__init__.py
|
||||
/views
|
||||
/__init__.py
|
||||
/admin.py
|
||||
/frontend.py
|
||||
|
||||
The `myapplication/__init__.py` can look like this::
|
||||
|
||||
from flask import Flask
|
||||
from myapplication.views.admin import admin
|
||||
from myapplication.views.frontend import frontend
|
||||
|
||||
app = Flask(__name__)
|
||||
app.register_module(admin, url_prefix='/admin')
|
||||
app.register_module(frontend)
|
||||
|
||||
And here's an example view module (`myapplication/views/admin.py`)::
|
||||
|
||||
from flask import Module
|
||||
|
||||
admin = Module(__name__)
|
||||
|
||||
@admin.route('/')
|
||||
def index():
|
||||
pass
|
||||
|
||||
@admin.route('/login')
|
||||
def login():
|
||||
pass
|
||||
|
||||
For a gentle introduction into modules, checkout the
|
||||
:ref:`working-with-modules` section.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
The `static_path` parameter was added and it's now possible for
|
||||
modules to refer to their own templates and static files. See
|
||||
:ref:`modules-and-resources` for more information.
|
||||
|
||||
.. versionadded:: 0.6
|
||||
The `subdomain` parameter was added.
|
||||
|
||||
:param import_name: the name of the Python package or module
|
||||
implementing this :class:`Module`.
|
||||
:param name: the internal short name for the module. Unless specified
|
||||
the rightmost part of the import name
|
||||
:param url_prefix: an optional string that is used to prefix all the
|
||||
URL rules of this module. This can also be specified
|
||||
when registering the module with the application.
|
||||
:param subdomain: used to set the subdomain setting for URL rules that
|
||||
do not have a subdomain setting set.
|
||||
:param static_path: can be used to specify a different path for the
|
||||
static files on the web. Defaults to ``/static``.
|
||||
This does not affect the folder the files are served
|
||||
*from*.
|
||||
.. versionchanged:: 0.7
|
||||
Modules were deprecated in favor for blueprints.
|
||||
"""
|
||||
|
||||
def __init__(self, import_name, name=None, url_prefix=None,
|
||||
@@ -120,111 +35,8 @@ class Module(_PackageBoundObject):
|
||||
assert '.' in import_name, 'name required if package name ' \
|
||||
'does not point to a submodule'
|
||||
name = import_name.rsplit('.', 1)[1]
|
||||
_PackageBoundObject.__init__(self, import_name)
|
||||
self.name = name
|
||||
self.url_prefix = url_prefix
|
||||
self.subdomain = subdomain
|
||||
self.view_functions = {}
|
||||
self._register_events = [_register_module(self, static_path)]
|
||||
Blueprint.__init__(self, name, import_name, url_prefix=url_prefix,
|
||||
subdomain=subdomain, template_folder='templates')
|
||||
|
||||
def route(self, rule, **options):
|
||||
"""Like :meth:`Flask.route` but for a module. The endpoint for the
|
||||
:func:`url_for` function is prefixed with the name of the module.
|
||||
"""
|
||||
def decorator(f):
|
||||
self.add_url_rule(rule, f.__name__, f, **options)
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
||||
"""Like :meth:`Flask.add_url_rule` but for a module. The endpoint for
|
||||
the :func:`url_for` function is prefixed with the name of the module.
|
||||
|
||||
.. versionchanged:: 0.6
|
||||
The `endpoint` argument is now optional and will default to the
|
||||
function name to consistent with the function of the same name
|
||||
on the application object.
|
||||
"""
|
||||
def register_rule(state):
|
||||
the_rule = rule
|
||||
if state.url_prefix:
|
||||
the_rule = state.url_prefix + rule
|
||||
options.setdefault('subdomain', state.subdomain)
|
||||
the_endpoint = endpoint
|
||||
if the_endpoint is None:
|
||||
the_endpoint = _endpoint_from_view_func(view_func)
|
||||
state.app.add_url_rule(the_rule, '%s.%s' % (self.name,
|
||||
the_endpoint),
|
||||
view_func, **options)
|
||||
self._record(register_rule)
|
||||
|
||||
def endpoint(self, endpoint):
|
||||
"""Like :meth:`Flask.endpoint` but for a module."""
|
||||
def decorator(f):
|
||||
self.view_functions[endpoint] = f
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def before_request(self, f):
|
||||
"""Like :meth:`Flask.before_request` but for a module. This function
|
||||
is only executed before each request that is handled by a function of
|
||||
that module.
|
||||
"""
|
||||
self._record(lambda s: s.app.before_request_funcs
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def before_app_request(self, f):
|
||||
"""Like :meth:`Flask.before_request`. Such a function is executed
|
||||
before each request, even if outside of a module.
|
||||
"""
|
||||
self._record(lambda s: s.app.before_request_funcs
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
def after_request(self, f):
|
||||
"""Like :meth:`Flask.after_request` but for a module. This function
|
||||
is only executed after each request that is handled by a function of
|
||||
that module.
|
||||
"""
|
||||
self._record(lambda s: s.app.after_request_funcs
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def after_app_request(self, f):
|
||||
"""Like :meth:`Flask.after_request` but for a module. Such a function
|
||||
is executed after each request, even if outside of the module.
|
||||
"""
|
||||
self._record(lambda s: s.app.after_request_funcs
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
def context_processor(self, f):
|
||||
"""Like :meth:`Flask.context_processor` but for a module. This
|
||||
function is only executed for requests handled by a module.
|
||||
"""
|
||||
self._record(lambda s: s.app.template_context_processors
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def app_context_processor(self, f):
|
||||
"""Like :meth:`Flask.context_processor` but for a module. Such a
|
||||
function is executed each request, even if outside of the module.
|
||||
"""
|
||||
self._record(lambda s: s.app.template_context_processors
|
||||
.setdefault(None, []).append(f))
|
||||
return f
|
||||
|
||||
def app_errorhandler(self, code):
|
||||
"""Like :meth:`Flask.errorhandler` but for a module. This
|
||||
handler is used for all requests, even if outside of the module.
|
||||
|
||||
.. versionadded:: 0.4
|
||||
"""
|
||||
def decorator(f):
|
||||
self._record(lambda s: s.app.errorhandler(code)(f))
|
||||
return f
|
||||
return decorator
|
||||
|
||||
def _record(self, func):
|
||||
self._register_events.append(func)
|
||||
if os.path.isdir(os.path.join(self.root_path, 'static')):
|
||||
self._static_folder = 'static'
|
||||
|
||||
+7
-31
@@ -3,41 +3,17 @@
|
||||
flask.session
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Implements cookie based sessions based on Werkzeug's secure cookie
|
||||
system.
|
||||
This module used to flask with the session global so we moved it
|
||||
over to flask.sessions
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from werkzeug.contrib.securecookie import SecureCookie
|
||||
from warnings import warn
|
||||
warn(DeprecationWarning('please use flask.sessions instead'))
|
||||
|
||||
from .sessions import *
|
||||
|
||||
class Session(SecureCookie):
|
||||
"""Expands the session with support for switching between permanent
|
||||
and non-permanent sessions.
|
||||
"""
|
||||
|
||||
def _get_permanent(self):
|
||||
return self.get('_permanent', False)
|
||||
|
||||
def _set_permanent(self, value):
|
||||
self['_permanent'] = bool(value)
|
||||
|
||||
permanent = property(_get_permanent, _set_permanent)
|
||||
del _get_permanent, _set_permanent
|
||||
|
||||
|
||||
class _NullSession(Session):
|
||||
"""Class used to generate nicer error messages if sessions are not
|
||||
available. Will still allow read-only access to the empty session
|
||||
but fail on setting.
|
||||
"""
|
||||
|
||||
def _fail(self, *args, **kwargs):
|
||||
raise RuntimeError('the session is unavailable because no secret '
|
||||
'key was set. Set the secret_key on the '
|
||||
'application to something unique and secret.')
|
||||
__setitem__ = __delitem__ = clear = pop = popitem = \
|
||||
update = setdefault = _fail
|
||||
del _fail
|
||||
Session = SecureCookieSession
|
||||
_NullSession = NullSession
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.sessions
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Implements cookie based sessions based on Werkzeug's secure cookie
|
||||
system.
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from werkzeug.contrib.securecookie import SecureCookie
|
||||
|
||||
|
||||
class SessionMixin(object):
|
||||
"""Expands a basic dictionary with an accessors that are expected
|
||||
by Flask extensions and users for the session.
|
||||
"""
|
||||
|
||||
def _get_permanent(self):
|
||||
return self.get('_permanent', False)
|
||||
|
||||
def _set_permanent(self, value):
|
||||
self['_permanent'] = bool(value)
|
||||
|
||||
#: this reflects the ``'_permanent'`` key in the dict.
|
||||
permanent = property(_get_permanent, _set_permanent)
|
||||
del _get_permanent, _set_permanent
|
||||
|
||||
#: some session backends can tell you if a session is new, but that is
|
||||
#: not necessarily guaranteed. Use with caution. The default mixin
|
||||
#: implementation just hardcodes `False` in.
|
||||
new = False
|
||||
|
||||
#: for some backends this will always be `True`, but some backends will
|
||||
#: default this to false and detect changes in the dictionary for as
|
||||
#: long as changes do not happen on mutable structures in the session.
|
||||
#: The default mixin implementation just hardcodes `True` in.
|
||||
modified = True
|
||||
|
||||
|
||||
class SecureCookieSession(SecureCookie, SessionMixin):
|
||||
"""Expands the session with support for switching between permanent
|
||||
and non-permanent sessions.
|
||||
"""
|
||||
|
||||
|
||||
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
|
||||
|
||||
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):
|
||||
"""Helpful helper method that returns the cookie domain that should
|
||||
be used for the session cookie if session cookies are used.
|
||||
"""
|
||||
if app.config['SERVER_NAME'] is not None:
|
||||
# chop of the port which is usually not supported by browsers
|
||||
return '.' + app.config['SERVER_NAME'].rsplit(':', 1)[0]
|
||||
|
||||
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 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()
|
||||
|
||||
|
||||
class SecureCookieSessionInterface(SessionInterface):
|
||||
"""The cookie session interface that uses the Werkzeug securecookie
|
||||
as client side session backend.
|
||||
"""
|
||||
session_class = SecureCookieSession
|
||||
|
||||
def open_session(self, app, request):
|
||||
key = app.secret_key
|
||||
if key is not None:
|
||||
return self.session_class.load_cookie(request,
|
||||
app.session_cookie_name,
|
||||
secret_key=key)
|
||||
|
||||
def save_session(self, app, session, response):
|
||||
expires = self.get_expiration_time(app, session)
|
||||
domain = self.get_cookie_domain(app)
|
||||
if session.modified and not session:
|
||||
response.delete_cookie(app.session_cookie_name,
|
||||
domain=domain)
|
||||
else:
|
||||
session.save_cookie(response, app.session_cookie_name,
|
||||
expires=expires, httponly=True, domain=domain)
|
||||
@@ -47,4 +47,5 @@ _signals = Namespace()
|
||||
template_rendered = _signals.signal('template-rendered')
|
||||
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')
|
||||
|
||||
+61
-23
@@ -9,10 +9,12 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import posixpath
|
||||
from jinja2 import BaseLoader, TemplateNotFound
|
||||
from jinja2 import BaseLoader, Environment as BaseEnvironment, \
|
||||
TemplateNotFound
|
||||
|
||||
from .globals import _request_ctx_stack
|
||||
from .signals import template_rendered
|
||||
from .module import blueprint_is_module
|
||||
|
||||
|
||||
def _default_template_ctx_processor():
|
||||
@@ -28,40 +30,76 @@ def _default_template_ctx_processor():
|
||||
)
|
||||
|
||||
|
||||
class _DispatchingJinjaLoader(BaseLoader):
|
||||
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 module folders.
|
||||
the blueprint folders.
|
||||
"""
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def get_source(self, environment, template):
|
||||
template = posixpath.normpath(template)
|
||||
if template.startswith('../'):
|
||||
raise TemplateNotFound(template)
|
||||
loader = None
|
||||
try:
|
||||
module, name = template.split('/', 1)
|
||||
loader = self.app.modules[module].jinja_loader
|
||||
except (ValueError, KeyError):
|
||||
pass
|
||||
# if there was a module and it has a loader, try this first
|
||||
if loader is not None:
|
||||
for loader, local_name in self._iter_loaders(template):
|
||||
try:
|
||||
return loader.get_source(environment, name)
|
||||
return loader.get_source(environment, local_name)
|
||||
except TemplateNotFound:
|
||||
pass
|
||||
# fall back to application loader if module failed
|
||||
return self.app.jinja_loader.get_source(environment, template)
|
||||
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
def _iter_loaders(self, template):
|
||||
loader = self.app.jinja_loader
|
||||
if loader is not None:
|
||||
yield loader, template
|
||||
|
||||
# old style module based loaders in case we are dealing with a
|
||||
# blueprint that is an old style module
|
||||
try:
|
||||
module, local_name = posixpath.normpath(template).split('/', 1)
|
||||
blueprint = self.app.blueprints[module]
|
||||
if blueprint_is_module(blueprint):
|
||||
loader = blueprint.jinja_loader
|
||||
if loader is not None:
|
||||
yield loader, local_name
|
||||
except (ValueError, KeyError):
|
||||
pass
|
||||
|
||||
for blueprint in self.app.blueprints.itervalues():
|
||||
if blueprint_is_module(blueprint):
|
||||
continue
|
||||
loader = blueprint.jinja_loader
|
||||
if loader is not None:
|
||||
yield loader, template
|
||||
|
||||
def list_templates(self):
|
||||
result = self.app.jinja_loader.list_templates()
|
||||
for name, module in self.app.modules.iteritems():
|
||||
if module.jinja_loader is not None:
|
||||
for template in module.jinja_loader.list_templates():
|
||||
result.append('%s/%s' % (name, template))
|
||||
return result
|
||||
result = set()
|
||||
loader = self.app.jinja_loader
|
||||
if loader is not None:
|
||||
result.update(loader.list_templates())
|
||||
|
||||
for name, blueprint in self.app.blueprints.iteritems():
|
||||
loader = blueprint.jinja_loader
|
||||
if loader is not None:
|
||||
for template in loader.list_templates():
|
||||
prefix = ''
|
||||
if blueprint_is_module(blueprint):
|
||||
prefix = name + '/'
|
||||
result.add(prefix + template)
|
||||
|
||||
return list(result)
|
||||
|
||||
|
||||
def _render(template, context, app):
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from werkzeug import Client, EnvironBuilder
|
||||
from werkzeug.test import Client, EnvironBuilder
|
||||
from flask import _request_ctx_stack
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.views
|
||||
~~~~~~~~~~~
|
||||
|
||||
This module provides class based views inspired by the ones in Django.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from .globals import request
|
||||
|
||||
|
||||
http_method_funcs = frozenset(['get', 'post', 'head', 'options',
|
||||
'delete', 'put', 'trace'])
|
||||
|
||||
|
||||
|
||||
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'))
|
||||
"""
|
||||
|
||||
methods = None
|
||||
|
||||
def dispatch_request(self):
|
||||
"""Subclasses have to override this method to implement the
|
||||
actual view functionc ode. 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. What it does internally is generating
|
||||
a function on the fly that will instanciate 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)
|
||||
# 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 instanciating
|
||||
# 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
|
||||
return view
|
||||
|
||||
|
||||
class MethodViewType(type):
|
||||
|
||||
def __new__(cls, name, bases, d):
|
||||
rv = type.__new__(cls, name, bases, d)
|
||||
if 'methods' not in d:
|
||||
methods = set(rv.methods or [])
|
||||
for key, value in d.iteritems():
|
||||
if key in http_method_funcs:
|
||||
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 baseclass or another subclass of a base method view
|
||||
# that does not introduce new methods).
|
||||
if methods:
|
||||
rv.methods = sorted(methods)
|
||||
return rv
|
||||
|
||||
|
||||
class MethodView(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
|
||||
the :meth:`dispatch_request` implementation will automatically
|
||||
forward your request to that. Also :attr:`options` is set for you
|
||||
automatically::
|
||||
|
||||
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'))
|
||||
"""
|
||||
__metaclass__ = MethodViewType
|
||||
|
||||
def dispatch_request(self, *args, **kwargs):
|
||||
meth = getattr(self, request.method.lower(), None)
|
||||
assert meth is not None, 'Not implemented method'
|
||||
return meth(*args, **kwargs)
|
||||
+55
-5
@@ -9,9 +9,11 @@
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from werkzeug import Request as RequestBase, Response as ResponseBase, \
|
||||
cached_property
|
||||
from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from werkzeug.utils import cached_property
|
||||
|
||||
from .debughelpers import attach_enctype_error_multidict
|
||||
from .helpers import json, _assert_have_json
|
||||
from .globals import _request_ctx_stack
|
||||
|
||||
@@ -23,6 +25,10 @@ class Request(RequestBase):
|
||||
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
|
||||
@@ -42,6 +48,10 @@ class Request(RequestBase):
|
||||
#: something similar.
|
||||
routing_exception = None
|
||||
|
||||
# switched by the request context until 1.0 to opt in deprecated
|
||||
# module functionality
|
||||
_is_old_module = False
|
||||
|
||||
@property
|
||||
def max_content_length(self):
|
||||
"""Read-only view of the `MAX_CONTENT_LENGTH` config key."""
|
||||
@@ -61,19 +71,59 @@ class Request(RequestBase):
|
||||
|
||||
@property
|
||||
def module(self):
|
||||
"""The name of the current module"""
|
||||
"""The name of the current module if the request was dispatched
|
||||
to an actual module. This is deprecated functionality, use blueprints
|
||||
instead.
|
||||
"""
|
||||
from warnings import warn
|
||||
warn(DeprecationWarning('modules were deprecated in favor of '
|
||||
'blueprints. Use request.blueprint '
|
||||
'instead.'), stacklevel=2)
|
||||
if self._is_old_module:
|
||||
return self.blueprint
|
||||
|
||||
@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]
|
||||
|
||||
@cached_property
|
||||
def json(self):
|
||||
"""If the mimetype is `application/json` this will contain the
|
||||
parsed JSON data.
|
||||
parsed JSON data. Otherwise this will be `None`.
|
||||
|
||||
This requires Python 2.6 or an installed version of simplejson.
|
||||
"""
|
||||
if __debug__:
|
||||
_assert_have_json()
|
||||
if self.mimetype == 'application/json':
|
||||
return json.loads(self.data)
|
||||
request_charset = self.mimetype_params.get('charset')
|
||||
try:
|
||||
if request_charset is not None:
|
||||
return json.loads(self.data, encoding=request_charset)
|
||||
return json.loads(self.data)
|
||||
except ValueError, e:
|
||||
return self.on_json_loading_failed(e)
|
||||
|
||||
def on_json_loading_failed(self, e):
|
||||
"""Called if decoding of the JSON data failed. The return value of
|
||||
this method is used by :attr:`json` when an error ocurred. The
|
||||
default implementation raises a :class:`~werkzeug.exceptions.BadRequest`.
|
||||
|
||||
.. versionadded:: 0.8
|
||||
"""
|
||||
raise BadRequest()
|
||||
|
||||
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.
|
||||
ctx = _request_ctx_stack.top
|
||||
if ctx is not None and ctx.app.debug and \
|
||||
self.mimetype != 'multipart/form-data' and not self.files:
|
||||
attach_enctype_error_multidict(self)
|
||||
|
||||
|
||||
class Response(ResponseBase):
|
||||
|
||||
Reference in New Issue
Block a user