Skip to content

Commit

Permalink
use in-house outcome to avoid attrs import
Browse files Browse the repository at this point in the history
  • Loading branch information
richardsheridan committed Dec 21, 2024
1 parent bf497f0 commit 2647871
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
2 changes: 1 addition & 1 deletion _trio_parallel_workers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
except ImportError:
from pickle import dumps, loads

from outcome import capture, Error
from ._cheap_outcome import capture, Error
from tblib.pickling_support import install as install_pickling_support

MAX_TIMEOUT = 24.0 * 60.0 * 60.0
Expand Down
66 changes: 66 additions & 0 deletions _trio_parallel_workers/_cheap_outcome.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Vendored from outcome in v1.3.0 under identical MIT/Apache2 license.
# Copyright Contributors to the outcome project.


class Outcome:
def __init__(self):
self._unwrapped = False

def _set_unwrapped(self) -> None:
assert not self._unwrapped, "An Outcome can only be unwrapped once."
object.__setattr__(self, "_unwrapped", True)


class Value(Outcome):
"""Concrete :class:`Outcome` subclass representing a regular value."""

def __init__(self, value):
self.value = value
super().__init__()

def unwrap(self):
self._set_unwrapped()
return self.value


class Error(Outcome):
"""Concrete :class:`Outcome` subclass representing a raised exception."""

def __init__(self, error):
self.error = error
super().__init__()

def unwrap(self):
self._set_unwrapped()
# Tracebacks show the 'raise' line below out of context, so let's give
# this variable a name that makes sense out of context.
captured_error = self.error
try:
raise captured_error
finally:
# We want to avoid creating a reference cycle here. Python does
# collect cycles just fine, so it wouldn't be the end of the world
# if we did create a cycle, but the cyclic garbage collector adds
# latency to Python programs, and the more cycles you create, the
# more often it runs, so it's nicer to avoid creating them in the
# first place. For more details see:
#
# https://github.com/python-trio/trio/issues/1770
#
# In particular, by deleting this local variables from the 'unwrap'
# methods frame, we avoid the 'captured_error' object's
# __traceback__ from indirectly referencing 'captured_error'.
del captured_error, self


def capture(sync_fn, *args, **kwargs):
"""Run ``sync_fn(*args, **kwargs)`` and capture the result.
Returns:
Either a :class:`Value` or :class:`Error` as appropriate.
"""
try:
return Value(sync_fn(*args, **kwargs))
except BaseException as exc: # noqa: ASYNC103
return Error(exc.with_traceback(exc.__traceback__.tb_next)) # noqa: ASYNC104

0 comments on commit 2647871

Please sign in to comment.