11from __future__ import annotations
22
3+ import enum
34import abc
45from typing import (
56 TYPE_CHECKING ,
1213 TypeVar ,
1314 Union ,
1415 overload ,
16+ Literal ,
1517)
1618
1719import attr
1820
1921from ._util import AlreadyUsedError , remove_tb_frames
2022
2123if TYPE_CHECKING :
22- from typing_extensions import ParamSpec , final
24+ from typing_extensions import ParamSpec , final , Final
2325 ArgsT = ParamSpec ("ArgsT" )
2426else :
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 )
110122class 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