Update Flask
This commit is contained in:
Executable → Regular
+7
-4
@@ -10,7 +10,7 @@
|
|||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = '0.9-dev'
|
__version__ = '0.9'
|
||||||
|
|
||||||
# utilities we import from Werkzeug and Jinja2 that are unused
|
# utilities we import from Werkzeug and Jinja2 that are unused
|
||||||
# in the module but are exported as public interface.
|
# in the module but are exported as public interface.
|
||||||
@@ -22,9 +22,12 @@ from .app import Flask, Request, Response
|
|||||||
from .config import Config
|
from .config import Config
|
||||||
from .helpers import url_for, jsonify, json_available, flash, \
|
from .helpers import url_for, jsonify, json_available, flash, \
|
||||||
send_file, send_from_directory, get_flashed_messages, \
|
send_file, send_from_directory, get_flashed_messages, \
|
||||||
get_template_attribute, make_response, safe_join
|
get_template_attribute, make_response, safe_join, \
|
||||||
from .globals import current_app, g, request, session, _request_ctx_stack
|
stream_with_context
|
||||||
from .ctx import has_request_context
|
from .globals import current_app, g, request, session, _request_ctx_stack, \
|
||||||
|
_app_ctx_stack
|
||||||
|
from .ctx import has_request_context, has_app_context, \
|
||||||
|
after_this_request
|
||||||
from .module import Module
|
from .module import Module
|
||||||
from .blueprints import Blueprint
|
from .blueprints import Blueprint
|
||||||
from .templating import render_template, render_template_string
|
from .templating import render_template, render_template_string
|
||||||
|
|||||||
Executable → Regular
+181
-24
@@ -19,7 +19,7 @@ from itertools import chain
|
|||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
|
|
||||||
from werkzeug.datastructures import ImmutableDict
|
from werkzeug.datastructures import ImmutableDict
|
||||||
from werkzeug.routing import Map, Rule, RequestRedirect
|
from werkzeug.routing import Map, Rule, RequestRedirect, BuildError
|
||||||
from werkzeug.exceptions import HTTPException, InternalServerError, \
|
from werkzeug.exceptions import HTTPException, InternalServerError, \
|
||||||
MethodNotAllowed, BadRequest
|
MethodNotAllowed, BadRequest
|
||||||
|
|
||||||
@@ -28,14 +28,14 @@ from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
|
|||||||
find_package
|
find_package
|
||||||
from .wrappers import Request, Response
|
from .wrappers import Request, Response
|
||||||
from .config import ConfigAttribute, Config
|
from .config import ConfigAttribute, Config
|
||||||
from .ctx import RequestContext
|
from .ctx import RequestContext, AppContext, _RequestGlobals
|
||||||
from .globals import _request_ctx_stack, request
|
from .globals import _request_ctx_stack, request
|
||||||
from .sessions import SecureCookieSessionInterface
|
from .sessions import SecureCookieSessionInterface
|
||||||
from .module import blueprint_is_module
|
from .module import blueprint_is_module
|
||||||
from .templating import DispatchingJinjaLoader, Environment, \
|
from .templating import DispatchingJinjaLoader, Environment, \
|
||||||
_default_template_ctx_processor
|
_default_template_ctx_processor
|
||||||
from .signals import request_started, request_finished, got_request_exception, \
|
from .signals import request_started, request_finished, got_request_exception, \
|
||||||
request_tearing_down
|
request_tearing_down, appcontext_tearing_down
|
||||||
|
|
||||||
# a lock used for logger initialization
|
# a lock used for logger initialization
|
||||||
_logger_lock = Lock()
|
_logger_lock = Lock()
|
||||||
@@ -148,6 +148,18 @@ class Flask(_PackageBoundObject):
|
|||||||
#: :class:`~flask.Response` for more information.
|
#: :class:`~flask.Response` for more information.
|
||||||
response_class = Response
|
response_class = Response
|
||||||
|
|
||||||
|
#: The class that is used for the :data:`~flask.g` instance.
|
||||||
|
#:
|
||||||
|
#: Example use cases for a custom class:
|
||||||
|
#:
|
||||||
|
#: 1. Store arbitrary attributes on flask.g.
|
||||||
|
#: 2. Add a property for lazy per-request database connectors.
|
||||||
|
#: 3. Return None instead of AttributeError on expected attributes.
|
||||||
|
#: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g.
|
||||||
|
#:
|
||||||
|
#: .. versionadded:: 0.9
|
||||||
|
request_globals_class = _RequestGlobals
|
||||||
|
|
||||||
#: The debug flag. Set this to `True` to enable debugging of the
|
#: The debug flag. Set this to `True` to enable debugging of the
|
||||||
#: application. In debug mode the debugger will kick in when an unhandled
|
#: application. In debug mode the debugger will kick in when an unhandled
|
||||||
#: exception ocurrs and the integrated server will automatically reload
|
#: exception ocurrs and the integrated server will automatically reload
|
||||||
@@ -249,8 +261,10 @@ class Flask(_PackageBoundObject):
|
|||||||
'SESSION_COOKIE_HTTPONLY': True,
|
'SESSION_COOKIE_HTTPONLY': True,
|
||||||
'SESSION_COOKIE_SECURE': False,
|
'SESSION_COOKIE_SECURE': False,
|
||||||
'MAX_CONTENT_LENGTH': None,
|
'MAX_CONTENT_LENGTH': None,
|
||||||
|
'SEND_FILE_MAX_AGE_DEFAULT': 12 * 60 * 60, # 12 hours
|
||||||
'TRAP_BAD_REQUEST_ERRORS': False,
|
'TRAP_BAD_REQUEST_ERRORS': False,
|
||||||
'TRAP_HTTP_EXCEPTIONS': False
|
'TRAP_HTTP_EXCEPTIONS': False,
|
||||||
|
'PREFERRED_URL_SCHEME': 'http'
|
||||||
})
|
})
|
||||||
|
|
||||||
#: The rule object to use for URL rules created. This is used by
|
#: The rule object to use for URL rules created. This is used by
|
||||||
@@ -327,6 +341,15 @@ class Flask(_PackageBoundObject):
|
|||||||
#: decorator.
|
#: decorator.
|
||||||
self.error_handler_spec = {None: self._error_handlers}
|
self.error_handler_spec = {None: self._error_handlers}
|
||||||
|
|
||||||
|
#: A list of functions that are called when :meth:`url_for` raises a
|
||||||
|
#: :exc:`~werkzeug.routing.BuildError`. Each function registered here
|
||||||
|
#: is called with `error`, `endpoint` and `values`. If a function
|
||||||
|
#: returns `None` or raises a `BuildError` the next function is
|
||||||
|
#: tried.
|
||||||
|
#:
|
||||||
|
#: .. versionadded:: 0.9
|
||||||
|
self.url_build_error_handlers = []
|
||||||
|
|
||||||
#: A dictionary with lists of functions that should be called at the
|
#: A dictionary with lists of functions that should be called at the
|
||||||
#: beginning of the request. The key of the dictionary is the name of
|
#: beginning of the request. The key of the dictionary is the name of
|
||||||
#: the blueprint this function is active for, `None` for all requests.
|
#: the blueprint this function is active for, `None` for all requests.
|
||||||
@@ -362,6 +385,14 @@ class Flask(_PackageBoundObject):
|
|||||||
#: .. versionadded:: 0.7
|
#: .. versionadded:: 0.7
|
||||||
self.teardown_request_funcs = {}
|
self.teardown_request_funcs = {}
|
||||||
|
|
||||||
|
#: A list of functions that are called when the application context
|
||||||
|
#: is destroyed. Since the application context is also torn down
|
||||||
|
#: if the request ends this is the place to store code that disconnects
|
||||||
|
#: from databases.
|
||||||
|
#:
|
||||||
|
#: .. versionadded:: 0.9
|
||||||
|
self.teardown_appcontext_funcs = []
|
||||||
|
|
||||||
#: A dictionary with lists of functions that can be used as URL
|
#: A dictionary with lists of functions that can be used as URL
|
||||||
#: value processor functions. Whenever a URL is built these functions
|
#: value processor functions. Whenever a URL is built these functions
|
||||||
#: are called to modify the dictionary of values in place. The key
|
#: are called to modify the dictionary of values in place. The key
|
||||||
@@ -884,6 +915,10 @@ class Flask(_PackageBoundObject):
|
|||||||
# a tuple of only `GET` as default.
|
# a tuple of only `GET` as default.
|
||||||
if methods is None:
|
if methods is None:
|
||||||
methods = getattr(view_func, 'methods', None) or ('GET',)
|
methods = getattr(view_func, 'methods', None) or ('GET',)
|
||||||
|
methods = set(methods)
|
||||||
|
|
||||||
|
# Methods that should always be added
|
||||||
|
required_methods = set(getattr(view_func, 'required_methods', ()))
|
||||||
|
|
||||||
# starting with Flask 0.8 the view_func object can disable and
|
# starting with Flask 0.8 the view_func object can disable and
|
||||||
# force-enable the automatic options handling.
|
# force-enable the automatic options handling.
|
||||||
@@ -892,11 +927,14 @@ class Flask(_PackageBoundObject):
|
|||||||
|
|
||||||
if provide_automatic_options is None:
|
if provide_automatic_options is None:
|
||||||
if 'OPTIONS' not in methods:
|
if 'OPTIONS' not in methods:
|
||||||
methods = tuple(methods) + ('OPTIONS',)
|
|
||||||
provide_automatic_options = True
|
provide_automatic_options = True
|
||||||
|
required_methods.add('OPTIONS')
|
||||||
else:
|
else:
|
||||||
provide_automatic_options = False
|
provide_automatic_options = False
|
||||||
|
|
||||||
|
# Add the required methods now.
|
||||||
|
methods |= required_methods
|
||||||
|
|
||||||
# due to a werkzeug bug we need to make sure that the defaults are
|
# due to a werkzeug bug we need to make sure that the defaults are
|
||||||
# None if they are an empty dictionary. This should not be necessary
|
# None if they are an empty dictionary. This should not be necessary
|
||||||
# with Werkzeug 0.7
|
# with Werkzeug 0.7
|
||||||
@@ -1098,10 +1136,42 @@ class Flask(_PackageBoundObject):
|
|||||||
that they will fail. If they do execute code that might fail they
|
that they will fail. If they do execute code that might fail they
|
||||||
will have to surround the execution of these code by try/except
|
will have to surround the execution of these code by try/except
|
||||||
statements and log ocurring errors.
|
statements and log ocurring errors.
|
||||||
|
|
||||||
|
When a teardown function was called because of a exception it will
|
||||||
|
be passed an error object.
|
||||||
"""
|
"""
|
||||||
self.teardown_request_funcs.setdefault(None, []).append(f)
|
self.teardown_request_funcs.setdefault(None, []).append(f)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def teardown_appcontext(self, f):
|
||||||
|
"""Registers a function to be called when the application context
|
||||||
|
ends. These functions are typically also called when the request
|
||||||
|
context is popped.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
ctx = app.app_context()
|
||||||
|
ctx.push()
|
||||||
|
...
|
||||||
|
ctx.pop()
|
||||||
|
|
||||||
|
When ``ctx.pop()`` is executed in the above example, the teardown
|
||||||
|
functions are called just before the app context moves from the
|
||||||
|
stack of active contexts. This becomes relevant if you are using
|
||||||
|
such constructs in tests.
|
||||||
|
|
||||||
|
Since a request context typically also manages an application
|
||||||
|
context it would also be called when you pop a request context.
|
||||||
|
|
||||||
|
When a teardown function was called because of an exception it will
|
||||||
|
be passed an error object.
|
||||||
|
|
||||||
|
.. versionadded:: 0.9
|
||||||
|
"""
|
||||||
|
self.teardown_appcontext_funcs.append(f)
|
||||||
|
return f
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def context_processor(self, f):
|
def context_processor(self, f):
|
||||||
"""Registers a template context processor function."""
|
"""Registers a template context processor function."""
|
||||||
@@ -1312,7 +1382,7 @@ class Flask(_PackageBoundObject):
|
|||||||
def make_default_options_response(self):
|
def make_default_options_response(self):
|
||||||
"""This method is called to create the default `OPTIONS` response.
|
"""This method is called to create the default `OPTIONS` response.
|
||||||
This can be changed through subclassing to change the default
|
This can be changed through subclassing to change the default
|
||||||
behaviour of `OPTIONS` responses.
|
behavior of `OPTIONS` responses.
|
||||||
|
|
||||||
.. versionadded:: 0.7
|
.. versionadded:: 0.7
|
||||||
"""
|
"""
|
||||||
@@ -1346,23 +1416,48 @@ class Flask(_PackageBoundObject):
|
|||||||
string as body
|
string as body
|
||||||
:class:`unicode` a response object is created with the
|
:class:`unicode` a response object is created with the
|
||||||
string encoded to utf-8 as body
|
string encoded to utf-8 as body
|
||||||
:class:`tuple` the response object is created with the
|
|
||||||
contents of the tuple as arguments
|
|
||||||
a WSGI function the function is called as WSGI application
|
a WSGI function the function is called as WSGI application
|
||||||
and buffered as response object
|
and buffered as response object
|
||||||
|
:class:`tuple` A tuple in the form ``(response, status,
|
||||||
|
headers)`` where `response` is any of the
|
||||||
|
types defined here, `status` is a string
|
||||||
|
or an integer and `headers` is a list of
|
||||||
|
a dictionary with header values.
|
||||||
======================= ===========================================
|
======================= ===========================================
|
||||||
|
|
||||||
:param rv: the return value from the view function
|
:param rv: the return value from the view function
|
||||||
|
|
||||||
|
.. versionchanged:: 0.9
|
||||||
|
Previously a tuple was interpreted as the arguments for the
|
||||||
|
response object.
|
||||||
"""
|
"""
|
||||||
|
status = headers = None
|
||||||
|
if isinstance(rv, tuple):
|
||||||
|
rv, status, headers = rv + (None,) * (3 - len(rv))
|
||||||
|
|
||||||
if rv is None:
|
if rv is None:
|
||||||
raise ValueError('View function did not return a response')
|
raise ValueError('View function did not return a response')
|
||||||
if isinstance(rv, self.response_class):
|
|
||||||
return rv
|
if not isinstance(rv, self.response_class):
|
||||||
if isinstance(rv, basestring):
|
# When we create a response object directly, we let the constructor
|
||||||
return self.response_class(rv)
|
# set the headers and status. We do this because there can be
|
||||||
if isinstance(rv, tuple):
|
# some extra logic involved when creating these objects with
|
||||||
return self.response_class(*rv)
|
# specific values (like defualt content type selection).
|
||||||
return self.response_class.force_type(rv, request.environ)
|
if isinstance(rv, basestring):
|
||||||
|
rv = self.response_class(rv, headers=headers, status=status)
|
||||||
|
headers = status = None
|
||||||
|
else:
|
||||||
|
rv = self.response_class.force_type(rv, request.environ)
|
||||||
|
|
||||||
|
if status is not None:
|
||||||
|
if isinstance(status, basestring):
|
||||||
|
rv.status = status
|
||||||
|
else:
|
||||||
|
rv.status_code = status
|
||||||
|
if headers:
|
||||||
|
rv.headers.extend(headers)
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
def create_url_adapter(self, request):
|
def create_url_adapter(self, request):
|
||||||
"""Creates a URL adapter for the given request. The URL adapter
|
"""Creates a URL adapter for the given request. The URL adapter
|
||||||
@@ -1370,9 +1465,21 @@ class Flask(_PackageBoundObject):
|
|||||||
so the request is passed explicitly.
|
so the request is passed explicitly.
|
||||||
|
|
||||||
.. versionadded:: 0.6
|
.. versionadded:: 0.6
|
||||||
|
|
||||||
|
.. versionchanged:: 0.9
|
||||||
|
This can now also be called without a request object when the
|
||||||
|
UR adapter is created for the application context.
|
||||||
"""
|
"""
|
||||||
return self.url_map.bind_to_environ(request.environ,
|
if request is not None:
|
||||||
server_name=self.config['SERVER_NAME'])
|
return self.url_map.bind_to_environ(request.environ,
|
||||||
|
server_name=self.config['SERVER_NAME'])
|
||||||
|
# We need at the very least the server name to be set for this
|
||||||
|
# to work.
|
||||||
|
if self.config['SERVER_NAME'] is not None:
|
||||||
|
return self.url_map.bind(
|
||||||
|
self.config['SERVER_NAME'],
|
||||||
|
script_name=self.config['APPLICATION_ROOT'] or '/',
|
||||||
|
url_scheme=self.config['PREFERRED_URL_SCHEME'])
|
||||||
|
|
||||||
def inject_url_defaults(self, endpoint, values):
|
def inject_url_defaults(self, endpoint, values):
|
||||||
"""Injects the URL defaults for the given endpoint directly into
|
"""Injects the URL defaults for the given endpoint directly into
|
||||||
@@ -1383,11 +1490,30 @@ class Flask(_PackageBoundObject):
|
|||||||
"""
|
"""
|
||||||
funcs = self.url_default_functions.get(None, ())
|
funcs = self.url_default_functions.get(None, ())
|
||||||
if '.' in endpoint:
|
if '.' in endpoint:
|
||||||
bp = endpoint.split('.', 1)[0]
|
bp = endpoint.rsplit('.', 1)[0]
|
||||||
funcs = chain(funcs, self.url_default_functions.get(bp, ()))
|
funcs = chain(funcs, self.url_default_functions.get(bp, ()))
|
||||||
for func in funcs:
|
for func in funcs:
|
||||||
func(endpoint, values)
|
func(endpoint, values)
|
||||||
|
|
||||||
|
def handle_url_build_error(self, error, endpoint, values):
|
||||||
|
"""Handle :class:`~werkzeug.routing.BuildError` on :meth:`url_for`.
|
||||||
|
"""
|
||||||
|
exc_type, exc_value, tb = sys.exc_info()
|
||||||
|
for handler in self.url_build_error_handlers:
|
||||||
|
try:
|
||||||
|
rv = handler(error, endpoint, values)
|
||||||
|
if rv is not None:
|
||||||
|
return rv
|
||||||
|
except BuildError, error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# At this point we want to reraise the exception. If the error is
|
||||||
|
# still the same one we can reraise it with the original traceback,
|
||||||
|
# otherwise we raise it from here.
|
||||||
|
if error is exc_value:
|
||||||
|
raise exc_type, exc_value, tb
|
||||||
|
raise error
|
||||||
|
|
||||||
def preprocess_request(self):
|
def preprocess_request(self):
|
||||||
"""Called before the actual request dispatching and will
|
"""Called before the actual request dispatching and will
|
||||||
call every as :meth:`before_request` decorated function.
|
call every as :meth:`before_request` decorated function.
|
||||||
@@ -1429,7 +1555,7 @@ class Flask(_PackageBoundObject):
|
|||||||
"""
|
"""
|
||||||
ctx = _request_ctx_stack.top
|
ctx = _request_ctx_stack.top
|
||||||
bp = ctx.request.blueprint
|
bp = ctx.request.blueprint
|
||||||
funcs = ()
|
funcs = ctx._after_request_functions
|
||||||
if bp is not None and bp in self.after_request_funcs:
|
if bp is not None and bp in self.after_request_funcs:
|
||||||
funcs = reversed(self.after_request_funcs[bp])
|
funcs = reversed(self.after_request_funcs[bp])
|
||||||
if None in self.after_request_funcs:
|
if None in self.after_request_funcs:
|
||||||
@@ -1440,23 +1566,54 @@ class Flask(_PackageBoundObject):
|
|||||||
self.save_session(ctx.session, response)
|
self.save_session(ctx.session, response)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def do_teardown_request(self):
|
def do_teardown_request(self, exc=None):
|
||||||
"""Called after the actual request dispatching and will
|
"""Called after the actual request dispatching and will
|
||||||
call every as :meth:`teardown_request` decorated function. This is
|
call every as :meth:`teardown_request` decorated function. This is
|
||||||
not actually called by the :class:`Flask` object itself but is always
|
not actually called by the :class:`Flask` object itself but is always
|
||||||
triggered when the request context is popped. That way we have a
|
triggered when the request context is popped. That way we have a
|
||||||
tighter control over certain resources under testing environments.
|
tighter control over certain resources under testing environments.
|
||||||
|
|
||||||
|
.. versionchanged:: 0.9
|
||||||
|
Added the `exc` argument. Previously this was always using the
|
||||||
|
current exception information.
|
||||||
"""
|
"""
|
||||||
|
if exc is None:
|
||||||
|
exc = sys.exc_info()[1]
|
||||||
funcs = reversed(self.teardown_request_funcs.get(None, ()))
|
funcs = reversed(self.teardown_request_funcs.get(None, ()))
|
||||||
bp = _request_ctx_stack.top.request.blueprint
|
bp = _request_ctx_stack.top.request.blueprint
|
||||||
if bp is not None and bp in self.teardown_request_funcs:
|
if bp is not None and bp in self.teardown_request_funcs:
|
||||||
funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
|
funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
|
||||||
exc = sys.exc_info()[1]
|
|
||||||
for func in funcs:
|
for func in funcs:
|
||||||
rv = func(exc)
|
rv = func(exc)
|
||||||
if rv is not None:
|
request_tearing_down.send(self, exc=exc)
|
||||||
return rv
|
|
||||||
request_tearing_down.send(self)
|
def do_teardown_appcontext(self, exc=None):
|
||||||
|
"""Called when an application context is popped. This works pretty
|
||||||
|
much the same as :meth:`do_teardown_request` but for the application
|
||||||
|
context.
|
||||||
|
|
||||||
|
.. versionadded:: 0.9
|
||||||
|
"""
|
||||||
|
if exc is None:
|
||||||
|
exc = sys.exc_info()[1]
|
||||||
|
for func in reversed(self.teardown_appcontext_funcs):
|
||||||
|
func(exc)
|
||||||
|
appcontext_tearing_down.send(self, exc=exc)
|
||||||
|
|
||||||
|
def app_context(self):
|
||||||
|
"""Binds the application only. For as long as the application is bound
|
||||||
|
to the current context the :data:`flask.current_app` points to that
|
||||||
|
application. An application context is automatically created when a
|
||||||
|
request context is pushed if necessary.
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
...
|
||||||
|
|
||||||
|
.. versionadded:: 0.9
|
||||||
|
"""
|
||||||
|
return AppContext(self)
|
||||||
|
|
||||||
def request_context(self, environ):
|
def request_context(self, environ):
|
||||||
"""Creates a :class:`~flask.ctx.RequestContext` from the given
|
"""Creates a :class:`~flask.ctx.RequestContext` from the given
|
||||||
|
|||||||
Executable → Regular
+1
-1
@@ -25,7 +25,7 @@ class BlueprintSetupState(object):
|
|||||||
#: a reference to the current application
|
#: a reference to the current application
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
#: a reference to the blurprint that created this setup state.
|
#: a reference to the blueprint that created this setup state.
|
||||||
self.blueprint = blueprint
|
self.blueprint = blueprint
|
||||||
|
|
||||||
#: a dictionary with all options that were passed to the
|
#: a dictionary with all options that were passed to the
|
||||||
|
|||||||
Executable → Regular
+1
-2
@@ -106,8 +106,7 @@ class Config(dict):
|
|||||||
'loaded. Set this variable and make it '
|
'loaded. Set this variable and make it '
|
||||||
'point to a configuration file' %
|
'point to a configuration file' %
|
||||||
variable_name)
|
variable_name)
|
||||||
self.from_pyfile(rv)
|
return self.from_pyfile(rv, silent=silent)
|
||||||
return True
|
|
||||||
|
|
||||||
def from_pyfile(self, filename, silent=False):
|
def from_pyfile(self, filename, silent=False):
|
||||||
"""Updates the values in the config from a Python file. This function
|
"""Updates the values in the config from a Python file. This function
|
||||||
|
|||||||
Executable → Regular
+124
-8
@@ -9,16 +9,44 @@
|
|||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
from werkzeug.exceptions import HTTPException
|
from werkzeug.exceptions import HTTPException
|
||||||
|
|
||||||
from .globals import _request_ctx_stack
|
from .globals import _request_ctx_stack, _app_ctx_stack
|
||||||
from .module import blueprint_is_module
|
from .module import blueprint_is_module
|
||||||
|
|
||||||
|
|
||||||
class _RequestGlobals(object):
|
class _RequestGlobals(object):
|
||||||
|
"""A plain object."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def after_this_request(f):
|
||||||
|
"""Executes a function after this request. This is useful to modify
|
||||||
|
response objects. The function is passed the response object and has
|
||||||
|
to return the same or a new one.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
@after_this_request
|
||||||
|
def add_header(response):
|
||||||
|
response.headers['X-Foo'] = 'Parachute'
|
||||||
|
return response
|
||||||
|
return 'Hello World!'
|
||||||
|
|
||||||
|
This is more useful if a function other than the view function wants to
|
||||||
|
modify a response. For instance think of a decorator that wants to add
|
||||||
|
some headers without converting the return value into a response object.
|
||||||
|
|
||||||
|
.. versionadded:: 0.9
|
||||||
|
"""
|
||||||
|
_request_ctx_stack.top._after_request_functions.append(f)
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
def has_request_context():
|
def has_request_context():
|
||||||
"""If you have code that wants to test if a request context is there or
|
"""If you have code that wants to test if a request context is there or
|
||||||
not this function can be used. For instance, you may want to take advantage
|
not this function can be used. For instance, you may want to take advantage
|
||||||
@@ -51,6 +79,57 @@ def has_request_context():
|
|||||||
return _request_ctx_stack.top is not None
|
return _request_ctx_stack.top is not None
|
||||||
|
|
||||||
|
|
||||||
|
def has_app_context():
|
||||||
|
"""Works like :func:`has_request_context` but for the application
|
||||||
|
context. You can also just do a boolean check on the
|
||||||
|
:data:`current_app` object instead.
|
||||||
|
|
||||||
|
.. versionadded:: 0.9
|
||||||
|
"""
|
||||||
|
return _app_ctx_stack.top is not None
|
||||||
|
|
||||||
|
|
||||||
|
class AppContext(object):
|
||||||
|
"""The application context binds an application object implicitly
|
||||||
|
to the current thread or greenlet, similar to how the
|
||||||
|
:class:`RequestContext` binds request information. The application
|
||||||
|
context is also implicitly created if a request context is created
|
||||||
|
but the application is not on top of the individual application
|
||||||
|
context.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, app):
|
||||||
|
self.app = app
|
||||||
|
self.url_adapter = app.create_url_adapter(None)
|
||||||
|
|
||||||
|
# Like request context, app contexts can be pushed multiple times
|
||||||
|
# but there a basic "refcount" is enough to track them.
|
||||||
|
self._refcnt = 0
|
||||||
|
|
||||||
|
def push(self):
|
||||||
|
"""Binds the app context to the current context."""
|
||||||
|
self._refcnt += 1
|
||||||
|
_app_ctx_stack.push(self)
|
||||||
|
|
||||||
|
def pop(self, exc=None):
|
||||||
|
"""Pops the app context."""
|
||||||
|
self._refcnt -= 1
|
||||||
|
if self._refcnt <= 0:
|
||||||
|
if exc is None:
|
||||||
|
exc = sys.exc_info()[1]
|
||||||
|
self.app.do_teardown_appcontext(exc)
|
||||||
|
rv = _app_ctx_stack.pop()
|
||||||
|
assert rv is self, 'Popped wrong app context. (%r instead of %r)' \
|
||||||
|
% (rv, self)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.push()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, tb):
|
||||||
|
self.pop(exc_value)
|
||||||
|
|
||||||
|
|
||||||
class RequestContext(object):
|
class RequestContext(object):
|
||||||
"""The request context contains all request relevant information. It is
|
"""The request context contains all request relevant information. It is
|
||||||
created at the beginning of the request and pushed to the
|
created at the beginning of the request and pushed to the
|
||||||
@@ -85,17 +164,28 @@ class RequestContext(object):
|
|||||||
self.app = app
|
self.app = app
|
||||||
self.request = app.request_class(environ)
|
self.request = app.request_class(environ)
|
||||||
self.url_adapter = app.create_url_adapter(self.request)
|
self.url_adapter = app.create_url_adapter(self.request)
|
||||||
self.g = _RequestGlobals()
|
self.g = app.request_globals_class()
|
||||||
self.flashes = None
|
self.flashes = None
|
||||||
self.session = None
|
self.session = None
|
||||||
|
|
||||||
|
# Request contexts can be pushed multiple times and interleaved with
|
||||||
|
# other request contexts. Now only if the last level is popped we
|
||||||
|
# get rid of them. Additionally if an application context is missing
|
||||||
|
# one is created implicitly so for each level we add this information
|
||||||
|
self._implicit_app_ctx_stack = []
|
||||||
|
|
||||||
# indicator if the context was preserved. Next time another context
|
# indicator if the context was preserved. Next time another context
|
||||||
# is pushed the preserved context is popped.
|
# is pushed the preserved context is popped.
|
||||||
self.preserved = False
|
self.preserved = False
|
||||||
|
|
||||||
|
# Functions that should be executed after the request on the response
|
||||||
|
# object. These will be called before the regular "after_request"
|
||||||
|
# functions.
|
||||||
|
self._after_request_functions = []
|
||||||
|
|
||||||
self.match_request()
|
self.match_request()
|
||||||
|
|
||||||
# XXX: Support for deprecated functionality. This is doing away with
|
# XXX: Support for deprecated functionality. This is going away with
|
||||||
# Flask 1.0
|
# Flask 1.0
|
||||||
blueprint = self.request.blueprint
|
blueprint = self.request.blueprint
|
||||||
if blueprint is not None:
|
if blueprint is not None:
|
||||||
@@ -130,6 +220,16 @@ class RequestContext(object):
|
|||||||
if top is not None and top.preserved:
|
if top is not None and top.preserved:
|
||||||
top.pop()
|
top.pop()
|
||||||
|
|
||||||
|
# Before we push the request context we have to ensure that there
|
||||||
|
# is an application context.
|
||||||
|
app_ctx = _app_ctx_stack.top
|
||||||
|
if app_ctx is None or app_ctx.app != self.app:
|
||||||
|
app_ctx = self.app.app_context()
|
||||||
|
app_ctx.push()
|
||||||
|
self._implicit_app_ctx_stack.append(app_ctx)
|
||||||
|
else:
|
||||||
|
self._implicit_app_ctx_stack.append(None)
|
||||||
|
|
||||||
_request_ctx_stack.push(self)
|
_request_ctx_stack.push(self)
|
||||||
|
|
||||||
# Open the session at the moment that the request context is
|
# Open the session at the moment that the request context is
|
||||||
@@ -139,20 +239,36 @@ class RequestContext(object):
|
|||||||
if self.session is None:
|
if self.session is None:
|
||||||
self.session = self.app.make_null_session()
|
self.session = self.app.make_null_session()
|
||||||
|
|
||||||
def pop(self):
|
def pop(self, exc=None):
|
||||||
"""Pops the request context and unbinds it by doing that. This will
|
"""Pops the request context and unbinds it by doing that. This will
|
||||||
also trigger the execution of functions registered by the
|
also trigger the execution of functions registered by the
|
||||||
:meth:`~flask.Flask.teardown_request` decorator.
|
:meth:`~flask.Flask.teardown_request` decorator.
|
||||||
|
|
||||||
|
.. versionchanged:: 0.9
|
||||||
|
Added the `exc` argument.
|
||||||
"""
|
"""
|
||||||
self.preserved = False
|
app_ctx = self._implicit_app_ctx_stack.pop()
|
||||||
self.app.do_teardown_request()
|
|
||||||
|
clear_request = False
|
||||||
|
if not self._implicit_app_ctx_stack:
|
||||||
|
self.preserved = False
|
||||||
|
if exc is None:
|
||||||
|
exc = sys.exc_info()[1]
|
||||||
|
self.app.do_teardown_request(exc)
|
||||||
|
clear_request = True
|
||||||
|
|
||||||
rv = _request_ctx_stack.pop()
|
rv = _request_ctx_stack.pop()
|
||||||
assert rv is self, 'Popped wrong request context. (%r instead of %r)' \
|
assert rv is self, 'Popped wrong request context. (%r instead of %r)' \
|
||||||
% (rv, self)
|
% (rv, self)
|
||||||
|
|
||||||
# get rid of circular dependencies at the end of the request
|
# get rid of circular dependencies at the end of the request
|
||||||
# so that we don't require the GC to be active.
|
# so that we don't require the GC to be active.
|
||||||
rv.request.environ['werkzeug.request'] = None
|
if clear_request:
|
||||||
|
rv.request.environ['werkzeug.request'] = None
|
||||||
|
|
||||||
|
# Get rid of the app as well if necessary.
|
||||||
|
if app_ctx is not None:
|
||||||
|
app_ctx.pop(exc)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.push()
|
self.push()
|
||||||
@@ -168,7 +284,7 @@ class RequestContext(object):
|
|||||||
(tb is not None and self.app.preserve_context_on_exception):
|
(tb is not None and self.app.preserve_context_on_exception):
|
||||||
self.preserved = True
|
self.preserved = True
|
||||||
else:
|
else:
|
||||||
self.pop()
|
self.pop(exc_value)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<%s \'%s\' [%s] of %s>' % (
|
return '<%s \'%s\' [%s] of %s>' % (
|
||||||
|
|||||||
Executable → Regular
@@ -0,0 +1,49 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
flask.exceptions
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Flask specific additions to :class:`~werkzeug.exceptions.HTTPException`
|
||||||
|
|
||||||
|
:copyright: (c) 2011 by Armin Ronacher.
|
||||||
|
:license: BSD, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
from werkzeug.exceptions import HTTPException, BadRequest
|
||||||
|
from .helpers import json
|
||||||
|
|
||||||
|
|
||||||
|
class JSONHTTPException(HTTPException):
|
||||||
|
"""A base class for HTTP exceptions with ``Content-Type:
|
||||||
|
application/json``.
|
||||||
|
|
||||||
|
The ``description`` attribute of this class must set to a string (*not* an
|
||||||
|
HTML string) which describes the error.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_body(self, environ):
|
||||||
|
"""Overrides :meth:`werkzeug.exceptions.HTTPException.get_body` to
|
||||||
|
return the description of this error in JSON format instead of HTML.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return json.dumps(dict(description=self.get_description(environ)))
|
||||||
|
|
||||||
|
def get_headers(self, environ):
|
||||||
|
"""Returns a list of headers including ``Content-Type:
|
||||||
|
application/json``.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return [('Content-Type', 'application/json')]
|
||||||
|
|
||||||
|
|
||||||
|
class JSONBadRequest(JSONHTTPException, BadRequest):
|
||||||
|
"""Represents an HTTP ``400 Bad Request`` error whose body contains an
|
||||||
|
error message in JSON format instead of HTML format (as in the superclass).
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
#: The description of the error which occurred as a string.
|
||||||
|
description = (
|
||||||
|
'The browser (or proxy) sent a request that this server could not '
|
||||||
|
'understand.'
|
||||||
|
)
|
||||||
Executable → Regular
Executable → Regular
Executable → Regular
+9
-1
@@ -20,9 +20,17 @@ def _lookup_object(name):
|
|||||||
return getattr(top, name)
|
return getattr(top, name)
|
||||||
|
|
||||||
|
|
||||||
|
def _find_app():
|
||||||
|
top = _app_ctx_stack.top
|
||||||
|
if top is None:
|
||||||
|
raise RuntimeError('working outside of application context')
|
||||||
|
return top.app
|
||||||
|
|
||||||
|
|
||||||
# context locals
|
# context locals
|
||||||
_request_ctx_stack = LocalStack()
|
_request_ctx_stack = LocalStack()
|
||||||
current_app = LocalProxy(partial(_lookup_object, 'app'))
|
_app_ctx_stack = LocalStack()
|
||||||
|
current_app = LocalProxy(_find_app)
|
||||||
request = LocalProxy(partial(_lookup_object, 'request'))
|
request = LocalProxy(partial(_lookup_object, 'request'))
|
||||||
session = LocalProxy(partial(_lookup_object, 'session'))
|
session = LocalProxy(partial(_lookup_object, 'session'))
|
||||||
g = LocalProxy(partial(_lookup_object, 'g'))
|
g = LocalProxy(partial(_lookup_object, 'g'))
|
||||||
|
|||||||
Executable → Regular
+234
-29
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
import imp
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import pkgutil
|
import pkgutil
|
||||||
@@ -20,7 +19,9 @@ import mimetypes
|
|||||||
from time import time
|
from time import time
|
||||||
from zlib import adler32
|
from zlib import adler32
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
|
from werkzeug.routing import BuildError
|
||||||
from werkzeug.urls import url_quote
|
from werkzeug.urls import url_quote
|
||||||
|
from functools import update_wrapper
|
||||||
|
|
||||||
# try to load the best simplejson implementation available. If JSON
|
# try to load the best simplejson implementation available. If JSON
|
||||||
# is not installed, we add a failing class.
|
# is not installed, we add a failing class.
|
||||||
@@ -50,7 +51,8 @@ except ImportError:
|
|||||||
|
|
||||||
from jinja2 import FileSystemLoader
|
from jinja2 import FileSystemLoader
|
||||||
|
|
||||||
from .globals import session, _request_ctx_stack, current_app, request
|
from .globals import session, _request_ctx_stack, _app_ctx_stack, \
|
||||||
|
current_app, request
|
||||||
|
|
||||||
|
|
||||||
def _assert_have_json():
|
def _assert_have_json():
|
||||||
@@ -59,7 +61,7 @@ def _assert_have_json():
|
|||||||
raise RuntimeError('simplejson not installed')
|
raise RuntimeError('simplejson not installed')
|
||||||
|
|
||||||
|
|
||||||
# figure out if simplejson escapes slashes. This behaviour was changed
|
# figure out if simplejson escapes slashes. This behavior was changed
|
||||||
# from one version to another without reason.
|
# from one version to another without reason.
|
||||||
if not json_available or '\\/' not in json.dumps('/'):
|
if not json_available or '\\/' not in json.dumps('/'):
|
||||||
|
|
||||||
@@ -91,6 +93,78 @@ def _endpoint_from_view_func(view_func):
|
|||||||
return view_func.__name__
|
return view_func.__name__
|
||||||
|
|
||||||
|
|
||||||
|
def stream_with_context(generator_or_function):
|
||||||
|
"""Request contexts disappear when the response is started on the server.
|
||||||
|
This is done for efficiency reasons and to make it less likely to encounter
|
||||||
|
memory leaks with badly written WSGI middlewares. The downside is that if
|
||||||
|
you are using streamed responses, the generator cannot access request bound
|
||||||
|
information any more.
|
||||||
|
|
||||||
|
This function however can help you keep the context around for longer::
|
||||||
|
|
||||||
|
from flask import stream_with_context, request, Response
|
||||||
|
|
||||||
|
@app.route('/stream')
|
||||||
|
def streamed_response():
|
||||||
|
@stream_with_context
|
||||||
|
def generate():
|
||||||
|
yield 'Hello '
|
||||||
|
yield request.args['name']
|
||||||
|
yield '!'
|
||||||
|
return Response(generate())
|
||||||
|
|
||||||
|
Alternatively it can also be used around a specific generator:
|
||||||
|
|
||||||
|
from flask import stream_with_context, request, Response
|
||||||
|
|
||||||
|
@app.route('/stream')
|
||||||
|
def streamed_response():
|
||||||
|
def generate():
|
||||||
|
yield 'Hello '
|
||||||
|
yield request.args['name']
|
||||||
|
yield '!'
|
||||||
|
return Response(stream_with_context(generate()))
|
||||||
|
|
||||||
|
.. versionadded:: 0.9
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
gen = iter(generator_or_function)
|
||||||
|
except TypeError:
|
||||||
|
def decorator(*args, **kwargs):
|
||||||
|
gen = generator_or_function()
|
||||||
|
return stream_with_context(gen)
|
||||||
|
return update_wrapper(decorator, generator_or_function)
|
||||||
|
|
||||||
|
def generator():
|
||||||
|
ctx = _request_ctx_stack.top
|
||||||
|
if ctx is None:
|
||||||
|
raise RuntimeError('Attempted to stream with context but '
|
||||||
|
'there was no context in the first place to keep around.')
|
||||||
|
with ctx:
|
||||||
|
# Dummy sentinel. Has to be inside the context block or we're
|
||||||
|
# not actually keeping the context around.
|
||||||
|
yield None
|
||||||
|
|
||||||
|
# The try/finally is here so that if someone passes a WSGI level
|
||||||
|
# iterator in we're still running the cleanup logic. Generators
|
||||||
|
# don't need that because they are closed on their destruction
|
||||||
|
# automatically.
|
||||||
|
try:
|
||||||
|
for item in gen:
|
||||||
|
yield item
|
||||||
|
finally:
|
||||||
|
if hasattr(gen, 'close'):
|
||||||
|
gen.close()
|
||||||
|
|
||||||
|
# The trick is to start the generator. Then the code execution runs until
|
||||||
|
# the first dummy None is yielded at which point the context was already
|
||||||
|
# pushed. This item is discarded. Then when the iteration continues the
|
||||||
|
# real generator is executed.
|
||||||
|
wrapped_g = generator()
|
||||||
|
wrapped_g.next()
|
||||||
|
return wrapped_g
|
||||||
|
|
||||||
|
|
||||||
def jsonify(*args, **kwargs):
|
def jsonify(*args, **kwargs):
|
||||||
"""Creates a :class:`~flask.Response` with the JSON representation of
|
"""Creates a :class:`~flask.Response` with the JSON representation of
|
||||||
the given arguments with an `application/json` mimetype. The arguments
|
the given arguments with an `application/json` mimetype. The arguments
|
||||||
@@ -188,36 +262,106 @@ def url_for(endpoint, **values):
|
|||||||
|
|
||||||
For more information, head over to the :ref:`Quickstart <url-building>`.
|
For more information, head over to the :ref:`Quickstart <url-building>`.
|
||||||
|
|
||||||
|
To integrate applications, :class:`Flask` has a hook to intercept URL build
|
||||||
|
errors through :attr:`Flask.build_error_handler`. The `url_for` function
|
||||||
|
results in a :exc:`~werkzeug.routing.BuildError` when the current app does
|
||||||
|
not have a URL for the given endpoint and values. When it does, the
|
||||||
|
:data:`~flask.current_app` calls its :attr:`~Flask.build_error_handler` if
|
||||||
|
it is not `None`, which can return a string to use as the result of
|
||||||
|
`url_for` (instead of `url_for`'s default to raise the
|
||||||
|
:exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception.
|
||||||
|
An example::
|
||||||
|
|
||||||
|
def external_url_handler(error, endpoint, **values):
|
||||||
|
"Looks up an external URL when `url_for` cannot build a URL."
|
||||||
|
# This is an example of hooking the build_error_handler.
|
||||||
|
# Here, lookup_url is some utility function you've built
|
||||||
|
# which looks up the endpoint in some external URL registry.
|
||||||
|
url = lookup_url(endpoint, **values)
|
||||||
|
if url is None:
|
||||||
|
# External lookup did not have a URL.
|
||||||
|
# Re-raise the BuildError, in context of original traceback.
|
||||||
|
exc_type, exc_value, tb = sys.exc_info()
|
||||||
|
if exc_value is error:
|
||||||
|
raise exc_type, exc_value, tb
|
||||||
|
else:
|
||||||
|
raise error
|
||||||
|
# url_for will use this result, instead of raising BuildError.
|
||||||
|
return url
|
||||||
|
|
||||||
|
app.build_error_handler = external_url_handler
|
||||||
|
|
||||||
|
Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and
|
||||||
|
`endpoint` and `**values` are the arguments passed into `url_for`. Note
|
||||||
|
that this is for building URLs outside the current application, and not for
|
||||||
|
handling 404 NotFound errors.
|
||||||
|
|
||||||
.. versionadded:: 0.9
|
.. versionadded:: 0.9
|
||||||
The `_anchor` and `_method` parameters were added.
|
The `_anchor` and `_method` parameters were added.
|
||||||
|
|
||||||
|
.. versionadded:: 0.9
|
||||||
|
Calls :meth:`Flask.handle_build_error` on
|
||||||
|
:exc:`~werkzeug.routing.BuildError`.
|
||||||
|
|
||||||
:param endpoint: the endpoint of the URL (name of the function)
|
:param endpoint: the endpoint of the URL (name of the function)
|
||||||
:param values: the variable arguments of the URL rule
|
:param values: the variable arguments of the URL rule
|
||||||
:param _external: if set to `True`, an absolute URL is generated.
|
:param _external: if set to `True`, an absolute URL is generated.
|
||||||
:param _anchor: if provided this is added as anchor to the URL.
|
:param _anchor: if provided this is added as anchor to the URL.
|
||||||
:param _method: if provided this explicitly specifies an HTTP method.
|
:param _method: if provided this explicitly specifies an HTTP method.
|
||||||
"""
|
"""
|
||||||
ctx = _request_ctx_stack.top
|
appctx = _app_ctx_stack.top
|
||||||
blueprint_name = request.blueprint
|
reqctx = _request_ctx_stack.top
|
||||||
if not ctx.request._is_old_module:
|
if appctx is None:
|
||||||
if endpoint[:1] == '.':
|
raise RuntimeError('Attempted to generate a URL with the application '
|
||||||
if blueprint_name is not None:
|
'context being pushed. This has to be executed ')
|
||||||
endpoint = blueprint_name + endpoint
|
|
||||||
else:
|
# If request specific information is available we have some extra
|
||||||
|
# features that support "relative" urls.
|
||||||
|
if reqctx is not None:
|
||||||
|
url_adapter = reqctx.url_adapter
|
||||||
|
blueprint_name = request.blueprint
|
||||||
|
if not reqctx.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:]
|
endpoint = endpoint[1:]
|
||||||
|
external = values.pop('_external', False)
|
||||||
|
|
||||||
|
# Otherwise go with the url adapter from the appctx and make
|
||||||
|
# the urls external by default.
|
||||||
else:
|
else:
|
||||||
# TODO: get rid of this deprecated functionality in 1.0
|
url_adapter = appctx.url_adapter
|
||||||
if '.' not in endpoint:
|
if url_adapter is None:
|
||||||
if blueprint_name is not None:
|
raise RuntimeError('Application was not able to create a URL '
|
||||||
endpoint = blueprint_name + '.' + endpoint
|
'adapter for request independent URL generation. '
|
||||||
elif endpoint.startswith('.'):
|
'You might be able to fix this by setting '
|
||||||
endpoint = endpoint[1:]
|
'the SERVER_NAME config variable.')
|
||||||
external = values.pop('_external', False)
|
external = values.pop('_external', True)
|
||||||
|
|
||||||
anchor = values.pop('_anchor', None)
|
anchor = values.pop('_anchor', None)
|
||||||
method = values.pop('_method', None)
|
method = values.pop('_method', None)
|
||||||
ctx.app.inject_url_defaults(endpoint, values)
|
appctx.app.inject_url_defaults(endpoint, values)
|
||||||
rv = ctx.url_adapter.build(endpoint, values, method=method,
|
try:
|
||||||
|
rv = url_adapter.build(endpoint, values, method=method,
|
||||||
force_external=external)
|
force_external=external)
|
||||||
|
except BuildError, error:
|
||||||
|
# We need to inject the values again so that the app callback can
|
||||||
|
# deal with that sort of stuff.
|
||||||
|
values['_external'] = external
|
||||||
|
values['_anchor'] = anchor
|
||||||
|
values['_method'] = method
|
||||||
|
return appctx.app.handle_url_build_error(error, endpoint, values)
|
||||||
|
|
||||||
|
rv = url_adapter.build(endpoint, values, method=method,
|
||||||
|
force_external=external)
|
||||||
if anchor is not None:
|
if anchor is not None:
|
||||||
rv += '#' + url_quote(anchor)
|
rv += '#' + url_quote(anchor)
|
||||||
return rv
|
return rv
|
||||||
@@ -261,7 +405,16 @@ def flash(message, category='message'):
|
|||||||
messages and ``'warning'`` for warnings. However any
|
messages and ``'warning'`` for warnings. However any
|
||||||
kind of string can be used as category.
|
kind of string can be used as category.
|
||||||
"""
|
"""
|
||||||
session.setdefault('_flashes', []).append((category, message))
|
# Original implementation:
|
||||||
|
#
|
||||||
|
# session.setdefault('_flashes', []).append((category, message))
|
||||||
|
#
|
||||||
|
# This assumed that changes made to mutable structures in the session are
|
||||||
|
# are always in sync with the sess on object, which is not true for session
|
||||||
|
# implementations that use external storage for keeping their keys/values.
|
||||||
|
flashes = session.get('_flashes', [])
|
||||||
|
flashes.append((category, message))
|
||||||
|
session['_flashes'] = flashes
|
||||||
|
|
||||||
|
|
||||||
def get_flashed_messages(with_categories=False, category_filter=[]):
|
def get_flashed_messages(with_categories=False, category_filter=[]):
|
||||||
@@ -305,7 +458,7 @@ def get_flashed_messages(with_categories=False, category_filter=[]):
|
|||||||
|
|
||||||
def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
||||||
attachment_filename=None, add_etags=True,
|
attachment_filename=None, add_etags=True,
|
||||||
cache_timeout=60 * 60 * 12, conditional=False):
|
cache_timeout=None, conditional=False):
|
||||||
"""Sends the contents of a file to the client. This will use the
|
"""Sends the contents of a file to the client. This will use the
|
||||||
most efficient method available and configured. By default it will
|
most efficient method available and configured. By default it will
|
||||||
try to use the WSGI server's file_wrapper support. Alternatively
|
try to use the WSGI server's file_wrapper support. Alternatively
|
||||||
@@ -330,7 +483,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||||||
|
|
||||||
.. versionadded:: 0.5
|
.. versionadded:: 0.5
|
||||||
The `add_etags`, `cache_timeout` and `conditional` parameters were
|
The `add_etags`, `cache_timeout` and `conditional` parameters were
|
||||||
added. The default behaviour is now to attach etags.
|
added. The default behavior is now to attach etags.
|
||||||
|
|
||||||
.. versionchanged:: 0.7
|
.. versionchanged:: 0.7
|
||||||
mimetype guessing and etag support for file objects was
|
mimetype guessing and etag support for file objects was
|
||||||
@@ -338,6 +491,9 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||||||
able to, otherwise attach an etag yourself. This functionality
|
able to, otherwise attach an etag yourself. This functionality
|
||||||
will be removed in Flask 1.0
|
will be removed in Flask 1.0
|
||||||
|
|
||||||
|
.. versionchanged:: 0.9
|
||||||
|
cache_timeout pulls its default from application config, when None.
|
||||||
|
|
||||||
:param filename_or_fp: the filename of the file to send. This is
|
:param filename_or_fp: the filename of the file to send. This is
|
||||||
relative to the :attr:`~Flask.root_path` if a
|
relative to the :attr:`~Flask.root_path` if a
|
||||||
relative path is specified.
|
relative path is specified.
|
||||||
@@ -354,7 +510,11 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||||||
differs from the file's filename.
|
differs from the file's filename.
|
||||||
:param add_etags: set to `False` to disable attaching of etags.
|
:param add_etags: set to `False` to disable attaching of etags.
|
||||||
:param conditional: set to `True` to enable conditional responses.
|
:param conditional: set to `True` to enable conditional responses.
|
||||||
:param cache_timeout: the timeout in seconds for the headers.
|
|
||||||
|
:param cache_timeout: the timeout in seconds for the headers. When `None`
|
||||||
|
(default), this value is set by
|
||||||
|
:meth:`~Flask.get_send_file_max_age` of
|
||||||
|
:data:`~flask.current_app`.
|
||||||
"""
|
"""
|
||||||
mtime = None
|
mtime = None
|
||||||
if isinstance(filename_or_fp, basestring):
|
if isinstance(filename_or_fp, basestring):
|
||||||
@@ -365,7 +525,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||||||
file = filename_or_fp
|
file = filename_or_fp
|
||||||
filename = getattr(file, 'name', None)
|
filename = getattr(file, 'name', None)
|
||||||
|
|
||||||
# XXX: this behaviour is now deprecated because it was unreliable.
|
# XXX: this behavior is now deprecated because it was unreliable.
|
||||||
# removed in Flask 1.0
|
# removed in Flask 1.0
|
||||||
if not attachment_filename and not mimetype \
|
if not attachment_filename and not mimetype \
|
||||||
and isinstance(filename, basestring):
|
and isinstance(filename, basestring):
|
||||||
@@ -376,7 +536,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||||||
if add_etags:
|
if add_etags:
|
||||||
warn(DeprecationWarning('In future flask releases etags will no '
|
warn(DeprecationWarning('In future flask releases etags will no '
|
||||||
'longer be generated for file objects passed to the send_file '
|
'longer be generated for file objects passed to the send_file '
|
||||||
'function because this behaviour was unreliable. Pass '
|
'function because this behavior was unreliable. Pass '
|
||||||
'filenames instead if possible, otherwise attach an etag '
|
'filenames instead if possible, otherwise attach an etag '
|
||||||
'yourself based on another value'), stacklevel=2)
|
'yourself based on another value'), stacklevel=2)
|
||||||
|
|
||||||
@@ -418,7 +578,9 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||||||
rv.last_modified = int(mtime)
|
rv.last_modified = int(mtime)
|
||||||
|
|
||||||
rv.cache_control.public = True
|
rv.cache_control.public = True
|
||||||
if cache_timeout:
|
if cache_timeout is None:
|
||||||
|
cache_timeout = current_app.get_send_file_max_age(filename)
|
||||||
|
if cache_timeout is not None:
|
||||||
rv.cache_control.max_age = cache_timeout
|
rv.cache_control.max_age = cache_timeout
|
||||||
rv.expires = int(time() + cache_timeout)
|
rv.expires = int(time() + cache_timeout)
|
||||||
|
|
||||||
@@ -495,7 +657,8 @@ def send_from_directory(directory, filename, **options):
|
|||||||
filename = safe_join(directory, filename)
|
filename = safe_join(directory, filename)
|
||||||
if not os.path.isfile(filename):
|
if not os.path.isfile(filename):
|
||||||
raise NotFound()
|
raise NotFound()
|
||||||
return send_file(filename, conditional=True, **options)
|
options.setdefault('conditional', True)
|
||||||
|
return send_file(filename, **options)
|
||||||
|
|
||||||
|
|
||||||
def get_root_path(import_name):
|
def get_root_path(import_name):
|
||||||
@@ -504,17 +667,29 @@ def get_root_path(import_name):
|
|||||||
|
|
||||||
Not to be confused with the package path returned by :func:`find_package`.
|
Not to be confused with the package path returned by :func:`find_package`.
|
||||||
"""
|
"""
|
||||||
|
# Module already imported and has a file attribute. Use that first.
|
||||||
|
mod = sys.modules.get(import_name)
|
||||||
|
if mod is not None and hasattr(mod, '__file__'):
|
||||||
|
return os.path.dirname(os.path.abspath(mod.__file__))
|
||||||
|
|
||||||
|
# Next attempt: check the loader.
|
||||||
loader = pkgutil.get_loader(import_name)
|
loader = pkgutil.get_loader(import_name)
|
||||||
|
|
||||||
|
# Loader does not exist or we're referring to an unloaded main module
|
||||||
|
# or a main module without path (interactive sessions), go with the
|
||||||
|
# current working directory.
|
||||||
if loader is None or import_name == '__main__':
|
if loader is None or import_name == '__main__':
|
||||||
# import name is not found, or interactive/main module
|
|
||||||
return os.getcwd()
|
return os.getcwd()
|
||||||
|
|
||||||
# For .egg, zipimporter does not have get_filename until Python 2.7.
|
# For .egg, zipimporter does not have get_filename until Python 2.7.
|
||||||
|
# Some other loaders might exhibit the same behavior.
|
||||||
if hasattr(loader, 'get_filename'):
|
if hasattr(loader, 'get_filename'):
|
||||||
filepath = loader.get_filename(import_name)
|
filepath = loader.get_filename(import_name)
|
||||||
else:
|
else:
|
||||||
# Fall back to imports.
|
# Fall back to imports.
|
||||||
__import__(import_name)
|
__import__(import_name)
|
||||||
filepath = sys.modules[import_name].__file__
|
filepath = sys.modules[import_name].__file__
|
||||||
|
|
||||||
# filepath is import_name.py for a module, or __init__.py for a package.
|
# filepath is import_name.py for a module, or __init__.py for a package.
|
||||||
return os.path.dirname(os.path.abspath(filepath))
|
return os.path.dirname(os.path.abspath(filepath))
|
||||||
|
|
||||||
@@ -651,6 +826,32 @@ class _PackageBoundObject(object):
|
|||||||
return FileSystemLoader(os.path.join(self.root_path,
|
return FileSystemLoader(os.path.join(self.root_path,
|
||||||
self.template_folder))
|
self.template_folder))
|
||||||
|
|
||||||
|
def get_send_file_max_age(self, filename):
|
||||||
|
"""Provides default cache_timeout for the :func:`send_file` functions.
|
||||||
|
|
||||||
|
By default, this function returns ``SEND_FILE_MAX_AGE_DEFAULT`` from
|
||||||
|
the configuration of :data:`~flask.current_app`.
|
||||||
|
|
||||||
|
Static file functions such as :func:`send_from_directory` use this
|
||||||
|
function, and :func:`send_file` calls this function on
|
||||||
|
:data:`~flask.current_app` when the given cache_timeout is `None`. If a
|
||||||
|
cache_timeout is given in :func:`send_file`, that timeout is used;
|
||||||
|
otherwise, this method is called.
|
||||||
|
|
||||||
|
This allows subclasses to change the behavior when sending files based
|
||||||
|
on the filename. For example, to set the cache timeout for .js files
|
||||||
|
to 60 seconds::
|
||||||
|
|
||||||
|
class MyFlask(flask.Flask):
|
||||||
|
def get_send_file_max_age(self, name):
|
||||||
|
if name.lower().endswith('.js'):
|
||||||
|
return 60
|
||||||
|
return flask.Flask.get_send_file_max_age(self, name)
|
||||||
|
|
||||||
|
.. versionadded:: 0.9
|
||||||
|
"""
|
||||||
|
return current_app.config['SEND_FILE_MAX_AGE_DEFAULT']
|
||||||
|
|
||||||
def send_static_file(self, filename):
|
def send_static_file(self, filename):
|
||||||
"""Function used internally to send static files from the static
|
"""Function used internally to send static files from the static
|
||||||
folder to the browser.
|
folder to the browser.
|
||||||
@@ -659,7 +860,11 @@ class _PackageBoundObject(object):
|
|||||||
"""
|
"""
|
||||||
if not self.has_static_folder:
|
if not self.has_static_folder:
|
||||||
raise RuntimeError('No static folder for this object')
|
raise RuntimeError('No static folder for this object')
|
||||||
return send_from_directory(self.static_folder, filename)
|
# Ensure get_send_file_max_age is called in all cases.
|
||||||
|
# Here, we ensure get_send_file_max_age is called for Blueprints.
|
||||||
|
cache_timeout = self.get_send_file_max_age(filename)
|
||||||
|
return send_from_directory(self.static_folder, filename,
|
||||||
|
cache_timeout=cache_timeout)
|
||||||
|
|
||||||
def open_resource(self, resource, mode='rb'):
|
def open_resource(self, resource, mode='rb'):
|
||||||
"""Opens a resource from the application's resource folder. To see
|
"""Opens a resource from the application's resource folder. To see
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
+1
@@ -49,3 +49,4 @@ request_started = _signals.signal('request-started')
|
|||||||
request_finished = _signals.signal('request-finished')
|
request_finished = _signals.signal('request-finished')
|
||||||
request_tearing_down = _signals.signal('request-tearing-down')
|
request_tearing_down = _signals.signal('request-tearing-down')
|
||||||
got_request_exception = _signals.signal('got-request-exception')
|
got_request_exception = _signals.signal('got-request-exception')
|
||||||
|
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')
|
||||||
|
|||||||
Executable → Regular
+5
-3
@@ -109,17 +109,19 @@ def _render(template, context, app):
|
|||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
def render_template(template_name, **context):
|
def render_template(template_name_or_list, **context):
|
||||||
"""Renders a template from the template folder with the given
|
"""Renders a template from the template folder with the given
|
||||||
context.
|
context.
|
||||||
|
|
||||||
:param template_name: the name of the template to be rendered
|
:param template_name_or_list: the name of the template to be
|
||||||
|
rendered, or an iterable with template names
|
||||||
|
the first one existing will be rendered
|
||||||
:param context: the variables that should be available in the
|
:param context: the variables that should be available in the
|
||||||
context of the template.
|
context of the template.
|
||||||
"""
|
"""
|
||||||
ctx = _request_ctx_stack.top
|
ctx = _request_ctx_stack.top
|
||||||
ctx.app.update_template_context(context)
|
ctx.app.update_template_context(context)
|
||||||
return _render(ctx.app.jinja_env.get_template(template_name),
|
return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list),
|
||||||
context, ctx.app)
|
context, ctx.app)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
+1
-1
@@ -107,7 +107,7 @@ class MethodViewType(type):
|
|||||||
rv = type.__new__(cls, name, bases, d)
|
rv = type.__new__(cls, name, bases, d)
|
||||||
if 'methods' not in d:
|
if 'methods' not in d:
|
||||||
methods = set(rv.methods or [])
|
methods = set(rv.methods or [])
|
||||||
for key, value in d.iteritems():
|
for key in d:
|
||||||
if key in http_method_funcs:
|
if key in http_method_funcs:
|
||||||
methods.add(key.upper())
|
methods.add(key.upper())
|
||||||
# if we have no method at all in there we don't want to
|
# if we have no method at all in there we don't want to
|
||||||
|
|||||||
Executable → Regular
+14
-4
@@ -10,9 +10,9 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase
|
from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase
|
||||||
from werkzeug.exceptions import BadRequest
|
|
||||||
from werkzeug.utils import cached_property
|
from werkzeug.utils import cached_property
|
||||||
|
|
||||||
|
from .exceptions import JSONBadRequest
|
||||||
from .debughelpers import attach_enctype_error_multidict
|
from .debughelpers import attach_enctype_error_multidict
|
||||||
from .helpers import json, _assert_have_json
|
from .helpers import json, _assert_have_json
|
||||||
from .globals import _request_ctx_stack
|
from .globals import _request_ctx_stack
|
||||||
@@ -108,12 +108,22 @@ class Request(RequestBase):
|
|||||||
|
|
||||||
def on_json_loading_failed(self, e):
|
def on_json_loading_failed(self, e):
|
||||||
"""Called if decoding of the JSON data failed. The return value of
|
"""Called if decoding of the JSON data failed. The return value of
|
||||||
this method is used by :attr:`json` when an error ocurred. The
|
this method is used by :attr:`json` when an error ocurred. The default
|
||||||
default implementation raises a :class:`~werkzeug.exceptions.BadRequest`.
|
implementation raises a :class:`JSONBadRequest`, which is a subclass of
|
||||||
|
:class:`~werkzeug.exceptions.BadRequest` which sets the
|
||||||
|
``Content-Type`` to ``application/json`` and provides a JSON-formatted
|
||||||
|
error description::
|
||||||
|
|
||||||
|
{"description": "The browser (or proxy) sent a request that \
|
||||||
|
this server could not understand."}
|
||||||
|
|
||||||
|
.. versionchanged:: 0.9
|
||||||
|
Return a :class:`JSONBadRequest` instead of a
|
||||||
|
:class:`~werkzeug.exceptions.BadRequest` by default.
|
||||||
|
|
||||||
.. versionadded:: 0.8
|
.. versionadded:: 0.8
|
||||||
"""
|
"""
|
||||||
raise BadRequest()
|
raise JSONBadRequest()
|
||||||
|
|
||||||
def _load_form_data(self):
|
def _load_form_data(self):
|
||||||
RequestBase._load_form_data(self)
|
RequestBase._load_form_data(self)
|
||||||
|
|||||||
Executable → Regular
+1
-1
@@ -27,7 +27,7 @@
|
|||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__version__ = '2.7-dev'
|
__version__ = '2.6'
|
||||||
|
|
||||||
# high level interface
|
# high level interface
|
||||||
from jinja2.environment import Environment, Template
|
from jinja2.environment import Environment, Template
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
+1
-1
@@ -77,7 +77,7 @@ def make_frame_proxy(frame):
|
|||||||
|
|
||||||
|
|
||||||
class ProcessedTraceback(object):
|
class ProcessedTraceback(object):
|
||||||
"""Holds a Jinja preprocessed traceback for printing or reraising."""
|
"""Holds a Jinja preprocessed traceback for priting or reraising."""
|
||||||
|
|
||||||
def __init__(self, exc_type, exc_value, frames):
|
def __init__(self, exc_type, exc_value, frames):
|
||||||
assert frames, 'no frames for this traceback?'
|
assert frames, 'no frames for this traceback?'
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
+5
-5
@@ -67,7 +67,7 @@ def copy_cache(cache):
|
|||||||
|
|
||||||
def load_extensions(environment, extensions):
|
def load_extensions(environment, extensions):
|
||||||
"""Load the extensions from the list and bind it to the environment.
|
"""Load the extensions from the list and bind it to the environment.
|
||||||
Returns a dict of instantiated environments.
|
Returns a dict of instanciated environments.
|
||||||
"""
|
"""
|
||||||
result = {}
|
result = {}
|
||||||
for extension in extensions:
|
for extension in extensions:
|
||||||
@@ -239,7 +239,7 @@ class Environment(object):
|
|||||||
# passed by keyword rather than position. However it's important to
|
# passed by keyword rather than position. However it's important to
|
||||||
# not change the order of arguments because it's used at least
|
# not change the order of arguments because it's used at least
|
||||||
# internally in those cases:
|
# internally in those cases:
|
||||||
# - spontaneous environments (i18n extension and Template)
|
# - spontaneus environments (i18n extension and Template)
|
||||||
# - unittests
|
# - unittests
|
||||||
# If parameter changes are required only add parameters at the end
|
# If parameter changes are required only add parameters at the end
|
||||||
# and don't change the arguments (or the defaults!) of the arguments
|
# and don't change the arguments (or the defaults!) of the arguments
|
||||||
@@ -435,7 +435,7 @@ class Environment(object):
|
|||||||
return stream
|
return stream
|
||||||
|
|
||||||
def _generate(self, source, name, filename, defer_init=False):
|
def _generate(self, source, name, filename, defer_init=False):
|
||||||
"""Internal hook that can be overridden to hook a different generate
|
"""Internal hook that can be overriden to hook a different generate
|
||||||
method in.
|
method in.
|
||||||
|
|
||||||
.. versionadded:: 2.5
|
.. versionadded:: 2.5
|
||||||
@@ -443,7 +443,7 @@ class Environment(object):
|
|||||||
return generate(source, self, name, filename, defer_init=defer_init)
|
return generate(source, self, name, filename, defer_init=defer_init)
|
||||||
|
|
||||||
def _compile(self, source, filename):
|
def _compile(self, source, filename):
|
||||||
"""Internal hook that can be overridden to hook a different compile
|
"""Internal hook that can be overriden to hook a different compile
|
||||||
method in.
|
method in.
|
||||||
|
|
||||||
.. versionadded:: 2.5
|
.. versionadded:: 2.5
|
||||||
@@ -1053,7 +1053,7 @@ class TemplateStream(object):
|
|||||||
def dump(self, fp, encoding=None, errors='strict'):
|
def dump(self, fp, encoding=None, errors='strict'):
|
||||||
"""Dump the complete stream into a file or file-like object.
|
"""Dump the complete stream into a file or file-like object.
|
||||||
Per default unicode strings are written, if you want to encode
|
Per default unicode strings are written, if you want to encode
|
||||||
before writing specify an `encoding`.
|
before writing specifiy an `encoding`.
|
||||||
|
|
||||||
Example usage::
|
Example usage::
|
||||||
|
|
||||||
|
|||||||
Executable → Regular
+1
-1
@@ -62,7 +62,7 @@ class TemplatesNotFound(TemplateNotFound):
|
|||||||
|
|
||||||
def __init__(self, names=(), message=None):
|
def __init__(self, names=(), message=None):
|
||||||
if message is None:
|
if message is None:
|
||||||
message = u'none of the templates given were found: ' + \
|
message = u'non of the templates given were found: ' + \
|
||||||
u', '.join(map(unicode, names))
|
u', '.join(map(unicode, names))
|
||||||
TemplateNotFound.__init__(self, names and names[-1] or None, message)
|
TemplateNotFound.__init__(self, names and names[-1] or None, message)
|
||||||
self.templates = list(names)
|
self.templates = list(names)
|
||||||
|
|||||||
Executable → Regular
+1
-9
@@ -552,10 +552,6 @@ def babel_extract(fileobj, keywords, comment_tags, options):
|
|||||||
The `newstyle_gettext` flag can be set to `True` to enable newstyle
|
The `newstyle_gettext` flag can be set to `True` to enable newstyle
|
||||||
gettext calls.
|
gettext calls.
|
||||||
|
|
||||||
.. versionchanged:: 2.7
|
|
||||||
A `silent` option can now be provided. If set to `False` template
|
|
||||||
syntax errors are propagated instead of being ignored.
|
|
||||||
|
|
||||||
:param fileobj: the file-like object the messages should be extracted from
|
:param fileobj: the file-like object the messages should be extracted from
|
||||||
:param keywords: a list of keywords (i.e. function names) that should be
|
:param keywords: a list of keywords (i.e. function names) that should be
|
||||||
recognized as translation functions
|
recognized as translation functions
|
||||||
@@ -575,10 +571,8 @@ def babel_extract(fileobj, keywords, comment_tags, options):
|
|||||||
extensions.add(InternationalizationExtension)
|
extensions.add(InternationalizationExtension)
|
||||||
|
|
||||||
def getbool(options, key, default=False):
|
def getbool(options, key, default=False):
|
||||||
return options.get(key, str(default)).lower() in \
|
options.get(key, str(default)).lower() in ('1', 'on', 'yes', 'true')
|
||||||
('1', 'on', 'yes', 'true')
|
|
||||||
|
|
||||||
silent = getbool(options, 'silent', True)
|
|
||||||
environment = Environment(
|
environment = Environment(
|
||||||
options.get('block_start_string', BLOCK_START_STRING),
|
options.get('block_start_string', BLOCK_START_STRING),
|
||||||
options.get('block_end_string', BLOCK_END_STRING),
|
options.get('block_end_string', BLOCK_END_STRING),
|
||||||
@@ -602,8 +596,6 @@ def babel_extract(fileobj, keywords, comment_tags, options):
|
|||||||
node = environment.parse(source)
|
node = environment.parse(source)
|
||||||
tokens = list(environment.lex(environment.preprocess(source)))
|
tokens = list(environment.lex(environment.preprocess(source)))
|
||||||
except TemplateSyntaxError, e:
|
except TemplateSyntaxError, e:
|
||||||
if not silent:
|
|
||||||
raise
|
|
||||||
# skip templates with syntax errors
|
# skip templates with syntax errors
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
Executable → Regular
+18
-45
@@ -13,10 +13,9 @@ import math
|
|||||||
from random import choice
|
from random import choice
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from itertools import imap, groupby
|
from itertools import imap, groupby
|
||||||
from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \
|
from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode
|
||||||
unicode_urlencode
|
|
||||||
from jinja2.runtime import Undefined
|
from jinja2.runtime import Undefined
|
||||||
from jinja2.exceptions import FilterArgumentError
|
from jinja2.exceptions import FilterArgumentError, SecurityError
|
||||||
|
|
||||||
|
|
||||||
_word_re = re.compile(r'\w+(?u)')
|
_word_re = re.compile(r'\w+(?u)')
|
||||||
@@ -71,26 +70,6 @@ def do_forceescape(value):
|
|||||||
return escape(unicode(value))
|
return escape(unicode(value))
|
||||||
|
|
||||||
|
|
||||||
def do_urlencode(value):
|
|
||||||
"""Escape strings for use in URLs (uses UTF-8 encoding). It accepts both
|
|
||||||
dictionaries and regular strings as well as pairwise iterables.
|
|
||||||
|
|
||||||
.. versionadded:: 2.7
|
|
||||||
"""
|
|
||||||
itemiter = None
|
|
||||||
if isinstance(value, dict):
|
|
||||||
itemiter = value.iteritems()
|
|
||||||
elif not isinstance(value, basestring):
|
|
||||||
try:
|
|
||||||
itemiter = iter(value)
|
|
||||||
except TypeError:
|
|
||||||
pass
|
|
||||||
if itemiter is None:
|
|
||||||
return unicode_urlencode(value)
|
|
||||||
return u'&'.join(unicode_urlencode(k) + '=' +
|
|
||||||
unicode_urlencode(v) for k, v in itemiter)
|
|
||||||
|
|
||||||
|
|
||||||
@evalcontextfilter
|
@evalcontextfilter
|
||||||
def do_replace(eval_ctx, s, old, new, count=None):
|
def do_replace(eval_ctx, s, old, new, count=None):
|
||||||
"""Return a copy of the value with all occurrences of a substring
|
"""Return a copy of the value with all occurrences of a substring
|
||||||
@@ -176,12 +155,7 @@ def do_title(s):
|
|||||||
"""Return a titlecased version of the value. I.e. words will start with
|
"""Return a titlecased version of the value. I.e. words will start with
|
||||||
uppercase letters, all remaining characters are lowercase.
|
uppercase letters, all remaining characters are lowercase.
|
||||||
"""
|
"""
|
||||||
rv = []
|
return soft_unicode(s).title()
|
||||||
for item in re.compile(r'([-\s]+)(?u)').split(s):
|
|
||||||
if not item:
|
|
||||||
continue
|
|
||||||
rv.append(item[0].upper() + item[1:])
|
|
||||||
return ''.join(rv)
|
|
||||||
|
|
||||||
|
|
||||||
def do_dictsort(value, case_sensitive=False, by='key'):
|
def do_dictsort(value, case_sensitive=False, by='key'):
|
||||||
@@ -372,25 +346,25 @@ def do_filesizeformat(value, binary=False):
|
|||||||
bytes = float(value)
|
bytes = float(value)
|
||||||
base = binary and 1024 or 1000
|
base = binary and 1024 or 1000
|
||||||
prefixes = [
|
prefixes = [
|
||||||
(binary and 'KiB' or 'kB'),
|
(binary and "KiB" or "kB"),
|
||||||
(binary and 'MiB' or 'MB'),
|
(binary and "MiB" or "MB"),
|
||||||
(binary and 'GiB' or 'GB'),
|
(binary and "GiB" or "GB"),
|
||||||
(binary and 'TiB' or 'TB'),
|
(binary and "TiB" or "TB"),
|
||||||
(binary and 'PiB' or 'PB'),
|
(binary and "PiB" or "PB"),
|
||||||
(binary and 'EiB' or 'EB'),
|
(binary and "EiB" or "EB"),
|
||||||
(binary and 'ZiB' or 'ZB'),
|
(binary and "ZiB" or "ZB"),
|
||||||
(binary and 'YiB' or 'YB')
|
(binary and "YiB" or "YB")
|
||||||
]
|
]
|
||||||
if bytes == 1:
|
if bytes == 1:
|
||||||
return '1 Byte'
|
return "1 Byte"
|
||||||
elif bytes < base:
|
elif bytes < base:
|
||||||
return '%d Bytes' % bytes
|
return "%d Bytes" % bytes
|
||||||
else:
|
else:
|
||||||
for i, prefix in enumerate(prefixes):
|
for i, prefix in enumerate(prefixes):
|
||||||
unit = base ** (i + 2)
|
unit = base * base ** (i + 1)
|
||||||
if bytes < unit:
|
if bytes < unit:
|
||||||
return '%.1f %s' % ((base * bytes / unit), prefix)
|
return "%.1f %s" % ((bytes / unit), prefix)
|
||||||
return '%.1f %s' % ((base * bytes / unit), prefix)
|
return "%.1f %s" % ((bytes / unit), prefix)
|
||||||
|
|
||||||
|
|
||||||
def do_pprint(value, verbose=False):
|
def do_pprint(value, verbose=False):
|
||||||
@@ -583,7 +557,7 @@ def do_batch(value, linecount, fill_with=None):
|
|||||||
A filter that batches items. It works pretty much like `slice`
|
A filter that batches items. It works pretty much like `slice`
|
||||||
just the other way round. It returns a list of lists with the
|
just the other way round. It returns a list of lists with the
|
||||||
given number of items. If you provide a second parameter this
|
given number of items. If you provide a second parameter this
|
||||||
is used to fill up missing items. See this example:
|
is used to fill missing items. See this example:
|
||||||
|
|
||||||
.. sourcecode:: html+jinja
|
.. sourcecode:: html+jinja
|
||||||
|
|
||||||
@@ -823,6 +797,5 @@ FILTERS = {
|
|||||||
'round': do_round,
|
'round': do_round,
|
||||||
'groupby': do_groupby,
|
'groupby': do_groupby,
|
||||||
'safe': do_mark_safe,
|
'safe': do_mark_safe,
|
||||||
'xmlattr': do_xmlattr,
|
'xmlattr': do_xmlattr
|
||||||
'urlencode': do_urlencode
|
|
||||||
}
|
}
|
||||||
|
|||||||
Executable → Regular
+4
-4
@@ -414,7 +414,7 @@ class Lexer(object):
|
|||||||
(operator_re, TOKEN_OPERATOR, None)
|
(operator_re, TOKEN_OPERATOR, None)
|
||||||
]
|
]
|
||||||
|
|
||||||
# assemble the root lexing rule. because "|" is ungreedy
|
# assamble the root lexing rule. because "|" is ungreedy
|
||||||
# we have to sort by length so that the lexer continues working
|
# we have to sort by length so that the lexer continues working
|
||||||
# as expected when we have parsing rules like <% for block and
|
# as expected when we have parsing rules like <% for block and
|
||||||
# <%= for variables. (if someone wants asp like syntax)
|
# <%= for variables. (if someone wants asp like syntax)
|
||||||
@@ -491,7 +491,7 @@ class Lexer(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _normalize_newlines(self, value):
|
def _normalize_newlines(self, value):
|
||||||
"""Called for strings and template data to normalize it to unicode."""
|
"""Called for strings and template data to normlize it to unicode."""
|
||||||
return newline_re.sub(self.newline_sequence, value)
|
return newline_re.sub(self.newline_sequence, value)
|
||||||
|
|
||||||
def tokenize(self, source, name=None, filename=None, state=None):
|
def tokenize(self, source, name=None, filename=None, state=None):
|
||||||
@@ -571,7 +571,7 @@ class Lexer(object):
|
|||||||
if m is None:
|
if m is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# we only match blocks and variables if braces / parentheses
|
# we only match blocks and variables if brances / parentheses
|
||||||
# are balanced. continue parsing with the lower rule which
|
# are balanced. continue parsing with the lower rule which
|
||||||
# is the operator rule. do this only if the end tags look
|
# is the operator rule. do this only if the end tags look
|
||||||
# like operators
|
# like operators
|
||||||
@@ -669,7 +669,7 @@ class Lexer(object):
|
|||||||
# publish new function and start again
|
# publish new function and start again
|
||||||
pos = pos2
|
pos = pos2
|
||||||
break
|
break
|
||||||
# if loop terminated without break we haven't found a single match
|
# if loop terminated without break we havn't found a single match
|
||||||
# either we are at the end of the file or we have a problem
|
# either we are at the end of the file or we have a problem
|
||||||
else:
|
else:
|
||||||
# end of text
|
# end of text
|
||||||
|
|||||||
Executable → Regular
+1
-24
@@ -330,16 +330,12 @@ class PrefixLoader(BaseLoader):
|
|||||||
self.mapping = mapping
|
self.mapping = mapping
|
||||||
self.delimiter = delimiter
|
self.delimiter = delimiter
|
||||||
|
|
||||||
def get_loader(self, template):
|
def get_source(self, environment, template):
|
||||||
try:
|
try:
|
||||||
prefix, name = template.split(self.delimiter, 1)
|
prefix, name = template.split(self.delimiter, 1)
|
||||||
loader = self.mapping[prefix]
|
loader = self.mapping[prefix]
|
||||||
except (ValueError, KeyError):
|
except (ValueError, KeyError):
|
||||||
raise TemplateNotFound(template)
|
raise TemplateNotFound(template)
|
||||||
return loader, name
|
|
||||||
|
|
||||||
def get_source(self, environment, template):
|
|
||||||
loader, name = self.get_loader(template)
|
|
||||||
try:
|
try:
|
||||||
return loader.get_source(environment, name)
|
return loader.get_source(environment, name)
|
||||||
except TemplateNotFound:
|
except TemplateNotFound:
|
||||||
@@ -347,16 +343,6 @@ class PrefixLoader(BaseLoader):
|
|||||||
# (the one that includes the prefix)
|
# (the one that includes the prefix)
|
||||||
raise TemplateNotFound(template)
|
raise TemplateNotFound(template)
|
||||||
|
|
||||||
@internalcode
|
|
||||||
def load(self, environment, name, globals=None):
|
|
||||||
loader, local_name = self.get_loader(name)
|
|
||||||
try:
|
|
||||||
return loader.load(environment, local_name)
|
|
||||||
except TemplateNotFound:
|
|
||||||
# re-raise the exception with the correct fileame here.
|
|
||||||
# (the one that includes the prefix)
|
|
||||||
raise TemplateNotFound(name)
|
|
||||||
|
|
||||||
def list_templates(self):
|
def list_templates(self):
|
||||||
result = []
|
result = []
|
||||||
for prefix, loader in self.mapping.iteritems():
|
for prefix, loader in self.mapping.iteritems():
|
||||||
@@ -390,15 +376,6 @@ class ChoiceLoader(BaseLoader):
|
|||||||
pass
|
pass
|
||||||
raise TemplateNotFound(template)
|
raise TemplateNotFound(template)
|
||||||
|
|
||||||
@internalcode
|
|
||||||
def load(self, environment, name, globals=None):
|
|
||||||
for loader in self.loaders:
|
|
||||||
try:
|
|
||||||
return loader.load(environment, name, globals)
|
|
||||||
except TemplateNotFound:
|
|
||||||
pass
|
|
||||||
raise TemplateNotFound(name)
|
|
||||||
|
|
||||||
def list_templates(self):
|
def list_templates(self):
|
||||||
found = set()
|
found = set()
|
||||||
for loader in self.loaders:
|
for loader in self.loaders:
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
+2
-1
@@ -223,7 +223,7 @@ class Parser(object):
|
|||||||
# raise a nicer error message in that case.
|
# raise a nicer error message in that case.
|
||||||
if self.stream.current.type == 'sub':
|
if self.stream.current.type == 'sub':
|
||||||
self.fail('Block names in Jinja have to be valid Python '
|
self.fail('Block names in Jinja have to be valid Python '
|
||||||
'identifiers and may not contain hyphens, use an '
|
'identifiers and may not contain hypens, use an '
|
||||||
'underscore instead.')
|
'underscore instead.')
|
||||||
|
|
||||||
node.body = self.parse_statements(('name:endblock',), drop_needle=True)
|
node.body = self.parse_statements(('name:endblock',), drop_needle=True)
|
||||||
@@ -698,6 +698,7 @@ class Parser(object):
|
|||||||
arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
|
arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
|
||||||
return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
|
return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
|
||||||
if token.type == 'lbracket':
|
if token.type == 'lbracket':
|
||||||
|
priority_on_attribute = False
|
||||||
args = []
|
args = []
|
||||||
while self.stream.current.type != 'rbracket':
|
while self.stream.current.type != 'rbracket':
|
||||||
if args:
|
if args:
|
||||||
|
|||||||
Executable → Regular
+2
-15
@@ -30,8 +30,6 @@ to_string = unicode
|
|||||||
#: the identity function. Useful for certain things in the environment
|
#: the identity function. Useful for certain things in the environment
|
||||||
identity = lambda x: x
|
identity = lambda x: x
|
||||||
|
|
||||||
_last_iteration = object()
|
|
||||||
|
|
||||||
|
|
||||||
def markup_join(seq):
|
def markup_join(seq):
|
||||||
"""Concatenation that escapes if necessary and converts to unicode."""
|
"""Concatenation that escapes if necessary and converts to unicode."""
|
||||||
@@ -272,7 +270,6 @@ class LoopContext(object):
|
|||||||
def __init__(self, iterable, recurse=None):
|
def __init__(self, iterable, recurse=None):
|
||||||
self._iterator = iter(iterable)
|
self._iterator = iter(iterable)
|
||||||
self._recurse = recurse
|
self._recurse = recurse
|
||||||
self._after = self._safe_next()
|
|
||||||
self.index0 = -1
|
self.index0 = -1
|
||||||
|
|
||||||
# try to get the length of the iterable early. This must be done
|
# try to get the length of the iterable early. This must be done
|
||||||
@@ -291,7 +288,7 @@ class LoopContext(object):
|
|||||||
return args[self.index0 % len(args)]
|
return args[self.index0 % len(args)]
|
||||||
|
|
||||||
first = property(lambda x: x.index0 == 0)
|
first = property(lambda x: x.index0 == 0)
|
||||||
last = property(lambda x: x._after is _last_iteration)
|
last = property(lambda x: x.index0 + 1 == x.length)
|
||||||
index = property(lambda x: x.index0 + 1)
|
index = property(lambda x: x.index0 + 1)
|
||||||
revindex = property(lambda x: x.length - x.index0)
|
revindex = property(lambda x: x.length - x.index0)
|
||||||
revindex0 = property(lambda x: x.length - x.index)
|
revindex0 = property(lambda x: x.length - x.index)
|
||||||
@@ -302,12 +299,6 @@ class LoopContext(object):
|
|||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return LoopContextIterator(self)
|
return LoopContextIterator(self)
|
||||||
|
|
||||||
def _safe_next(self):
|
|
||||||
try:
|
|
||||||
return next(self._iterator)
|
|
||||||
except StopIteration:
|
|
||||||
return _last_iteration
|
|
||||||
|
|
||||||
@internalcode
|
@internalcode
|
||||||
def loop(self, iterable):
|
def loop(self, iterable):
|
||||||
if self._recurse is None:
|
if self._recurse is None:
|
||||||
@@ -353,11 +344,7 @@ class LoopContextIterator(object):
|
|||||||
def next(self):
|
def next(self):
|
||||||
ctx = self.context
|
ctx = self.context
|
||||||
ctx.index0 += 1
|
ctx.index0 += 1
|
||||||
if ctx._after is _last_iteration:
|
return next(ctx._iterator), ctx
|
||||||
raise StopIteration()
|
|
||||||
next_elem = ctx._after
|
|
||||||
ctx._after = ctx._safe_next()
|
|
||||||
return next_elem, ctx
|
|
||||||
|
|
||||||
|
|
||||||
class Macro(object):
|
class Macro(object):
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
+3
-43
@@ -84,28 +84,10 @@ class FilterTestCase(JinjaTestCase):
|
|||||||
'{{ 1000000000000|filesizeformat(true) }}'
|
'{{ 1000000000000|filesizeformat(true) }}'
|
||||||
)
|
)
|
||||||
out = tmpl.render()
|
out = tmpl.render()
|
||||||
self.assert_equal(out, (
|
assert out == (
|
||||||
'100 Bytes|1.0 kB|1.0 MB|1.0 GB|1.0 TB|100 Bytes|'
|
'100 Bytes|0.0 kB|0.0 MB|0.0 GB|0.0 TB|100 Bytes|'
|
||||||
'1000 Bytes|976.6 KiB|953.7 MiB|931.3 GiB'
|
'1000 Bytes|1.0 KiB|0.9 MiB|0.9 GiB'
|
||||||
))
|
|
||||||
|
|
||||||
def test_filesizeformat_issue59(self):
|
|
||||||
tmpl = env.from_string(
|
|
||||||
'{{ 300|filesizeformat }}|'
|
|
||||||
'{{ 3000|filesizeformat }}|'
|
|
||||||
'{{ 3000000|filesizeformat }}|'
|
|
||||||
'{{ 3000000000|filesizeformat }}|'
|
|
||||||
'{{ 3000000000000|filesizeformat }}|'
|
|
||||||
'{{ 300|filesizeformat(true) }}|'
|
|
||||||
'{{ 3000|filesizeformat(true) }}|'
|
|
||||||
'{{ 3000000|filesizeformat(true) }}'
|
|
||||||
)
|
)
|
||||||
out = tmpl.render()
|
|
||||||
self.assert_equal(out, (
|
|
||||||
'300 Bytes|3.0 kB|3.0 MB|3.0 GB|3.0 TB|300 Bytes|'
|
|
||||||
'2.9 KiB|2.9 MiB'
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
def test_first(self):
|
def test_first(self):
|
||||||
tmpl = env.from_string('{{ foo|first }}')
|
tmpl = env.from_string('{{ foo|first }}')
|
||||||
@@ -193,16 +175,6 @@ class FilterTestCase(JinjaTestCase):
|
|||||||
def test_title(self):
|
def test_title(self):
|
||||||
tmpl = env.from_string('''{{ "foo bar"|title }}''')
|
tmpl = env.from_string('''{{ "foo bar"|title }}''')
|
||||||
assert tmpl.render() == "Foo Bar"
|
assert tmpl.render() == "Foo Bar"
|
||||||
tmpl = env.from_string('''{{ "foo's bar"|title }}''')
|
|
||||||
assert tmpl.render() == "Foo's Bar"
|
|
||||||
tmpl = env.from_string('''{{ "foo bar"|title }}''')
|
|
||||||
assert tmpl.render() == "Foo Bar"
|
|
||||||
tmpl = env.from_string('''{{ "f bar f"|title }}''')
|
|
||||||
assert tmpl.render() == "F Bar F"
|
|
||||||
tmpl = env.from_string('''{{ "foo-bar"|title }}''')
|
|
||||||
assert tmpl.render() == "Foo-Bar"
|
|
||||||
tmpl = env.from_string('''{{ "foo\tbar"|title }}''')
|
|
||||||
assert tmpl.render() == "Foo\tBar"
|
|
||||||
|
|
||||||
def test_truncate(self):
|
def test_truncate(self):
|
||||||
tmpl = env.from_string(
|
tmpl = env.from_string(
|
||||||
@@ -377,18 +349,6 @@ class FilterTestCase(JinjaTestCase):
|
|||||||
tmpl = env.from_string('{{ "<div>foo</div>" }}')
|
tmpl = env.from_string('{{ "<div>foo</div>" }}')
|
||||||
assert tmpl.render() == '<div>foo</div>'
|
assert tmpl.render() == '<div>foo</div>'
|
||||||
|
|
||||||
def test_urlencode(self):
|
|
||||||
env = Environment(autoescape=True)
|
|
||||||
tmpl = env.from_string('{{ "Hello, world!"|urlencode }}')
|
|
||||||
assert tmpl.render() == 'Hello%2C%20world%21'
|
|
||||||
tmpl = env.from_string('{{ o|urlencode }}')
|
|
||||||
assert tmpl.render(o=u"Hello, world\u203d") == "Hello%2C%20world%E2%80%BD"
|
|
||||||
assert tmpl.render(o=(("f", 1),)) == "f=1"
|
|
||||||
assert tmpl.render(o=(('f', 1), ("z", 2))) == "f=1&z=2"
|
|
||||||
assert tmpl.render(o=((u"\u203d", 1),)) == "%E2%80%BD=1"
|
|
||||||
assert tmpl.render(o={u"\u203d": 1}) == "%E2%80%BD=1"
|
|
||||||
assert tmpl.render(o={0: 1}) == "0=1"
|
|
||||||
|
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
Executable → Regular
+4
-4
@@ -169,7 +169,7 @@ and bar comment #}
|
|||||||
except TemplateSyntaxError, e:
|
except TemplateSyntaxError, e:
|
||||||
assert str(e) == expected, 'unexpected error message'
|
assert str(e) == expected, 'unexpected error message'
|
||||||
else:
|
else:
|
||||||
assert False, 'that was supposed to be an error'
|
assert False, 'that was suposed to be an error'
|
||||||
|
|
||||||
assert_error('{% for item in seq %}...{% endif %}',
|
assert_error('{% for item in seq %}...{% endif %}',
|
||||||
"Encountered unknown tag 'endif'. Jinja was looking "
|
"Encountered unknown tag 'endif'. Jinja was looking "
|
||||||
@@ -189,7 +189,7 @@ and bar comment #}
|
|||||||
"that needs to be closed is 'for'.")
|
"that needs to be closed is 'for'.")
|
||||||
assert_error('{% block foo-bar-baz %}',
|
assert_error('{% block foo-bar-baz %}',
|
||||||
"Block names in Jinja have to be valid Python identifiers "
|
"Block names in Jinja have to be valid Python identifiers "
|
||||||
"and may not contain hyphens, use an underscore instead.")
|
"and may not contain hypens, use an underscore instead.")
|
||||||
assert_error('{% unknown_tag %}',
|
assert_error('{% unknown_tag %}',
|
||||||
"Encountered unknown tag 'unknown_tag'.")
|
"Encountered unknown tag 'unknown_tag'.")
|
||||||
|
|
||||||
@@ -317,7 +317,7 @@ class SyntaxTestCase(JinjaTestCase):
|
|||||||
self.assert_raises(TemplateSyntaxError, env.from_string,
|
self.assert_raises(TemplateSyntaxError, env.from_string,
|
||||||
'{% block x %}{% endblock y %}')
|
'{% block x %}{% endblock y %}')
|
||||||
|
|
||||||
def test_constant_casing(self):
|
def test_contant_casing(self):
|
||||||
for const in True, False, None:
|
for const in True, False, None:
|
||||||
tmpl = env.from_string('{{ %s }}|{{ %s }}|{{ %s }}' % (
|
tmpl = env.from_string('{{ %s }}|{{ %s }}|{{ %s }}' % (
|
||||||
str(const), str(const).lower(), str(const).upper()
|
str(const), str(const).lower(), str(const).upper()
|
||||||
@@ -327,7 +327,7 @@ class SyntaxTestCase(JinjaTestCase):
|
|||||||
def test_test_chaining(self):
|
def test_test_chaining(self):
|
||||||
self.assert_raises(TemplateSyntaxError, env.from_string,
|
self.assert_raises(TemplateSyntaxError, env.from_string,
|
||||||
'{{ foo is string is sequence }}')
|
'{{ foo is string is sequence }}')
|
||||||
assert env.from_string('{{ 42 is string or 42 is number }}'
|
env.from_string('{{ 42 is string or 42 is number }}'
|
||||||
).render() == 'True'
|
).render() == 'True'
|
||||||
|
|
||||||
def test_string_concatenation(self):
|
def test_string_concatenation(self):
|
||||||
|
|||||||
Executable → Regular
-28
@@ -182,34 +182,6 @@ class ModuleLoaderTestCase(JinjaTestCase):
|
|||||||
tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490
|
tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490
|
||||||
assert mod.__file__.endswith('.pyc')
|
assert mod.__file__.endswith('.pyc')
|
||||||
|
|
||||||
def test_choice_loader(self):
|
|
||||||
log = self.compile_down(py_compile=True)
|
|
||||||
assert 'Byte-compiled "a/test.html"' in log
|
|
||||||
|
|
||||||
self.mod_env.loader = loaders.ChoiceLoader([
|
|
||||||
self.mod_env.loader,
|
|
||||||
loaders.DictLoader({'DICT_SOURCE': 'DICT_TEMPLATE'})
|
|
||||||
])
|
|
||||||
|
|
||||||
tmpl1 = self.mod_env.get_template('a/test.html')
|
|
||||||
self.assert_equal(tmpl1.render(), 'BAR')
|
|
||||||
tmpl2 = self.mod_env.get_template('DICT_SOURCE')
|
|
||||||
self.assert_equal(tmpl2.render(), 'DICT_TEMPLATE')
|
|
||||||
|
|
||||||
def test_prefix_loader(self):
|
|
||||||
log = self.compile_down(py_compile=True)
|
|
||||||
assert 'Byte-compiled "a/test.html"' in log
|
|
||||||
|
|
||||||
self.mod_env.loader = loaders.PrefixLoader({
|
|
||||||
'MOD': self.mod_env.loader,
|
|
||||||
'DICT': loaders.DictLoader({'test.html': 'DICT_TEMPLATE'})
|
|
||||||
})
|
|
||||||
|
|
||||||
tmpl1 = self.mod_env.get_template('MOD/a/test.html')
|
|
||||||
self.assert_equal(tmpl1.render(), 'BAR')
|
|
||||||
tmpl2 = self.mod_env.get_template('DICT/test.html')
|
|
||||||
self.assert_equal(tmpl2.render(), 'DICT_TEMPLATE')
|
|
||||||
|
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
+7
-26
@@ -11,10 +11,6 @@
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import errno
|
import errno
|
||||||
try:
|
|
||||||
from urllib.parse import quote_from_bytes as url_quote
|
|
||||||
except ImportError:
|
|
||||||
from urllib import quote as url_quote
|
|
||||||
try:
|
try:
|
||||||
from thread import allocate_lock
|
from thread import allocate_lock
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -67,7 +63,7 @@ except TypeError, _error:
|
|||||||
del _test_gen_bug, _error
|
del _test_gen_bug, _error
|
||||||
|
|
||||||
|
|
||||||
# for python 2.x we create ourselves a next() function that does the
|
# for python 2.x we create outselves a next() function that does the
|
||||||
# basics without exception catching.
|
# basics without exception catching.
|
||||||
try:
|
try:
|
||||||
next = next
|
next = next
|
||||||
@@ -132,7 +128,7 @@ def contextfunction(f):
|
|||||||
|
|
||||||
|
|
||||||
def evalcontextfunction(f):
|
def evalcontextfunction(f):
|
||||||
"""This decorator can be used to mark a function or method as an eval
|
"""This decoraotr can be used to mark a function or method as an eval
|
||||||
context callable. This is similar to the :func:`contextfunction`
|
context callable. This is similar to the :func:`contextfunction`
|
||||||
but instead of passing the context, an evaluation context object is
|
but instead of passing the context, an evaluation context object is
|
||||||
passed. For more information about the eval context, see
|
passed. For more information about the eval context, see
|
||||||
@@ -195,7 +191,7 @@ def clear_caches():
|
|||||||
|
|
||||||
|
|
||||||
def import_string(import_name, silent=False):
|
def import_string(import_name, silent=False):
|
||||||
"""Imports an object based on a string. This is useful if you want to
|
"""Imports an object based on a string. This use useful if you want to
|
||||||
use import paths as endpoints or something similar. An import path can
|
use import paths as endpoints or something similar. An import path can
|
||||||
be specified either in dotted notation (``xml.sax.saxutils.escape``)
|
be specified either in dotted notation (``xml.sax.saxutils.escape``)
|
||||||
or with a colon as object delimiter (``xml.sax.saxutils:escape``).
|
or with a colon as object delimiter (``xml.sax.saxutils:escape``).
|
||||||
@@ -353,21 +349,6 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
|
|||||||
return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
|
return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
|
||||||
|
|
||||||
|
|
||||||
def unicode_urlencode(obj, charset='utf-8'):
|
|
||||||
"""URL escapes a single bytestring or unicode string with the
|
|
||||||
given charset if applicable to URL safe quoting under all rules
|
|
||||||
that need to be considered under all supported Python versions.
|
|
||||||
|
|
||||||
If non strings are provided they are converted to their unicode
|
|
||||||
representation first.
|
|
||||||
"""
|
|
||||||
if not isinstance(obj, basestring):
|
|
||||||
obj = unicode(obj)
|
|
||||||
if isinstance(obj, unicode):
|
|
||||||
obj = obj.encode(charset)
|
|
||||||
return unicode(url_quote(obj))
|
|
||||||
|
|
||||||
|
|
||||||
class LRUCache(object):
|
class LRUCache(object):
|
||||||
"""A simple LRU Cache implementation."""
|
"""A simple LRU Cache implementation."""
|
||||||
|
|
||||||
@@ -412,7 +393,7 @@ class LRUCache(object):
|
|||||||
return (self.capacity,)
|
return (self.capacity,)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
"""Return a shallow copy of the instance."""
|
"""Return an shallow copy of the instance."""
|
||||||
rv = self.__class__(self.capacity)
|
rv = self.__class__(self.capacity)
|
||||||
rv._mapping.update(self._mapping)
|
rv._mapping.update(self._mapping)
|
||||||
rv._queue = deque(self._queue)
|
rv._queue = deque(self._queue)
|
||||||
@@ -462,7 +443,7 @@ class LRUCache(object):
|
|||||||
"""Get an item from the cache. Moves the item up so that it has the
|
"""Get an item from the cache. Moves the item up so that it has the
|
||||||
highest priority then.
|
highest priority then.
|
||||||
|
|
||||||
Raise a `KeyError` if it does not exist.
|
Raise an `KeyError` if it does not exist.
|
||||||
"""
|
"""
|
||||||
rv = self._mapping[key]
|
rv = self._mapping[key]
|
||||||
if self._queue[-1] != key:
|
if self._queue[-1] != key:
|
||||||
@@ -497,7 +478,7 @@ class LRUCache(object):
|
|||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
"""Remove an item from the cache dict.
|
"""Remove an item from the cache dict.
|
||||||
Raise a `KeyError` if it does not exist.
|
Raise an `KeyError` if it does not exist.
|
||||||
"""
|
"""
|
||||||
self._wlock.acquire()
|
self._wlock.acquire()
|
||||||
try:
|
try:
|
||||||
@@ -598,7 +579,7 @@ class Joiner(object):
|
|||||||
|
|
||||||
# try markupsafe first, if that fails go with Jinja2's bundled version
|
# try markupsafe first, if that fails go with Jinja2's bundled version
|
||||||
# of markupsafe. Markupsafe was previously Jinja2's implementation of
|
# of markupsafe. Markupsafe was previously Jinja2's implementation of
|
||||||
# the Markup object but was moved into a separate package in a patchlevel
|
# the Markup object but was moved into a separate package in a patchleve
|
||||||
# release
|
# release
|
||||||
try:
|
try:
|
||||||
from markupsafe import Markup, escape, soft_unicode
|
from markupsafe import Markup, escape, soft_unicode
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
+1
-1
@@ -19,7 +19,7 @@ import sys
|
|||||||
|
|
||||||
|
|
||||||
# the version. Usually set automatically by a script.
|
# the version. Usually set automatically by a script.
|
||||||
__version__ = '0.9-dev'
|
__version__ = '0.8.3'
|
||||||
|
|
||||||
|
|
||||||
# This import magic raises concerns quite often which is why the implementation
|
# This import magic raises concerns quite often which is why the implementation
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
+16
-1
@@ -88,6 +88,7 @@ r"""
|
|||||||
:copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
|
:copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
|
||||||
:license: BSD, see LICENSE for more details.
|
:license: BSD, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
import sys
|
||||||
import cPickle as pickle
|
import cPickle as pickle
|
||||||
from hmac import new as hmac
|
from hmac import new as hmac
|
||||||
from time import time
|
from time import time
|
||||||
@@ -97,7 +98,21 @@ from werkzeug.contrib.sessions import ModificationTrackingDict
|
|||||||
from werkzeug.security import safe_str_cmp
|
from werkzeug.security import safe_str_cmp
|
||||||
|
|
||||||
|
|
||||||
from hashlib import sha1 as _default_hash
|
# rather ugly way to import the correct hash method. Because
|
||||||
|
# hmac either accepts modules with a new method (sha, md5 etc.)
|
||||||
|
# or a hashlib factory function we have to figure out what to
|
||||||
|
# pass to it. If we have 2.5 or higher (so not 2.4 with a
|
||||||
|
# custom hashlib) we import from hashlib and fail if it does
|
||||||
|
# not exist (have seen that in old OS X versions).
|
||||||
|
# in all other cases the now deprecated sha module is used.
|
||||||
|
_default_hash = None
|
||||||
|
if sys.version_info >= (2, 5):
|
||||||
|
try:
|
||||||
|
from hashlib import sha1 as _default_hash
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
if _default_hash is None:
|
||||||
|
import sha as _default_hash
|
||||||
|
|
||||||
|
|
||||||
class UnquoteError(Exception):
|
class UnquoteError(Exception):
|
||||||
|
|||||||
Executable → Regular
+4
-1
@@ -58,7 +58,10 @@ import tempfile
|
|||||||
from os import path
|
from os import path
|
||||||
from time import time
|
from time import time
|
||||||
from random import random
|
from random import random
|
||||||
from hashlib import sha1
|
try:
|
||||||
|
from hashlib import sha1
|
||||||
|
except ImportError:
|
||||||
|
from sha import new as sha1
|
||||||
from cPickle import dump, load, HIGHEST_PROTOCOL
|
from cPickle import dump, load, HIGHEST_PROTOCOL
|
||||||
|
|
||||||
from werkzeug.datastructures import CallbackDict
|
from werkzeug.datastructures import CallbackDict
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
Executable → Regular
+1
-1
@@ -2079,7 +2079,7 @@ class ETags(object):
|
|||||||
return etag in self._strong
|
return etag in self._strong
|
||||||
|
|
||||||
def __nonzero__(self):
|
def __nonzero__(self):
|
||||||
return bool(self.star_tag or self._strong or self._weak)
|
return bool(self.star_tag or self._strong)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.to_header()
|
return self.to_header()
|
||||||
|
|||||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
+3
-4
@@ -100,7 +100,7 @@ import posixpath
|
|||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from urlparse import urljoin
|
from urlparse import urljoin
|
||||||
|
|
||||||
from werkzeug.urls import url_encode, url_quote
|
from werkzeug.urls import url_encode, url_decode, url_quote
|
||||||
from werkzeug.utils import redirect, format_string
|
from werkzeug.utils import redirect, format_string
|
||||||
from werkzeug.exceptions import HTTPException, NotFound, MethodNotAllowed
|
from werkzeug.exceptions import HTTPException, NotFound, MethodNotAllowed
|
||||||
from werkzeug._internal import _get_environ
|
from werkzeug._internal import _get_environ
|
||||||
@@ -715,7 +715,7 @@ class Rule(RuleFactory):
|
|||||||
return
|
return
|
||||||
processed.add(data)
|
processed.add(data)
|
||||||
else:
|
else:
|
||||||
add(url_quote(data, self.map.charset, safe='/:|+'))
|
add(url_quote(data, self.map.charset, safe='/:|'))
|
||||||
domain_part, url = (u''.join(tmp)).split('|', 1)
|
domain_part, url = (u''.join(tmp)).split('|', 1)
|
||||||
|
|
||||||
if append_unknown:
|
if append_unknown:
|
||||||
@@ -1503,8 +1503,7 @@ class MapAdapter(object):
|
|||||||
self.url_scheme,
|
self.url_scheme,
|
||||||
self.get_host(domain_part),
|
self.get_host(domain_part),
|
||||||
posixpath.join(self.script_name[:-1].lstrip('/'),
|
posixpath.join(self.script_name[:-1].lstrip('/'),
|
||||||
url_quote(path_info.lstrip('/'), self.map.charset,
|
url_quote(path_info.lstrip('/'), self.map.charset)),
|
||||||
safe='/:|+')),
|
|
||||||
suffix
|
suffix
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user