From 575a24adf198b28fb57f8d3150dcacef94c7b6e0 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Tue, 10 Oct 2023 09:45:49 -0400 Subject: [PATCH] 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 --- tractor/_context.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/tractor/_context.py b/tractor/_context.py index e35188c..0df1e80 100644 --- a/tractor/_context.py +++ b/tractor/_context.py @@ -102,10 +102,14 @@ class Context: _remote_error: BaseException | None = None # cancellation state - _cancel_called: bool = False - _cancelled_remote: tuple | None = None + _cancel_called: bool = False # did WE cancel the far end? + _cancelled_remote: tuple[str, str] | None = None _cancel_msg: str | 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 @property @@ -207,7 +211,7 @@ class Context: # XXX: set the remote side's error so that after we cancel # whatever task is the opener of this context it can raise # that error as the reason. - self._remote_error = error + self._remote_error: BaseException = error # always record the remote actor's uid since its cancellation # 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._recv_chan - # from . import _debug - # await _debug.breakpoint() - - re = self._remote_error - if re: + if re := self._remote_error: self._maybe_raise_remote_err(re) return re @@ -507,7 +507,7 @@ class Context: while True: msg = await self._recv_chan.receive() try: - self._result = msg['return'] + self._result: Any = msg['return'] # NOTE: we don't need to do this right? # XXX: only close the rx mem chan AFTER @@ -516,6 +516,21 @@ class Context: # await self._recv_chan.aclose() 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: if 'yield' in msg: @@ -537,7 +552,7 @@ class Context: ) # from msgerr err = self._maybe_raise_remote_err(err) - self._remote_err = err + self._remote_error = err return self._remote_error or self._result