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
|
.. c:type:: PyContextEvent
|
||||||
|
|
||||||
Enumeration of possible context object watcher events:
|
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
|
.. versionadded:: 3.14
|
||||||
|
|
||||||
.. c:type:: int (*PyContext_WatchCallback)(PyContextEvent event, PyContext* ctx)
|
.. c:type:: int (*PyContext_WatchCallback)(PyContextEvent event, PyContext* ctx)
|
||||||
|
|
||||||
Type of a context object watcher callback function.
|
Context object watcher callback function. The object passed to the callback
|
||||||
If *event* is ``Py_CONTEXT_EVENT_ENTER``, then the callback is invoked
|
is event-specific; see :c:type:`PyContextEvent` for details.
|
||||||
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.
|
|
||||||
|
|
||||||
If the callback returns with an exception set, it must return ``-1``; this
|
If the callback returns with an exception set, it must return ``-1``; this
|
||||||
exception will be printed as an unraisable exception using
|
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,
|
advanced mathematical feature. If you're not aware of a need for them,
|
||||||
it's almost certain you can safely ignore 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
|
context manager
|
||||||
An object which controls the environment seen in a :keyword:`with`
|
An object which implements the :term:`context management protocol` and
|
||||||
statement by defining :meth:`~object.__enter__` and :meth:`~object.__exit__` methods.
|
controls the environment seen in a :keyword:`with` statement. See
|
||||||
See :pep:`343`.
|
:pep:`343`.
|
||||||
|
|
||||||
context variable
|
context variable
|
||||||
A variable which can have different values depending on its context.
|
A variable whose value depends on which context is the :term:`current
|
||||||
This is similar to Thread-Local Storage in which each execution
|
context`. Values are accessed via :class:`contextvars.ContextVar`
|
||||||
thread may have a different value for a variable. However, with context
|
objects. Context variables are primarily used to isolate state between
|
||||||
variables, there may be several contexts in one execution thread and the
|
|
||||||
main usage for context variables is to keep track of variables in
|
|
||||||
concurrent asynchronous tasks.
|
concurrent asynchronous tasks.
|
||||||
See :mod:`contextvars`.
|
|
||||||
|
|
||||||
contiguous
|
contiguous
|
||||||
.. index:: C-contiguous, Fortran contiguous
|
.. index:: C-contiguous, Fortran contiguous
|
||||||
|
@ -311,6 +325,14 @@ Glossary
|
||||||
is used when necessary to distinguish this implementation from others
|
is used when necessary to distinguish this implementation from others
|
||||||
such as Jython or IronPython.
|
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
|
decorator
|
||||||
A function returning another function, usually applied as a function
|
A function returning another function, usually applied as a function
|
||||||
transformation using the ``@wrapper`` syntax. Common examples for
|
transformation using the ``@wrapper`` syntax. Common examples for
|
||||||
|
|
|
@ -144,51 +144,89 @@ Manual Context Management
|
||||||
To get a copy of the current context use the
|
To get a copy of the current context use the
|
||||||
:func:`~contextvars.copy_context` function.
|
:func:`~contextvars.copy_context` function.
|
||||||
|
|
||||||
Every thread will have a different top-level :class:`~contextvars.Context`
|
Each thread has its own effective stack of :class:`!Context` objects. The
|
||||||
object. This means that a :class:`ContextVar` object behaves in a similar
|
:term:`current context` is the :class:`!Context` object at the top of the
|
||||||
fashion to :func:`threading.local` when values are assigned in different
|
current thread's stack. All :class:`!Context` objects in the stacks are
|
||||||
threads.
|
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.
|
Context implements the :class:`collections.abc.Mapping` interface.
|
||||||
|
|
||||||
.. method:: run(callable, *args, **kwargs)
|
.. method:: run(callable, *args, **kwargs)
|
||||||
|
|
||||||
Execute ``callable(*args, **kwargs)`` code in the context object
|
Enters the Context, executes ``callable(*args, **kwargs)``, then exits the
|
||||||
the *run* method is called on. Return the result of the execution
|
Context. Returns *callable*'s return value, or propagates an exception if
|
||||||
or propagate an exception if one occurred.
|
one occurred.
|
||||||
|
|
||||||
Any changes to any context variables that *callable* makes will
|
Example:
|
||||||
be contained in the context object::
|
|
||||||
|
|
||||||
var = ContextVar('var')
|
.. testcode::
|
||||||
var.set('spam')
|
|
||||||
|
|
||||||
def main():
|
import contextvars
|
||||||
# 'var' was set to 'spam' before
|
|
||||||
# calling 'copy_context()' and 'ctx.run(main)', so:
|
|
||||||
# var.get() == ctx[var] == 'spam'
|
|
||||||
|
|
||||||
var.set('ham')
|
var = contextvars.ContextVar('var')
|
||||||
|
var.set('spam')
|
||||||
|
print(var.get()) # 'spam'
|
||||||
|
|
||||||
# Now, after setting 'var' to 'ham':
|
ctx = contextvars.copy_context()
|
||||||
# var.get() == ctx[var] == 'ham'
|
|
||||||
|
|
||||||
ctx = copy_context()
|
def main():
|
||||||
|
# 'var' was set to 'spam' before
|
||||||
|
# calling 'copy_context()' and 'ctx.run(main)', so:
|
||||||
|
print(var.get()) # 'spam'
|
||||||
|
print(ctx[var]) # 'spam'
|
||||||
|
|
||||||
# Any changes that the 'main' function makes to 'var'
|
var.set('ham')
|
||||||
# will be contained in 'ctx'.
|
|
||||||
ctx.run(main)
|
|
||||||
|
|
||||||
# The 'main()' function was run in the 'ctx' context,
|
# Now, after setting 'var' to 'ham':
|
||||||
# so changes to 'var' are contained in it:
|
print(var.get()) # 'ham'
|
||||||
# ctx[var] == 'ham'
|
print(ctx[var]) # 'ham'
|
||||||
|
|
||||||
# However, outside of 'ctx', 'var' is still set to 'spam':
|
# Any changes that the 'main' function makes to 'var'
|
||||||
# var.get() == 'spam'
|
# will be contained in 'ctx'.
|
||||||
|
ctx.run(main)
|
||||||
|
|
||||||
The method raises a :exc:`RuntimeError` when called on the same
|
# The 'main()' function was run in the 'ctx' context,
|
||||||
context object from more than one OS thread, or when called
|
# so changes to 'var' are contained in it:
|
||||||
recursively.
|
print(ctx[var]) # 'ham'
|
||||||
|
|
||||||
|
# However, outside of 'ctx', 'var' is still set to 'spam':
|
||||||
|
print(var.get()) # 'spam'
|
||||||
|
|
||||||
|
.. testoutput::
|
||||||
|
:hide:
|
||||||
|
|
||||||
|
spam
|
||||||
|
spam
|
||||||
|
spam
|
||||||
|
ham
|
||||||
|
ham
|
||||||
|
ham
|
||||||
|
spam
|
||||||
|
|
||||||
.. method:: copy()
|
.. method:: copy()
|
||||||
|
|
||||||
|
|
|
@ -28,15 +28,26 @@ PyAPI_FUNC(int) PyContext_Enter(PyObject *);
|
||||||
PyAPI_FUNC(int) PyContext_Exit(PyObject *);
|
PyAPI_FUNC(int) PyContext_Exit(PyObject *);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
Py_CONTEXT_EVENT_ENTER,
|
/*
|
||||||
Py_CONTEXT_EVENT_EXIT,
|
* 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;
|
} PyContextEvent;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback to be invoked when a context object is entered or exited.
|
* Context object watcher callback function. The object passed to the callback
|
||||||
*
|
* is event-specific; see PyContextEvent for details.
|
||||||
* The callback is invoked with the event and a reference to
|
|
||||||
* the context after its entered and before its exited.
|
|
||||||
*
|
*
|
||||||
* if the callback returns with an exception set, it must return -1. Otherwise
|
* if the callback returns with an exception set, it must return -1. Otherwise
|
||||||
* it should return 0
|
* 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