Browse Source

rework context docs

pull/2685/head
David Lord 7 years ago
parent
commit
2411707492
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
  1. 61
      docs/api.rst
  2. 209
      docs/appcontext.rst
  3. 379
      docs/reqcontext.rst
  4. 161
      flask/app.py
  5. 44
      flask/ctx.py
  6. 4
      flask/globals.py
  7. 21
      flask/testing.py

61
docs/api.rst

@ -211,29 +211,22 @@ thing, like it does for :class:`request` and :class:`session`.
.. data:: g
Just store on this whatever you want. For example a database
connection or the user that is currently logged in.
A namespace object that can store data during an
:doc:`application context </appcontext>`. This is an instance of
:attr:`Flask.app_ctx_globals_class`, which defaults to
:class:`ctx._AppCtxGlobals`.
Starting with Flask 0.10 this is stored on the application context and
no longer on the request context which means it becomes available if
only the application context is bound and not yet a request. This
is especially useful when combined with the :ref:`faking-resources`
pattern for testing.
This is a good place to store resources during a request. During
testing, you can use the :ref:`faking-resources` pattern to
pre-configure such resources.
Additionally as of 0.10 you can use the :meth:`get` method to
get an attribute or ``None`` (or the second argument) if it's not set.
These two usages are now equivalent::
user = getattr(flask.g, 'user', None)
user = flask.g.get('user', None)
It's now also possible to use the ``in`` operator on it to see if an
attribute is defined and it yields all keys on iteration.
This is a proxy. See :ref:`notes-on-proxies` for more information.
As of 0.11 you can use :meth:`pop` and :meth:`setdefault` in the same
way you would use them on a dictionary.
.. versionchanged:: 0.10
Bound to the application context instead of the request context.
This is a proxy. See :ref:`notes-on-proxies` for more information.
.. autoclass:: flask.ctx._AppCtxGlobals
:members:
Useful Functions and Classes
@ -241,11 +234,15 @@ Useful Functions and Classes
.. data:: current_app
Points to the application handling the request. This is useful for
extensions that want to support multiple applications running side
by side. This is powered by the application context and not by the
request context, so you can change the value of this proxy by
using the :meth:`~flask.Flask.app_context` method.
A proxy to the application handling the current request. This is
useful to access the application without needing to import it, or if
it can't be imported, such as when using the application factory
pattern or in blueprints and extensions.
This is only available when an
:doc:`application context </appcontext>` is pushed. This happens
automatically during requests and CLI commands. It can be controlled
manually with :meth:`~flask.Flask.app_context`.
This is a proxy. See :ref:`notes-on-proxies` for more information.
@ -384,10 +381,11 @@ Useful Internals
.. data:: _request_ctx_stack
The internal :class:`~werkzeug.local.LocalStack` that is used to implement
all the context local objects used in Flask. This is a documented
instance and can be used by extensions and application code but the
use is discouraged in general.
The internal :class:`~werkzeug.local.LocalStack` that holds
:class:`~flask.ctx.RequestContext` instances. Typically, the
:data:`request` and :data:`session` proxies should be accessed
instead of the stack. It may be useful to access the stack in
extension code.
The following attributes are always present on each layer of the
stack:
@ -424,8 +422,11 @@ Useful Internals
.. data:: _app_ctx_stack
Works similar to the request context but only binds the application.
This is mainly there for extensions to store data.
The internal :class:`~werkzeug.local.LocalStack` that holds
:class:`~flask.ctx.AppContext` instances. Typically, the
:data:`current_app` and :data:`g` proxies should be accessed instead
of the stack. Extensions can access the contexts on the stack as a
namespace to store data.
.. versionadded:: 0.9

209
docs/appcontext.rst

