Assert cancellation at the startup boundary
Replace the pre-start wall-clock sleep with an indefinite checkpoint so cancellation ordering cannot race a timer in slow CI. Record that `Cancelled` escapes each `start_or_cancel()` await before the enclosing nursery or cancel scope handles it. Review: PR #479 (goodboy) https://github.com/goodboy/tractor/pull/479 (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))wkt/ai_skillz_run_tests_landing
parent
1c7d0c7f3e
commit
ae67e2f429
|
|
@ -46,10 +46,9 @@ async def absorbs_cancel_pre_started(
|
||||||
|
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
await trio.sleep(2)
|
await trio.sleep_forever()
|
||||||
except trio.Cancelled:
|
except trio.Cancelled:
|
||||||
return
|
return
|
||||||
task_status.started()
|
|
||||||
|
|
||||||
|
|
||||||
async def raise_val_err():
|
async def raise_val_err():
|
||||||
|
|
@ -89,15 +88,25 @@ def test_sibling_err_not_masked_by_startup_rte(
|
||||||
alongside, obscuring that the child was in fact
|
alongside, obscuring that the child was in fact
|
||||||
cancelled due to the sibling's error.
|
cancelled due to the sibling's error.
|
||||||
|
|
||||||
|
`cancelled_at_start` records that the wrapper's own await
|
||||||
|
raises `Cancelled`, rather than merely relying on the
|
||||||
|
nursery's eventual exception-group shape.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
cancelled_at_start: list[bool] = []
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
async with trio.open_nursery() as tn:
|
async with trio.open_nursery() as tn:
|
||||||
tn.start_soon(raise_val_err)
|
tn.start_soon(raise_val_err)
|
||||||
if use_start_or_cancel:
|
if use_start_or_cancel:
|
||||||
|
try:
|
||||||
await start_or_cancel(
|
await start_or_cancel(
|
||||||
tn,
|
tn,
|
||||||
absorbs_cancel_pre_started,
|
absorbs_cancel_pre_started,
|
||||||
)
|
)
|
||||||
|
except trio.Cancelled:
|
||||||
|
cancelled_at_start.append(True)
|
||||||
|
raise
|
||||||
else:
|
else:
|
||||||
await tn.start(absorbs_cancel_pre_started)
|
await tn.start(absorbs_cancel_pre_started)
|
||||||
|
|
||||||
|
|
@ -109,6 +118,7 @@ def test_sibling_err_not_masked_by_startup_rte(
|
||||||
assert len(val_eg.exceptions) == 1
|
assert len(val_eg.exceptions) == 1
|
||||||
|
|
||||||
if use_start_or_cancel:
|
if use_start_or_cancel:
|
||||||
|
assert cancelled_at_start == [True]
|
||||||
# the re-surfaced `Cancelled` is absorbed by the
|
# the re-surfaced `Cancelled` is absorbed by the
|
||||||
# (sibling-error cancelled) nursery scope leaving
|
# (sibling-error cancelled) nursery scope leaving
|
||||||
# NO startup-noise, just the root cause.
|
# NO startup-noise, just the root cause.
|
||||||
|
|
@ -142,7 +152,12 @@ def test_pure_oob_cancel_not_morphed_to_rte(
|
||||||
- a bare `.start()` (currently) morphs the plain
|
- a bare `.start()` (currently) morphs the plain
|
||||||
cancel into an (eg-wrapped) startup `RuntimeError`.
|
cancel into an (eg-wrapped) startup `RuntimeError`.
|
||||||
|
|
||||||
|
`cancelled_at_start` proves cancellation interrupts the
|
||||||
|
wrapper call itself before the cancelled scope exits.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
cancelled_at_start: list[bool] = []
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
with trio.CancelScope() as cs:
|
with trio.CancelScope() as cs:
|
||||||
async with trio.open_nursery() as tn:
|
async with trio.open_nursery() as tn:
|
||||||
|
|
@ -153,10 +168,14 @@ def test_pure_oob_cancel_not_morphed_to_rte(
|
||||||
|
|
||||||
tn.start_soon(canceller)
|
tn.start_soon(canceller)
|
||||||
if use_start_or_cancel:
|
if use_start_or_cancel:
|
||||||
|
try:
|
||||||
await start_or_cancel(
|
await start_or_cancel(
|
||||||
tn,
|
tn,
|
||||||
absorbs_cancel_pre_started,
|
absorbs_cancel_pre_started,
|
||||||
)
|
)
|
||||||
|
except trio.Cancelled:
|
||||||
|
cancelled_at_start.append(True)
|
||||||
|
raise
|
||||||
else:
|
else:
|
||||||
await tn.start(
|
await tn.start(
|
||||||
absorbs_cancel_pre_started,
|
absorbs_cancel_pre_started,
|
||||||
|
|
@ -166,6 +185,7 @@ def test_pure_oob_cancel_not_morphed_to_rte(
|
||||||
|
|
||||||
if use_start_or_cancel:
|
if use_start_or_cancel:
|
||||||
trio.run(main)
|
trio.run(main)
|
||||||
|
assert cancelled_at_start == [True]
|
||||||
else:
|
else:
|
||||||
with pytest.raises(ExceptionGroup) as excinfo:
|
with pytest.raises(ExceptionGroup) as excinfo:
|
||||||
trio.run(main)
|
trio.run(main)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue