On Saturday, August 26, 2017 2:34:29 AM EDT Nathaniel Smith wrote: > On Fri, Aug 25, 2017 at 3:32 PM, Yury Selivanov <yselivanov...@gmail.com> wrote: > > Coroutines and Asynchronous Tasks > > --------------------------------- > > > > In coroutines, like in generators, context variable changes are > > local> > > and are not visible to the caller:: > > import asyncio > > > > var = new_context_var() > > > > async def sub(): > > assert var.lookup() == 'main' > > var.set('sub') > > assert var.lookup() == 'sub' > > > > async def main(): > > var.set('main') > > await sub() > > assert var.lookup() == 'main' > > > > loop = asyncio.get_event_loop() > > loop.run_until_complete(main()) > > I think this change is a bad idea. I think that generally, an async > call like 'await async_sub()' should have the equivalent semantics > to a synchronous call like 'sync_sub()', except for the part where > the former is able to contain yields. Giving every coroutine an LC > breaks that equivalence. It also makes it so in async code, you > can't necessarily refactor by moving code in and out of > subroutines. Like, if we inline 'sub' into 'main', that shouldn't > change the semantics, but...
If we could easily, we'd given each _normal function_ its own logical context as well. What we are talking about here is variable scope leaking up the call stack. I think this is a bad pattern. For decimal context-like uses of the EC you should always use a context manager. For uses like Web request locals, you always have a top function that sets the context vars. > > I think I see the motivation: you want to make > > await sub() > > and > > await ensure_future(sub()) > > have the same semantics, right? And the latter has to create a Task What we want is for `await sub()` to be equivalent to `await asyncio.wait_for(sub())` and to `await asyncio.gather(sub())`. Imagine we allow context var changes to leak out of `async def`. It's easy to write code that relies on this: async def init(): var.set('foo') async def main(): await init() assert var.lookup() == 'foo' If we change `await init()` to `await asyncio.wait_for(init())`, the code will break (and in real world, possibly very subtly). > It also adds non-trivial overhead, because now lookup() is O(depth > of async callstack), instead of O(depth of (async) generator > nesting), which is generally much smaller. You would hit cache in lookup() most of the time. Elvis _______________________________________________ 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