3 # © Copyright 2021-2023, Scott Gasch
5 """This is the base class of
6 :class:`pyutils.parallelize.smart_future.SmartFuture`, which is a
7 piece of the simple parallelization framework.
9 This base class is essentially tries to have every Python `__dunder__`
10 method defined with a reasonabe default implementation so that, when
11 it is used in a manner that requires the value to be known, it calls
12 :meth:`DeferredOperand.resolve` and either gets the requisite value or
13 blocks until the data necessary to resolve the value is ready. This
14 is meant to enable more transparent :class:`Future` objects that can
15 be just used directly.
17 See :class:`pyutils.parallelize.smart_future.SmartFuture` for more
22 from abc import ABC, abstractmethod
23 from typing import Any, Generic, Set, TypeVar
25 # This module is commonly used by others in here and should avoid
26 # taking any unnecessary dependencies back on them.
31 class DeferredOperand(ABC, Generic[T]):
32 """A wrapper around an operand whose value is deferred until it is
33 needed (i.e. accessed). See the subclass
34 :class:`pyutils.parallelize.smart_future.SmartFuture` for an
35 example usage and/or a more useful patten.
38 def __init__(self, local_attributes: Set[str] = None):
41 local_attributes: because this class attempts to act as a
42 transparent wrapper around a normal Future, it needs
43 to be able to differentiate between attempts to set a
44 property of it/its subclasses or the wrapped object.
45 The local_attributes argument is a set of names that,
46 if we intercept a set operation for, refer to an
47 attribute in the wrapper and not the wrapped class.
49 self.__dict__['local_attributes'] = local_attributes
52 def _resolve(self, timeout=None) -> T:
56 def resolve(x: Any) -> Any:
58 When this object is used in a manner that requires it to know
59 its value, this method is called to either return the value or
60 block until it can do so.
63 x: the object whose value is required
66 The value of x... immediately if possible, eventually if
69 while isinstance(x, DeferredOperand):
73 def __lt__(self, other: Any) -> bool:
74 return DeferredOperand.resolve(self) < DeferredOperand.resolve(other)
76 def __le__(self, other: Any) -> bool:
77 return DeferredOperand.resolve(self) <= DeferredOperand.resolve(other)
79 def __eq__(self, other: Any) -> bool:
80 return DeferredOperand.resolve(self) == DeferredOperand.resolve(other)
82 def __ne__(self, other: Any) -> bool:
83 return DeferredOperand.resolve(self) != DeferredOperand.resolve(other)
85 def __gt__(self, other: Any) -> bool:
86 return DeferredOperand.resolve(self) > DeferredOperand.resolve(other)
88 def __ge__(self, other: Any) -> bool:
89 return DeferredOperand.resolve(self) >= DeferredOperand.resolve(other)
91 def __not__(self) -> bool:
92 return not DeferredOperand.resolve(self)
94 def bool(self) -> bool:
95 return DeferredOperand.resolve(self)
97 def __add__(self, other: Any) -> T:
98 return DeferredOperand.resolve(self) + DeferredOperand.resolve(other)
100 def __iadd__(self, other: Any) -> T:
101 return DeferredOperand.resolve(self) + DeferredOperand.resolve(other)
103 def __radd__(self, other: Any) -> T:
104 return DeferredOperand.resolve(self) + DeferredOperand.resolve(other)
106 def __sub__(self, other: Any) -> T:
107 return DeferredOperand.resolve(self) - DeferredOperand.resolve(other)
109 def __mul__(self, other: Any) -> T:
110 return DeferredOperand.resolve(self) * DeferredOperand.resolve(other)
112 def __pow__(self, other: Any) -> T:
113 return DeferredOperand.resolve(self) ** DeferredOperand.resolve(other)
115 def __truediv__(self, other: Any) -> Any:
116 return DeferredOperand.resolve(self) / DeferredOperand.resolve(other)
118 def __floordiv__(self, other: Any) -> T:
119 return DeferredOperand.resolve(self) // DeferredOperand.resolve(other)
121 def __contains__(self, other):
122 return DeferredOperand.resolve(other) in DeferredOperand.resolve(self)
124 def and_(self, other):
125 return DeferredOperand.resolve(self) & DeferredOperand.resolve(other)
127 def or_(self, other):
128 return DeferredOperand.resolve(self) & DeferredOperand.resolve(other)
130 def xor(self, other):
131 return DeferredOperand.resolve(self) & DeferredOperand.resolve(other)
134 return ~(DeferredOperand.resolve(self))
136 def is_(self, other):
137 return DeferredOperand.resolve(self) is DeferredOperand.resolve(other)
139 def is_not(self, other):
140 return DeferredOperand.resolve(self) is not DeferredOperand.resolve(other)
143 return abs(DeferredOperand.resolve(self))
145 def setitem(self, k, v):
146 DeferredOperand.resolve(self)[DeferredOperand.resolve(k)] = v
148 def delitem(self, k):
149 del DeferredOperand.resolve(self)[DeferredOperand.resolve(k)]
151 def getitem(self, k):
152 return DeferredOperand.resolve(self)[DeferredOperand.resolve(k)]
154 def lshift(self, other):
155 return DeferredOperand.resolve(self) << DeferredOperand.resolve(other)
157 def rshift(self, other):
158 return DeferredOperand.resolve(self) >> DeferredOperand.resolve(other)
160 def mod(self, other):
161 return DeferredOperand.resolve(self) % DeferredOperand.resolve(other)
163 def matmul(self, other):
164 return DeferredOperand.resolve(self) @ DeferredOperand.resolve(other)
167 return -(DeferredOperand.resolve(self))
170 return +(DeferredOperand.resolve(self))
173 return DeferredOperand.resolve(self)
176 return DeferredOperand.resolve(self).__hash__()
179 return DeferredOperand.resolve(self)()
182 return DeferredOperand.resolve(self).__iter__()
184 def __repr__(self) -> str:
185 return DeferredOperand.resolve(self).__repr__()
187 def __bytes__(self) -> bytes:
188 return DeferredOperand.resolve(self).__bytes__()
190 def __int__(self) -> int:
191 return int(DeferredOperand.resolve(self))
193 def __float__(self) -> float:
194 return float(DeferredOperand.resolve(self))
196 def __getattr__(self, name):
197 return getattr(DeferredOperand.resolve(self), name)
199 def __setattr__(self, name, value):
200 # subclass setting its own properties
201 if name in self.local_attributes:
202 object.__setattr__(self, name, value)
205 # otherwise operate on the wrapped result
206 DeferredOperand.resolve(self).__setattr__(name, value)
208 def __delattr__(self, name):
209 return delattr(DeferredOperand.resolve(self), name)
212 return dir(DeferredOperand.resolve(self))