Skip to content

Instantly share code, notes, and snippets.

@rectalogic
Last active December 25, 2022 16:29
Show Gist options
  • Save rectalogic/e54cb7e013bd6013a7a2426edf88f223 to your computer and use it in GitHub Desktop.
Save rectalogic/e54cb7e013bd6013a7a2426edf88f223 to your computer and use it in GitHub Desktop.
from __future__ import annotations
import typing as ta
# https://stackoverflow.com/questions/64161037/how-can-i-use-mypy-to-overload-the-init-method-to-adjust-a-getters-return-v
T = ta.TypeVar("T", covariant=True)
RT = ta.TypeVar("RT")
class Field(ta.Generic[T, RT]):
value: ta.Optional[ta.Union[T, ta.Sequence[T]]]
@ta.overload
def __init__(self: Field[T, T], key: str, value: T, sequence: ta.Literal[False] = False):
...
@ta.overload
def __init__(self: Field[T, ta.Sequence[T]], key: str, value: ta.Sequence[T], sequence: ta.Literal[True]):
...
@ta.overload
def __init__(self: Field[T, ta.Union[T, ta.Sequence[T]]], key: str, value: ta.Union[T, ta.Sequence[T]], sequence: ta.Literal[True]):
...
def __init__(self, key: str, value: ta.Union[T, ta.Sequence[T]], sequence: bool = False):
self.key = key
self.sequence = sequence
self.value = value
@ta.overload
def __get__(self, instance: None, owner=None) -> Field:
...
@ta.overload
def __get__(self: Field[T, T], instance: ta.Any, owner=None) -> ta.Optional[T]:
...
@ta.overload
def __get__(self: Field[T, ta.Sequence[T]], instance: ta.Any, owner=None) -> ta.Optional[ta.Sequence[T]]:
...
def __get__(self, instance: ta.Optional[ta.Any], owner=None) -> ta.Union[Field, ta.Optional[T], ta.Sequence[T]]:
if instance is None:
return self
#return ([self.value] if self.value else []) if self.sequence else self.value
return self.value
@ta.overload
def __set__(self: Field[T, T], instance, value: ta.Optional[T]):
...
@ta.overload
def __set__(self: Field[T, ta.Sequence[T]], instance, value: ta.Optional[ta.Sequence[T]]):
...
@ta.overload
def __set__(self: Field[T, ta.Union[T, ta.Sequence[T]]], instance, value: ta.Optional[ta.Union[T, ta.Sequence[T]]]):
...
def __set__(self, instance, value: ta.Optional[ta.Union[T, ta.Sequence[T]]]):
self.value = value
class Owner:
f = Field("key1", "val")
ll = Field("key2", ["val"], True)
o = Owner()
reveal_type(o.f)
o.f = "hello"
print(o.f)
reveal_type(o.ll)
o.ll = ["hello"]
print(o.ll)
reveal_type(Owner.f)
print(Owner.f.key)
from __future__ import annotations
from typing import TypeVar, overload, Literal, Generic
_GetReturnT = TypeVar('_GetReturnT', str, list[str], str | list[str])
class StringProperty(Generic[_GetReturnT]):
# Handles the default value case too.
@overload
def __init__(self: StringProperty[str], repeated: Literal[False]=False, **kwargs) -> None: ...
@overload
def __init__(self: StringProperty[list[str]], repeated: Literal[True], **kwargs) -> None: ...
# Callers won't always pass a literal bool right at the call site. The bool
# could come from somewhere far. Then we can't know what exactly get()
# will return.
@overload
def __init__(self: StringProperty[str | list[str]], repeated: bool, **kwargs) -> None: ...
def __init__(self, repeated: bool = False, **kwargs) -> None:
self._repeated = repeated
@overload
def get(self: StringProperty[str]) -> str:
...
@overload
def get(self: StringProperty[list[str]]) -> list[str]:
...
@overload
def get(self: StringProperty[str | list[str]]) -> str | list[str]:
...
def get(self) -> str | list[str]:
if self._repeated:
return ["Hello", "world!"]
else:
return "just one string"
default = StringProperty() # StringProperty[str]
default_get = default.get() # str
false_literal = StringProperty(repeated=False) # StringProperty[str]
false_literal_get = false_literal.get() # str
true_literal = StringProperty(repeated=True) # StringProperty[list[str]]
true_literal_get = true_literal.get() # list[str]
import random
some_bool = random.choice([True, False]) # bool
unknown_bool = StringProperty(repeated=some_bool) # StringProperty[str | list[str]]
unknown_bool_get = unknown_bool.get() # str | list[str]
reveal_locals()
# error: Value of type variable "_GetReturnT" of "StringProperty" cannot be "int"
#
# This error happens because we limited _GetReturnT's possible types in
# TypeVar(). If we didn't limit the types, users could accidentally refer to a
# type in an annotation that's impossible to instantiate.
def some_user_function(prop: StringProperty[int]) -> None:
prop.get()
from __future__ import annotations
import typing as ta
T = ta.TypeVar("T")
RT = ta.TypeVar("RT")
class Base: pass
class BaseValueField(ta.Generic[T, RT]):
sequence: ta.ClassVar[bool] = False
value: ta.Optional[ta.Optional[T] | ta.Sequence[T]] = None
@ta.overload
def __get__(self, model: None, model_cls: type) -> BaseValueField[T, RT]:
...
@ta.overload
def __get__(
self: BaseValueField[T, RT], model: Base, model_cls: type
) -> RT:
...
def __get__(self, model, model_cls):
if model is None:
return self
if self.sequence:
return ta.cast(ta.Sequence[T], self.value) if self.value is not None else []
else:
return self.value
class _StringValueField(BaseValueField[str, RT]):
@ta.overload
def __get__(self, model: None, model_cls: type) -> BaseValueField[str, RT]:
...
@ta.overload
def __get__(
self: _StringValueField[RT], model: Base, model_cls: type
) -> RT:
...
def __get__(self, model, model_cls):
return super().__get__(model, model_cls)
StringValueField = _StringValueField[ta.Optional[str]]
class SequenceStringValueField(_StringValueField[ta.Sequence[str]]):
sequence = True
class Owner(Base):
ss = SequenceStringValueField()
s = StringValueField()
reveal_type(Owner.s)
reveal_type(Owner().s)
reveal_type(Owner.ss)
reveal_type(Owner().ss)
from __future__ import annotations
import typing as ta
T = ta.TypeVar("T")
RT = ta.TypeVar("RT")
class BaseValueField(ta.Generic[T, RT]):
# @ta.overload
# def __set__(
# self: BaseValueField[T, ta.Sequence[T]], model: object, value: ta.Optional[ta.Sequence[T]]
# ):
# ...
# @ta.overload
# def __set__(self: BaseValueField[T, ta.Optional[T]], model: object, value: ta.Optional[T]):
# ...
def __set__(self, model: object, value: ta.Optional[T | ta.Sequence[T]]):
pass
class StringValueField(BaseValueField[str, RT]):
# @ta.overload
# def __set__(
# self: StringValueField[ta.Sequence[str]], model: object, value: ta.Optional[ta.Sequence[str]]
# ):
# ...
# @ta.overload
# def __set__(self: StringValueField[ta.Optional[str]], model: object, value: ta.Optional[str]):
# ...
def __set__(self, model: object, value: ta.Optional[str | ta.Sequence[str]]):
super().__set__(model, value)
from __future__ import annotations
import typing as ta
RT = ta.TypeVar("RT")
class Field(ta.Generic[RT]):
value: ta.Optional[RT]
@ta.overload
def __init__(self: Field[str], sequence: ta.Literal[False] = False):
...
@ta.overload
def __init__(self: Field[ta.Sequence[str]], sequence: ta.Literal[True]):
...
def __init__(self, sequence: bool = False):
self.sequence = sequence
self.value = None
@ta.overload
def __get__(self: Field[str], instance: None, owner=None) -> Field[RT]:
...
@ta.overload
def __get__(self: Field[ta.Sequence[str]], instance: None, owner=None) -> Field[RT]:
...
@ta.overload
def __get__(self: Field[str], instance: ta.Any, owner=None) -> ta.Optional[RT]:
...
@ta.overload
def __get__(self: Field[ta.Sequence[str]], instance: ta.Any, owner=None) -> ta.Optional[RT]:
...
def __get__(self, instance: ta.Optional[ta.Any], owner=None) -> ta.Union[Field[RT], ta.Optional[RT]]:
if instance is None:
return self
return self.value
@ta.overload
def __set__(self: Field[str], instance, value: ta.Optional[RT]):
...
@ta.overload
def __set__(self: Field[ta.Sequence[str]], instance, value: ta.Optional[RT]):
...
def __set__(self, instance, value: ta.Optional[RT]):
self.value = value
class Owner:
f = Field(False)
fl = Field(True)
o = Owner()
field_str = Field(False) # sfield.Field[builtins.str]
field_str_get = field_str.__get__(o) # Union[builtins.str, None]
instance_field_str = o.f # Union[builtins.str, None]
field_str_accessor = Owner.f # sfield.Field[builtins.str]
field_seq_str = Field(True) # sfield.Field[typing.Sequence[builtins.str]]
field_seq_str_get = field_seq_str.__get__(o) # Union[typing.Sequence[builtins.str], None]
instance_field_seq_str = o.fl # sfield.Field[typing.Sequence[builtins.str]]
field_seq_str_accessor = Owner.fl # sfield.Field[typing.Sequence[builtins.str]]
if ta.TYPE_CHECKING:
reveal_locals()
o.f = "hello"
print(o.f)
o.fl = ["hello"]
print(o.fl[0])
if ta.TYPE_CHECKING:
reveal_type(o.fl) # Revealed type is "builtins.list[builtins.str]"
print(o.fl)
from __future__ import annotations
import typing as ta
RT = ta.TypeVar("RT")
class Field(ta.Generic[RT]):
value: ta.Optional[RT]
@ta.overload
def __init__(self: Field[str], sequence: ta.Literal[False] = False):
...
@ta.overload
def __init__(self: Field[ta.Sequence[str]], sequence: ta.Literal[True]):
...
def __init__(self, sequence: bool = False):
self.sequence = sequence
self.value = None
@ta.overload
def __get__(self: Field[str], instance: None, owner=None) -> Field[RT]:
...
@ta.overload
def __get__(self: Field[ta.Sequence[str]], instance: None, owner=None) -> Field[RT]:
...
@ta.overload
def __get__(self: Field[str], instance: ta.Any, owner=None) -> ta.Optional[RT]:
...
@ta.overload
def __get__(self: Field[ta.Sequence[str]], instance: ta.Any, owner=None) -> ta.Optional[RT]:
...
def __get__(self, instance: ta.Optional[ta.Any], owner=None) -> ta.Union[Field[RT], ta.Optional[RT]]:
if instance is None:
return self
return self.value
field_str = Field(False) # sfield.Field[builtins.str]
field_seq_str = Field(True) # sfield.Field[typing.Sequence[builtins.str]]
class Owner:
f = field_str
fl = field_seq_str
o = Owner()
field_str_get = field_str.__get__(o) # Union[builtins.str, None]
instance_field_str = o.f # Union[builtins.str, None]
field_str_accessor = Owner.f # sfield.Field[builtins.str]
field_seq_str_get = field_seq_str.__get__(o) # Union[typing.Sequence[builtins.str], None]
instance_field_seq_str = o.fl # XXX Union[builtins.str, None]
field_seq_str_accessor = Owner.fl # XXX sfield.Field[builtins.str]
if ta.TYPE_CHECKING:
reveal_locals()
o.f = "hello"
print(o.f)
o.fl = ["hello"]
print(o.fl[0])
if ta.TYPE_CHECKING:
reveal_type(o.fl) # XXX Revealed type is "Union[builtins.str, None]"
print(o.fl)
from __future__ import annotations
import typing as ta
T = ta.TypeVar("T")
RT = ta.TypeVar("RT")
class Base: pass
class BaseValueField(ta.Generic[T, RT]):
value: ta.Optional[RT]
class ValueField(BaseValueField[T, ta.Optional[T]]):
@ta.overload
def __get__(self, model: None, model_cls: ta.Type[Base]) -> ValueField[T]:
...
@ta.overload
def __get__(
self, model: Base, model_cls: ta.Type[Base]
) -> ta.Optional[T]:
...
def __get__(
self, model: ta.Optional[Base], model_cls: ta.Type[Base]
) -> ValueField[T] | ta.Optional[T]:
if model is None:
return self
return self.value
class SequenceValueField(BaseValueField[T, ta.Sequence[T]]):
@ta.overload
def __get__(self, model: None, model_cls: ta.Type[Base]) -> SequenceValueField[T]:
...
@ta.overload
def __get__(
self, model: Base, model_cls: ta.Type[Base]
) -> ta.Sequence[T]:
...
def __get__(
self, model: ta.Optional[Base], model_cls: ta.Type[Base]
) -> SequenceValueField[T] | ta.Sequence[T]:
if model is None:
return self
return self.value if self.value is not None else []
class StringValueField(ValueField[str]):
pass
# @ta.overload
# def __get__(self, model: None, model_cls: ta.Type[Base]) -> StringValueField:
# ...
# @ta.overload
# def __get__(
# self, model: Base, model_cls: ta.Type[Base]
# ) -> ta.Optional[str]:
# ...
# def __get__(
# self, model: ta.Optional[Base], model_cls: ta.Type[Base]
# ) -> StringValueField | ta.Optional[str]:
# return super().__get__(model, model_cls)
class SequenceStringValueField(SequenceValueField[str]):
pass
# @ta.overload
# def __get__(self, model: None, model_cls: ta.Type[Base]) -> SequenceStringValueField:
# ...
# @ta.overload
# def __get__(
# self, model: Base, model_cls: ta.Type[Base]
# ) -> ta.Sequence[str]:
# ...
# def __get__(
# self, model: ta.Optional[Base], model_cls: ta.Type[Base]
# ) -> SequenceStringValueField | ta.Sequence[str]:
# return super().__get__(model, model_cls)
class Owner(Base):
ss = SequenceStringValueField()
s = StringValueField()
reveal_type(Owner.ss)
reveal_type(Owner().ss)
reveal_type(Owner.s)
reveal_type(Owner().s)
from __future__ import annotations
import typing as ta
# https://stackoverflow.com/questions/64161037/how-can-i-use-mypy-to-overload-the-init-method-to-adjust-a-getters-return-v
RT = ta.TypeVar("RT")
class Field(ta.Generic[RT]):
value: ta.Optional[ta.Union[str, ta.Sequence[str]]]
@ta.overload
def __init__(self: Field[str], key: str, sequence: ta.Literal[False] = False):
...
#@ta.overload
#def __init__(self: Field[ta.Sequence[str]], key: str, sequence: ta.Literal[True]):
# ...
@ta.overload
def __init__(self: Field[ta.Union[str, ta.Sequence[str]]], key: str, sequence: ta.Literal[True]):
...
def __init__(self, key: str, sequence: bool = False):
self.key = key
self.sequence = sequence
self.value = None
@ta.overload
def __get__(self, instance: None, owner=None) -> Field:
...
@ta.overload
def __get__(self: Field[str], instance: ta.Any, owner=None) -> ta.Optional[str]:
...
@ta.overload
def __get__(self: Field[ta.Sequence[str]], instance: ta.Any, owner=None) -> ta.Optional[ta.Sequence[str]]:
...
def __get__(self, instance: ta.Optional[ta.Any], owner=None) -> ta.Union[Field, ta.Optional[ta.Union[str, ta.Sequence[str]]]]:
if instance is None:
return self
#return ([self.value] if self.value else []) if self.sequence else self.value
return self.value
@ta.overload
def __set__(self: Field[str], instance, value: ta.Optional[str]):
...
@ta.overload
def __set__(self: Field[ta.Sequence[str]], instance, value: ta.Optional[ta.Sequence[str]]):
...
def __set__(self, instance, value: ta.Optional[ta.Union[str, ta.Sequence[str]]]):
self.value = value
class Owner:
f = Field("key1")
ll = Field("key2", True)
o = Owner()
false_literal = Field("key1", False)
false_literal_get = false_literal.__get__(o)
true_literal = Field("key1", True)
true_literal_get = true_literal.__get__(o)
reveal_locals()
reveal_type(o.f)
o.f = "hello"
print(o.f)
reveal_type(o.ll)
o.ll = ["hello"]
print(o.ll)
reveal_type(Owner.f)
print(Owner.f.key)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment