Skip to content

Commit fb240ea

Browse files
Support autodoc_type_aliases configuration (#459)
* support autodoc_type_aliases configuration * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed app.config access * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * removed numpy dependency for 3.12 compatibility * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * use arg_type.annotation instead of arg_type._annotation * skip mypy test on all test_integration* files * removed extreneous lines * fixed the mypy exclude regexp --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 41b2900 commit fb240ea

File tree

3 files changed

+117
-5
lines changed

3 files changed

+117
-5
lines changed

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ run.plugins = [
145145
[tool.mypy]
146146
python_version = "3.10"
147147
strict = true
148-
exclude = "^(.*/roots/.*)|(tests/test_integration.py)$"
148+
exclude = "^(.*/roots/.*)|(tests/test_integration.*.py)$"
149149
overrides = [
150150
{ module = [
151151
"sphobjinv.*",

src/sphinx_autodoc_typehints/__init__.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ def process_signature( # noqa: C901, PLR0913, PLR0917
345345
return None
346346

347347
obj = inspect.unwrap(obj)
348-
sph_signature = sphinx_signature(obj)
348+
sph_signature = sphinx_signature(obj, type_aliases=app.config["autodoc_type_aliases"])
349349

350350
if app.config.typehints_use_signature:
351351
parameters = list(sph_signature.parameters.values())
@@ -642,7 +642,7 @@ def process_docstring( # noqa: PLR0913, PLR0917
642642
obj = inspect.unwrap(obj)
643643

644644
try:
645-
signature = sphinx_signature(obj)
645+
signature = sphinx_signature(obj, type_aliases=app.config["autodoc_type_aliases"])
646646
except (ValueError, TypeError):
647647
signature = None
648648
type_hints = get_all_type_hints(app.config.autodoc_mock_imports, obj, name)
@@ -715,8 +715,10 @@ def _inject_signature( # noqa: C901
715715
app: Sphinx,
716716
lines: list[str],
717717
) -> None:
718-
for arg_name in signature.parameters:
719-
annotation = type_hints.get(arg_name)
718+
type_aliases = app.config["autodoc_type_aliases"]
719+
720+
for arg_name, arg_type in signature.parameters.items():
721+
annotation = arg_type.annotation if arg_type.annotation in type_aliases else type_hints.get(arg_name)
720722

721723
default = signature.parameters[arg_name].default
722724

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
from __future__ import annotations
2+
3+
import re
4+
import sys
5+
from pathlib import Path
6+
from textwrap import dedent, indent
7+
from typing import TYPE_CHECKING, Any, Callable, Literal, NewType, TypeVar # no type comments
8+
9+
import pytest
10+
11+
if TYPE_CHECKING:
12+
from io import StringIO
13+
14+
from sphinx.testing.util import SphinxTestApp
15+
16+
T = TypeVar("T")
17+
W = NewType("W", str)
18+
19+
20+
def expected(expected: str, **options: dict[str, Any]) -> Callable[[T], T]:
21+
def dec(val: T) -> T:
22+
val.EXPECTED = expected
23+
val.OPTIONS = options
24+
return val
25+
26+
return dec
27+
28+
29+
def warns(pattern: str) -> Callable[[T], T]:
30+
def dec(val: T) -> T:
31+
val.WARNING = pattern
32+
return val
33+
34+
return dec
35+
36+
37+
ArrayLike = Literal["test"]
38+
39+
40+
@expected(
41+
"""\
42+
mod.function(x)
43+
44+
Function docstring.
45+
46+
Parameters:
47+
**x** (ArrayLike) -- foo
48+
49+
Returns:
50+
something
51+
52+
Return type:
53+
bytes
54+
""",
55+
)
56+
def function(x: ArrayLike) -> str: # noqa: ARG001
57+
"""
58+
Function docstring.
59+
60+
:param x: foo
61+
:return: something
62+
:rtype: bytes
63+
"""
64+
65+
66+
# Config settings for each test run.
67+
# Config Name: Sphinx Options as Dict.
68+
configs = {
69+
"default_conf": {
70+
"autodoc_type_aliases": {
71+
"ArrayLike": "ArrayLike",
72+
}
73+
}
74+
}
75+
76+
77+
@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
78+
@pytest.mark.parametrize("conf_run", list(configs.keys()))
79+
@pytest.mark.sphinx("text", testroot="integration")
80+
def test_integration(
81+
app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str
82+
) -> None:
83+
template = ".. autofunction:: mod.{}"
84+
85+
(Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__))
86+
app.config.__dict__.update(configs[conf_run])
87+
app.config.__dict__.update(val.OPTIONS)
88+
monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__])
89+
app.build()
90+
assert "build succeeded" in status.getvalue() # Build succeeded
91+
92+
regexp = getattr(val, "WARNING", None)
93+
value = warning.getvalue().strip()
94+
if regexp:
95+
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
96+
assert re.search(regexp, value), msg
97+
else:
98+
assert not value
99+
100+
result = (Path(app.srcdir) / "_build/text/index.txt").read_text()
101+
102+
expected = val.EXPECTED
103+
if sys.version_info < (3, 10):
104+
expected = expected.replace("NewType", "NewType()")
105+
try:
106+
assert result.strip() == dedent(expected).strip()
107+
except Exception:
108+
indented = indent(f'"""\n{result}\n"""', " " * 4)
109+
print(f"@expected(\n{indented}\n)\n") # noqa: T201
110+
raise

0 commit comments

Comments
 (0)