Skip to content

Commit 5d9e647

Browse files
committed
remove reference to value/error when unwrapping outcome
1 parent 03ed621 commit 5d9e647

File tree

1 file changed

+37
-19
lines changed

1 file changed

+37
-19
lines changed

src/outcome/_impl.py

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import enum
34
import abc
45
from typing import (
56
TYPE_CHECKING,
@@ -12,17 +13,20 @@
1213
TypeVar,
1314
Union,
1415
overload,
16+
Literal,
1517
)
1618

1719
import attr
1820

1921
from ._util import AlreadyUsedError, remove_tb_frames
2022

2123
if TYPE_CHECKING:
22-
from typing_extensions import ParamSpec, final
24+
from typing_extensions import ParamSpec, final, Final
2325
ArgsT = ParamSpec("ArgsT")
2426
else:
2527

28+
Final = object
29+
2630
def final(func):
2731
return func
2832

@@ -106,6 +110,14 @@ async def acapture(
106110
return Error(exc)
107111

108112

113+
class _Unwrapped(enum.Enum):
114+
IS_UNWRAPPED = enum.auto()
115+
116+
117+
IS_UNWRAPPED: Final = _Unwrapped.IS_UNWRAPPED
118+
IsUnwrapped: TypeAlias = Literal[_Unwrapped.IS_UNWRAPPED]
119+
120+
109121
@attr.s(repr=False, init=False, slots=True)
110122
class Outcome(abc.ABC, Generic[ValueT]):
111123
"""An abstract class representing the result of a Python computation.
@@ -122,13 +134,6 @@ class Outcome(abc.ABC, Generic[ValueT]):
122134
hashable.
123135
124136
"""
125-
_unwrapped: bool = attr.ib(default=False, eq=False, init=False)
126-
127-
def _set_unwrapped(self) -> None:
128-
if self._unwrapped:
129-
raise AlreadyUsedError
130-
object.__setattr__(self, '_unwrapped', True)
131-
132137
@abc.abstractmethod
133138
def unwrap(self) -> ValueT:
134139
"""Return or raise the contained value or exception.
@@ -170,23 +175,30 @@ class Value(Outcome[ValueT], Generic[ValueT]):
170175
171176
"""
172177

173-
value: ValueT = attr.ib()
178+
_value: IsUnwrapped | ValueT = attr.ib()
174179
"""The contained value."""
175180

181+
@property
182+
def value(self) -> ValueT:
183+
return self.unwrap()
184+
176185
def __repr__(self) -> str:
177186
return f'Value({self.value!r})'
178187

179188
def unwrap(self) -> ValueT:
180-
self._set_unwrapped()
181-
return self.value
189+
if self._value is IS_UNWRAPPED:
190+
raise AlreadyUsedError
191+
192+
try:
193+
return self._value
194+
finally:
195+
object.__setattr__(self, "_value", IS_UNWRAPPED)
182196

183197
def send(self, gen: Generator[ResultT, ValueT, object]) -> ResultT:
184-
self._set_unwrapped()
185-
return gen.send(self.value)
198+
return gen.send(self.unwrap())
186199

187200
async def asend(self, agen: AsyncGenerator[ResultT, ValueT]) -> ResultT:
188-
self._set_unwrapped()
189-
return await agen.asend(self.value)
201+
return await agen.asend(self.unwrap())
190202

191203

192204
@final
@@ -196,16 +208,24 @@ class Error(Outcome[NoReturn]):
196208
197209
"""
198210

199-
error: BaseException = attr.ib(
211+
_error: IsUnwrapped | BaseException = attr.ib(
200212
validator=attr.validators.instance_of(BaseException)
201213
)
202214
"""The contained exception object."""
203215

216+
@property
217+
def error(self):
218+
if self._error is IS_UNWRAPPED:
219+
raise AlreadyUsedError
220+
try:
221+
return self._error
222+
finally:
223+
object.__setattr__(self, "_error", IsUnwrapped)
224+
204225
def __repr__(self) -> str:
205226
return f'Error({self.error!r})'
206227

207228
def unwrap(self) -> NoReturn:
208-
self._set_unwrapped()
209229
# Tracebacks show the 'raise' line below out of context, so let's give
210230
# this variable a name that makes sense out of context.
211231
captured_error = self.error
@@ -227,11 +247,9 @@ def unwrap(self) -> NoReturn:
227247
del captured_error, self
228248

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

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

237255

0 commit comments

Comments
 (0)