From 09197b80cd5a5aa3a3a7a69fcea4d0ca410d1e9f Mon Sep 17 00:00:00 2001 From: Zeb Nicholls Date: Fri, 14 Apr 2023 04:31:42 +1000 Subject: [PATCH] Handle attrs forward refs (#345) * Add failing test * Pass test * Alternate solution * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Pre-commit --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/sphinx_autodoc_typehints/__init__.py | 8 ++- .../test-resolve-typing-guard-tmp/conf.py | 9 +++ .../demo_typing_guard.py | 59 +++++++++++++++++++ .../test-resolve-typing-guard-tmp/index.rst | 2 + tests/test_sphinx_autodoc_typehints.py | 8 +++ 5 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 tests/roots/test-resolve-typing-guard-tmp/conf.py create mode 100644 tests/roots/test-resolve-typing-guard-tmp/demo_typing_guard.py create mode 100644 tests/roots/test-resolve-typing-guard-tmp/index.rst diff --git a/src/sphinx_autodoc_typehints/__init__.py b/src/sphinx_autodoc_typehints/__init__.py index 70d092a9..eefba418 100644 --- a/src/sphinx_autodoc_typehints/__init__.py +++ b/src/sphinx_autodoc_typehints/__init__.py @@ -359,12 +359,18 @@ def get_all_type_hints(autodoc_mock_imports: list[str], obj: Any, name: str) -> _TYPE_GUARD_IMPORT_RE = re.compile(r"\nif (typing.)?TYPE_CHECKING:[^\n]*([\s\S]*?)(?=\n\S)") _TYPE_GUARD_IMPORTS_RESOLVED = set() +_TYPE_GUARD_IMPORTS_RESOLVED_GLOBALS_ID = set() def _resolve_type_guarded_imports(autodoc_mock_imports: list[str], obj: Any) -> None: - if hasattr(obj, "__module__") and obj.__module__ not in _TYPE_GUARD_IMPORTS_RESOLVED: + if (hasattr(obj, "__module__") and obj.__module__ not in _TYPE_GUARD_IMPORTS_RESOLVED) or ( + hasattr(obj, "__globals__") and id(obj.__globals__) not in _TYPE_GUARD_IMPORTS_RESOLVED_GLOBALS_ID + ): _TYPE_GUARD_IMPORTS_RESOLVED.add(obj.__module__) if obj.__module__ not in sys.builtin_module_names: + if hasattr(obj, "__globals__"): + _TYPE_GUARD_IMPORTS_RESOLVED_GLOBALS_ID.add(id(obj.__globals__)) + module = inspect.getmodule(obj) if module: try: diff --git a/tests/roots/test-resolve-typing-guard-tmp/conf.py b/tests/roots/test-resolve-typing-guard-tmp/conf.py new file mode 100644 index 00000000..bb939ea4 --- /dev/null +++ b/tests/roots/test-resolve-typing-guard-tmp/conf.py @@ -0,0 +1,9 @@ +import pathlib +import sys + +master_doc = "index" +sys.path.insert(0, str(pathlib.Path(__file__).parent)) +extensions = [ + "sphinx.ext.autodoc", + "sphinx_autodoc_typehints", +] diff --git a/tests/roots/test-resolve-typing-guard-tmp/demo_typing_guard.py b/tests/roots/test-resolve-typing-guard-tmp/demo_typing_guard.py new file mode 100644 index 00000000..392488aa --- /dev/null +++ b/tests/roots/test-resolve-typing-guard-tmp/demo_typing_guard.py @@ -0,0 +1,59 @@ +"""Module demonstrating imports that are type guarded""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +from attrs import define + +if TYPE_CHECKING: + import datetime + + +@define() +class SomeClass: + """This class does something.""" + + date: datetime.date + """Date to handle""" + + @classmethod + def from_str(cls, input_value: str) -> SomeClass: + """ + Initialise from string + + :param input_value: Input + :return: result + """ + return cls(input_value) + + @classmethod + def from_date(cls, input_value: datetime.date) -> SomeClass: + """ + Initialise from date + + :param input_value: Input + :return: result + """ + return cls(input_value) + + @classmethod + def from_time(cls, input_value: datetime.time) -> SomeClass: + """ + Initialise from time + + :param input_value: Input + :return: result + """ + return cls(input_value) + + def calculate_thing(self, number: float) -> datetime.timedelta: + """ + Calculate a thing + + :param number: Input + :return: result + """ + return datetime.timedelta(number) + + +__all__ = ["SomeClass"] diff --git a/tests/roots/test-resolve-typing-guard-tmp/index.rst b/tests/roots/test-resolve-typing-guard-tmp/index.rst new file mode 100644 index 00000000..f92eae81 --- /dev/null +++ b/tests/roots/test-resolve-typing-guard-tmp/index.rst @@ -0,0 +1,2 @@ +.. automodule:: demo_typing_guard + :members: diff --git a/tests/test_sphinx_autodoc_typehints.py b/tests/test_sphinx_autodoc_typehints.py index d6f8ed80..7bc9892b 100644 --- a/tests/test_sphinx_autodoc_typehints.py +++ b/tests/test_sphinx_autodoc_typehints.py @@ -754,6 +754,14 @@ def test_resolve_typing_guard_imports(app: SphinxTestApp, status: StringIO, warn assert re.search(pat, err) +@pytest.mark.sphinx("text", testroot="resolve-typing-guard-tmp") +def test_resolve_typing_guard_attrs_imports(app: SphinxTestApp, status: StringIO, warning: StringIO) -> None: + set_python_path() + app.build() + assert "build succeeded" in status.getvalue() + assert not warning.getvalue() + + def test_no_source_code_type_guard() -> None: from csv import Error