def test_ismock(): with mock(['sphinx.unknown']): mod1 = import_module('sphinx.unknown') mod2 = import_module('sphinx.application') class Inherited(mod1.Class): pass assert ismock(mod1) is True assert ismock(mod1.Class) is True assert ismock(Inherited) is False assert ismock(mod2) is False assert ismock(mod2.Sphinx) is False
def restify(cls: Optional[Type], mode: str = 'fully-qualified-except-typing') -> str: """Convert python class to a reST reference. :param mode: Specify a method how annotations will be stringified. 'fully-qualified-except-typing' Show the module name and qualified name of the annotation except the "typing" module. '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': modprefix = '~' else: modprefix = '' try: if cls is None or cls is NoneType: return ':py:obj:`None`' elif cls is Ellipsis: 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 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): # newtypes have correct module info since Python 3.10+ return ':py:class:`%s%s.%s`' % (modprefix, cls.__module__, cls.__name__) else: return ':py:class:`%s`' % cls.__name__ elif UnionType and isinstance(cls, UnionType): if len(cls.__args__) > 1 and None in cls.__args__: args = ' | '.join(restify(a, mode) for a in cls.__args__ if a) return 'Optional[%s]' % args else: return ' | '.join(restify(a, mode) for a in cls.__args__) elif cls.__module__ in ('__builtin__', 'builtins'): if hasattr(cls, '__args__'): return ':py:class:`%s`\\ [%s]' % ( cls.__name__, ', '.join(restify(arg, mode) for arg in cls.__args__), ) else: return ':py:class:`%s`' % cls.__name__ else: return _restify_py37(cls, mode) except (AttributeError, TypeError): return inspect.object_description(cls)
def import_object(self, raiseerror: bool = False) -> bool: """Overrides the default object importing method""" success = False with mock(self.config.autodoc_mock_imports): try: ret = importer.import_object( self.modname, self.objpath, self.objtype, 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) success = True except ImportError as exc: if raiseerror: raise logger.warning(exc.args[0], type="autodoc", subtype="import_object") self.env.note_reread() success = False return success
def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable) -> Dict[str, "ObjectMember"]: """Get members and attributes of target class.""" from sphinx.ext.autodoc import INSTANCEATTR, ObjectMember # the members directly defined in the class obj_dict = attrgetter(subject, '__dict__', {}) members = {} # type: Dict[str, ObjectMember] # enum members if isenumclass(subject): for name, value in subject.__members__.items(): if name not in members: members[name] = ObjectMember(name, value, class_=subject) superclass = subject.__mro__[1] for name in obj_dict: if name not in superclass.__dict__: value = safe_getattr(subject, name) members[name] = ObjectMember(name, value, class_=subject) # members in __slots__ try: __slots__ = getslots(subject) if __slots__: from sphinx.ext.autodoc import SLOTSATTR for name, docstring in __slots__.items(): members[name] = ObjectMember(name, SLOTSATTR, class_=subject, docstring=docstring) except (TypeError, ValueError): pass # other members 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: members[unmangled] = ObjectMember(unmangled, value, class_=subject) else: members[unmangled] = ObjectMember(unmangled, value) except AttributeError: continue try: for cls in getmro(subject): try: modname = safe_getattr(cls, '__module__') qualname = safe_getattr(cls, '__qualname__') analyzer = ModuleAnalyzer.for_module(modname) analyzer.analyze() except AttributeError: qualname = None analyzer = None except PycodeError: analyzer = None # annotation only member (ex. attr: int) for name in getannotations(cls): name = unmangle(cls, name) if name and name not in members: if analyzer and (qualname, name) in analyzer.attr_docs: docstring = '\n'.join(analyzer.attr_docs[qualname, name]) else: docstring = None members[name] = ObjectMember(name, INSTANCEATTR, class_=cls, docstring=docstring) # append instance attributes (cf. self.attr1) if analyzer knows if analyzer: for (ns, name), docstring in analyzer.attr_docs.items(): if ns == qualname and name not in members: members[name] = ObjectMember( name, INSTANCEATTR, class_=cls, docstring='\n'.join(docstring)) except AttributeError: pass return members
def stringify(annotation: Any, mode: str = 'fully-qualified-except-typing') -> str: """Stringify type annotation object. :param mode: Specify a method how annotations will be stringified. 'fully-qualified-except-typing' Show the module name and qualified name of the annotation except the "typing" module. 'smart' Show the name of the annotation. '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': modprefix = '~' else: modprefix = '' if isinstance(annotation, str): if annotation.startswith("'") and annotation.endswith("'"): # might be a double Forward-ref'ed type. Go unquoting. return annotation[1:-1] else: return annotation elif isinstance(annotation, TypeVar): if (annotation.__module__ == 'typing' and mode in ('fully-qualified-except-typing', 'smart')): return annotation.__name__ else: return modprefix + '.'.join([annotation.__module__, annotation.__name__]) elif inspect.isNewType(annotation): if sys.version_info > (3, 10): # newtypes have correct module info since Python 3.10+ return modprefix + '%s.%s' % (annotation.__module__, annotation.__name__) else: return annotation.__name__ elif not annotation: 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 is_invalid_builtin_class(annotation): return modprefix + INVALID_BUILTIN_CLASSES[annotation] elif str(annotation).startswith('typing.Annotated'): # for py310+ pass elif (getattr(annotation, '__module__', None) == 'builtins' and getattr(annotation, '__qualname__', None)): if hasattr(annotation, '__args__'): # PEP 585 generic return repr(annotation) else: return annotation.__qualname__ elif annotation is Ellipsis: return '...' if sys.version_info >= (3, 7): # py37+ return _stringify_py37(annotation, mode) else: return _stringify_py36(annotation, mode)
def get_class_members( subject: Any, objpath: List[str], attrgetter: Callable, inherit_docstrings: bool = True) -> Dict[str, "ObjectMember"]: """Get members and attributes of target class.""" from sphinx.ext.autodoc import INSTANCEATTR, ObjectMember # the members directly defined in the class obj_dict = attrgetter(subject, '__dict__', {}) members: Dict[str, ObjectMember] = {} # enum members if isenumclass(subject): for name, value in subject.__members__.items(): if name not in members: members[name] = ObjectMember(name, value, class_=subject) superclass = subject.__mro__[1] for name in obj_dict: if name not in superclass.__dict__: value = safe_getattr(subject, name) members[name] = ObjectMember(name, value, class_=subject) # members in __slots__ try: __slots__ = getslots(subject) if __slots__: from sphinx.ext.autodoc import SLOTSATTR for name, docstring in __slots__.items(): members[name] = ObjectMember(name, SLOTSATTR, class_=subject, docstring=docstring) except (TypeError, ValueError): pass # other members 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: members[unmangled] = ObjectMember(unmangled, value, class_=subject) else: members[unmangled] = ObjectMember(unmangled, value) except AttributeError: continue try: for cls in getmro(subject): try: modname = safe_getattr(cls, '__module__') qualname = safe_getattr(cls, '__qualname__') analyzer = ModuleAnalyzer.for_module(modname) analyzer.analyze() except AttributeError: qualname = None analyzer = None except PycodeError: analyzer = None # annotation only member (ex. attr: int) for name in getannotations(cls): name = unmangle(cls, name) if name and name not in members: if analyzer and (qualname, name) in analyzer.attr_docs: docstring = '\n'.join(analyzer.attr_docs[qualname, name]) else: docstring = None members[name] = ObjectMember(name, INSTANCEATTR, class_=cls, docstring=docstring) # append or complete instance attributes (cf. self.attr1) if analyzer knows if analyzer: for (ns, name), docstring in analyzer.attr_docs.items(): if ns == qualname and name not in members: # otherwise unknown instance attribute members[name] = ObjectMember( name, INSTANCEATTR, class_=cls, docstring='\n'.join(docstring)) elif (ns == qualname and docstring and isinstance(members[name], ObjectMember) and not members[name].docstring): if cls != subject and not inherit_docstrings: # If we are in the MRO of the class and not the class itself, # and we do not want to inherit docstrings, then skip setting # the docstring below continue # attribute is already known, because dir(subject) enumerates it. # But it has no docstring yet members[name].docstring = '\n'.join(docstring) except AttributeError: pass return members