diff --git a/CHANGES.rst b/CHANGES.rst index faf6e269a60..44ba2da10d3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -121,6 +121,7 @@ Bugs fixed * #12975: Avoid rendering a trailing comma in C and C++ multi-line signatures. * #13178: autodoc: Fix resolution for ``pathlib`` types. Patch by Adam Turner. +* #13276: autodoc: Allow TypeVars to be reimported from other modules Testing ------- diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index df951ccd1bf..8a78af59fa2 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -763,6 +763,18 @@ def is_filtered_inherited_member(name: str, obj: Any) -> bool: for obj in members: membername = obj.__name__ member = obj.object + # If the member doesn't have docs in the current module, see if it does + # where it is imported from + key = (namespace, membername) + if key not in attr_docs and hasattr(obj.object, '__module__'): + try: + analyzer = ModuleAnalyzer.for_module(obj.object.__module__) + except PycodeError: + pass + else: + orig_docs = analyzer.find_attr_docs() + if key in orig_docs: + attr_docs[key] = orig_docs[key] # if isattr is True, the member is documented as an attribute isattr = member is INSTANCEATTR or (namespace, membername) in attr_docs diff --git a/tests/roots/test-ext-autodoc/target/reimport/__init__.py b/tests/roots/test-ext-autodoc/target/reimport/__init__.py new file mode 100644 index 00000000000..8cec7916be3 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/reimport/__init__.py @@ -0,0 +1,9 @@ +from typing import TypeVar + +from ._private import T +from ._usage import add + +V = TypeVar('V') +"""A locally defined TypeVar""" + +__all__ = ['T', 'V', 'add'] diff --git a/tests/roots/test-ext-autodoc/target/reimport/_private.py b/tests/roots/test-ext-autodoc/target/reimport/_private.py new file mode 100644 index 00000000000..24bf559743c --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/reimport/_private.py @@ -0,0 +1,4 @@ +from typing import TypeVar + +T = TypeVar('T', bound=int | str) +"""A reimported TypeVar""" diff --git a/tests/roots/test-ext-autodoc/target/reimport/_usage.py b/tests/roots/test-ext-autodoc/target/reimport/_usage.py new file mode 100644 index 00000000000..10a7de93558 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/reimport/_usage.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from ._private import T + + +def add(x: T, y: T) -> T: + """Add two things together""" + return x + y diff --git a/tests/test_extensions/test_ext_autodoc.py b/tests/test_extensions/test_ext_autodoc.py index fe7ff9a907b..5d5dbeeda78 100644 --- a/tests/test_extensions/test_ext_autodoc.py +++ b/tests/test_extensions/test_ext_autodoc.py @@ -3119,6 +3119,43 @@ def test_canonical(app): ] +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_reimport_typevar(app): + options = { + 'members': None, + 'imported-members': None, + } + actual = do_autodoc(app, 'module', 'target.reimport', options) + assert list(actual) == [ + '', + '.. py:module:: target.reimport', + '', + '', + '.. py:class:: T', + ' :module: target.reimport', + '', + ' A reimported TypeVar', + '', + " alias of TypeVar('T', bound=\\ :py:class:`int` | :py:class:`str`)", + '', + '', + '.. py:class:: V', + ' :module: target.reimport', + '', + ' A locally defined TypeVar', + '', + " alias of TypeVar('V')", + '', + '', + '.. py:function:: add(x: ~target.reimport.T, y: ~target.reimport.T) ->' + ' ~target.reimport.T', + ' :module: target.reimport', + '', + ' Add two things together', + '', + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_literal_render(app): def bounded_typevar_rst(name, bound):