Last active
December 25, 2022 16:29
-
-
Save rectalogic/e54cb7e013bd6013a7a2426edf88f223 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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