Skip to content

Instantly share code, notes, and snippets.

@numberoverzero
Last active March 24, 2017 13:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save numberoverzero/a18deec64e6434d4c95854754d71a619 to your computer and use it in GitHub Desktop.
Save numberoverzero/a18deec64e6434d4c95854754d71a619 to your computer and use it in GitHub Desktop.
PEP 487 is insane, holy fuck this is awesome
from typing import Any, Dict, List, NamedTuple, Optional, Tuple, TypeVar, Generic, Type
T = TypeVar("T")
class Field(Generic[T]):
key: str
t: Type[T]
readonly: bool
def __init__(self, t: Type[T], readonly: bool=True) -> None:
self.t = t
self.readonly = readonly
def __get__(self, instance: Any, owner: Any) -> T:
return instance.__dict__[self.key]
def __set__(self, instance: Any, new: T):
assert not self.readonly
old = instance.__dict__.get(self.key)
instance.__dict__[self.key] = new
if old != new:
self.notify(instance, old, new)
def notify(self, instance: Any, old: Any, new: Any) -> None:
print(self, instance, old, new)
class MutableField(Field[T]):
def __init__(self, t: Type[T]) -> None:
super().__init__(t, readonly=False)
class BaseModel:
def __init_subclass__(cls, **kwargs) -> None:
cls.fields = fields = {}
for name, attr in cls.__annotations__.items():
if issubclass(attr.__origin__, Field):
field_cls = attr.__origin__
fields[name] = field = field_cls(*attr.__args__)
setattr(cls, name, field)
field.key = name
def __init__(self, *args, **kwargs: Any) -> None:
for name, value in zip(cls.fields, args):
self.__dict__[name] = value
for name in kwargs:
if name in cls.fields:
self.__dict__[name] = kwargs[name]
cls.__init__ = __init__
super().__init_subclass__()
# USAGE
import pendulum
class Blob(BaseModel):
id: Field[int]
content: Field[bytes]
last_modified: MutableField[pendulum.Pendulum]
blob = Blob(3, b"hello")
blob.last_modified = pendulum.now()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment