Fix #10133: autodoc: Crashed when mocked module is used for type annotation

This commit is contained in:
Takeshi KOMIYA 2022-01-27 02:40:37 +09:00
parent 2be0630951
commit 444e27865d
4 changed files with 19 additions and 0 deletions

View File

@ -19,6 +19,8 @@ Features added
Bugs fixed
----------
* #10133: autodoc: Crashed when mocked module is used for type annotation
Testing
--------

View File

@ -154,6 +154,11 @@ def mock(modnames: List[str]) -> Generator[None, None, None]:
finder.invalidate_caches()
def ismockmodule(subject: Any) -> bool:
"""Check if the object is a mocked module."""
return isinstance(subject, _MockModule)
def ismock(subject: Any) -> bool:
"""Check if the object is mocked."""
# check the object has '__sphinx_mock__' attribute

View File

@ -116,6 +116,7 @@ def restify(cls: Optional[Type], mode: str = 'fully-qualified-except-typing') ->
'smart'
Show the name of the annotation.
"""
from sphinx.ext.autodoc.mock import ismock, ismockmodule # lazy loading
from sphinx.util import inspect # lazy loading
if mode == 'smart':
@ -130,6 +131,10 @@ def restify(cls: Optional[Type], mode: str = 'fully-qualified-except-typing') ->
return '...'
elif isinstance(cls, str):
return cls
elif ismockmodule(cls):
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:
return ':py:class:`%s%s`' % (modprefix, INVALID_BUILTIN_CLASSES[cls])
elif inspect.isNewType(cls):
@ -335,6 +340,7 @@ def stringify(annotation: Any, mode: str = 'fully-qualified-except-typing') -> s
'fully-qualified'
Show the module name and qualified name of the annotation.
"""
from sphinx.ext.autodoc.mock import ismock, ismockmodule # lazy loading
from sphinx.util import inspect # lazy loading
if mode == 'smart':
@ -364,6 +370,10 @@ def stringify(annotation: Any, mode: str = 'fully-qualified-except-typing') -> s
return repr(annotation)
elif annotation is NoneType:
return 'None'
elif ismockmodule(annotation):
return modprefix + annotation.__name__
elif ismock(annotation):
return modprefix + '%s.%s' % (annotation.__module__, annotation.__name__)
elif annotation in INVALID_BUILTIN_CLASSES:
return modprefix + INVALID_BUILTIN_CLASSES[annotation]
elif str(annotation).startswith('typing.Annotated'): # for py310+

View File

@ -213,6 +213,7 @@ def test_restify_broken_type_hints():
def test_restify_mock():
with mock(['unknown']):
import unknown
assert restify(unknown) == ':py:class:`unknown`'
assert restify(unknown.secret.Class) == ':py:class:`unknown.secret.Class`'
assert restify(unknown.secret.Class, "smart") == ':py:class:`~unknown.secret.Class`'
@ -480,5 +481,6 @@ def test_stringify_broken_type_hints():
def test_stringify_mock():
with mock(['unknown']):
import unknown
assert stringify(unknown) == 'unknown'
assert stringify(unknown.secret.Class) == 'unknown.secret.Class'
assert stringify(unknown.secret.Class, "smart") == 'unknown.secret.Class'