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:
Yury Selivanov 2017-12-28 01:01:05 -05:00 committed by GitHub
parent f7316723e9
commit 25324f863f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 39 additions and 21 deletions

View File

@ -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
=========