Module arti.types.python
None
None
View Source
from __future__ import annotations
import datetime
from collections.abc import Mapping
from functools import partial
from itertools import chain
from typing import Any, Literal, Optional, TypedDict, Union, get_args, get_origin, get_type_hints
import arti.types
from arti.internal.type_hints import (
NoneType,
is_optional_hint,
is_typeddict,
is_union,
lenient_issubclass,
)
from arti.types import Type, TypeAdapter, TypeSystem, _ContainerMixin, _ScalarClassTypeAdapter
python_type_system = TypeSystem(key="python")
_generate = partial(_ScalarClassTypeAdapter.generate, type_system=python_type_system)
_generate(artigraph=arti.types.Binary, system=bytes)
# NOTE: issubclass(bool, int) is True, so set higher priority
_generate(artigraph=arti.types.Boolean, system=bool, priority=1000)
_generate(artigraph=arti.types.Date, system=datetime.date)
_generate(artigraph=arti.types.String, system=str)
for _precision in (16, 32, 64):
_generate(
artigraph=getattr(arti.types, f"Float{_precision}"),
system=float,
priority=_precision,
)
for _precision in (8, 16, 32, 64):
_generate(
artigraph=getattr(arti.types, f"Int{_precision}"),
system=int,
priority=_precision,
)
@python_type_system.register_adapter
class PyNone(_ScalarClassTypeAdapter):
# Python represents None types in type hints with the `None` value (not `NoneType`).
artigraph = arti.types.Null
system = None
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return type_ is None
@python_type_system.register_adapter
class PyDatetime(_ScalarClassTypeAdapter):
artigraph = arti.types.Timestamp
system = datetime.datetime
priority = 1 # Prioritize above Date (isinstance(datetime, date) is True)
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
return cls.artigraph(precision="microsecond")
class PyValueContainer(TypeAdapter):
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
(element,) = get_args(type_)
assert issubclass(cls.artigraph, _ContainerMixin)
return cls.artigraph(
element=type_system.to_artigraph(element, hints=hints),
)
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return lenient_issubclass(get_origin(type_), cls.system)
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[type_system.to_system(type_.element, hints=hints)] # type: ignore[attr-defined]
@python_type_system.register_adapter
class PyList(PyValueContainer):
artigraph = arti.types.List
system = list
priority = 1
# NOTE: PyTuple only covers sequences (eg: tuple[int, ...]), not structure (eg: tuple[int, str]).
@python_type_system.register_adapter
class PyTuple(PyValueContainer):
artigraph = arti.types.List
system = tuple
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
origin, args = get_origin(type_), get_args(type_)
assert origin is not None
assert len(args) == 2
assert args[1] is ...
return super().to_artigraph(origin[args[0]], hints=hints, type_system=type_system)
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
if super().matches_system(type_, hints=hints):
args = get_args(type_)
if len(args) == 2 and args[1] is ...:
return True
return False
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
ret = super().to_system(type_, hints=hints, type_system=type_system)
origin, args = get_origin(ret), get_args(ret)
assert origin is not None
assert len(args) == 1
return origin[args[0], ...]
@python_type_system.register_adapter
class PyFrozenset(PyValueContainer):
artigraph = arti.types.Set
system = frozenset
@python_type_system.register_adapter
class PySet(PyValueContainer):
artigraph = arti.types.Set
system = set
priority = 1 # Set above frozenset
@python_type_system.register_adapter
class PyLiteral(TypeAdapter):
artigraph = arti.types.Enum
system = Literal
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
origin, items = get_origin(type_), get_args(type_)
if is_union(origin):
assert not is_optional_hint(type_) # Should be handled by PyOptional
# We only support Enums currently, so all subtypes must be Literal
if non_literals := [sub for sub in items if get_origin(sub) is not Literal]:
raise NotImplementedError(
f"Only Union[Literal[...], ...] (enums) are currently supported, got invalid subtypes: {non_literals}"
)
# Flatten Union[Literal[1], Literal[1,2,3]]
origin, items = Literal, tuple(chain.from_iterable(get_args(sub) for sub in items))
assert origin is Literal
assert isinstance(items, tuple)
if len(items) == 0:
raise NotImplementedError(f"Invalid Literal with no values: {type_}")
py_type, *other_types = (type(v) for v in items)
if not all(t is py_type for t in other_types):
raise ValueError("All Literals must be the same type, got: {(py_type, *other_types)}")
return cls.artigraph(type=type_system.to_artigraph(py_type, hints=hints), items=items)
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
# We don't (currently) support arbitrary Unions, but can map Union[Literal[1], Literal[2]]
# to an Enum. Python's Optional is also represented as a Union, but we handle that with the
# high priority PyOptional.
origin, items = get_origin(type_), get_args(type_)
return origin is Literal or (
is_union(origin) and all(get_origin(sub) is Literal for sub in items)
)
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[tuple(type_.items)]
@python_type_system.register_adapter
class PyMap(TypeAdapter):
artigraph = arti.types.Map
system = dict
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
key, value = get_args(type_)
return cls.artigraph(
key=type_system.to_artigraph(key, hints=hints),
value=type_system.to_artigraph(value, hints=hints),
)
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return lenient_issubclass(get_origin(type_), (cls.system, Mapping))
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[ # type: ignore[index]
type_system.to_system(type_.key, hints=hints),
type_system.to_system(type_.value, hints=hints),
]
@python_type_system.register_adapter
class PyOptional(TypeAdapter):
artigraph = arti.types.Type # Check against isinstance *and* .nullable
system = Optional
# Set very high priority to intercept other matching arti.types.Types/py Union in order to set .nullable
priority = int(1e9)
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return super().matches_artigraph(type_, hints=hints) and type_.nullable
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
# Optional is represented as a Union; strip out NoneType before dispatching
type_ = Union[tuple(subtype for subtype in get_args(type_) if subtype is not NoneType)]
return type_system.to_artigraph(type_, hints=hints).copy(update={"nullable": True})
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return is_optional_hint(type_)
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
return cls.system[
type_system.to_system(type_.copy(update={"nullable": False}), hints=hints)
]
@python_type_system.register_adapter
class PyStruct(TypeAdapter):
artigraph = arti.types.Struct
system = TypedDict
# TODO: Support and inspect TypedDict's '__optional_keys__', '__required_keys__', '__total__'
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
return arti.types.Struct(
name=type_.__name__,
fields={
field_name: type_system.to_artigraph(field_type, hints=hints)
for field_name, field_type in get_type_hints(type_).items()
},
)
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
# NOTE: This check is probably a little shaky, particularly across python versions. Consider
# using the typing_inspect package.
return is_typeddict(type_)
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return TypedDict( # type: ignore[operator]
type_.name,
{
field_name: type_system.to_system(field_type, hints=hints)
for field_name, field_type in type_.fields.items()
},
)
Variables
python_type_system
Classes
PyDatetime
class PyDatetime(
/,
*args,
**kwargs
)
View Source
@python_type_system.register_adapter
class PyDatetime(_ScalarClassTypeAdapter):
artigraph = arti.types.Timestamp
system = datetime.datetime
priority = 1 # Prioritize above Date (isinstance(datetime, date) is True)
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
return cls.artigraph(precision="microsecond")
Ancestors (in MRO)
- arti.types._ScalarClassTypeAdapter
- arti.types.TypeAdapter
Class variables
artigraph
key
priority
system
Static methods
generate
def generate(
*,
artigraph: 'type[Type]',
system: 'Any',
priority: 'int' = 0,
type_system: 'TypeSystem',
name: 'Optional[str]' = None
) -> 'type[TypeAdapter]'
Generate a _ScalarClassTypeAdapter subclass for the scalar system type.
View Source
@classmethod
def generate(
cls,
*,
artigraph: type[Type],
system: Any,
priority: int = 0,
type_system: TypeSystem,
name: Optional[str] = None,
) -> type[TypeAdapter]:
"""Generate a _ScalarClassTypeAdapter subclass for the scalar system type."""
name = name or f"{type_system.key}{artigraph.__name__}"
return type_system.register_adapter(
type(
name,
(cls,),
{"artigraph": artigraph, "system": system, "priority": priority},
)
)
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return isinstance(type_, cls.artigraph)
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return lenient_issubclass(type_, cls.system)
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
return cls.artigraph(precision="microsecond")
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
return cls.system
PyFrozenset
class PyFrozenset(
/,
*args,
**kwargs
)
View Source
@python_type_system.register_adapter
class PyFrozenset(PyValueContainer):
artigraph = arti.types.Set
system = frozenset
Ancestors (in MRO)
- arti.types.python.PyValueContainer
- arti.types.TypeAdapter
Class variables
artigraph
key
priority
system
Static methods
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return isinstance(type_, cls.artigraph)
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return lenient_issubclass(get_origin(type_), cls.system)
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
(element,) = get_args(type_)
assert issubclass(cls.artigraph, _ContainerMixin)
return cls.artigraph(
element=type_system.to_artigraph(element, hints=hints),
)
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[type_system.to_system(type_.element, hints=hints)] # type: ignore[attr-defined]
PyList
class PyList(
/,
*args,
**kwargs
)
View Source
@python_type_system.register_adapter
class PyList(PyValueContainer):
artigraph = arti.types.List
system = list
priority = 1
Ancestors (in MRO)
- arti.types.python.PyValueContainer
- arti.types.TypeAdapter
Class variables
artigraph
key
priority
system
Static methods
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return isinstance(type_, cls.artigraph)
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return lenient_issubclass(get_origin(type_), cls.system)
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
(element,) = get_args(type_)
assert issubclass(cls.artigraph, _ContainerMixin)
return cls.artigraph(
element=type_system.to_artigraph(element, hints=hints),
)
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[type_system.to_system(type_.element, hints=hints)] # type: ignore[attr-defined]
PyLiteral
class PyLiteral(
/,
*args,
**kwargs
)
View Source
@python_type_system.register_adapter
class PyLiteral(TypeAdapter):
artigraph = arti.types.Enum
system = Literal
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
origin, items = get_origin(type_), get_args(type_)
if is_union(origin):
assert not is_optional_hint(type_) # Should be handled by PyOptional
# We only support Enums currently, so all subtypes must be Literal
if non_literals := [sub for sub in items if get_origin(sub) is not Literal]:
raise NotImplementedError(
f"Only Union[Literal[...], ...] (enums) are currently supported, got invalid subtypes: {non_literals}"
)
# Flatten Union[Literal[1], Literal[1,2,3]]
origin, items = Literal, tuple(chain.from_iterable(get_args(sub) for sub in items))
assert origin is Literal
assert isinstance(items, tuple)
if len(items) == 0:
raise NotImplementedError(f"Invalid Literal with no values: {type_}")
py_type, *other_types = (type(v) for v in items)
if not all(t is py_type for t in other_types):
raise ValueError("All Literals must be the same type, got: {(py_type, *other_types)}")
return cls.artigraph(type=type_system.to_artigraph(py_type, hints=hints), items=items)
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
# We don't (currently) support arbitrary Unions, but can map Union[Literal[1], Literal[2]]
# to an Enum. Python's Optional is also represented as a Union, but we handle that with the
# high priority PyOptional.
origin, items = get_origin(type_), get_args(type_)
return origin is Literal or (
is_union(origin) and all(get_origin(sub) is Literal for sub in items)
)
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[tuple(type_.items)]
Ancestors (in MRO)
- arti.types.TypeAdapter
Class variables
artigraph
key
priority
system
Static methods
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return isinstance(type_, cls.artigraph)
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
# We don't (currently) support arbitrary Unions, but can map Union[Literal[1], Literal[2]]
# to an Enum. Python's Optional is also represented as a Union, but we handle that with the
# high priority PyOptional.
origin, items = get_origin(type_), get_args(type_)
return origin is Literal or (
is_union(origin) and all(get_origin(sub) is Literal for sub in items)
)
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
origin, items = get_origin(type_), get_args(type_)
if is_union(origin):
assert not is_optional_hint(type_) # Should be handled by PyOptional
# We only support Enums currently, so all subtypes must be Literal
if non_literals := [sub for sub in items if get_origin(sub) is not Literal]:
raise NotImplementedError(
f"Only Union[Literal[...], ...] (enums) are currently supported, got invalid subtypes: {non_literals}"
)
# Flatten Union[Literal[1], Literal[1,2,3]]
origin, items = Literal, tuple(chain.from_iterable(get_args(sub) for sub in items))
assert origin is Literal
assert isinstance(items, tuple)
if len(items) == 0:
raise NotImplementedError(f"Invalid Literal with no values: {type_}")
py_type, *other_types = (type(v) for v in items)
if not all(t is py_type for t in other_types):
raise ValueError("All Literals must be the same type, got: {(py_type, *other_types)}")
return cls.artigraph(type=type_system.to_artigraph(py_type, hints=hints), items=items)
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[tuple(type_.items)]
PyMap
class PyMap(
/,
*args,
**kwargs
)
View Source
@python_type_system.register_adapter
class PyMap(TypeAdapter):
artigraph = arti.types.Map
system = dict
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
key, value = get_args(type_)
return cls.artigraph(
key=type_system.to_artigraph(key, hints=hints),
value=type_system.to_artigraph(value, hints=hints),
)
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return lenient_issubclass(get_origin(type_), (cls.system, Mapping))
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[ # type: ignore[index]
type_system.to_system(type_.key, hints=hints),
type_system.to_system(type_.value, hints=hints),
]
Ancestors (in MRO)
- arti.types.TypeAdapter
Class variables
artigraph
key
priority
system
Static methods
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return isinstance(type_, cls.artigraph)
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return lenient_issubclass(get_origin(type_), (cls.system, Mapping))
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
key, value = get_args(type_)
return cls.artigraph(
key=type_system.to_artigraph(key, hints=hints),
value=type_system.to_artigraph(value, hints=hints),
)
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[ # type: ignore[index]
type_system.to_system(type_.key, hints=hints),
type_system.to_system(type_.value, hints=hints),
]
PyNone
class PyNone(
/,
*args,
**kwargs
)
View Source
@python_type_system.register_adapter
class PyNone(_ScalarClassTypeAdapter):
# Python represents None types in type hints with the `None` value (not `NoneType`).
artigraph = arti.types.Null
system = None
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return type_ is None
Ancestors (in MRO)
- arti.types._ScalarClassTypeAdapter
- arti.types.TypeAdapter
Class variables
artigraph
key
priority
system
Static methods
generate
def generate(
*,
artigraph: 'type[Type]',
system: 'Any',
priority: 'int' = 0,
type_system: 'TypeSystem',
name: 'Optional[str]' = None
) -> 'type[TypeAdapter]'
Generate a _ScalarClassTypeAdapter subclass for the scalar system type.
View Source
@classmethod
def generate(
cls,
*,
artigraph: type[Type],
system: Any,
priority: int = 0,
type_system: TypeSystem,
name: Optional[str] = None,
) -> type[TypeAdapter]:
"""Generate a _ScalarClassTypeAdapter subclass for the scalar system type."""
name = name or f"{type_system.key}{artigraph.__name__}"
return type_system.register_adapter(
type(
name,
(cls,),
{"artigraph": artigraph, "system": system, "priority": priority},
)
)
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return isinstance(type_, cls.artigraph)
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return type_ is None
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
return cls.artigraph()
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
return cls.system
PyOptional
class PyOptional(
/,
*args,
**kwargs
)
View Source
@python_type_system.register_adapter
class PyOptional(TypeAdapter):
artigraph = arti.types.Type # Check against isinstance *and* .nullable
system = Optional
# Set very high priority to intercept other matching arti.types.Types/py Union in order to set .nullable
priority = int(1e9)
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return super().matches_artigraph(type_, hints=hints) and type_.nullable
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
# Optional is represented as a Union; strip out NoneType before dispatching
type_ = Union[tuple(subtype for subtype in get_args(type_) if subtype is not NoneType)]
return type_system.to_artigraph(type_, hints=hints).copy(update={"nullable": True})
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return is_optional_hint(type_)
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
return cls.system[
type_system.to_system(type_.copy(update={"nullable": False}), hints=hints)
]
Ancestors (in MRO)
- arti.types.TypeAdapter
Class variables
artigraph
key
priority
system
Static methods
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return super().matches_artigraph(type_, hints=hints) and type_.nullable
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return is_optional_hint(type_)
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
# Optional is represented as a Union; strip out NoneType before dispatching
type_ = Union[tuple(subtype for subtype in get_args(type_) if subtype is not NoneType)]
return type_system.to_artigraph(type_, hints=hints).copy(update={"nullable": True})
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
return cls.system[
type_system.to_system(type_.copy(update={"nullable": False}), hints=hints)
]
PySet
class PySet(
/,
*args,
**kwargs
)
View Source
@python_type_system.register_adapter
class PySet(PyValueContainer):
artigraph = arti.types.Set
system = set
priority = 1 # Set above frozenset
Ancestors (in MRO)
- arti.types.python.PyValueContainer
- arti.types.TypeAdapter
Class variables
artigraph
key
priority
system
Static methods
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return isinstance(type_, cls.artigraph)
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return lenient_issubclass(get_origin(type_), cls.system)
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
(element,) = get_args(type_)
assert issubclass(cls.artigraph, _ContainerMixin)
return cls.artigraph(
element=type_system.to_artigraph(element, hints=hints),
)
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[type_system.to_system(type_.element, hints=hints)] # type: ignore[attr-defined]
PyStruct
class PyStruct(
/,
*args,
**kwargs
)
View Source
@python_type_system.register_adapter
class PyStruct(TypeAdapter):
artigraph = arti.types.Struct
system = TypedDict
# TODO: Support and inspect TypedDict's '__optional_keys__', '__required_keys__', '__total__'
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
return arti.types.Struct(
name=type_.__name__,
fields={
field_name: type_system.to_artigraph(field_type, hints=hints)
for field_name, field_type in get_type_hints(type_).items()
},
)
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
# NOTE: This check is probably a little shaky, particularly across python versions. Consider
# using the typing_inspect package.
return is_typeddict(type_)
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return TypedDict( # type: ignore[operator]
type_.name,
{
field_name: type_system.to_system(field_type, hints=hints)
for field_name, field_type in type_.fields.items()
},
)
Ancestors (in MRO)
- arti.types.TypeAdapter
Class variables
artigraph
key
priority
Static methods
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return isinstance(type_, cls.artigraph)
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
# NOTE: This check is probably a little shaky, particularly across python versions. Consider
# using the typing_inspect package.
return is_typeddict(type_)
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
return arti.types.Struct(
name=type_.__name__,
fields={
field_name: type_system.to_artigraph(field_type, hints=hints)
for field_name, field_type in get_type_hints(type_).items()
},
)
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return TypedDict( # type: ignore[operator]
type_.name,
{
field_name: type_system.to_system(field_type, hints=hints)
for field_name, field_type in type_.fields.items()
},
)
Methods
system
def system(
typename,
fields=None,
/,
*,
total=True,
**kwargs
)
A simple typed namespace. At runtime it is equivalent to a plain dict.
TypedDict creates a dictionary type that expects all of its instances to have a certain set of keys, where each key is associated with a value of a consistent type. This expectation is not checked at runtime but is only enforced by type checkers. Usage::
class Point2D(TypedDict):
x: int
y: int
label: str
a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK
b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check
assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
The type info can be accessed via the Point2D.annotations dict, and the Point2D.required_keys and Point2D.optional_keys frozensets. TypedDict supports an additional equivalent form::
Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
By default, all keys must be present in a TypedDict. It is possible to override this by specifying totality. Usage::
class point2D(TypedDict, total=False):
x: int
y: int
This means that a point2D TypedDict can have any of the keys omitted.A type checker is only expected to support a literal False or True as the value of the total argument. True is the default, and makes all items defined in the class body be required.
The class syntax is only supported in Python 3.6+, while the other syntax form works for Python 2.7 and 3.2+
View Source
def TypedDict(typename, fields=None, /, *, total=True, **kwargs):
"""A simple typed namespace. At runtime it is equivalent to a plain dict.
TypedDict creates a dictionary type that expects all of its
instances to have a certain set of keys, where each key is
associated with a value of a consistent type. This expectation
is not checked at runtime but is only enforced by type checkers.
Usage::
class Point2D(TypedDict):
x: int
y: int
label: str
a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK
b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check
assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
The type info can be accessed via the Point2D.__annotations__ dict, and
the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
TypedDict supports an additional equivalent form::
Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
By default, all keys must be present in a TypedDict. It is possible
to override this by specifying totality.
Usage::
class point2D(TypedDict, total=False):
x: int
y: int
This means that a point2D TypedDict can have any of the keys omitted.A type
checker is only expected to support a literal False or True as the value of
the total argument. True is the default, and makes all items defined in the
class body be required.
The class syntax is only supported in Python 3.6+, while the other
syntax form works for Python 2.7 and 3.2+
"""
if fields is None:
fields = kwargs
elif kwargs:
raise TypeError("TypedDict takes either a dict or keyword arguments,"
" but not both")
if kwargs:
warnings.warn(
"The kwargs-based syntax for TypedDict definitions is deprecated "
"in Python 3.11, will be removed in Python 3.13, and may not be "
"understood by third-party type checkers.",
DeprecationWarning,
stacklevel=2,
)
ns = {'__annotations__': dict(fields)}
module = _caller()
if module is not None:
# Setting correct module is necessary to make typed dict classes pickleable.
ns['__module__'] = module
return _TypedDictMeta(typename, (), ns, total=total)
PyTuple
class PyTuple(
/,
*args,
**kwargs
)
View Source
@python_type_system.register_adapter
class PyTuple(PyValueContainer):
artigraph = arti.types.List
system = tuple
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
origin, args = get_origin(type_), get_args(type_)
assert origin is not None
assert len(args) == 2
assert args[1] is ...
return super().to_artigraph(origin[args[0]], hints=hints, type_system=type_system)
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
if super().matches_system(type_, hints=hints):
args = get_args(type_)
if len(args) == 2 and args[1] is ...:
return True
return False
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
ret = super().to_system(type_, hints=hints, type_system=type_system)
origin, args = get_origin(ret), get_args(ret)
assert origin is not None
assert len(args) == 1
return origin[args[0], ...]
Ancestors (in MRO)
- arti.types.python.PyValueContainer
- arti.types.TypeAdapter
Class variables
artigraph
key
priority
system
Static methods
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return isinstance(type_, cls.artigraph)
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
if super().matches_system(type_, hints=hints):
args = get_args(type_)
if len(args) == 2 and args[1] is ...:
return True
return False
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
origin, args = get_origin(type_), get_args(type_)
assert origin is not None
assert len(args) == 2
assert args[1] is ...
return super().to_artigraph(origin[args[0]], hints=hints, type_system=type_system)
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
ret = super().to_system(type_, hints=hints, type_system=type_system)
origin, args = get_origin(ret), get_args(ret)
assert origin is not None
assert len(args) == 1
return origin[args[0], ...]
PyValueContainer
class PyValueContainer(
/,
*args,
**kwargs
)
View Source
class PyValueContainer(TypeAdapter):
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
(element,) = get_args(type_)
assert issubclass(cls.artigraph, _ContainerMixin)
return cls.artigraph(
element=type_system.to_artigraph(element, hints=hints),
)
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return lenient_issubclass(get_origin(type_), cls.system)
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[type_system.to_system(type_.element, hints=hints)] # type: ignore[attr-defined]
Ancestors (in MRO)
- arti.types.TypeAdapter
Descendants
- arti.types.python.PyList
- arti.types.python.PyTuple
- arti.types.python.PyFrozenset
- arti.types.python.PySet
Class variables
key
priority
Static methods
matches_artigraph
def matches_artigraph(
type_: 'Type',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_artigraph(cls, type_: Type, *, hints: dict[str, Any]) -> bool:
return isinstance(type_, cls.artigraph)
matches_system
def matches_system(
type_: 'Any',
*,
hints: 'dict[str, Any]'
) -> 'bool'
View Source
@classmethod
def matches_system(cls, type_: Any, *, hints: dict[str, Any]) -> bool:
return lenient_issubclass(get_origin(type_), cls.system)
to_artigraph
def to_artigraph(
type_: 'Any',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Type'
View Source
@classmethod
def to_artigraph(cls, type_: Any, *, hints: dict[str, Any], type_system: TypeSystem) -> Type:
(element,) = get_args(type_)
assert issubclass(cls.artigraph, _ContainerMixin)
return cls.artigraph(
element=type_system.to_artigraph(element, hints=hints),
)
to_system
def to_system(
type_: 'Type',
*,
hints: 'dict[str, Any]',
type_system: 'TypeSystem'
) -> 'Any'
View Source
@classmethod
def to_system(cls, type_: Type, *, hints: dict[str, Any], type_system: TypeSystem) -> Any:
assert isinstance(type_, cls.artigraph)
return cls.system[type_system.to_system(type_.element, hints=hints)] # type: ignore[attr-defined]