Skip to content

Commit bf68ffc

Browse files
authored
Handle types from types module (#292)
* Handle types from types module * Add return type annotation to _is_newtype
1 parent 3ecdb42 commit bf68ffc

File tree

3 files changed

+34
-9
lines changed

3 files changed

+34
-9
lines changed

src/sphinx_autodoc_typehints/__init__.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55
import sys
66
import textwrap
7+
import types
78
from ast import FunctionDef, Module, stmt
89
from typing import Any, AnyStr, Callable, ForwardRef, NewType, TypeVar, get_type_hints
910

@@ -21,8 +22,16 @@
2122
_LOGGER = logging.getLogger(__name__)
2223
_PYDATA_ANNOTATIONS = {"Any", "AnyStr", "Callable", "ClassVar", "Literal", "NoReturn", "Optional", "Tuple", "Union"}
2324

25+
# types has a bunch of things like ModuleType where ModuleType.__module__ is
26+
# "builtins" and ModuleType.__name__ is "module", so we have to check for this.
27+
_TYPES_DICT = {getattr(types, name): name for name in types.__all__}
28+
# Prefer FunctionType to LambdaType (they are synonymous)
29+
_TYPES_DICT[types.FunctionType] = "FunctionType"
30+
2431

2532
def get_annotation_module(annotation: Any) -> str:
33+
if annotation in _TYPES_DICT:
34+
return "types"
2635
if annotation is None:
2736
return "builtins"
2837
is_new_type = sys.version_info >= (3, 10) and isinstance(annotation, NewType)
@@ -35,17 +44,22 @@ def get_annotation_module(annotation: Any) -> str:
3544
raise ValueError(f"Cannot determine the module of {annotation}")
3645

3746

47+
def _is_newtype(annotation: Any) -> bool:
48+
if sys.version_info < (3, 10):
49+
return inspect.isfunction(annotation) and hasattr(annotation, "__supertype__")
50+
else:
51+
return isinstance(annotation, NewType)
52+
53+
3854
def get_annotation_class_name(annotation: Any, module: str) -> str:
3955
# Special cases
4056
if annotation is None:
4157
return "None"
42-
elif annotation is Any:
43-
return "Any"
44-
elif annotation is AnyStr:
58+
if annotation is AnyStr:
4559
return "AnyStr"
46-
elif (sys.version_info < (3, 10) and inspect.isfunction(annotation) and hasattr(annotation, "__supertype__")) or (
47-
sys.version_info >= (3, 10) and isinstance(annotation, NewType)
48-
):
60+
if annotation in _TYPES_DICT:
61+
return _TYPES_DICT[annotation]
62+
if _is_newtype(annotation):
4963
return "NewType"
5064

5165
if getattr(annotation, "__qualname__", None):

tests/test_sphinx_autodoc_typehints.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pathlib
55
import re
66
import sys
7+
import types
78
import typing
89
from functools import cmp_to_key
910
from io import StringIO
@@ -134,6 +135,10 @@ def __getitem__(self, params):
134135
[
135136
pytest.param(str, "builtins", "str", (), id="str"),
136137
pytest.param(None, "builtins", "None", (), id="None"),
138+
pytest.param(ModuleType, "types", "ModuleType", (), id="ModuleType"),
139+
pytest.param(FunctionType, "types", "FunctionType", (), id="FunctionType"),
140+
pytest.param(types.CodeType, "types", "CodeType", (), id="CodeType"),
141+
pytest.param(types.CoroutineType, "types", "CoroutineType", (), id="CoroutineType"),
137142
pytest.param(Any, "typing", "Any", (), id="Any"),
138143
pytest.param(AnyStr, "typing", "AnyStr", (), id="AnyStr"),
139144
pytest.param(Dict, "typing", "Dict", (), id="Dict"),
@@ -170,9 +175,10 @@ def __getitem__(self, params):
170175
],
171176
)
172177
def test_parse_annotation(annotation: Any, module: str, class_name: str, args: tuple[Any, ...]) -> None:
173-
assert get_annotation_module(annotation) == module
174-
assert get_annotation_class_name(annotation, module) == class_name
175-
assert get_annotation_args(annotation, module, class_name) == args
178+
got_mod = get_annotation_module(annotation)
179+
got_cls = get_annotation_class_name(annotation, module)
180+
got_args = get_annotation_args(annotation, module, class_name)
181+
assert (got_mod, got_cls, got_args) == (module, class_name, args)
176182

177183

178184
@pytest.mark.parametrize(
@@ -181,6 +187,8 @@ def test_parse_annotation(annotation: Any, module: str, class_name: str, args: t
181187
(str, ":py:class:`str`"),
182188
(int, ":py:class:`int`"),
183189
(StringIO, ":py:class:`~io.StringIO`"),
190+
(FunctionType, ":py:class:`~types.FunctionType`"),
191+
(ModuleType, ":py:class:`~types.ModuleType`"),
184192
(type(None), ":py:obj:`None`"),
185193
(type, ":py:class:`type`"),
186194
(collections.abc.Callable, ":py:class:`~collections.abc.Callable`"),

whitelist.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ autouse
44
backfill
55
conf
66
contravariant
7+
Coroutine
78
cpython
89
csv
910
dedent
@@ -31,7 +32,9 @@ iterdir
3132
kwonlyargs
3233
libs
3334
metaclass
35+
ModuleType
3436
multiline
37+
newtype
3538
nptyping
3639
param
3740
parametrized

0 commit comments

Comments
 (0)