On 13 June 2015 at 17:22, Nick Coghlan <ncogh...@gmail.com> wrote: > On 13 June 2015 at 04:13, Guido van Rossum <gu...@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() > 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