text: Abstract
  Contents
  While we generally try to avoid global state when possible, there nonetheless exist a number of situations where it is agreed to be the best approach. In Python, a standard pattern(mgr) tmp = yield foo resume(mgr)
  Appropriate _:
  async def test_foo_emits_warning(): with warnings.catch_warnings(record=True) as w: await foo() assert len(w) == 1 assert "xyzzy" in w[0].message
  And notice that this last example isn't artificial at all -- this is exactly how you write a test that an async/await-using coroutine correctly raises a warning. Similar issues arise for pretty much any use of warnings.catch_warnings, decimal.localcontext, or numpy.errstate in async/await. It's also unclear how this would even work given that the warnings context manager __exit__ would be called before f.
  So this PEP takes the position that __suspend__/__resume__ and "task-local storage" are two complementary tools that are both useful in different circumstances.. Furthermore, because we only do attribute checks for __suspend__ and __resume__ once at the start of a with block, when these attributes are undefined then the per-yield overhead can be optimized down to a single C-level if (frame->needs_suspend_resume_calls) { ... }. Therefore, we expect the overall overhead to be negligible.
  Interaction with PEP 492
  PEP 492 added new asynchronous context managers, which are like regular context managers, but instead of having regular methods __enter__ and __exit__ they have coroutine methods __aenter__ and __aexit__.
  Following this pattern, one might expect this proposal to add __asuspend__ and __aresume__ coroutine methods. But this doesn't make much sense, since the whole point is that __suspend__ should be called before yielding our thread of execution and allowing other code to run. The only thing we accomplish by making __asuspend__ a coroutine is to make it possible for __asuspend__ itself to yield. So either we need to recursively call __asuspend__ from inside __asuspend__, or else we need to give up and allow these yields to happen without calling the suspend callback; either way it defeats the whole point.
  Well, with one exception: one possible pattern for coroutine code is to call yield in order to communicate with the coroutine runner, but without actually suspending their execution (i.e., the coroutine might know that the coroutine runner will resume them immediately after processing the yielded message). An example of this is the curio.timeout_after async context manager, which yields a special set_timeout message to the curio kernel, and then the kernel immediately (synchronously) resumes the coroutine which sent the message. And from the user point of view, this timeout value acts just like the kinds of global variables that motivated this PEP. But, there is a crucal difference: this kind of async context manager is, by definition, tightly integrated with the coroutine runner. So, the coroutine runner can take over responsibility for keeping track of which timeouts apply to which coroutines without any need for this PEP at all (and this is indeed how curio.timeout_after works).
  That leaves two reasonable approaches to handling async context managers:
  - Add plain __suspend__ and __resume__ methods.
  - Leave async context managers alone for now until we have more experience with them.
  Either seems plausible, so out of laziness / YAGNI this PEP tentatively proposes to stick with option (2).