From f447504c2882f2f05c8e34d874d67db1548eaf7f Mon Sep 17 00:00:00 2001 From: mdartiailh Date: Thu, 17 Sep 2026 14:12:35 +0200 Subject: [PATCH 1/7] rework type annotation validation to be able to run them cross type checkers --- tests/type_checking/annotations_checks.py | 13 + tests/type_checking/coerced_checks.py | 46 ++ tests/type_checking/delegator_checks.py | 14 + tests/type_checking/dict_checks.py | 142 +++++ tests/type_checking/enum_checks.py | 21 + tests/type_checking/event_checks.py | 15 + tests/type_checking/generic_alias_checks.py | 33 ++ tests/type_checking/list_checks.py | 102 ++++ tests/type_checking/property_checks.py | 57 ++ tests/type_checking/scalar_checks.py | 100 ++++ tests/type_checking/set_checks.py | 75 +++ tests/type_checking/signal_checks.py | 15 + tests/type_checking/subclass_checks.py | 51 ++ tests/type_checking/test_annotations.yml | 24 - tests/type_checking/test_coerced.yml | 82 --- tests/type_checking/test_delegator.yml | 18 - tests/type_checking/test_dict.yml | 380 ------------ tests/type_checking/test_enum.yml | 43 -- tests/type_checking/test_event.yml | 36 -- tests/type_checking/test_generic_aliases.yml | 49 -- tests/type_checking/test_list.yml | 201 ------- tests/type_checking/test_property.yml | 74 --- tests/type_checking/test_scalars.yml | 229 -------- tests/type_checking/test_set.yml | 103 ---- tests/type_checking/test_signal.yml | 17 - tests/type_checking/test_subclass.yml | 85 --- tests/type_checking/test_tuple.yml | 134 ----- tests/type_checking/test_typed_instance.yml | 579 ------------------- tests/type_checking/tuple_checks.py | 88 +++ tests/type_checking/typed_instance_checks.py | 77 +++ 30 files changed, 849 insertions(+), 2054 deletions(-) create mode 100644 tests/type_checking/annotations_checks.py create mode 100644 tests/type_checking/coerced_checks.py create mode 100644 tests/type_checking/delegator_checks.py create mode 100644 tests/type_checking/dict_checks.py create mode 100644 tests/type_checking/enum_checks.py create mode 100644 tests/type_checking/event_checks.py create mode 100644 tests/type_checking/generic_alias_checks.py create mode 100644 tests/type_checking/list_checks.py create mode 100644 tests/type_checking/property_checks.py create mode 100644 tests/type_checking/scalar_checks.py create mode 100644 tests/type_checking/set_checks.py create mode 100644 tests/type_checking/signal_checks.py create mode 100644 tests/type_checking/subclass_checks.py delete mode 100644 tests/type_checking/test_annotations.yml delete mode 100644 tests/type_checking/test_coerced.yml delete mode 100644 tests/type_checking/test_delegator.yml delete mode 100644 tests/type_checking/test_dict.yml delete mode 100644 tests/type_checking/test_enum.yml delete mode 100644 tests/type_checking/test_event.yml delete mode 100644 tests/type_checking/test_generic_aliases.yml delete mode 100644 tests/type_checking/test_list.yml delete mode 100644 tests/type_checking/test_property.yml delete mode 100644 tests/type_checking/test_scalars.yml delete mode 100644 tests/type_checking/test_set.yml delete mode 100644 tests/type_checking/test_signal.yml delete mode 100644 tests/type_checking/test_subclass.yml delete mode 100644 tests/type_checking/test_tuple.yml delete mode 100644 tests/type_checking/test_typed_instance.yml create mode 100644 tests/type_checking/tuple_checks.py create mode 100644 tests/type_checking/typed_instance_checks.py diff --git a/tests/type_checking/annotations_checks.py b/tests/type_checking/annotations_checks.py new file mode 100644 index 00000000..acc83502 --- /dev/null +++ b/tests/type_checking/annotations_checks.py @@ -0,0 +1,13 @@ +from typing_extensions import assert_type + +from atom.api import Atom, List + + +# Annotated member declarations should expose the Atom descriptor type on the class +# and the corresponding concrete Python container on the instance. +class A(Atom): + m: List[int] = List() + + +assert_type(A.m, List[int]) +assert_type(A().m, list[int]) diff --git a/tests/type_checking/coerced_checks.py b/tests/type_checking/coerced_checks.py new file mode 100644 index 00000000..f79ce2d4 --- /dev/null +++ b/tests/type_checking/coerced_checks.py @@ -0,0 +1,46 @@ +import _io + +from typing_extensions import assert_type + +from atom.api import Atom, Coerced + + +# Coerced members validate the same class/instance split as the other descriptors: the +# class exposes the Coerced[...] descriptor, while the instance value remains the concrete +# runtime type after coercion. Each class below exercises a distinct coercion branch. +def g() -> _io.StringIO: + return _io.StringIO() + + +class ACoercedScalar(Atom): + # A scalar coercion target preserves the exact runtime type and keeps the descriptor + # parameterized with that scalar. + m = Coerced(int) + + +class ACoercedTuple(Atom): + # A tuple of valid runtime types widens the coercion descriptor to the corresponding + # union while the instance still stores only one concrete type. + m = Coerced((int, float)) + + +class ACoercedStringIO(Atom): + # StringIO inputs with a default factory-like initialization should still resolve to the + # concrete StringIO type on the instance. + m = Coerced(_io.StringIO, kwargs=dict(initial_value="1")) + + +class ACoercedFactory(Atom): + # Factory-based coercion should behave the same as the other direct object coercion + # scenarios and remain concrete on the instance even though the descriptor is generic. + m = Coerced(_io.StringIO, factory=g) + + +assert_type(ACoercedScalar.m, Coerced[int, int]) +assert_type(ACoercedScalar().m, int) +assert_type(ACoercedTuple.m, Coerced[int | float, int | float]) +assert_type(ACoercedTuple().m, int | float) +assert_type(ACoercedStringIO.m, Coerced[_io.StringIO, _io.StringIO]) +assert_type(ACoercedStringIO().m, _io.StringIO) +assert_type(ACoercedFactory.m, Coerced[_io.StringIO, _io.StringIO]) +assert_type(ACoercedFactory().m, _io.StringIO) diff --git a/tests/type_checking/delegator_checks.py b/tests/type_checking/delegator_checks.py new file mode 100644 index 00000000..365f043b --- /dev/null +++ b/tests/type_checking/delegator_checks.py @@ -0,0 +1,14 @@ +from typing_extensions import assert_type + +from atom.api import Atom, Delegator, Int + + +# Delegator retains the descriptor wrapper on the class but exposes the delegated +# value on the instance. +class A(Atom): + i = Int(strict=False) + m = Delegator(i) + + +assert_type(A.m, Delegator[int, int | float]) +assert_type(A().m, int) diff --git a/tests/type_checking/dict_checks.py b/tests/type_checking/dict_checks.py new file mode 100644 index 00000000..d8ab5849 --- /dev/null +++ b/tests/type_checking/dict_checks.py @@ -0,0 +1,142 @@ +from typing import Any + +from typing_extensions import assert_type + +from atom.api import Atom, Dict, Int + + +# Dict members are checked along both axes: the class exposes Dict[key_type, value_type] +# and the instance exposes dict[key_type, value_type]. The classes below cover the main +# key/value inference combinations without depending on a generated fixture matrix. +class AUnspecified(Atom): + # No key or value constraints means the descriptor stays completely generic. + m = Dict() + + +class ADefaultDict(Atom): + # The default value is only a factory for instance state; it does not widen the type + # declaration itself. + m = Dict(default={"a": 1}) + + +class AKeyScalar(Atom): + # Key-only typing narrows only the key side and leaves the value side free. + m = Dict(int) + + +class AKeyTuple(Atom): + # A tuple of key types creates a key union and should propagate to the descriptor. + m = Dict((int, float, str)) + + +class AValueScalar(Atom): + # Value-only typing narrows only the value side while the key remains unconstrained. + m = Dict(None, int) + + +class AValueTuple(Atom): + # Tuple value types widen to the same union semantics used elsewhere in Atom. + m = Dict(None, (int, float, str)) + + +class AKeywordValue(Atom): + # The keyword form is equivalent to positional value specification; it still narrows + # only the value side. + m = Dict(value=(int, float, str)) + + +class AKeyValueScalar(Atom): + # Fully specified key/value typing should produce the precise Dict[int, int] result. + m = Dict(int, int) + + +class AKeyValueTuple(Atom): + # Tuple-based keys and values produce union inference on both sides of the dictionary. + m = Dict((int, str), (int, float)) + + +class AKeyValueTripleTuple(Atom): + # Three-way key and value unions keep the same shape but widen the key/value unions to + # their combined set of valid types. + m = Dict((int, str, bytes), (int, float, str)) + + +class AMemberKey(Atom): + # Member-based key specification should resolve to the member's concrete type and keep + # the value side typed independently. + m = Dict(Int(), (int, float)) + + +assert_type(AUnspecified.m, Dict[Any, Any]) +assert_type(AUnspecified().m, dict[Any, Any]) +assert_type(ADefaultDict.m, Dict[Any, Any]) +assert_type(ADefaultDict().m, dict[Any, Any]) +assert_type(AKeyScalar.m, Dict[int, Any]) +assert_type(AKeyScalar().m, dict[int, Any]) +assert_type(AKeyTuple.m, Dict[int | float | str, Any]) +assert_type(AKeyTuple().m, dict[int | float | str, Any]) +assert_type(AValueScalar.m, Dict[Any, int]) +assert_type(AValueScalar().m, dict[Any, int]) +assert_type(AValueTuple.m, Dict[Any, int | float | str]) +assert_type(AValueTuple().m, dict[Any, int | float | str]) +assert_type(AKeywordValue.m, Dict[Any, int | float | str]) +assert_type(AKeywordValue().m, dict[Any, int | float | str]) +assert_type(AKeyValueScalar.m, Dict[int, int]) +assert_type(AKeyValueScalar().m, dict[int, int]) +assert_type(AKeyValueTuple.m, Dict[int | str, int | float]) +assert_type(AKeyValueTuple().m, dict[int | str, int | float]) +assert_type(AKeyValueTripleTuple.m, Dict[int | str | bytes, int | float | str]) +assert_type(AKeyValueTripleTuple().m, dict[int | str | bytes, int | float | str]) +assert_type(AMemberKey.m, Dict[int, int | float]) +assert_type(AMemberKey().m, dict[int, int | float]) + +# The additional classes below exercise the single-element tuple variants and the member- +# based key/value shorthand that are easy to miss in a manual suite. They are redundant in +# meaning but important as coverage for the constructor overloads. +class AKeyOneTuple(Atom): + # A one-element tuple key is equivalent to the scalar key type case and should collapse + # to a single key type. + m = Dict((int,), int) + + +class AKeyTwoTuple(Atom): + # Two-element tuple keys widen the dictionary key union to the union of both member + # types while the value side remains fixed. + m = Dict((int, str), int) + + +class AKeyThreeTuple(Atom): + # Three-element tuple keys widen to the three-way union while preserving the value + # typing contract. + m = Dict((int, str, bytes), int) + + +class AValueFromIntMember(Atom): + # Value-side Int() members should infer as int exactly like the scalar int case. + m = Dict(None, Int()) + + +class AKeywordValueFromIntMember(Atom): + # The keyword value form with an Int() member is covered separately to ensure the + # overload resolution remains the same as the positional equivalent. + m = Dict(value=Int()) + + +class AMemberAsKeyAndValue(Atom): + # Using a member on both sides verifies that the key and value validation paths each + # resolve to the member's concrete type without cross-contaminating the other side. + m = Dict(Int(), Int()) + + +assert_type(AKeyOneTuple.m, Dict[int, int]) +assert_type(AKeyOneTuple().m, dict[int, int]) +assert_type(AKeyTwoTuple.m, Dict[int | str, int]) +assert_type(AKeyTwoTuple().m, dict[int | str, int]) +assert_type(AKeyThreeTuple.m, Dict[int | str | bytes, int]) +assert_type(AKeyThreeTuple().m, dict[int | str | bytes, int]) +assert_type(AValueFromIntMember.m, Dict[Any, int]) +assert_type(AValueFromIntMember().m, dict[Any, int]) +assert_type(AKeywordValueFromIntMember.m, Dict[Any, int]) +assert_type(AKeywordValueFromIntMember().m, dict[Any, int]) +assert_type(AMemberAsKeyAndValue.m, Dict[int, int]) +assert_type(AMemberAsKeyAndValue().m, dict[int, int]) diff --git a/tests/type_checking/enum_checks.py b/tests/type_checking/enum_checks.py new file mode 100644 index 00000000..2a89cf4a --- /dev/null +++ b/tests/type_checking/enum_checks.py @@ -0,0 +1,21 @@ +from typing_extensions import assert_type + +from atom.api import Atom, Enum + + +# Enum descriptors hold the member kind on the class while the instance sees the +# specific enum value; helper methods may widen the enum value union. +e = Enum(1, 2) + + +class A(Atom): + e1 = e + e2 = e("1") + e3 = e.added("1") + e4 = e.removed(2) + + +assert_type(A.e1, Enum[int]) +assert_type(A.e2, Enum[int | str]) +assert_type(A.e3, Enum[int | str]) +assert_type(A.e4, Enum[int]) diff --git a/tests/type_checking/event_checks.py b/tests/type_checking/event_checks.py new file mode 100644 index 00000000..5ae728e4 --- /dev/null +++ b/tests/type_checking/event_checks.py @@ -0,0 +1,15 @@ +from typing_extensions import assert_type + +from atom.api import Atom, Event +from atom.catom import EventBinder + + +# Event members keep the Event descriptor on the class but bind an EventBinder on the +# instance; assignment is permitted because the binder is the runtime object. +class A(Atom): + m = Event(int) + + +assert_type(A.m, Event[int]) +assert_type(A().m, EventBinder) +A().m = 1 diff --git a/tests/type_checking/generic_alias_checks.py b/tests/type_checking/generic_alias_checks.py new file mode 100644 index 00000000..31ae40a8 --- /dev/null +++ b/tests/type_checking/generic_alias_checks.py @@ -0,0 +1,33 @@ +from typing_extensions import assert_type + +from atom.api import Atom, ForwardInstance, ForwardTyped, Instance, Member, Typed + + +# Generic aliases are accepted as raw type arguments and should infer the same +# concrete container type as the corresponding Atom member descriptor. +class A(Atom): + m = Typed(list[int], optional=False) + + +assert_type(A.m, Typed[list[int]]) +assert_type(A().m, list[int]) + + +class B(Atom): + m = Instance((list[int], int), optional=False) + + +assert_type(B.m, Instance[list[int] | int]) +assert_type(B().m, list[int] | int) + + +class C(Atom): + m = ForwardTyped(lambda: list[int], optional=False) + + +assert_type(C.m, ForwardTyped[list[int]]) +assert_type(C().m, list[int]) + + +m: Member[int, str] = Member() +assert_type(m, Member[int, str]) diff --git a/tests/type_checking/list_checks.py b/tests/type_checking/list_checks.py new file mode 100644 index 00000000..39bbba2b --- /dev/null +++ b/tests/type_checking/list_checks.py @@ -0,0 +1,102 @@ +from typing import Any + +from typing_extensions import assert_type + +from atom.api import Atom, ContainerList, Int, List + + +class Z(Atom): + pass + + +# List-like members are validated by checking the public descriptor type on the class +# and the concrete runtime container type on the instance. The classes below isolate the +# major inference branches directly so each case remains easy to read without relying on +# a generated fixture matrix. +class ANoDefault(Atom): + # An untyped list member accepts any element type. The class exposes List[Any], and + # the instance exposes the concrete list[Any] value type. + m = List() + + +class ASingleType(Atom): + # A single element type narrows the descriptor to List[int] while the instance still + # stores a plain Python list[int]. + m = List(int) + + +class AUnionOfScalarTypes(Atom): + # A tuple of accepted element types widens the list element union in the same way as + # the equivalent scalar validation path without changing the class/instance split. + m = List((int, float, str)) + + +class AMemberBasedList(Atom): + # Member-based element validation is equivalent to validating against the member's + # concrete runtime type; Int() should therefore tighten the list to List[int]. + m = List(Int()) + + +class ADefaultAny(Atom): + # A default list remains valid even when no element type is declared. The descriptor + # stays generic, but the runtime value is still a real Python list. + m = List(default=[1]) + + +class ADefaultTyped(Atom): + # A default list plus an explicit element type should keep the declared element type + # and preserve the instance value as list[int]. + m = List(int, default=[3]) + + +class ADefaultTupleUnion(Atom): + # Mixed default values are accepted so long as they are compatible with one of the + # tuple members. The descriptor must still widen to the union type required by the + # validation model. + m = List((int, str), default=[1, str(1)]) + + +class ADefaultMixedWithAtom(Atom): + # Atom subclasses participate in the same union resolution as builtins. The default + # value may include instances of Z(), but the descriptor remains the union of all valid + # permitted element types. + m = List((int, str, Z), default=[1, str(1), Z()]) + + +class AContainerDefault(Atom): + # ContainerList uses the same inference semantics as List but exposes its own + # descriptor class on the attribute itself. + m = ContainerList() + + +class AContainerTyped(Atom): + # A typed ContainerList keeps the class-side descriptor as ContainerList[...] while the + # instance value remains a concrete list[...] object. + m = ContainerList((int, float)) + + +assert_type(ANoDefault.m, List[Any]) +assert_type(ANoDefault().m, list[Any]) +assert_type(ASingleType.m, List[int]) +assert_type(ASingleType().m, list[int]) +assert_type(AUnionOfScalarTypes.m, List[int | float | str]) +assert_type(AUnionOfScalarTypes().m, list[int | float | str]) +assert_type(AMemberBasedList.m, List[int]) +assert_type(AMemberBasedList().m, list[int]) +assert_type(ADefaultAny.m, List[Any]) +assert_type(ADefaultAny().m, list[Any]) +assert_type(ADefaultTyped.m, List[int]) +assert_type(ADefaultTyped().m, list[int]) +assert_type(ADefaultTupleUnion.m, List[int | str]) +assert_type(ADefaultTupleUnion().m, list[int | str]) +assert_type(ADefaultMixedWithAtom.m, List[int | str | Z]) +assert_type(ADefaultMixedWithAtom().m, list[int | str | Z]) +assert_type(List(Int()), List[int]) + +# ContainerList is a distinct list-like descriptor, but the semantics match List exactly: +# the descriptor stays on the class and the actual stored value is a plain Python list. +assert_type(AContainerDefault.m, ContainerList[Any]) +assert_type(AContainerDefault().m, list[Any]) +assert_type(AContainerTyped.m, ContainerList[int | float]) +assert_type(AContainerTyped().m, list[int | float]) +assert_type(ContainerList(Int(), default=[2]), ContainerList[int]) diff --git a/tests/type_checking/property_checks.py b/tests/type_checking/property_checks.py new file mode 100644 index 00000000..c9b182d6 --- /dev/null +++ b/tests/type_checking/property_checks.py @@ -0,0 +1,57 @@ +from typing import Never, Union + +from typing_extensions import assert_type + +from atom.api import Atom, Property, cached_property + + +# Empty properties expose Never on the instance side; getter-only properties and +# property setters both carry the expected descriptor and value types. +class AEmpty(Atom): + m = Property() + + +assert_type(AEmpty.m, Property[Never, Never]) +assert_type(AEmpty().m, Never) + + +def getter(a) -> int: + return a.b + + +class AGetterOnly(Atom): + m = Property(getter) + + +assert_type(AGetterOnly.m, Property[int, Never]) +assert_type(AGetterOnly().m, int) + + +def state_getter(a) -> int: + return a._b + + +def state_setter(a, v: Union[int, str]) -> None: + a._b = int(v) + + +def deleter(a): + pass + + +class AProperty(Atom): + m = Property(state_getter, state_setter, deleter) + + +assert_type(AProperty.m, Property[int, int | str]) +assert_type(AProperty().m, int) + + +class ACached(Atom): + @cached_property + def m(self) -> int: + return 1 + + +assert_type(ACached.m, Property[int, Never]) +assert_type(ACached().m, int) diff --git a/tests/type_checking/scalar_checks.py b/tests/type_checking/scalar_checks.py new file mode 100644 index 00000000..1ab00cf5 --- /dev/null +++ b/tests/type_checking/scalar_checks.py @@ -0,0 +1,100 @@ +from typing import Any + +from typing_extensions import assert_type + +from atom.api import Atom, Bool, Bytes, Constant, Float, FloatRange, Int, Range, ReadOnly, Str, Value + + +def g() -> int: + return 1 + + +# Scalar member checks focus on the descriptor/value split: the class receives the scalar +# member descriptor while the instance exposes the concrete Python value. Each class below +# covers a distinct scalar validation path. +class AValue(Atom): + # A plain Value member accepts any runtime value and keeps the descriptor generic. + m = Value() + + +class AReadOnlyDefault(Atom): + # ReadOnly accepts a union of allowed types and should keep that union on the class + # while instantiating a concrete scalar value on the instance. + m = ReadOnly((int, str), default=0) + + +class AReadOnlyFactory(Atom): + # A factory default is semantically equivalent to the same allowed union, but derived + # through the factory path rather than a literal default. + m = ReadOnly((int, str), factory=g) + + +class AConstantKindUnion(Atom): + # Constant kinds behave like a fixed allowed-union descriptor, preserving the allowed + # scalar types on the class. + m = Constant(kind=(int, str)) + + +class AIntLoose(Atom): + # Int(strict=False) accepts both int and float values, so the descriptor widens to the + # union while the runtime instance value remains the concrete int when it is used. + m = Int(strict=False) + + +class AFloatRangeDefault(Atom): + # FloatRange keeps the descriptor as a floating-range value while the instance value is + # the concrete float object. + m = FloatRange() + + +class ARangeDefault(Atom): + # Range is a constrained integer-like scalar and should narrow the descriptor to int. + m = Range() + + +class AFloatStrict(Atom): + # The strict float descriptor keeps the exact float type on the class and float on the + # instance. + m = Float(strict=True) + + +class ABytesLoose(Atom): + # Bytes(strict=False) accepts both bytes and str values while the value at runtime is + # the concrete bytes object. + m = Bytes(strict=False) + + +class AStrLoose(Atom): + # Str(strict=False) accepts both str and bytes at validation time while the runtime + # instance value remains the concrete str object. + m = Str(strict=False) + + +class ABool(Atom): + # Bool members are the canonical boolean scalar case and should remain bool on both the + # descriptor and the instance value. + m = Bool() + + +assert_type(AValue.m, Value[Any]) +assert_type(AValue().m, Any) +assert_type(AReadOnlyDefault.m, ReadOnly[int | str]) +assert_type(AReadOnlyDefault().m, int | str) +assert_type(AReadOnlyFactory.m, ReadOnly[int | str]) +assert_type(AReadOnlyFactory().m, int | str) +assert_type(AConstantKindUnion.m, Constant[int | str]) +assert_type(AConstantKindUnion().m, int | str) +assert_type(AIntLoose.m, Int[int | float]) +assert_type(AIntLoose().m, int) +assert_type(AFloatRangeDefault.m, FloatRange[int | float]) +assert_type(AFloatRangeDefault().m, float) +assert_type(ARangeDefault.m, Range[int]) +assert_type(ARangeDefault().m, int) +assert_type(AFloatStrict.m, Float[float]) +assert_type(AFloatStrict().m, float) +assert_type(ABytesLoose.m, Bytes[bytes | str]) +assert_type(ABytesLoose().m, bytes) +assert_type(AStrLoose.m, Str[str | bytes]) +assert_type(AStrLoose().m, str) +assert_type(ABool.m, Bool[bool]) +assert_type(ABool().m, bool) diff --git a/tests/type_checking/set_checks.py b/tests/type_checking/set_checks.py new file mode 100644 index 00000000..0d306d6a --- /dev/null +++ b/tests/type_checking/set_checks.py @@ -0,0 +1,75 @@ +from typing import Any + +from typing_extensions import assert_type + +from atom.api import Atom, Int, Set + + +class Z(Atom): + pass + + +# Set members follow the same rule as the other container descriptors: the class exposes a +# Set[...] descriptor, while the instance exposes a concrete builtins.set[...] value. Each +# class below isolates one of the main element-inference variants. +class ANoDefault(Atom): + # No declared element type means the set accepts any element and keeps the descriptor + # generic. + m = Set() + + +class AScalarElement(Atom): + # A scalar element type narrows the descriptor to Set[int] but preserves a runtime set[int]. + m = Set(int) + + +class AUnionOfScalarElements(Atom): + # A tuple of element types widens the descriptor to the union of all accepted types. + m = Set((int, float, str)) + + +class AMemberBasedSet(Atom): + # Member-based element validation resolves to the member's concrete runtime type. + m = Set(Int()) + + +class ADefaultAny(Atom): + # A default set does not change the generic descriptor type, only the instance value. + m = Set(default={1}) + + +class ADefaultTyped(Atom): + # An explicit element type plus a default still narrows the descriptor to the declared + # set type. + m = Set(int, default={3}) + + +class ADefaultTupleUnion(Atom): + # Tuple-based union defaults should widen the descriptor to the union of allowed types + # while remaining valid for each concrete default item. + m = Set((int, str), default={1, str(1)}) + + +class ADefaultMixedWithAtom(Atom): + # Atom subclasses participate in the same union semantics as builtins and are accepted + # when they appear in a valid default value. + m = Set((int, str, Z), default={1, str(1), Z()}) + + +assert_type(ANoDefault.m, Set[Any]) +assert_type(ANoDefault().m, set[Any]) +assert_type(AScalarElement.m, Set[int]) +assert_type(AScalarElement().m, set[int]) +assert_type(AUnionOfScalarElements.m, Set[int | float | str]) +assert_type(AUnionOfScalarElements().m, set[int | float | str]) +assert_type(AMemberBasedSet.m, Set[int]) +assert_type(AMemberBasedSet().m, set[int]) +assert_type(ADefaultAny.m, Set[Any]) +assert_type(ADefaultAny().m, set[Any]) +assert_type(ADefaultTyped.m, Set[int]) +assert_type(ADefaultTyped().m, set[int]) +assert_type(ADefaultTupleUnion.m, Set[int | str]) +assert_type(ADefaultTupleUnion().m, set[int | str]) +assert_type(ADefaultMixedWithAtom.m, Set[int | str | Z]) +assert_type(ADefaultMixedWithAtom().m, set[int | str | Z]) +assert_type(Set(Int()), Set[int]) diff --git a/tests/type_checking/signal_checks.py b/tests/type_checking/signal_checks.py new file mode 100644 index 00000000..bb59d3fe --- /dev/null +++ b/tests/type_checking/signal_checks.py @@ -0,0 +1,15 @@ +from typing_extensions import assert_type + +from atom.api import Atom, Signal +from atom.catom import SignalConnector + + +# Signal members are descriptor objects on the class and a connector object on the +# instance. +class A(Atom): + m = Signal() + + +assert_type(A.m, Signal) +assert_type(A().m, SignalConnector) +A().m() diff --git a/tests/type_checking/subclass_checks.py b/tests/type_checking/subclass_checks.py new file mode 100644 index 00000000..3398d5ce --- /dev/null +++ b/tests/type_checking/subclass_checks.py @@ -0,0 +1,51 @@ +from typing import Type + +from typing_extensions import assert_type + +from atom.api import Atom, ForwardSubclass, Subclass + + +# Subclass members carry the class object descriptor on the class and the concrete +# class type on the instance, including unioning the accepted classes. +class A: + pass + + +class B(A): + pass + + +class T(Atom): + m = Subclass(A) + + +assert_type(T.m, Subclass[type[A]]) +assert_type(T().m, type[A]) + + +class T2(Atom): + m = Subclass((int, A), B) + + +assert_type(T2.m, Subclass[type[int] | type[A]]) +assert_type(T2().m, type[int] | type[A]) + + +def resolve1() -> Type[A]: + return A + + +def resolve2() -> tuple[Type[A]]: + return (A,) + + +def resolve3() -> tuple[Type[int], Type[A]]: + return (int, A) + + +class T3(Atom): + m = ForwardSubclass(resolve3) + + +assert_type(T3.m, ForwardSubclass[type[int] | type[A]]) +assert_type(T3().m, type[int] | type[A]) diff --git a/tests/type_checking/test_annotations.yml b/tests/type_checking/test_annotations.yml deleted file mode 100644 index 6de6fbd4..00000000 --- a/tests/type_checking/test_annotations.yml +++ /dev/null @@ -1,24 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2022-2024, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: annotated_member - parametrized: - - member: List - annotation: List[int] - member_instance: List() - member_type: atom.list.List[builtins.int] - member_value_type: builtins.list[builtins.int] - main: | - import _io - from typing import Type - from atom.api import Atom, {{ member }} - - class A(Atom): - m: {{ annotation }} = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" diff --git a/tests/type_checking/test_coerced.yml b/tests/type_checking/test_coerced.yml deleted file mode 100644 index 15559e6e..00000000 --- a/tests/type_checking/test_coerced.yml +++ /dev/null @@ -1,82 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: coerced_no_coercer - parametrized: - - member: Coerced - member_instance: Coerced(int) - member_type: atom.coerced.Coerced[builtins.int, builtins.int] - member_value_type: builtins.int - - member: Coerced - member_instance: Coerced(int, ()) - member_type: atom.coerced.Coerced[builtins.int, builtins.int] - member_value_type: builtins.int - - member: Coerced - member_instance: Coerced(_io.StringIO, None, dict(initial_value=str(1))) - member_type: atom.coerced.Coerced[_io.StringIO, _io.StringIO] - member_value_type: _io.StringIO - - member: Coerced - member_instance: Coerced(_io.StringIO, kwargs=dict(initial_value=str(1))) - member_type: atom.coerced.Coerced[_io.StringIO, _io.StringIO] - member_value_type: _io.StringIO - - member: Coerced - member_instance: Coerced(_io.StringIO, factory=g) - member_type: atom.coerced.Coerced[_io.StringIO, _io.StringIO] - member_value_type: _io.StringIO - main: | - import _io - from typing import Type - from atom.api import Atom, {{ member }} - - def g() -> _io.StringIO: - return _io.StringIO() - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" - -# FIXME Those are broken on mypy 1.17 and it is unclear why -# - case: coerced_coercer -# parametrized: -# - member: Coerced -# member_instance: Coerced(_io.StringIO, coercer=coercer) -# member_type: atom.coerced.Coerced[_io.StringIO, _io.StringIO | builtins.str] -# member_value_type: _io.StringIO -# - member: Coerced -# member_instance: Coerced(_io.StringIO, (), coercer=coercer) -# member_type: atom.coerced.Coerced[_io.StringIO, _io.StringIO | builtins.str] -# member_value_type: _io.StringIO -# - member: Coerced -# member_instance: Coerced(_io.StringIO, None, dict(initial_value=str(1)), coercer=coercer) -# member_type: atom.coerced.Coerced[_io.StringIO, _io.StringIO | builtins.str] -# member_value_type: _io.StringIO -# - member: Coerced -# member_instance: Coerced(_io.StringIO, kwargs=dict(initial_value=str(1)), coercer=coercer) -# member_type: atom.coerced.Coerced[_io.StringIO, _io.StringIO | builtins.str] -# member_value_type: _io.StringIO -# - member: Coerced -# member_instance: Coerced(_io.StringIO, factory=g, coercer=coercer) -# member_type: atom.coerced.Coerced[_io.StringIO, _io.StringIO | builtins.str] -# member_value_type: _io.StringIO -# main: | -# import _io -# from typing import Type -# from atom.api import Atom, {{ member }} - -# def coercer(x: str) -> _io.StringIO: -# return _io.StringIO(x) - -# def g() -> _io.StringIO: -# return _io.StringIO() - -# class A(Atom): -# m = {{ member_instance }} - -# reveal_type(A.m) # N: Revealed type is "{{ member_type }}" -# reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" \ No newline at end of file diff --git a/tests/type_checking/test_delegator.yml b/tests/type_checking/test_delegator.yml deleted file mode 100644 index 15725ef0..00000000 --- a/tests/type_checking/test_delegator.yml +++ /dev/null @@ -1,18 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: delegator - main: | - from atom.api import Atom, Delegator, Int - - class A(Atom): - - i = Int(strict=False) - m = Delegator(i) - - reveal_type(A.m) # N: Revealed type is "atom.delegator.Delegator[builtins.int, builtins.int | builtins.float]" - reveal_type(A().m) # N: Revealed type is "builtins.int" diff --git a/tests/type_checking/test_dict.yml b/tests/type_checking/test_dict.yml deleted file mode 100644 index a7b2be48..00000000 --- a/tests/type_checking/test_dict.yml +++ /dev/null @@ -1,380 +0,0 @@ -# ------------------------------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -# ------------------------------------------------------------------------------------------------------ -- case: dict - parametrized: - # Dict untyped - - member: Dict - member_instance: Dict() - member_type: atom.dict.Dict[Any, Any] - member_value_type: builtins.dict[Any, Any] - - # Dict untyped - - member: Dict - member_instance: Dict(default=dict(a=1)) - member_type: atom.dict.Dict[Any, Any] - member_value_type: builtins.dict[Any, Any] - # Dict with typed key - - member: Dict - member_instance: Dict(int) - member_type: atom.dict.Dict[builtins.int, Any] - member_value_type: builtins.dict[builtins.int, Any] - - member: Dict - member_instance: Dict((int,)) - member_type: atom.dict.Dict[builtins.int, Any] - member_value_type: builtins.dict[builtins.int, Any] - - member: Dict - member_instance: Dict((int, float)) - member_type: atom.dict.Dict[builtins.int | builtins.float, Any] - member_value_type: builtins.dict[builtins.int | builtins.float, Any] - - member: Dict - member_instance: Dict((int, float, str)) - member_type: atom.dict.Dict[builtins.int | builtins.float | builtins.str, Any] - member_value_type: builtins.dict[builtins.int | builtins.float | builtins.str, Any] - - member: Dict, Int - member_instance: Dict(Int()) - member_type: atom.dict.Dict[builtins.int, Any] - member_value_type: builtins.dict[builtins.int, Any] - # Dict with typed value - - member: Dict - member_instance: Dict(None, int) - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - - member: Dict - member_instance: Dict(None, (int,)) - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - - member: Dict - member_instance: Dict(None, (int, float)) - member_type: atom.dict.Dict[Any, builtins.int | builtins.float] - member_value_type: builtins.dict[Any, builtins.int | builtins.float] - - member: Dict - member_instance: Dict(None, (int, float, str)) - member_type: atom.dict.Dict[Any, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[Any, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict(None, Int()) - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - # Dict with typed value as keyword - - member: Dict - member_instance: Dict(value=int) - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - - member: Dict - member_instance: Dict(value=(int,)) - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - - member: Dict - member_instance: Dict(value=(int, float)) - member_type: atom.dict.Dict[Any, builtins.int | builtins.float] - member_value_type: builtins.dict[Any, builtins.int | builtins.float] - - member: Dict - member_instance: Dict(value=(int, float, str)) - member_type: atom.dict.Dict[Any, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[Any, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict(value=Int()) - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - # Typed key and value - # simple type as key - - member: Dict - member_instance: Dict(int, int) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict - member_instance: Dict(int, (int,)) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict - member_instance: Dict(int, (int, float)) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float] - - member: Dict - member_instance: Dict(int, (int, float, str)) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict(int, Int()) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - # 1-tuple as key - - member: Dict - member_instance: Dict((int,), int) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict - member_instance: Dict((int,), (int,)) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict - member_instance: Dict((int,), (int, float)) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float] - - member: Dict - member_instance: Dict((int,), (int, float, str)) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict((int,), Int()) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - # 2-tuple as key - - member: Dict - member_instance: Dict((int, str), int) - member_type: atom.dict.Dict[builtins.int | builtins.str, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str, builtins.int] - - member: Dict - member_instance: Dict((int, str), (int,)) - member_type: atom.dict.Dict[builtins.int | builtins.str, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str, builtins.int] - - member: Dict - member_instance: Dict((int, str), (int, float)) - member_type: atom.dict.Dict[builtins.int | builtins.str, builtins.int | builtins.float] - member_value_type: builtins.dict[builtins.int | builtins.str, builtins.int | builtins.float] - - member: Dict - member_instance: Dict((int, str), (int, float, str)) - member_type: atom.dict.Dict[builtins.int | builtins.str, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[builtins.int | builtins.str, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict((int, str), Int()) - member_type: atom.dict.Dict[builtins.int | builtins.str, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str, builtins.int] - # 3-tuple as key - - member: Dict - member_instance: Dict((int, str, bytes), int) - member_type: atom.dict.Dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - - member: Dict - member_instance: Dict((int, str, bytes), (int,)) - member_type: atom.dict.Dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - - member: Dict - member_instance: Dict((int, str, bytes), (int, float)) - member_type: atom.dict.Dict[builtins.int | builtins.str | builtins.bytes, builtins.int | builtins.float] - member_value_type: builtins.dict[builtins.int | builtins.str | builtins.bytes, builtins.int | builtins.float] - - member: Dict - member_instance: Dict((int, str, bytes), (int, float, str)) - member_type: atom.dict.Dict[builtins.int | builtins.str | builtins.bytes, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[builtins.int | builtins.str | builtins.bytes, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict((int, str, bytes), Int()) - member_type: atom.dict.Dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - # member as key - - member: Dict, Int - member_instance: Dict(Int(), int) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict, Int - member_instance: Dict(Int(), (int,)) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict, Int - member_instance: Dict(Int(), (int, float)) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float] - - member: Dict, Int - member_instance: Dict(Int(), (int, float, str)) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict(Int(), Int()) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - main: | - from atom.api import Atom, {{ member }} - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" - - -- case: dict_with_default - parametrized: - # Dict with typed key - - member: Dict - member_instance: Dict(int - member_type: atom.dict.Dict[builtins.int, Any] - member_value_type: builtins.dict[builtins.int, Any] - - member: Dict - member_instance: Dict((int,) - member_type: atom.dict.Dict[builtins.int, Any] - member_value_type: builtins.dict[builtins.int, Any] - - member: Dict - member_instance: Dict((int, float) - member_type: atom.dict.Dict[builtins.int | builtins.float, Any] - member_value_type: builtins.dict[builtins.int | builtins.float, Any] - - member: Dict - member_instance: Dict((int, float, str) - member_type: atom.dict.Dict[builtins.int | builtins.float | builtins.str, Any] - member_value_type: builtins.dict[builtins.int | builtins.float | builtins.str, Any] - - member: Dict, Int - member_instance: Dict(Int() - member_type: atom.dict.Dict[builtins.int, Any] - member_value_type: builtins.dict[builtins.int, Any] - # Dict with typed value - - member: Dict - member_instance: Dict(None, int - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - - member: Dict - member_instance: Dict(None, (int,) - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - - member: Dict - member_instance: Dict(None, (int, float) - member_type: atom.dict.Dict[Any, builtins.int | builtins.float] - member_value_type: builtins.dict[Any, builtins.int | builtins.float] - - member: Dict - member_instance: Dict(None, (int, float, str) - member_type: atom.dict.Dict[Any, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[Any, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict(None, Int() - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - # Dict with typed value as keyword - - member: Dict - member_instance: Dict(value=int - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - - member: Dict - member_instance: Dict(value=(int,) - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - - member: Dict - member_instance: Dict(value=(int, float) - member_type: atom.dict.Dict[Any, builtins.int | builtins.float] - member_value_type: builtins.dict[Any, builtins.int | builtins.float] - - member: Dict - member_instance: Dict(value=(int, float, str) - member_type: atom.dict.Dict[Any, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[Any, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict(value=Int() - member_type: atom.dict.Dict[Any, builtins.int] - member_value_type: builtins.dict[Any, builtins.int] - # Typed key ad value - # simple type as key - - member: Dict - member_instance: Dict(int, int - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict - member_instance: Dict(int, (int,) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict - member_instance: Dict(int, (int, float) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float] - - member: Dict - member_instance: Dict(int, (int, float, str) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict(int, Int() - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - # 1-tuple as key - - member: Dict - member_instance: Dict((int,), int - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict - member_instance: Dict((int,), (int,) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict - member_instance: Dict((int,), (int, float) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float] - - member: Dict - member_instance: Dict((int,), (int, float, str) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict((int,), Int() - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - # 2-tuple as key - - member: Dict - member_instance: Dict((int, str), int - member_type: atom.dict.Dict[builtins.int | builtins.str, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str, builtins.int] - - member: Dict - member_instance: Dict((int, str), (int,) - member_type: atom.dict.Dict[builtins.int | builtins.str, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str, builtins.int] - - member: Dict - member_instance: Dict((int, str), (int, float) - member_type: atom.dict.Dict[builtins.int | builtins.str, builtins.int | builtins.float] - member_value_type: builtins.dict[builtins.int | builtins.str, builtins.int | builtins.float] - - member: Dict - member_instance: Dict((int, str), (int, float, str) - member_type: atom.dict.Dict[builtins.int | builtins.str, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[builtins.int | builtins.str, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict((int, str), Int() - member_type: atom.dict.Dict[builtins.int | builtins.str, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str, builtins.int] - # 3-tuple as key - - member: Dict - member_instance: Dict((int, str, bytes), int - member_type: atom.dict.Dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - - member: Dict - member_instance: Dict((int, str, bytes), (int,) - member_type: atom.dict.Dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - - member: Dict - member_instance: Dict((int, str, bytes), (int, float) - member_type: atom.dict.Dict[builtins.int | builtins.str | builtins.bytes, builtins.int | builtins.float] - member_value_type: builtins.dict[builtins.int | builtins.str | builtins.bytes, builtins.int | builtins.float] - - member: Dict - member_instance: Dict((int, str, bytes), (int, float, str) - member_type: atom.dict.Dict[builtins.int | builtins.str | builtins.bytes, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[builtins.int | builtins.str | builtins.bytes, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict((int, str, bytes), Int() - member_type: atom.dict.Dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - member_value_type: builtins.dict[builtins.int | builtins.str | builtins.bytes, builtins.int] - # member as key - - member: Dict, Int - member_instance: Dict(Int(), int - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict, Int - member_instance: Dict(Int(), (int,) - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - - member: Dict, Int - member_instance: Dict(Int(), (int, float) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float] - - member: Dict, Int - member_instance: Dict(Int(), (int, float, str) - member_type: atom.dict.Dict[builtins.int, builtins.int | builtins.float | builtins.str] - member_value_type: builtins.dict[builtins.int, builtins.int | builtins.float | builtins.str] - - member: Dict, Int - member_instance: Dict(Int(), Int() - member_type: atom.dict.Dict[builtins.int, builtins.int] - member_value_type: builtins.dict[builtins.int, builtins.int] - main: | - from atom.api import Atom, {{ member }} - - class A(Atom): - # Testing default value for dict is a pain since literal dict cannot be used - # in yaml parametrization. - m = {{ member_instance }}, default={1: 1}) - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" \ No newline at end of file diff --git a/tests/type_checking/test_enum.yml b/tests/type_checking/test_enum.yml deleted file mode 100644 index 4c43b644..00000000 --- a/tests/type_checking/test_enum.yml +++ /dev/null @@ -1,43 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: enum - parametrized: - - member: Enum - member_instance: Enum(1, 2) - member_type: atom.enum.Enum[builtins.int] - member_value_type: builtins.int - - member: Enum - member_instance: Enum(1, str(1)) - member_type: atom.enum.Enum[builtins.object] - member_value_type: builtins.object - main: | - from atom.api import Atom, {{ member }} - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" - A().m = 1 - -- case: enum_methods - main: | - from atom.api import Atom, Enum - - e = Enum(1, 2) - - class A(Atom): - e1 = e - e2 = e("1") - e3 = e.added("1") - e4 = e.removed(2) - - reveal_type(A.e1) # N: Revealed type is "atom.enum.Enum[builtins.int]" - reveal_type(A.e2) # N: Revealed type is "atom.enum.Enum[builtins.int | builtins.str]" - reveal_type(A.e3) # N: Revealed type is "atom.enum.Enum[builtins.int | builtins.str]" - reveal_type(A.e4) # N: Revealed type is "atom.enum.Enum[builtins.int]" diff --git a/tests/type_checking/test_event.yml b/tests/type_checking/test_event.yml deleted file mode 100644 index 04a64555..00000000 --- a/tests/type_checking/test_event.yml +++ /dev/null @@ -1,36 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: event - parametrized: - - member: Event - member_instance: Event() - member_type: atom.event.Event[Any] - - member: Event - member_instance: Event(int) - member_type: atom.event.Event[builtins.int] - - member: Event - member_instance: Event((int,)) - member_type: atom.event.Event[builtins.int] - - member: Event - member_instance: Event((int, float)) - member_type: atom.event.Event[builtins.int | builtins.float] - - member: Event - member_instance: Event((int, float, str)) - member_type: atom.event.Event[builtins.int | builtins.float | builtins.str] - - member: Event, Int - member_instance: Event(Int()) - member_type: atom.event.Event[builtins.int] - main: | - from atom.api import Atom, {{ member }} - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "atom.catom.EventBinder" - A().m = 1 \ No newline at end of file diff --git a/tests/type_checking/test_generic_aliases.yml b/tests/type_checking/test_generic_aliases.yml deleted file mode 100644 index 47233e85..00000000 --- a/tests/type_checking/test_generic_aliases.yml +++ /dev/null @@ -1,49 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: member - main: | - from atom.api import Member - - m: Member[int, str] = Member() - - reveal_type(m) # N: Revealed type is "atom.catom.Member[builtins.int, builtins.str]" - -- case: generic_alias_as_type - parametrized: - - member: Typed - member_instance: Typed(list[int], optional=False) - member_type: atom.typed.Typed[builtins.list[builtins.int]] - member_value_type: builtins.list[builtins.int] - - member: ForwardTyped - member_instance: ForwardTyped(lambda:list[int], optional=False) - member_type: atom.typed.ForwardTyped[builtins.list[builtins.int]] - member_value_type: builtins.list[builtins.int] - - member: Instance - member_instance: Instance(list[int], optional=False) - member_type: atom.instance.Instance[builtins.list[builtins.int]] - member_value_type: builtins.list[builtins.int] - - member: Instance - member_instance: Instance((list[int], int), optional=False) - member_type: atom.instance.Instance[builtins.list[builtins.int] | builtins.int] - member_value_type: builtins.list[builtins.int] | builtins.int - - member: ForwardInstance - member_instance: ForwardInstance(lambda:list[int], optional=False) - member_type: atom.instance.ForwardInstance[builtins.list[builtins.int]] - member_value_type: builtins.list[builtins.int] - - member: ForwardInstance - member_instance: ForwardInstance(lambda:(list[int], int), optional=False) - member_type: atom.instance.ForwardInstance[builtins.list[builtins.int] | builtins.int] - member_value_type: builtins.list[builtins.int] | builtins.int - main: | - from atom.api import Atom, {{ member }} - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" \ No newline at end of file diff --git a/tests/type_checking/test_list.yml b/tests/type_checking/test_list.yml deleted file mode 100644 index c853ef04..00000000 --- a/tests/type_checking/test_list.yml +++ /dev/null @@ -1,201 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: list - parametrized: - # List with no defaults - - member: List - member_instance: List() - member_type: atom.list.List[Any] - member_value_type: builtins.list[Any] - - member: List - member_instance: List(int) - member_type: atom.list.List[builtins.int] - member_value_type: builtins.list[builtins.int] - - member: List - member_instance: List((int,)) - member_type: atom.list.List[builtins.int] - member_value_type: builtins.list[builtins.int] - - member: List - member_instance: List((int, float)) - member_type: atom.list.List[builtins.int | builtins.float] - member_value_type: builtins.list[builtins.int | builtins.float] - - member: List - member_instance: List((int, float, str)) - member_type: atom.list.List[builtins.int | builtins.float | builtins.str] - member_value_type: builtins.list[builtins.int | builtins.float | builtins.str] - - member: List, Int - member_instance: List(Int()) - member_type: atom.list.List[builtins.int] - member_value_type: builtins.list[builtins.int] - # List with defaults - - member: List - member_instance: List(default=[1]) - member_type: atom.list.List[Any] - member_value_type: builtins.list[Any] - - member: List - member_instance: List(int, default=[3]) - member_type: atom.list.List[builtins.int] - member_value_type: builtins.list[builtins.int] - - member: List - member_instance: List((int,), default=[1,2]) - member_type: atom.list.List[builtins.int] - member_value_type: builtins.list[builtins.int] - # Ensure that for tuple of types any valid default works - - member: List - member_instance: List((int, str), default=[1]) - member_type: atom.list.List[builtins.int | builtins.str] - member_value_type: builtins.list[builtins.int | builtins.str] - - member: List - member_instance: List((int, str), default=[str(1)]) - member_type: atom.list.List[builtins.int | builtins.str] - member_value_type: builtins.list[builtins.int | builtins.str] - - member: List - member_instance: List((int, str), default=[1, str(1)]) - member_type: atom.list.List[builtins.int | builtins.str] - member_value_type: builtins.list[builtins.int | builtins.str] - - member: List - member_instance: List((int, str, Z), default=[1]) - member_type: atom.list.List[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: List - member_instance: List((int, str, Z), default=[str(1)]) - member_type: atom.list.List[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: List - member_instance: List((int, str, Z), default=[Z()]) - member_type: atom.list.List[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: List - member_instance: List((int, str, Z), default=[1, str(1)]) - member_type: atom.list.List[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: List - member_instance: List((int, str, Z), default=[1, Z()]) - member_type: atom.list.List[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: List - member_instance: List((int, str, Z), default=[str(1), Z()]) - member_type: atom.list.List[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: List - member_instance: List((int, str, Z), default=[1, str(1), Z()]) - member_type: atom.list.List[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: List, Int - member_instance: List(Int(), default=[2]) - member_type: atom.list.List[builtins.int] - member_value_type: builtins.list[builtins.int] - main: | - from atom.api import Atom, {{ member }} - - class Z(Atom): - pass - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" - - -- case: container_list - parametrized: - # List with no defaults - - member: ContainerList - member_instance: ContainerList() - member_type: atom.containerlist.ContainerList[Any] - member_value_type: builtins.list[Any] - - member: ContainerList - member_instance: ContainerList(int) - member_type: atom.containerlist.ContainerList[builtins.int] - member_value_type: builtins.list[builtins.int] - - member: ContainerList - member_instance: ContainerList((int,)) - member_type: atom.containerlist.ContainerList[builtins.int] - member_value_type: builtins.list[builtins.int] - - member: ContainerList - member_instance: ContainerList((int, float)) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.float] - member_value_type: builtins.list[builtins.int | builtins.float] - - member: ContainerList - member_instance: ContainerList((int, float, str)) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.float | builtins.str] - member_value_type: builtins.list[builtins.int | builtins.float | builtins.str] - - member: ContainerList, Int - member_instance: ContainerList(Int()) - member_type: atom.containerlist.ContainerList[builtins.int] - member_value_type: builtins.list[builtins.int] - # ContainerList with defaults - - member: ContainerList - member_instance: ContainerList(default=[1]) - member_type: atom.containerlist.ContainerList[Any] - member_value_type: builtins.list[Any] - - member: ContainerList - member_instance: ContainerList(int, default=[3]) - member_type: atom.containerlist.ContainerList[builtins.int] - member_value_type: builtins.list[builtins.int] - - member: ContainerList - member_instance: ContainerList((int,), default=[1,2]) - member_type: atom.containerlist.ContainerList[builtins.int] - member_value_type: builtins.list[builtins.int] - # Ensure that for tuple of types any valid default works - - member: ContainerList - member_instance: ContainerList((int, str), default=[1]) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.str] - member_value_type: builtins.list[builtins.int | builtins.str] - - member: ContainerList - member_instance: ContainerList((int, str), default=[str(1)]) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.str] - member_value_type: builtins.list[builtins.int | builtins.str] - - member: ContainerList - member_instance: ContainerList((int, str), default=[1, str(1)]) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.str] - member_value_type: builtins.list[builtins.int | builtins.str] - - member: ContainerList - member_instance: ContainerList((int, str, Z), default=[1]) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: ContainerList - member_instance: ContainerList((int, str, Z), default=[str(1)]) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: ContainerList - member_instance: ContainerList((int, str, Z), default=[Z()]) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: ContainerList - member_instance: ContainerList((int, str, Z), default=[1, str(1)]) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: ContainerList - member_instance: ContainerList((int, str, Z), default=[1, Z()]) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: ContainerList - member_instance: ContainerList((int, str, Z), default=[str(1), Z()]) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: ContainerList - member_instance: ContainerList((int, str, Z), default=[1, str(1), Z()]) - member_type: atom.containerlist.ContainerList[builtins.int | builtins.str | main.Z] - member_value_type: builtins.list[builtins.int | builtins.str | main.Z] - - member: ContainerList, Int - member_instance: ContainerList(Int(), default=[2]) - member_type: atom.containerlist.ContainerList[builtins.int] - member_value_type: builtins.list[builtins.int] - main: | - from atom.api import Atom, {{ member }} - - class Z(Atom): - pass - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" \ No newline at end of file diff --git a/tests/type_checking/test_property.yml b/tests/type_checking/test_property.yml deleted file mode 100644 index 74441243..00000000 --- a/tests/type_checking/test_property.yml +++ /dev/null @@ -1,74 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: property_empty - main: | - from atom.api import Atom, Property - - class A(Atom): - m = Property() - - reveal_type(A.m) # N: Revealed type is "atom.property.Property[Never, Never]" - reveal_type(A().m) # N: Revealed type is "Never" - - -- case: property_no_setter - main: | - from atom.api import Atom, Property - - def g(a) -> int: - return a.b - - class A(Atom): - m = Property(g) - - reveal_type(A.m) # N: Revealed type is "atom.property.Property[builtins.int, Never]" - reveal_type(A().m) # N: Revealed type is "builtins.int" - -- case: property - parametrized: - - member: Property - member_instance: Property(g, s) - member_type: atom.property.Property[builtins.int, builtins.int | builtins.str] - member_value_type: builtins.int - - member: Property - member_instance: Property(g, s, d) - member_type: atom.property.Property[builtins.int, builtins.int | builtins.str] - member_value_type: builtins.int - main: | - from typing import Union - - from atom.api import Atom, {{ member }} - - def g(a) -> int: - return a._b - - def s(a, v: Union[int, str]) -> None: - a._b = int(v) - - def d(a): - pass - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" - - -- case: cached_property - main: | - from atom.api import Atom, cached_property - - class A(Atom): - - @cached_property - def m(self) -> int: - return 1 - - reveal_type(A.m) # N: Revealed type is "atom.property.Property[builtins.int, Never]" - reveal_type(A().m) # N: Revealed type is "builtins.int" \ No newline at end of file diff --git a/tests/type_checking/test_scalars.yml b/tests/type_checking/test_scalars.yml deleted file mode 100644 index 51c92b2f..00000000 --- a/tests/type_checking/test_scalars.yml +++ /dev/null @@ -1,229 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: scalars_no_annotations - parametrized: - - member: Value - member_instance: Value() - member_type: atom.scalars.Value[Any] - member_value_type: Any - - member: ReadOnly - member_instance: ReadOnly() - member_type: atom.scalars.ReadOnly[Any] - member_value_type: Any - - member: ReadOnly - member_instance: ReadOnly(default=0) - member_type: atom.scalars.ReadOnly[builtins.int] - member_value_type: builtins.int - - member: ReadOnly - member_instance: ReadOnly(factory=g) - member_type: atom.scalars.ReadOnly[builtins.int] - member_value_type: builtins.int - - member: ReadOnly - member_instance: ReadOnly(int) - member_type: atom.scalars.ReadOnly[builtins.int] - member_value_type: builtins.int - - member: ReadOnly - member_instance: ReadOnly(int, default=0) - member_type: atom.scalars.ReadOnly[builtins.int] - member_value_type: builtins.int - - member: ReadOnly - member_instance: ReadOnly(int, factory=g) - member_type: atom.scalars.ReadOnly[builtins.int] - member_value_type: builtins.int - - member: ReadOnly - member_instance: ReadOnly((int,)) - member_type: atom.scalars.ReadOnly[builtins.int] - member_value_type: builtins.int - - member: ReadOnly - member_instance: ReadOnly((int,), default=0) - member_type: atom.scalars.ReadOnly[builtins.int] - member_value_type: builtins.int - - member: ReadOnly - member_instance: ReadOnly((int,), factory=g) - member_type: atom.scalars.ReadOnly[builtins.int] - member_value_type: builtins.int - - member: ReadOnly - member_instance: ReadOnly((int, str)) - member_type: atom.scalars.ReadOnly[builtins.int | builtins.str] - member_value_type: builtins.int | builtins.str - - member: ReadOnly - member_instance: ReadOnly((int, str), default=0) - member_type: atom.scalars.ReadOnly[builtins.int | builtins.str] - member_value_type: builtins.int | builtins.str - - member: ReadOnly - member_instance: ReadOnly((int, str), factory=g) - member_type: atom.scalars.ReadOnly[builtins.int | builtins.str] - member_value_type: builtins.int | builtins.str - - member: ReadOnly - member_instance: ReadOnly((int, str, float)) - member_type: atom.scalars.ReadOnly[builtins.int | builtins.str | builtins.float] - member_value_type: builtins.int | builtins.str | builtins.float - - member: ReadOnly - member_instance: ReadOnly((int, str, float), default=0) - member_type: atom.scalars.ReadOnly[builtins.int | builtins.str | builtins.float] - member_value_type: builtins.int | builtins.str | builtins.float - - member: ReadOnly - member_instance: ReadOnly((int, str, float), factory=g) - member_type: atom.scalars.ReadOnly[builtins.int | builtins.str | builtins.float] - member_value_type: builtins.int | builtins.str | builtins.float - - member: Constant - member_instance: Constant() - member_type: atom.scalars.Constant[Any] - member_value_type: Any - - member: Constant - member_instance: Constant(0) - member_type: atom.scalars.Constant[builtins.int] - member_value_type: builtins.int - - member: Constant - member_instance: Constant(factory=g) - member_type: atom.scalars.Constant[builtins.int] - member_value_type: builtins.int - - member: Constant - member_instance: Constant(kind=int) - member_type: atom.scalars.Constant[builtins.int] - member_value_type: builtins.int - - member: Constant - member_instance: Constant(0, kind=int) - member_type: atom.scalars.Constant[builtins.int] - member_value_type: builtins.int - - member: Constant - member_instance: Constant(factory=g, kind=int) - member_type: atom.scalars.Constant[builtins.int] - member_value_type: builtins.int - - member: Constant - member_instance: Constant(kind=(int,)) - member_type: atom.scalars.Constant[builtins.int] - member_value_type: builtins.int - - member: Constant - member_instance: Constant(0, kind=(int,)) - member_type: atom.scalars.Constant[builtins.int] - member_value_type: builtins.int - - member: Constant - member_instance: Constant(factory=g, kind=(int,)) - member_type: atom.scalars.Constant[builtins.int] - member_value_type: builtins.int - - member: Constant - member_instance: Constant(kind=(int, str)) - member_type: atom.scalars.Constant[builtins.int | builtins.str] - member_value_type: builtins.int | builtins.str - - member: Constant - member_instance: Constant(0, kind=(int, str)) - member_type: atom.scalars.Constant[builtins.int | builtins.str] - member_value_type: builtins.int | builtins.str - - member: Constant - member_instance: Constant(factory=g, kind=(int, str)) - member_type: atom.scalars.Constant[builtins.int | builtins.str] - member_value_type: builtins.int | builtins.str - - member: Constant - member_instance: Constant(kind=(int, str, float)) - member_type: atom.scalars.Constant[builtins.int | builtins.str | builtins.float] - member_value_type: builtins.int | builtins.str | builtins.float - - member: Constant - member_instance: Constant(0, kind=(int, str, float)) - member_type: atom.scalars.Constant[builtins.int | builtins.str | builtins.float] - member_value_type: builtins.int | builtins.str | builtins.float - - member: Constant - member_instance: Constant(factory=g, kind=(int, str, float)) - member_type: atom.scalars.Constant[builtins.int | builtins.str | builtins.float] - member_value_type: builtins.int | builtins.str | builtins.float - - member: Bool - member_instance: Bool() - member_type: atom.scalars.Bool[builtins.bool] - member_value_type: builtins.bool - - member: Int - member_instance: Int() - member_type: atom.scalars.Int[builtins.int] - member_value_type: builtins.int - - member: Int - member_instance: Int(strict=False) - member_type: atom.scalars.Int[builtins.int | builtins.float] - member_value_type: builtins.int - - member: Int - member_instance: Int(factory=f, strict=False) - member_type: atom.scalars.Int[builtins.int | builtins.float] - member_value_type: builtins.int - # XXX add strict cases - - member: FloatRange - member_instance: FloatRange() - member_type: atom.scalars.FloatRange[builtins.int | builtins.float] - member_value_type: builtins.float - - member: Range - member_instance: Range() - member_type: atom.scalars.Range[builtins.int] - member_value_type: builtins.int - - member: Float - member_instance: Float() - member_type: atom.scalars.Float[builtins.int | builtins.float] - member_value_type: builtins.float - - member: Float - member_instance: Float(strict=True) - member_type: atom.scalars.Float[builtins.float] - member_value_type: builtins.float - - member: Bytes - member_instance: Bytes(strict=False) - member_type: atom.scalars.Bytes[builtins.bytes | builtins.str] - member_value_type: builtins.bytes - - member: Bytes - member_instance: Bytes() - member_type: atom.scalars.Bytes[builtins.bytes] - member_value_type: builtins.bytes - - member: Str - member_instance: Str(strict=False) - member_type: atom.scalars.Str[builtins.str | builtins.bytes] - member_value_type: builtins.str - - member: Str - member_instance: Str() - member_type: atom.scalars.Str[builtins.str] - member_value_type: builtins.str - main: | - from atom.api import Atom, {{ member }} - - def f(): - pass - - def g() -> int: - return 1 - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" - -- case: callable - main: | - from atom.api import Atom, Callable - - class A(Atom): - m = Callable() - - reveal_type(A.m) # N: Revealed type is "atom.scalars.Callable[def (*Any, **Any) -> Any]" - reveal_type(A().m) # N: Revealed type is "def (*Any, **Any) -> Any" - -- case: str_with_default - main: | - from atom.api import Atom, Str - - class A(Atom): - m = Str('', strict=True) - - reveal_type(A.m) # N: Revealed type is "atom.scalars.Str[builtins.str]" - reveal_type(A().m) # N: Revealed type is "builtins.str" - -- case: bytes_with_default - main: | - from atom.api import Atom, Bytes - - class A(Atom): - m = Bytes(b"", strict=True) - - reveal_type(A.m) # N: Revealed type is "atom.scalars.Bytes[builtins.bytes]" - reveal_type(A().m) # N: Revealed type is "builtins.bytes" - - -# XXX add tests with explicit annotations diff --git a/tests/type_checking/test_set.yml b/tests/type_checking/test_set.yml deleted file mode 100644 index 3735e58e..00000000 --- a/tests/type_checking/test_set.yml +++ /dev/null @@ -1,103 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: set - parametrized: - # Set with no defaults - - member: Set - member_instance: Set() - member_type: atom.set.Set[Any] - member_value_type: builtins.set[Any] - - member: Set - member_instance: Set(int) - member_type: atom.set.Set[builtins.int] - member_value_type: builtins.set[builtins.int] - - member: Set - member_instance: Set((int,)) - member_type: atom.set.Set[builtins.int] - member_value_type: builtins.set[builtins.int] - - member: Set - member_instance: Set((int, float)) - member_type: atom.set.Set[builtins.int | builtins.float] - member_value_type: builtins.set[builtins.int | builtins.float] - - member: Set - member_instance: Set((int, float, str)) - member_type: atom.set.Set[builtins.int | builtins.float | builtins.str] - member_value_type: builtins.set[builtins.int | builtins.float | builtins.str] - - member: Set, Int - member_instance: Set(Int()) - member_type: atom.set.Set[builtins.int] - member_value_type: builtins.set[builtins.int] - # Set with defaults - - member: Set - member_instance: Set(default={1}) - member_type: atom.set.Set[Any] - member_value_type: builtins.set[Any] - - member: Set - member_instance: Set(int, default={3}) - member_type: atom.set.Set[builtins.int] - member_value_type: builtins.set[builtins.int] - - member: Set - member_instance: Set((int,), default={1,2}) - member_type: atom.set.Set[builtins.int] - member_value_type: builtins.set[builtins.int] - # Ensure that for tuple of types any valid default works - - member: Set - member_instance: Set((int, str), default={1}) - member_type: atom.set.Set[builtins.int | builtins.str] - member_value_type: builtins.set[builtins.int | builtins.str] - - member: Set - member_instance: Set((int, str), default={str(1)}) - member_type: atom.set.Set[builtins.int | builtins.str] - member_value_type: builtins.set[builtins.int | builtins.str] - - member: Set - member_instance: Set((int, str), default={1, str(1)}) - member_type: atom.set.Set[builtins.int | builtins.str] - member_value_type: builtins.set[builtins.int | builtins.str] - - member: Set - member_instance: Set((int, str, Z), default={1}) - member_type: atom.set.Set[builtins.int | builtins.str | main.Z] - member_value_type: builtins.set[builtins.int | builtins.str | main.Z] - - member: Set - member_instance: Set((int, str, Z), default={str(1)}) - member_type: atom.set.Set[builtins.int | builtins.str | main.Z] - member_value_type: builtins.set[builtins.int | builtins.str | main.Z] - - member: Set - member_instance: Set((int, str, Z), default={Z()}) - member_type: atom.set.Set[builtins.int | builtins.str | main.Z] - member_value_type: builtins.set[builtins.int | builtins.str | main.Z] - - member: Set - member_instance: Set((int, str, Z), default={1, str(1)}) - member_type: atom.set.Set[builtins.int | builtins.str | main.Z] - member_value_type: builtins.set[builtins.int | builtins.str | main.Z] - - member: Set - member_instance: Set((int, str, Z), default={1, Z()}) - member_type: atom.set.Set[builtins.int | builtins.str | main.Z] - member_value_type: builtins.set[builtins.int | builtins.str | main.Z] - - member: Set - member_instance: Set((int, str, Z), default={str(1), Z()}) - member_type: atom.set.Set[builtins.int | builtins.str | main.Z] - member_value_type: builtins.set[builtins.int | builtins.str | main.Z] - - member: Set - member_instance: Set((int, str, Z), default={1, str(1), Z()}) - member_type: atom.set.Set[builtins.int | builtins.str | main.Z] - member_value_type: builtins.set[builtins.int | builtins.str | main.Z] - - member: Set, Int - member_instance: Set(Int(), default={2}) - member_type: atom.set.Set[builtins.int] - member_value_type: builtins.set[builtins.int] - main: | - from atom.api import Atom, {{ member }} - - class Z(Atom): - pass - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" diff --git a/tests/type_checking/test_signal.yml b/tests/type_checking/test_signal.yml deleted file mode 100644 index 4b6e207a..00000000 --- a/tests/type_checking/test_signal.yml +++ /dev/null @@ -1,17 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: signal - main: | - from atom.api import Atom, Signal - - class A(Atom): - m = Signal() - - reveal_type(A.m) # N: Revealed type is "atom.signal.Signal" - reveal_type(A().m) # N: Revealed type is "atom.catom.SignalConnector" - A().m() \ No newline at end of file diff --git a/tests/type_checking/test_subclass.yml b/tests/type_checking/test_subclass.yml deleted file mode 100644 index 48877d58..00000000 --- a/tests/type_checking/test_subclass.yml +++ /dev/null @@ -1,85 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: subclass - parametrized: - - member: Subclass - member_instance: Subclass(A) - member_type: atom.subclass.Subclass[type[main.A]] - member_value_type: type[main.A] - - member: Subclass - member_instance: Subclass(A, B) - member_type: atom.subclass.Subclass[type[main.A]] - member_value_type: type[main.A] - - member: Subclass - member_instance: Subclass((A,)) - member_type: atom.subclass.Subclass[type[main.A]] - member_value_type: type[main.A] - - member: Subclass - member_instance: Subclass((A,), B) - member_type: atom.subclass.Subclass[type[main.A]] - member_value_type: type[main.A] - - member: Subclass - member_instance: Subclass((int, A)) - member_type: atom.subclass.Subclass[type[builtins.int] | type[main.A]] - member_value_type: type[builtins.int] | type[main.A] - - member: Subclass - member_instance: Subclass((int, A), B) - member_type: atom.subclass.Subclass[type[builtins.int] | type[main.A]] - member_value_type: type[builtins.int] | type[main.A] - - member: Subclass - member_instance: Subclass((int, A, str)) - member_type: atom.subclass.Subclass[type[builtins.int] | type[main.A] | type[builtins.str]] - member_value_type: type[builtins.int] | type[main.A] | type[builtins.str] - - member: Subclass - member_instance: Subclass((int, A, str), B) - member_type: atom.subclass.Subclass[type[builtins.int] | type[main.A] | type[builtins.str]] - member_value_type: type[builtins.int] | type[main.A] | type[builtins.str] - - member: ForwardSubclass - member_instance: ForwardSubclass(resolve1) - member_type: atom.subclass.ForwardSubclass[type[main.A]] - member_value_type: type[main.A] - - member: ForwardSubclass - member_instance: ForwardSubclass(resolve2) - member_type: atom.subclass.ForwardSubclass[type[main.A]] - member_value_type: type[main.A] - - member: ForwardSubclass - member_instance: ForwardSubclass(resolve3) - member_type: atom.subclass.ForwardSubclass[type[builtins.int] | type[main.A]] - member_value_type: type[builtins.int] | type[main.A] - - member: ForwardSubclass - member_instance: ForwardSubclass(resolve4) - member_type: atom.subclass.ForwardSubclass[type[builtins.int] | type[main.A] | type[builtins.str]] - member_value_type: type[builtins.int] | type[main.A] | type[builtins.str] - main: | - import _io - from typing import Tuple, Type - from atom.api import Atom, {{ member }} - - class A: - pass - - class B(A): - pass - - def resolve1() -> Type[A]: - return A - - def resolve2() -> Tuple[Type[A]]: - return A, - - def resolve3() -> Tuple[Type[int], Type[A]]: - return int, A - - def resolve4() -> Tuple[Type[int], Type[A], Type[str]]: - return int, A, str - - class T(Atom): - m = {{ member_instance }} - - reveal_type(T.m) # N: Revealed type is "{{ member_type }}" - reveal_type(T().m) # N: Revealed type is "{{ member_value_type }}" \ No newline at end of file diff --git a/tests/type_checking/test_tuple.yml b/tests/type_checking/test_tuple.yml deleted file mode 100644 index ab183181..00000000 --- a/tests/type_checking/test_tuple.yml +++ /dev/null @@ -1,134 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -#------------------------------------------------------------------------------ -- case: tuple - parametrized: - # Tuple with no defaults - - member: Tuple - member_instance: Tuple() - member_type: atom.tuple.Tuple[Any] - member_value_type: builtins.tuple[Any, ...] - - member: Tuple - member_instance: Tuple(int) - member_type: atom.tuple.Tuple[builtins.int] - member_value_type: builtins.tuple[builtins.int, ...] - - member: Tuple - member_instance: Tuple((int,)) - member_type: atom.tuple.Tuple[builtins.int] - member_value_type: builtins.tuple[builtins.int, ...] - - member: Tuple - member_instance: Tuple((int, float)) - member_type: atom.tuple.Tuple[builtins.int | builtins.float] - member_value_type: builtins.tuple[builtins.int | builtins.float, ...] - - member: Tuple - member_instance: Tuple((int, float, str)) - member_type: atom.tuple.Tuple[builtins.int | builtins.float | builtins.str] - member_value_type: builtins.tuple[builtins.int | builtins.float | builtins.str, ...] - - member: Tuple, Int - member_instance: Tuple(Int()) - member_type: atom.tuple.Tuple[builtins.int] - member_value_type: builtins.tuple[builtins.int, ...] - # Tuple with defaults - - member: Tuple - member_instance: Tuple(default=(1,)) - member_type: atom.tuple.Tuple[Any] - member_value_type: builtins.tuple[Any, ...] - - member: Tuple - member_instance: Tuple(int, default=(3,)) - member_type: atom.tuple.Tuple[builtins.int] - member_value_type: builtins.tuple[builtins.int, ...] - - member: Tuple - member_instance: Tuple((int,), default=(1, 2)) - member_type: atom.tuple.Tuple[builtins.int] - member_value_type: builtins.tuple[builtins.int, ...] - # Ensure that for tuple of types any valid default works - - member: Tuple - member_instance: Tuple((int, str), default=(1,)) - member_type: atom.tuple.Tuple[builtins.int | builtins.str] - member_value_type: builtins.tuple[builtins.int | builtins.str, ...] - - member: Tuple - member_instance: Tuple((int, str), default=(str(1),)) - member_type: atom.tuple.Tuple[builtins.int | builtins.str] - member_value_type: builtins.tuple[builtins.int | builtins.str, ...] - - member: Tuple - member_instance: Tuple((int, str), default=(1, str(1))) - member_type: atom.tuple.Tuple[builtins.int | builtins.str] - member_value_type: builtins.tuple[builtins.int | builtins.str, ...] - - member: Tuple - member_instance: Tuple((int, str, bytes), default=(1,)) - member_type: atom.tuple.Tuple[builtins.int | builtins.str | builtins.bytes] - member_value_type: builtins.tuple[builtins.int | builtins.str | builtins.bytes, ...] - - member: Tuple - member_instance: Tuple((int, str, bytes), default=(str(1),)) - member_type: atom.tuple.Tuple[builtins.int | builtins.str | builtins.bytes] - member_value_type: builtins.tuple[builtins.int | builtins.str | builtins.bytes, ...] - - member: Tuple - member_instance: Tuple((int, str, bytes), default=(bytes(1),)) - member_type: atom.tuple.Tuple[builtins.int | builtins.str | builtins.bytes] - member_value_type: builtins.tuple[builtins.int | builtins.str | builtins.bytes, ...] - - member: Tuple - member_instance: Tuple((int, str, bytes), default=(1, str(1),)) - member_type: atom.tuple.Tuple[builtins.int | builtins.str | builtins.bytes] - member_value_type: builtins.tuple[builtins.int | builtins.str | builtins.bytes, ...] - - member: Tuple - member_instance: Tuple((int, str, bytes), default=(1, bytes(1),)) - member_type: atom.tuple.Tuple[builtins.int | builtins.str | builtins.bytes] - member_value_type: builtins.tuple[builtins.int | builtins.str | builtins.bytes, ...] - - member: Tuple - member_instance: Tuple((int, str, bytes), default=(str(1), bytes(1))) - member_type: atom.tuple.Tuple[builtins.int | builtins.str | builtins.bytes] - member_value_type: builtins.tuple[builtins.int | builtins.str | builtins.bytes, ...] - - member: Tuple - member_instance: Tuple((int, str, bytes), default=(1, str(1), bytes(1))) - member_type: atom.tuple.Tuple[builtins.int | builtins.str | builtins.bytes] - member_value_type: builtins.tuple[builtins.int | builtins.str | builtins.bytes, ...] - - member: Tuple, Int - member_instance: Tuple(Int(), default=(2,)) - member_type: atom.tuple.Tuple[builtins.int] - member_value_type: builtins.tuple[builtins.int, ...] - main: | - from atom.api import Atom, {{ member }} - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" - -- case: fixed_tuple - parametrized: - # Tuple with no defaults - - member: FixedTuple - member_instance: FixedTuple(int) - member_type: atom.tuple.FixedTuple[tuple[builtins.int]] - member_value_type: tuple[builtins.int] - - member: FixedTuple - member_instance: FixedTuple(int, float) - member_type: atom.tuple.FixedTuple[tuple[builtins.int, builtins.float]] - member_value_type: tuple[builtins.int, builtins.float] - - member: FixedTuple - member_instance: FixedTuple(int, float, str) - member_type: atom.tuple.FixedTuple[tuple[builtins.int, builtins.float, builtins.str]] - member_value_type: tuple[builtins.int, builtins.float, builtins.str] - - member: FixedTuple, Int - member_instance: FixedTuple(Int()) - member_type: atom.tuple.FixedTuple[tuple[builtins.int]] - member_value_type: tuple[builtins.int] - # Tuple with defaults - - member: FixedTuple - member_instance: FixedTuple(int, default=(3,)) - member_type: atom.tuple.FixedTuple[tuple[builtins.int]] - member_value_type: tuple[builtins.int] - main: | - from atom.api import Atom, {{ member }} - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" - diff --git a/tests/type_checking/test_typed_instance.yml b/tests/type_checking/test_typed_instance.yml deleted file mode 100644 index abab0bc4..00000000 --- a/tests/type_checking/test_typed_instance.yml +++ /dev/null @@ -1,579 +0,0 @@ -# ------------------------------------------------------------------------------------------------------ -# Copyright (c) 2021-2025, Nucleic Development Team. -# -# Distributed under the terms of the Modified BSD License. -# -# The full license is in the file LICENSE, distributed with this software. -# ------------------------------------------------------------------------------------------------------ -- case: typed_no_annotations - parametrized: - # Typed inferred optional - - member: Typed - member_instance: Typed(int) - member_type: atom.typed.Typed[builtins.int | None] - member_value_type: builtins.int | None - - member: Typed - member_instance: Typed(List[int]) - member_type: atom.typed.Typed[builtins.list[builtins.int] | None] - member_value_type: builtins.list[builtins.int] | None - - member: Typed - member_instance: Typed(int, ()) - member_type: atom.typed.Typed[builtins.int] - member_value_type: builtins.int - - member: Typed - member_instance: Typed(_io.StringIO, kwargs=dict(initial_value=str(1))) - member_type: atom.typed.Typed[_io.StringIO] - member_value_type: _io.StringIO - - member: Typed - member_instance: Typed(_io.StringIO, factory=g) - member_type: atom.typed.Typed[_io.StringIO] - member_value_type: _io.StringIO - # Forward typed inferred optional - - member: ForwardTyped - member_instance: ForwardTyped(resolver) - member_type: atom.typed.ForwardTyped[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardTyped - member_instance: ForwardTyped(resolver, ()) - member_type: atom.typed.ForwardTyped[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardTyped - member_instance: ForwardTyped(resolver, kwargs=dict(initial_value=str(1))) - member_type: atom.typed.ForwardTyped[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardTyped - member_instance: ForwardTyped(resolver, factory=g) - member_type: atom.typed.ForwardTyped[_io.StringIO] - member_value_type: _io.StringIO - # Typed optional - - member: Typed - member_instance: Typed(int, optional=True) - member_type: atom.typed.Typed[builtins.int | None] - member_value_type: builtins.int | None - - member: Typed - member_instance: Typed(int, (), optional=True) - member_type: atom.typed.Typed[builtins.int | None] - member_value_type: builtins.int | None - - member: Typed - member_instance: Typed(_io.StringIO, kwargs=dict(initial_value=str(1)), optional=True) - member_type: atom.typed.Typed[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: Typed - member_instance: Typed(_io.StringIO, factory=g, optional=True) - member_type: atom.typed.Typed[_io.StringIO | None] - member_value_type: _io.StringIO | None - # Forward typed optional - - member: ForwardTyped - member_instance: ForwardTyped(resolver, optional=True) - member_type: atom.typed.ForwardTyped[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardTyped - member_instance: ForwardTyped(resolver, (), optional=True) - member_type: atom.typed.ForwardTyped[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardTyped - member_instance: ForwardTyped(resolver, kwargs=dict(initial_value=str(1)), optional=True) - member_type: atom.typed.ForwardTyped[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardTyped - member_instance: ForwardTyped(resolver, factory=g, optional=True) - member_type: atom.typed.ForwardTyped[_io.StringIO | None] - member_value_type: _io.StringIO | None - # Typed non optional - - member: Typed - member_instance: Typed(int, optional=False) - member_type: atom.typed.Typed[builtins.int] - member_value_type: builtins.int - - member: Typed - member_instance: Typed(int, (), optional=False) - member_type: atom.typed.Typed[builtins.int] - member_value_type: builtins.int - - member: Typed - member_instance: Typed(_io.StringIO, kwargs=dict(initial_value=str(1)), optional=False) - member_type: atom.typed.Typed[_io.StringIO] - member_value_type: _io.StringIO - - member: Typed - member_instance: Typed(_io.StringIO, factory=g, optional=False) - member_type: atom.typed.Typed[_io.StringIO] - member_value_type: _io.StringIO - # Forward typed non optional - - member: ForwardTyped - member_instance: ForwardTyped(resolver, optional=False) - member_type: atom.typed.ForwardTyped[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardTyped - member_instance: ForwardTyped(resolver, (), optional=False) - member_type: atom.typed.ForwardTyped[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardTyped - member_instance: ForwardTyped(resolver, kwargs=dict(initial_value=str(1)), optional=False) - member_type: atom.typed.ForwardTyped[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardTyped - member_instance: ForwardTyped(resolver, factory=g, optional=False) - member_type: atom.typed.ForwardTyped[_io.StringIO] - member_value_type: _io.StringIO - main: | - import _io - from typing import List, Type - from atom.api import Atom, {{ member }} - - def resolver() -> Type[_io.StringIO]: - return _io.StringIO - - def g() -> _io.StringIO: - return _io.StringIO() - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" - - -- case: instance_no_annotations - parametrized: - # Instance inferred optional - - member: Instance - member_instance: Instance(int) - member_type: atom.instance.Instance[builtins.int | None] - member_value_type: builtins.int | None - - member: Instance - member_instance: Instance((int,)) - member_type: atom.instance.Instance[builtins.int | None] - member_value_type: builtins.int | None - - member: Instance - member_instance: Instance((int, float)) - member_type: atom.instance.Instance[builtins.int | builtins.float | None] - member_value_type: builtins.int | builtins.float | None - - member: Instance - member_instance: Instance((int, float, str)) - member_type: atom.instance.Instance[builtins.int | builtins.float | builtins.str | None] - member_value_type: builtins.int | builtins.float | builtins.str | None - - member: Instance - member_instance: Instance(List[int]) - member_type: atom.instance.Instance[builtins.list[builtins.int] | None] - member_value_type: builtins.list[builtins.int] | None - - member: Instance - member_instance: Instance(int, ()) - member_type: atom.instance.Instance[builtins.int] - member_value_type: builtins.int - - member: Instance - member_instance: Instance((int,), ()) - member_type: atom.instance.Instance[builtins.int] - member_value_type: builtins.int - - member: Instance - member_instance: Instance((int, float), ()) - member_type: atom.instance.Instance[builtins.int | builtins.float] - member_value_type: builtins.int | builtins.float - - member: Instance - member_instance: Instance((int, float, str), ()) - member_type: atom.instance.Instance[builtins.int | builtins.float | builtins.str] - member_value_type: builtins.int | builtins.float | builtins.str - - member: Instance - member_instance: Instance(_io.StringIO, kwargs=dict(initial_value=str(1))) - member_type: atom.instance.Instance[_io.StringIO] - member_value_type: _io.StringIO - - member: Instance - member_instance: Instance((_io.StringIO,), kwargs=dict(initial_value=str(1))) - member_type: atom.instance.Instance[_io.StringIO] - member_value_type: _io.StringIO - - member: Instance - member_instance: Instance((_io.StringIO, str), kwargs=dict(initial_value=str(1))) - member_type: atom.instance.Instance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: Instance - member_instance: Instance((_io.StringIO, str, bytes), kwargs=dict(initial_value=str(1))) - member_type: atom.instance.Instance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - - member: Instance - member_instance: Instance(_io.StringIO, factory=g) - member_type: atom.instance.Instance[_io.StringIO] - member_value_type: _io.StringIO - - member: Instance - member_instance: Instance((_io.StringIO,), factory=g) - member_type: atom.instance.Instance[_io.StringIO] - member_value_type: _io.StringIO - - member: Instance - member_instance: Instance((_io.StringIO, str), factory=g) - member_type: atom.instance.Instance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: Instance - member_instance: Instance((_io.StringIO, str, bytes), factory=g) - member_type: atom.instance.Instance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - # Forward instance inferred optional - - member: ForwardInstance - member_instance: ForwardInstance(resolver) - member_type: atom.instance.ForwardInstance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver1) - member_type: atom.instance.ForwardInstance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver2) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | None] - member_value_type: _io.StringIO | builtins.str | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver3) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes | None] - member_value_type: _io.StringIO | builtins.str | builtins.bytes | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver, ()) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, ()) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, ()) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, ()) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - - member: ForwardInstance - member_instance: ForwardInstance(resolver, kwargs=dict(initial_value=str(1))) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, kwargs=dict(initial_value=str(1))) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, kwargs=dict(initial_value=str(1))) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, kwargs=dict(initial_value=str(1))) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - - member: ForwardInstance - member_instance: ForwardInstance(resolver, factory=g) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, factory=g) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, factory=g) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, factory=g) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - # optional instance - - member: Instance - member_instance: Instance(int, optional=True) - member_type: atom.instance.Instance[builtins.int | None] - member_value_type: builtins.int | None - - member: Instance - member_instance: Instance((int,), optional=True) - member_type: atom.instance.Instance[builtins.int | None] - member_value_type: builtins.int | None - - member: Instance - member_instance: Instance((int, float), optional=True) - member_type: atom.instance.Instance[builtins.int | builtins.float | None] - member_value_type: builtins.int | builtins.float | None - - member: Instance - member_instance: Instance((int, float, str), optional=True) - member_type: atom.instance.Instance[builtins.int | builtins.float | builtins.str | None] - member_value_type: builtins.int | builtins.float | builtins.str | None - - member: Instance - member_instance: Instance(int, (), optional=True) - member_type: atom.instance.Instance[builtins.int | None] - member_value_type: builtins.int | None - - member: Instance - member_instance: Instance((int,), (), optional=True) - member_type: atom.instance.Instance[builtins.int | None] - member_value_type: builtins.int | None - - member: Instance - member_instance: Instance((int, float), (), optional=True) - member_type: atom.instance.Instance[builtins.int | builtins.float | None] - member_value_type: builtins.int | builtins.float | None - - member: Instance - member_instance: Instance((int, float, str), (), optional=True) - member_type: atom.instance.Instance[builtins.int | builtins.float | builtins.str | None] - member_value_type: builtins.int | builtins.float | builtins.str | None - - member: Instance - member_instance: Instance(_io.StringIO, kwargs=dict(initial_value=str(1)), optional=True) - member_type: atom.instance.Instance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: Instance - member_instance: Instance((_io.StringIO,), kwargs=dict(initial_value=str(1)), optional=True) - member_type: atom.instance.Instance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: Instance - member_instance: Instance((_io.StringIO, str), kwargs=dict(initial_value=str(1)), optional=True) - member_type: atom.instance.Instance[_io.StringIO | builtins.str | None] - member_value_type: _io.StringIO | builtins.str | None - - member: Instance - member_instance: Instance((_io.StringIO, str, bytes), kwargs=dict(initial_value=str(1)), optional=True) - member_type: atom.instance.Instance[_io.StringIO | builtins.str | builtins.bytes | None] - member_value_type: _io.StringIO | builtins.str | builtins.bytes | None - - member: Instance - member_instance: Instance(_io.StringIO, factory=g, optional=True) - member_type: atom.instance.Instance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: Instance - member_instance: Instance((_io.StringIO,), factory=g, optional=True) - member_type: atom.instance.Instance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: Instance - member_instance: Instance((_io.StringIO, str), factory=g, optional=True) - member_type: atom.instance.Instance[_io.StringIO | builtins.str | None] - member_value_type: _io.StringIO | builtins.str | None - - member: Instance - member_instance: Instance((_io.StringIO, str, bytes), factory=g, optional=True) - member_type: atom.instance.Instance[_io.StringIO | builtins.str | builtins.bytes | None] - member_value_type: _io.StringIO | builtins.str | builtins.bytes | None - # optional forward instance - - member: ForwardInstance - member_instance: ForwardInstance(resolver, optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | None] - member_value_type: _io.StringIO | builtins.str | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes | None] - member_value_type: _io.StringIO | builtins.str | builtins.bytes | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver, (), optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, (), optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, (), optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | None] - member_value_type: _io.StringIO | builtins.str | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, (), optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes | None] - member_value_type: _io.StringIO | builtins.str | builtins.bytes | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver, kwargs=dict(initial_value=str(1)), optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, kwargs=dict(initial_value=str(1)), optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, kwargs=dict(initial_value=str(1)), optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | None] - member_value_type: _io.StringIO | builtins.str | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, kwargs=dict(initial_value=str(1)), optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes | None] - member_value_type: _io.StringIO | builtins.str | builtins.bytes | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver, factory=g, optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, factory=g, optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | None] - member_value_type: _io.StringIO | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, factory=g, optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | None] - member_value_type: _io.StringIO | builtins.str | None - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, factory=g, optional=True) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes | None] - member_value_type: _io.StringIO | builtins.str | builtins.bytes | None - # non optional instance - - member: Instance - member_instance: Instance(int, optional=False) - member_type: atom.instance.Instance[builtins.int] - member_value_type: builtins.int - - member: Instance - member_instance: Instance((int,), optional=False) - member_type: atom.instance.Instance[builtins.int] - member_value_type: builtins.int - - member: Instance - member_instance: Instance((int, float), optional=False) - member_type: atom.instance.Instance[builtins.int | builtins.float] - member_value_type: builtins.int | builtins.float - - member: Instance - member_instance: Instance((int, float, str), optional=False) - member_type: atom.instance.Instance[builtins.int | builtins.float | builtins.str] - member_value_type: builtins.int | builtins.float | builtins.str - - member: Instance - member_instance: Instance(int, (), optional=False) - member_type: atom.instance.Instance[builtins.int] - member_value_type: builtins.int - - member: Instance - member_instance: Instance((int,), (), optional=False) - member_type: atom.instance.Instance[builtins.int] - member_value_type: builtins.int - - member: Instance - member_instance: Instance((int, float), (), optional=False) - member_type: atom.instance.Instance[builtins.int | builtins.float] - member_value_type: builtins.int | builtins.float - - member: Instance - member_instance: Instance((int, float, str), (), optional=False) - member_type: atom.instance.Instance[builtins.int | builtins.float | builtins.str] - member_value_type: builtins.int | builtins.float | builtins.str - - member: Instance - member_instance: Instance(_io.StringIO, None, dict(initial_value=str(1)), optional=False) - member_type: atom.instance.Instance[_io.StringIO] - member_value_type: _io.StringIO - - member: Instance - member_instance: Instance((_io.StringIO,), None, dict(initial_value=str(1)), optional=False) - member_type: atom.instance.Instance[_io.StringIO] - member_value_type: _io.StringIO - - member: Instance - member_instance: Instance(_io.StringIO, kwargs=dict(initial_value=str(1)), optional=False) - member_type: atom.instance.Instance[_io.StringIO] - member_value_type: _io.StringIO - - member: Instance - member_instance: Instance((_io.StringIO,), kwargs=dict(initial_value=str(1)), optional=False) - member_type: atom.instance.Instance[_io.StringIO] - member_value_type: _io.StringIO - - member: Instance - member_instance: Instance((_io.StringIO, str), kwargs=dict(initial_value=str(1)), optional=False) - member_type: atom.instance.Instance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: Instance - member_instance: Instance((_io.StringIO, str, bytes), kwargs=dict(initial_value=str(1)), optional=False) - member_type: atom.instance.Instance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - - member: Instance - member_instance: Instance(_io.StringIO, factory=g, optional=False) - member_type: atom.instance.Instance[_io.StringIO] - member_value_type: _io.StringIO - - member: Instance - member_instance: Instance((_io.StringIO,), factory=g, optional=False) - member_type: atom.instance.Instance[_io.StringIO] - member_value_type: _io.StringIO - - member: Instance - member_instance: Instance((_io.StringIO, str), factory=g, optional=False) - member_type: atom.instance.Instance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: Instance - member_instance: Instance((_io.StringIO, str, bytes), factory=g, optional=False) - member_type: atom.instance.Instance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - # optional forward instance - - member: ForwardInstance - member_instance: ForwardInstance(resolver, optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - - member: ForwardInstance - member_instance: ForwardInstance(resolver, (), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, (), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, (), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, (), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - - member: ForwardInstance - member_instance: ForwardInstance(resolver, None, dict(initial_value=str(1)), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, None, dict(initial_value=str(1)), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, None, dict(initial_value=str(1)), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, None, dict(initial_value=str(1)), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - - member: ForwardInstance - member_instance: ForwardInstance(resolver, kwargs=dict(initial_value=str(1)), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, kwargs=dict(initial_value=str(1)), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, kwargs=dict(initial_value=str(1)), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, kwargs=dict(initial_value=str(1)), optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - - member: ForwardInstance - member_instance: ForwardInstance(resolver, factory=g, optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver1, factory=g, optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO] - member_value_type: _io.StringIO - - member: ForwardInstance - member_instance: ForwardInstance(resolver2, factory=g, optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str] - member_value_type: _io.StringIO | builtins.str - - member: ForwardInstance - member_instance: ForwardInstance(resolver3, factory=g, optional=False) - member_type: atom.instance.ForwardInstance[_io.StringIO | builtins.str | builtins.bytes] - member_value_type: _io.StringIO | builtins.str | builtins.bytes - main: | - import _io - from typing import List, Tuple, Type - from atom.api import Atom, {{ member }} - - def resolver() -> Type[_io.StringIO]: - return _io.StringIO - - def resolver1() -> Tuple[Type[_io.StringIO]]: - return (_io.StringIO,) - - def resolver2() -> Tuple[Type[_io.StringIO], Type[str]]: - return (_io.StringIO, str) - - def resolver3() -> Tuple[Type[_io.StringIO], Type[str], Type[bytes]]: - return (_io.StringIO, str, bytes) - - def g() -> _io.StringIO: - return _io.StringIO() - - - class A(Atom): - m = {{ member_instance }} - - reveal_type(A.m) # N: Revealed type is "{{ member_type }}" - reveal_type(A().m) # N: Revealed type is "{{ member_value_type }}" diff --git a/tests/type_checking/tuple_checks.py b/tests/type_checking/tuple_checks.py new file mode 100644 index 00000000..a5e0e892 --- /dev/null +++ b/tests/type_checking/tuple_checks.py @@ -0,0 +1,88 @@ +from typing import Any + +from typing_extensions import assert_type + +from atom.api import Atom, FixedTuple, Int, Tuple + + +# Tuple members keep the descriptor on the class and a concrete tuple on the instance. +# The classes below cover the main inference variants for dynamic tuples and fixed-length +# tuples without relying on generated fixture metadata. +class ANoDefault(Atom): + # An untyped tuple member accepts any tuple element and leaves the descriptor generic. + m = Tuple() + + +class AScalarTuple(Atom): + # A scalar type narrows tuple element inference while the instance still stores a real + # tuple of the matching element type. + m = Tuple(int) + + +class AUnionTuple(Atom): + # Tuple-based unions widen the element type to the union of all accepted values. + m = Tuple((int, float, str)) + + +class AMemberTuple(Atom): + # A member value should resolve to the same concrete type as the member itself. + m = Tuple(Int()) + + +class ADefaultAny(Atom): + # Any default tuple should not force the descriptor to a narrower type when no element + # type is declared. + m = Tuple(default=(1,)) + + +class ADefaultTyped(Atom): + # An explicit element type plus a default keeps the descriptor narrowed and preserves + # the instance tuple type. + m = Tuple(int, default=(3,)) + + +class ADefaultUnion(Atom): + # Defaults that contain valid instances from multiple tuple members should widen to the + # full union accepted by the descriptor. + m = Tuple((int, str), default=(1, str(1))) + + +class ADefaultBytesUnion(Atom): + # Three-way unions follow the same inference pattern as two-way unions, with bytes + # included in the accepted type set. + m = Tuple((int, str, bytes), default=(1, str(1), bytes(1))) + + +class AFixed(Atom): + # FixedTuple records the exact element shape on the descriptor and the concrete tuple + # type on the instance. + m = FixedTuple(int, float) + + +class AFixedDefault(Atom): + # A fixed tuple with a default still exposes the exact fixed-length tuple descriptor and + # the runtime tuple value type. + m = FixedTuple(int, default=(3,)) + + +assert_type(ANoDefault.m, Tuple[Any]) +assert_type(ANoDefault().m, tuple[Any, ...]) +assert_type(AScalarTuple.m, Tuple[int]) +assert_type(AScalarTuple().m, tuple[int, ...]) +assert_type(AUnionTuple.m, Tuple[int | float | str]) +assert_type(AUnionTuple().m, tuple[int | float | str, ...]) +assert_type(AMemberTuple.m, Tuple[int]) +assert_type(AMemberTuple().m, tuple[int, ...]) +assert_type(ADefaultAny.m, Tuple[Any]) +assert_type(ADefaultAny().m, tuple[Any, ...]) +assert_type(ADefaultTyped.m, Tuple[int]) +assert_type(ADefaultTyped().m, tuple[int, ...]) +assert_type(ADefaultUnion.m, Tuple[int | str]) +assert_type(ADefaultUnion().m, tuple[int | str, ...]) +assert_type(ADefaultBytesUnion.m, Tuple[int | str | bytes]) +assert_type(ADefaultBytesUnion().m, tuple[int | str | bytes, ...]) +assert_type(AFixed.m, FixedTuple[tuple[int, float]]) +assert_type(AFixed().m, tuple[int, float]) +assert_type(AFixedDefault.m, FixedTuple[tuple[int]]) +assert_type(AFixedDefault().m, tuple[int]) +assert_type(Tuple(Int()), Tuple[int]) diff --git a/tests/type_checking/typed_instance_checks.py b/tests/type_checking/typed_instance_checks.py new file mode 100644 index 00000000..5aaa6531 --- /dev/null +++ b/tests/type_checking/typed_instance_checks.py @@ -0,0 +1,77 @@ +import _io + +from typing_extensions import assert_type + +from atom.api import Atom, ForwardInstance, ForwardTyped, Instance, Int, List, Typed + + +def resolver() -> type[_io.StringIO]: + return _io.StringIO + + +def resolver1() -> type[_io.StringIO]: + return _io.StringIO + + +def resolver2() -> type[_io.StringIO] | type[str]: + return _io.StringIO + + +def resolver3() -> type[_io.StringIO] | type[str] | type[bytes]: + return _io.StringIO + + +def g() -> _io.StringIO: + return _io.StringIO() + + +# Typed members infer the descriptor type from their constructor arguments while the +# instance values are the concrete runtime types. +class ATypedDefault(Atom): + m = Typed(int) + + +class AForwardTypedDefault(Atom): + m = ForwardTyped(resolver) + + +class AInstanceDefault(Atom): + m = Instance((_io.StringIO, str), kwargs=dict(initial_value=str(1))) + + +class AForwardInstanceDefault(Atom): + m = ForwardInstance(resolver2) + + +assert_type(ATypedDefault.m, Typed[int | None]) +assert_type(ATypedDefault().m, int | None) +assert_type(AForwardTypedDefault.m, ForwardTyped[_io.StringIO | None]) +assert_type(AForwardTypedDefault().m, _io.StringIO | None) +assert_type(AInstanceDefault.m, Instance[_io.StringIO | str]) +assert_type(AInstanceDefault().m, _io.StringIO | str) +assert_type(AForwardInstanceDefault.m, ForwardInstance[_io.StringIO | str | None]) +assert_type(AForwardInstanceDefault().m, _io.StringIO | str | None) + + +class AListTyped(Atom): + m = Typed(List[int], optional=False) + + +assert_type(AListTyped.m, Typed[List[int]]) +assert_type(AListTyped().m, List[int]) + + +class AOptionalTyped(Atom): + m = Typed(int, optional=True) + + +assert_type(AOptionalTyped.m, Typed[int | None]) +assert_type(AOptionalTyped().m, int | None) + + +class AOptionalInstance(Atom): + m = Instance(int, optional=True) + + +assert_type(AOptionalInstance.m, Instance[int | None]) +assert_type(AOptionalInstance().m, int | None) From c170d17cd22aaad51ffc0f1cc5a4a22d400fcb9e Mon Sep 17 00:00:00 2001 From: mdartiailh Date: Thu, 17 Sep 2026 14:16:01 +0200 Subject: [PATCH 2/7] update testing logic for new schema --- .github/workflows/ci.yml | 10 +++------- test_requirements.txt | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae5958d4..75120687 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,14 +77,10 @@ jobs: - name: Install project run: | pip install . - - name: Run Mypy + - name: Validate static assertions run: | - pip install mypy pytest - mypy atom - - name: Test with pytest - run: | - pip install pytest-mypy-plugins regex - python -X dev -m pytest tests/type_checking -v + pip install -r test_requirements.txt + python -X dev -m mypy tests/type_checking --config-file pyproject.toml tests: name: Unit tests runs-on: ${{ matrix.os }} diff --git a/test_requirements.txt b/test_requirements.txt index 74280bad..f4fadecd 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,5 +1,5 @@ +mypy pytest pytest-cov -pytest-mypy-plugins pytest-benchmark psutil From a42b30da60eeda1fd22e26cacf42c3fa80375270 Mon Sep 17 00:00:00 2001 From: mdartiailh Date: Thu, 17 Sep 2026 15:09:06 +0200 Subject: [PATCH 3/7] fix Member.__init__ signature --- atom/catom.pyi | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/atom/catom.pyi b/atom/catom.pyi index 6b8bd4d6..c3d7cdb9 100644 --- a/atom/catom.pyi +++ b/atom/catom.pyi @@ -15,6 +15,7 @@ from typing import ( Literal, Optional, Sequence, + Self, Set, Tuple, Type, @@ -22,8 +23,6 @@ from typing import ( overload, ) -from typing_extensions import Self - from .atom import Atom from .property import Property from .typing_utils import ChangeDict @@ -76,7 +75,12 @@ class Member(Generic[T, S]): setattr_mode: Tuple[SetAttr, Any] = ... validate_mode: Tuple[Validate, Any] = ... getstate_mode: Tuple[GetState, Any] = ... - def __init__(self) -> None: ... + # Runtime constructors normalize a broad set of keyword and positional arguments + # before delegating to the validation machinery. The public stubs intentionally keep + # this initializer permissive while the specialized __new__ overloads capture the + # type-level API contract. This avoids duplicating the runtime normalization logic in + # the .pyi files while still allowing the checker to validate the supported calls. + def __init__(self, *args: Any, **kwargs: Any) -> None: ... @overload def __get__(self, instance: None, owner: Type[Atom]) -> Self: ... @overload From e4ffcb158f3baa91fd666cf6ad9dd59994f39529 Mon Sep 17 00:00:00 2001 From: mdartiailh Date: Thu, 17 Sep 2026 15:12:15 +0200 Subject: [PATCH 4/7] bump minimum Python version to 3.11 and test stubs against mypy, ty and pyrefly --- .github/workflows/ci.yml | 6 ++-- .readthedocs.yaml | 2 +- atom/tuple.pyi | 3 +- pyproject.toml | 4 +-- test_requirements.txt | 2 ++ tests/type_checking/annotations_checks.py | 2 +- tests/type_checking/coerced_checks.py | 2 +- tests/type_checking/delegator_checks.py | 2 +- tests/type_checking/dict_checks.py | 2 +- tests/type_checking/enum_checks.py | 2 +- tests/type_checking/event_checks.py | 2 +- tests/type_checking/generic_alias_checks.py | 2 +- tests/type_checking/list_checks.py | 2 +- tests/type_checking/property_checks.py | 4 +-- tests/type_checking/scalar_checks.py | 2 +- tests/type_checking/set_checks.py | 2 +- tests/type_checking/signal_checks.py | 2 +- tests/type_checking/subclass_checks.py | 37 ++++++++++++++------ tests/type_checking/tuple_checks.py | 2 +- tests/type_checking/typed_instance_checks.py | 2 +- 20 files changed, 50 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75120687..813bb633 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,7 +60,7 @@ jobs: if: needs.lint.result == 'success' strategy: matrix: - python-version: ['3.10'] + python-version: ['3.11'] steps: - uses: actions/checkout@v5 - name: Get history and tags for SCM versioning to work @@ -81,6 +81,8 @@ jobs: run: | pip install -r test_requirements.txt python -X dev -m mypy tests/type_checking --config-file pyproject.toml + ty check tests/type_checking + python -X dev -m pyrefly check tests/type_checking tests: name: Unit tests runs-on: ${{ matrix.os }} @@ -90,7 +92,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.10', '3.11', '3.12', '3.13', '3.14', '3.15-dev'] + python-version: ['3.11', '3.12', '3.13', '3.14', '3.15-dev'] steps: - uses: actions/checkout@v5 - name: Get history and tags for SCM versioning to work diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 11f893a5..976c35f1 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,7 +9,7 @@ version: 2 build: os: ubuntu-20.04 tools: - python: "3.10" + python: "3.11" apt_packages: - graphviz diff --git a/atom/tuple.pyi b/atom/tuple.pyi index 52cfb6ae..418e04b2 100644 --- a/atom/tuple.pyi +++ b/atom/tuple.pyi @@ -12,11 +12,10 @@ from typing import ( Type, TypeVar, Union, + Unpack, overload, ) -from typing_extensions import Unpack - from .catom import Member T = TypeVar("T") diff --git a/pyproject.toml b/pyproject.toml index 19ab1d4c..1800bf4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ name = "atom" description = "Memory efficient Python objects" readme = "README.rst" - requires-python = ">=3.10" + requires-python = ">=3.11" license = "BSD-3-Clause" license-files = ["LICENSE"] authors = [{ name = "The Nucleic Development Team", email = "sccolbert@gmail.com" }] @@ -18,7 +18,6 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", @@ -26,7 +25,6 @@ "Programming Language :: Python :: 3.15", "Programming Language :: Python :: Implementation :: CPython", ] - dependencies = ["typing_extensions;python_version<'3.11'"] dynamic = ["version"] [project.urls] diff --git a/test_requirements.txt b/test_requirements.txt index f4fadecd..d647248c 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,4 +1,6 @@ mypy +pyrefly +ty pytest pytest-cov pytest-benchmark diff --git a/tests/type_checking/annotations_checks.py b/tests/type_checking/annotations_checks.py index acc83502..21d5e090 100644 --- a/tests/type_checking/annotations_checks.py +++ b/tests/type_checking/annotations_checks.py @@ -1,4 +1,4 @@ -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, List diff --git a/tests/type_checking/coerced_checks.py b/tests/type_checking/coerced_checks.py index f79ce2d4..690296ce 100644 --- a/tests/type_checking/coerced_checks.py +++ b/tests/type_checking/coerced_checks.py @@ -1,6 +1,6 @@ import _io -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, Coerced diff --git a/tests/type_checking/delegator_checks.py b/tests/type_checking/delegator_checks.py index 365f043b..bd39be4e 100644 --- a/tests/type_checking/delegator_checks.py +++ b/tests/type_checking/delegator_checks.py @@ -1,4 +1,4 @@ -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, Delegator, Int diff --git a/tests/type_checking/dict_checks.py b/tests/type_checking/dict_checks.py index d8ab5849..daae2420 100644 --- a/tests/type_checking/dict_checks.py +++ b/tests/type_checking/dict_checks.py @@ -1,6 +1,6 @@ from typing import Any -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, Dict, Int diff --git a/tests/type_checking/enum_checks.py b/tests/type_checking/enum_checks.py index 2a89cf4a..31b4e298 100644 --- a/tests/type_checking/enum_checks.py +++ b/tests/type_checking/enum_checks.py @@ -1,4 +1,4 @@ -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, Enum diff --git a/tests/type_checking/event_checks.py b/tests/type_checking/event_checks.py index 5ae728e4..af46a8f1 100644 --- a/tests/type_checking/event_checks.py +++ b/tests/type_checking/event_checks.py @@ -1,4 +1,4 @@ -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, Event from atom.catom import EventBinder diff --git a/tests/type_checking/generic_alias_checks.py b/tests/type_checking/generic_alias_checks.py index 31ae40a8..6c7e3bcd 100644 --- a/tests/type_checking/generic_alias_checks.py +++ b/tests/type_checking/generic_alias_checks.py @@ -1,4 +1,4 @@ -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, ForwardInstance, ForwardTyped, Instance, Member, Typed diff --git a/tests/type_checking/list_checks.py b/tests/type_checking/list_checks.py index 39bbba2b..5902f9ec 100644 --- a/tests/type_checking/list_checks.py +++ b/tests/type_checking/list_checks.py @@ -1,6 +1,6 @@ from typing import Any -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, ContainerList, Int, List diff --git a/tests/type_checking/property_checks.py b/tests/type_checking/property_checks.py index c9b182d6..4bc56a5e 100644 --- a/tests/type_checking/property_checks.py +++ b/tests/type_checking/property_checks.py @@ -1,6 +1,4 @@ -from typing import Never, Union - -from typing_extensions import assert_type +from typing import Never, Union, assert_type from atom.api import Atom, Property, cached_property diff --git a/tests/type_checking/scalar_checks.py b/tests/type_checking/scalar_checks.py index 1ab00cf5..62edb1f1 100644 --- a/tests/type_checking/scalar_checks.py +++ b/tests/type_checking/scalar_checks.py @@ -1,6 +1,6 @@ from typing import Any -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, Bool, Bytes, Constant, Float, FloatRange, Int, Range, ReadOnly, Str, Value diff --git a/tests/type_checking/set_checks.py b/tests/type_checking/set_checks.py index 0d306d6a..6afed017 100644 --- a/tests/type_checking/set_checks.py +++ b/tests/type_checking/set_checks.py @@ -1,6 +1,6 @@ from typing import Any -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, Int, Set diff --git a/tests/type_checking/signal_checks.py b/tests/type_checking/signal_checks.py index bb59d3fe..3b654d5d 100644 --- a/tests/type_checking/signal_checks.py +++ b/tests/type_checking/signal_checks.py @@ -1,4 +1,4 @@ -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, Signal from atom.catom import SignalConnector diff --git a/tests/type_checking/subclass_checks.py b/tests/type_checking/subclass_checks.py index 3398d5ce..f4aeb160 100644 --- a/tests/type_checking/subclass_checks.py +++ b/tests/type_checking/subclass_checks.py @@ -1,6 +1,4 @@ -from typing import Type - -from typing_extensions import assert_type +from typing import Type, assert_type from atom.api import Atom, ForwardSubclass, Subclass @@ -19,16 +17,19 @@ class T(Atom): m = Subclass(A) -assert_type(T.m, Subclass[type[A]]) -assert_type(T().m, type[A]) +# ty treats the class-level descriptor and instance-level value as a stricter +# `type[type[...]]` exactness boundary than mypy/pyrefly, so the semantic checks are +# intentionally ignored for ty until the upstream checker matches the runtime model. +assert_type(T.m, Subclass[type[A]]) # ty: ignore[type-assertion-failure] +assert_type(T().m, type[A]) # ty: ignore[type-assertion-failure] class T2(Atom): m = Subclass((int, A), B) -assert_type(T2.m, Subclass[type[int] | type[A]]) -assert_type(T2().m, type[int] | type[A]) +assert_type(T2.m, Subclass[type[int | A]]) # ty: ignore[type-assertion-failure] +assert_type(T2().m, type[int | A]) # ty: ignore[type-assertion-failure] def resolve1() -> Type[A]: @@ -39,13 +40,29 @@ def resolve2() -> tuple[Type[A]]: return (A,) +class T3(Atom): + m = ForwardSubclass(resolve1) + + +assert_type(T3.m, ForwardSubclass[type[A]]) # ty: ignore[type-assertion-failure] +assert_type(T3().m, type[A]) # ty: ignore[type-assertion-failure] + + +class T4(Atom): + m = ForwardSubclass(resolve2) + + +assert_type(T4.m, ForwardSubclass[type[A]]) # ty: ignore[type-assertion-failure] +assert_type(T4().m, type[A]) # ty: ignore[type-assertion-failure] + + def resolve3() -> tuple[Type[int], Type[A]]: return (int, A) -class T3(Atom): +class T5(Atom): m = ForwardSubclass(resolve3) -assert_type(T3.m, ForwardSubclass[type[int] | type[A]]) -assert_type(T3().m, type[int] | type[A]) +assert_type(T5.m, ForwardSubclass[type[int | A]]) # ty: ignore[type-assertion-failure] +assert_type(T5().m, type[int | A]) # ty: ignore[type-assertion-failure] diff --git a/tests/type_checking/tuple_checks.py b/tests/type_checking/tuple_checks.py index a5e0e892..319d26e1 100644 --- a/tests/type_checking/tuple_checks.py +++ b/tests/type_checking/tuple_checks.py @@ -1,6 +1,6 @@ from typing import Any -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, FixedTuple, Int, Tuple diff --git a/tests/type_checking/typed_instance_checks.py b/tests/type_checking/typed_instance_checks.py index 5aaa6531..3ed6bcac 100644 --- a/tests/type_checking/typed_instance_checks.py +++ b/tests/type_checking/typed_instance_checks.py @@ -1,6 +1,6 @@ import _io -from typing_extensions import assert_type +from typing import assert_type from atom.api import Atom, ForwardInstance, ForwardTyped, Instance, Int, List, Typed From 65a208176b0474f73a0d6a59135bdd0e0f06b13b Mon Sep 17 00:00:00 2001 From: mdartiailh Date: Thu, 17 Sep 2026 15:17:04 +0200 Subject: [PATCH 5/7] cleanup C++ code --- atom/src/atomdict.cpp | 3 --- atom/src/catom.cpp | 3 --- atom/src/member.cpp | 3 --- 3 files changed, 9 deletions(-) diff --git a/atom/src/atomdict.cpp b/atom/src/atomdict.cpp index 7c48585c..2db924d8 100644 --- a/atom/src/atomdict.cpp +++ b/atom/src/atomdict.cpp @@ -103,10 +103,7 @@ int AtomDict_traverse( AtomDict* self, visitproc visit, void* arg ) { Py_VISIT( self->m_key_validator ); Py_VISIT( self->m_value_validator ); -#if PY_VERSION_HEX >= 0x03090000 - // This was not needed before Python 3.9 (Python issue 35810 and 40217) Py_VISIT(Py_TYPE(self)); -#endif // PyDict_type is not heap allocated so it does visit the type return PyDict_Type.tp_traverse( pyobject_cast( self ), visit, arg ); } diff --git a/atom/src/catom.cpp b/atom/src/catom.cpp index c7107590..539ea2ab 100644 --- a/atom/src/catom.cpp +++ b/atom/src/catom.cpp @@ -117,10 +117,7 @@ CAtom_traverse( CAtom* self, visitproc visit, void* arg ) { Py_VISIT( self->slots[ i ] ); } -#if PY_VERSION_HEX >= 0x03090000 - // This was not needed before Python 3.9 (Python issue 35810 and 40217) Py_VISIT(Py_TYPE(self)); -#endif if( self->observers ) { return self->observers->py_traverse( visit, arg ); diff --git a/atom/src/member.cpp b/atom/src/member.cpp index 31e09504..fca66907 100644 --- a/atom/src/member.cpp +++ b/atom/src/member.cpp @@ -86,10 +86,7 @@ Member_traverse( Member* self, visitproc visit, void* arg ) for( it = self->static_observers->begin(); it != end; ++it ) Py_VISIT( it->m_observer.get() ); } -#if PY_VERSION_HEX >= 0x03090000 - // This was not needed before Python 3.9 (Python issue 35810 and 40217) Py_VISIT(Py_TYPE(self)); -#endif return 0; } From 006339c20542af1303dd6e03fba4b92545ae5691 Mon Sep 17 00:00:00 2001 From: mdartiailh Date: Thu, 17 Sep 2026 15:30:30 +0200 Subject: [PATCH 6/7] linter fixes --- atom/catom.pyi | 2 +- tests/type_checking/coerced_checks.py | 3 +-- tests/type_checking/dict_checks.py | 4 +--- tests/type_checking/enum_checks.py | 1 - tests/type_checking/generic_alias_checks.py | 2 +- tests/type_checking/list_checks.py | 4 +--- tests/type_checking/scalar_checks.py | 20 +++++++++++++++----- tests/type_checking/set_checks.py | 4 +--- tests/type_checking/tuple_checks.py | 4 +--- tests/type_checking/typed_instance_checks.py | 5 ++--- 10 files changed, 24 insertions(+), 25 deletions(-) diff --git a/atom/catom.pyi b/atom/catom.pyi index c3d7cdb9..d211a643 100644 --- a/atom/catom.pyi +++ b/atom/catom.pyi @@ -14,8 +14,8 @@ from typing import ( List, Literal, Optional, - Sequence, Self, + Sequence, Set, Tuple, Type, diff --git a/tests/type_checking/coerced_checks.py b/tests/type_checking/coerced_checks.py index 690296ce..b475a3f8 100644 --- a/tests/type_checking/coerced_checks.py +++ b/tests/type_checking/coerced_checks.py @@ -1,5 +1,4 @@ import _io - from typing import assert_type from atom.api import Atom, Coerced @@ -27,7 +26,7 @@ class ACoercedTuple(Atom): class ACoercedStringIO(Atom): # StringIO inputs with a default factory-like initialization should still resolve to the # concrete StringIO type on the instance. - m = Coerced(_io.StringIO, kwargs=dict(initial_value="1")) + m = Coerced(_io.StringIO, kwargs={"initial_value": "1"}) class ACoercedFactory(Atom): diff --git a/tests/type_checking/dict_checks.py b/tests/type_checking/dict_checks.py index daae2420..8764679a 100644 --- a/tests/type_checking/dict_checks.py +++ b/tests/type_checking/dict_checks.py @@ -1,6 +1,4 @@ -from typing import Any - -from typing import assert_type +from typing import Any, assert_type from atom.api import Atom, Dict, Int diff --git a/tests/type_checking/enum_checks.py b/tests/type_checking/enum_checks.py index 31b4e298..db780b33 100644 --- a/tests/type_checking/enum_checks.py +++ b/tests/type_checking/enum_checks.py @@ -2,7 +2,6 @@ from atom.api import Atom, Enum - # Enum descriptors hold the member kind on the class while the instance sees the # specific enum value; helper methods may widen the enum value union. e = Enum(1, 2) diff --git a/tests/type_checking/generic_alias_checks.py b/tests/type_checking/generic_alias_checks.py index 6c7e3bcd..f2a3ada1 100644 --- a/tests/type_checking/generic_alias_checks.py +++ b/tests/type_checking/generic_alias_checks.py @@ -1,6 +1,6 @@ from typing import assert_type -from atom.api import Atom, ForwardInstance, ForwardTyped, Instance, Member, Typed +from atom.api import Atom, ForwardTyped, Instance, Member, Typed # Generic aliases are accepted as raw type arguments and should infer the same diff --git a/tests/type_checking/list_checks.py b/tests/type_checking/list_checks.py index 5902f9ec..47d2054e 100644 --- a/tests/type_checking/list_checks.py +++ b/tests/type_checking/list_checks.py @@ -1,6 +1,4 @@ -from typing import Any - -from typing import assert_type +from typing import Any, assert_type from atom.api import Atom, ContainerList, Int, List diff --git a/tests/type_checking/scalar_checks.py b/tests/type_checking/scalar_checks.py index 62edb1f1..9c1dd24c 100644 --- a/tests/type_checking/scalar_checks.py +++ b/tests/type_checking/scalar_checks.py @@ -1,8 +1,18 @@ -from typing import Any - -from typing import assert_type - -from atom.api import Atom, Bool, Bytes, Constant, Float, FloatRange, Int, Range, ReadOnly, Str, Value +from typing import Any, assert_type + +from atom.api import ( + Atom, + Bool, + Bytes, + Constant, + Float, + FloatRange, + Int, + Range, + ReadOnly, + Str, + Value, +) def g() -> int: diff --git a/tests/type_checking/set_checks.py b/tests/type_checking/set_checks.py index 6afed017..9f7c7a3f 100644 --- a/tests/type_checking/set_checks.py +++ b/tests/type_checking/set_checks.py @@ -1,6 +1,4 @@ -from typing import Any - -from typing import assert_type +from typing import Any, assert_type from atom.api import Atom, Int, Set diff --git a/tests/type_checking/tuple_checks.py b/tests/type_checking/tuple_checks.py index 319d26e1..7de48218 100644 --- a/tests/type_checking/tuple_checks.py +++ b/tests/type_checking/tuple_checks.py @@ -1,6 +1,4 @@ -from typing import Any - -from typing import assert_type +from typing import Any, assert_type from atom.api import Atom, FixedTuple, Int, Tuple diff --git a/tests/type_checking/typed_instance_checks.py b/tests/type_checking/typed_instance_checks.py index 3ed6bcac..eb42dba4 100644 --- a/tests/type_checking/typed_instance_checks.py +++ b/tests/type_checking/typed_instance_checks.py @@ -1,8 +1,7 @@ import _io - from typing import assert_type -from atom.api import Atom, ForwardInstance, ForwardTyped, Instance, Int, List, Typed +from atom.api import Atom, ForwardInstance, ForwardTyped, Instance, List, Typed def resolver() -> type[_io.StringIO]: @@ -36,7 +35,7 @@ class AForwardTypedDefault(Atom): class AInstanceDefault(Atom): - m = Instance((_io.StringIO, str), kwargs=dict(initial_value=str(1))) + m = Instance((_io.StringIO, str), kwargs={"initial_value": str(1)}) class AForwardInstanceDefault(Atom): From da0c27eaf06a2c959237d55e329cafcb065e9c51 Mon Sep 17 00:00:00 2001 From: mdartiailh Date: Thu, 17 Sep 2026 15:32:44 +0200 Subject: [PATCH 7/7] formatting --- tests/type_checking/dict_checks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/type_checking/dict_checks.py b/tests/type_checking/dict_checks.py index 8764679a..3b6d000e 100644 --- a/tests/type_checking/dict_checks.py +++ b/tests/type_checking/dict_checks.py @@ -88,6 +88,7 @@ class AMemberKey(Atom): assert_type(AMemberKey.m, Dict[int, int | float]) assert_type(AMemberKey().m, dict[int, int | float]) + # The additional classes below exercise the single-element tuple variants and the member- # based key/value shorthand that are easy to miss in a manual suite. They are redundant in # meaning but important as coverage for the constructor overloads.