Permalink
Browse files

Added documentation for appcontext and teardown handlers

  • Loading branch information...
1 parent 34bbd31 commit 9bed20c07c40a163077b936f740a4e96a6213688 @mitsuhiko mitsuhiko committed Apr 9, 2012
Showing with 287 additions and 64 deletions.
  1. +12 −2 docs/api.rst
  2. +88 −0 docs/appcontext.rst
  3. +1 −0 docs/contents.rst.inc
  4. +69 −31 docs/extensiondev.rst
  5. +5 −21 docs/reqcontext.rst
  6. +23 −0 docs/signals.rst
  7. +62 −6 flask/app.py
  8. +14 −4 flask/ctx.py
  9. +1 −0 flask/signals.py
  10. +12 −0 flask/testsuite/appctx.py
View
@@ -469,8 +469,18 @@ Signals
.. data:: request_tearing_down
This signal is sent when the application is tearing down the request.
- This is always called, even if an error happened. No arguments are
- provided.
+ This is always called, even if an error happened. An `exc` keyword
+ 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
View
@@ -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`.
View
@@ -18,6 +18,7 @@ instructions for web development with Flask.
config
signals
views
+ appcontext
reqcontext
blueprints
extensions
View
@@ -1,3 +1,5 @@
+.. _extension-dev:
+
Flask Extension Development
===========================
@@ -152,14 +154,25 @@ 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
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
------------------
Here's the contents of the `flask_sqlite3.py` for copy/paste::
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):
@@ -172,26 +185,28 @@ Here's the contents of the `flask_sqlite3.py` for copy/paste::
self.app = None
def init_app(self, app):
- self.app = app
- self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
- self.app.teardown_request(self.teardown_request)
- self.app.before_request(self.before_request)
+ app.config.setdefault('SQLITE3_DATABASE', ':memory:')
+ # Use the newstyle teardown_appcontext if it's available,
+ # otherwise fall back to the request context
+ if hasattr(app, 'teardown_appcontext'):
+ app.teardown_appcontext(self.teardown)
+ else:
+ app.teardown_request(self.teardown)
def connect(self):
return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
- def before_request(self):
- ctx = _request_ctx_stack.top
- ctx.sqlite3_db = self.connect()
-
- def teardown_request(self, exception):
- ctx = _request_ctx_stack.top
- ctx.sqlite3_db.close()
+ def teardown(self, exception):
+ ctx = stack.top
+ if hasattr(ctx, 'sqlite3_db'):
+ ctx.sqlite3_db.close()
@property
def connection(self):
- ctx = _request_ctx_stack.top
+ ctx = stack.top
if ctx is not None:
+ if not hasattr(ctx, 'sqlite3_db'):
+ ctx.sqlite3_db = self.connect()
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
configuration for the database, defaulting to an in memory database if
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.
-4. Then we set up the request handlers we bound to the app above. Note here
- that we're attaching our database connection to the top request context via
- ``_request_ctx_stack.top``. Extensions should use the top context and not the
- ``g`` object to store things like database connections.
-5. Finally, we add a ``connection`` property that simplifies access to the context's
- database.
+4. Finally, we add a ``connection`` property that on first access opens
+ the database connection and stores it on the context.
+
+ Note here that we're attaching our database connection to the top
+ application context via ``_app_ctx_stack.top``. Extensions should use
+ 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
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
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
-to ``_request_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.
+ When the extension needs to find the current application and it does
+ not have a reference to it, it must either use the
+ :data:`~flask.current_app` context local or change the API in a way
+ that you can pass the application explicitly.
-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
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:
app.after_request(close_connection)
-
Strictly speaking the above code is wrong, because teardown functions are
passed the exception and typically don't return anything. However because
the return value is discarded this will just work assuming that the code
View
@@ -6,27 +6,7 @@ The Request Context
This document describes the behavior in Flask 0.7 which is mostly in line
with the old behavior but has some small, subtle differences.
-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.
-
-The magic that makes this works is internally referred in Flask as the
-“request context”.
+It is recommended that you read the :api:`app-context` chapter first.
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
: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
View
@@ -268,4 +268,27 @@ The following signals exist in Flask:
from flask import request_tearing_down
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
Oops, something went wrong.

0 comments on commit 9bed20c

Please sign in to comment.