Browse Source

Merge pull request #2685 from davidism/app-context-docs

rework context docs
pull/2676/head
David Lord 7 years ago committed by GitHub
parent
commit
16d83d6bb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 93
      docs/api.rst
  2. 211
      docs/appcontext.rst
  3. 381
      docs/reqcontext.rst
  4. 163
      flask/app.py
  5. 44
      flask/ctx.py
  6. 4
      flask/globals.py
  7. 21
      flask/testing.py

93
docs/api.rst

@ -211,29 +211,22 @@ thing, like it does for :class:`request` and :class:`session`.
.. data:: g .. data:: g
Just store on this whatever you want. For example a database A namespace object that can store data during an
connection or the user that is currently logged in. :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 This is a good place to store resources during a request. During
no longer on the request context which means it becomes available if testing, you can use the :ref:`faking-resources` pattern to
only the application context is bound and not yet a request. This pre-configure such resources.
is especially useful when combined with the :ref:`faking-resources`
pattern for testing.
Additionally as of 0.10 you can use the :meth:`get` method to This is a proxy. See :ref:`notes-on-proxies` for more information.
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) .. versionchanged:: 0.10
user = flask.g.get('user', None) Bound to the application context instead of the request context.
It's now also possible to use the ``in`` operator on it to see if an .. autoclass:: flask.ctx._AppCtxGlobals
attribute is defined and it yields all keys on iteration. :members:
As of 0.11 you can use :meth:`pop` and :meth:`setdefault` in the same
way you would use them on a dictionary.
This is a proxy. See :ref:`notes-on-proxies` for more information.
Useful Functions and Classes Useful Functions and Classes
@ -241,13 +234,17 @@ Useful Functions and Classes
.. data:: current_app .. data:: current_app
Points to the application handling the request. This is useful for A proxy to the application handling the current request. This is
extensions that want to support multiple applications running side useful to access the application without needing to import it, or if
by side. This is powered by the application context and not by the it can't be imported, such as when using the application factory
request context, so you can change the value of this proxy by pattern or in blueprints and extensions.
using the :meth:`~flask.Flask.app_context` method.
This is a proxy. See :ref:`notes-on-proxies` for more information. 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.
.. autofunction:: has_request_context .. autofunction:: has_request_context
@ -384,50 +381,54 @@ Useful Internals
.. data:: _request_ctx_stack .. data:: _request_ctx_stack
The internal :class:`~werkzeug.local.LocalStack` that is used to implement The internal :class:`~werkzeug.local.LocalStack` that holds
all the context local objects used in Flask. This is a documented :class:`~flask.ctx.RequestContext` instances. Typically, the
instance and can be used by extensions and application code but the :data:`request` and :data:`session` proxies should be accessed
use is discouraged in general. 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 The following attributes are always present on each layer of the
stack: stack:
`app` `app`
the active Flask application. the active Flask application.
`url_adapter` `url_adapter`
the URL adapter that was used to match the request. the URL adapter that was used to match the request.
`request` `request`
the current request object. the current request object.
`session` `session`
the active session object. the active session object.
`g` `g`
an object with all the attributes of the :data:`flask.g` object. an object with all the attributes of the :data:`flask.g` object.
`flashes` `flashes`
an internal cache for the flashed messages. an internal cache for the flashed messages.
Example usage:: Example usage::
from flask import _request_ctx_stack from flask import _request_ctx_stack
def get_session(): def get_session():
ctx = _request_ctx_stack.top ctx = _request_ctx_stack.top
if ctx is not None: if ctx is not None:
return ctx.session return ctx.session
.. autoclass:: flask.ctx.AppContext .. autoclass:: flask.ctx.AppContext
:members: :members:
.. data:: _app_ctx_stack .. data:: _app_ctx_stack
Works similar to the request context but only binds the application. The internal :class:`~werkzeug.local.LocalStack` that holds
This is mainly there for extensions to store data. :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 .. versionadded:: 0.9
.. autoclass:: flask.blueprints.BlueprintSetupState .. autoclass:: flask.blueprints.BlueprintSetupState
:members: :members:

211
docs/appcontext.rst