@ -1,144 +1,159 @@
.. currentmodule:: flask
.. _app-context:
The Application Context
=======================
.. versionadded:: 0.9
The application context keeps track of the application-level data during
a request, CLI command, or other activity. Rather than passing the
application around to each function, the :data:`current_app` and
:data:`g` proxies are accessed instead.
One of the design ideas behind Flask is that there are at least two
different “states” in which code is executed:
This is similar to the :doc:`/reqcontext`, which keeps track of
request-level data during a request. A corresponding application context
is pushed when a request context is pushed.
1. The application setup state, in which the application implicitly is
on the module level.
Purpose of the Context
----------------------
This state 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 :class:`Flask` application object has attributes, such as
:attr:`~Flask.config`, that are useful to access within views and
:doc:`CLI commands </cli>`. However, importing the ``app`` instance
within the modules in your project is prone to circular import issues.
When using the :doc:`app factory pattern </patterns/appfactories>` or
writing reusable :doc:`blueprints </blueprints>` or
:doc:`extensions </extensions>` there won't be an ``app`` instance to
import at all.
- 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.
Flask solves this issue with the *application context*. Rather than
referring to an ``app`` directly, you use the the :data:`current_app`
proxy, which points to the application handling the current activity.
2. In contrast, in the request handling state, a couple of other rules
exist:
Flask automatically *pushes* an application context when handling a
request. View functions, error handlers, and other functions that run
during a request will have access to :data:`current_app`.
- 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.
Flask will also automatically push an app context when running CLI
commands registered with :attr:`Flask.cli` using ``@app.cli.command()``.
3. There is also a third state somewhere in between 'module-level' and
'request-handling':
Sometimes you are dealing with an application in a way that is similar to
how you interact with applications during request handling, but without
there being an active request. Consider, for instance, that you're
sitting in an interactive Python shell and interacting with the
application, or a command line application.
Lifetime of the Context
-----------------------
The application context is what powers the :data:`~flask.current_app`
context local.
The application context is created and destroyed as necessary. When a
Flask application begins handling a request, it pushes an application
context and a :doc:`request context </reqcontext>`. When the request
ends it pops the request context then the application context.
Typically, an application context will have the same lifetime as a
request.
Purpose of the Application Context
----------------------------------
See :doc:`/reqcontext` for more information about how the contexts work
and the full lifecycle of a request.
The main reason for the application's context existence is that in the
past a bunch of functionality was attached to the request context for lack
of a better solution. Since one of the pillars 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.
Manually Push a Context
-----------------------
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 creating such a request context is
an unnecessarily expensive operation in case there is no request around,
the application context was introduced.
If you try to access :data:`current_app`, or anything that uses it,
outside an application context, you'll get this error message:
Creating an Application Context
-------------------------------
.. code-block:: pytb
There are two ways to make an application context. The first one is
implicit: whenever a request context is pushed, an application context
will be created alongside if this is necessary. As a result, you can
ignore the existence of the application context unless you need it.
RuntimeError: Working outside of application context.
The second way is the explicit way using the
:meth:`~flask.Flask.app_context` method::
This typically means that you attempted to use functionality that
needed to interface with the current application object in some way.
To solve this, set up an application context with app.app_context().
from flask import Flask, current_app
If you see that error while configuring your application, such as when
initializing an extension, you can push a context manually since you
have direct access to the ``app``. Use :meth:`~Flask.app_context` in a
``with`` block, and everything that runs in the block will have access
to :data:`current_app`. ::
def create_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.
If no request context has been pushed and an application context has
not been explicitly set, a ``RuntimeError`` will be raised. ::
RuntimeError: Working outside of application context.
with app.app_context():
init_db()
Locality of the Context
-----------------------
return app
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 and should put their
information there, instead of on the :data:`flask.g` object which is reserved
for user code.
If you see that error somewhere else in your code not related to
configuring the application, it most likely indicates that you should
move that code into a view function or CLI command.
For more information about that, see :ref:`extension-dev`.
Context Usage
-------------
Storing Data
------------
The context is typically used to cache resources that need to be created
on a per-request or usage case. For instance, database connections are
destined to go there. When storing things on the application context
unique names should be chosen as this is a place that is shared between
Flask applications and extensions.
The application context is a good place to store common data during a
request or CLI command. Flask provides the :data:`g object <g>` for this
purpose. It is a simple namespace object that has the same lifetime as
an application context.
The most common usage is to split resource management into two parts:
.. note::
The ``g`` name stands for "global", but that is referring to the
data being global *within a context*. The data on ``g`` is lost
after the context ends, and it is not an appropriate place to store
data between requests. Use the :data:`session` or a database to
store data across requests.
1. an implicit resource caching on the context.
2. a context teardown based resource deallocation.
A common use for :data:`g` is to manage resources during a request.
Generally there would be a ``get_X()`` function that creates resource
``X`` if it does not exist yet and otherwise returns the same resource,
and a ``teardown_X()`` function that is registered as teardown handler.
1. ``get_X()`` creates resource ``X`` if it does not exist, caching it
as ``g.X``.
2. ``teardown_X()`` closes or otherwise deallocates the resource if it
exists. It is registered as a :meth:`~Flask.teardown_appcontext`
handler.
This is an example that connects to a database::
For example, you can manage a database connection using this pattern::
import sqlite3
from flask import g
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = connect_to_database()
return db
if 'db' not in g:
g.db = connect_to_database()
return g.db
@app.teardown_appcontext
def teardown_db(exception):
db = getattr(g, '_database', None)
def teardown_db():
db = g.pop('db', None)
if db is not None:
db.close()
The first time ``get_db()`` is called the connection will be established.
To make this implicit a :class:`~werkzeug.local.LocalProxy` can be used::
During a request, every call to ``get_db()`` will return the same
connection, and it will be closed automatically at the end of the
request.
You can use :class:`~werkzeug.local.LocalProxy` to make a new context
local from ``get_db()``::
from werkzeug.local import LocalProxy
db = LocalProxy(get_db)
That way a user can directly access ``db`` which internally calls
``get_db()``.
Accessing ``db`` will call ``get_db`` internally, in the same way that
:data:`current_app` works.
----
If you're writing an extension, :data:`g` should be reserved for user
code. You may store internal data on the context itself, but be sure to
use a sufficiently unique name. The current context is accessed with
:data:`_app_ctx_stack.top <_app_ctx_stack>`. For more information see
:doc:`extensiondev`.
Events and Signals
------------------
The application will call functions registered with
:meth:`~Flask.teardown_appcontext` when the application context is
popped.
If :data:`~signals.signals_available` is true, the following signals are
sent: :data:`appcontext_pushed`, :data:`appcontext_tearing_down`, and
:data:`appcontext_popped`.

