I would like to reboot this discussion (again). It feels to me we're getting farther and farther from solving any of the problems we might solve.

I think we need to give up on doing anything about generators; the use cases point in too many conflicting directions. So we should keep the semantics there, and if you don't want your numeric or decimal context to leak out of a generator, don't put `yield` inside `with`. (Yury and Stefan have both remarked that this is not a problem in practice, given that there are no bug reports or StackOverflow questions about this topic.)

Nobody understands async generators, so let's not worry about them.

That leaves coroutines (`async def` and `await`). It looks like we don't want to change the original semantics here either, *except* when a framework like asyncio or Twisted has some kind of abstraction for a "task". (I intentionally don't define tasks, but task switches should be explicit, e.g. via `await` or some API -- note that even gevent qualifies, since it only switches when you make a blocking call.)

The key things we want then are (a) an interface to get and set context variables whose API is independent from the framework in use (if any), and (b) a way for a framework to decide when context variables are copied, shared or reinitialized.

For (a) I like the API from PEP 550:

var = contextvars.ContextVar('description')
value = var.get()
var.set(value)

It should be easy to adopt this e.g. in the decimal module instead of the current approach based on thread-local state.

For (b) I am leaning towards something simple that emulates thread-local state. Let's define "context" as a mutable mapping whose keys are ContextVar objects, tied to the current thread (each Python thread knows about exactly one context, which is deemed the current context). A framework can decide to clone the current context and assign it to a new task, or initialize a fresh context, etc. The one key feature we want here is that the right thing happens when we switch tasks via `await`, just as the right thing happens when we switch threads. (When a framework uses some other API to switch tasks, the framework do what it pleases.)

I don't have a complete design, but I don't want chained lookups, and I don't want to obsess over performance. (I would be fine with some kind of copy-on-write implementation, and switching out the current context should be fast.) I also don't want to obsess over API abstraction. Finally I don't want the design to be closely tied to `with`.

Maybe I need to write my own PEP?

--
--Guido van Rossum (python.org/~guido)