Skip to content

Commit

Permalink
Fix sphinx-doc#10133: autodoc: Crashed when mocked module is used for…
Browse files Browse the repository at this point in the history
… type annotation
  • Loading branch information
tk0miya committed Jan 26, 2022
1 parent 2be0630 commit c609f36
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES
Expand Up @@ -13,6 +13,7 @@ Deprecated
Features added
--------------

* #10133: autodoc: Crashed when mocked module is used for type annotation
* #9494, #9456: html search: Add a config variable
:confval:`html_show_search_summary` to enable/disable the search summaries

Expand Down
6 changes: 6 additions & 0 deletions sphinx/ext/autodoc/mock.py
Expand Up @@ -154,6 +154,12 @@ def mock(modnames: List[str]) -> Generator[None, None, None]:
finder.invalidate_caches()


def ismockmod(subject: Any) -> bool:
"""Check if the object is a mocked module."""
if isinstance(subject, _MockModule):
return True


def ismock(subject: Any) -> bool:
"""Check if the object is mocked."""
# check the object has '__sphinx_mock__' attribute
Expand Down
10 changes: 10 additions & 0 deletions sphinx/util/typing.py
Expand Up @@ -117,6 +117,7 @@ def restify(cls: Optional[Type], mode: str = 'fully-qualified-except-typing') ->
Show the name of the annotation.
"""
from sphinx.util import inspect # lazy loading
from sphinx.ext.autodoc.mock import ismock, ismockmod

if mode == 'smart':
modprefix = '~'
Expand All @@ -130,6 +131,10 @@ def restify(cls: Optional[Type], mode: str = 'fully-qualified-except-typing') ->
return '...'
elif isinstance(cls, str):
return cls
elif ismockmod(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):
Expand Down Expand Up @@ -336,6 +341,7 @@ def stringify(annotation: Any, mode: str = 'fully-qualified-except-typing') -> s
Show the module name and qualified name of the annotation.
"""
from sphinx.util import inspect # lazy loading
from sphinx.ext.autodoc.mock import ismock, ismockmod

if mode == 'smart':
modprefix = '~'
Expand Down Expand Up @@ -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 ismockmod(annotation):
return modprefix + annotation.__name__
elif ismock(annotation):
return modprefix + '%s.%s' % (annotation.__module__, cls.__name__)
elif annotation in INVALID_BUILTIN_CLASSES:
return modprefix + INVALID_BUILTIN_CLASSES[annotation]
elif str(annotation).startswith('typing.Annotated'): # for py310+
Expand Down
2 changes: 2 additions & 0 deletions tests/test_util_typing.py
Expand Up @@ -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`'

Expand Down Expand Up @@ -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'

0 comments on commit c609f36

Please sign in to comment.