Compare commits

...

3 Commits

Author SHA1 Message Date
Tyler Goodlet d2ad58d5d6 Toss in masked `.set_trace()` for unshielded `.pause()` debug 2025-08-10 15:07:42 -04:00
Tyler Goodlet f7ca31c0e4 Set `shield` when `.cancel_called` for root crashes
Such that we handle them despite a cancellation condition. This is
almost always the case, that `root_tn.cancel_scope.cancel_called` is
set, by the time the `debug._maybe_enter_pm()` hits. Previous I guess we
just weren't actually ever REPL-debugging such cases? Still needs
a test obvi.
2025-08-10 15:03:15 -04:00
Tyler Goodlet e50ba3e8a6 Toss in some breakpointed collapsers to catch eg
Such that when that strange (now marked xfail) case from 6348c83 shows
we can attempt to REPL it.

Thus far this has led to zero insight as to why the fail-after is not
raising a `TooSlowError` and instead the eg with embedded assert-error,
but likely mucking with the `trio.NurseryManager.__aexit__()` (prolly by
overriding it unfortunately..) will get us somewhere??
2025-08-10 14:56:09 -04:00
3 changed files with 51 additions and 11 deletions

View File

@ -478,7 +478,10 @@ async def open_root_actor(
# start runtime in a bg sub-task, yield to caller.
async with (
collapse_eg(),
collapse_eg(
bp=True,
hide_tb=False,
),
trio.open_nursery() as root_tn,
# XXX, finally-footgun below?
@ -523,6 +526,12 @@ async def open_root_actor(
err,
api_frame=inspect.currentframe(),
debug_filter=debug_filter,
# XXX NOTE, required to debug root-actor
# crashes under cancellation conditions; so
# most of them!
shield=root_tn.cancel_scope.cancel_called,
# ^TODO? write a (debugger) test for this ya?
)
if (

View File

@ -468,7 +468,11 @@ async def _open_and_supervise_one_cancels_all_nursery(
# errors from this daemon actor nursery bubble up to caller
async with (
collapse_eg(),
# collapse_eg(),
collapse_eg(
bp=True,
hide_tb=False,
),
trio.open_nursery() as da_nursery,
):
try:
@ -481,7 +485,11 @@ async def _open_and_supervise_one_cancels_all_nursery(
# As such if the strategy propagates any error(s) upwards
# the above "daemon actor" nursery will be notified.
async with (
collapse_eg(),
# collapse_eg(),
collapse_eg(
bp=True,
hide_tb=False,
),
trio.open_nursery() as ria_nursery,
):
an = ActorNursery(
@ -621,11 +629,17 @@ async def _open_and_supervise_one_cancels_all_nursery(
# use `BaseExceptionGroup` as needed
if len(errors) > 1:
raise BaseExceptionGroup(
beg = BaseExceptionGroup(
'tractor.ActorNursery errored with',
tuple(errors.values()),
)
beg.add_note(
'This beg was created from an actor-nursery!\n'
)
await debug.pause(shield=True)
raise beg
else:
# await debug.pause(shield=True)
raise list(errors.values())[0]
# show frame on any (likely) internal error
@ -683,16 +697,30 @@ async def open_nursery(
# mark us for teardown on exit
implicit_runtime: bool = True
async with open_root_actor(
hide_tb=hide_tb,
**kwargs,
) as actor:
async with (
# collapse_eg(),
collapse_eg(
bp=True,
hide_tb=False,
),
open_root_actor(
hide_tb=hide_tb,
**kwargs,
) as actor
):
assert actor is current_actor()
try:
async with _open_and_supervise_one_cancels_all_nursery(
actor
) as an:
async with (
# collapse_eg(),
collapse_eg(
bp=True,
hide_tb=False,
),
_open_and_supervise_one_cancels_all_nursery(
actor
) as an,
):
# NOTE: mark this nursery as having
# implicitly started the root actor so

View File

@ -561,6 +561,9 @@ async def _pause(
return
elif isinstance(pause_err, trio.Cancelled):
__tracebackhide__: bool = False
# XXX, unmask to REPL it.
# mk_pdb().set_trace(frame=inspect.currentframe())
_repl_fail_report += (
'You called `tractor.pause()` from an already cancelled scope!\n\n'
'Consider `await tractor.pause(shield=True)` to make it work B)\n'