From 6d914184f876107dbb03f0d288e27706786b5d38 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 29 Aug 2024 18:24:44 +0200 Subject: [PATCH] Add module name rewrite configuration option (#474) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bernát Gábor --- src/sphinx_autodoc_typehints/__init__.py | 21 +++++++++++------ tests/roots/test-dummy/export_module.py | 5 ++++ tests/roots/test-dummy/future_annotations.rst | 2 ++ tests/roots/test-dummy/simple.rst | 2 ++ .../roots/test-dummy/simple_default_role.rst | 2 ++ .../roots/test-dummy/simple_no_use_rtype.rst | 2 ++ .../test-dummy/without_complete_typehints.rst | 2 ++ tests/roots/test-dummy/wrong_module_path.py | 9 ++++++++ tests/roots/test-dummy/wrong_module_path.rst | 5 ++++ tests/test_sphinx_autodoc_typehints.py | 23 +++++++++++++++++++ 10 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 tests/roots/test-dummy/export_module.py create mode 100644 tests/roots/test-dummy/wrong_module_path.py create mode 100644 tests/roots/test-dummy/wrong_module_path.rst diff --git a/src/sphinx_autodoc_typehints/__init__.py b/src/sphinx_autodoc_typehints/__init__.py index 824f458e..fbd57ce7 100644 --- a/src/sphinx_autodoc_typehints/__init__.py +++ b/src/sphinx_autodoc_typehints/__init__.py @@ -169,6 +169,18 @@ def format_internal_tuple(t: tuple[Any, ...], config: Config) -> str: return f"({', '.join(fmt)})" +def fixup_module_name(config: Config, module: str) -> str: + if getattr(config, "typehints_fixup_module_name", None): + module = config.typehints_fixup_module_name(module) + + if module == "typing_extensions": + module = "typing" + + if module == "_io": + module = "io" + return module + + def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901, PLR0911, PLR0912, PLR0915, PLR0914 """ Format the annotation. @@ -204,13 +216,7 @@ def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901, PL except ValueError: return str(annotation).strip("'") - # Redirect all typing_extensions types to the stdlib typing module - if module == "typing_extensions": - module = "typing" - - if module == "_io": - module = "io" - + module = fixup_module_name(config, module) full_name = f"{module}.{class_name}" if module != "builtins" else class_name fully_qualified: bool = getattr(config, "typehints_fully_qualified", False) prefix = "" if fully_qualified or full_name == class_name else "~" @@ -967,6 +973,7 @@ def setup(app: Sphinx) -> dict[str, bool]: app.add_config_value("typehints_formatter", None, "env") app.add_config_value("typehints_use_signature", False, "env") # noqa: FBT003 app.add_config_value("typehints_use_signature_return", False, "env") # noqa: FBT003 + app.add_config_value("typehints_fixup_module_name", None, "env") app.add_role("sphinx_autodoc_typehints_type", sphinx_autodoc_typehints_type_role) app.connect("env-before-read-docs", validate_config) # config may be changed after “config-inited” event app.connect("autodoc-process-signature", process_signature) diff --git a/tests/roots/test-dummy/export_module.py b/tests/roots/test-dummy/export_module.py new file mode 100644 index 00000000..0fa35a85 --- /dev/null +++ b/tests/roots/test-dummy/export_module.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from wrong_module_path import A, f + +__all__ = ["A", "f"] diff --git a/tests/roots/test-dummy/future_annotations.rst b/tests/roots/test-dummy/future_annotations.rst index 3d774cb8..97c2bce1 100644 --- a/tests/roots/test-dummy/future_annotations.rst +++ b/tests/roots/test-dummy/future_annotations.rst @@ -1,3 +1,5 @@ +:orphan: + Dummy Module ============ diff --git a/tests/roots/test-dummy/simple.rst b/tests/roots/test-dummy/simple.rst index 36347075..c920b7de 100644 --- a/tests/roots/test-dummy/simple.rst +++ b/tests/roots/test-dummy/simple.rst @@ -1,3 +1,5 @@ +:orphan: + Simple Module ============= diff --git a/tests/roots/test-dummy/simple_default_role.rst b/tests/roots/test-dummy/simple_default_role.rst index c3148a79..186f1a8e 100644 --- a/tests/roots/test-dummy/simple_default_role.rst +++ b/tests/roots/test-dummy/simple_default_role.rst @@ -1,3 +1,5 @@ +:orphan: + Simple Module ============= diff --git a/tests/roots/test-dummy/simple_no_use_rtype.rst b/tests/roots/test-dummy/simple_no_use_rtype.rst index 00b2d61e..f7b30345 100644 --- a/tests/roots/test-dummy/simple_no_use_rtype.rst +++ b/tests/roots/test-dummy/simple_no_use_rtype.rst @@ -1,3 +1,5 @@ +:orphan: + Simple Module ============= diff --git a/tests/roots/test-dummy/without_complete_typehints.rst b/tests/roots/test-dummy/without_complete_typehints.rst index 2e7a204e..4eb63ce3 100644 --- a/tests/roots/test-dummy/without_complete_typehints.rst +++ b/tests/roots/test-dummy/without_complete_typehints.rst @@ -1,3 +1,5 @@ +:orphan: + Simple Module ============= diff --git a/tests/roots/test-dummy/wrong_module_path.py b/tests/roots/test-dummy/wrong_module_path.py new file mode 100644 index 00000000..e5542ed2 --- /dev/null +++ b/tests/roots/test-dummy/wrong_module_path.py @@ -0,0 +1,9 @@ +from __future__ import annotations + + +class A: + pass + + +def f() -> A: + pass diff --git a/tests/roots/test-dummy/wrong_module_path.rst b/tests/roots/test-dummy/wrong_module_path.rst new file mode 100644 index 00000000..04b28b0d --- /dev/null +++ b/tests/roots/test-dummy/wrong_module_path.rst @@ -0,0 +1,5 @@ +:orphan: + +.. class:: export_module.A + +.. autofunction:: export_module.f diff --git a/tests/test_sphinx_autodoc_typehints.py b/tests/test_sphinx_autodoc_typehints.py index 93df0fe8..94c1c869 100644 --- a/tests/test_sphinx_autodoc_typehints.py +++ b/tests/test_sphinx_autodoc_typehints.py @@ -1123,3 +1123,26 @@ def test_default_annotation_without_typehints(app: SphinxTestApp, status: String "str" """ assert text_contents == dedent(expected_contents) + + +@pytest.mark.sphinx("text", testroot="dummy") +@patch("sphinx.writers.text.MAXWIDTH", 2000) +def test_wrong_module_path(app: SphinxTestApp, status: StringIO, warning: StringIO) -> None: + set_python_path() + + app.config.master_doc = "wrong_module_path" # create flag + app.config.default_role = "literal" + app.config.nitpicky = True + app.config.nitpick_ignore = {("py:data", "typing.Optional")} + + def fixup_module_name(mod: str) -> str: + if not mod.startswith("wrong_module_path"): + return mod + return "export_module" + mod.removeprefix("wrong_module_path") + + app.config.suppress_warnings = ["config.cache"] + app.config.typehints_fixup_module_name = fixup_module_name + app.build() + + assert "build succeeded" in status.getvalue() # Build succeeded + assert not warning.getvalue().strip()