forked from orbit-oss/flask
Added documentation for appcontext and teardown handlers
This commit is contained in:
parent
34bbd3100b
commit
9bed20c07c
10 changed files with 287 additions and 64 deletions
68
flask/app.py
68
flask/app.py
|
|
@ -35,7 +35,7 @@ from .module import blueprint_is_module
|
|||
from .templating import DispatchingJinjaLoader, Environment, \
|
||||
_default_template_ctx_processor
|
||||
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
|
||||
_logger_lock = Lock()
|
||||
|
|
@ -364,6 +364,14 @@ class Flask(_PackageBoundObject):
|
|||
#: .. versionadded:: 0.7
|
||||
self.teardown_request_funcs = {}
|
||||
|
||||
#: A list of functions that are called when the application context
|
||||
#: is destroyed. Since the application context is also torn down
|
||||
#: if the request ends this is the place to store code that disconnects
|
||||
#: from databases.
|
||||
#:
|
||||
#: .. versionadded:: 0.9
|
||||
self.teardown_appcontext_funcs = []
|
||||
|
||||
#: A dictionary with lists of functions that can be used as URL
|
||||
#: value processor functions. Whenever a URL is built these functions
|
||||
#: are called to modify the dictionary of values in place. The key
|
||||
|
|
@ -1106,10 +1114,42 @@ class Flask(_PackageBoundObject):
|
|||
that they will fail. If they do execute code that might fail they
|
||||
will have to surround the execution of these code by try/except
|
||||
statements and log 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)
|
||||
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
|
||||
def context_processor(self, f):
|
||||
"""Registers a template context processor function."""
|
||||
|
|
@ -1485,23 +1525,39 @@ class Flask(_PackageBoundObject):
|
|||
self.save_session(ctx.session, response)
|
||||
return response
|
||||
|
||||
def do_teardown_request(self):
|
||||
def do_teardown_request(self, exc=None):
|
||||
"""Called after the actual request dispatching and will
|
||||
call every as :meth:`teardown_request` decorated function. This is
|
||||
not actually called by the :class:`Flask` object itself but is always
|
||||
triggered when the request context is popped. That way we have a
|
||||
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, ()))
|
||||
bp = _request_ctx_stack.top.request.blueprint
|
||||
if bp is not None and bp in self.teardown_request_funcs:
|
||||
funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
|
||||
exc = sys.exc_info()[1]
|
||||
for func in funcs:
|
||||
rv = func(exc)
|
||||
if rv is not None:
|
||||
return rv
|
||||
request_tearing_down.send(self)
|
||||
request_tearing_down.send(self, exc=exc)
|
||||
|
||||
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
|
||||
|
|
|
|||
18
flask/ctx.py
18
flask/ctx.py
|
|
@ -9,6 +9,8 @@
|
|||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
from .globals import _request_ctx_stack, _app_ctx_stack
|
||||
|
|
@ -86,8 +88,11 @@ class AppContext(object):
|
|||
"""Binds the app context to the current context."""
|
||||
_app_ctx_stack.push(self)
|
||||
|
||||
def pop(self):
|
||||
def pop(self, exc=None):
|
||||
"""Pops the app context."""
|
||||
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)
|
||||
|
|
@ -197,13 +202,18 @@ class RequestContext(object):
|
|||
if self.session is None:
|
||||
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
|
||||
also trigger the execution of functions registered by the
|
||||
:meth:`~flask.Flask.teardown_request` decorator.
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
Added the `exc` argument.
|
||||
"""
|
||||
self.preserved = False
|
||||
self.app.do_teardown_request()
|
||||
if exc is None:
|
||||
exc = sys.exc_info()[1]
|
||||
self.app.do_teardown_request(exc)
|
||||
rv = _request_ctx_stack.pop()
|
||||
assert rv is self, 'Popped wrong request context. (%r instead of %r)' \
|
||||
% (rv, self)
|
||||
|
|
@ -231,7 +241,7 @@ class RequestContext(object):
|
|||
(tb is not None and self.app.preserve_context_on_exception):
|
||||
self.preserved = True
|
||||
else:
|
||||
self.pop()
|
||||
self.pop(exc_value)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s \'%s\' [%s] of %s>' % (
|
||||
|
|
|
|||
|
|
@ -49,3 +49,4 @@ request_started = _signals.signal('request-started')
|
|||
request_finished = _signals.signal('request-finished')
|
||||
request_tearing_down = _signals.signal('request-tearing-down')
|
||||
got_request_exception = _signals.signal('got-request-exception')
|
||||
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')
|
||||
|
|
|
|||
|
|
@ -53,6 +53,18 @@ class AppContextTestCase(FlaskTestCase):
|
|||
self.assert_equal(flask.current_app._get_current_object(), app)
|
||||
self.assert_equal(flask._app_ctx_stack.top, None)
|
||||
|
||||
def test_app_tearing_down(self):
|
||||
cleanup_stuff = []
|
||||
app = flask.Flask(__name__)
|
||||
@app.teardown_appcontext
|
||||
def cleanup(exception):
|
||||
cleanup_stuff.append(exception)
|
||||
|
||||
with app.app_context():
|
||||
pass
|
||||
|
||||
self.assert_equal(cleanup_stuff, [None])
|
||||
|
||||
|
||||
def suite():
|
||||
suite = unittest.TestSuite()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue