Skip to content

Commit 2afe6ae

Browse files
committed
perform yield from delegation by repeating YIELD_FROM opcode (closes python#14230)
This allows generators that are using yield from to be seen by debuggers. It also kills the f_yieldfrom field on frame objects. Patch mostly from Mark Shannon with a few tweaks by me.
1 parent 3270d11 commit 2afe6ae

9 files changed

Lines changed: 113 additions & 166 deletions

File tree

Include/frameobject.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ typedef struct _frame {
2727
to the current stack top. */
2828
PyObject **f_stacktop;
2929
PyObject *f_trace; /* Trace function */
30-
PyObject *f_yieldfrom; /* Iterator being delegated to by yield from */
3130

3231
/* In a generator, we need to be able to swap between the exception
3332
state inside the generator and the exception state of the calling

Include/genobject.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ typedef struct {
1919

2020
/* True if generator is being executed. */
2121
char gi_running;
22-
22+
2323
/* The code object backing the generator */
2424
PyObject *gi_code;
2525

@@ -35,6 +35,7 @@ PyAPI_DATA(PyTypeObject) PyGen_Type;
3535
PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *);
3636
PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *);
3737
PyAPI_FUNC(int) PyGen_FetchStopIterationValue(PyObject **);
38+
PyObject *_PyGen_Send(PyGenObject *, PyObject *);
3839

3940
#ifdef __cplusplus
4041
}

Lib/test/test_pep380.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import unittest
1111
import io
1212
import sys
13-
import traceback
13+
import inspect
1414
import parser
1515

1616
from test.support import captured_stderr
@@ -919,6 +919,27 @@ def one():
919919
next(g1)
920920
g1.close()
921921

922+
def test_delegator_is_visible_to_debugger(self):
923+
def call_stack():
924+
return [f[3] for f in inspect.stack()]
925+
926+
def gen():
927+
yield call_stack()
928+
yield call_stack()
929+
yield call_stack()
930+
931+
def spam(g):
932+
yield from g
933+
934+
def eggs(g):
935+
yield from g
936+
937+
for stack in spam(gen()):
938+
self.assertTrue('spam' in stack)
939+
940+
for stack in spam(eggs(gen())):
941+
self.assertTrue('spam' in stack and 'eggs' in stack)
942+
922943

923944
def test_main():
924945
from test import support

Lib/test/test_sys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ class C(object): pass
730730
nfrees = len(x.f_code.co_freevars)
731731
extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
732732
ncells + nfrees - 1
733-
check(x, size(vh + '13P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
733+
check(x, size(vh + '12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
734734
# function
735735
def func(): pass
736736
check(func, size(h + '12P'))

Objects/frameobject.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,6 @@ frame_dealloc(PyFrameObject *f)
444444
Py_CLEAR(f->f_exc_type);
445445
Py_CLEAR(f->f_exc_value);
446446
Py_CLEAR(f->f_exc_traceback);
447-
Py_CLEAR(f->f_yieldfrom);
448447

449448
co = f->f_code;
450449
if (co->co_zombieframe == NULL)
@@ -476,7 +475,6 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
476475
Py_VISIT(f->f_exc_type);
477476
Py_VISIT(f->f_exc_value);
478477
Py_VISIT(f->f_exc_traceback);
479-
Py_VISIT(f->f_yieldfrom);
480478

481479
/* locals */
482480
slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars);
@@ -510,7 +508,6 @@ frame_clear(PyFrameObject *f)
510508
Py_CLEAR(f->f_exc_value);
511509
Py_CLEAR(f->f_exc_traceback);
512510
Py_CLEAR(f->f_trace);
513-
Py_CLEAR(f->f_yieldfrom);
514511

515512
/* locals */
516513
slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars);
@@ -714,7 +711,6 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
714711
f->f_lasti = -1;
715712
f->f_lineno = code->co_firstlineno;
716713
f->f_iblock = 0;
717-
f->f_yieldfrom = NULL;
718714

719715
_PyObject_GC_TRACK(f);
720716
return f;

0 commit comments

Comments
 (0)