Skip to content

Commit

Permalink
Fix issue #462 and another alias processing bug (#463)
Browse files Browse the repository at this point in the history
* test update (non-reproducible)

* fix candidate #1

* fixes issue #462

* Fix annotation check exception (#462)
Fix processing type aliases

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* reverted test_integration.py

* make _SchemaMeta hashable for ruff pre-commit test

* pre-commit ci fix try no 2

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* _get_type_hint(): fixed the type  hint of localns arg

---------

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 21, 2024
1 parent fb240ea commit 0435d07
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 22 deletions.
27 changes: 16 additions & 11 deletions src/sphinx_autodoc_typehints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
from sphinx.ext.autodoc.mock import mock
from sphinx.parsers import RSTParser
from sphinx.util import logging, rst
from sphinx.util.inspect import TypeAliasForwardRef, TypeAliasNamespace, stringify_signature
from sphinx.util.inspect import signature as sphinx_signature
from sphinx.util.inspect import stringify_signature

from .parser import parse
from .patches import install_patches
Expand Down Expand Up @@ -194,6 +194,9 @@ def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901, PL
if isinstance(annotation, tuple):
return format_internal_tuple(annotation, config)

if isinstance(annotation, TypeAliasForwardRef):
return str(annotation)

try:
module = get_annotation_module(annotation)
class_name = get_annotation_class_name(annotation, module)
Expand Down Expand Up @@ -404,16 +407,18 @@ def _future_annotations_imported(obj: Any) -> bool:
return bool(_annotations.compiler_flag == future_annotations)


def get_all_type_hints(autodoc_mock_imports: list[str], obj: Any, name: str) -> dict[str, Any]:
result = _get_type_hint(autodoc_mock_imports, name, obj)
def get_all_type_hints(
autodoc_mock_imports: list[str], obj: Any, name: str, localns: TypeAliasNamespace
) -> dict[str, Any]:
result = _get_type_hint(autodoc_mock_imports, name, obj, localns)
if not result:
result = backfill_type_hints(obj, name)
try:
obj.__annotations__ = result
except (AttributeError, TypeError):
pass
else:
result = _get_type_hint(autodoc_mock_imports, name, obj)
result = _get_type_hint(autodoc_mock_imports, name, obj, localns)
return result


Expand Down Expand Up @@ -474,10 +479,10 @@ def _resolve_type_guarded_imports(autodoc_mock_imports: list[str], obj: Any) ->
_execute_guarded_code(autodoc_mock_imports, obj, module_code)


def _get_type_hint(autodoc_mock_imports: list[str], name: str, obj: Any) -> dict[str, Any]:
def _get_type_hint(autodoc_mock_imports: list[str], name: str, obj: Any, localns: TypeAliasNamespace) -> dict[str, Any]:
_resolve_type_guarded_imports(autodoc_mock_imports, obj)
try:
result = get_type_hints(obj)
result = get_type_hints(obj, None, localns)
except (AttributeError, TypeError, RecursionError) as exc:
# TypeError - slot wrapper, PEP-563 when part of new syntax not supported
# RecursionError - some recursive type definitions https://github.com/python/typing/issues/574
Expand Down Expand Up @@ -645,7 +650,9 @@ def process_docstring( # noqa: PLR0913, PLR0917
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)

localns = TypeAliasNamespace(app.config["autodoc_type_aliases"])
type_hints = get_all_type_hints(app.config.autodoc_mock_imports, obj, name, localns)
app.config._annotation_globals = getattr(obj, "__globals__", {}) # type: ignore[attr-defined] # noqa: SLF001
try:
_inject_types_to_docstring(type_hints, signature, original_obj, app, what, name, lines)
Expand Down Expand Up @@ -715,10 +722,8 @@ def _inject_signature( # noqa: C901
app: Sphinx,
lines: list[str],
) -> None:
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)
for arg_name in signature.parameters:
annotation = type_hints.get(arg_name)

default = signature.parameters[arg_name].default

Expand Down
78 changes: 67 additions & 11 deletions tests/test_integration_autodoc_type_aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,41 +37,97 @@ def dec(val: T) -> T:
ArrayLike = Literal["test"]


class _SchemaMeta(type): # noqa: PLW1641
def __eq__(cls, other: object) -> bool:
return True


class Schema(metaclass=_SchemaMeta):
pass


@expected(
"""
mod.f(s)
Do something.
Parameters:
**s** ("Schema") -- Some schema.
Return type:
"Schema"
"""
)
def f(s: Schema) -> Schema:
"""
Do something.
Args:
s: Some schema.
"""
return s


class AliasedClass: ...


@expected(
"""
mod.g(s)
Do something.
Parameters:
**s** ("Class Alias") -- Some schema.
Return type:
"Class Alias"
"""
)
def g(s: AliasedClass) -> AliasedClass:
"""
Do something.
Args:
s: Some schema.
"""
return s


@expected(
"""\
mod.function(x)
mod.function(x, y)
Function docstring.
Parameters:
**x** (ArrayLike) -- foo
* **x** (Array) -- foo
* **y** ("Schema") -- boo
Returns:
something
Return type:
bytes
""",
)
def function(x: ArrayLike) -> str: # noqa: ARG001
def function(x: ArrayLike, y: Schema) -> str: # noqa: ARG001
"""
Function docstring.
:param x: foo
:param y: boo
:return: something
:rtype: bytes
"""


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


@pytest.mark.parametrize("val", [x for x in globals().values() if hasattr(x, "EXPECTED")])
Expand All @@ -94,7 +150,7 @@ def test_integration(
if regexp:
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
assert re.search(regexp, value), msg
else:
elif not re.search("WARNING: Inline strong start-string without end-string.", value):
assert not value

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

0 comments on commit 0435d07

Please sign in to comment.