Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Include/internal/pycore_genobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ extern PyObject *_PyAsyncGenValueWrapperNew(PyThreadState *state, PyObject *);
extern PyTypeObject _PyCoroWrapper_Type;
extern PyTypeObject _PyAsyncGenWrappedValue_Type;
extern PyTypeObject _PyAsyncGenAThrow_Type;
extern PyTypeObject _PyAsyncGenWrappedValue_Type;

typedef struct _PyAsyncGenWrappedValue {
PyObject_HEAD
PyObject *agw_val;
} _PyAsyncGenWrappedValue;

#define _PyAsyncGenWrappedValue_CheckExact(o) \
Py_IS_TYPE(o, &_PyAsyncGenWrappedValue_Type)
#define _PyAsyncGenWrappedValue_CAST(op) \
(assert(_PyAsyncGenWrappedValue_CheckExact(op)), \
_Py_CAST(_PyAsyncGenWrappedValue*, (op)))

#ifdef __cplusplus
}
Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_monitoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -2083,6 +2083,30 @@ def callback(code, instruction_offset):
sys.monitoring.restart_events()
sys.monitoring.set_events(0, 0)

@test.support.requires_working_socket() # For asyncio
def test_yield_async_generator(self):
# gh-129013: Async generators have a special type that they
# use to yield values. This type shouldn't be exposed by PY_YIELD
# events.
asyncio = test.support.import_helper.import_module("asyncio")

async def gen():
yield 42

async def main():
async for _ in gen():
pass

def handle_yield(code, offset, value):
self.assertEqual(value, 42)

sys.monitoring.use_tool_id(0, "test")
sys.monitoring.register_callback(0, sys.monitoring.events.PY_YIELD, handle_yield)
sys.monitoring.set_events(0, sys.monitoring.events.PY_YIELD)

asyncio.run(main())
sys.monitoring.set_events(0, 0)
Comment on lines +2181 to +2182
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would seem slightly cleaner.

Suggested change
asyncio.run(main())
sys.monitoring.set_events(0, 0)
try:
asyncio.run(main())
finally:
sys.monitoring.clear_tool_id(0)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not disagreeing, but I'd rather stay consistent with the rest of the file. try/finally doesn't seem to be used with set_events(0, 0) in these tests, so I'm going to skip it for now. Sounds reasonable to add that in a follow-up PR, though.



class TestOptimizer(MonitoringTestBase, unittest.TestCase):

Expand Down Expand Up @@ -2329,3 +2353,6 @@ def test_enter_scope_two_events(self):

finally:
sys.monitoring.set_events(TEST_TOOL, 0)

if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix odd/unusable value when using :monitoring-event:`PY_YIELD` with an
:term:`asynchronous generator`.
13 changes: 0 additions & 13 deletions Objects/genobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1441,19 +1441,6 @@ typedef struct PyAsyncGenAThrow {
} PyAsyncGenAThrow;


typedef struct _PyAsyncGenWrappedValue {
PyObject_HEAD
PyObject *agw_val;
} _PyAsyncGenWrappedValue;


#define _PyAsyncGenWrappedValue_CheckExact(o) \
Py_IS_TYPE(o, &_PyAsyncGenWrappedValue_Type)
#define _PyAsyncGenWrappedValue_CAST(op) \
(assert(_PyAsyncGenWrappedValue_CheckExact(op)), \
_Py_CAST(_PyAsyncGenWrappedValue*, (op)))


static int
async_gen_traverse(PyObject *self, visitproc visit, void *arg)
{
Expand Down
9 changes: 8 additions & 1 deletion Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1312,9 +1312,16 @@ dummy_func(
}

tier1 op(_YIELD_VALUE_EVENT, (val -- val)) {
PyObject *yielded = PyStackRef_AsPyObjectBorrow(val);
if (_PyAsyncGenWrappedValue_CheckExact(yielded))
{
/* gh-129013: Async generators have a special wrapper that they
yield. Don't expose that to the user. */
yielded = _PyAsyncGenWrappedValue_CAST(yielded)->agw_val;
}
int err = _Py_call_instrumentation_arg(
tstate, PY_MONITORING_EVENT_PY_YIELD,
frame, this_instr, PyStackRef_AsPyObjectBorrow(val));
frame, this_instr, yielded);
if (err) {
ERROR_NO_POP();
}
Expand Down
11 changes: 10 additions & 1 deletion Python/generated_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading