diff --git a/CHANGES b/CHANGES index cca11712e01..849fc297119 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,8 @@ Bugs fixed * #10133: autodoc: Crashed when mocked module is used for type annotation * #10146: autodoc: :confval:`autodoc_default_options` does not support ``no-value`` option +* #9971: autodoc: TypeError is raised when the target object is annotated by + unhashable object * #9529: LaTeX: named auto numbered footnote (ex. ``[#named]``) that is referred multiple times was rendered to a question mark * #10122: sphinx-build: make.bat does not check the installation of sphinx-build diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index 5cd0c230ef2..f47788005c6 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -50,6 +50,14 @@ def _evaluate(self, globalns: Dict, localns: Dict) -> Any: } +def is_invalid_builtin_class(obj: Any) -> bool: + """Check *obj* is an invalid built-in class.""" + try: + return obj in INVALID_BUILTIN_CLASSES + except TypeError: # unhashable type + return False + + # Text like nodes which are initialized with text and rawsource TextlikeNode = Union[nodes.Text, nodes.TextElement] @@ -135,7 +143,7 @@ def restify(cls: Optional[Type], mode: str = 'fully-qualified-except-typing') -> return ':py:class:`%s%s`' % (modprefix, cls.__name__) elif ismock(cls): return ':py:class:`%s%s.%s`' % (modprefix, cls.__module__, cls.__name__) - elif cls in INVALID_BUILTIN_CLASSES: + elif is_invalid_builtin_class(cls): return ':py:class:`%s%s`' % (modprefix, INVALID_BUILTIN_CLASSES[cls]) elif inspect.isNewType(cls): if sys.version_info > (3, 10): @@ -374,7 +382,7 @@ def stringify(annotation: Any, mode: str = 'fully-qualified-except-typing') -> s return modprefix + annotation.__name__ elif ismock(annotation): return modprefix + '%s.%s' % (annotation.__module__, annotation.__name__) - elif annotation in INVALID_BUILTIN_CLASSES: + elif is_invalid_builtin_class(annotation): return modprefix + INVALID_BUILTIN_CLASSES[annotation] elif str(annotation).startswith('typing.Annotated'): # for py310+ pass