mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Refactor `sphinx.util.inspect` and tests (#11527)
This commit is contained in:
@@ -43,12 +43,11 @@ memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
|
||||
|
||||
def unwrap(obj: Any) -> Any:
|
||||
"""Get an original object from wrapped object (wrapped functions)."""
|
||||
if hasattr(obj, '__sphinx_mock__'):
|
||||
# Skip unwrapping mock object to avoid RecursionError
|
||||
return obj
|
||||
try:
|
||||
if hasattr(obj, '__sphinx_mock__'):
|
||||
# Skip unwrapping mock object to avoid RecursionError
|
||||
return obj
|
||||
else:
|
||||
return inspect.unwrap(obj)
|
||||
return inspect.unwrap(obj)
|
||||
except ValueError:
|
||||
# might be a mock object
|
||||
return obj
|
||||
@@ -81,11 +80,9 @@ def getall(obj: Any) -> Sequence[str] | None:
|
||||
__all__ = safe_getattr(obj, '__all__', None)
|
||||
if __all__ is None:
|
||||
return None
|
||||
else:
|
||||
if (isinstance(__all__, (list, tuple)) and all(isinstance(e, str) for e in __all__)):
|
||||
return __all__
|
||||
else:
|
||||
raise ValueError(__all__)
|
||||
if isinstance(__all__, (list, tuple)) and all(isinstance(e, str) for e in __all__):
|
||||
return __all__
|
||||
raise ValueError(__all__)
|
||||
|
||||
|
||||
def getannotations(obj: Any) -> Mapping[str, Any]:
|
||||
@@ -157,10 +154,9 @@ def isNewType(obj: Any) -> bool:
|
||||
"""Check the if object is a kind of NewType."""
|
||||
if sys.version_info[:2] >= (3, 10):
|
||||
return isinstance(obj, typing.NewType)
|
||||
else:
|
||||
__module__ = safe_getattr(obj, '__module__', None)
|
||||
__qualname__ = safe_getattr(obj, '__qualname__', None)
|
||||
return __module__ == 'typing' and __qualname__ == 'NewType.<locals>.new_type'
|
||||
__module__ = safe_getattr(obj, '__module__', None)
|
||||
__qualname__ = safe_getattr(obj, '__qualname__', None)
|
||||
return __module__ == 'typing' and __qualname__ == 'NewType.<locals>.new_type'
|
||||
|
||||
|
||||
def isenumclass(x: Any) -> bool:
|
||||
@@ -209,7 +205,7 @@ def isstaticmethod(obj: Any, cls: Any = None, name: str | None = None) -> bool:
|
||||
"""Check if the object is staticmethod."""
|
||||
if isinstance(obj, staticmethod):
|
||||
return True
|
||||
elif cls and name:
|
||||
if cls and name:
|
||||
# trace __mro__ if the method is defined in parent class
|
||||
#
|
||||
# .. note:: This only works well with new style classes.
|
||||
@@ -217,7 +213,6 @@ def isstaticmethod(obj: Any, cls: Any = None, name: str | None = None) -> bool:
|
||||
meth = basecls.__dict__.get(name)
|
||||
if meth:
|
||||
return isinstance(meth, staticmethod)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@@ -291,17 +286,17 @@ def is_singledispatch_method(obj: Any) -> bool:
|
||||
|
||||
def isfunction(obj: Any) -> bool:
|
||||
"""Check if the object is function."""
|
||||
return inspect.isfunction(unwrap_all(obj))
|
||||
return inspect.isfunction(unpartial(obj))
|
||||
|
||||
|
||||
def isbuiltin(obj: Any) -> bool:
|
||||
"""Check if the object is builtin."""
|
||||
return inspect.isbuiltin(unwrap_all(obj))
|
||||
"""Check if the object is function."""
|
||||
return inspect.isbuiltin(unpartial(obj))
|
||||
|
||||
|
||||
def isroutine(obj: Any) -> bool:
|
||||
"""Check is any kind of function or method."""
|
||||
return inspect.isroutine(unwrap_all(obj))
|
||||
return inspect.isroutine(unpartial(obj))
|
||||
|
||||
|
||||
def iscoroutinefunction(obj: Any) -> bool:
|
||||
|
||||
@@ -9,7 +9,7 @@ import functools
|
||||
import sys
|
||||
import types
|
||||
from inspect import Parameter
|
||||
from typing import Optional
|
||||
from typing import Callable, List, Optional, Union # NoQA: UP035
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -18,6 +18,74 @@ from sphinx.util.inspect import TypeAliasForwardRef, TypeAliasNamespace, stringi
|
||||
from sphinx.util.typing import stringify_annotation
|
||||
|
||||
|
||||
class Base:
|
||||
def meth(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def staticmeth():
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def classmeth(cls):
|
||||
pass
|
||||
|
||||
@property
|
||||
def prop(self):
|
||||
pass
|
||||
|
||||
partialmeth = functools.partialmethod(meth)
|
||||
|
||||
async def coroutinemeth(self):
|
||||
pass
|
||||
|
||||
partial_coroutinemeth = functools.partialmethod(coroutinemeth)
|
||||
|
||||
@classmethod
|
||||
async def coroutineclassmeth(cls):
|
||||
"""A documented coroutine classmethod"""
|
||||
pass
|
||||
|
||||
|
||||
class Inherited(Base):
|
||||
pass
|
||||
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
|
||||
async def coroutinefunc():
|
||||
pass
|
||||
|
||||
|
||||
async def asyncgenerator():
|
||||
yield
|
||||
|
||||
partial_func = functools.partial(func)
|
||||
partial_coroutinefunc = functools.partial(coroutinefunc)
|
||||
|
||||
builtin_func = print
|
||||
partial_builtin_func = functools.partial(print)
|
||||
|
||||
|
||||
class Descriptor:
|
||||
def __get__(self, obj, typ=None):
|
||||
pass
|
||||
|
||||
|
||||
class _Callable:
|
||||
def __call__(self):
|
||||
pass
|
||||
|
||||
|
||||
def _decorator(f):
|
||||
@functools.wraps(f)
|
||||
def wrapper():
|
||||
return f()
|
||||
return wrapper
|
||||
|
||||
|
||||
def test_TypeAliasForwardRef():
|
||||
alias = TypeAliasForwardRef('example')
|
||||
assert stringify_annotation(alias, 'fully-qualified-except-typing') == 'example'
|
||||
@@ -619,47 +687,39 @@ def test_getslots():
|
||||
inspect.getslots(Bar())
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||
def test_isclassmethod(app):
|
||||
from target.methods import Base, Inherited
|
||||
|
||||
def test_isclassmethod():
|
||||
assert inspect.isclassmethod(Base.classmeth) is True
|
||||
assert inspect.isclassmethod(Base.meth) is False
|
||||
assert inspect.isclassmethod(Inherited.classmeth) is True
|
||||
assert inspect.isclassmethod(Inherited.meth) is False
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||
def test_isstaticmethod(app):
|
||||
from target.methods import Base, Inherited
|
||||
|
||||
def test_isstaticmethod():
|
||||
assert inspect.isstaticmethod(Base.staticmeth, Base, 'staticmeth') is True
|
||||
assert inspect.isstaticmethod(Base.meth, Base, 'meth') is False
|
||||
assert inspect.isstaticmethod(Inherited.staticmeth, Inherited, 'staticmeth') is True
|
||||
assert inspect.isstaticmethod(Inherited.meth, Inherited, 'meth') is False
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||
def test_iscoroutinefunction(app):
|
||||
from target.functions import coroutinefunc, func, partial_coroutinefunc
|
||||
from target.methods import Base
|
||||
|
||||
def test_iscoroutinefunction():
|
||||
assert inspect.iscoroutinefunction(func) is False # function
|
||||
assert inspect.iscoroutinefunction(coroutinefunc) is True # coroutine
|
||||
assert inspect.iscoroutinefunction(partial_coroutinefunc) is True # partial-ed coroutine
|
||||
assert inspect.iscoroutinefunction(Base.meth) is False # method
|
||||
assert inspect.iscoroutinefunction(Base.coroutinemeth) is True # coroutine-method
|
||||
assert inspect.iscoroutinefunction(Base.__dict__["coroutineclassmeth"]) is True # coroutine classmethod
|
||||
|
||||
# partial-ed coroutine-method
|
||||
partial_coroutinemeth = Base.__dict__['partial_coroutinemeth']
|
||||
assert inspect.iscoroutinefunction(partial_coroutinemeth) is True
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||
def test_isfunction(app):
|
||||
from target.functions import builtin_func, func, partial_builtin_func, partial_func
|
||||
from target.methods import Base
|
||||
def test_iscoroutinefunction_wrapped():
|
||||
# function wrapping a callable obj
|
||||
assert inspect.isfunction(_decorator(coroutinefunc)) is True
|
||||
|
||||
|
||||
def test_isfunction():
|
||||
assert inspect.isfunction(func) is True # function
|
||||
assert inspect.isfunction(partial_func) is True # partial-ed function
|
||||
assert inspect.isfunction(Base.meth) is True # method of class
|
||||
@@ -669,11 +729,12 @@ def test_isfunction(app):
|
||||
assert inspect.isfunction(partial_builtin_func) is False # partial-ed builtin function
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||
def test_isbuiltin(app):
|
||||
from target.functions import builtin_func, func, partial_builtin_func, partial_func
|
||||
from target.methods import Base
|
||||
def test_isfunction_wrapped():
|
||||
# function wrapping a callable obj
|
||||
assert inspect.isfunction(_decorator(_Callable())) is True
|
||||
|
||||
|
||||
def test_isbuiltin():
|
||||
assert inspect.isbuiltin(builtin_func) is True # builtin function
|
||||
assert inspect.isbuiltin(partial_builtin_func) is True # partial-ed builtin function
|
||||
assert inspect.isbuiltin(func) is False # function
|
||||
@@ -682,11 +743,7 @@ def test_isbuiltin(app):
|
||||
assert inspect.isbuiltin(Base().meth) is False # method of instance
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||
def test_isdescriptor(app):
|
||||
from target.functions import func
|
||||
from target.methods import Base
|
||||
|
||||
def test_isdescriptor():
|
||||
assert inspect.isdescriptor(Base.prop) is True # property of class
|
||||
assert inspect.isdescriptor(Base().prop) is False # property of instance
|
||||
assert inspect.isdescriptor(Base.meth) is True # method of class
|
||||
@@ -694,14 +751,7 @@ def test_isdescriptor(app):
|
||||
assert inspect.isdescriptor(func) is True # function
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||
def test_isattributedescriptor(app):
|
||||
from target.methods import Base
|
||||
|
||||
class Descriptor:
|
||||
def __get__(self, obj, typ=None):
|
||||
pass
|
||||
|
||||
def test_isattributedescriptor():
|
||||
assert inspect.isattributedescriptor(Base.prop) is True # property
|
||||
assert inspect.isattributedescriptor(Base.meth) is False # method
|
||||
assert inspect.isattributedescriptor(Base.staticmeth) is False # staticmethod
|
||||
@@ -724,11 +774,7 @@ def test_isattributedescriptor(app):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||
def test_isproperty(app):
|
||||
from target.functions import func
|
||||
from target.methods import Base
|
||||
|
||||
def test_isproperty():
|
||||
assert inspect.isproperty(Base.prop) is True # property of class
|
||||
assert inspect.isproperty(Base().prop) is False # property of instance
|
||||
assert inspect.isproperty(Base.meth) is False # method of class
|
||||
@@ -736,13 +782,20 @@ def test_isproperty(app):
|
||||
assert inspect.isproperty(func) is False # function
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||
def test_isgenericalias(app):
|
||||
from target.genericalias import C, T
|
||||
from target.methods import Base
|
||||
def test_isgenericalias():
|
||||
#: A list of int
|
||||
T = List[int] # NoQA: UP006
|
||||
S = list[Union[str, None]]
|
||||
|
||||
C = Callable[[int], None] # a generic alias not having a doccomment
|
||||
|
||||
assert inspect.isgenericalias(C) is True
|
||||
assert inspect.isgenericalias(Callable) is True
|
||||
assert inspect.isgenericalias(T) is True
|
||||
assert inspect.isgenericalias(List) is True # NoQA: UP006
|
||||
assert inspect.isgenericalias(S) is True
|
||||
assert inspect.isgenericalias(list) is False
|
||||
assert inspect.isgenericalias([]) is False
|
||||
assert inspect.isgenericalias(object()) is False
|
||||
assert inspect.isgenericalias(Base) is False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user