Skip to content

Support autodoc_type_aliases configuration #459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ run.plugins = [
[tool.mypy]
python_version = "3.10"
strict = true
exclude = "^(.*/roots/.*)|(tests/test_integration.py)$"
exclude = "^(.*/roots/.*)|(tests/test_integration.*.py)$"
overrides = [
{ module = [
"sphobjinv.*",
Expand Down
10 changes: 6 additions & 4 deletions src/sphinx_autodoc_typehints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ def process_signature( # noqa: C901, PLR0913, PLR0917
return None

obj = inspect.unwrap(obj)
sph_signature = sphinx_signature(obj)
sph_signature = sphinx_signature(obj, type_aliases=app.config["autodoc_type_aliases"])

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

try:
signature = sphinx_signature(obj)
signature = sphinx_signature(obj, type_aliases=app.config["autodoc_type_aliases"])
except (ValueError, TypeError):
signature = None
type_hints = get_all_type_hints(app.config.autodoc_mock_imports, obj, name)
Expand Down Expand Up @@ -715,8 +715,10 @@ def _inject_signature( # noqa: C901
app: Sphinx,
lines: list[str],
) -> None:
for arg_name in signature.parameters:
annotation = type_hints.get(arg_name)
type_aliases = app.config["autodoc_type_aliases"]

for arg_name, arg_type in signature.parameters.items():
annotation = arg_type.annotation if arg_type.annotation in type_aliases else type_hints.get(arg_name)

default = signature.parameters[arg_name].default

Expand Down
110 changes: 110 additions & 0 deletions tests/test_integration_autodoc_type_aliases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
from __future__ import annotations

import re
import sys
from pathlib import Path
from textwrap import dedent, indent
from typing import TYPE_CHECKING, Any, Callable, Literal, NewType, TypeVar # no type comments

import pytest

if TYPE_CHECKING:
from io import StringIO

from sphinx.testing.util import SphinxTestApp

T = TypeVar("T")
W = NewType("W", str)


def expected(expected: str, **options: dict[str, Any]) -> Callable[[T], T]:
def dec(val: T) -> T:
val.EXPECTED = expected
val.OPTIONS = options
return val

return dec


def warns(pattern: str) -> Callable[[T], T]:
def dec(val: T) -> T:
val.WARNING = pattern
return val

return dec


ArrayLike = Literal["test"]


@expected(
"""\
mod.function(x)

Function docstring.

Parameters:
**x** (ArrayLike) -- foo

Returns:
something

Return type:
bytes
""",
)
def function(x: ArrayLike) -> str: # noqa: ARG001
"""
Function docstring.

:param x: foo
:return: something
:rtype: bytes
"""


# Config settings for each test run.
# Config Name: Sphinx Options as Dict.
configs = {
"default_conf": {
"autodoc_type_aliases": {
"ArrayLike": "ArrayLike",
}
}
}


@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
@pytest.mark.parametrize("conf_run", list(configs.keys()))
@pytest.mark.sphinx("text", testroot="integration")
def test_integration(
app: SphinxTestApp, status: StringIO, warning: StringIO, monkeypatch: pytest.MonkeyPatch, val: Any, conf_run: str
) -> None:
template = ".. autofunction:: mod.{}"

(Path(app.srcdir) / "index.rst").write_text(template.format(val.__name__))
app.config.__dict__.update(configs[conf_run])
app.config.__dict__.update(val.OPTIONS)
monkeypatch.setitem(sys.modules, "mod", sys.modules[__name__])
app.build()
assert "build succeeded" in status.getvalue() # Build succeeded

regexp = getattr(val, "WARNING", None)
value = warning.getvalue().strip()
if regexp:
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
assert re.search(regexp, value), msg
else:
assert not value

result = (Path(app.srcdir) / "_build/text/index.txt").read_text()

expected = val.EXPECTED
if sys.version_info < (3, 10):
expected = expected.replace("NewType", "NewType()")
try:
assert result.strip() == dedent(expected).strip()
except Exception:
indented = indent(f'"""\n{result}\n"""', " " * 4)
print(f"@expected(\n{indented}\n)\n") # noqa: T201
raise
Loading