Re: [Python-Dev] PEP 550 v4

2017年8月26日 08:01:49 -0700

On Saturday, August 26, 2017 2:34:29 AM EDT Nathaniel Smith wrote:
> On Fri, Aug 25, 2017 at 3:32 PM, Yury Selivanov 
<[email protected]> 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
[email protected]
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to