:mod:`contextvars` --- Context Variables
.. module:: contextvars :synopsis: Context Variables
.. sectionauthor:: Yury Selivanov <[email protected]>
This module provides APIs to manage, store, and access context-local state. The :class:`~contextvars.ContextVar` class is used to declare and work with Context Variables. The :func:`~contextvars.copy_context` function and the :class:`~contextvars.Context` class should be used to manage the current context in asynchronous frameworks.
Context managers that have state should use Context Variables instead of :func:`threading.local()` to prevent their state from bleeding to other code unexpectedly, when used in concurrent code.
See also PEP 567 for additional details.
.. versionadded:: 3.7
Token objects are returned by the :meth:`ContextVar.set` method. They can be passed to the :meth:`ContextVar.reset` method to revert the value of the variable to what it was before the corresponding set.
.. attribute:: Token.var A read-only property. Points to the :class:`ContextVar` object that created the token.
.. attribute:: Token.old_value A read-only property. Set to the value the variable had before the :meth:`ContextVar.set` method call that created the token. It points to :attr:`Token.MISSING` is the variable was not set before the call.
.. attribute:: Token.MISSING A marker object used by :attr:`Token.old_value`.
.. function:: copy_context()
Returns a copy of the current :class:`~contextvars.Context` object.
The following snippet gets a copy of the current context and prints
all variables and their values that are set in it::
ctx: Context = copy_context()
print(list(ctx.items()))
The function has an O(1) complexity, i.e. works equally fast for
contexts with a few context variables and for contexts that have
a lot of them.
A mapping of :class:`ContextVars <ContextVar>` to their values.
Context() creates an empty context with no values in it.
To get a copy of the current context use the
:func:`~contextvars.copy_context` function.
Context implements the :class:`collections.abc.Mapping` interface.
.. method:: run(callable, \*args, \*\*kwargs)
Execute ``callable(*args, **kwargs)`` code in the context object
the *run* method is called on. Return the result of the execution
or propagate an exception if one occurred.
Any changes to any context variables that *callable* makes will
be contained in the context object::
var = ContextVar('var')
var.set('spam')
def main():
# 'var' was set to 'spam' before
# calling 'copy_context()' and 'ctx.run(main)', so:
# var.get() == ctx[var] == 'spam'
var.set('ham')
# Now, after setting 'var' to 'ham':
# var.get() == ctx[var] == 'ham'
ctx = copy_context()
# Any changes that the 'main' function makes to 'var'
# will be contained in 'ctx'.
ctx.run(main)
# The 'main()' function was run in the 'ctx' context,
# so changes to 'var' are contained in it:
# ctx[var] == 'ham'
# However, outside of 'ctx', 'var' is still set to 'spam':
# var.get() == 'spam'
The method raises a :exc:`RuntimeError` when called on the same
context object from more than one OS thread, or when called
recursively.
.. method:: copy() Return a shallow copy of the context object.
.. describe:: var in context Return ``True`` if the *context* has a value for *var* set; return ``False`` otherwise.
.. describe:: context[var] Return the value of the *var* :class:`ContextVar` variable. If the variable is not set in the context object, a :exc:`KeyError` is raised.
.. method:: get(var, [default]) Return the value for *var* if *var* has the value in the context object. Return *default* otherwise. If *default* is not given, return ``None``.
.. describe:: iter(context) Return an iterator over the variables stored in the context object.
.. describe:: len(proxy) Return the number of variables set in the context object.
.. method:: keys() Return a list of all variables in the context object.
.. method:: values() Return a list of all variables' values in the context object.
.. method:: items() Return a list of 2-tuples containing all variables and their values in the context object.
Context variables are natively supported in :mod:`asyncio` and are ready to be used without any extra configuration. For example, here is a simple echo server, that uses a context variable to make the address of a remote client available in the Task that handles that client:
import asyncio
import contextvars
client_addr_var = contextvars.ContextVar('client_addr')
def render_goodbye():
# The address of the currently handled client can be accessed
# without passing it explicitly to this function.
client_addr = client_addr_var.get()
return f'Good bye, client @ {client_addr}\n'.encode()
async def handle_request(reader, writer):
addr = writer.transport.get_extra_info('socket').getpeername()
client_addr_var.set(addr)
# In any code that we call is now possible to get
# client's address by calling 'client_addr_var.get()'.
while True:
line = await reader.readline()
print(line)
if not line.strip():
break
writer.write(line)
writer.write(render_goodbye())
writer.close()
async def main():
srv = await asyncio.start_server(
handle_request, '127.0.0.1', 8081)
async with srv:
await srv.serve_forever()
asyncio.run(main())
# To test it you can use telnet:
# telnet 127.0.0.1 8081