Skip to content

Commit 5bb2358

Browse files
hmc-cs-mdrissiMehdi Drissi
and
Mehdi Drissi
authored
Add support for recursive type hints (#248)
Co-authored-by: Mehdi Drissi <mdrissi@snapchat.com>
1 parent 1ef3776 commit 5bb2358

File tree

3 files changed

+21
-15
lines changed

3 files changed

+21
-15
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 1.19.1
4+
5+
- Fix bug for recursive type alias.
6+
37
## 1.19.0
48

59
- Support for CPython 3.11, no longer adds `Optional` when the argument is default per

src/sphinx_autodoc_typehints/__init__.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import sys
66
import textwrap
77
from ast import FunctionDef, Module, stmt
8-
from typing import _eval_type # type: ignore # no import defined in stubs
98
from typing import Any, AnyStr, Callable, ForwardRef, NewType, TypeVar, get_type_hints
109

1110
from sphinx.application import Sphinx
@@ -120,8 +119,7 @@ def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901 # t
120119

121120
# Special cases
122121
if isinstance(annotation, ForwardRef):
123-
value = _resolve_forward_ref(annotation, config)
124-
return format_annotation(value, config)
122+
return annotation.__forward_arg__
125123
if annotation is None or annotation is type(None): # noqa: E721
126124
return ":py:obj:`None`"
127125
if annotation is Ellipsis:
@@ -195,17 +193,6 @@ def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901 # t
195193
return result
196194

197195

198-
def _resolve_forward_ref(annotation: ForwardRef, config: Config) -> Any:
199-
raw, base_globals = annotation.__forward_arg__, config._annotation_globals
200-
params = {"is_class": True} if (3, 10) > sys.version_info >= (3, 9, 8) or sys.version_info >= (3, 10, 1) else {}
201-
value = ForwardRef(raw, is_argument=False, **params)
202-
try:
203-
result = _eval_type(value, base_globals, None)
204-
except NameError:
205-
result = raw # fallback to the value itself as string
206-
return result
207-
208-
209196
# reference: https://github.com/pytorch/pytorch/pull/46548/files
210197
def normalize_source_lines(source_lines: str) -> str:
211198
"""

tests/test_sphinx_autodoc_typehints.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
Callable,
1616
Dict,
1717
Generic,
18+
List,
1819
Mapping,
1920
Match,
2021
NewType,
@@ -55,6 +56,12 @@
5556
S = TypeVar("S", bound="miss") # type: ignore # miss not defined on purpose # noqa: F821
5657
W = NewType("W", str)
5758

59+
# Mypy does not support recursive type aliases, but
60+
# other type checkers do.
61+
RecList = Union[int, List["RecList"]] # type: ignore
62+
MutualRecA = Union[bool, List["MutualRecB"]] # type: ignore
63+
MutualRecB = Union[str, List["MutualRecA"]] # type: ignore
64+
5865

5966
class A:
6067
def get_type(self) -> type:
@@ -228,7 +235,7 @@ def test_parse_annotation(annotation: Any, module: str, class_name: str, args: t
228235
(V, ":py:class:`~typing.TypeVar`\\(``V``, contravariant=True)"),
229236
(X, ":py:class:`~typing.TypeVar`\\(``X``, :py:class:`str`, :py:class:`int`)"),
230237
(Y, ":py:class:`~typing.TypeVar`\\(``Y``, bound= :py:class:`str`)"),
231-
(Z, ":py:class:`~typing.TypeVar`\\(``Z``, bound= :py:class:`~test_sphinx_autodoc_typehints.A`)"),
238+
(Z, ":py:class:`~typing.TypeVar`\\(``Z``, bound= A)"),
232239
(S, ":py:class:`~typing.TypeVar`\\(``S``, bound= miss)"),
233240
# ## These test for correct internal tuple rendering, even if not all are valid Tuple types
234241
# Zero-length tuple remains
@@ -279,6 +286,14 @@ def test_parse_annotation(annotation: Any, module: str, class_name: str, args: t
279286
"...], :py:class:`~numpy.float64`]"
280287
),
281288
),
289+
(
290+
RecList,
291+
(":py:data:`~typing.Union`\\[:py:class:`int`, :py:class:`~typing.List`\\[RecList]]"),
292+
),
293+
(
294+
MutualRecA,
295+
(":py:data:`~typing.Union`\\[:py:class:`bool`, :py:class:`~typing.List`\\[MutualRecB]]"),
296+
),
282297
],
283298
)
284299
def test_format_annotation(inv: Inventory, annotation: Any, expected_result: str) -> None:

0 commit comments

Comments
 (0)