On Sat, Jun 13, 2015 at 12:22 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On 13 June 2015 at 04:13, Guido van Rossum <guido@python.org> 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()
    # 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)