diff --git a/tests/test_atom_from_annotations.py b/tests/test_atom_from_annotations.py index e881e018..195c6fb6 100644 --- a/tests/test_atom_from_annotations.py +++ b/tests/test_atom_from_annotations.py @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------------ -# Copyright (c) 2021-2023, Nucleic Development Team. +# Copyright (c) 2021-2024, Nucleic Development Team. # # Distributed under the terms of the Modified BSD License. # @@ -35,6 +35,7 @@ Callable, DefaultDict, Dict, + FixedTuple, Float, Instance, Int, @@ -49,6 +50,8 @@ ) from atom.atom import set_default +# XXX fixed tuple from annotations + def test_ignore_annotations(): class A(Atom, use_annotations=False): @@ -202,10 +205,11 @@ class A(Atom, use_annotations=True): (TDict[int, int], Dict(Int(), Int()), 1), (TDefaultDict[int, int], DefaultDict(Int(), Int()), 1), (TTuple[int], Tuple(), 0), - (TTuple[int], Tuple(Int()), 1), + (TTuple[int], FixedTuple(Int()), 1), (TTuple[int, ...], Tuple(Int()), 1), - (TTuple[int, float], Tuple(), 1), - (TTuple[tuple, int], Tuple(), 1), + (TTuple[int, float], FixedTuple(Int(), Float()), 1), + (TTuple[tuple, int], FixedTuple(Tuple(), Int()), 1), + (TTuple[tuple[int, int], int], FixedTuple(FixedTuple(Int(), Int()), Int()), -1), ] + ( [ @@ -219,10 +223,15 @@ class A(Atom, use_annotations=True): (dict[int, int], Dict(Int(), Int()), 1), (defaultdict[int, int], DefaultDict(Int(), Int()), 1), (tuple[int], Tuple(), 0), - (tuple[int], Tuple(Int()), 1), + (tuple[int], FixedTuple(Int()), 1), (tuple[int, ...], Tuple(Int()), 1), - (tuple[int, float], Tuple(), 1), - (tuple[tuple, int], Tuple(), 1), + (tuple[int, float], FixedTuple(Int(), Float()), 1), + (tuple[tuple, int], FixedTuple(Tuple(), Int()), 1), + ( + tuple[tuple[int, int], int], + FixedTuple(FixedTuple(Int(), Int()), Int()), + -1, + ), ] if sys.version_info >= (3, 9) else [] @@ -236,6 +245,8 @@ class A(Atom, use_annotations=True, type_containers=depth): if depth == 0: if isinstance(member, Dict): assert A.a.validate_mode[1] == (None, None) + elif isinstance(member, FixedTuple): + assert A.a.items == () else: assert A.a.item is None else: @@ -252,6 +263,9 @@ class A(Atom, use_annotations=True, type_containers=depth): assert type(k) is type(mk) assert type(v) is type(mv) assert f(A()) == mf(A()) + elif isinstance(member, FixedTuple): + for v, mv in zip(A.a.validate_mode[1], member.validate_mode[1]): + assert type(v) is type(mv) else: assert type(A.a.item) is type(member.item) # noqa: E721 if isinstance(member.item, List): @@ -270,6 +284,7 @@ class A(Atom, use_annotations=True, type_containers=depth): (Union[Any, None], Value, None), (object, Value, 2), (TCallable, Callable, lambda x: x), + (TTuple[int], FixedTuple(Int()), (1,)), (TList, List, [1]), (TSet, Set, {1}), (TDict, Dict, {1: 2}), @@ -279,6 +294,7 @@ class A(Atom, use_annotations=True, type_containers=depth): ] + ( [ + (tuple[int], FixedTuple(Int()), (1,)), (list, List, [1]), (set, Set, {1}), (dict, Dict, {1: 2}), diff --git a/tests/test_typing_utils.py b/tests/test_typing_utils.py index d3f306af..3456f55b 100644 --- a/tests/test_typing_utils.py +++ b/tests/test_typing_utils.py @@ -9,7 +9,7 @@ import sys from collections.abc import Iterable -from typing import Dict, List, Optional, Set, TypeVar, Union +from typing import Dict, List, Optional, Set, Tuple, TypeVar, Union import pytest @@ -25,6 +25,7 @@ @pytest.mark.parametrize( "ty, outputs", [ + (Tuple[int], (tuple,)), (List[int], (list,)), (Dict[str, int], (dict,)), (Set[int], (set,)), @@ -35,6 +36,7 @@ ] + ( [ + (tuple[int], (tuple,)), (list[int], (list,)), (dict[str, int], (dict,)), (set[int], (set,)), diff --git a/tests/test_validators.py b/tests/test_validators.py index 80ed7959..87261f06 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -20,6 +20,7 @@ unicode_handler unicode_promote_handler tuple_handler +fixed_tuple_handler list_handler container_list_handler set_handler @@ -57,6 +58,7 @@ Dict, Enum, Event, + FixedTuple, Float, FloatRange, ForwardInstance, @@ -133,6 +135,10 @@ def c(x: object) -> int: (Tuple(int), [(1,)], [(1,)], [(1.0,), (None,)]), (Tuple(TSet[int]), [({1},)], [({1},)], [(1.0,), (None,)]), (Tuple(Optional[int]), [(1, None)], [(1, None)], [("",)]), + (FixedTuple(Int()), [(1,)], [(1,)], [(None,), (1, 2)]), + (FixedTuple(Int(), Str()), [(1, "")], [(1, "")], [(None,), (1, 2)]), + (FixedTuple(int), [(1,)], [(1,)], [(None,), (1, 2)]), + (FixedTuple(TSet[int]), [({1},)], [({1},)], [(None,), (1, 2)]), (List(), [[1]], [[1]], [(1,)]), (List(Int()), [[1]], [[1]], [[1.0]]), (List(float), [[1.0]], [[1.0]], [[1], [None]]), @@ -253,6 +259,7 @@ class MemberTest(Atom): [ (List(), "List", 1, "Member or None"), (Tuple(), "Tuple", 1, "Member or None"), + (FixedTuple(int), "FixedTuple", 1, "tuple of types or Members"), (ContainerList(), "ContainerList", 1, "Member or None"), (Set(), "Set", 1, "Member or None"), (Dict(), "Dict", 1, "2-tuple of Member or None"), diff --git a/tests/type_checking/test_tuple.yml b/tests/type_checking/test_tuple.yml index 8f854ab8..0ef5f1f4 100644 --- a/tests/type_checking/test_tuple.yml +++ b/tests/type_checking/test_tuple.yml @@ -98,3 +98,37 @@ 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[builtins.tuple[builtins.int]] + member_value_type: builtins.tuple[builtins.int] + - member: FixedTuple + member_instance: FixedTuple(int, float) + member_type: atom.tuple.FixedTuple[builtins.tuple[builtins.int, builtins.float]] + member_value_type: builtins.tuple[builtins.int, builtins.float] + - member: FixedTuple + member_instance: FixedTuple(int, float, str) + member_type: atom.tuple.Tuple[builtins.tuple[builtins.int, builtins.float, builtins.str]] + member_value_type: builtins.tuple[builtins.int, builtins.float, builtins.str] + - member: FixedTuple, Int + member_instance: FixedTuple(Int()) + member_type: atom.tuple.Tuple[builtins.tuple[builtins.int]] + member_value_type: builtins.tuple[builtins.int] + # Tuple with defaults + - member: FixedTuple + member_instance: FixedTuple(int, default=(3,)) + member_type: atom.tuple.FixedTuple[builtins.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 }}" +