Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread Nick Coghlan
On 13 June 2015 at 04:13, Guido van Rossum  wrote:
> IOW I don't think that the problem here is that you haven't sufficiently
> motivated your use case -- you are asking for information that just isn't
> available. (Which is actually where you started the thread -- you can get to
> the frame of the coroutine but there's nowhere to go from that frame.)

If I'm understanding Ben's request correctly, it isn't really the
stack trace that he's interested in (as you say, that specific
phrasing doesn't match the way coroutine suspension works), but rather
having visibility into the chain of control flow delegation for
currently suspended frames: what operation is the outermost frame
ultimately blocked *on*, and how did it get to the point of waiting
for that operation?

At the moment, all of the coroutine and generator-iterator resumption
information is implicit in the frame state, so we can't externally
introspect the delegation of control flow in a case like Ben's
original example (for coroutines) or like this one for generators:

def g1():
yield 42

def g2():
yield from g1()

g = g2()
next(g)
# We can tell here that g is suspended
# We can't tell that it delegated flow to a g1() instance

I wonder if in 3.6 it might be possible to *add* some bookkeeping to
"await" and "yield from" expressions that provides external visibility
into the underlying iterable or coroutine that the generator-iterator
or coroutine has delegated flow control to. As an initial assessment,
the runtime cost would be:

* an additional pointer added to generator/coroutine objects to track
control flow delegation
* setting that when suspending in "await" and "yield from" expressions
* clearing it when resuming in "await" and "yield from" expressions

(This would be a read-only borrowed reference from a Python level
perspective, so it shouldn't be necessary to alter the reference count
- we'd just be aliasing the existing reference from the frame's
internal stack state)

Regards,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread Ben Leslie
On 13 June 2015 at 17:22, Nick Coghlan  wrote:
> On 13 June 2015 at 04:13, Guido van Rossum  wrote:
>> IOW I don't think that the problem here is that you haven't sufficiently
>> motivated your use case -- you are asking for information that just isn't
>> available. (Which is actually where you started the thread -- you can get to
>> the frame of the coroutine but there's nowhere to go from that frame.)
>
> If I'm understanding Ben's request correctly, it isn't really the
> stack trace that he's interested in (as you say, that specific
> phrasing doesn't match the way coroutine suspension works), but rather
> having visibility into the chain of control flow delegation for
> currently suspended frames: what operation is the outermost frame
> ultimately blocked *on*, and how did it get to the point of waiting
> for that operation?
>
> At the moment, all of the coroutine and generator-iterator resumption
> information is implicit in the frame state, so we can't externally
> introspect the delegation of control flow in a case like Ben's
> original example (for coroutines) or like this one for generators:
>
> def g1():
> yield 42
>
> def g2():
> yield from g1()
>
> g = g2()
> next(g)
> # We can tell here that g is suspended
> # We can't tell that it delegated flow to a g1() instance
>
> I wonder if in 3.6 it might be possible to *add* some bookkeeping to
> "await" and "yield from" expressions that provides external visibility
> into the underlying iterable or coroutine that the generator-iterator
> or coroutine has delegated flow control to. As an initial assessment,
> the runtime cost would be:
>
> * an additional pointer added to generator/coroutine objects to track
> control flow delegation
> * setting that when suspending in "await" and "yield from" expressions
> * clearing it when resuming in "await" and "yield from" expressions

Thanks Nick for rephrasing with the appropriate terminology. I had tried
to get it right but with a background of implementing OS kernels, I have
a strong habit of existing terminology to break.

I agree with your suggestion that explicitly having the pointers is much
nicer than my opcode hack implementation.

Without side-tracking this discussion I do just want to say that the
hypothetical code is something that actually works if frame objects
expose the stack, which is possibly as easy as:

+static PyObject *
+frame_getstack(PyFrameObject *f, void *closure)
+{
+PyObject **p;
+PyObject *list = PyList_New(0);
+
+if (list == NULL)
+return NULL;
+
+if (f->f_stacktop != NULL) {
+for (p = f->f_valuestack; p < f->f_stacktop; p++) {
+/* FIXME: is this the correct error handling condition? */
+if (PyList_Append(list, *p)) {
+Py_DECREF(list);
+return NULL;
+}
+}
+}
+
+return list;
+}

I have implemented and tested this and it worked well (although I really
don't know CPython internals well enough to know if the above code doesn't
have some serious issues with it).

Is there any reason an f_stack attribute is not exposed for frames? Many of the
other PyFrameObject values are exposed. I'm guessing that there probably
aren't too many places where you can get hold of a frame that doesn't have an
empty stack in normal operation, so it probably isn't necessary.

Anyway, I'm not suggesting that adding f_stack is better than explicitly
adding pointers, but it does seem a more general thing that can be exposed
and enable this use case without requiring extra book-keeping data structures.

Cheers,

Ben
___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread Guido van Rossum
On Sat, Jun 13, 2015 at 12:22 AM, Nick Coghlan  wrote:

> On 13 June 2015 at 04:13, Guido van Rossum  wrote:
> > IOW I don't think that the problem here is that you haven't sufficiently
> > motivated your use case -- you are asking for information that just isn't
> > available. (Which is actually where you started the thread -- you can
> get to
> > the frame of the coroutine but there's nowhere to go from that frame.)
>
> If I'm understanding Ben's request correctly, it isn't really the
> stack trace that he's interested in (as you say, that specific
> phrasing doesn't match the way coroutine suspension works), but rather
> having visibility into the chain of control flow delegation for
> currently suspended frames: what operation is the outermost frame
> ultimately blocked *on*, and how did it get to the point of waiting
> for that operation?
>
> At the moment, all of the coroutine and generator-iterator resumption
> information is implicit in the frame state, so we can't externally
> introspect the delegation of control flow in a case like Ben's
> original example (for coroutines) or like this one for generators:
>
> def g1():
> yield 42
>
> def g2():
> yield from g1()
>
> g = g2()
> next(g)
> # We can tell here that g is suspended
> # We can't tell that it delegated flow to a g1() instance
>
> I wonder if in 3.6 it might be possible to *add* some bookkeeping to
> "await" and "yield from" expressions that provides external visibility
> into the underlying iterable or coroutine that the generator-iterator
> or coroutine has delegated flow control to. As an initial assessment,
> the runtime cost would be:
>
> * an additional pointer added to generator/coroutine objects to track
> control flow delegation
> * setting that when suspending in "await" and "yield from" expressions
> * clearing it when resuming in "await" and "yield from" expressions
>
> (This would be a read-only borrowed reference from a Python level
> perspective, so it shouldn't be necessary to alter the reference count
> - we'd just be aliasing the existing reference from the frame's
> internal stack state)
>

Ah, this makes sense. I think the object you're after is 'reciever' [sic]
in the YIELD_FROM opcode implementation, right?

-- 
--Guido van Rossum (python.org/~guido)
___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread Ben Leslie
On 13 June 2015 at 19:03, Guido van Rossum  wrote:
> On Sat, Jun 13, 2015 at 12:22 AM, Nick Coghlan  wrote:
>>
>> On 13 June 2015 at 04:13, Guido van Rossum  wrote:
>> > IOW I don't think that the problem here is that you haven't sufficiently
>> > motivated your use case -- you are asking for information that just
>> > isn't
>> > available. (Which is actually where you started the thread -- you can
>> > get to
>> > the frame of the coroutine but there's nowhere to go from that frame.)
>>
>> If I'm understanding Ben's request correctly, it isn't really the
>> stack trace that he's interested in (as you say, that specific
>> phrasing doesn't match the way coroutine suspension works), but rather
>> having visibility into the chain of control flow delegation for
>> currently suspended frames: what operation is the outermost frame
>> ultimately blocked *on*, and how did it get to the point of waiting
>> for that operation?
>>
>> At the moment, all of the coroutine and generator-iterator resumption
>> information is implicit in the frame state, so we can't externally
>> introspect the delegation of control flow in a case like Ben's
>> original example (for coroutines) or like this one for generators:
>>
>> def g1():
>> yield 42
>>
>> def g2():
>> yield from g1()
>>
>> g = g2()
>> next(g)
>> # We can tell here that g is suspended
>> # We can't tell that it delegated flow to a g1() instance
>>
>> I wonder if in 3.6 it might be possible to *add* some bookkeeping to
>> "await" and "yield from" expressions that provides external visibility
>> into the underlying iterable or coroutine that the generator-iterator
>> or coroutine has delegated flow control to. As an initial assessment,
>> the runtime cost would be:
>>
>> * an additional pointer added to generator/coroutine objects to track
>> control flow delegation
>> * setting that when suspending in "await" and "yield from" expressions
>> * clearing it when resuming in "await" and "yield from" expressions
>>
>> (This would be a read-only borrowed reference from a Python level
>> perspective, so it shouldn't be necessary to alter the reference count
>> - we'd just be aliasing the existing reference from the frame's
>> internal stack state)
>
>
> Ah, this makes sense. I think the object you're after is 'reciever' [sic] in
> the YIELD_FROM opcode implementation, right?

Right this is the exact book-keeping that I was originally referring to in
my previous emails (sorry for not making that more explicit earlier).
The 'reciever' is actually on the stack part of the co-routine's frame all
the time (i.e.: pointed to via f->f_stacktop).

So from my point of view the book-keeping is there (albeit somewhat
obscurely!), but the objects on the frame's stack aren't exposed via
the Python wrapping of PyFrameObject, (although could, I think, easily
be exposed). Once you get at the receiver object (which is another
co-routine) you can traverse down to the point the co-routine 'switched'.

Cheers,

Ben
___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread jaivish kothari
Hi ,

   I had a Question,i hope i'll find the solution here.

Say i have a Queue.
 >>> h = Queue.Queue(maxsize=0)
>>> h.put(1)
>>> h.put(2)
>>> h.empty()
False
>>> h.join()
>>> h.empty()
False
>>> h.get()
1
>>> h.get()
2
>>> h.get()
Blocked...

My Question is :
In a single threaded environment why does the get() gets blocked , instead
of raising an exception.On interpreter i have no way to resume working.

And my second question is :
Why doe we have to explicitly call task_done after get(). why doesn't get()
implicitly call task_done().
as for put() entry for unfinished_task is automatically added .why not get
deleted in get() then.

Thanks.

On Fri, May 29, 2015 at 10:16 AM, Ben Leslie  wrote:

> Hi all,
>
> Apologies in advance; I'm not a regular, and this may have been
> handled already (but I couldn't find it when searching).
>
> I've been using the new async/await functionality (congrats again to
> Yury on getting that through!), and I'd like to get a stack trace
> between the place at which blocking occurs and the outer co-routine.
>
> For example, consider this code:
>
> """
> async def a():
> await b()
>
> async def b():
> await switch()
>
> @types.coroutine
> def switch():
> yield
>
> coro_a = a()
> coro_a.send(None)
> """
>
> At this point I'd really like to be able to somehow get a stack trace
> similar to:
>
> test.py:2
> test.py:4
> test.py:9
>
> Using the gi_frame attribute of coro_a, I can get the line number of
> the outer frame (e.g.: line 2), but from there there is no way to
> descend the stack to reach the actual yield point.
>
> I thought that perhaps the switch() co-routine could yield the frame
> object returned from inspect.currentframe(), however once that
> function yields that frame object has f_back changed to None.
>
> A hypothetical approach would be to work the way down form the
> outer-frame, but that requires getting access to the co-routine object
> that the outer-frame is currently await-ing. Some hypothetical code
> could be:
>
> """
> def show(coro):
> print("{}:{}".format(coro.gi_frame.f_code.co_filename,
> coro.gi_frame.f_lineno))
> if dis.opname[coro.gi_code.co_code[coro.gi_frame.f_lasti + 1]] ==
> 'YIELD_FROM':
> show(coro.gi_frame.f_stack[0])
> """
>
> This relies on the fact that an await-ing co-routine will be executing
> a YIELD_FROM instruction. The above code uses a completely
> hypothetical 'f_stack' property of frame objects to pull the
> co-routine object which a co-routine is currently await-ing from the
> stack. I've implemented a proof-of-concept f_stack property in the
> frameobject.c just to test out the above code, and it seems to work.
>
> With all that, some questions:
>
> 1) Does anyone else see value in trying to get the stack-trace down to
> the actual yield point?
> 2) Is there a different way of doing it that doesn't require changes
> to Python internals?
> 3) Assuming no to #2 is there a better way of getting the information
> compared to the pretty hacking byte-code/stack inspection?
>
> Thanks,
>
> Ben
> ___
> 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/jaivishkothari10104733%40gmail.com
>
___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread Nick Coghlan
On 13 June 2015 at 20:25, Ben Leslie  wrote:
> Is there any reason an f_stack attribute is not exposed for frames? Many of 
> the
> other PyFrameObject values are exposed. I'm guessing that there probably
> aren't too many places where you can get hold of a frame that doesn't have an
> empty stack in normal operation, so it probably isn't necessary.

There's also the fact that anything we do in CPython that assumes a
value stack based interpreter implementation might not work on other
Python implementations, so we generally try to keep that level of
detail hidden.

> Anyway, I'm not suggesting that adding f_stack is better than explicitly
> adding pointers, but it does seem a more general thing that can be exposed
> and enable this use case without requiring extra book-keeping data structures.

Emulating CPython frames on other implementations is already hard
enough, without exposing the value stack directly.

Compared to "emulate CPython's value stack", "keep track of delegation
to subiterators and subcoroutines" is a far more reasonable request to
make of developers of other implementations.

>From a learnability perspective, there's also nothing about an
"f_stack" attribute that says "you can use this to find out where a
generator or coroutine has delegated control", while attributes like
"gi_delegate" or "cr_delegate" would be more self-explanatory.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread Guido van Rossum
On Sat, Jun 13, 2015 at 9:21 AM, Nick Coghlan  wrote:

> On 13 June 2015 at 20:25, Ben Leslie  wrote:
> > Is there any reason an f_stack attribute is not exposed for frames? Many
> of the
> > other PyFrameObject values are exposed. I'm guessing that there probably
> > aren't too many places where you can get hold of a frame that doesn't
> have an
> > empty stack in normal operation, so it probably isn't necessary.
>
> There's also the fact that anything we do in CPython that assumes a
> value stack based interpreter implementation might not work on other
> Python implementations, so we generally try to keep that level of
> detail hidden.
>
> > Anyway, I'm not suggesting that adding f_stack is better than explicitly
> > adding pointers, but it does seem a more general thing that can be
> exposed
> > and enable this use case without requiring extra book-keeping data
> structures.
>
> Emulating CPython frames on other implementations is already hard
> enough, without exposing the value stack directly.
>

I'm not sure how strong this argument is. We also expose bytecode, which is
about as unportable as anything. (Though arguably we can't keep bytecode a
secret, because it's written to .pyc files.) I find Ben's implementation
pretty straightforward, and because it makes a copy, I don't think there's
anything one could do with the exposed stack that could violate any of the
interpreter's invariants (I might change it to return a tuple to emphasize
this point to the caller though).

But I agree it isn't a solution to the question about the suspension
"stack".


> Compared to "emulate CPython's value stack", "keep track of delegation
> to subiterators and subcoroutines" is a far more reasonable request to
> make of developers of other implementations.
>
> From a learnability perspective, there's also nothing about an
> "f_stack" attribute that says "you can use this to find out where a
> generator or coroutine has delegated control", while attributes like
> "gi_delegate" or "cr_delegate" would be more self-explanatory.
>

Stack frame objects are kind of expensive and I would hate to add an extra
pointer to every frame just to support this functionality. Perhaps we could
have a flag though that says whether the top of the stack is in fact the
generator object on which we're waiting in a yield-from? This flag could
perhaps sit next to f_executing (also note that this new flag is mutually
exclusive with f_executing). We could then easily provide a new method or
property on the frame object that returns the desired generator if the flag
is set or None if the flag is not set -- other Python implementations could
choose to implement this differently.

-- 
--Guido van Rossum (python.org/~guido)
___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread MRAB

On 2015-06-13 11:38, jaivish kothari wrote:

Hi ,

I had a Question,i hope i'll find the solution here.

Say i have a Queue.
  >>> h = Queue.Queue(maxsize=0)
 >>> h.put(1)
 >>> h.put(2)
 >>> h.empty()
False
 >>> h.join()
 >>> h.empty()
False
 >>> h.get()
1
 >>> h.get()
2
 >>> h.get()
Blocked...

My Question is :
In a single threaded environment why does the get() gets blocked ,
instead of raising an exception.On interpreter i have no way to resume
working.

And my second question is :
Why doe we have to explicitly call task_done after get(). why doesn't
get() implicitly call task_done().
as for put() entry for unfinished_task is automatically added .why not
get deleted in get() then.


The way it's used is to get a item, process it, and then signal that it
has finished with it. There's going to be a period of time when an item
is not in the queue, but is still being processed.

___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread Greg Ewing

Nick Coghlan wrote:

I wonder if in 3.6 it might be possible to *add* some bookkeeping to
"await" and "yield from" expressions that provides external visibility
into the underlying iterable or coroutine that the generator-iterator
or coroutine has delegated flow control to.


In my original implementation of yield-from, stack frames
had an f_yieldfrom slot referring to the object being
yielded from.

I gather that slot no longer exists at the C level, but
it ought to be possible to provide a Python attribute or
method returning the same information.

--
Greg
___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread Ben Leslie
On 14 June 2015 at 09:20, Greg Ewing  wrote:
> Nick Coghlan wrote:
>>
>> I wonder if in 3.6 it might be possible to *add* some bookkeeping to
>> "await" and "yield from" expressions that provides external visibility
>> into the underlying iterable or coroutine that the generator-iterator
>> or coroutine has delegated flow control to.
>
>
> In my original implementation of yield-from, stack frames
> had an f_yieldfrom slot referring to the object being
> yielded from.
>
> I gather that slot no longer exists at the C level, but
> it ought to be possible to provide a Python attribute or
> method returning the same information.

OK, this is really easy to implement actually. I implemented it as gi_yieldfrom
on the generator object rather than f_yieldfrom on the frame object,
primarily as most of the related code is already in the genobject.c module.

Something like this:

diff --git a/Objects/genobject.c b/Objects/genobject.c
index 3c32e7b..bc42fe5 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -553,11 +553,22 @@ gen_set_qualname(PyGenObject *op, PyObject *value)
 return 0;
 }

