Pep567 v2 (#524)
* pep-567: Update C API; get_context() -> copy_context() * pep-567: Add ref implementation; update C API; copy_context()
This commit is contained in:
parent
f7316723e9
commit
25324f863f
60
pep-0567.rst
60
pep-0567.rst
|
@ -8,7 +8,7 @@ Type: Standards Track
|
|||
Content-Type: text/x-rst
|
||||
Created: 12-Dec-2017
|
||||
Python-Version: 3.7
|
||||
Post-History: 12-Dec-2017
|
||||
Post-History: 12-Dec-2017, 28-Dec-2017
|
||||
|
||||
|
||||
Abstract
|
||||
|
@ -25,6 +25,10 @@ difference is that this PEP is concerned only with solving the case
|
|||
for asynchronous tasks, not for generators. There are no proposed
|
||||
modifications to any built-in types or to the interpreter.
|
||||
|
||||
This proposal is not strictly related to Python Context Managers.
|
||||
Although it does provide a mechanism that can be used by Context
|
||||
Managers to store their state.
|
||||
|
||||
|
||||
Rationale
|
||||
=========
|
||||
|
@ -73,7 +77,7 @@ may have different values for the same key. This idea is well-known
|
|||
from thread-local storage but in this case the locality of the value is
|
||||
not necessarily bound to a thread. Instead, there is the notion of the
|
||||
"current ``Context``" which is stored in thread-local storage, and
|
||||
is accessed via ``contextvars.get_context()`` function.
|
||||
is accessed via ``contextvars.copy_context()`` function.
|
||||
Manipulation of the current ``Context`` is the responsibility of the
|
||||
task framework, e.g. asyncio.
|
||||
|
||||
|
@ -94,8 +98,8 @@ Specification
|
|||
A new standard library module ``contextvars`` is added with the
|
||||
following APIs:
|
||||
|
||||
1. ``get_context() -> Context`` function is used to get the current
|
||||
``Context`` object for the current OS thread.
|
||||
1. ``copy_context() -> Context`` function is used to get a copy of
|
||||
the current ``Context`` object for the current OS thread.
|
||||
|
||||
2. ``ContextVar`` class to declare and access context variables.
|
||||
|
||||
|
@ -180,11 +184,11 @@ contextvars.Context
|
|||
|
||||
``Context`` object is a mapping of context variables to values.
|
||||
|
||||
``Context()`` creates an empty context. To get the current ``Context``
|
||||
for the current OS thread, use the ``contextvars.get_context()``
|
||||
method::
|
||||
``Context()`` creates an empty context. To get a copy of the current
|
||||
``Context`` for the current OS thread, use the
|
||||
``contextvars.copy_context()`` method::
|
||||
|
||||
ctx = contextvars.get_context()
|
||||
ctx = contextvars.copy_context()
|
||||
|
||||
To run Python code in some ``Context``, use ``Context.run()``
|
||||
method::
|
||||
|
@ -203,7 +207,7 @@ be contained in the ``ctx`` context::
|
|||
var.set('ham')
|
||||
assert var.get() == 'ham'
|
||||
|
||||
ctx = get_context()
|
||||
ctx = copy_context()
|
||||
|
||||
# Any changes that 'function' makes to 'var' will stay
|
||||
# isolated in the 'ctx'.
|
||||
|
@ -219,7 +223,7 @@ callbacks and Tasks are executed. It can also be used to run some
|
|||
code in a different thread in the context of the current thread::
|
||||
|
||||
executor = ThreadPoolExecutor()
|
||||
current_context = contextvars.get_context()
|
||||
current_context = contextvars.copy_context()
|
||||
|
||||
executor.submit(
|
||||
lambda: current_context.run(some_function))
|
||||
|
@ -227,7 +231,7 @@ code in a different thread in the context of the current thread::
|
|||
``Context`` objects implement the ``collections.abc.Mapping`` ABC.
|
||||
This can be used to introspect context objects::
|
||||
|
||||
ctx = contextvars.get_context()
|
||||
ctx = contextvars.copy_context()
|
||||
|
||||
# Print all context variables and their values in 'ctx':
|
||||
print(ctx.items())
|
||||
|
@ -250,7 +254,7 @@ keyword-only argument, which defaults to the current context::
|
|||
|
||||
def call_soon(self, callback, *args, context=None):
|
||||
if context is None:
|
||||
context = contextvars.get_context()
|
||||
context = contextvars.copy_context()
|
||||
|
||||
# ... some time later
|
||||
context.run(callback, *args)
|
||||
|
@ -263,7 +267,7 @@ as follows::
|
|||
def __init__(self, coro):
|
||||
...
|
||||
# Get the current context snapshot.
|
||||
self._context = contextvars.get_context()
|
||||
self._context = contextvars.copy_context()
|
||||
self._loop.call_soon(self._step, context=self._context)
|
||||
|
||||
def _step(self, exc=None):
|
||||
|
@ -280,8 +284,14 @@ C API
|
|||
1. ``PyContextVar * PyContextVar_New(char *name, PyObject *default)``:
|
||||
create a ``ContextVar`` object.
|
||||
|
||||
2. ``PyObject * PyContextVar_Get(PyContextVar *)``:
|
||||
return the value of the variable in the current context.
|
||||
2. ``int PyContextVar_Get(PyContextVar *, PyObject *default_value, PyObject **value)``:
|
||||
return ``-1`` if an error occurs during the lookup, ``0`` otherwise.
|
||||
If a value for the context variable is found, it will be set to the
|
||||
``value`` pointer. Otherwise, ``value`` will be set to
|
||||
``default_value`` when it is not ``NULL``. If ``default_value`` is
|
||||
``NULL``, ``value`` will be set to the default value of the
|
||||
variable, which can be ``NULL`` too. ``value`` is always a borrowed
|
||||
reference.
|
||||
|
||||
3. ``PyContextToken * PyContextVar_Set(PyContextVar *, PyObject *)``:
|
||||
set the value of the variable in the current context.
|
||||
|
@ -291,14 +301,14 @@ C API
|
|||
|
||||
5. ``PyContext * PyContext_New()``: create a new empty context.
|
||||
|
||||
6. ``PyContext * PyContext_Get()``: get the current context.
|
||||
6. ``PyContext * PyContext_Copy()``: get a copy of the current context.
|
||||
|
||||
7. ``int PyContext_Enter(PyContext *)`` and
|
||||
``int PyContext_Exit(PyContext *)`` allow to set and restore
|
||||
the context for the current OS thread. It is required to always
|
||||
restore the previous context::
|
||||
|
||||
PyContext *old_ctx = PyContext_Get();
|
||||
PyContext *old_ctx = PyContext_Copy();
|
||||
if (old_ctx == NULL) goto error;
|
||||
|
||||
if (PyContext_Enter(new_ctx)) goto error;
|
||||
|
@ -345,9 +355,9 @@ points to a ``_ContextData`` object::
|
|||
class PyThreadState:
|
||||
context_data: _ContextData
|
||||
|
||||
``contextvars.get_context()`` is implemented as follows::
|
||||
``contextvars.copy_context()`` is implemented as follows::
|
||||
|
||||
def get_context():
|
||||
def copy_context():
|
||||
ts : PyThreadState = PyThreadState_Get()
|
||||
|
||||
if ts.context_data is None:
|
||||
|
@ -456,7 +466,7 @@ Implementation Notes
|
|||
|
||||
* The internal immutable dictionary for ``Context`` is implemented
|
||||
using Hash Array Mapped Tries (HAMT). They allow for O(log N)
|
||||
``set`` operation, and for O(1) ``get_context()`` function, where
|
||||
``set`` operation, and for O(1) ``copy_context()`` function, where
|
||||
*N* is the number of items in the dictionary. For a detailed
|
||||
analysis of HAMT performance please refer to :pep:`550` [1]_.
|
||||
|
||||
|
@ -472,7 +482,7 @@ Summary of the New APIs
|
|||
=======================
|
||||
|
||||
* A new ``contextvars`` module with ``ContextVar``, ``Context``,
|
||||
and ``Token`` classes, and a ``get_context()`` function.
|
||||
and ``Token`` classes, and a ``copy_context()`` function.
|
||||
|
||||
* ``asyncio.Loop.call_at()``, ``asyncio.Loop.call_later()``,
|
||||
``asyncio.Loop.call_soon()``, and
|
||||
|
@ -541,6 +551,12 @@ code unmodified, but will automatically enable support for
|
|||
asynchronous code.
|
||||
|
||||
|
||||
Reference Implementation
|
||||
========================
|
||||
|
||||
The reference implementation can be found here: [3]_.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
|
@ -548,6 +564,8 @@ References
|
|||
|
||||
.. [2] https://www.python.org/dev/peps/pep-0550/#replication-of-threading-local-interface
|
||||
|
||||
.. [3] https://github.com/python/cpython/pull/5027
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
|
Loading…
Reference in New Issue