Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sphinx-doc/sphinx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2021-01-23 20:08:19 +0300
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2021-01-24 08:18:00 +0300
commita78c6b799f0dab414dadc79fa3920d0581c01279 (patch)
tree3d25df9bc31db18c7d3f1c5e6793ce0b8ab3a425 /sphinx/ext
parent3a0a6556c59a7b31586dd97b43101f8dbfd2ef63 (diff)
Fix #8134: autodoc: crashes when mocked decorator takes arguments
autodoc crashed when a decorator in mocked module takes arguments because mock system returns the first argument for the decorator as a decorated object. This changes the approach for mocking decorators that remembers arguments for each decoration, and fetch the latest argument on generating document.
Diffstat (limited to 'sphinx/ext')
-rw-r--r--sphinx/ext/autodoc/__init__.py6
-rw-r--r--sphinx/ext/autodoc/importer.py4
-rw-r--r--sphinx/ext/autodoc/mock.py27
3 files changed, 29 insertions, 8 deletions
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 83c7d28c4..1490d1551 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -27,7 +27,7 @@ from sphinx.deprecation import (RemovedInSphinx40Warning, RemovedInSphinx50Warni
from sphinx.environment import BuildEnvironment
from sphinx.ext.autodoc.importer import (get_class_members, get_object_members, import_module,
import_object)
-from sphinx.ext.autodoc.mock import ismock, mock
+from sphinx.ext.autodoc.mock import ismock, mock, undecorate
from sphinx.locale import _, __
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.util import inspect, logging
@@ -422,6 +422,8 @@ class Documenter:
attrgetter=self.get_attr,
warningiserror=self.config.autodoc_warningiserror)
self.module, self.parent, self.object_name, self.object = ret
+ if ismock(self.object):
+ self.object = undecorate(self.object)
return True
except ImportError as exc:
if raiseerror:
@@ -1054,6 +1056,8 @@ class ModuleDocumenter(Documenter):
for name in dir(self.object):
try:
value = safe_getattr(self.object, name, None)
+ if ismock(value):
+ value = undecorate(value)
docstring = attr_docs.get(('', name), [])
members[name] = ObjectMember(name, value, docstring="\n".join(docstring))
except AttributeError:
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index ffcb27ecc..d7c9b93f5 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -15,6 +15,7 @@ from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Optional, Tup
from sphinx.deprecation import (RemovedInSphinx40Warning, RemovedInSphinx50Warning,
deprecated_alias)
+from sphinx.ext.autodoc.mock import ismock, undecorate
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.util import logging
from sphinx.util.inspect import (getannotations, getmro, getslots, isclass, isenumclass,
@@ -285,6 +286,9 @@ def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable
for name in dir(subject):
try:
value = attrgetter(subject, name)
+ if ismock(value):
+ value = undecorate(value)
+
unmangled = unmangle(subject, name)
if unmangled and unmangled not in members:
if name in obj_dict:
diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/mock.py
index 3d4f76410..d3f4291c2 100644
--- a/sphinx/ext/autodoc/mock.py
+++ b/sphinx/ext/autodoc/mock.py
@@ -13,7 +13,7 @@ import os
import sys
from importlib.abc import Loader, MetaPathFinder
from importlib.machinery import ModuleSpec
-from types import FunctionType, MethodType, ModuleType
+from types import ModuleType
from typing import Any, Generator, Iterator, List, Sequence, Tuple, Union
from sphinx.util import logging
@@ -27,6 +27,7 @@ class _MockObject:
__display_name__ = '_MockObject'
__sphinx_mock__ = True
+ __sphinx_decorator_args__ = () # type: Tuple[Any, ...]
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
if len(args) == 3 and isinstance(args[1], tuple):
@@ -60,18 +61,19 @@ class _MockObject:
return _make_subclass(key, self.__display_name__, self.__class__)()
def __call__(self, *args: Any, **kwargs: Any) -> Any:
- if args and type(args[0]) in [type, FunctionType, MethodType]:
- # Appears to be a decorator, pass through unchanged
- return args[0]
- return self
+ call = self.__class__()
+ call.__sphinx_decorator_args__ = args
+ return call
def __repr__(self) -> str:
return self.__display_name__
def _make_subclass(name: str, module: str, superclass: Any = _MockObject,
- attributes: Any = None) -> Any:
- attrs = {'__module__': module, '__display_name__': module + '.' + name}
+ attributes: Any = None, decorator_args: Tuple = ()) -> Any:
+ attrs = {'__module__': module,
+ '__display_name__': module + '.' + name,
+ '__sphinx_decorator_args__': decorator_args}
attrs.update(attributes or {})
return type(name, (superclass,), attrs)
@@ -172,3 +174,14 @@ def ismock(subject: Any) -> bool:
pass
return False
+
+
+def undecorate(subject: _MockObject) -> Any:
+ """Unwrap mock if *subject* is decorated by mocked object.
+
+ If not decorated, returns given *subject* itself.
+ """
+ if ismock(subject) and subject.__sphinx_decorator_args__:
+ return subject.__sphinx_decorator_args__[0]
+ else:
+ return subject