diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 9beec4f2512..2b595fd2d5b 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -702,7 +702,7 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True, if show_annotation and param.annotation is not param.empty: arg.write(': ') - arg.write(stringify_annotation(param.annotation, unqualified_typehints)) + arg.write(stringify_annotation(param.annotation, unqualified_typehints, True)) if param.default is not param.empty: if show_annotation and param.annotation is not param.empty: arg.write(' = ') @@ -722,7 +722,7 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True, show_return_annotation is False): return '(%s)' % ', '.join(args) else: - annotation = stringify_annotation(sig.return_annotation, unqualified_typehints) + annotation = stringify_annotation(sig.return_annotation, unqualified_typehints, True) return '(%s) -> %s' % (', '.join(args), annotation) diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index 259384ec70f..d00c3ba09b7 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -299,11 +299,12 @@ def _restify_py36(cls: Optional[Type]) -> str: return ':py:obj:`%s.%s`' % (cls.__module__, qualname) -def stringify(annotation: Any, smartref: bool = False) -> str: +def stringify(annotation: Any, smartref: bool = False, show_typing: bool = False) -> str: """Stringify type annotation object. :param smartref: If true, add "~" prefix to the result to remove the leading module and class names from the reference text + :param show_typing: If true, do not suppress the "typing" module name """ from sphinx.util import inspect # lazy loading @@ -319,7 +320,7 @@ def stringify(annotation: Any, smartref: bool = False) -> str: else: return annotation elif isinstance(annotation, TypeVar): - if annotation.__module__ == 'typing': + if show_typing is False and annotation.__module__ == 'typing': return annotation.__name__ else: return prefix + '.'.join([annotation.__module__, annotation.__name__]) @@ -347,12 +348,12 @@ def stringify(annotation: Any, smartref: bool = False) -> str: return '...' if sys.version_info >= (3, 7): # py37+ - return _stringify_py37(annotation, smartref) + return _stringify_py37(annotation, smartref, show_typing) else: - return _stringify_py36(annotation, smartref) + return _stringify_py36(annotation, smartref, show_typing) -def _stringify_py37(annotation: Any, smartref: bool = False) -> str: +def _stringify_py37(annotation: Any, smartref: bool = False, show_typing: bool = False) -> str: """stringify() for py37+.""" module = getattr(annotation, '__module__', None) modprefix = '' @@ -364,10 +365,12 @@ def _stringify_py37(annotation: Any, smartref: bool = False) -> str: elif getattr(annotation, '__qualname__', None): qualname = annotation.__qualname__ else: - qualname = stringify(annotation.__origin__) # ex. Union + qualname = stringify(annotation.__origin__).replace('typing.', '') # ex. Union if smartref: modprefix = '~%s.' % module + elif show_typing: + modprefix = '%s.' % module elif hasattr(annotation, '__qualname__'): if smartref: modprefix = '~%s.' % module @@ -376,7 +379,7 @@ def _stringify_py37(annotation: Any, smartref: bool = False) -> str: qualname = annotation.__qualname__ elif hasattr(annotation, '__origin__'): # instantiated generic provided by a user - qualname = stringify(annotation.__origin__, smartref) + qualname = stringify(annotation.__origin__, smartref, show_typing) elif UnionType and isinstance(annotation, UnionType): # types.Union (for py3.10+) qualname = 'types.Union' else: @@ -391,13 +394,15 @@ def _stringify_py37(annotation: Any, smartref: bool = False) -> str: elif qualname in ('Optional', 'Union'): if len(annotation.__args__) > 1 and annotation.__args__[-1] is NoneType: if len(annotation.__args__) > 2: - args = ', '.join(stringify(a, smartref) for a in annotation.__args__[:-1]) + args = ', '.join(stringify(a, smartref, show_typing) for a + in annotation.__args__[:-1]) return '%sOptional[%sUnion[%s]]' % (modprefix, modprefix, args) else: - return '%sOptional[%s]' % (modprefix, - stringify(annotation.__args__[0], smartref)) + return '%sOptional[%s]' % (modprefix, stringify(annotation.__args__[0], + smartref, show_typing)) else: - args = ', '.join(stringify(a, smartref) for a in annotation.__args__) + args = ', '.join(stringify(a, smartref, show_typing) for a + in annotation.__args__) return '%sUnion[%s]' % (modprefix, args) elif qualname == 'types.Union': if len(annotation.__args__) > 1 and None in annotation.__args__: @@ -406,25 +411,27 @@ def _stringify_py37(annotation: Any, smartref: bool = False) -> str: else: return ' | '.join(stringify(a) for a in annotation.__args__) elif qualname == 'Callable': - args = ', '.join(stringify(a, smartref) for a in annotation.__args__[:-1]) - returns = stringify(annotation.__args__[-1], smartref) + args = ', '.join(stringify(a, smartref, show_typing) for a + in annotation.__args__[:-1]) + returns = stringify(annotation.__args__[-1], smartref, show_typing) return '%s%s[[%s], %s]' % (modprefix, qualname, args, returns) elif qualname == 'Literal': args = ', '.join(repr(a) for a in annotation.__args__) return '%s%s[%s]' % (modprefix, qualname, args) elif str(annotation).startswith('typing.Annotated'): # for py39+ - return stringify(annotation.__args__[0], smartref) + return stringify(annotation.__args__[0], smartref, show_typing) elif all(is_system_TypeVar(a) for a in annotation.__args__): # Suppress arguments if all system defined TypeVars (ex. Dict[KT, VT]) return modprefix + qualname else: - args = ', '.join(stringify(a, smartref) for a in annotation.__args__) + args = ', '.join(stringify(a, smartref, show_typing) for a + in annotation.__args__) return '%s%s[%s]' % (modprefix, qualname, args) return modprefix + qualname -def _stringify_py36(annotation: Any, smartref: bool = False) -> str: +def _stringify_py36(annotation: Any, smartref: bool = False, show_typing: bool = False) -> str: """stringify() for py36.""" module = getattr(annotation, '__module__', None) modprefix = '' @@ -442,6 +449,8 @@ def _stringify_py36(annotation: Any, smartref: bool = False) -> str: if smartref: modprefix = '~%s.' % module + elif show_typing: + modprefix = '%s.' % module elif hasattr(annotation, '__qualname__'): if smartref: modprefix = '~%s.' % module @@ -455,7 +464,7 @@ def _stringify_py36(annotation: Any, smartref: bool = False) -> str: not hasattr(annotation, '__tuple_params__')): # for Python 3.6 params = annotation.__args__ if params: - param_str = ', '.join(stringify(p, smartref) for p in params) + param_str = ', '.join(stringify(p, smartref, show_typing) for p in params) return '%s%s[%s]' % (modprefix, qualname, param_str) else: return modprefix + qualname @@ -466,12 +475,12 @@ def _stringify_py36(annotation: Any, smartref: bool = False) -> str: elif annotation.__origin__ == Generator: # type: ignore params = annotation.__args__ # type: ignore else: # typing.Callable - args = ', '.join(stringify(arg, smartref) for arg + args = ', '.join(stringify(arg, smartref, show_typing) for arg in annotation.__args__[:-1]) # type: ignore result = stringify(annotation.__args__[-1]) # type: ignore return '%s%s[[%s], %s]' % (modprefix, qualname, args, result) if params is not None: - param_str = ', '.join(stringify(p, smartref) for p in params) + param_str = ', '.join(stringify(p, smartref, show_typing) for p in params) return '%s%s[%s]' % (modprefix, qualname, param_str) elif (hasattr(annotation, '__origin__') and annotation.__origin__ is typing.Union): @@ -479,12 +488,13 @@ def _stringify_py36(annotation: Any, smartref: bool = False) -> str: if params is not None: if len(params) > 1 and params[-1] is NoneType: if len(params) > 2: - param_str = ", ".join(stringify(p, smartref) for p in params[:-1]) + param_str = ", ".join(stringify(p, smartref, show_typing) for p + in params[:-1]) return '%sOptional[%sUnion[%s]]' % (modprefix, modprefix, param_str) else: return '%sOptional[%s]' % (modprefix, stringify(params[0])) else: - param_str = ', '.join(stringify(p, smartref) for p in params) + param_str = ', '.join(stringify(p, smartref, show_typing) for p in params) return '%sUnion[%s]' % (modprefix, param_str) return modprefix + qualname diff --git a/tests/test_ext_autodoc_autofunction.py b/tests/test_ext_autodoc_autofunction.py index 52af51abbfc..e3db8ca0d7e 100644 --- a/tests/test_ext_autodoc_autofunction.py +++ b/tests/test_ext_autodoc_autofunction.py @@ -162,7 +162,7 @@ def test_wrapped_function_contextmanager(app): actual = do_autodoc(app, 'function', 'target.wrappedfunction.feeling_good') assert list(actual) == [ '', - '.. py:function:: feeling_good(x: int, y: int) -> Generator', + '.. py:function:: feeling_good(x: int, y: int) -> typing.Generator', ' :module: target.wrappedfunction', '', " You'll feel better in this context!", diff --git a/tests/test_ext_autodoc_automodule.py b/tests/test_ext_autodoc_automodule.py index 59296a981a3..1f4d590793f 100644 --- a/tests/test_ext_autodoc_automodule.py +++ b/tests/test_ext_autodoc_automodule.py @@ -130,4 +130,4 @@ def test_subclass_of_mocked_object(app): options = {'members': None} actual = do_autodoc(app, 'module', 'target.need_mocks', options) - assert '.. py:class:: Inherited(*args: Any, **kwargs: Any)' in actual + assert '.. py:class:: Inherited(*args: typing.Any, **kwargs: typing.Any)' in actual diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index f3bcd6a97a1..899bf9ff024 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -612,7 +612,7 @@ def test_autodoc_typehints_signature(app): ' :type: int', '', '', - '.. py:class:: Math(s: str, o: Optional[Any] = None)', + '.. py:class:: Math(s: str, o: typing.Optional[typing.Any] = None)', ' :module: target.typehints', '', '', @@ -677,7 +677,8 @@ def test_autodoc_typehints_signature(app): ' :module: target.typehints', '', '', - '.. py:function:: tuple_args(x: Tuple[int, Union[int, str]]) -> Tuple[int, int]', + '.. py:function:: tuple_args(x: typing.Tuple[int, typing.Union[int, str]]) ' + '-> typing.Tuple[int, int]', ' :module: target.typehints', '', ] diff --git a/tests/test_ext_autodoc_preserve_defaults.py b/tests/test_ext_autodoc_preserve_defaults.py index 955c60aa4bf..b9634eef871 100644 --- a/tests/test_ext_autodoc_preserve_defaults.py +++ b/tests/test_ext_autodoc_preserve_defaults.py @@ -36,15 +36,15 @@ def test_preserve_defaults(app): ' docstring', '', '', - ' .. py:method:: Class.meth(name: str = CONSTANT, sentinel: Any = SENTINEL, ' - 'now: datetime.datetime = datetime.now(), color: int = %s) -> None' % color, + ' .. py:method:: Class.meth(name: str = CONSTANT, sentinel: typing.Any = ' + 'SENTINEL, now: datetime.datetime = datetime.now(), color: int = %s) -> None' % color, ' :module: target.preserve_defaults', '', ' docstring', '', '', - '.. py:function:: foo(name: str = CONSTANT, sentinel: Any = SENTINEL, now: ' - 'datetime.datetime = datetime.now(), color: int = %s) -> None' % color, + '.. py:function:: foo(name: str = CONSTANT, sentinel: typing.Any = SENTINEL, ' + 'now: datetime.datetime = datetime.now(), color: int = %s) -> None' % color, ' :module: target.preserve_defaults', '', ' docstring', diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py index f331acb233e..25709a37f61 100644 --- a/tests/test_util_inspect.py +++ b/tests/test_util_inspect.py @@ -157,21 +157,22 @@ def test_signature_annotations(): # Generic types with concrete parameters sig = inspect.signature(f1) - assert stringify_signature(sig) == '(x: List[int]) -> List[int]' + assert stringify_signature(sig) == '(x: typing.List[int]) -> typing.List[int]' # TypeVars and generic types with TypeVars sig = inspect.signature(f2) if sys.version_info < (3, 7): - assert stringify_signature(sig) == '(x: List[T], y: List[T_co], z: T) -> List[T_contra]' + assert stringify_signature(sig) == ('(x: typing.List[T], y: typing.List[T_co], z: T) ' + '-> typing.List[T_contra]') else: - assert stringify_signature(sig) == ('(x: List[tests.typing_test_data.T],' - ' y: List[tests.typing_test_data.T_co],' + assert stringify_signature(sig) == ('(x: typing.List[tests.typing_test_data.T],' + ' y: typing.List[tests.typing_test_data.T_co],' ' z: tests.typing_test_data.T' - ') -> List[tests.typing_test_data.T_contra]') + ') -> typing.List[tests.typing_test_data.T_contra]') # Union types sig = inspect.signature(f3) - assert stringify_signature(sig) == '(x: Union[str, numbers.Integral]) -> None' + assert stringify_signature(sig) == '(x: typing.Union[str, numbers.Integral]) -> None' # Quoted annotations sig = inspect.signature(f4) @@ -187,18 +188,18 @@ def test_signature_annotations(): # Space around '=' for defaults sig = inspect.signature(f7) - assert stringify_signature(sig) == '(x: Optional[int] = None, y: dict = {}) -> None' + assert stringify_signature(sig) == '(x: typing.Optional[int] = None, y: dict = {}) -> None' # Callable types sig = inspect.signature(f8) - assert stringify_signature(sig) == '(x: Callable[[int, str], int]) -> None' + assert stringify_signature(sig) == '(x: typing.Callable[[int, str], int]) -> None' sig = inspect.signature(f9) - assert stringify_signature(sig) == '(x: Callable) -> None' + assert stringify_signature(sig) == '(x: typing.Callable) -> None' # Tuple types sig = inspect.signature(f10) - assert stringify_signature(sig) == '(x: Tuple[int, str], y: Tuple[int, ...]) -> None' + assert stringify_signature(sig) == '(x: typing.Tuple[int, str], y: typing.Tuple[int, ...]) -> None' # Instance annotations sig = inspect.signature(f11) @@ -206,24 +207,24 @@ def test_signature_annotations(): # tuple with more than two items sig = inspect.signature(f12) - assert stringify_signature(sig) == '() -> Tuple[int, str, int]' + assert stringify_signature(sig) == '() -> typing.Tuple[int, str, int]' # optional sig = inspect.signature(f13) - assert stringify_signature(sig) == '() -> Optional[str]' + assert stringify_signature(sig) == '() -> typing.Optional[str]' # optional union sig = inspect.signature(f20) - assert stringify_signature(sig) in ('() -> Optional[Union[int, str]]', - '() -> Optional[Union[str, int]]') + assert stringify_signature(sig) in ('() -> typing.Optional[typing.Union[int, str]]', + '() -> typing.Optional[typing.Union[str, int]]') # Any sig = inspect.signature(f14) - assert stringify_signature(sig) == '() -> Any' + assert stringify_signature(sig) == '() -> typing.Any' # ForwardRef sig = inspect.signature(f15) - assert stringify_signature(sig) == '(x: Unknown, y: int) -> Any' + assert stringify_signature(sig) == '(x: Unknown, y: int) -> typing.Any' # keyword only arguments (1) sig = inspect.signature(f16) @@ -234,7 +235,8 @@ def test_signature_annotations(): assert stringify_signature(sig) == '(*, arg3, arg4)' sig = inspect.signature(f18) - assert stringify_signature(sig) == '(self, arg1: Union[int, Tuple] = 10) -> List[Dict]' + assert stringify_signature(sig) == ('(self, arg1: typing.Union[int, typing.Tuple] = 10) -> ' + 'typing.List[typing.Dict]') # annotations for variadic and keyword parameters sig = inspect.signature(f19) @@ -246,10 +248,10 @@ def test_signature_annotations(): # type hints by string sig = inspect.signature(Node.children) - assert stringify_signature(sig) == '(self) -> List[tests.typing_test_data.Node]' + assert stringify_signature(sig) == '(self) -> typing.List[tests.typing_test_data.Node]' sig = inspect.signature(Node.__init__) - assert stringify_signature(sig) == '(self, parent: Optional[tests.typing_test_data.Node]) -> None' + assert stringify_signature(sig) == '(self, parent: typing.Optional[tests.typing_test_data.Node]) -> None' # show_annotation is False sig = inspect.signature(f7) @@ -257,7 +259,7 @@ def test_signature_annotations(): # show_return_annotation is False sig = inspect.signature(f7) - assert stringify_signature(sig, show_return_annotation=False) == '(x: Optional[int] = None, y: dict = {})' + assert stringify_signature(sig, show_return_annotation=False) == '(x: typing.Optional[int] = None, y: dict = {})' # unqualified_typehints is True sig = inspect.signature(f7) diff --git a/tests/test_util_typing.py b/tests/test_util_typing.py index 0b2324e294b..f17221458e7 100644 --- a/tests/test_util_typing.py +++ b/tests/test_util_typing.py @@ -197,41 +197,53 @@ def test_stringify(): assert stringify(TracebackType, True) == "~types.TracebackType" assert stringify(Any, False) == "Any" + assert stringify(Any, False, True) == "typing.Any" assert stringify(Any, True) == "~typing.Any" def test_stringify_type_hints_containers(): assert stringify(List, False) == "List" + assert stringify(List, False, True) == "typing.List" assert stringify(List, True) == "~typing.List" assert stringify(Dict, False) == "Dict" + assert stringify(Dict, False, True) == "typing.Dict" assert stringify(Dict, True) == "~typing.Dict" assert stringify(List[int], False) == "List[int]" + assert stringify(List[int], False, True) == "typing.List[int]" assert stringify(List[int], True) == "~typing.List[int]" assert stringify(List[str], False) == "List[str]" + assert stringify(List[str], False, True) == "typing.List[str]" assert stringify(List[str], True) == "~typing.List[str]" assert stringify(Dict[str, float], False) == "Dict[str, float]" + assert stringify(Dict[str, float], False, True) == "typing.Dict[str, float]" assert stringify(Dict[str, float], True) == "~typing.Dict[str, float]" assert stringify(Tuple[str, str, str], False) == "Tuple[str, str, str]" + assert stringify(Tuple[str, str, str], False, True) == "typing.Tuple[str, str, str]" assert stringify(Tuple[str, str, str], True) == "~typing.Tuple[str, str, str]" assert stringify(Tuple[str, ...], False) == "Tuple[str, ...]" + assert stringify(Tuple[str, ...], False, True) == "typing.Tuple[str, ...]" assert stringify(Tuple[str, ...], True) == "~typing.Tuple[str, ...]" assert stringify(Tuple[()], False) == "Tuple[()]" + assert stringify(Tuple[()], False, True) == "typing.Tuple[()]" assert stringify(Tuple[()], True) == "~typing.Tuple[()]" assert stringify(List[Dict[str, Tuple]], False) == "List[Dict[str, Tuple]]" + assert stringify(List[Dict[str, Tuple]], False, True) == "typing.List[typing.Dict[str, typing.Tuple]]" assert stringify(List[Dict[str, Tuple]], True) == "~typing.List[~typing.Dict[str, ~typing.Tuple]]" assert stringify(MyList[Tuple[int, int]], False) == "tests.test_util_typing.MyList[Tuple[int, int]]" + assert stringify(MyList[Tuple[int, int]], False, True) == "tests.test_util_typing.MyList[typing.Tuple[int, int]]" assert stringify(MyList[Tuple[int, int]], True) == "~tests.test_util_typing.MyList[~typing.Tuple[int, int]]" assert stringify(Generator[None, None, None], False) == "Generator[None, None, None]" + assert stringify(Generator[None, None, None], False, True) == "typing.Generator[None, None, None]" assert stringify(Generator[None, None, None], True) == "~typing.Generator[None, None, None]" @@ -288,45 +300,58 @@ def test_stringify_type_hints_string(): def test_stringify_type_hints_Callable(): assert stringify(Callable, False) == "Callable" + assert stringify(Callable, False, True) == "typing.Callable" assert stringify(Callable, True) == "~typing.Callable" if sys.version_info >= (3, 7): assert stringify(Callable[[str], int], False) == "Callable[[str], int]" + assert stringify(Callable[[str], int], False, True) == "typing.Callable[[str], int]" assert stringify(Callable[[str], int], True) == "~typing.Callable[[str], int]" assert stringify(Callable[..., int], False) == "Callable[[...], int]" + assert stringify(Callable[..., int], False, True) == "typing.Callable[[...], int]" assert stringify(Callable[..., int], True) == "~typing.Callable[[...], int]" else: assert stringify(Callable[[str], int], False) == "Callable[str, int]" + assert stringify(Callable[[str], int], False, True) == "typing.Callable[str, int]" assert stringify(Callable[[str], int], True) == "~typing.Callable[str, int]" assert stringify(Callable[..., int], False) == "Callable[..., int]" + assert stringify(Callable[..., int], False, True) == "typing.Callable[..., int]" assert stringify(Callable[..., int], True) == "~typing.Callable[..., int]" def test_stringify_type_hints_Union(): assert stringify(Optional[int], False) == "Optional[int]" + assert stringify(Optional[int], False, True) == "typing.Optional[int]" assert stringify(Optional[int], True) == "~typing.Optional[int]" assert stringify(Union[str, None], False) == "Optional[str]" + assert stringify(Union[str, None], False, True) == "typing.Optional[str]" assert stringify(Union[str, None], True) == "~typing.Optional[str]" assert stringify(Union[int, str], False) == "Union[int, str]" + assert stringify(Union[int, str], False, True) == "typing.Union[int, str]" assert stringify(Union[int, str], True) == "~typing.Union[int, str]" if sys.version_info >= (3, 7): assert stringify(Union[int, Integral], False) == "Union[int, numbers.Integral]" + assert stringify(Union[int, Integral], False, True) == "typing.Union[int, numbers.Integral]" assert stringify(Union[int, Integral], True) == "~typing.Union[int, ~numbers.Integral]" assert (stringify(Union[MyClass1, MyClass2], False) == "Union[tests.test_util_typing.MyClass1, tests.test_util_typing.]") + assert (stringify(Union[MyClass1, MyClass2], False, True) == + "typing.Union[tests.test_util_typing.MyClass1, tests.test_util_typing.]") assert (stringify(Union[MyClass1, MyClass2], True) == "~typing.Union[~tests.test_util_typing.MyClass1, ~tests.test_util_typing.]") else: assert stringify(Union[int, Integral], False) == "numbers.Integral" + assert stringify(Union[int, Integral], False, True) == "numbers.Integral" assert stringify(Union[int, Integral], True) == "~numbers.Integral" assert stringify(Union[MyClass1, MyClass2], False) == "tests.test_util_typing.MyClass1" + assert stringify(Union[MyClass1, MyClass2], False, True) == "tests.test_util_typing.MyClass1" assert stringify(Union[MyClass1, MyClass2], True) == "~tests.test_util_typing.MyClass1" @@ -391,6 +416,7 @@ def test_stringify_type_hints_alias(): def test_stringify_type_Literal(): from typing import Literal # type: ignore assert stringify(Literal[1, "2", "\r"], False) == "Literal[1, '2', '\\r']" + assert stringify(Literal[1, "2", "\r"], False, True) == "typing.Literal[1, '2', '\\r']" assert stringify(Literal[1, "2", "\r"], True) == "~typing.Literal[1, '2', '\\r']"