Always raise remote (cancelled) error if set

Previously we weren't raising a remote error if the local scope was
cancelled during a call to `Context.result()` which is problematic if
the caller WAS NOT the requester for said remote cancellation; in that
case we still want a `ContextCancelled` raised with the `.canceller:
str` set to the cancelling actor uid.

Further fix a naming bug where the (seemingly older) `._remote_err` was
being set to such an error instead of `._remote_error` XD
multihomed
Tyler Goodlet 2023-10-10 09:45:49 -04:00
parent 919e462f88
commit 575a24adf1
1 changed files with 25 additions and 10 deletions

View File

@ -102,10 +102,14 @@ class Context:
_remote_error: BaseException | None = None _remote_error: BaseException | None = None
# cancellation state # cancellation state
_cancel_called: bool = False _cancel_called: bool = False # did WE cancel the far end?
_cancelled_remote: tuple | None = None _cancelled_remote: tuple[str, str] | None = None
_cancel_msg: str | None = None _cancel_msg: str | None = None
_scope: trio.CancelScope | None = None _scope: trio.CancelScope | None = None
# NOTE: this is set by the `.devx._debug` machinery
# to indicate whether code in `._runtime` should handle
# cancelled context crashes in the pdbp REPL.
_enter_debugger_on_cancel: bool = True _enter_debugger_on_cancel: bool = True
@property @property
@ -207,7 +211,7 @@ class Context:
# XXX: set the remote side's error so that after we cancel # XXX: set the remote side's error so that after we cancel
# whatever task is the opener of this context it can raise # whatever task is the opener of this context it can raise
# that error as the reason. # that error as the reason.
self._remote_error = error self._remote_error: BaseException = error
# always record the remote actor's uid since its cancellation # always record the remote actor's uid since its cancellation
# state is directly linked to ours (the local one). # state is directly linked to ours (the local one).
@ -488,11 +492,7 @@ class Context:
assert self._portal, "Context.result() can not be called from callee!" assert self._portal, "Context.result() can not be called from callee!"
assert self._recv_chan assert self._recv_chan
# from . import _debug if re := self._remote_error:
# await _debug.breakpoint()
re = self._remote_error
if re:
self._maybe_raise_remote_err(re) self._maybe_raise_remote_err(re)
return re return re
@ -507,7 +507,7 @@ class Context:
while True: while True:
msg = await self._recv_chan.receive() msg = await self._recv_chan.receive()
try: try:
self._result = msg['return'] self._result: Any = msg['return']
# NOTE: we don't need to do this right? # NOTE: we don't need to do this right?
# XXX: only close the rx mem chan AFTER # XXX: only close the rx mem chan AFTER
@ -516,6 +516,21 @@ class Context:
# await self._recv_chan.aclose() # await self._recv_chan.aclose()
break break
# NOTE: we get here if the far end was
# `ContextCancelled` in 2 cases:
# - we requested the cancellation and thus
# SHOULD NOT raise that far end error,
# - WE DID NOT REQUEST that cancel and thus
# SHOULD RAISE HERE!
except trio.Cancelled:
if not self._cancel_called:
raise self._remote_error
else:
# if we DID request the cancel we simply
# continue as normal.
raise
except KeyError: # as msgerr: except KeyError: # as msgerr:
if 'yield' in msg: if 'yield' in msg:
@ -537,7 +552,7 @@ class Context:
) # from msgerr ) # from msgerr
err = self._maybe_raise_remote_err(err) err = self._maybe_raise_remote_err(err)
self._remote_err = err self._remote_error = err
return self._remote_error or self._result return self._remote_error or self._result