@ -1,144 +1,159 @@
.. currentmodule:: flask
.. _app-context: .. _app-context:
The Application 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
One of the design ideas behind Flask is that there are at least two application around to each function, the :data:`current_app` and
different “states” in which code is executed: :data:`g` proxies are accessed instead.
1. The application setup state, in which the application implicitly is
on the module level.
This state starts when the :class:`Flask` object is instantiated, and This is similar to the :doc:`/reqcontext`, which keeps track of
it implicitly ends when the first request comes in. While the request-level data during a request. A corresponding application context
application is in this state, a few assumptions are true: is pushed when a request context is pushed.
- the programmer can modify the application object safely. Purpose of the Context
- 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.
2. In contrast, in the request handling state, a couple of other rules The :class:`Flask` application object has attributes, such as
exist: :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.
- while a request is active, the context local objects Flask solves this issue with the *application context*. Rather than
(:data:`flask.request` and others) point to the current request. referring to an ``app`` directly, you use the the :data:`current_app`
- any code can get hold of these objects at any time. proxy, which points to the application handling the current activity.
3. There is also a third state somewhere in between 'module-level' and Flask automatically *pushes* an application context when handling a
'request-handling': request. View functions, error handlers, and other functions that run
during a request will have access to :data:`current_app`.
Sometimes you are dealing with an application in a way that is similar to Flask will also automatically push an app context when running CLI
how you interact with applications during request handling, but without commands registered with :attr:`Flask.cli` using ``@app.cli.command()``.
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.
The application context is what powers the :data:`~flask.current_app`
context local.
Purpose of the Application Context Lifetime of the Context
---------------------------------- -----------------------
The main reason for the application's context existence is that in the The application context is created and destroyed as necessary. When a
past a bunch of functionality was attached to the request context for lack Flask application begins handling a request, it pushes an application
of a better solution. Since one of the pillars of Flask's design is that context and a :doc:`request context </reqcontext>`. When the request
you can have more than one application in the same Python process. ends it pops the request context then the application context.
Typically, an application context will have the same lifetime as a
request.
So how does the code find the “right” application? In the past we See :doc:`/reqcontext` for more information about how the contexts work
recommended passing applications around explicitly, but that caused issues and the full lifecycle of a request.
with libraries that were not designed with that in mind.
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.
Creating an Application Context Manually Push a Context
------------------------------- -----------------------
There are two ways to make an application context. The first one is If you try to access :data:`current_app`, or anything that uses it,
implicit: whenever a request context is pushed, an application context outside an application context, you'll get this error message:
will be created alongside if this is necessary. As a result, you can
ignore the existence of the application context unless you need it.
The second way is the explicit way using the .. code-block:: pytb
:meth:`~flask.Flask.app_context` method::
from flask import Flask, current_app RuntimeError: Working outside of application context.
app = Flask(__name__) This typically means that you attempted to use functionality that
with app.app_context(): needed to interface with the current application object in some way.
# within this block, current_app points to app. To solve this, set up an application context with app.app_context().
print current_app.name
The application context is also used by the :func:`~flask.url_for` If you see that error while configuring your application, such as when
function in case a ``SERVER_NAME`` was configured. This allows you to initializing an extension, you can push a context manually since you
generate URLs even in the absence of a request. 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`. ::
If no request context has been pushed and an application context has def create_app():
not been explicitly set, a ``RuntimeError`` will be raised. :: app = Flask(__name__)
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 If you see that error somewhere else in your code not related to
moves between threads and it will not be shared between requests. As such configuring the application, it most likely indicates that you should
it is the perfect place to store database connection information and other move that code into a view function or CLI command.
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.
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 The application context is a good place to store common data during a
on a per-request or usage case. For instance, database connections are request or CLI command. Flask provides the :data:`g object <g>` for this
destined to go there. When storing things on the application context purpose. It is a simple namespace object that has the same lifetime as
unique names should be chosen as this is a place that is shared between an application context.
Flask applications and extensions.
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. A common use for :data:`g` is to manage resources during a request.
2. a context teardown based resource deallocation.
Generally there would be a ``get_X()`` function that creates resource 1. ``get_X()`` creates resource ``X`` if it does not exist, caching it
``X`` if it does not exist yet and otherwise returns the same resource, as ``g.X``.
and a ``teardown_X()`` function that is registered as teardown handler. 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 from flask import g
def get_db(): def get_db():
db = getattr(g, '_database', None) if 'db' not in g:
if db is None: g.db = connect_to_database()
db = g._database = connect_to_database()
return db return g.db
@app.teardown_appcontext @app.teardown_appcontext
def teardown_db(exception): def teardown_db():
db = getattr(g, '_database', None) db = g.pop('db', None)
if db is not None: if db is not None:
db.close() db.close()
The first time ``get_db()`` is called the connection will be established. During a request, every call to ``get_db()`` will return the same
To make this implicit a :class:`~werkzeug.local.LocalProxy` can be used:: 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 from werkzeug.local import LocalProxy
db = LocalProxy(get_db) db = LocalProxy(get_db)
That way a user can directly access ``db`` which internally calls Accessing ``db`` will call ``get_db`` internally, in the same way that
``get_db()``. :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`.

381
docs/reqcontext.rst

@ -1,224 +1,267 @@
.. currentmodule:: flask
.. _request-context: .. _request-context:
The Request Context The Request Context
=================== ===================
This document describes the behavior in Flask 0.7 which is mostly in line The request context keeps track of the request-level data during a
with the old behavior but has some small, subtle differences. 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.
Purpose of the Context
----------------------
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.
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.
It is recommended that you read the :ref:`app-context` chapter first.
Diving into Context Locals Lifetime of the Context
-------------------------- -----------------------
Say you have a utility function that returns the URL the user should be When a Flask application begins handling a request, it pushes a request
redirected to. Imagine it would always redirect to the URL's ``next`` context, which also pushes an :doc:`/appcontext`. When the request ends
parameter or the HTTP referrer or the index page:: it pops the request context then the application context.
from flask import request, url_for 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.
def redirect_url(): Context locals are implemented in Werkzeug. See :doc:`werkzeug:local`
return request.args.get('next') or \ for more information on how this works internally.
request.referrer or \
url_for('index')
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:
>>> redirect_url() Manually Push a Context
Traceback (most recent call last): -----------------------
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'request'
That makes a lot of sense because we currently do not have a request we If you try to access :data:`request`, or anything that uses it, outside
could access. So we have to make a request and bind it to the current a request context, you'll get this error message:
context. The :attr:`~flask.Flask.test_request_context` method can create
us a :class:`~flask.ctx.RequestContext`:
>>> ctx = app.test_request_context('/?next=http://example.com/') .. code-block:: pytb
This context can be used in two ways. Either with the ``with`` statement RuntimeError: Working outside of request context.
or by calling the :meth:`~flask.ctx.RequestContext.push` and
:meth:`~flask.ctx.RequestContext.pop` methods:
>>> ctx.push() 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.
From that point onwards you can work with the request object: 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. ::
>>> redirect_url() def generate_report(year):
u'http://example.com/' format = request.args.get('format')
...
Until you call `pop`: with app.test_request_context(
'/make_report/2017', data={'format': 'short'}):
generate_report()
>>> ctx.pop() 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.
Because the request context is internally maintained as a stack you can For information on how to use the request context from the interactive
push and pop multiple times. This is very handy to implement things like Python shell, see :doc:`/shell`.
internal redirects.
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 How the Context Works
--------------------- ---------------------
If you look into how the Flask WSGI application internally works, you will The :meth:`Flask.wsgi_app` method is called to handle each request. It
find a piece of code that looks very much like this:: manages the contexts during the request. Internally, the request and
application contexts work as stacks, :data:`_request_ctx_stack` and
def wsgi_app(self, environ): :data:`_app_ctx_stack`. When contexts are pushed onto the stack, the
with self.request_context(environ): proxies that depend on them are available and point at information from
try: the top context on the stack.
response = self.full_dispatch_request()
except Exception as e: When the request starts, a :class:`~ctx.RequestContext` is created and
response = self.make_response(self.handle_exception(e)) pushed, which creates and pushes an :class:`~ctx.AppContext` first if
return response(environ, start_response) a context for that application is not already the top context. While
these contexts are pushed, the :data:`current_app`, :data:`g`,
The method :meth:`~Flask.request_context` returns a new :data:`request`, and :data:`session` proxies are available to the
:class:`~flask.ctx.RequestContext` object and uses it in combination with original thread handling the request.
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`` Because the contexts are stacks, other contexts may be pushed to change
statement will have access to the request globals (:data:`flask.request` the proxies during a request. While this is not a common pattern, it
and others). can be used in advanced applications to, for example, do internal
redirects or chain different applications together.
The request context internally works like a stack: The topmost level on
the stack is the current active request. After the request is dispatched and a response is generated and sent,
:meth:`~flask.ctx.RequestContext.push` adds the context to the stack on the request context is popped, which then pops the application context.
the very top, :meth:`~flask.ctx.RequestContext.pop` removes it from the Immediately before they are popped, the :meth:`~Flask.teardown_request`
stack again. On popping the application's and :meth:`~Flask.teardown_appcontext` functions are are executed. These
:func:`~flask.Flask.teardown_request` functions are also executed. execute even if an unhandled exception occurred during dispatch.
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
-------------------- --------------------
What happens if an error occurs in Flask during request processing? This Flask dispatches a request in multiple stages which can affect the
particular behavior changed in 0.7 because we wanted to make it easier to request, response, and how errors are handled. The contexts are active
understand what is actually happening. The new behavior is quite simple: during all of these stages.
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.
1. Before each request, :meth:`~flask.Flask.before_request` functions are #. Before each request, :meth:`~Flask.before_request` functions are
executed. If one of these functions return a response, the other called. If one of these functions return a value, the other
functions are no longer called. In any case however the return value functions are skipped. The return value is treated as the response
is treated as a replacement for the view's return value. and the view function is not called.
2. If the :meth:`~flask.Flask.before_request` functions did not return a #. If the :meth:`~Flask.before_request` functions did not return a
response, the regular request handling kicks in and the view function response, the view function for the matched route is called and
that was matched has the chance to return a response. returns a response.
3. The return value of the view is then converted into an actual response #. The return value of the view is converted into an actual response
object and handed over to the :meth:`~flask.Flask.after_request` object and passed to the :meth:`~Flask.after_request`
functions which have the chance to replace it or modify it in place. functions. Each function returns a modified or new response object.
4. At the end of the request the :meth:`~flask.Flask.teardown_request` #. After the response is returned, the contexts are popped, which calls
functions are executed. This always happens, even in case of an the :meth:`~Flask.teardown_request` and
unhandled exception down the road or if a before-request handler was :meth:`~Flask.teardown_appcontext` functions. These functions are
not executed yet or at all (for example in test environments sometimes called even if an unhandled exception was raised at any point above.
you might want to not execute before-request callbacks).
Now what happens on errors? If you are not in debug mode and an exception is not If an exception is raised before the teardown functions, Flask tries to
caught, the 500 internal server handler is called. In debug mode match it with an :meth:`~Flask.errorhandler` function to handle the
however the exception is not further processed and bubbles up to the WSGI exception and return a response. If no error handler is found, or the
server. That way things like the interactive debugger can provide helpful handler itself raises an exception, Flask returns a generic
debug information. ``500 Internal Server Error`` response. The teardown functions are still
called, and are passed the exception object.
An important change in 0.7 is that the internal server error is now no If debug mode is enabled, unhandled exceptions are not converted to a
longer post processed by the after request callbacks and after request ``500`` response and instead are propagated to the WSGI server. This
callbacks are no longer guaranteed to be executed. This way the internal allows the development server to present the interactive debugger with
dispatching code looks cleaner and is easier to customize and understand. 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 Teardown Callbacks
------------------ ~~~~~~~~~~~~~~~~~~
The teardown callbacks are special callbacks in that they are executed at The teardown callbacks are independent of the request dispatch, and are
a different point. Strictly speaking they are independent of the actual instead called by the contexts when they are popped. The functions are
request handling as they are bound to the lifecycle of the called even if there is an unhandled exception during dispatch, and for
:class:`~flask.ctx.RequestContext` object. When the request context is manually pushed contexts. This means there is no guarantee that any
popped, the :meth:`~flask.Flask.teardown_request` functions are called. 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
This is important to know if the life of the request context is prolonged will not fail.
by using the test client in a with statement or when using the request
context from the command line:: 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.
with app.test_client() as client: Using the :meth:`~Flask.test_client` as a ``with`` block to preserve the
resp = client.get('/foo') contexts until the with block exits.
# the teardown functions are still not called at that point
# even though the response ended and you have the response .. code-block:: python
# object in your hand
from flask import Flask, request
# only when the code reaches this point the teardown functions
# are called. Alternatively the same thing happens if another app = Flask(__name__)
# request was triggered from the test client
@app.route('/')
It's easy to see the behavior from the command line: def hello():
print('during view')
>>> app = Flask(__name__) return 'Hello, World!'
>>> @app.teardown_request
... def teardown_request(exception=None): @app.teardown_request
... print 'this runs after request' def show_teardown(exception):
... print('after with block')
>>> ctx = app.test_request_context()
>>> ctx.push() with app.test_request_context():
>>> ctx.pop() print('during with block')
this runs after request
>>> # teardown functions are called after the context with block exits
Keep in mind that teardown callbacks are always executed, even if with app.test_client():
before-request callbacks were not executed yet but an exception happened. client.get('/')
Certain parts of the test system might also temporarily create a request # the contexts are not popped even though the request ended
context without calling the before-request handlers. Make sure to write print(request.path)
your teardown-request handlers in a way that they will never fail.
# 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: .. _notes-on-proxies:
Notes On Proxies Notes On Proxies
---------------- ----------------
Some of the objects provided by Flask are proxies to other objects. The 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 proxies are accessed in the same way for each worker thread, but
they have to dispatch to the actual object bound to a thread behind the point to the unique object bound to each worker behind the scenes as
scenes as necessary. described on this page.
Most of the time you don't have to care about that, but there are some 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: 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 - The proxy objects cannot fake their type as the actual object types.
perform actual instance checks, you have to do that on the instance If you want to perform instance checks, you have to do that on the
that is being proxied (see `_get_current_object` below). object being proxied.
- if the object reference is important (so for example for sending - If the specific object reference is important, for example for
:ref:`signals`) sending :ref:`signals` or passing data to a background thread.
If you need to get access to the underlying object that is proxied, you If you need to access the underlying object that is proxied, use the
can use the :meth:`~werkzeug.local.LocalProxy._get_current_object` method:: :meth:`~werkzeug.local.LocalProxy._get_current_object` method::
app = current_app._get_current_object() app = current_app._get_current_object()
my_signal.send(app) 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.

163
flask/app.py

@ -2106,15 +2106,25 @@ class Flask(_PackageBoundObject):
return response return response
def do_teardown_request(self, exc=_sentinel): def do_teardown_request(self, exc=_sentinel):
"""Called after the actual request dispatching and will """Called after the request is dispatched and the response is
call every as :meth:`teardown_request` decorated function. This is returned, right before the request context is popped.
not actually called by the :class:`Flask` object itself but is always
triggered when the request context is popped. That way we have a This calls all functions decorated with
tighter control over certain resources under testing environments. :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 .. versionchanged:: 0.9
Added the `exc` argument. Previously this was always using the Added the ``exc`` argument.
current exception information.
""" """
if exc is _sentinel: if exc is _sentinel:
exc = sys.exc_info()[1] exc = sys.exc_info()[1]
@ -2127,9 +2137,17 @@ class Flask(_PackageBoundObject):
request_tearing_down.send(self, exc=exc) request_tearing_down.send(self, exc=exc)
def do_teardown_appcontext(self, exc=_sentinel): def do_teardown_appcontext(self, exc=_sentinel):
"""Called when an application context is popped. This works pretty """Called right before the application context is popped.
much the same as :meth:`do_teardown_request` but for the application
context. 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 .. versionadded:: 0.9
""" """
@ -2140,62 +2158,89 @@ class Flask(_PackageBoundObject):
appcontext_tearing_down.send(self, exc=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 """Create an :class:`~flask.ctx.AppContext`. Use as a ``with``
to the current context the :data:`flask.current_app` points to that block to push the context, which will make :data:`current_app`
application. An application context is automatically created when a point at this application.
request context is pushed if necessary.
Example usage:: 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.
::
with app.app_context(): with app.app_context():
... init_db()
See :doc:`/appcontext`.
.. versionadded:: 0.9 .. versionadded:: 0.9
""" """
return AppContext(self) return AppContext(self)
def request_context(self, environ): def request_context(self, environ):
"""Creates a :class:`~flask.ctx.RequestContext` from the given """Create a :class:`~flask.ctx.RequestContext` representing a
environment and binds it to the current context. This must be used in WSGI environment. Use a ``with`` block to push the context,
combination with the ``with`` statement because the request is only bound which will make :data:`request` point at this request.
to the current context for the duration of the ``with`` block.
Example usage::
with app.request_context(environ):
do_something_with(request)
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) See :doc:`/reqcontext`.
ctx.push()
try:
do_something_with(request)
finally:
ctx.pop()
.. versionchanged:: 0.3 Typically you should not call this from your own code. A request
Added support for non-with statement usage and ``with`` statement context is automatically pushed by the :meth:`wsgi_app` when
is now passed the ctx object. handling a request. Use :meth:`test_request_context` to create
an environment and context instead of this method.
:param environ: a WSGI environment :param environ: a WSGI environment
""" """
return RequestContext(self, environ) return RequestContext(self, environ)
def test_request_context(self, *args, **kwargs): def test_request_context(self, *args, **kwargs):
"""Creates a :class:`~flask.ctx.RequestContext` from the given values """Create a :class:`~flask.ctx.RequestContext` for a WSGI
(see :class:`werkzeug.test.EnvironBuilder` for more information, this environment created from the given values. This is mostly useful
function accepts the same arguments plus two additional). 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 Use a ``with`` block to push the context, which will make
:param url_scheme: scheme for the request, default :data:`request` point at the request for the created
``PREFERRED_URL_SCHEME`` or ``http``. 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 from flask.testing import make_test_environ_builder
builder = make_test_environ_builder(self, *args, **kwargs) builder = make_test_environ_builder(self, *args, **kwargs)
@ -2206,9 +2251,9 @@ class Flask(_PackageBoundObject):
builder.close() builder.close()
def wsgi_app(self, environ, start_response): def wsgi_app(self, environ, start_response):
"""The actual WSGI application. This is not implemented in """The actual WSGI application. This is not implemented in
`__call__` so that middlewares can be applied without losing a :meth:`__call__` so that middlewares can be applied without
reference to the class. So instead of doing this:: losing a reference to the app object. Instead of doing this::
app = MyMiddleware(app) app = MyMiddleware(app)
@ -2220,15 +2265,15 @@ class Flask(_PackageBoundObject):
can continue to call methods on it. can continue to call methods on it.
.. versionchanged:: 0.7 .. versionchanged:: 0.7
The behavior of the before and after request callbacks was changed Teardown events for the request and app contexts are called
under error conditions and a new callback was added that will even if an unhandled error occurs. Other events may not be
always execute at the end of the request, independent on if an called depending on when an error occurs during dispatch.
error occurred or not. See :ref:`callbacks-and-errors`. See :ref:`callbacks-and-errors`.
:param environ: a WSGI environment :param environ: A WSGI environment.
:param start_response: a callable accepting a status code, :param start_response: A callable accepting a status code,
a list of headers and an optional a list of headers, and an optional exception context to
exception context to start the response start the response.
""" """
ctx = self.request_context(environ) ctx = self.request_context(environ)
error = None error = None
@ -2249,7 +2294,9 @@ class Flask(_PackageBoundObject):
ctx.auto_pop(error) ctx.auto_pop(error)
def __call__(self, environ, start_response): 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) return self.wsgi_app(environ, start_response)
def __repr__(self): def __repr__(self):

44
flask/ctx.py

@ -24,18 +24,60 @@ _sentinel = object()
class _AppCtxGlobals(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): 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) return self.__dict__.get(name, default)
def pop(self, name, default=_sentinel): 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: if default is _sentinel:
return self.__dict__.pop(name) return self.__dict__.pop(name)
else: else:
return self.__dict__.pop(name, default) return self.__dict__.pop(name, default)
def setdefault(self, name, default=None): 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) return self.__dict__.setdefault(name, default)
def __contains__(self, item): def __contains__(self, item):

4
flask/globals.py

@ -25,8 +25,8 @@ _app_ctx_err_msg = '''\
Working outside of application context. Working outside of application context.
This typically means that you attempted to use functionality that needed This typically means that you attempted to use functionality that needed
to interface with the current application object in a way. To solve to interface with the current application object in some way. To solve
this set up an application context with app.app_context(). See the this, set up an application context with app.app_context(). See the
documentation for more information.\ 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, app, path='/', base_url=None, subdomain=None, url_scheme=None,
*args, **kwargs *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 ( assert (
not (base_url or subdomain or url_scheme) not (base_url or subdomain or url_scheme)

Loading…
Cancel
Save