Hi, It seems like many people, including myself, are confused by the lack of concrete current context in the PEP 567 (contextvars). But it isn't difficult to implement the current context (I implemented it, see below). It might have a *minor* impact on performance, but Context mapping API is supposed to only be used for introspection according to Yury.
With contextvars.get_context(), it becomes possible to introspect the current context, rather than a copy, and it becomes even more obvious than a context is mutable ;-) vstinner@apu$ ./python >>> import contextvars >>> ctx=contextvars.get_context() >>> name=contextvars.ContextVar('name', default='victor') >>> print(list(ctx.items())) [] >>> name.set('yury') <Token object at 0x7f4cc71f8ad8> >>> print(list(ctx.items())) [(<ContextVar name='name' default='victor' at 0x7f4cc71f18d8>, 'yury')] With my changes, the running context remains up to date in Context.run(). Example: --- import contextvars name = contextvars.ContextVar('name', default='victor') def func(): name.set('yury') print(f"context[name]: {context.get(name)}") print(f"name in context: {name in context}") context = contextvars.copy_context() context.run(func) --- Output: --- context[name]: yury name in context: True --- Compare it to the output without my changes: --- context[name]: None name in context: False --- If we have contextvars.get_context(), maybe contextvars.copy_context() can be removed and add a new Context.copy() method instead: new_context = contextvars.get_context().copy() *** I implemented contextvars.get_context() which returns the current context: https://github.com/vstinner/cpython/commit/1e5ee71c15e2b1387c888d6eca2b08ef14595130 from https://github.com/vstinner/cpython/commits/current_context I added a context thread local storage (TLS): class PyThreadState: context: Context # can be None context_data: _ContextData I modified Context mapping API to use the context variables from the current thread state if it's the current thread state. PyContext_Enter() now not only sets PyThreadState.context_data, but also PyThreadState.context to itself. Pseudo-code for Context.get(): --- class Context(collections.abc.Mapping): def __init__(self): self._data = _ContextData() def _get_data(self): ts : PyThreadState = PyThreadState_Get() if ts.context is self: return ts.context_data else: return self._data def get(self, var): # FIXME: implement default data = self._get_data() return data.get(var) --- And Context.run() is modified to set also the context TLS: --- def run(self, callable, *args, **kwargs): ts : PyThreadState = PyThreadState_Get() saved_context : Optional[Context] = ts.context # NEW saved_data : _ContextData = ts.context_data try: ts.context_data = self._data return callable(*args, **kwargs) finally: self._data = ts.context_data ts.context = saved_context # NEW ts.context_data = saved_data --- Victor _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com