diff --git a/src/execnet/gateway_base.py b/src/execnet/gateway_base.py index 252bdbcb..f0bd2512 100644 --- a/src/execnet/gateway_base.py +++ b/src/execnet/gateway_base.py @@ -316,6 +316,8 @@ class Reply: """Provide access to the result of a function execution that got dispatched through WorkerPool.spawn().""" + _exception: BaseException | None = None + def __init__(self, task, threadmodel: ExecModel) -> None: self.task = task self._result_ready = threadmodel.Event() @@ -328,10 +330,10 @@ def get(self, timeout: float | None = None): including its traceback. """ self.waitfinish(timeout) - try: + if self._exception is None: return self._result - except AttributeError: - raise self._exc from None + else: + raise self._exception.with_traceback(self._exception.__traceback__) def waitfinish(self, timeout: float | None = None) -> None: if not self._result_ready.wait(timeout): @@ -342,8 +344,9 @@ def run(self) -> None: try: try: self._result = func(*args, **kwargs) - except BaseException as exc: - self._exc = exc + except BaseException as e: + # sys may be already None when shutting down the interpreter + self._exception = e finally: self._result_ready.set() self.running = False @@ -526,7 +529,9 @@ def __init__(self, outfile, infile, execmodel: ExecModel) -> None: except (AttributeError, OSError): pass self._read = getattr(infile, "buffer", infile).read - self._write = getattr(outfile, "buffer", outfile).write + _outfile = getattr(outfile, "buffer", outfile) + self._write = _outfile.write + self._flush = _outfile.flush self.execmodel = execmodel def read(self, numbytes: int) -> bytes: @@ -544,7 +549,7 @@ def write(self, data: bytes) -> None: """Write out all data bytes.""" assert isinstance(data, bytes) self._write(data) - self.outfile.flush() + self._flush() def close_read(self) -> None: self.infile.close() diff --git a/testing/test_xspec.py b/testing/test_xspec.py index 4c9ff8d6..de011c0f 100644 --- a/testing/test_xspec.py +++ b/testing/test_xspec.py @@ -12,6 +12,7 @@ from execnet import XSpec from execnet.gateway import Gateway from execnet.gateway_io import popen_args +from execnet.gateway_io import popen_bootstrapline from execnet.gateway_io import ssh_args from execnet.gateway_io import vagrant_ssh_args @@ -78,13 +79,7 @@ def test_vagrant_options(self) -> None: def test_popen_with_sudo_python(self) -> None: spec = XSpec("popen//python=sudo python3") - assert popen_args(spec) == [ - "sudo", - "python3", - "-u", - "-c", - "import sys;exec(eval(sys.stdin.readline()))", - ] + assert popen_args(spec) == ["sudo", "python3", "-u", "-c", popen_bootstrapline] def test_env(self) -> None: xspec = XSpec("popen//env:NAME=value1")