diff --git a/tests/test_type_view.py b/tests/test_type_view.py index 6e95059..c83c14b 100644 --- a/tests/test_type_view.py +++ b/tests/test_type_view.py @@ -278,3 +278,17 @@ def test_parsed_type_equality() -> None: assert TypeView(list[int]) != TypeView(list[str]) assert TypeView(list[str]) != TypeView(tuple[str]) assert TypeView(Optional[str]) == TypeView(Union[str, None]) + + +def test_tuple(): + assert TypeView(list[int]).is_tuple is False + assert TypeView(list[int]).is_variadic_tuple is False + + assert TypeView(tuple[int]).is_tuple is True + assert TypeView(tuple[int]).is_variadic_tuple is False + + assert TypeView(tuple[int, int]).is_tuple is True + assert TypeView(tuple[int, int]).is_variadic_tuple is False + + assert TypeView(tuple[int, ...]).is_tuple is True + assert TypeView(tuple[int, ...]).is_variadic_tuple is True diff --git a/type_lens/type_view.py b/type_lens/type_view.py index 868a93b..5bddf46 100644 --- a/type_lens/type_view.py +++ b/type_lens/type_view.py @@ -80,6 +80,15 @@ def is_tuple(self) -> bool: """Whether the annotation is a ``tuple`` or not.""" return self.is_subclass_of(tuple) + @property + def is_variadic_tuple(self) -> bool: + """Whether the annotation is a ``tuple`` **and** is of unbounded length. + + Tuples like `tuple[int, ...]` represent a list-like unbounded sequence + of a single type T. + """ + return self.is_tuple and len(self.args) == 2 and self.args[1] == ... + @property def is_type_var(self) -> bool: """Whether the annotation is a TypeVar or not."""