Skip to content

Commit

Permalink
Support autodoc_type_aliases configuration (#459)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
tikuma-lsuhsc and pre-commit-ci[bot] authored Jun 19, 2024
1 parent 41b2900 commit fb240ea
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 5 deletions.
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

0 comments on commit fb240ea

Please sign in to comment.