379
docs/reqcontext.rst

@ -1,179 +1,245 @@
.. currentmodule:: flask
.. _request-context:
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.
The request context keeps track of the request-level data during a
request. Rather than passing the request object to each function that
runs during a request, the :data:`request` and :data:`session` proxies
are accessed instead.
This is similar to the :doc:`/appcontext`, which keeps track of the
application-level data independent of a request. A corresponding
application context is pushed when a request context is pushed.
It is recommended that you read the :ref:`app-context` chapter first.
Purpose of the Context
----------------------
Diving into Context Locals
--------------------------
When the :class:`Flask` application handles a request, it creates a
:class:`Request` object based on the environment it received from the
WSGI server. Because a *worker* (thread, process, or coroutine depending
on the server) handles only one request at a time, the request data can
be considered global to that worker during that request. Flask uses the
term *context local* for this.
Say you have a utility function that returns the URL the user should be
redirected to. Imagine it would always redirect to the URL's ``next``
parameter or the HTTP referrer or the index page::
Flask automatically *pushes* a request context when handling a request.
View functions, error handlers, and other functions that run during a
request will have access to the :data:`request` proxy, which points to
the request object for the current request.
from flask import request, url_for
def redirect_url():
return request.args.get('next') or \
request.referrer or \
url_for('index')
Lifetime of the Context
-----------------------
As you can see, it accesses the request object. If you try to run this
from a plain Python shell, this is the exception you will see:
When a Flask application begins handling a request, it pushes a request
context, which also pushes an :doc:`/appcontext`. When the request ends
it pops the request context then the application context.
>>> redirect_url()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'request'
The context is unique to each thread (or other worker type).
:data:`request` cannot be passed to another thread, the other thread
will have a different context stack and will not know about the request
the parent thread was pointing to.
That makes a lot of sense because we currently do not have a request we
could access. So we have to make a request and bind it to the current
context. The :attr:`~flask.Flask.test_request_context` method can create
us a :class:`~flask.ctx.RequestContext`:
Context locals are implemented in Werkzeug. See :doc:`werkzeug:local`
for more information on how this works internally.
>>> ctx = app.test_request_context('/?next=http://example.com/')
This context can be used in two ways. Either with the ``with`` statement
or by calling the :meth:`~flask.ctx.RequestContext.push` and
:meth:`~flask.ctx.RequestContext.pop` methods:
Manually Push a Context
-----------------------
>>> ctx.push()
If you try to access :data:`request`, or anything that uses it, outside
a request context, you'll get this error message:
From that point onwards you can work with the request object:
.. code-block:: pytb
>>> redirect_url()
u'http://example.com/'
RuntimeError: Working outside of request context.
Until you call `pop`:
This typically means that you attempted to use functionality that
needed an active HTTP request. Consult the documentation on testing
for information about how to avoid this problem.
This should typically only happen when testing code that expects an
active request. One option is to use the
:meth:`test client <Flask.test_client>` to simulate a full request. Or
you can use :meth:`~Flask.test_request_context` in a ``with`` block, and
everything that runs in the block will have access to :data:`request`,
populated with your test data. ::
def generate_report(year):
format = request.args.get('format')
...
>>> ctx.pop()
with app.test_request_context(
'/make_report/2017', data={'format': 'short'}):
generate_report()
Because the request context is internally maintained as a stack you can
push and pop multiple times. This is very handy to implement things like
internal redirects.
If you see that error somewhere else in your code not related to
testing, it most likely indicates that you should move that code into a
view function.
For information on how to use the request context from the interactive
Python shell, see :doc:`/shell`.
For more information of how to utilize the request context from the
interactive Python shell, head over to the :ref:`shell` chapter.
How the Context Works
---------------------
If you look into how the Flask WSGI application internally works, you will
find a piece of code that looks very much like this::
def wsgi_app(self, environ):
with self.request_context(environ):
try:
response = self.full_dispatch_request()
except Exception as e:
response = self.make_response(self.handle_exception(e))
return response(environ, start_response)
The method :meth:`~Flask.request_context` returns a new
:class:`~flask.ctx.RequestContext` object and uses it in combination with
the ``with`` statement to bind the context. Everything that is called from
the same thread from this point onwards until the end of the ``with``
statement will have access to the request globals (:data:`flask.request`
and others).
The request context internally works like a stack: The topmost level on
the stack is the current active request.
:meth:`~flask.ctx.RequestContext.push` adds the context to the stack on
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.
The :meth:`Flask.wsgi_app` method is called to handle each request. It
manages the contexts during the request. Internally, the request and
application contexts work as stacks, :data:`_request_ctx_stack` and
:data:`_app_ctx_stack`. When contexts are pushed onto the stack, the
proxies that depend on them are available and point at information from
the top context on the stack.
When the request starts, a :class:`~ctx.RequestContext` is created and
pushed, which creates and pushes an :class:`~ctx.AppContext` first if
a context for that application is not already the top context. While
these contexts are pushed, the :data:`current_app`, :data:`g`,
:data:`request`, and :data:`session` proxies are available to the
original thread handling the request.
Because the contexts are stacks, other contexts may be pushed to change
the proxies during a request. While this is not a common pattern, it
can be used in advanced applications to, for example, do internal
redirects or chain different applications together.
After the request is dispatched and a response is generated and sent,
the request context is popped, which then pops the application context.
Immediately before they are popped, the :meth:`~Flask.teardown_request`
and :meth:`~Flask.teardown_appcontext` functions are are executed. These
execute even if an unhandled exception occurred during dispatch.
.. _callbacks-and-errors:
Callbacks and Errors
--------------------
What happens if an error occurs in Flask during request processing? This
particular behavior changed in 0.7 because we wanted to make it easier to
understand what is actually happening. The new behavior is quite simple:
Flask dispatches a request in multiple stages which can affect the
request, response, and how errors are handled. The contexts are active
during all of these stages.
1. Before each request, :meth:`~flask.Flask.before_request` functions are
executed. If one of these functions return a response, the other
functions are no longer called. In any case however the return value
is treated as a replacement for the view's return value.
A :class:`Blueprint` can add handlers for these events that are specific
to the blueprint. The handlers for a blueprint will run if the blueprint
owns the route that matches the request.
2. If the :meth:`~flask.Flask.before_request` functions did not return a
response, the regular request handling kicks in and the view function
that was matched has the chance to return a response.
#. Before each request, :meth:`~Flask.before_request` functions are
called. If one of these functions return a value, the other
functions are skipped. The return value is treated as the response
and the view function is not called.
3. The return value of the view is then converted into an actual response
object and handed over to the :meth:`~flask.Flask.after_request`
functions which have the chance to replace it or modify it in place.
#. If the :meth:`~Flask.before_request` functions did not return a
response, the view function for the matched route is called and
returns a response.
4. At the end of the request the :meth:`~flask.Flask.teardown_request`
functions are executed. This always happens, even in case of an
unhandled exception down the road or if a before-request handler was
not executed yet or at all (for example in test environments sometimes
you might want to not execute before-request callbacks).
#. The return value of the view is converted into an actual response
object and passed to the :meth:`~Flask.after_request`
functions. Each function returns a modified or new response object.
Now what happens on errors? If you are not in debug mode and an exception is not
caught, the 500 internal server handler is called. In debug mode
however the exception is not further processed and bubbles up to the WSGI
server. That way things like the interactive debugger can provide helpful
debug information.
#. After the response is returned, the contexts are popped, which calls
the :meth:`~Flask.teardown_request` and
:meth:`~Flask.teardown_appcontext` functions. These functions are
called even if an unhandled exception was raised at any point above.
An important change in 0.7 is that the internal server error is now no
longer post processed by the after request callbacks and after request
callbacks are no longer guaranteed to be executed. This way the internal
dispatching code looks cleaner and is easier to customize and understand.
If an exception is raised before the teardown functions, Flask tries to
match it with an :meth:`~Flask.errorhandler` function to handle the
exception and return a response. If no error handler is found, or the
handler itself raises an exception, Flask returns a generic
``500 Internal Server Error`` response. The teardown functions are still
called, and are passed the exception object.
If debug mode is enabled, unhandled exceptions are not converted to a
``500`` response and instead are propagated to the WSGI server. This
allows the development server to present the interactive debugger with
the traceback.
The new teardown functions are supposed to be used as a replacement for
things that absolutely need to happen at the end of request.
Teardown Callbacks
------------------
The teardown callbacks are special callbacks in that they are executed at
a different point. Strictly speaking they are independent of the actual
request handling as they are bound to the lifecycle of the
:class:`~flask.ctx.RequestContext` object. When the request context is
popped, the :meth:`~flask.Flask.teardown_request` functions are called.
This is important to know if the life of the request context is prolonged
by using the test client in a with statement or when using the request
context from the command line::
with app.test_client() as client:
resp = client.get('/foo')
# the teardown functions are still not called at that point
# even though the response ended and you have the response
# object in your hand
# only when the code reaches this point the teardown functions
# are called. Alternatively the same thing happens if another
# request was triggered from the test client
It's easy to see the behavior from the command line:
>>> app = Flask(__name__)
>>> @app.teardown_request
... def teardown_request(exception=None):
... print 'this runs after request'
...
>>> ctx = app.test_request_context()
>>> ctx.push()
>>> ctx.pop()
this runs after request
>>>
Keep in mind that teardown callbacks are always executed, even if
before-request callbacks were not executed yet but an exception happened.
Certain parts of the test system might also temporarily create a request
context without calling the before-request handlers. Make sure to write
your teardown-request handlers in a way that they will never fail.
~~~~~~~~~~~~~~~~~~
The teardown callbacks are independent of the request dispatch, and are
instead called by the contexts when they are popped. The functions are
called even if there is an unhandled exception during dispatch, and for
manually pushed contexts. This means there is no guarantee that any
other parts of the request dispatch have run first. Be sure to write
these functions in a way that does not depend on other callbacks and
will not fail.
During testing, it can be useful to defer popping the contexts after the
request ends, so that their data can be accessed in the test function.
Using the :meth:`~Flask.test_client` as a ``with`` block to preserve the
contexts until the with block exits.
.. code-block:: python
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello():
print('during view')
return 'Hello, World!'
@app.teardown_request
def show_teardown(exception):
print('after with block')
with app.test_request_context():
print('during with block')
# teardown functions are called after the context with block exits
with app.test_client():
client.get('/')
# the contexts are not popped even though the request ended
print(request.path)
# the contexts are popped and teardown functions are called after
# the client with block exists
Signals
~~~~~~~
If :data:`~signals.signals_available` is true, the following signals are
sent:
#. :data:`request_started` is sent before the
:meth:`~Flask.before_request` functions are called.
#. :data:`request_finished` is sent after the
:meth:`~Flask.after_request` functions are called.
#. :data:`got_request_exception` is sent when an exception begins to
be handled, but before an :meth:`~Flask.errorhandler` is looked up or
called.
#. :data:`request_tearing_down` is sent after the
:meth:`~Flask.teardown_request` functions are called.
Context Preservation on Error
-----------------------------
At the end of a request, the request context is popped and all data
associated with it is destroyed. If an error occurs during development,
it is useful to delay destroying the data for debugging purposes.
When the development server is running in development mode (the
``FLASK_ENV`` environment variable is set to ``'development'``), the
error and data will be preserved and shown in the interactive debugger.
This behavior can be controlled with the
:data:`PRESERVE_CONTEXT_ON_EXCEPTION` config. As described above, it
defaults to ``True`` in the development environment.
Do not enable :data:`PRESERVE_CONTEXT_ON_EXCEPTION` in production, as it
will cause your application to leak memory on exceptions.
.. _notes-on-proxies:
@ -181,44 +247,21 @@ Notes On Proxies
----------------
Some of the objects provided by Flask are proxies to other objects. The
reason behind this is that these proxies are shared between threads and
they have to dispatch to the actual object bound to a thread behind the
scenes as necessary.
proxies are accessed in the same way for each worker thread, but
point to the unique object bound to each worker behind the scenes as
described on this page.
Most of the time you don't have to care about that, but there are some
exceptions where it is good to know that this object is an actual proxy:
- The proxy objects do not fake their inherited types, so if you want to
perform actual instance checks, you have to do that on the instance
that is being proxied (see `_get_current_object` below).
- if the object reference is important (so for example for sending
:ref:`signals`)
- The proxy objects cannot fake their type as the actual object types.
If you want to perform instance checks, you have to do that on the
object being proxied.
- If the specific object reference is important, for example for
sending :ref:`signals` or passing data to a background thread.
If you need to get access to the underlying object that is proxied, you
can use the :meth:`~werkzeug.local.LocalProxy._get_current_object` method::
If you need to access the underlying object that is proxied, use the
:meth:`~werkzeug.local.LocalProxy._get_current_object` method::
app = current_app._get_current_object()
my_signal.send(app)
Context Preservation on Error
-----------------------------
If an error occurs or not, at the end of the request the request context
is popped and all data associated with it is destroyed. During
development however that can be problematic as you might want to have the
information around for a longer time in case an exception occurred. In
Flask 0.6 and earlier in debug mode, if an exception occurred, the
request context was not popped so that the interactive debugger can still
provide you with important information.
Starting with Flask 0.7 you have finer control over that behavior by
setting the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. By
default it's linked to the setting of ``DEBUG``. If the application is in
debug mode the context is preserved. If debug mode is set to off, the context
is not preserved.
Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` if debug mode is set to off
as it will cause your application to leak memory on exceptions. However,
it can be useful during development to get the same error preserving
behavior as debug mode when attempting to debug an error that
only occurs under production settings.

161
flask/app.py

@ -2106,15 +2106,25 @@ class Flask(_PackageBoundObject):
return response
def do_teardown_request(self, exc=_sentinel):
"""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.
"""Called after the request is dispatched and the response is
returned, right before the request context is popped.
This calls all functions decorated with
:meth:`teardown_request`, and :meth:`Blueprint.teardown_request`
if a blueprint handled the request. Finally, the
:data:`request_tearing_down` signal is sent.
This is called by
:meth:`RequestContext.pop() <flask.ctx.RequestContext.pop>`,
which may be delayed during testing to maintain access to
resources.
:param exc: An unhandled exception raised while dispatching the
request. Detected from the current exception information if
not passed. Passed to each teardown function.
.. versionchanged:: 0.9
Added the `exc` argument. Previously this was always using the
current exception information.
Added the ``exc`` argument.
"""
if exc is _sentinel:
exc = sys.exc_info()[1]
@ -2127,9 +2137,17 @@ class Flask(_PackageBoundObject):
request_tearing_down.send(self, exc=exc)
def do_teardown_appcontext(self, exc=_sentinel):
"""Called when an application context is popped. This works pretty
much the same as :meth:`do_teardown_request` but for the application
context.
"""Called right before the application context is popped.
When handling a request, the application context is popped
after the request context. See :meth:`do_teardown_request`.
This calls all functions decorated with
:meth:`teardown_appcontext`. Then the
:data:`appcontext_tearing_down` signal is sent.
This is called by
:meth:`AppContext.pop() <flask.ctx.AppContext.pop>`.
.. versionadded:: 0.9
"""
@ -2140,62 +2158,89 @@ class Flask(_PackageBoundObject):
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.
"""Create an :class:`~flask.ctx.AppContext`. Use as a ``with``
block to push the context, which will make :data:`current_app`
point at this application.
An application context is automatically pushed by
:meth:`RequestContext.push() <flask.ctx.RequestContext.push>`
when handling a request, and when running a CLI command. Use
this to manually create a context outside of these situations.
Example usage::
::
with app.app_context():
...
init_db()
See :doc:`/appcontext`.
.. versionadded:: 0.9
"""
return AppContext(self)
def request_context(self, environ):
"""Creates a :class:`~flask.ctx.RequestContext` from the given
environment and binds it to the current context. This must be used in
combination with the ``with`` statement because the request is only bound
to the current context for the duration of the ``with`` block.
Example usage::
"""Create a :class:`~flask.ctx.RequestContext` representing a
WSGI environment. Use a ``with`` block to push the context,
which will make :data:`request` point at this request.
with app.request_context(environ):
do_something_with(request)
See :doc:`/reqcontext`.
The object returned can also be used without the ``with`` statement
which is useful for working in the shell. The example above is
doing exactly the same as this code::
ctx = app.request_context(environ)
ctx.push()
try:
do_something_with(request)
finally:
ctx.pop()
.. versionchanged:: 0.3
Added support for non-with statement usage and ``with`` statement
is now passed the ctx object.
Typically you should not call this from your own code. A request
context is automatically pushed by the :meth:`wsgi_app` when
handling a request. Use :meth:`test_request_context` to create
an environment and context instead of this method.
:param environ: a WSGI environment
"""
return RequestContext(self, environ)
def test_request_context(self, *args, **kwargs):
"""Creates a :class:`~flask.ctx.RequestContext` from the given values
(see :class:`werkzeug.test.EnvironBuilder` for more information, this
function accepts the same arguments plus two additional).
"""Create a :class:`~flask.ctx.RequestContext` for a WSGI
environment created from the given values. This is mostly useful
during testing, where you may want to run a function that uses
request data without dispatching a full request.
Additional arguments (only if ``base_url`` is not specified):
See :doc:`/reqcontext`.
:param subdomain: subdomain to use for route matching
:param url_scheme: scheme for the request, default
``PREFERRED_URL_SCHEME`` or ``http``.
"""
Use a ``with`` block to push the context, which will make
:data:`request` point at the request for the created
environment. ::
with test_request_context(...):
generate_report()
When using the shell, it may be easier to push and pop the
context manually to avoid indentation. ::
ctx = app.test_request_context(...)
ctx.push()
...
ctx.pop()
Takes the same arguments as Werkzeug's
:class:`~werkzeug.test.EnvironBuilder`, with some defaults from
the application. See the linked Werkzeug docs for most of the
available arguments. Flask-specific behavior is listed here.
:param path: URL path being requested.
:param base_url: Base URL where the app is being served, which
``path`` is relative to. If not given, built from
:data:`PREFERRED_URL_SCHEME`, ``subdomain``,
:data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`.
:param subdomain: Subdomain name to append to
:data:`SERVER_NAME`.
:param url_scheme: Scheme to use instead of
:data:`PREFERRED_URL_SCHEME`.
:param data: The request body, either as a string or a dict of
form keys and values.
:param json: If given, this is serialized as JSON and passed as
``data``. Also defaults ``content_type`` to
``application/json``.
:param args: other positional arguments passed to
:class:`~werkzeug.test.EnvironBuilder`.
:param kwargs: other keyword arguments passed to
:class:`~werkzeug.test.EnvironBuilder`.
"""
from flask.testing import make_test_environ_builder
builder = make_test_environ_builder(self, *args, **kwargs)
@ -2207,8 +2252,8 @@ class Flask(_PackageBoundObject):
def wsgi_app(self, environ, start_response):
"""The actual WSGI application. This is not implemented in
`__call__` so that middlewares can be applied without losing a
reference to the class. So instead of doing this::
:meth:`__call__` so that middlewares can be applied without
losing a reference to the app object. Instead of doing this::
app = MyMiddleware(app)
@ -2220,15 +2265,15 @@ class Flask(_PackageBoundObject):
can continue to call methods on it.
.. versionchanged:: 0.7
The behavior of the before and after request callbacks was changed
under error conditions and a new callback was added that will
always execute at the end of the request, independent on if an
error occurred or not. See :ref:`callbacks-and-errors`.
Teardown events for the request and app contexts are called
even if an unhandled error occurs. Other events may not be
called depending on when an error occurs during dispatch.
See :ref:`callbacks-and-errors`.
:param environ: a WSGI environment
:param start_response: a callable accepting a status code,
a list of headers and an optional
exception context to start the response
:param environ: A WSGI environment.
:param start_response: A callable accepting a status code,
a list of headers, and an optional exception context to
start the response.
"""
ctx = self.request_context(environ)
error = None
@ -2249,7 +2294,9 @@ class Flask(_PackageBoundObject):
ctx.auto_pop(error)
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`."""
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
def __repr__(self):

