"""Tests util.typing functions.""" from __future__ import annotations import dataclasses import sys import typing as t from collections import abc from contextvars import Context, ContextVar, Token from enum import Enum from numbers import Integral from struct import Struct from types import ( AsyncGeneratorType, BuiltinFunctionType, BuiltinMethodType, CellType, ClassMethodDescriptorType, CodeType, CoroutineType, FrameType, FunctionType, GeneratorType, GetSetDescriptorType, LambdaType, MappingProxyType, MemberDescriptorType, MethodDescriptorType, MethodType, MethodWrapperType, ModuleType, TracebackType, WrapperDescriptorType, ) from typing import ( Annotated, Any, Dict, ForwardRef, List, Literal, NewType, Optional, ParamSpec, Tuple, TypeVar, Union, ) from sphinx.ext.autodoc import mock from sphinx.util.typing import _INVALID_BUILTIN_CLASSES, restify, stringify_annotation class MyClass1: pass class MyClass2(MyClass1): __qualname__ = '' class MyEnum(Enum): a = 1 T = TypeVar('T') MyInt = NewType('MyInt', int) class MyList(List[T]): pass class BrokenType: __args__ = int @dataclasses.dataclass(frozen=True) class Gt: gt: float def test_restify(): assert restify(int) == ':py:class:`int`' assert restify(int, 'smart') == ':py:class:`int`' assert restify(str) == ':py:class:`str`' assert restify(str, 'smart') == ':py:class:`str`' assert restify(None) == ':py:obj:`None`' assert restify(None, 'smart') == ':py:obj:`None`' assert restify(Integral) == ':py:class:`numbers.Integral`' assert restify(Integral, 'smart') == ':py:class:`~numbers.Integral`' assert restify(Struct) == ':py:class:`struct.Struct`' assert restify(Struct, 'smart') == ':py:class:`~struct.Struct`' assert restify(TracebackType) == ':py:class:`types.TracebackType`' assert restify(TracebackType, 'smart') == ':py:class:`~types.TracebackType`' assert restify(Any) == ':py:obj:`~typing.Any`' assert restify(Any, 'smart') == ':py:obj:`~typing.Any`' assert restify('str') == 'str' assert restify('str', 'smart') == 'str' def test_is_invalid_builtin_class(): # if these tests start failing, it means that the __module__ # of one of these classes has changed, and _INVALID_BUILTIN_CLASSES # in sphinx.util.typing needs to be updated. assert _INVALID_BUILTIN_CLASSES.keys() == { Context, ContextVar, Token, Struct, AsyncGeneratorType, BuiltinFunctionType, BuiltinMethodType, CellType, ClassMethodDescriptorType, CodeType, CoroutineType, FrameType, FunctionType, GeneratorType, GetSetDescriptorType, LambdaType, MappingProxyType, MemberDescriptorType, MethodDescriptorType, MethodType, MethodWrapperType, ModuleType, TracebackType, WrapperDescriptorType, } assert Struct.__module__ == '_struct' assert AsyncGeneratorType.__module__ == 'builtins' assert BuiltinFunctionType.__module__ == 'builtins' assert BuiltinMethodType.__module__ == 'builtins' assert CellType.__module__ == 'builtins' assert ClassMethodDescriptorType.__module__ == 'builtins' assert CodeType.__module__ == 'builtins' assert CoroutineType.__module__ == 'builtins' assert FrameType.__module__ == 'builtins' assert FunctionType.__module__ == 'builtins' assert GeneratorType.__module__ == 'builtins' assert GetSetDescriptorType.__module__ == 'builtins' assert LambdaType.__module__ == 'builtins' assert MappingProxyType.__module__ == 'builtins' assert MemberDescriptorType.__module__ == 'builtins' assert MethodDescriptorType.__module__ == 'builtins' assert MethodType.__module__ == 'builtins' assert MethodWrapperType.__module__ == 'builtins' assert ModuleType.__module__ == 'builtins' assert TracebackType.__module__ == 'builtins' assert WrapperDescriptorType.__module__ == 'builtins' def test_restify_type_hints_containers(): assert restify(List) == ':py:class:`~typing.List`' assert restify(Dict) == ':py:class:`~typing.Dict`' assert restify(List[int]) == ':py:class:`~typing.List`\\ [:py:class:`int`]' assert restify(List[str]) == ':py:class:`~typing.List`\\ [:py:class:`str`]' assert restify(Dict[str, float]) == ( ':py:class:`~typing.Dict`\\ [:py:class:`str`, :py:class:`float`]' ) assert restify(Tuple[str, str, str]) == ( ':py:class:`~typing.Tuple`\\ ' '[:py:class:`str`, :py:class:`str`, ' ':py:class:`str`]' ) ann_rst = restify(Tuple[str, ...]) assert ann_rst == ':py:class:`~typing.Tuple`\\ [:py:class:`str`, ...]' assert restify(Tuple[()]) == ':py:class:`~typing.Tuple`' assert restify(List[Dict[str, Tuple]]) == ( ':py:class:`~typing.List`\\ ' '[:py:class:`~typing.Dict`\\ ' '[:py:class:`str`, :py:class:`~typing.Tuple`]]' ) assert restify(MyList[Tuple[int, int]]) == ( ':py:class:`tests.test_util.test_util_typing.MyList`\\ ' '[:py:class:`~typing.Tuple`\\ ' '[:py:class:`int`, :py:class:`int`]]' ) assert restify(t.Generator[None, None, None]) == ( ':py:class:`~typing.Generator`\\ ' '[:py:obj:`None`, :py:obj:`None`, ' ':py:obj:`None`]' ) assert restify(abc.Generator[None, None, None]) == ( ':py:class:`collections.abc.Generator`\\ ' '[:py:obj:`None`, :py:obj:`None`, ' ':py:obj:`None`]' ) assert restify(t.Iterator[None]) == ( ':py:class:`~typing.Iterator`\\ [:py:obj:`None`]' ) assert restify(abc.Iterator[None]) == ( ':py:class:`collections.abc.Iterator`\\ [:py:obj:`None`]' ) def test_restify_Annotated(): ann_rst = restify(Annotated[str, 'foo', 'bar']) assert ann_rst == ( ":py:class:`~typing.Annotated`\\ [:py:class:`str`, 'foo', 'bar']" ) ann_rst = restify(Annotated[str, 'foo', 'bar'], 'smart') assert ann_rst == ( ":py:class:`~typing.Annotated`\\ [:py:class:`str`, 'foo', 'bar']" ) ann_rst = restify(Annotated[float, Gt(-10.0)]) assert ann_rst == ( ':py:class:`~typing.Annotated`\\ [:py:class:`float`, :py:class:`tests.test_util.test_util_typing.Gt`\\ (gt=\\ -10.0)]' ) ann_rst = restify(Annotated[float, Gt(-10.0)], 'smart') assert ann_rst == ( ':py:class:`~typing.Annotated`\\ [:py:class:`float`, :py:class:`~tests.test_util.test_util_typing.Gt`\\ (gt=\\ -10.0)]' ) def test_restify_type_hints_Callable(): assert restify(t.Callable) == ':py:class:`~typing.Callable`' assert restify(t.Callable[[str], int]) == ( ':py:class:`~typing.Callable`\\ [[:py:class:`str`], :py:class:`int`]' ) assert restify(t.Callable[..., int]) == ( ':py:class:`~typing.Callable`\\ [[...], :py:class:`int`]' ) assert restify(abc.Callable) == ':py:class:`collections.abc.Callable`' assert restify(abc.Callable[[str], int]) == ( ':py:class:`collections.abc.Callable`\\ [[:py:class:`str`], :py:class:`int`]' ) assert restify(abc.Callable[..., int]) == ( ':py:class:`collections.abc.Callable`\\ [[...], :py:class:`int`]' ) def test_restify_type_hints_Union(): assert restify(Union[int]) == ':py:class:`int`' assert restify(Union[int, str]) == ':py:class:`int` | :py:class:`str`' assert restify(Optional[int]) == ':py:class:`int` | :py:obj:`None`' assert restify(Union[str, None]) == ':py:class:`str` | :py:obj:`None`' assert restify(Union[None, str]) == ':py:obj:`None` | :py:class:`str`' assert restify(Optional[str]) == ':py:class:`str` | :py:obj:`None`' assert restify(Union[int, str, None]) == ( ':py:class:`int` | :py:class:`str` | :py:obj:`None`' ) assert restify(Optional[Union[int, str]]) in { ':py:class:`str` | :py:class:`int` | :py:obj:`None`', ':py:class:`int` | :py:class:`str` | :py:obj:`None`', } assert restify(Union[int, Integral]) == ( ':py:class:`int` | :py:class:`numbers.Integral`' ) assert restify(Union[int, Integral], 'smart') == ( ':py:class:`int` | :py:class:`~numbers.Integral`' ) assert restify(Union[MyClass1, MyClass2]) == ( ':py:class:`tests.test_util.test_util_typing.MyClass1`' ' | :py:class:`tests.test_util.test_util_typing.`' ) assert restify(Union[MyClass1, MyClass2], 'smart') == ( ':py:class:`~tests.test_util.test_util_typing.MyClass1`' ' | :py:class:`~tests.test_util.test_util_typing.`' ) assert restify(Optional[Union[MyClass1, MyClass2]]) == ( ':py:class:`tests.test_util.test_util_typing.MyClass1`' ' | :py:class:`tests.test_util.test_util_typing.`' ' | :py:obj:`None`' ) assert restify(Optional[Union[MyClass1, MyClass2]], 'smart') == ( ':py:class:`~tests.test_util.test_util_typing.MyClass1`' ' | :py:class:`~tests.test_util.test_util_typing.`' ' | :py:obj:`None`' ) def test_restify_type_hints_typevars(): T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) T_contra = TypeVar('T_contra', contravariant=True) assert restify(T) == ':py:obj:`tests.test_util.test_util_typing.T`' assert restify(T, 'smart') == ':py:obj:`~tests.test_util.test_util_typing.T`' assert restify(T_co) == ':py:obj:`tests.test_util.test_util_typing.T_co`' assert restify(T_co, 'smart') == ':py:obj:`~tests.test_util.test_util_typing.T_co`' assert restify(T_contra) == ':py:obj:`tests.test_util.test_util_typing.T_contra`' ann_rst = restify(T_contra, 'smart') assert ann_rst == ':py:obj:`~tests.test_util.test_util_typing.T_contra`' ann_rst = restify(List[T]) assert ann_rst == ( ':py:class:`~typing.List`\\ [:py:obj:`tests.test_util.test_util_typing.T`]' ) ann_rst = restify(List[T], 'smart') assert ann_rst == ( ':py:class:`~typing.List`\\ [:py:obj:`~tests.test_util.test_util_typing.T`]' ) ann_rst = restify(list[T]) assert ann_rst == ( ':py:class:`list`\\ [:py:obj:`tests.test_util.test_util_typing.T`]' ) ann_rst = restify(list[T], 'smart') assert ann_rst == ( ':py:class:`list`\\ [:py:obj:`~tests.test_util.test_util_typing.T`]' ) assert restify(MyInt) == ':py:class:`tests.test_util.test_util_typing.MyInt`' ann_rst = restify(MyInt, 'smart') assert ann_rst == ':py:class:`~tests.test_util.test_util_typing.MyInt`' def test_restify_type_hints_custom_class(): assert restify(MyClass1) == ':py:class:`tests.test_util.test_util_typing.MyClass1`' ann_rst = restify(MyClass1, 'smart') assert ann_rst == ':py:class:`~tests.test_util.test_util_typing.MyClass1`' ann_rst = restify(MyClass2) assert ann_rst == ':py:class:`tests.test_util.test_util_typing.`' ann_rst = restify(MyClass2, 'smart') assert ann_rst == ':py:class:`~tests.test_util.test_util_typing.`' def test_restify_type_hints_alias(): MyStr = str MyTypingTuple = Tuple[str, str] MyTuple = tuple[str, str] assert restify(MyStr) == ':py:class:`str`' ann_rst = restify(MyTypingTuple) assert ann_rst == ':py:class:`~typing.Tuple`\\ [:py:class:`str`, :py:class:`str`]' assert restify(MyTuple) == ':py:class:`tuple`\\ [:py:class:`str`, :py:class:`str`]' def test_restify_type_ForwardRef(): assert restify(ForwardRef('MyInt')) == ':py:class:`MyInt`' assert ( restify(list[ForwardRef('MyInt')]) == ':py:class:`list`\\ [:py:class:`MyInt`]' ) ann_rst = restify(Tuple[dict[ForwardRef('MyInt'), str], list[List[int]]]) # type: ignore[attr-defined] assert ann_rst == ( ':py:class:`~typing.Tuple`\\ [:py:class:`dict`\\ [:py:class:`MyInt`, :py:class:`str`], :py:class:`list`\\ [:py:class:`~typing.List`\\ [:py:class:`int`]]]' ) def test_restify_type_Literal(): ann_rst = restify(Literal[1, '2', '\r']) assert ann_rst == ":py:obj:`~typing.Literal`\\ [1, '2', '\\r']" ann_rst = restify(Literal[MyEnum.a], 'fully-qualified-except-typing') assert ann_rst == ( ':py:obj:`~typing.Literal`\\ [:py:attr:`tests.test_util.test_util_typing.MyEnum.a`]' ) ann_rst = restify(Literal[MyEnum.a], 'smart') assert ann_rst == ( ':py:obj:`~typing.Literal`\\ [:py:attr:`~tests.test_util.test_util_typing.MyEnum.a`]' ) def test_restify_pep_585(): assert restify(list[str]) == ':py:class:`list`\\ [:py:class:`str`]' # type: ignore[attr-defined] ann_rst = restify(dict[str, str]) # type: ignore[attr-defined] assert ann_rst == ':py:class:`dict`\\ [:py:class:`str`, :py:class:`str`]' assert restify(tuple[str, ...]) == ':py:class:`tuple`\\ [:py:class:`str`, ...]' assert restify(tuple[str, str, str]) == ( ':py:class:`tuple`\\ [:py:class:`str`, :py:class:`str`, :py:class:`str`]' ) ann_rst = restify(dict[str, tuple[int, ...]]) # type: ignore[attr-defined] assert ann_rst == ( ':py:class:`dict`\\ ' '[:py:class:`str`, :py:class:`tuple`\\ ' '[:py:class:`int`, ...]]' ) assert restify(tuple[()]) == ':py:class:`tuple`\\ [()]' # Mix old typing with PEP 585 assert restify(List[dict[str, Tuple[str, ...]]]) == ( ':py:class:`~typing.List`\\ ' '[:py:class:`dict`\\ ' '[:py:class:`str`, :py:class:`~typing.Tuple`\\ ' '[:py:class:`str`, ...]]]' ) assert restify(tuple[MyList[list[int]], int]) == ( ':py:class:`tuple`\\ [' ':py:class:`tests.test_util.test_util_typing.MyList`\\ ' '[:py:class:`list`\\ [:py:class:`int`]], ' ':py:class:`int`]' ) def test_restify_Unpack(): from typing_extensions import Unpack as UnpackCompat class X(t.TypedDict): x: int y: int label: str # Unpack is considered as typing special form so we always have '~' if sys.version_info[:2] >= (3, 12): expect = r':py:obj:`~typing.Unpack`\ [:py:class:`X`]' assert restify(UnpackCompat['X'], 'fully-qualified-except-typing') == expect assert restify(UnpackCompat['X'], 'smart') == expect else: expect = r':py:obj:`~typing_extensions.Unpack`\ [:py:class:`X`]' assert restify(UnpackCompat['X'], 'fully-qualified-except-typing') == expect assert restify(UnpackCompat['X'], 'smart') == expect expect = r':py:obj:`~typing.Unpack`\ [:py:class:`X`]' assert restify(t.Unpack['X'], 'fully-qualified-except-typing') == expect assert restify(t.Unpack['X'], 'smart') == expect def test_restify_type_union_operator(): assert restify(int | None) == ':py:class:`int` | :py:obj:`None`' # type: ignore[attr-defined] assert restify(None | int) == ':py:obj:`None` | :py:class:`int`' # type: ignore[attr-defined] assert restify(int | str) == ':py:class:`int` | :py:class:`str`' # type: ignore[attr-defined] ann_rst = restify(int | str | None) # type: ignore[attr-defined] assert ann_rst == ':py:class:`int` | :py:class:`str` | :py:obj:`None`' def test_restify_broken_type_hints(): ann_rst = restify(BrokenType) assert ann_rst == ':py:class:`tests.test_util.test_util_typing.BrokenType`' ann_rst = restify(BrokenType, 'smart') assert ann_rst == ':py:class:`~tests.test_util.test_util_typing.BrokenType`' def test_restify_mock(): with mock(['unknown']): import unknown assert restify(unknown) == ':py:class:`unknown`' assert restify(unknown.secret.Class) == ':py:class:`unknown.secret.Class`' ann_rst = restify(unknown.secret.Class, 'smart') assert ann_rst == ':py:class:`~unknown.secret.Class`' def test_restify_type_hints_paramspec(): P = ParamSpec('P') assert restify(P) == ':py:obj:`tests.test_util.test_util_typing.P`' assert restify(P, 'smart') == ':py:obj:`~tests.test_util.test_util_typing.P`' assert restify(P.args) == 'P.args' assert restify(P.args, 'smart') == 'P.args' assert restify(P.kwargs) == 'P.kwargs' assert restify(P.kwargs, 'smart') == 'P.kwargs' def test_stringify_annotation(): assert stringify_annotation(int, 'fully-qualified-except-typing') == 'int' assert stringify_annotation(int, 'smart') == 'int' assert stringify_annotation(str, 'fully-qualified-except-typing') == 'str' assert stringify_annotation(str, 'smart') == 'str' assert stringify_annotation(None, 'fully-qualified-except-typing') == 'None' assert stringify_annotation(None, 'smart') == 'None' ann_str = stringify_annotation(Integral, 'fully-qualified-except-typing') assert ann_str == 'numbers.Integral' assert stringify_annotation(Integral, 'smart') == '~numbers.Integral' ann_str = stringify_annotation(Struct, 'fully-qualified-except-typing') assert ann_str == 'struct.Struct' assert stringify_annotation(Struct, 'smart') == '~struct.Struct' ann_str = stringify_annotation(TracebackType, 'fully-qualified-except-typing') assert ann_str == 'types.TracebackType' assert stringify_annotation(TracebackType, 'smart') == '~types.TracebackType' assert stringify_annotation(Any, 'fully-qualified-except-typing') == 'Any' assert stringify_annotation(Any, 'fully-qualified') == 'typing.Any' assert stringify_annotation(Any, 'smart') == '~typing.Any' def test_stringify_type_hints_containers(): assert stringify_annotation(List, 'fully-qualified-except-typing') == 'List' assert stringify_annotation(List, 'fully-qualified') == 'typing.List' assert stringify_annotation(List, 'smart') == '~typing.List' assert stringify_annotation(Dict, 'fully-qualified-except-typing') == 'Dict' assert stringify_annotation(Dict, 'fully-qualified') == 'typing.Dict' assert stringify_annotation(Dict, 'smart') == '~typing.Dict' ann_str = stringify_annotation(List[int], 'fully-qualified-except-typing') assert ann_str == 'List[int]' assert stringify_annotation(List[int], 'fully-qualified') == 'typing.List[int]' assert stringify_annotation(List[int], 'smart') == '~typing.List[int]' ann_str = stringify_annotation(List[str], 'fully-qualified-except-typing') assert ann_str == 'List[str]' assert stringify_annotation(List[str], 'fully-qualified') == 'typing.List[str]' assert stringify_annotation(List[str], 'smart') == '~typing.List[str]' ann_str = stringify_annotation(Dict[str, float], 'fully-qualified-except-typing') assert ann_str == 'Dict[str, float]' ann_str = stringify_annotation(Dict[str, float], 'fully-qualified') assert ann_str == 'typing.Dict[str, float]' assert stringify_annotation(Dict[str, float], 'smart') == '~typing.Dict[str, float]' ann_str = stringify_annotation( Tuple[str, str, str], 'fully-qualified-except-typing' ) assert ann_str == 'Tuple[str, str, str]' ann_str = stringify_annotation(Tuple[str, str, str], 'fully-qualified') assert ann_str == 'typing.Tuple[str, str, str]' ann_str = stringify_annotation(Tuple[str, str, str], 'smart') assert ann_str == '~typing.Tuple[str, str, str]' ann_str = stringify_annotation(Tuple[str, ...], 'fully-qualified-except-typing') assert ann_str == 'Tuple[str, ...]' ann_str = stringify_annotation(Tuple[str, ...], 'fully-qualified') assert ann_str == 'typing.Tuple[str, ...]' assert stringify_annotation(Tuple[str, ...], 'smart') == '~typing.Tuple[str, ...]' assert stringify_annotation(Tuple[()], 'fully-qualified-except-typing') == 'Tuple' assert stringify_annotation(Tuple[()], 'fully-qualified') == 'typing.Tuple' assert stringify_annotation(Tuple[()], 'smart') == '~typing.Tuple' ann_str = stringify_annotation( List[Dict[str, Tuple]], 'fully-qualified-except-typing' ) assert ann_str == 'List[Dict[str, Tuple]]' ann_str = stringify_annotation(List[Dict[str, Tuple]], 'fully-qualified') assert ann_str == 'typing.List[typing.Dict[str, typing.Tuple]]' ann_str = stringify_annotation(List[Dict[str, Tuple]], 'smart') assert ann_str == '~typing.List[~typing.Dict[str, ~typing.Tuple]]' ann_str = stringify_annotation( MyList[Tuple[int, int]], 'fully-qualified-except-typing' ) assert ann_str == 'tests.test_util.test_util_typing.MyList[Tuple[int, int]]' ann_str = stringify_annotation(MyList[Tuple[int, int]], 'fully-qualified') assert ann_str == ( 'tests.test_util.test_util_typing.MyList[typing.Tuple[int, int]]' ) ann_str = stringify_annotation(MyList[Tuple[int, int]], 'smart') assert ann_str == ( '~tests.test_util.test_util_typing.MyList[~typing.Tuple[int, int]]' ) ann_str = stringify_annotation( t.Generator[None, None, None], 'fully-qualified-except-typing' ) assert ann_str == 'Generator[None, None, None]' ann_str = stringify_annotation(t.Generator[None, None, None], 'fully-qualified') assert ann_str == 'typing.Generator[None, None, None]' ann_str = stringify_annotation(t.Generator[None, None, None], 'smart') assert ann_str == '~typing.Generator[None, None, None]' ann_str = stringify_annotation( abc.Generator[None, None, None], 'fully-qualified-except-typing' ) assert ann_str == 'collections.abc.Generator[None, None, None]' ann_str = stringify_annotation(abc.Generator[None, None, None], 'fully-qualified') assert ann_str == 'collections.abc.Generator[None, None, None]' ann_str = stringify_annotation(abc.Generator[None, None, None], 'smart') assert ann_str == '~collections.abc.Generator[None, None, None]' ann_str = stringify_annotation(t.Iterator[None], 'fully-qualified-except-typing') assert ann_str == 'Iterator[None]' ann_str = stringify_annotation(t.Iterator[None], 'fully-qualified') assert ann_str == 'typing.Iterator[None]' assert stringify_annotation(t.Iterator[None], 'smart') == '~typing.Iterator[None]' ann_str = stringify_annotation(abc.Iterator[None], 'fully-qualified-except-typing') assert ann_str == 'collections.abc.Iterator[None]' ann_str = stringify_annotation(abc.Iterator[None], 'fully-qualified') assert ann_str == 'collections.abc.Iterator[None]' ann_str = stringify_annotation(abc.Iterator[None], 'smart') assert ann_str == '~collections.abc.Iterator[None]' def test_stringify_type_hints_pep_585(): ann_str = stringify_annotation(list[int], 'fully-qualified-except-typing') assert ann_str == 'list[int]' assert stringify_annotation(list[int], 'smart') == 'list[int]' ann_str = stringify_annotation(list[str], 'fully-qualified-except-typing') assert ann_str == 'list[str]' assert stringify_annotation(list[str], 'smart') == 'list[str]' ann_str = stringify_annotation(dict[str, float], 'fully-qualified-except-typing') assert ann_str == 'dict[str, float]' assert stringify_annotation(dict[str, float], 'smart') == 'dict[str, float]' ann_str = stringify_annotation( tuple[str, str, str], 'fully-qualified-except-typing' ) assert ann_str == 'tuple[str, str, str]' assert stringify_annotation(tuple[str, str, str], 'smart') == 'tuple[str, str, str]' ann_str = stringify_annotation(tuple[str, ...], 'fully-qualified-except-typing') assert ann_str == 'tuple[str, ...]' assert stringify_annotation(tuple[str, ...], 'smart') == 'tuple[str, ...]' assert ( stringify_annotation(tuple[()], 'fully-qualified-except-typing') == 'tuple[()]' ) assert stringify_annotation(tuple[()], 'smart') == 'tuple[()]' ann_str = stringify_annotation( list[dict[str, tuple]], 'fully-qualified-except-typing' ) assert ann_str == 'list[dict[str, tuple]]' ann_str = stringify_annotation(list[dict[str, tuple]], 'smart') assert ann_str == 'list[dict[str, tuple]]' ann_str = stringify_annotation( MyList[tuple[int, int]], 'fully-qualified-except-typing' ) assert ann_str == 'tests.test_util.test_util_typing.MyList[tuple[int, int]]' ann_str = stringify_annotation(MyList[tuple[int, int]], 'fully-qualified') assert ann_str == 'tests.test_util.test_util_typing.MyList[tuple[int, int]]' ann_str = stringify_annotation(MyList[tuple[int, int]], 'smart') assert ann_str == '~tests.test_util.test_util_typing.MyList[tuple[int, int]]' ann_str = stringify_annotation(type[int], 'fully-qualified-except-typing') assert ann_str == 'type[int]' assert stringify_annotation(type[int], 'smart') == 'type[int]' # Mix typing and pep 585 ann_str = stringify_annotation( tuple[List[dict[int, str]], str, ...], 'fully-qualified-except-typing' ) assert ann_str == 'tuple[List[dict[int, str]], str, ...]' ann_str = stringify_annotation(tuple[List[dict[int, str]], str, ...], 'smart') assert ann_str == 'tuple[~typing.List[dict[int, str]], str, ...]' def test_stringify_Annotated(): ann_str = stringify_annotation( Annotated[str, 'foo', 'bar'], 'fully-qualified-except-typing' ) assert ann_str == "Annotated[str, 'foo', 'bar']" ann_str = stringify_annotation(Annotated[str, 'foo', 'bar'], 'smart') assert ann_str == "~typing.Annotated[str, 'foo', 'bar']" ann_str = stringify_annotation( Annotated[float, Gt(-10.0)], 'fully-qualified-except-typing' ) assert ann_str == ( 'Annotated[float, tests.test_util.test_util_typing.Gt(gt=-10.0)]' ) ann_str = stringify_annotation(Annotated[float, Gt(-10.0)], 'smart') assert ann_str == ( '~typing.Annotated[float, ~tests.test_util.test_util_typing.Gt(gt=-10.0)]' ) def test_stringify_Unpack(): class X(t.TypedDict): x: int y: int label: str assert stringify_annotation(t.Unpack['X']) == 'Unpack[X]' assert stringify_annotation(t.Unpack['X'], 'smart') == '~typing.Unpack[X]' def test_stringify_type_hints_string(): assert stringify_annotation('int', 'fully-qualified-except-typing') == 'int' assert stringify_annotation('int', 'fully-qualified') == 'int' assert stringify_annotation('int', 'smart') == 'int' assert stringify_annotation('str', 'fully-qualified-except-typing') == 'str' assert stringify_annotation('str', 'fully-qualified') == 'str' assert stringify_annotation('str', 'smart') == 'str' ann_str = stringify_annotation(List['int'], 'fully-qualified-except-typing') assert ann_str == 'List[int]' assert stringify_annotation(List['int'], 'fully-qualified') == 'typing.List[int]' assert stringify_annotation(List['int'], 'smart') == '~typing.List[int]' ann_str = stringify_annotation(list['int'], 'fully-qualified-except-typing') assert ann_str == 'list[int]' assert stringify_annotation(list['int'], 'fully-qualified') == 'list[int]' assert stringify_annotation(list['int'], 'smart') == 'list[int]' ann_str = stringify_annotation('Tuple[str]', 'fully-qualified-except-typing') assert ann_str == 'Tuple[str]' assert stringify_annotation('Tuple[str]', 'fully-qualified') == 'Tuple[str]' assert stringify_annotation('Tuple[str]', 'smart') == 'Tuple[str]' ann_str = stringify_annotation('tuple[str]', 'fully-qualified-except-typing') assert ann_str == 'tuple[str]' assert stringify_annotation('tuple[str]', 'fully-qualified') == 'tuple[str]' assert stringify_annotation('tuple[str]', 'smart') == 'tuple[str]' assert stringify_annotation('unknown', 'fully-qualified-except-typing') == 'unknown' assert stringify_annotation('unknown', 'fully-qualified') == 'unknown' assert stringify_annotation('unknown', 'smart') == 'unknown' def test_stringify_type_hints_Callable(): ann_str = stringify_annotation(t.Callable, 'fully-qualified-except-typing') assert ann_str == 'Callable' assert stringify_annotation(t.Callable, 'fully-qualified') == 'typing.Callable' assert stringify_annotation(t.Callable, 'smart') == '~typing.Callable' ann_str = stringify_annotation( t.Callable[[str], int], 'fully-qualified-except-typing' ) assert ann_str == 'Callable[[str], int]' ann_str = stringify_annotation(t.Callable[[str], int], 'fully-qualified') assert ann_str == 'typing.Callable[[str], int]' ann_str = stringify_annotation(t.Callable[[str], int], 'smart') assert ann_str == '~typing.Callable[[str], int]' ann_str = stringify_annotation( t.Callable[..., int], 'fully-qualified-except-typing' ) assert ann_str == 'Callable[[...], int]' ann_str = stringify_annotation(t.Callable[..., int], 'fully-qualified') assert ann_str == 'typing.Callable[[...], int]' ann_str = stringify_annotation(t.Callable[..., int], 'smart') assert ann_str == '~typing.Callable[[...], int]' ann_str = stringify_annotation(abc.Callable, 'fully-qualified-except-typing') assert ann_str == 'collections.abc.Callable' ann_str = stringify_annotation(abc.Callable, 'fully-qualified') assert ann_str == 'collections.abc.Callable' assert stringify_annotation(abc.Callable, 'smart') == '~collections.abc.Callable' ann_str = stringify_annotation( abc.Callable[[str], int], 'fully-qualified-except-typing' ) assert ann_str == 'collections.abc.Callable[[str], int]' ann_str = stringify_annotation(abc.Callable[[str], int], 'fully-qualified') assert ann_str == 'collections.abc.Callable[[str], int]' ann_str = stringify_annotation(abc.Callable[[str], int], 'smart') assert ann_str == '~collections.abc.Callable[[str], int]' ann_str = stringify_annotation( abc.Callable[..., int], 'fully-qualified-except-typing' ) assert ann_str == 'collections.abc.Callable[[...], int]' ann_str = stringify_annotation(abc.Callable[..., int], 'fully-qualified') assert ann_str == 'collections.abc.Callable[[...], int]' ann_str = stringify_annotation(abc.Callable[..., int], 'smart') assert ann_str == '~collections.abc.Callable[[...], int]' def test_stringify_type_hints_Union(): ann_str = stringify_annotation(Optional[int], 'fully-qualified-except-typing') assert ann_str == 'int | None' assert stringify_annotation(Optional[int], 'fully-qualified') == 'int | None' assert stringify_annotation(Optional[int], 'smart') == 'int | None' ann_str = stringify_annotation(Union[int, None], 'fully-qualified-except-typing') assert ann_str == 'int | None' ann_str = stringify_annotation(Union[None, int], 'fully-qualified-except-typing') assert ann_str == 'None | int' assert stringify_annotation(Union[int, None], 'fully-qualified') == 'int | None' assert stringify_annotation(Union[None, int], 'fully-qualified') == 'None | int' assert stringify_annotation(Union[int, None], 'smart') == 'int | None' assert stringify_annotation(Union[None, int], 'smart') == 'None | int' ann_str = stringify_annotation(Union[int, str], 'fully-qualified-except-typing') assert ann_str == 'int | str' assert stringify_annotation(Union[int, str], 'fully-qualified') == 'int | str' assert stringify_annotation(Union[int, str], 'smart') == 'int | str' ann_str = stringify_annotation( Union[int, Integral], 'fully-qualified-except-typing' ) assert ann_str == 'int | numbers.Integral' ann_str = stringify_annotation(Union[int, Integral], 'fully-qualified') assert ann_str == 'int | numbers.Integral' ann_str = stringify_annotation(Union[int, Integral], 'smart') assert ann_str == 'int | ~numbers.Integral' ann_str = stringify_annotation( Union[MyClass1, MyClass2], 'fully-qualified-except-typing' ) assert ann_str == ( 'tests.test_util.test_util_typing.MyClass1 | tests.test_util.test_util_typing.' ) ann_str = stringify_annotation(Union[MyClass1, MyClass2], 'fully-qualified') assert ann_str == ( 'tests.test_util.test_util_typing.MyClass1 | tests.test_util.test_util_typing.' ) ann_str = stringify_annotation(Union[MyClass1, MyClass2], 'smart') assert ann_str == ( '~tests.test_util.test_util_typing.MyClass1 | ~tests.test_util.test_util_typing.' ) def test_stringify_type_hints_typevars(): T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) T_contra = TypeVar('T_contra', contravariant=True) ann_str = stringify_annotation(T, 'fully-qualified-except-typing') assert ann_str == 'tests.test_util.test_util_typing.T' assert stringify_annotation(T, 'smart') == '~tests.test_util.test_util_typing.T' ann_str = stringify_annotation(T_co, 'fully-qualified-except-typing') assert ann_str == 'tests.test_util.test_util_typing.T_co' ann_str = stringify_annotation(T_co, 'smart') assert ann_str == '~tests.test_util.test_util_typing.T_co' ann_str = stringify_annotation(T_contra, 'fully-qualified-except-typing') assert ann_str == 'tests.test_util.test_util_typing.T_contra' ann_str = stringify_annotation(T_contra, 'smart') assert ann_str == '~tests.test_util.test_util_typing.T_contra' ann_str = stringify_annotation(List[T], 'fully-qualified-except-typing') assert ann_str == 'List[tests.test_util.test_util_typing.T]' ann_str = stringify_annotation(List[T], 'smart') assert ann_str == '~typing.List[~tests.test_util.test_util_typing.T]' ann_str = stringify_annotation(list[T], 'fully-qualified-except-typing') assert ann_str == 'list[tests.test_util.test_util_typing.T]' ann_str = stringify_annotation(list[T], 'smart') assert ann_str == 'list[~tests.test_util.test_util_typing.T]' ann_str = stringify_annotation(MyInt, 'fully-qualified-except-typing') assert ann_str == 'tests.test_util.test_util_typing.MyInt' ann_str = stringify_annotation(MyInt, 'smart') assert ann_str == '~tests.test_util.test_util_typing.MyInt' def test_stringify_type_hints_custom_class(): ann_str = stringify_annotation(MyClass1, 'fully-qualified-except-typing') assert ann_str == 'tests.test_util.test_util_typing.MyClass1' ann_str = stringify_annotation(MyClass1, 'smart') assert ann_str == '~tests.test_util.test_util_typing.MyClass1' ann_str = stringify_annotation(MyClass2, 'fully-qualified-except-typing') assert ann_str == 'tests.test_util.test_util_typing.' ann_str = stringify_annotation(MyClass2, 'smart') assert ann_str == '~tests.test_util.test_util_typing.' def test_stringify_type_hints_alias(): MyStr = str MyTuple = Tuple[str, str] assert stringify_annotation(MyStr, 'fully-qualified-except-typing') == 'str' assert stringify_annotation(MyStr, 'smart') == 'str' assert stringify_annotation(MyTuple) == 'Tuple[str, str]' # type: ignore[attr-defined] assert stringify_annotation(MyTuple, 'smart') == '~typing.Tuple[str, str]' # type: ignore[attr-defined] def test_stringify_type_Literal(): ann_str = stringify_annotation( Literal[1, '2', '\r'], 'fully-qualified-except-typing' ) assert ann_str == "Literal[1, '2', '\\r']" ann_str = stringify_annotation(Literal[1, '2', '\r'], 'fully-qualified') assert ann_str == "typing.Literal[1, '2', '\\r']" ann_str = stringify_annotation(Literal[1, '2', '\r'], 'smart') assert ann_str == "~typing.Literal[1, '2', '\\r']" ann_str = stringify_annotation(Literal[MyEnum.a], 'fully-qualified-except-typing') assert ann_str == 'Literal[tests.test_util.test_util_typing.MyEnum.a]' ann_str = stringify_annotation(Literal[MyEnum.a], 'fully-qualified') assert ann_str == 'typing.Literal[tests.test_util.test_util_typing.MyEnum.a]' ann_str = stringify_annotation(Literal[MyEnum.a], 'smart') assert ann_str == '~typing.Literal[MyEnum.a]' def test_stringify_type_union_operator(): assert stringify_annotation(int | None) == 'int | None' # type: ignore[attr-defined] assert stringify_annotation(int | None, 'smart') == 'int | None' # type: ignore[attr-defined] assert stringify_annotation(int | str) == 'int | str' # type: ignore[attr-defined] assert stringify_annotation(int | str, 'smart') == 'int | str' # type: ignore[attr-defined] assert stringify_annotation(int | str | None) == 'int | str | None' # type: ignore[attr-defined] assert stringify_annotation(int | str | None, 'smart') == 'int | str | None' # type: ignore[attr-defined] ann_str = stringify_annotation( int | tuple[dict[str, int | None], list[int | str]] | None ) assert ann_str == 'int | tuple[dict[str, int | None], list[int | str]] | None' # type: ignore[attr-defined] ann_str = stringify_annotation( int | tuple[dict[str, int | None], list[int | str]] | None, 'smart' ) assert ann_str == 'int | tuple[dict[str, int | None], list[int | str]] | None' # type: ignore[attr-defined] assert stringify_annotation(int | Struct) == 'int | struct.Struct' # type: ignore[attr-defined] assert stringify_annotation(int | Struct, 'smart') == 'int | ~struct.Struct' # type: ignore[attr-defined] def test_stringify_broken_type_hints(): ann_str = stringify_annotation(BrokenType, 'fully-qualified-except-typing') assert ann_str == 'tests.test_util.test_util_typing.BrokenType' ann_str = stringify_annotation(BrokenType, 'smart') assert ann_str == '~tests.test_util.test_util_typing.BrokenType' def test_stringify_mock(): with mock(['unknown']): import unknown ann_str = stringify_annotation(unknown, 'fully-qualified-except-typing') assert ann_str == 'unknown' ann_str = stringify_annotation( unknown.secret.Class, 'fully-qualified-except-typing' ) assert ann_str == 'unknown.secret.Class' ann_str = stringify_annotation(unknown.secret.Class, 'smart') assert ann_str == 'unknown.secret.Class' def test_stringify_type_ForwardRef(): assert stringify_annotation(ForwardRef('MyInt')) == 'MyInt' assert stringify_annotation(ForwardRef('MyInt'), 'smart') == 'MyInt' assert stringify_annotation(list[ForwardRef('MyInt')]) == 'list[MyInt]' assert stringify_annotation(list[ForwardRef('MyInt')], 'smart') == 'list[MyInt]' ann_str = stringify_annotation( Tuple[dict[ForwardRef('MyInt'), str], list[List[int]]] ) assert ann_str == 'Tuple[dict[MyInt, str], list[List[int]]]' # type: ignore[attr-defined] ann_str = stringify_annotation( Tuple[dict[ForwardRef('MyInt'), str], list[List[int]]], 'fully-qualified-except-typing', ) assert ann_str == 'Tuple[dict[MyInt, str], list[List[int]]]' # type: ignore[attr-defined] ann_str = stringify_annotation( Tuple[dict[ForwardRef('MyInt'), str], list[List[int]]], 'smart' ) assert ann_str == '~typing.Tuple[dict[MyInt, str], list[~typing.List[int]]]' # type: ignore[attr-defined] def test_stringify_type_hints_paramspec(): P = ParamSpec('P') assert stringify_annotation(P, 'fully-qualified') == '~P' assert stringify_annotation(P, 'fully-qualified-except-typing') == '~P' assert stringify_annotation(P, 'smart') == '~P' assert stringify_annotation(P.args, 'fully-qualified') == 'typing.~P' assert stringify_annotation(P.args, 'fully-qualified-except-typing') == '~P' assert stringify_annotation(P.args, 'smart') == '~typing.~P' assert stringify_annotation(P.kwargs, 'fully-qualified') == 'typing.~P' assert stringify_annotation(P.kwargs, 'fully-qualified-except-typing') == '~P' assert stringify_annotation(P.kwargs, 'smart') == '~typing.~P'