Skip to content

Commit 1878954

Browse files
committed
Add the profiling stuff.
1 parent cc73475 commit 1878954

5 files changed

Lines changed: 102 additions & 14 deletions

File tree

‎Lib/test/test_asyncgen.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,25 @@ def tearDown(self):
611611
self.loop = None
612612
asyncio.set_event_loop(None)
613613

614+
def test_async_gen_asend_exhaustion_args(self):
615+
async def gen(bare_return):
616+
yield 1
617+
if bare_return:
618+
return
619+
620+
async def run():
621+
for bare_return in (False, True):
622+
with self.subTest(bare_return=bare_return):
623+
g = gen(bare_return)
624+
self.assertEqual(await anext(g), 1)
625+
for _ in range(2):
626+
with self.assertRaises(StopAsyncIteration) as caught:
627+
await g.asend(None)
628+
self.assertEqual(caught.exception.args, ())
629+
self.assertEqual(str(caught.exception), '')
630+
631+
self.loop.run_until_complete(run())
632+
614633
def check_async_iterator_anext(self, ait_class):
615634
with self.subTest(anext="pure-Python"):
616635
self._check_async_iterator_anext(ait_class, py_anext)

‎Lib/test/test_external_inspection.py‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3692,6 +3692,58 @@ def _sample_frames(
36923692
client_socket.sendall(send_ack)
36933693
return frames
36943694

3695+
@skip_if_not_supported
3696+
@unittest.skipIf(
3697+
sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,
3698+
"Test only runs on Linux with process_vm_readv support",
3699+
)
3700+
def test_generator_throw_from_different_callers(self):
3701+
script_body = """\
3702+
class ReadByte(Exception):
3703+
def __index__(self):
3704+
return 1
3705+
3706+
class Delegate:
3707+
def __iter__(self):
3708+
return self
3709+
def __next__(self):
3710+
return None
3711+
# Block in C with the generator as the top Python frame.
3712+
throw = sock.recv
3713+
3714+
def delegating(iterator):
3715+
yield from iterator
3716+
3717+
gen = delegating(delegating(Delegate()))
3718+
next(gen)
3719+
3720+
def first():
3721+
sock.sendall(b"first")
3722+
gen.throw(ReadByte())
3723+
3724+
def second():
3725+
sock.sendall(b"second")
3726+
gen.throw(ReadByte())
3727+
3728+
first()
3729+
second()
3730+
"""
3731+
3732+
with self._target_process(script_body) as (p, sock, make_unwinder):
3733+
unwinder = make_unwinder(cache_frames=True)
3734+
frames1 = self._sample_frames(
3735+
sock, unwinder, b"first", b"x", {"delegating", "first"}
3736+
)
3737+
frames2 = self._sample_frames(
3738+
sock, unwinder, b"second", b"x", {"delegating"}
3739+
)
3740+
3741+
self.assertIsNotNone(frames1)
3742+
self.assertIsNotNone(frames2)
3743+
names = [frame.funcname for frame in frames2]
3744+
self.assertIn("second", names)
3745+
self.assertNotIn("first", names)
3746+
36953747
@skip_if_not_supported
36963748
@unittest.skipIf(
36973749
sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED,

‎Lib/test/test_yield_from_async.py‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ async def g(r):
783783
"f sending spam to g",
784784
"g received 'spam'",
785785
"g returning None",
786-
"f caught StopAsyncIteration(None)",
786+
"f caught StopAsyncIteration()",
787787
"g starting",
788788
"f sending spam to g",
789789
"g received 'spam'",
@@ -1696,6 +1696,18 @@ class TestPEP828Extras(unittest.TestCase):
16961696
parity-checked against ``test_yield_from``.
16971697
"""
16981698

1699+
@_async_test
1700+
async def test_delegation_ignores_shadowed_special_methods(self):
1701+
async def outer(iterator):
1702+
yield from iterator
1703+
1704+
for name in ("__aiter__", "__anext__"):
1705+
with self.subTest(name=name):
1706+
iterator = AsAsyncIterator([1, 2])
1707+
setattr(iterator, name, None)
1708+
self.assertEqual([item async for item in outer(iterator)],
1709+
[1, 2])
1710+
16991711
@_async_test
17001712
async def test_custom_iterator_deleted_return_value(self):
17011713
class Iterator:

‎Objects/genobject.c‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,12 @@ static PyObject *
388388
gen_set_stop_iteration(PyGenObject *gen, PyObject *result)
389389
{
390390
if (PyAsyncGen_CheckExact(gen)) {
391-
_PyAsyncGen_SetStopIterationValue(result);
391+
if (result == Py_None) {
392+
PyErr_SetNone(PyExc_StopAsyncIteration);
393+
}
394+
else {
395+
_PyAsyncGen_SetStopIterationValue(result);
396+
}
392397
}
393398
else if (result == Py_None) {
394399
PyErr_SetNone(PyExc_StopIteration);
@@ -684,6 +689,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
684689
'yield from' or awaiting on with 'await'. */
685690
ret = _gen_throw((PyGenObject *)yf, close_on_genexit,
686691
typ, val, tb);
692+
_PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev);
687693
tstate->current_frame = prev;
688694
frame->previous = NULL;
689695
}
@@ -704,6 +710,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
704710
frame->previous = prev;
705711
tstate->current_frame = frame;
706712
ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);
713+
_PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev);
707714
tstate->current_frame = prev;
708715
frame->previous = NULL;
709716
Py_DECREF(meth);

‎Python/codegen.c‎

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ codegen_yield_from_async(compiler *c, location loc, expr_ty e)
527527
NEW_JUMP_TARGET_LABEL(c, send);
528528
NEW_JUMP_TARGET_LABEL(c, exit);
529529
NEW_JUMP_TARGET_LABEL(c, use_anext);
530-
NEW_JUMP_TARGET_LABEL(c, got_coroutine);
530+
NEW_JUMP_TARGET_LABEL(c, got_awaitable);
531531
NEW_JUMP_TARGET_LABEL(c, yield);
532532
NEW_JUMP_TARGET_LABEL(c, throw);
533533
NEW_JUMP_TARGET_LABEL(c, athrow);
@@ -538,9 +538,7 @@ codegen_yield_from_async(compiler *c, location loc, expr_ty e)
538538
VISIT(c, expr, e->v.YieldFrom.value);
539539
// Stack: [value]
540540

541-
ADDOP_NAME(c, loc, LOAD_ATTR, &_Py_ID(__aiter__), names);
542-
ADDOP(c, loc, PUSH_NULL);
543-
ADDOP_I(c, loc, CALL, 0);
541+
ADDOP(c, loc, GET_AITER);
544542
// Stack: [aiterator]
545543

546544
ADDOP_LOAD_CONST(c, loc, Py_None);
@@ -558,19 +556,19 @@ codegen_yield_from_async(compiler *c, location loc, expr_ty e)
558556
ADDOP_I(c, loc, COPY, 3);
559557
// Stack: [aiterator, asend_value, method, NULL, asend_value]
560558
ADDOP_I(c, loc, CALL, 1);
561-
ADDOP_JUMP(c, loc, JUMP_NO_INTERRUPT, got_coroutine);
559+
ADDOP_I(c, loc, GET_AWAITABLE, 0);
560+
ADDOP_JUMP(c, loc, JUMP_NO_INTERRUPT, got_awaitable);
562561

563562
USE_LABEL(c, use_anext);
564563
// Stack: [aiterator, asend_value]
565564
ADDOP_I(c, loc, COPY, 2);
566-
ADDOP_NAME(c, loc, LOAD_ATTR, &_Py_ID(__anext__), names);
567-
ADDOP(c, loc, PUSH_NULL);
568-
// Stack: [aiterator, asend_value, method, NULL]
569-
ADDOP_I(c, loc, CALL, 0);
570-
USE_LABEL(c, got_coroutine);
571-
// Stack: [aiterator, asend_value, awaitable]
565+
ADDOP(c, loc, GET_ANEXT);
566+
// Stack: [aiterator, asend_value, aiterator, await_iter]
567+
ADDOP_I(c, loc, SWAP, 2);
568+
ADDOP(c, loc, POP_TOP);
572569

573-
ADDOP_I(c, loc, GET_AWAITABLE, 0);
570+
USE_LABEL(c, got_awaitable);
571+
// Stack: [aiterator, asend_value, await_iter]
574572
ADDOP(c, loc, PUSH_NULL);
575573
ADDOP_LOAD_CONST(c, loc, Py_None);
576574
// Stack: [aiterator, asend_value, await_iter, NULL, None]

0 commit comments

Comments
 (0)