Skip to content

Commit d181c2d

Browse files
FIX TypeError: unhashable type (#319)
Fixes #318. The access to `_TYPES_DICT` should be wrapped in a try block so that if the type is unhashable the error can be caught and discarded.
1 parent d931001 commit d181c2d

File tree

3 files changed

+24
-5
lines changed

3 files changed

+24
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## 1.22
44

55
- Allow Sphinx explicitly to write in parallel.
6+
- Fixed crash when documenting ParamSpecArgs
67

78
## 1.21.7
89

src/sphinx_autodoc_typehints/__init__.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,25 @@
3535
_TYPES_DICT[types.FunctionType] = "FunctionType"
3636

3737

38+
def _get_types_type(obj: Any) -> str | None:
39+
try:
40+
return _TYPES_DICT.get(obj)
41+
except Exception:
42+
# e.g. exception: unhashable type
43+
return None
44+
45+
3846
def get_annotation_module(annotation: Any) -> str:
39-
if annotation in _TYPES_DICT:
40-
return "types"
4147
if annotation is None:
4248
return "builtins"
49+
if _get_types_type(annotation) is not None:
50+
return "types"
4351
is_new_type = sys.version_info >= (3, 10) and isinstance(annotation, NewType)
44-
if is_new_type or isinstance(annotation, TypeVar) or type(annotation).__name__ == "ParamSpec":
52+
if (
53+
is_new_type
54+
or isinstance(annotation, TypeVar)
55+
or type(annotation).__name__ in ("ParamSpec", "ParamSpecArgs", "ParamSpecKwargs")
56+
):
4557
return "typing"
4658
if hasattr(annotation, "__module__"):
4759
return annotation.__module__ # type: ignore # deduced Any
@@ -63,8 +75,9 @@ def get_annotation_class_name(annotation: Any, module: str) -> str:
6375
return "None"
6476
if annotation is AnyStr:
6577
return "AnyStr"
66-
if annotation in _TYPES_DICT:
67-
return _TYPES_DICT[annotation]
78+
val = _get_types_type(annotation)
79+
if val is not None:
80+
return val
6881
if _is_newtype(annotation):
6982
return "NewType"
7083

tests/test_sphinx_autodoc_typehints.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
S = TypeVar("S", bound="miss") # type: ignore # miss not defined on purpose # noqa: F821
5959
W = NewType("W", str)
6060
P = typing_extensions.ParamSpec("P")
61+
P_args = P.args # type:ignore[attr-defined]
62+
P_kwargs = P.kwargs # type:ignore[attr-defined]
6163
P_co = typing_extensions.ParamSpec("P_co", covariant=True) # type: ignore
6264
P_contra = typing_extensions.ParamSpec("P_contra", contravariant=True) # type: ignore
6365
P_bound = typing_extensions.ParamSpec("P_bound", bound=str) # type: ignore
@@ -163,6 +165,9 @@ def __getitem__(self, params):
163165
pytest.param(Match[str], "typing", "Match", (str,), id="Match_parametrized"),
164166
pytest.param(IO, "typing", "IO", (), id="IO"),
165167
pytest.param(W, "typing", "NewType", (str,), id="W"),
168+
pytest.param(P, "typing", "ParamSpec", (), id="P"),
169+
pytest.param(P_args, "typing", "ParamSpecArgs", (), id="P_args"),
170+
pytest.param(P_kwargs, "typing", "ParamSpecKwargs", (), id="P_kwargs"),
166171
pytest.param(Metaclass, __name__, "Metaclass", (), id="Metaclass"),
167172
pytest.param(Slotted, __name__, "Slotted", (), id="Slotted"),
168173
pytest.param(A, __name__, "A", (), id="A"),

0 commit comments

Comments
 (0)