44
flask/ctx.py

@ -24,18 +24,60 @@ _sentinel = object()
class _AppCtxGlobals(object):
"""A plain object."""
"""A plain object. Used as a namespace for storing data during an
application context.
Creating an app context automatically creates this object, which is
made available as the :data:`g` proxy.
.. describe:: 'key' in g
Check whether an attribute is present.
.. versionadded:: 0.10
.. describe:: iter(g)
Return an iterator over the attribute names.
.. versionadded:: 0.10
"""
def get(self, name, default=None):
"""Get an attribute by name, or a default value. Like
:meth:`dict.get`.
:param name: Name of attribute to get.
:param default: Value to return if the attribute is not present.
.. versionadded:: 0.10
"""
return self.__dict__.get(name, default)
def pop(self, name, default=_sentinel):
"""Get and remove an attribute by name. Like :meth:`dict.pop`.
:param name: Name of attribute to pop.
:param default: Value to return if the attribute is not present,
instead of raise a ``KeyError``.
.. versionadded:: 0.11
"""
if default is _sentinel:
return self.__dict__.pop(name)
else:
return self.__dict__.pop(name, default)
def setdefault(self, name, default=None):
"""Get the value of an attribute if it is present, otherwise
set and return a default value. Like :meth:`dict.setdefault`.
:param name: Name of attribute to get.
:param: default: Value to set and return if the attribute is not
present.
.. versionadded:: 0.11
"""
return self.__dict__.setdefault(name, default)
def __contains__(self, item):

