From c71ff1cd21ce796f871adef54e8a53c0eaa9e53a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 3 Dec 2021 01:28:10 +0900 Subject: [PATCH] Close #9075: autodoc: Add a config variable autodoc_unqualified_typehints If autodoc_unqualified_typehints feature enabled, autodoc suppresses the leading module names of typehints of function signatures (ex. `io.StringIO` -> `StringIO`) --- CHANGES | 3 + doc/usage/extensions/autodoc.rst | 7 +++ sphinx/ext/autodoc/__init__.py | 16 ++++++ tests/test_ext_autodoc_configs.py | 93 +++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+) diff --git a/CHANGES b/CHANGES index 9ce12b924aa..727ff22fe3e 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,9 @@ Deprecated Features added -------------- +* #9075: autodoc: Add a config variable :confval:`autodoc_unqualified_typehints` + to suppress the leading module names of typehints of function signatures (ex. + ``io.StringIO`` -> ``StringIO``) * #9831: Autosummary now documents only the members specified in a module's ``__all__`` attribute if :confval:`autosummary_ignore_module_all` is set to ``False``. The default behaviour is unchanged. Autogen also now supports diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst index 5ac3cd17179..dab09c92c5f 100644 --- a/doc/usage/extensions/autodoc.rst +++ b/doc/usage/extensions/autodoc.rst @@ -662,6 +662,13 @@ There are also config values that you can set: .. __: https://mypy.readthedocs.io/en/latest/kinds_of_types.html#type-aliases .. versionadded:: 3.3 +.. confval:: autodoc_unqualified_typehints + + If True, the leading module names of typehints of function signatures (ex. + ``io.StringIO`` -> ``StringIO``). Defaults to False. + + .. versionadded:: 4.4 + .. confval:: autodoc_preserve_defaults If True, the default argument values of functions will be not evaluated on diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index a4d5884e89e..5ada06a6a50 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1295,6 +1295,8 @@ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: def format_args(self, **kwargs: Any) -> str: if self.config.autodoc_typehints in ('none', 'description'): kwargs.setdefault('show_annotation', False) + if self.config.autodoc_unqualified_typehints: + kwargs.setdefault('unqualified_typehints', True) try: self.env.app.emit('autodoc-before-process-signature', self.object, False) @@ -1323,6 +1325,9 @@ def add_directive_header(self, sig: str) -> None: self.add_line(' :async:', sourcename) def format_signature(self, **kwargs: Any) -> str: + if self.config.autodoc_unqualified_typehints: + kwargs.setdefault('unqualified_typehints', True) + sigs = [] if (self.analyzer and '.'.join(self.objpath) in self.analyzer.overloads and @@ -1561,6 +1566,8 @@ def get_user_defined_function_or_method(obj: Any, attr: str) -> Any: def format_args(self, **kwargs: Any) -> str: if self.config.autodoc_typehints in ('none', 'description'): kwargs.setdefault('show_annotation', False) + if self.config.autodoc_unqualified_typehints: + kwargs.setdefault('unqualified_typehints', True) try: self._signature_class, self._signature_method_name, sig = self._get_signature() @@ -1582,6 +1589,9 @@ def format_signature(self, **kwargs: Any) -> str: # do not show signatures return '' + if self.config.autodoc_unqualified_typehints: + kwargs.setdefault('unqualified_typehints', True) + sig = super().format_signature() sigs = [] @@ -2110,6 +2120,8 @@ def import_object(self, raiseerror: bool = False) -> bool: def format_args(self, **kwargs: Any) -> str: if self.config.autodoc_typehints in ('none', 'description'): kwargs.setdefault('show_annotation', False) + if self.config.autodoc_unqualified_typehints: + kwargs.setdefault('unqualified_typehints', True) try: if self.object == object.__init__ and self.parent != object: @@ -2160,6 +2172,9 @@ def document_members(self, all_members: bool = False) -> None: pass def format_signature(self, **kwargs: Any) -> str: + if self.config.autodoc_unqualified_typehints: + kwargs.setdefault('unqualified_typehints', True) + sigs = [] if (self.analyzer and '.'.join(self.objpath) in self.analyzer.overloads and @@ -2833,6 +2848,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_config_value('autodoc_typehints_description_target', 'all', True, ENUM('all', 'documented')) app.add_config_value('autodoc_type_aliases', {}, True) + app.add_config_value('autodoc_unqualified_typehints', False, 'env') app.add_config_value('autodoc_warningiserror', True, True) app.add_config_value('autodoc_inherit_docstrings', True, True) app.add_event('autodoc-before-process-signature') diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index 643899286a4..f3bcd6a97a1 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -1142,6 +1142,99 @@ def test_autodoc_typehints_description_and_type_aliases(app): ' myint\n' == context) +@pytest.mark.sphinx('html', testroot='ext-autodoc', + confoverrides={'autodoc_unqualified_typehints': True}) +def test_autodoc_unqualified_typehints(app): + if sys.version_info < (3, 7): + Any = 'Any' + else: + Any = '~typing.Any' + + options = {"members": None, + "undoc-members": None} + actual = do_autodoc(app, 'module', 'target.typehints', options) + assert list(actual) == [ + '', + '.. py:module:: target.typehints', + '', + '', + '.. py:data:: CONST1', + ' :module: target.typehints', + ' :type: int', + '', + '', + '.. py:class:: Math(s: str, o: ~typing.Optional[%s] = None)' % Any, + ' :module: target.typehints', + '', + '', + ' .. py:attribute:: Math.CONST1', + ' :module: target.typehints', + ' :type: int', + '', + '', + ' .. py:attribute:: Math.CONST2', + ' :module: target.typehints', + ' :type: int', + ' :value: 1', + '', + '', + ' .. py:method:: Math.decr(a: int, b: int = 1) -> int', + ' :module: target.typehints', + '', + '', + ' .. py:method:: Math.horse(a: str, b: int) -> None', + ' :module: target.typehints', + '', + '', + ' .. py:method:: Math.incr(a: int, b: int = 1) -> int', + ' :module: target.typehints', + '', + '', + ' .. py:method:: Math.nothing() -> None', + ' :module: target.typehints', + '', + '', + ' .. py:property:: Math.prop', + ' :module: target.typehints', + ' :type: int', + '', + '', + '.. py:class:: NewAnnotation(i: int)', + ' :module: target.typehints', + '', + '', + '.. py:class:: NewComment(i: int)', + ' :module: target.typehints', + '', + '', + '.. py:class:: SignatureFromMetaclass(a: int)', + ' :module: target.typehints', + '', + '', + '.. py:function:: complex_func(arg1: str, arg2: List[int], arg3: Tuple[int, ' + 'Union[str, Unknown]] = None, *args: str, **kwargs: str) -> None', + ' :module: target.typehints', + '', + '', + '.. py:function:: decr(a: int, b: int = 1) -> int', + ' :module: target.typehints', + '', + '', + '.. py:function:: incr(a: int, b: int = 1) -> int', + ' :module: target.typehints', + '', + '', + '.. py:function:: missing_attr(c, a: str, b: Optional[str] = None) -> str', + ' :module: target.typehints', + '', + '', + '.. py:function:: tuple_args(x: ~typing.Tuple[int, ~typing.Union[int, str]]) ' + '-> ~typing.Tuple[int, int]', + ' :module: target.typehints', + '', + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_default_options(app): # no settings