Skip to content

Instantly share code, notes, and snippets.

@FFY00
Created January 4, 2025 09:29
Show Gist options
  • Save FFY00/77e0275793d8445528955924b7771d42 to your computer and use it in GitHub Desktop.
Save FFY00/77e0275793d8445528955924b7771d42 to your computer and use it in GitHub Desktop.
class Transform[T, SetterT=None]:
def __init__(self, fn: Callable[[SetterT], T] | None = None, *, default: T = typing.NoDefault) -> None:
self._fn = fn
self._default = default
self._values: dict[object, T] = {}
@functools.cached_property
def _type(self):
if not hasattr(self, '__orig_class__'):
raise TypeError('Use Transform[...](...) instead of Transform(...).')
return typing.get_args(self.__orig_class__)[0]
@functools.cached_property
def _optional(self):
if typing.get_origin(self._type) == types.UnionType:
for typ in typing.get_args(self._type):
if typ is types.NoneType:
return True
return False
def __get__[ObjT](self, obj: ObjT, owner: type[ObjT] | None = None) -> T:
if self._default is typing.NoDefault:
return self._values[obj]
else:
return self._values.get(obj, self._default)
def __set__[ObjT](self, obj: ObjT, value: T | SetterT) -> None:
if isinstance(value, self._type):
value = typing.cast(T, value)
self._values[obj] = value
elif self._fn is not None:
value = typing.cast(SetterT, value)
self._values[obj] = self._fn(value)
else:
raise TypeError(f'Unable to convert {value!r} to {self._type}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment