MyType: A Powerful-yet-constrained Type Wrapper#
- class my.typing.MyType.MyType(root: TypeArg[R] = typing.Any, uid: int = 0, *, raw: Any = <class 'NoneType'>, main: type[T] | type[Union] | type[None] | None = None, name: str = '', args: tuple[~my.typing.MyType.MyType, ...]=(), vals: MyType | None = None, keys: MyType | None = None, origin: type | None = None, literal_members: list[Any] = [])#
A wrapper for any type annotation that normalizes the wide variety of interfaces.
Examples
Consider this simplified subset of the hierarchy:
str_t = MyType(str) bytes_t = MyType(bytes) int_t = MyType(int) float_t = MyType(float) String = str | bytes string_t = MyType(String) Scalar = int | float scalar_t = MyType(Scalar) Atom = str | int | bytes | float atom_t = MyType(Atom)
``__and__()``
# Affirms if either is part of the other, fails otherwise assert str_t & atom_t assert atom_t & str_t assert not str_t & int_t # Works with a raw *plain type* on either side, as long as the other side is wrapped assert str & atom_t and str_t & Atom assert atom_t & str #! assert str & Atom # two raw annotations know nothing of each other #! assert Atom & str_t # a raw union LHS cannot reflect onto the wrapped RHS # Works with unions and tuples of plain types (when LHS is wrapped) assert str_t & (str | Scalar) assert str_t & (str, int) assert not str_t & (bytes, int)
``__contains__()``
# Determines whether the LHS is a subset of the RHS, but not the other way around. assert str in atom_t assert Atom not in str_t # RHS must be a MyType. #! assert str in Atom # Works with tuples assert str in (str_t, Scalar)
“Main” types (i.e.
MyType.main)The main type of an instance represents the most meaningfully active part of that type in this moment. Some cases:
assert MyType(dict[str, int]).main is dict assert MyType(Literal['a', 'b']).main is None assert MyType(Optional[int]).is_split
- Some cases:
Basic generics use their origins, while the vast majority of Atoms use themselves in full.
Literals use
Literal.Monotomic “Special Forms” (e.g.
Annotated[int, ...],Final[str]) use a type or union representing the wrapped content of the inner form.Polytomic “Special Forms” (e.g.
Optional[int]/Union[int, str]->UnionType,Unpack[str, str]->Unpack) use a type representing the wrapping content of the outer form.
I Initial Methods#
- static MyType.new() MyType[Any]#
- static MyType.new(root: None) MyType[NoneType]
- static MyType.new(root: tuple[type[R], ...]) MyType[type[R] | ...]
- static MyType.new(root: TypeArg[R]) MyType[R]
- static MyType.new(root: type[R] | MyType[R]) MyType[R]
- static MyType.new(root: R) MyType[R]
Create a new MyType instance by parsing a type OR inferring the full type of a value.
Examples
Types parse; values infer:
>>> from my import MyType >>> MyType.new(int) MyType[<class 'int'>](main=<class 'int'>) >>> MyType.new(5) MyType[<class 'int'>](main=<class 'int'>)
- classmethod MyType.parse(root: Rm, throw: bool = False) Rm#
- classmethod MyType.parse(root: type[R], throw: bool = False) MyType[R]
- classmethod MyType.parse(root: Rt, throw: bool = False) MyType[Rt]
- classmethod MyType.parse(root: Union, throw: bool = False) MyType
- classmethod MyType.parse(root: TypeAliasType, throw: bool = False) MyType
- classmethod MyType.parse(root: None, throw: bool = False) MyType[NoneType]
- classmethod MyType.parse(root: object, throw: bool = False) MyType
Decompose a given type so that other methods can intelligently handle each part in turn.
By far the most likely usecase is for containers such as
dict[str, int](which becomes the tuple(dict, str, int)) andlist[int](which becomes(list, int, None)), but it’s useful for other generics, unions (e.g.string | int), and special non-type forms (e.g.AnnotatedandLiteral).- Parameters:
root – The type annotation to decompose – either a type, a union of types, or None.
throw – If True, will re-raise any exceptions encountered during parsing.
- Returns:
A MyType instance with the root set to the original type, and the other fields populated according to the structure of that type.
Examples
Decompose parameterized generics into their parts:
>>> from my import MyType >>> MyType.parse(dict[str, int]).summarize() (<class 'dict'>, <class 'str'>, <class 'int'>) >>> MyType.parse(int | str).is_split True
- classmethod MyType.typeof(data: R) MyType[TypeVar]#
Infer the type annotation of a given data value, recursing into containers.
- Parameters:
data – Data value to infer type from.
- Returns:
Parsed MyType instance representing the inferred type.
Examples
Infer full container annotations from runtime values:
>>> from my import MyType >>> str(MyType.typeof({'a': [1]})) 'dict[str, list[int]]'
II Properties#
- property MyType.rtype: type | Union#
A version of the root that has been lightly coerced into being a regular type.
Plain types and unions return themselves, and an
Unpackunwraps to its content. Otherwise a split type collapses to the bareUnionType, a parameterized generic yields its first argument’s root (e.g.dict[str, int]->str, notdict), and anything else falls back toAny.
III Operators#
- MyType.__contains__(child: None) TypeIs[None]#
- MyType.__contains__(child: type) TypeIs[type[T]]
- MyType.__contains__(child: MyType) TypeIs[MyType[T]]
- MyType.__contains__(child: tuple[type, ...]) TypeIs[tuple[type[T], ...]]
- MyType.__contains__(child: object) TypeIs[type[T]]
Determine whether the preceding type is a valid subset of this one.
- MyType.__and__(other: None) TypeIs[None]#
- MyType.__and__(other: type) TypeIs[type[T]]
- MyType.__and__(other: MyType) TypeIs[MyType[T]]
- MyType.__and__(other: tuple[type, ...]) TypeIs[tuple[type[T], ...]]
Determine whether either of the two types contains the other.
- MyType.__rand__(other: None) TypeIs[None]#
- MyType.__rand__(other: type) TypeIs[type[T]]
- MyType.__rand__(other: MyType) TypeIs[MyType[T]]
- MyType.__rand__(other: tuple[type, ...]) TypeIs[tuple[type[T], ...]]
- MyType.__rand__(other: object) TypeIs[type[T]]
Determine whether either of the two types contains the other (reflected form).
IV Application#
- MyType.match(other: None) TypeIs[None]#
- MyType.match(other: type) TypeIs[type[T]]
- MyType.match(other: MyType) TypeIs[MyType[T]]
- MyType.match(other: tuple[type, ...]) TypeIs[tuple[type[T], ...]]
- MyType.match(other: object) TypeIs[type[T]]
Determine whether the given type value is a subset of this type.
Examples
Membership runs from the argument into this type:
>>> from my import MyType >>> MyType(int | str).match(int) True >>> MyType(int).match(int | str) False
- MyType.check(data: object) TypeIs[T]#
Determine whether a given data value matches this type, recursing into containers.
- Parameters:
data – The data value to check. Ideally not an exhaustable iter.
- Returns:
True if all aspects of this type are satisfied by this data, including nested types.
Examples
Validate values against the parsed type:
>>> from my import MyType >>> t = MyType.parse(dict[str, int]) >>> t.check({'a': 1}), t.check({'a': 'b'}) (True, False)
- MyType.check_iter(data: Iterable) Iterator[bool]#
Yield a boolean for each element of
dataindicating if it matches this type.- Parameters:
data – The iterable of values to check. Ideally not an exhaustable iter.
- Yields:
One boolean per element, True if that element matches this type.
- MyType.literal_check(val: object) bool#
Determine whether a value satisfies this Literal or tuple-literal type.
- MyType.is_map_item() bool#
Whether this type is a map item: a bare
tupleor atupleof exactly two types.
- MyType.members() Iterator[MyType]#
Yield all field types for Pydantic models or TypedDicts.
- Returns:
Iterator of MyType instances for each field in the type.