python-werkzeug/docs/local.rst

111 lines
3.6 KiB
ReStructuredText

Context Locals
==============
.. module:: werkzeug.local
You may find that you have some data during each request that you want
to use across functions. Instead of passing these as arguments between
every function, you may want to access them as global data. However,
using global variables in Python web applications is not thread safe;
different workers might interfere with each others' data.
Instead of storing common data during a request using global variables,
you must use context-local variables instead. A context local is
defined/imported globally, but the data it contains is specific to the
current thread, asyncio task, or greenlet. You won't accidentally get
or overwrite another worker's data.
The current approach for storing per-context data in Python is the
:class:`contextvars` module. Context vars store data per thread, async
task, or greenlet. This replaces the older :class:`threading.local`
which only handled threads.
Werkzeug provides wrappers around :class:`~contextvars.ContextVar` to
make it easier to work with.
Proxy Objects
=============
:class:`LocalProxy` allows treating a context var as an object directly
instead of needing to use and check
:meth:`ContextVar.get() <contextvars.ContextVar.get>`. If the context
var is set, the local proxy will look and behave like the object the var
is set to. If it's not set, a ``RuntimeError`` is raised for most
operations.
.. code-block:: python
from contextvars import ContextVar
from werkzeug.local import LocalProxy
_request_var = ContextVar("request")
request = LocalProxy(_request_var)
from werkzeug.wrappers import Request
@Request.application
def app(r):
_request_var.set(r)
check_auth()
...
from werkzeug.exceptions import Unauthorized
def check_auth():
if request.form["username"] != "admin":
raise Unauthorized()
Accessing ``request`` will point to the specific request that each
server worker is handling. You can treat ``request`` just like an actual
``Request`` object.
``bool(proxy)`` will always return ``False`` if the var is not set. If
you need access to the object directly instead of the proxy, you can get
it with the :meth:`~LocalProxy._get_current_object` method.
.. autoclass:: LocalProxy
:members: _get_current_object
Stacks and Namespaces
=====================
:class:`~contextvars.ContextVar` stores one value at a time. You may
find that you need to store a stack of items, or a namespace with
multiple attributes. A list or dict can be used for these, but using
them as context var values requires some extra care. Werkzeug provides
:class:`LocalStack` which wraps a list, and :class:`Local` which wraps a
dict.
There is some amount of performance penalty associated with these
objects. Because lists and dicts are mutable, :class:`LocalStack` and
:class:`Local` need to do extra work to ensure data isn't shared between
nested contexts. If possible, design your application to use
:class:`LocalProxy` around a context var directly.
.. autoclass:: LocalStack
:members: push, pop, top, __call__
.. autoclass:: Local
:members: __call__
Releasing Data
==============
A previous implementation of ``Local`` used internal data structures
which could not be cleaned up automatically when each context ended.
Instead, the following utilities could be used to release the data.
.. warning::
This should not be needed with the modern implementation, as the
data in context vars is automatically managed by Python. It is kept
for compatibility for now, but may be removed in the future.
.. autoclass:: LocalManager
:members: cleanup, make_middleware, middleware
.. autofunction:: release_local