Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 49 additions & 22 deletions src/outcome/_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,6 @@ class Outcome(abc.ABC, Generic[ValueT]):
hashable.

"""
_unwrapped: bool = attr.ib(default=False, eq=False, init=False)

def _set_unwrapped(self) -> None:
if self._unwrapped:
raise AlreadyUsedError
object.__setattr__(self, '_unwrapped', True)

@abc.abstractmethod
def unwrap(self) -> ValueT:
Expand Down Expand Up @@ -170,23 +164,38 @@ class Value(Outcome[ValueT], Generic[ValueT]):

"""

value: ValueT = attr.ib()
_value: ValueT = attr.ib()
"""The contained value."""
Comment thread
graingert marked this conversation as resolved.
Outdated

def __repr__(self) -> str:
return f'Value({self.value!r})'
try:
return f'Value({self._value!r})'
except AttributeError:
return 'Value(<AlreadyUsed>)'

def unwrap(self) -> ValueT:
self._set_unwrapped()
return self.value
try:
v = self._value
except AttributeError as e:
pass
else:
object.__delattr__(self, "_value")
return v
raise AlreadyUsedError

def send(self, gen: Generator[ResultT, ValueT, object]) -> ResultT:
self._set_unwrapped()
return gen.send(self.value)
return gen.send(self.unwrap())

async def asend(self, agen: AsyncGenerator[ResultT, ValueT]) -> ResultT:
self._set_unwrapped()
return await agen.asend(self.value)
return await agen.asend(self.unwrap())

@property
def value(self) -> ValueT:
try:
return self._value
except AttributeError as e:
pass
raise AlreadyUsedError


@final
Expand All @@ -196,19 +205,31 @@ class Error(Outcome[NoReturn]):

"""

error: BaseException = attr.ib(
_error: BaseException = attr.ib(
validator=attr.validators.instance_of(BaseException)
)
"""The contained exception object."""

def __repr__(self) -> str:
return f'Error({self.error!r})'
try:
return f'Error({self._error!r})'
except AttributeError:
return 'Error(<AlreadyUsed>)'

def _unwrap_error(self) -> BaseException:
try:
v = self._error
except AttributeError:
Comment thread
graingert marked this conversation as resolved.
Outdated
pass
else:
object.__delattr__(self, "_error")
return v
raise AlreadyUsedError
Comment thread
graingert marked this conversation as resolved.
Outdated

def unwrap(self) -> NoReturn:
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
captured_error = self._unwrap_error()
try:
raise captured_error
finally:
Expand All @@ -227,12 +248,18 @@ def unwrap(self) -> NoReturn:
del captured_error, self

def send(self, gen: Generator[ResultT, NoReturn, object]) -> ResultT:
self._set_unwrapped()
return gen.throw(self.error)
return gen.throw(self._unwrap_error())

async def asend(self, agen: AsyncGenerator[ResultT, NoReturn]) -> ResultT:
self._set_unwrapped()
return await agen.athrow(self.error)
return await agen.athrow(self._unwrap_error())

@property
def error(self) -> BaseException:
try:
return self._error
except AttributeError:
pass
raise AlreadyUsedError


# A convenience alias to a union of both results, allowing exhaustiveness checking.
Expand Down
6 changes: 4 additions & 2 deletions tests/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
def test_Outcome():
v = Value(1)
assert v.value == 1
assert v.unwrap() == 1
assert repr(v) == "Value(1)"
assert v.unwrap() == 1
assert repr(v) == "Value(<AlreadyUsed>)"

with pytest.raises(AlreadyUsedError):
v.unwrap()
Expand All @@ -21,11 +22,12 @@ def test_Outcome():
exc = RuntimeError("oops")
e = Error(exc)
assert e.error is exc
assert repr(e) == f"Error({exc!r})"
with pytest.raises(RuntimeError):
e.unwrap()
with pytest.raises(AlreadyUsedError):
e.unwrap()
assert repr(e) == f"Error({exc!r})"
assert repr(e) == "Error(<AlreadyUsed>)"

e = Error(exc)
with pytest.raises(TypeError):
Expand Down
Loading