+static PyObject *
+gen_getyieldfrom(PyGenObject *gen)
+{
+PyObject *yf = gen_yf(gen);
+if (yf == NULL)
+Py_RETURN_NONE;
+
+return yf;
+}
+
 static PyGetSetDef gen_getsetlist[] = {
 {"__name__", (getter)gen_get_name, (setter)gen_set_name,
  PyDoc_STR("name of the generator")},
 {"__qualname__", (getter)gen_get_qualname, (setter)gen_set_qualname,
  PyDoc_STR("qualified name of the generator")},
+{"gi_yieldfrom", (getter)gen_getyieldfrom, NULL, NULL},
 {NULL} /* Sentinel */
 };

(Note: Above probably doesn't exactly follow correct coding conventions, and
probably need to add an appropriate doc string.)

This greatly simplifies my very original 'show' routine to:

def show(coro):
print("{}:{} ({})".format(coro.gi_frame.f_code.co_filename,
coro.gi_frame.f_lineno, coro))
if coro.gi_yieldfrom:
show(coro.gi_yieldfrom)

I think this would give the widest flexibility for non-CPython
implementations to implement
the same property in the most appropriate manner.

If this seems like a good approach I'll try and work it in to a
suitable patch for contribution.

Cheers,

Ben
___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread Nick Coghlan
On 14 Jun 2015 03:35, "Guido van Rossum"  wrote:
>
> On Sat, Jun 13, 2015 at 9:21 AM, Nick Coghlan  wrote:
>> From a learnability perspective, there's also nothing about an
>> "f_stack" attribute that says "you can use this to find out where a
>> generator or coroutine has delegated control", while attributes like
>> "gi_delegate" or "cr_delegate" would be more self-explanatory.
>
> Stack frame objects are kind of expensive and I would hate to add an
extra pointer to every frame just to support this functionality. Perhaps we
could have a flag though that says whether the top of the stack is in fact
the generator object on which we're waiting in a yield-from? This flag
could perhaps sit next to f_executing (also note that this new flag is
mutually exclusive with f_executing). We could then easily provide a new
method or property on the frame object that returns the desired generator
if the flag is set or None if the flag is not set -- other Python
implementations could choose to implement this differently.

Fortunately, we can expose this control flow delegation info through the
generator-iterator and coroutine object APIs, rather than needing to do it
directly on the frame.

I'd missed that it could be done without *any* new C level state though - I
now think Ben's right that we should be able to just expose the delegation
lookup from the resumption logic itself as a calculated property.

Cheers,
Nick.
___
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


Re: [Python-Dev] Obtaining stack-frames from co-routine objects

2015-06-13 Thread Nick Coghlan
On 14 Jun 2015 10:01, "Ben Leslie"  wrote:
>
> If this seems like a good approach I'll try and work it in to a
> suitable patch for contribution.

I think it's a good approach, and worth opening an enhancement issue for.

I expect any patch would need some adjustments after Yury has finished
revising the async/await implementation to address some beta compatibility
issues with functools.singledispatch and Tornado.

Cheers,
Nick.
___
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