4
flask/globals.py

@ -25,8 +25,8 @@ _app_ctx_err_msg = '''\
Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in a way. To solve
this set up an application context with app.app_context(). See the
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.\
'''

21
flask/testing.py

@ -25,7 +25,26 @@ def make_test_environ_builder(
app, path='/', base_url=None, subdomain=None, url_scheme=None,
*args, **kwargs
):
"""Creates a new test builder with some application defaults thrown in."""
"""Create a :class:`~werkzeug.test.EnvironBuilder`, taking some
defaults from the application.
:param app: The Flask application to configure the environment from.
:param path: URL path being requested.
:param base_url: Base URL where the app is being served, which
``path`` is relative to. If not given, built from
:data:`PREFERRED_URL_SCHEME`, ``subdomain``,
:data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`.
:param subdomain: Subdomain name to append to :data:`SERVER_NAME`.
:param url_scheme: Scheme to use instead of
:data:`PREFERRED_URL_SCHEME`.
:param json: If given, this is serialized as JSON and passed as
``data``. Also defaults ``content_type`` to
``application/json``.
:param args: other positional arguments passed to
:class:`~werkzeug.test.EnvironBuilder`.
:param kwargs: other keyword arguments passed to
:class:`~werkzeug.test.EnvironBuilder`.
"""
assert (
not (base_url or subdomain or url_scheme)

Loading…
Cancel
Save