mirror of https://github.com/python/cpython
gh-124872: Refine contextvars documentation (#124773)
* Add definitions for "context", "current context", and "context management protocol". * Update related definitions to be consistent with the new definitions. * Restructure the documentation for the `contextvars.Context` class to prepare for adding context manager support, and for consistency with the definitions. * Use `testcode` and `testoutput` to test the `Context.run` example. * Expand the documentation for the `Py_CONTEXT_EVENT_ENTER` and `Py_CONTEXT_EVENT_EXIT` events to clarify and to prepare for planned changes.
This commit is contained in:
parent
942916378a
commit
99400930ac
|
@ -122,18 +122,24 @@ Context object management functions:
|
|||
.. c:type:: PyContextEvent
|
||||
|
||||
Enumeration of possible context object watcher events:
|
||||
- ``Py_CONTEXT_EVENT_ENTER``
|
||||
- ``Py_CONTEXT_EVENT_EXIT``
|
||||
|
||||
- ``Py_CONTEXT_EVENT_ENTER``: A context has been entered, causing the
|
||||
:term:`current context` to switch to it. The object passed to the watch
|
||||
callback is the now-current :class:`contextvars.Context` object. Each
|
||||
enter event will eventually have a corresponding exit event for the same
|
||||
context object after any subsequently entered contexts have themselves been
|
||||
exited.
|
||||
- ``Py_CONTEXT_EVENT_EXIT``: A context is about to be exited, which will
|
||||
cause the :term:`current context` to switch back to what it was before the
|
||||
context was entered. The object passed to the watch callback is the
|
||||
still-current :class:`contextvars.Context` object.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:type:: int (*PyContext_WatchCallback)(PyContextEvent event, PyContext* ctx)
|
||||
|
||||
Type of a context object watcher callback function.
|
||||
If *event* is ``Py_CONTEXT_EVENT_ENTER``, then the callback is invoked
|
||||
after *ctx* has been set as the current context for the current thread.
|
||||
Otherwise, the callback is invoked before the deactivation of *ctx* as the current context
|
||||
and the restoration of the previous contex object for the current thread.
|
||||
Context object watcher callback function. The object passed to the callback
|
||||
is event-specific; see :c:type:`PyContextEvent` for details.
|
||||
|
||||
If the callback returns with an exception set, it must return ``-1``; this
|
||||
exception will be printed as an unraisable exception using
|
||||
|
|
|
@ -265,19 +265,33 @@ Glossary
|
|||
advanced mathematical feature. If you're not aware of a need for them,
|
||||
it's almost certain you can safely ignore them.
|
||||
|
||||
context
|
||||
This term has different meanings depending on where and how it is used.
|
||||
Some common meanings:
|
||||
|
||||
* The temporary state or environment established by a :term:`context
|
||||
manager` via a :keyword:`with` statement.
|
||||
* The collection of keyvalue bindings associated with a particular
|
||||
:class:`contextvars.Context` object and accessed via
|
||||
:class:`~contextvars.ContextVar` objects. Also see :term:`context
|
||||
variable`.
|
||||
* A :class:`contextvars.Context` object. Also see :term:`current
|
||||
context`.
|
||||
|
||||
context management protocol
|
||||
The :meth:`~object.__enter__` and :meth:`~object.__exit__` methods called
|
||||
by the :keyword:`with` statement. See :pep:`343`.
|
||||
|
||||
context manager
|
||||
An object which controls the environment seen in a :keyword:`with`
|
||||
statement by defining :meth:`~object.__enter__` and :meth:`~object.__exit__` methods.
|
||||
See :pep:`343`.
|
||||
An object which implements the :term:`context management protocol` and
|
||||
controls the environment seen in a :keyword:`with` statement. See
|
||||
:pep:`343`.
|
||||
|
||||
context variable
|
||||
A variable which can have different values depending on its context.
|
||||
This is similar to Thread-Local Storage in which each execution
|
||||
thread may have a different value for a variable. However, with context
|
||||
variables, there may be several contexts in one execution thread and the
|
||||
main usage for context variables is to keep track of variables in
|
||||
A variable whose value depends on which context is the :term:`current
|
||||
context`. Values are accessed via :class:`contextvars.ContextVar`
|
||||
objects. Context variables are primarily used to isolate state between
|
||||
concurrent asynchronous tasks.
|
||||
See :mod:`contextvars`.
|
||||
|
||||
contiguous
|
||||
.. index:: C-contiguous, Fortran contiguous
|
||||
|
@ -311,6 +325,14 @@ Glossary
|
|||
is used when necessary to distinguish this implementation from others
|
||||
such as Jython or IronPython.
|
||||
|
||||
current context
|
||||
The :term:`context` (:class:`contextvars.Context` object) that is
|
||||
currently used by :class:`~contextvars.ContextVar` objects to access (get
|
||||
or set) the values of :term:`context variables <context variable>`. Each
|
||||
thread has its own current context. Frameworks for executing asynchronous
|
||||
tasks (see :mod:`asyncio`) associate each task with a context which
|
||||
becomes the current context whenever the task starts or resumes execution.
|
||||
|
||||
decorator
|
||||
A function returning another function, usually applied as a function
|
||||
transformation using the ``@wrapper`` syntax. Common examples for
|
||||
|
|
|
@ -144,36 +144,67 @@ Manual Context Management
|
|||
To get a copy of the current context use the
|
||||
:func:`~contextvars.copy_context` function.
|
||||
|
||||
Every thread will have a different top-level :class:`~contextvars.Context`
|
||||
object. This means that a :class:`ContextVar` object behaves in a similar
|
||||
fashion to :func:`threading.local` when values are assigned in different
|
||||
threads.
|
||||
Each thread has its own effective stack of :class:`!Context` objects. The
|
||||
:term:`current context` is the :class:`!Context` object at the top of the
|
||||
current thread's stack. All :class:`!Context` objects in the stacks are
|
||||
considered to be *entered*.
|
||||
|
||||
*Entering* a context, which can be done by calling its :meth:`~Context.run`
|
||||
method, makes the context the current context by pushing it onto the top of
|
||||
the current thread's context stack.
|
||||
|
||||
*Exiting* from the current context, which can be done by returning from the
|
||||
callback passed to the :meth:`~Context.run` method, restores the current
|
||||
context to what it was before the context was entered by popping the context
|
||||
off the top of the context stack.
|
||||
|
||||
Since each thread has its own context stack, :class:`ContextVar` objects
|
||||
behave in a similar fashion to :func:`threading.local` when values are
|
||||
assigned in different threads.
|
||||
|
||||
Attempting to enter an already entered context, including contexts entered in
|
||||
other threads, raises a :exc:`RuntimeError`.
|
||||
|
||||
After exiting a context, it can later be re-entered (from any thread).
|
||||
|
||||
Any changes to :class:`ContextVar` values via the :meth:`ContextVar.set`
|
||||
method are recorded in the current context. The :meth:`ContextVar.get`
|
||||
method returns the value associated with the current context. Exiting a
|
||||
context effectively reverts any changes made to context variables while the
|
||||
context was entered (if needed, the values can be restored by re-entering the
|
||||
context).
|
||||
|
||||
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.
|
||||
Enters the Context, executes ``callable(*args, **kwargs)``, then exits the
|
||||
Context. Returns *callable*'s return value, or propagates an exception if
|
||||
one occurred.
|
||||
|
||||
Any changes to any context variables that *callable* makes will
|
||||
be contained in the context object::
|
||||
Example:
|
||||
|
||||
var = ContextVar('var')
|
||||
.. testcode::
|
||||
|
||||
import contextvars
|
||||
|
||||
var = contextvars.ContextVar('var')
|
||||
var.set('spam')
|
||||
print(var.get()) # 'spam'
|
||||
|
||||
ctx = contextvars.copy_context()
|
||||
|
||||
def main():
|
||||
# 'var' was set to 'spam' before
|
||||
# calling 'copy_context()' and 'ctx.run(main)', so:
|
||||
# var.get() == ctx[var] == 'spam'
|
||||
print(var.get()) # 'spam'
|
||||
print(ctx[var]) # 'spam'
|
||||
|
||||
var.set('ham')
|
||||
|
||||
# Now, after setting 'var' to 'ham':
|
||||
# var.get() == ctx[var] == 'ham'
|
||||
|
||||
ctx = copy_context()
|
||||
print(var.get()) # 'ham'
|
||||
print(ctx[var]) # 'ham'
|
||||
|
||||
# Any changes that the 'main' function makes to 'var'
|
||||
# will be contained in 'ctx'.
|
||||
|
@ -181,14 +212,21 @@ Manual Context Management
|
|||
|
||||
# The 'main()' function was run in the 'ctx' context,
|
||||
# so changes to 'var' are contained in it:
|
||||
# ctx[var] == 'ham'
|
||||
print(ctx[var]) # 'ham'
|
||||
|
||||
# However, outside of 'ctx', 'var' is still set to 'spam':
|
||||
# var.get() == 'spam'
|
||||
print(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.
|
||||
.. testoutput::
|
||||
:hide:
|
||||
|
||||
spam
|
||||
spam
|
||||
spam
|
||||
ham
|
||||
ham
|
||||
ham
|
||||
spam
|
||||
|
||||
.. method:: copy()
|
||||
|
||||
|
|
|
@ -28,15 +28,26 @@ PyAPI_FUNC(int) PyContext_Enter(PyObject *);
|
|||
PyAPI_FUNC(int) PyContext_Exit(PyObject *);
|
||||
|
||||
typedef enum {
|
||||
/*
|
||||
* A context has been entered, causing the "current context" to switch to
|
||||
* it. The object passed to the watch callback is the now-current
|
||||
* contextvars.Context object. Each enter event will eventually have a
|
||||
* corresponding exit event for the same context object after any
|
||||
* subsequently entered contexts have themselves been exited.
|
||||
*/
|
||||
Py_CONTEXT_EVENT_ENTER,
|
||||
/*
|
||||
* A context is about to be exited, which will cause the "current context"
|
||||
* to switch back to what it was before the context was entered. The
|
||||
* object passed to the watch callback is the still-current
|
||||
* contextvars.Context object.
|
||||
*/
|
||||
Py_CONTEXT_EVENT_EXIT,
|
||||
} PyContextEvent;
|
||||
|
||||
/*
|
||||
* Callback to be invoked when a context object is entered or exited.
|
||||
*
|
||||
* The callback is invoked with the event and a reference to
|
||||
* the context after its entered and before its exited.
|
||||
* Context object watcher callback function. The object passed to the callback
|
||||
* is event-specific; see PyContextEvent for details.
|
||||
*
|
||||
* if the callback returns with an exception set, it must return -1. Otherwise
|
||||
* it should return 0
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Added definitions for :term:`context`, :term:`current context`, and
|
||||
:term:`context management protocol`, updated related definitions to be
|
||||
consistent, and expanded the documentation for :class:`contextvars.Context`.
|
Loading…
Reference in New Issue