Skip to content

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]