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
14
docs/api.rst
14
docs/api.rst
|
|
@ -469,8 +469,18 @@ Signals
|
||||||
.. data:: request_tearing_down
|
.. data:: request_tearing_down
|
||||||
|
|
||||||
This signal is sent when the application is tearing down the request.
|
This signal is sent when the application is tearing down the request.
|
||||||
This is always called, even if an error happened. No arguments are
|
This is always called, even if an error happened. An `exc` keyword
|
||||||
provided.
|
argument is passed with the exception that caused the teardown.
|
||||||
|
|
||||||
|
.. versionchanged:: 0.9
|
||||||
|
The `exc` parameter was added.
|
||||||
|
|
||||||
|
.. data:: appcontext_tearing_down
|
||||||
|
|
||||||
|
This signal is sent when the application is tearing down the
|
||||||
|
application context. This is always called, even if an error happened.
|
||||||
|
An `exc` keyword argument is passed with the exception that caused the
|
||||||
|
teardown.
|
||||||
|
|
||||||
.. currentmodule:: None
|
.. currentmodule:: None
|
||||||
|
|
||||||
|
|
|
||||||
88
docs/appcontext.rst
Normal file
88
docs/appcontext.rst
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
.. _app_context:
|
||||||
|
|
||||||
|
The Application Context
|
||||||
|
=======================
|
||||||
|
|
||||||
|
.. versionadded:: 0.9
|
||||||
|
|
||||||
|
One of the design ideas behind Flask is that there are two different
|
||||||
|
“states” in which code is executed. The application setup state in which
|
||||||
|
the application implicitly is on the module level. It starts when the
|
||||||
|
:class:`Flask` object is instantiated, and it implicitly ends when the
|
||||||
|
first request comes in. While the application is in this state a few
|
||||||
|
assumptions are true:
|
||||||
|
|
||||||
|
- the programmer can modify the application object safely.
|
||||||
|
- no request handling happened so far
|
||||||
|
- you have to have a reference to the application object in order to
|
||||||
|
modify it, there is no magic proxy that can give you a reference to
|
||||||
|
the application object you're currently creating or modifying.
|
||||||
|
|
||||||
|
On the contrast, during request handling, a couple of other rules exist:
|
||||||
|
|
||||||
|
- while a request is active, the context local objects
|
||||||
|
(:data:`flask.request` and others) point to the current request.
|
||||||
|
- any code can get hold of these objects at any time.
|
||||||
|
|
||||||
|
There is a third state which is sitting in between a little bit.
|
||||||
|
Sometimes you are dealing with an application in a way that is similar to
|
||||||
|
how you interact with applications during request handling just that there
|
||||||
|
is no request active. Consider for instance that you're sitting in an
|
||||||
|
interactive Python shell and interacting with the application, or a
|
||||||
|
command line application.
|
||||||
|
|
||||||
|
The application context is what powers the :data:`~flask.current_app`
|
||||||
|
context local.
|
||||||
|
|
||||||
|
Purpose of the Application Context
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
The main reason for the application's context existance is that in the
|
||||||
|
past a bunch of functionality was attached to the request context in lack
|
||||||
|
of a better solution. Since one of the pillar's of Flask's design is that
|
||||||
|
you can have more than one application in the same Python process.
|
||||||
|
|
||||||
|
So how does the code find the “right” application? In the past we
|
||||||
|
recommended passing applications around explicitly, but that caused issues
|
||||||
|
with libraries that were not designed with that in mind for libraries for
|
||||||
|
which it was too inconvenient to make this work.
|
||||||
|
|
||||||
|
A common workaround for that problem was to use the
|
||||||
|
:data:`~flask.current_app` proxy later on, which was bound to the current
|
||||||
|
request's application reference. Since however creating such a request
|
||||||
|
context is an unnecessarily expensive operation in case there is no
|
||||||
|
request around, the application context was introduced.
|
||||||
|
|
||||||
|
Creating an Application Context
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
To make an application context there are two ways. The first one is the
|
||||||
|
implicit one: whenever a request context is pushed, an application context
|
||||||
|
will be created alongside if this is necessary. As a result of that, you
|
||||||
|
can ignore the existance of the application context unless you need it.
|
||||||
|
|
||||||
|
The second way is the explicit way using the
|
||||||
|
:meth:`~flask.Flask.app_context` method::
|
||||||
|
|
||||||
|
from flask import Flask, current_app
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
with app.app_context():
|
||||||
|
# within this block, current_app points to app.
|
||||||
|
print current_app.name
|
||||||
|
|
||||||
|
The application context is also used by the :func:`~flask.url_for`
|
||||||
|
function in case a ``SERVER_NAME`` was configured. This allows you to
|
||||||
|
generate URLs even in the absence of a request.
|
||||||
|
|
||||||
|
Locality of the Context
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
The application context is created and destroyed as necessary. It never
|
||||||
|
moves between threads and it will not be shared between requests. As such
|
||||||
|
it is the perfect place to store database connection information and other
|
||||||
|
things. The internal stack object is called :data:`flask._app_ctx_stack`.
|
||||||
|
Extensions are free to store additional information on the topmost level,
|
||||||
|
assuming they pick a sufficiently unique name.
|
||||||
|
|
||||||
|
For more information about that, see :ref:`extension-dev`.
|
||||||
|
|
@ -18,6 +18,7 @@ instructions for web development with Flask.
|
||||||
config
|
config
|
||||||
signals
|
signals
|
||||||
views
|
views
|
||||||
|
appcontext
|
||||||
reqcontext
|
reqcontext
|
||||||
blueprints
|
blueprints
|
||||||
extensions
|
extensions
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
.. _extension-dev:
|
||||||
|
|
||||||
Flask Extension Development
|
Flask Extension Development
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
|
@ -152,6 +154,11 @@ What to use depends on what you have in mind. For the SQLite 3 extension
|
||||||
we will use the class-based approach because it will provide users with an
|
we will use the class-based approach because it will provide users with an
|
||||||
object that handles opening and closing database connections.
|
object that handles opening and closing database connections.
|
||||||
|
|
||||||
|
What's important about classes is that they encourage to be shared around
|
||||||
|
on module level. In that case, the object itself must not under any
|
||||||
|
circumstances store any application specific state and must be shareable
|
||||||
|
between different application.
|
||||||
|
|
||||||
The Extension Code
|
The Extension Code
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
@ -159,7 +166,13 @@ Here's the contents of the `flask_sqlite3.py` for copy/paste::
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from flask import _request_ctx_stack
|
# Find the stack on which we want to store the database connection.
|
||||||
|
# Starting with Flask 0.9, the _app_ctx_stack is the correct one,
|
||||||
|
# before that we need to use the _request_ctx_stack.
|
||||||
|
try:
|
||||||
|
from flask import _app_ctx_stack as stack
|
||||||
|
except ImportError:
|
||||||
|
from flask import _request_ctx_stack as stack
|
||||||
|
|
||||||
|
|
||||||
class SQLite3(object):
|
class SQLite3(object):
|
||||||
|
|
@ -172,26 +185,28 @@ Here's the contents of the `flask_sqlite3.py` for copy/paste::
|
||||||
self.app = None
|
self.app = None
|
||||||
|
|
||||||
def init_app(self, app):
|
def init_app(self, app):
|
||||||
self.app = app
|
app.config.setdefault('SQLITE3_DATABASE', ':memory:')
|
||||||
self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
|
# Use the newstyle teardown_appcontext if it's available,
|
||||||
self.app.teardown_request(self.teardown_request)
|
# otherwise fall back to the request context
|
||||||
self.app.before_request(self.before_request)
|
if hasattr(app, 'teardown_appcontext'):
|
||||||
|
app.teardown_appcontext(self.teardown)
|
||||||
|
else:
|
||||||
|
app.teardown_request(self.teardown)
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
|
return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
|
||||||
|
|
||||||
def before_request(self):
|
def teardown(self, exception):
|
||||||
ctx = _request_ctx_stack.top
|
ctx = stack.top
|
||||||
ctx.sqlite3_db = self.connect()
|
if hasattr(ctx, 'sqlite3_db'):
|
||||||
|
ctx.sqlite3_db.close()
|
||||||
def teardown_request(self, exception):
|
|
||||||
ctx = _request_ctx_stack.top
|
|
||||||
ctx.sqlite3_db.close()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def connection(self):
|
def connection(self):
|
||||||
ctx = _request_ctx_stack.top
|
ctx = stack.top
|
||||||
if ctx is not None:
|
if ctx is not None:
|
||||||
|
if not hasattr(ctx, 'sqlite3_db'):
|
||||||
|
ctx.sqlite3_db = self.connect()
|
||||||
return ctx.sqlite3_db
|
return ctx.sqlite3_db
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -204,14 +219,19 @@ So here's what these lines of code do:
|
||||||
factory pattern for creating applications. The ``init_app`` will set the
|
factory pattern for creating applications. The ``init_app`` will set the
|
||||||
configuration for the database, defaulting to an in memory database if
|
configuration for the database, defaulting to an in memory database if
|
||||||
no configuration is supplied. In addition, the ``init_app`` method attaches
|
no configuration is supplied. In addition, the ``init_app`` method attaches
|
||||||
``before_request`` and ``teardown_request`` handlers.
|
the ``teardown`` handler. It will try to use the newstyle app context
|
||||||
|
handler and if it does not exist, falls back to the request context
|
||||||
|
one.
|
||||||
3. Next, we define a ``connect`` method that opens a database connection.
|
3. Next, we define a ``connect`` method that opens a database connection.
|
||||||
4. Then we set up the request handlers we bound to the app above. Note here
|
4. Finally, we add a ``connection`` property that on first access opens
|
||||||
that we're attaching our database connection to the top request context via
|
the database connection and stores it on the context.
|
||||||
``_request_ctx_stack.top``. Extensions should use the top context and not the
|
|
||||||
``g`` object to store things like database connections.
|
Note here that we're attaching our database connection to the top
|
||||||
5. Finally, we add a ``connection`` property that simplifies access to the context's
|
application context via ``_app_ctx_stack.top``. Extensions should use
|
||||||
database.
|
the top context for storing their own information with a sufficiently
|
||||||
|
complex name. Note that we're falling back to the
|
||||||
|
``_request_ctx_stack.top`` if the application is using an older
|
||||||
|
version of Flask that does not support it.
|
||||||
|
|
||||||
So why did we decide on a class-based approach here? Because using our
|
So why did we decide on a class-based approach here? Because using our
|
||||||
extension looks something like this::
|
extension looks something like this::
|
||||||
|
|
@ -241,19 +261,38 @@ for creating apps::
|
||||||
Keep in mind that supporting this factory pattern for creating apps is required
|
Keep in mind that supporting this factory pattern for creating apps is required
|
||||||
for approved flask extensions (described below).
|
for approved flask extensions (described below).
|
||||||
|
|
||||||
|
.. admonition:: Note on ``init_app``
|
||||||
|
|
||||||
Using _request_ctx_stack
|
As you noticed, ``init_app`` does not assign ``app`` to ``self``. This
|
||||||
------------------------
|
is intentional! Class based Flask extensions must only store the
|
||||||
|
application on the object when the application was passed to the
|
||||||
|
constructor. This tells the extension: I am not interested in using
|
||||||
|
multiple applications.
|
||||||
|
|
||||||
In the example above, before every request, a ``sqlite3_db`` variable is assigned
|
When the extension needs to find the current application and it does
|
||||||
to ``_request_ctx_stack.top``. In a view function, this variable is accessible
|
not have a reference to it, it must either use the
|
||||||
using the ``connection`` property of ``SQLite3``. During the teardown of a
|
:data:`~flask.current_app` context local or change the API in a way
|
||||||
request, the ``sqlite3_db`` connection is closed. By using this pattern, the
|
that you can pass the application explicitly.
|
||||||
*same* connection to the sqlite3 database is accessible to anything that needs it
|
|
||||||
for the duration of the request.
|
|
||||||
|
|
||||||
End-Of-Request Behavior
|
|
||||||
-----------------------
|
Using _app_ctx_stack
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
In the example above, before every request, a ``sqlite3_db`` variable is
|
||||||
|
assigned to ``_app_ctx_stack.top``. In a view function, this variable is
|
||||||
|
accessible using the ``connection`` property of ``SQLite3``. During the
|
||||||
|
teardown of a request, the ``sqlite3_db`` connection is closed. By using
|
||||||
|
this pattern, the *same* connection to the sqlite3 database is accessible
|
||||||
|
to anything that needs it for the duration of the request.
|
||||||
|
|
||||||
|
If the :data:`~flask._app_ctx_stack` does not exist because the user uses
|
||||||
|
an old version of Flask, it is recommended to fall back to
|
||||||
|
:data:`~flask._request_ctx_stack` which is bound to a request.
|
||||||
|
|
||||||
|
Teardown Behavior
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
*This is only relevant if you want to support Flask 0.6 and older*
|
||||||
|
|
||||||
Due to the change in Flask 0.7 regarding functions that are run at the end
|
Due to the change in Flask 0.7 regarding functions that are run at the end
|
||||||
of the request your extension will have to be extra careful there if it
|
of the request your extension will have to be extra careful there if it
|
||||||
|
|
@ -270,7 +309,6 @@ pattern is a good way to support both::
|
||||||
else:
|
else:
|
||||||
app.after_request(close_connection)
|
app.after_request(close_connection)
|
||||||
|
|
||||||
|
|
||||||
Strictly speaking the above code is wrong, because teardown functions are
|
Strictly speaking the above code is wrong, because teardown functions are
|
||||||
passed the exception and typically don't return anything. However because
|
passed the exception and typically don't return anything. However because
|
||||||
the return value is discarded this will just work assuming that the code
|
the return value is discarded this will just work assuming that the code
|
||||||
|
|
|
||||||
|
|
@ -6,27 +6,7 @@ The Request Context
|
||||||
This document describes the behavior in Flask 0.7 which is mostly in line
|
This document describes the behavior in Flask 0.7 which is mostly in line
|
||||||
with the old behavior but has some small, subtle differences.
|
with the old behavior but has some small, subtle differences.
|
||||||
|
|
||||||
One of the design ideas behind Flask is that there are two different
|
It is recommended that you read the :api:`app-context` chapter first.
|
||||||
“states” in which code is executed. The application setup state in which
|
|
||||||
the application implicitly is on the module level. It starts when the
|
|
||||||
:class:`Flask` object is instantiated, and it implicitly ends when the
|
|
||||||
first request comes in. While the application is in this state a few
|
|
||||||
assumptions are true:
|
|
||||||
|
|
||||||
- the programmer can modify the application object safely.
|
|
||||||
- no request handling happened so far
|
|
||||||
- you have to have a reference to the application object in order to
|
|
||||||
modify it, there is no magic proxy that can give you a reference to
|
|
||||||
the application object you're currently creating or modifying.
|
|
||||||
|
|
||||||
On the contrast, during request handling, a couple of other rules exist:
|
|
||||||
|
|
||||||
- while a request is active, the context local objects
|
|
||||||
(:data:`flask.request` and others) point to the current request.
|
|
||||||
- any code can get hold of these objects at any time.
|
|
||||||
|
|
||||||
The magic that makes this works is internally referred in Flask as the
|
|
||||||
“request context”.
|
|
||||||
|
|
||||||
Diving into Context Locals
|
Diving into Context Locals
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
@ -107,6 +87,10 @@ the very top, :meth:`~flask.ctx.RequestContext.pop` removes it from the
|
||||||
stack again. On popping the application's
|
stack again. On popping the application's
|
||||||
:func:`~flask.Flask.teardown_request` functions are also executed.
|
:func:`~flask.Flask.teardown_request` functions are also executed.
|
||||||
|
|
||||||
|
Another thing of note is that the request context will automatically also
|
||||||
|
create an :ref:`application context <app-context>` when it's pushed and
|
||||||
|
there is no application context for that application so far.
|
||||||
|
|
||||||
.. _callbacks-and-errors:
|
.. _callbacks-and-errors:
|
||||||
|
|
||||||
Callbacks and Errors
|
Callbacks and Errors
|
||||||
|
|
|
||||||
|
|
@ -268,4 +268,27 @@ The following signals exist in Flask:
|
||||||
from flask import request_tearing_down
|
from flask import request_tearing_down
|
||||||
request_tearing_down.connect(close_db_connection, app)
|
request_tearing_down.connect(close_db_connection, app)
|
||||||
|
|
||||||
|
As of Flask 0.9, this will also be passed an `exc` keyword argument
|
||||||
|
that has a reference to the exception that caused the teardown if
|
||||||
|
there was one.
|
||||||
|
|
||||||
|
.. data:: flask.appcontext_tearing_down
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
This signal is sent when the app context is tearing down. This is always
|
||||||
|
called, even if an exception is caused. Currently functions listening
|
||||||
|
to this signal are called after the regular teardown handlers, but this
|
||||||
|
is not something you can rely on.
|
||||||
|
|
||||||
|
Example subscriber::
|
||||||
|
|
||||||
|
def close_db_connection(sender, **extra):
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
from flask import request_tearing_down
|
||||||
|
appcontext_tearing_down.connect(close_db_connection, app)
|
||||||
|
|
||||||
|
This will also be passed an `exc` keyword argument that has a reference
|
||||||
|
to the exception that caused the teardown if there was one.
|
||||||
|
|
||||||
.. _blinker: http://pypi.python.org/pypi/blinker
|
.. _blinker: http://pypi.python.org/pypi/blinker
|
||||||
|
|
|
||||||
68
flask/app.py
68
flask/app.py
|
|
@ -35,7 +35,7 @@ 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()
|
||||||
|
|
@ -364,6 +364,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
|
||||||
|
|
@ -1106,10 +1114,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."""
|
||||||
|
|
@ -1485,23 +1525,39 @@ 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):
|
def app_context(self):
|
||||||
"""Binds the application only. For as long as the application is bound
|
"""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.
|
: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, _app_ctx_stack
|
from .globals import _request_ctx_stack, _app_ctx_stack
|
||||||
|
|
@ -86,8 +88,11 @@ class AppContext(object):
|
||||||
"""Binds the app context to the current context."""
|
"""Binds the app context to the current context."""
|
||||||
_app_ctx_stack.push(self)
|
_app_ctx_stack.push(self)
|
||||||
|
|
||||||
def pop(self):
|
def pop(self, exc=None):
|
||||||
"""Pops the app context."""
|
"""Pops the app context."""
|
||||||
|
if exc is None:
|
||||||
|
exc = sys.exc_info()[1]
|
||||||
|
self.app.do_teardown_appcontext(exc)
|
||||||
rv = _app_ctx_stack.pop()
|
rv = _app_ctx_stack.pop()
|
||||||
assert rv is self, 'Popped wrong app context. (%r instead of %r)' \
|
assert rv is self, 'Popped wrong app context. (%r instead of %r)' \
|
||||||
% (rv, self)
|
% (rv, self)
|
||||||
|
|
@ -197,13 +202,18 @@ 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
|
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()
|
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)
|
||||||
|
|
@ -231,7 +241,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>' % (
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,18 @@ class AppContextTestCase(FlaskTestCase):
|
||||||
self.assert_equal(flask.current_app._get_current_object(), app)
|
self.assert_equal(flask.current_app._get_current_object(), app)
|
||||||
self.assert_equal(flask._app_ctx_stack.top, None)
|
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():
|
def suite():
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue