mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
`sphinx.util.inspect.getdoc()` clean the docstring up if the method is inherited and not having docstring. That causes indentations are removed on processing it.
708 lines
25 KiB
Python
708 lines
25 KiB
Python
"""
|
|
test_util_inspect
|
|
~~~~~~~~~~~~~~~
|
|
|
|
Tests util.inspect functions.
|
|
|
|
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
|
|
:license: BSD, see LICENSE for details.
|
|
"""
|
|
|
|
import ast
|
|
import datetime
|
|
import functools
|
|
import sys
|
|
import types
|
|
from inspect import Parameter
|
|
|
|
import _testcapi
|
|
import pytest
|
|
|
|
from sphinx.util import inspect
|
|
from sphinx.util.inspect import TypeAliasNamespace, stringify_signature
|
|
|
|
|
|
def test_TypeAliasNamespace():
|
|
import logging.config
|
|
type_alias = TypeAliasNamespace({'logging.Filter': 'MyFilter',
|
|
'logging.Handler': 'MyHandler',
|
|
'logging.handlers.SyslogHandler': 'MySyslogHandler'})
|
|
|
|
assert type_alias['logging'].Filter == 'MyFilter'
|
|
assert type_alias['logging'].Handler == 'MyHandler'
|
|
assert type_alias['logging'].handlers.SyslogHandler == 'MySyslogHandler'
|
|
assert type_alias['logging'].Logger == logging.Logger
|
|
assert type_alias['logging'].config == logging.config
|
|
|
|
with pytest.raises(KeyError):
|
|
assert type_alias['log']
|
|
|
|
with pytest.raises(KeyError):
|
|
assert type_alias['unknown']
|
|
|
|
|
|
def test_signature():
|
|
# literals
|
|
with pytest.raises(TypeError):
|
|
inspect.signature(1)
|
|
|
|
with pytest.raises(TypeError):
|
|
inspect.signature('')
|
|
|
|
# builtins are supported on a case-by-case basis, depending on whether
|
|
# they define __text_signature__
|
|
if getattr(list, '__text_signature__', None):
|
|
sig = inspect.stringify_signature(inspect.signature(list))
|
|
assert sig == '(iterable=(), /)'
|
|
else:
|
|
with pytest.raises(ValueError):
|
|
inspect.signature(list)
|
|
|
|
# normal function
|
|
def func(a, b, c=1, d=2, *e, **f):
|
|
pass
|
|
|
|
sig = inspect.stringify_signature(inspect.signature(func))
|
|
assert sig == '(a, b, c=1, d=2, *e, **f)'
|
|
|
|
|
|
def test_signature_partial():
|
|
def fun(a, b, c=1, d=2):
|
|
pass
|
|
p = functools.partial(fun, 10, c=11)
|
|
|
|
sig = inspect.signature(p)
|
|
assert stringify_signature(sig) == '(b, *, c=11, d=2)'
|
|
|
|
|
|
def test_signature_methods():
|
|
class Foo:
|
|
def meth1(self, arg1, **kwargs):
|
|
pass
|
|
|
|
@classmethod
|
|
def meth2(cls, arg1, *args, **kwargs):
|
|
pass
|
|
|
|
@staticmethod
|
|
def meth3(arg1, *args, **kwargs):
|
|
pass
|
|
|
|
@functools.wraps(Foo().meth1)
|
|
def wrapped_bound_method(*args, **kwargs):
|
|
pass
|
|
|
|
# unbound method
|
|
sig = inspect.signature(Foo.meth1)
|
|
assert stringify_signature(sig) == '(self, arg1, **kwargs)'
|
|
|
|
sig = inspect.signature(Foo.meth1, bound_method=True)
|
|
assert stringify_signature(sig) == '(arg1, **kwargs)'
|
|
|
|
# bound method
|
|
sig = inspect.signature(Foo().meth1)
|
|
assert stringify_signature(sig) == '(arg1, **kwargs)'
|
|
|
|
# class method
|
|
sig = inspect.signature(Foo.meth2)
|
|
assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
|
|
|
|
sig = inspect.signature(Foo().meth2)
|
|
assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
|
|
|
|
# static method
|
|
sig = inspect.signature(Foo.meth3)
|
|
assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
|
|
|
|
sig = inspect.signature(Foo().meth3)
|
|
assert stringify_signature(sig) == '(arg1, *args, **kwargs)'
|
|
|
|
# wrapped bound method
|
|
sig = inspect.signature(wrapped_bound_method)
|
|
assert stringify_signature(sig) == '(arg1, **kwargs)'
|
|
|
|
|
|
def test_signature_partialmethod():
|
|
from functools import partialmethod
|
|
|
|
class Foo:
|
|
def meth1(self, arg1, arg2, arg3=None, arg4=None):
|
|
pass
|
|
|
|
def meth2(self, arg1, arg2):
|
|
pass
|
|
|
|
foo = partialmethod(meth1, 1, 2)
|
|
bar = partialmethod(meth1, 1, arg3=3)
|
|
baz = partialmethod(meth2, 1, 2)
|
|
|
|
subject = Foo()
|
|
sig = inspect.signature(subject.foo)
|
|
assert stringify_signature(sig) == '(arg3=None, arg4=None)'
|
|
|
|
sig = inspect.signature(subject.bar)
|
|
assert stringify_signature(sig) == '(arg2, *, arg3=3, arg4=None)'
|
|
|
|
sig = inspect.signature(subject.baz)
|
|
assert stringify_signature(sig) == '()'
|
|
|
|
|
|
def test_signature_annotations():
|
|
from .typing_test_data import (Node, f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12,
|
|
f13, f14, f15, f16, f17, f18, f19, f20, f21)
|
|
|
|
# Class annotations
|
|
sig = inspect.signature(f0)
|
|
assert stringify_signature(sig) == '(x: int, y: numbers.Integral) -> None'
|
|
|
|
# Generic types with concrete parameters
|
|
sig = inspect.signature(f1)
|
|
assert stringify_signature(sig) == '(x: List[int]) -> List[int]'
|
|
|
|
# TypeVars and generic types with TypeVars
|
|
sig = inspect.signature(f2)
|
|
if sys.version_info < (3, 7):
|
|
assert stringify_signature(sig) == '(x: List[T], y: List[T_co], z: T) -> List[T_contra]'
|
|
else:
|
|
assert stringify_signature(sig) == ('(x: List[tests.typing_test_data.T],'
|
|
' y: List[tests.typing_test_data.T_co],'
|
|
' z: tests.typing_test_data.T'
|
|
') -> List[tests.typing_test_data.T_contra]')
|
|
|
|
# Union types
|
|
sig = inspect.signature(f3)
|
|
assert stringify_signature(sig) == '(x: Union[str, numbers.Integral]) -> None'
|
|
|
|
# Quoted annotations
|
|
sig = inspect.signature(f4)
|
|
assert stringify_signature(sig) == '(x: str, y: str) -> None'
|
|
|
|
# Keyword-only arguments
|
|
sig = inspect.signature(f5)
|
|
assert stringify_signature(sig) == '(x: int, *, y: str, z: str) -> None'
|
|
|
|
# Keyword-only arguments with varargs
|
|
sig = inspect.signature(f6)
|
|
assert stringify_signature(sig) == '(x: int, *args, y: str, z: str) -> None'
|
|
|
|
# Space around '=' for defaults
|
|
sig = inspect.signature(f7)
|
|
assert stringify_signature(sig) == '(x: Optional[int] = None, y: dict = {}) -> None'
|
|
|
|
# Callable types
|
|
sig = inspect.signature(f8)
|
|
assert stringify_signature(sig) == '(x: Callable[[int, str], int]) -> None'
|
|
|
|
sig = inspect.signature(f9)
|
|
assert stringify_signature(sig) == '(x: Callable) -> None'
|
|
|
|
# Tuple types
|
|
sig = inspect.signature(f10)
|
|
assert stringify_signature(sig) == '(x: Tuple[int, str], y: Tuple[int, ...]) -> None'
|
|
|
|
# Instance annotations
|
|
sig = inspect.signature(f11)
|
|
assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None'
|
|
|
|
# tuple with more than two items
|
|
sig = inspect.signature(f12)
|
|
assert stringify_signature(sig) == '() -> Tuple[int, str, int]'
|
|
|
|
# optional
|
|
sig = inspect.signature(f13)
|
|
assert stringify_signature(sig) == '() -> Optional[str]'
|
|
|
|
# optional union
|
|
sig = inspect.signature(f20)
|
|
if sys.version_info < (3, 7):
|
|
assert stringify_signature(sig) in ('() -> Optional[Union[int, str]]',
|
|
'() -> Optional[Union[str, int]]')
|
|
else:
|
|
assert stringify_signature(sig) == '() -> Optional[Union[int, str]]'
|
|
|
|
# Any
|
|
sig = inspect.signature(f14)
|
|
assert stringify_signature(sig) == '() -> Any'
|
|
|
|
# ForwardRef
|
|
sig = inspect.signature(f15)
|
|
assert stringify_signature(sig) == '(x: Unknown, y: int) -> Any'
|
|
|
|
# keyword only arguments (1)
|
|
sig = inspect.signature(f16)
|
|
assert stringify_signature(sig) == '(arg1, arg2, *, arg3=None, arg4=None)'
|
|
|
|
# keyword only arguments (2)
|
|
sig = inspect.signature(f17)
|
|
assert stringify_signature(sig) == '(*, arg3, arg4)'
|
|
|
|
sig = inspect.signature(f18)
|
|
assert stringify_signature(sig) == '(self, arg1: Union[int, Tuple] = 10) -> List[Dict]'
|
|
|
|
# annotations for variadic and keyword parameters
|
|
sig = inspect.signature(f19)
|
|
assert stringify_signature(sig) == '(*args: int, **kwargs: str)'
|
|
|
|
# default value is inspect.Signature.empty
|
|
sig = inspect.signature(f21)
|
|
assert stringify_signature(sig) == "(arg1='whatever', arg2)"
|
|
|
|
# type hints by string
|
|
sig = inspect.signature(Node.children)
|
|
assert stringify_signature(sig) == '(self) -> List[tests.typing_test_data.Node]'
|
|
|
|
sig = inspect.signature(Node.__init__)
|
|
assert stringify_signature(sig) == '(self, parent: Optional[tests.typing_test_data.Node]) -> None'
|
|
|
|
# show_annotation is False
|
|
sig = inspect.signature(f7)
|
|
assert stringify_signature(sig, show_annotation=False) == '(x=None, y={})'
|
|
|
|
# show_return_annotation is False
|
|
sig = inspect.signature(f7)
|
|
assert stringify_signature(sig, show_return_annotation=False) == '(x: Optional[int] = None, y: dict = {})'
|
|
|
|
|
|
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
|
|
@pytest.mark.sphinx(testroot='ext-autodoc')
|
|
def test_signature_annotations_py38(app):
|
|
from target.pep570 import bar, baz, foo, qux
|
|
|
|
# case: separator at head
|
|
sig = inspect.signature(foo)
|
|
assert stringify_signature(sig) == '(*, a, b)'
|
|
|
|
# case: separator in the middle
|
|
sig = inspect.signature(bar)
|
|
assert stringify_signature(sig) == '(a, b, /, c, d)'
|
|
|
|
sig = inspect.signature(baz)
|
|
assert stringify_signature(sig) == '(a, /, *, b)'
|
|
|
|
# case: separator at tail
|
|
sig = inspect.signature(qux)
|
|
assert stringify_signature(sig) == '(a, b, /)'
|
|
|
|
|
|
def test_signature_from_str_basic():
|
|
signature = '(a, b, *args, c=0, d="blah", **kwargs)'
|
|
sig = inspect.signature_from_str(signature)
|
|
assert list(sig.parameters.keys()) == ['a', 'b', 'args', 'c', 'd', 'kwargs']
|
|
assert sig.parameters['a'].name == 'a'
|
|
assert sig.parameters['a'].kind == Parameter.POSITIONAL_OR_KEYWORD
|
|
assert sig.parameters['a'].default == Parameter.empty
|
|
assert sig.parameters['a'].annotation == Parameter.empty
|
|
assert sig.parameters['b'].name == 'b'
|
|
assert sig.parameters['b'].kind == Parameter.POSITIONAL_OR_KEYWORD
|
|
assert sig.parameters['b'].default == Parameter.empty
|
|
assert sig.parameters['b'].annotation == Parameter.empty
|
|
assert sig.parameters['args'].name == 'args'
|
|
assert sig.parameters['args'].kind == Parameter.VAR_POSITIONAL
|
|
assert sig.parameters['args'].default == Parameter.empty
|
|
assert sig.parameters['args'].annotation == Parameter.empty
|
|
assert sig.parameters['c'].name == 'c'
|
|
assert sig.parameters['c'].kind == Parameter.KEYWORD_ONLY
|
|
assert sig.parameters['c'].default == '0'
|
|
assert sig.parameters['c'].annotation == Parameter.empty
|
|
assert sig.parameters['d'].name == 'd'
|
|
assert sig.parameters['d'].kind == Parameter.KEYWORD_ONLY
|
|
assert sig.parameters['d'].default == "'blah'"
|
|
assert sig.parameters['d'].annotation == Parameter.empty
|
|
assert sig.parameters['kwargs'].name == 'kwargs'
|
|
assert sig.parameters['kwargs'].kind == Parameter.VAR_KEYWORD
|
|
assert sig.parameters['kwargs'].default == Parameter.empty
|
|
assert sig.parameters['kwargs'].annotation == Parameter.empty
|
|
assert sig.return_annotation == Parameter.empty
|
|
|
|
|
|
def test_signature_from_str_default_values():
|
|
signature = ('(a=0, b=0.0, c="str", d=b"bytes", e=..., f=True, '
|
|
'g=[1, 2, 3], h={"a": 1}, i={1, 2, 3}, '
|
|
'j=lambda x, y: None, k=None, l=object(), m=foo.bar.CONSTANT)')
|
|
sig = inspect.signature_from_str(signature)
|
|
assert sig.parameters['a'].default == '0'
|
|
assert sig.parameters['b'].default == '0.0'
|
|
assert sig.parameters['c'].default == "'str'"
|
|
assert sig.parameters['d'].default == "b'bytes'"
|
|
assert sig.parameters['e'].default == '...'
|
|
assert sig.parameters['f'].default == 'True'
|
|
assert sig.parameters['g'].default == '[1, 2, 3]'
|
|
assert sig.parameters['h'].default == "{'a': 1}"
|
|
assert sig.parameters['i'].default == '{1, 2, 3}'
|
|
assert sig.parameters['j'].default == 'lambda x, y: ...'
|
|
assert sig.parameters['k'].default == 'None'
|
|
assert sig.parameters['l'].default == 'object()'
|
|
assert sig.parameters['m'].default == 'foo.bar.CONSTANT'
|
|
|
|
|
|
def test_signature_from_str_annotations():
|
|
signature = '(a: int, *args: bytes, b: str = "blah", **kwargs: float) -> None'
|
|
sig = inspect.signature_from_str(signature)
|
|
assert list(sig.parameters.keys()) == ['a', 'args', 'b', 'kwargs']
|
|
assert sig.parameters['a'].annotation == "int"
|
|
assert sig.parameters['args'].annotation == "bytes"
|
|
assert sig.parameters['b'].annotation == "str"
|
|
assert sig.parameters['kwargs'].annotation == "float"
|
|
assert sig.return_annotation == 'None'
|
|
|
|
|
|
def test_signature_from_str_complex_annotations():
|
|
sig = inspect.signature_from_str('() -> Tuple[str, int, ...]')
|
|
assert sig.return_annotation == 'Tuple[str, int, ...]'
|
|
|
|
sig = inspect.signature_from_str('() -> Callable[[int, int], int]')
|
|
assert sig.return_annotation == 'Callable[[int, int], int]'
|
|
|
|
|
|
def test_signature_from_str_kwonly_args():
|
|
sig = inspect.signature_from_str('(a, *, b)')
|
|
assert list(sig.parameters.keys()) == ['a', 'b']
|
|
assert sig.parameters['a'].kind == Parameter.POSITIONAL_OR_KEYWORD
|
|
assert sig.parameters['a'].default == Parameter.empty
|
|
assert sig.parameters['b'].kind == Parameter.KEYWORD_ONLY
|
|
assert sig.parameters['b'].default == Parameter.empty
|
|
|
|
|
|
@pytest.mark.skipif(sys.version_info < (3, 8),
|
|
reason='python-3.8 or above is required')
|
|
def test_signature_from_str_positionaly_only_args():
|
|
sig = inspect.signature_from_str('(a, b=0, /, c=1)')
|
|
assert list(sig.parameters.keys()) == ['a', 'b', 'c']
|
|
assert sig.parameters['a'].kind == Parameter.POSITIONAL_ONLY
|
|
assert sig.parameters['a'].default == Parameter.empty
|
|
assert sig.parameters['b'].kind == Parameter.POSITIONAL_ONLY
|
|
assert sig.parameters['b'].default == '0'
|
|
assert sig.parameters['c'].kind == Parameter.POSITIONAL_OR_KEYWORD
|
|
assert sig.parameters['c'].default == '1'
|
|
|
|
|
|
def test_signature_from_str_invalid():
|
|
with pytest.raises(SyntaxError):
|
|
inspect.signature_from_str('')
|
|
|
|
|
|
def test_signature_from_ast():
|
|
signature = 'def func(a, b, *args, c=0, d="blah", **kwargs): pass'
|
|
tree = ast.parse(signature)
|
|
sig = inspect.signature_from_ast(tree.body[0])
|
|
assert list(sig.parameters.keys()) == ['a', 'b', 'args', 'c', 'd', 'kwargs']
|
|
assert sig.parameters['a'].name == 'a'
|
|
assert sig.parameters['a'].kind == Parameter.POSITIONAL_OR_KEYWORD
|
|
assert sig.parameters['a'].default == Parameter.empty
|
|
assert sig.parameters['a'].annotation == Parameter.empty
|
|
assert sig.parameters['b'].name == 'b'
|
|
assert sig.parameters['b'].kind == Parameter.POSITIONAL_OR_KEYWORD
|
|
assert sig.parameters['b'].default == Parameter.empty
|
|
assert sig.parameters['b'].annotation == Parameter.empty
|
|
assert sig.parameters['args'].name == 'args'
|
|
assert sig.parameters['args'].kind == Parameter.VAR_POSITIONAL
|
|
assert sig.parameters['args'].default == Parameter.empty
|
|
assert sig.parameters['args'].annotation == Parameter.empty
|
|
assert sig.parameters['c'].name == 'c'
|
|
assert sig.parameters['c'].kind == Parameter.KEYWORD_ONLY
|
|
assert sig.parameters['c'].default == '0'
|
|
assert sig.parameters['c'].annotation == Parameter.empty
|
|
assert sig.parameters['d'].name == 'd'
|
|
assert sig.parameters['d'].kind == Parameter.KEYWORD_ONLY
|
|
assert sig.parameters['d'].default == "'blah'"
|
|
assert sig.parameters['d'].annotation == Parameter.empty
|
|
assert sig.parameters['kwargs'].name == 'kwargs'
|
|
assert sig.parameters['kwargs'].kind == Parameter.VAR_KEYWORD
|
|
assert sig.parameters['kwargs'].default == Parameter.empty
|
|
assert sig.parameters['kwargs'].annotation == Parameter.empty
|
|
assert sig.return_annotation == Parameter.empty
|
|
|
|
|
|
def test_safe_getattr_with_default():
|
|
class Foo:
|
|
def __getattr__(self, item):
|
|
raise Exception
|
|
|
|
obj = Foo()
|
|
|
|
result = inspect.safe_getattr(obj, 'bar', 'baz')
|
|
|
|
assert result == 'baz'
|
|
|
|
|
|
def test_safe_getattr_with_exception():
|
|
class Foo:
|
|
def __getattr__(self, item):
|
|
raise Exception
|
|
|
|
obj = Foo()
|
|
|
|
try:
|
|
inspect.safe_getattr(obj, 'bar')
|
|
except AttributeError as exc:
|
|
assert exc.args[0] == 'bar'
|
|
else:
|
|
pytest.fail('AttributeError not raised')
|
|
|
|
|
|
def test_safe_getattr_with_property_exception():
|
|
class Foo:
|
|
@property
|
|
def bar(self):
|
|
raise Exception
|
|
|
|
obj = Foo()
|
|
|
|
try:
|
|
inspect.safe_getattr(obj, 'bar')
|
|
except AttributeError as exc:
|
|
assert exc.args[0] == 'bar'
|
|
else:
|
|
pytest.fail('AttributeError not raised')
|
|
|
|
|
|
def test_safe_getattr_with___dict___override():
|
|
class Foo:
|
|
@property
|
|
def __dict__(self):
|
|
raise Exception
|
|
|
|
obj = Foo()
|
|
|
|
try:
|
|
inspect.safe_getattr(obj, 'bar')
|
|
except AttributeError as exc:
|
|
assert exc.args[0] == 'bar'
|
|
else:
|
|
pytest.fail('AttributeError not raised')
|
|
|
|
|
|
def test_dictionary_sorting():
|
|
dictionary = {"c": 3, "a": 1, "d": 2, "b": 4}
|
|
description = inspect.object_description(dictionary)
|
|
assert description == "{'a': 1, 'b': 4, 'c': 3, 'd': 2}"
|
|
|
|
|
|
def test_set_sorting():
|
|
set_ = set("gfedcba")
|
|
description = inspect.object_description(set_)
|
|
assert description == "{'a', 'b', 'c', 'd', 'e', 'f', 'g'}"
|
|
|
|
|
|
def test_set_sorting_fallback():
|
|
set_ = {None, 1}
|
|
description = inspect.object_description(set_)
|
|
assert description in ("{1, None}", "{None, 1}")
|
|
|
|
|
|
def test_frozenset_sorting():
|
|
frozenset_ = frozenset("gfedcba")
|
|
description = inspect.object_description(frozenset_)
|
|
assert description == "frozenset({'a', 'b', 'c', 'd', 'e', 'f', 'g'})"
|
|
|
|
|
|
def test_frozenset_sorting_fallback():
|
|
frozenset_ = frozenset((None, 1))
|
|
description = inspect.object_description(frozenset_)
|
|
assert description in ("frozenset({1, None})", "frozenset({None, 1})")
|
|
|
|
|
|
def test_dict_customtype():
|
|
class CustomType:
|
|
def __init__(self, value):
|
|
self._value = value
|
|
|
|
def __repr__(self):
|
|
return "<CustomType(%r)>" % self._value
|
|
|
|
dictionary = {CustomType(2): 2, CustomType(1): 1}
|
|
description = inspect.object_description(dictionary)
|
|
# Type is unsortable, just check that it does not crash
|
|
assert "<CustomType(2)>: 2" in description
|
|
|
|
|
|
def test_getslots():
|
|
class Foo:
|
|
pass
|
|
|
|
class Bar:
|
|
__slots__ = ['attr']
|
|
|
|
class Baz:
|
|
__slots__ = {'attr': 'docstring'}
|
|
|
|
class Qux:
|
|
__slots__ = 'attr'
|
|
|
|
assert inspect.getslots(Foo) is None
|
|
assert inspect.getslots(Bar) == {'attr': None}
|
|
assert inspect.getslots(Baz) == {'attr': 'docstring'}
|
|
assert inspect.getslots(Qux) == {'attr': None}
|
|
|
|
with pytest.raises(TypeError):
|
|
inspect.getslots(Bar())
|
|
|
|
|
|
@pytest.mark.sphinx(testroot='ext-autodoc')
|
|
def test_isclassmethod(app):
|
|
from target.methods import Base, Inherited
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
# 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
|
|
|
|
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
|
|
assert inspect.isfunction(Base.partialmeth) is True # partial-ed method of class
|
|
assert inspect.isfunction(Base().meth) is False # method of instance
|
|
assert inspect.isfunction(builtin_func) is False # builtin function
|
|
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
|
|
|
|
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
|
|
assert inspect.isbuiltin(partial_func) is False # partial-ed function
|
|
assert inspect.isbuiltin(Base.meth) is False # method of class
|
|
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
|
|
|
|
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
|
|
assert inspect.isdescriptor(Base().meth) is True # method of instance
|
|
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
|
|
|
|
testinstancemethod = _testcapi.instancemethod(str.__repr__)
|
|
|
|
assert inspect.isattributedescriptor(Base.prop) is True # property
|
|
assert inspect.isattributedescriptor(Base.meth) is False # method
|
|
assert inspect.isattributedescriptor(Base.staticmeth) is False # staticmethod
|
|
assert inspect.isattributedescriptor(Base.classmeth) is False # classmetho
|
|
assert inspect.isattributedescriptor(Descriptor) is False # custom descriptor class # NOQA
|
|
assert inspect.isattributedescriptor(str.join) is False # MethodDescriptorType # NOQA
|
|
assert inspect.isattributedescriptor(object.__init__) is False # WrapperDescriptorType # NOQA
|
|
assert inspect.isattributedescriptor(dict.__dict__['fromkeys']) is False # ClassMethodDescriptorType # NOQA
|
|
assert inspect.isattributedescriptor(types.FrameType.f_locals) is True # GetSetDescriptorType # NOQA
|
|
assert inspect.isattributedescriptor(datetime.timedelta.days) is True # MemberDescriptorType # NOQA
|
|
assert inspect.isattributedescriptor(testinstancemethod) is False # instancemethod (C-API) # NOQA
|
|
|
|
|
|
def test_isproperty(app):
|
|
from target.functions import func
|
|
from target.methods import Base
|
|
|
|
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
|
|
assert inspect.isproperty(Base().meth) is False # method of instance
|
|
assert inspect.isproperty(func) is False # function
|
|
|
|
|
|
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
|
|
@pytest.mark.sphinx(testroot='ext-autodoc')
|
|
def test_isgenericalias(app):
|
|
from target.genericalias import C, T
|
|
from target.methods import Base
|
|
|
|
assert inspect.isgenericalias(C) is True
|
|
assert inspect.isgenericalias(T) is True
|
|
assert inspect.isgenericalias(object()) is False
|
|
assert inspect.isgenericalias(Base) is False
|
|
|
|
|
|
def test_unpartial():
|
|
def func1(a, b, c):
|
|
pass
|
|
|
|
func2 = functools.partial(func1, 1)
|
|
func2.__doc__ = "func2"
|
|
func3 = functools.partial(func2, 2) # nested partial object
|
|
|
|
assert inspect.unpartial(func2) is func1
|
|
assert inspect.unpartial(func3) is func1
|
|
|
|
|
|
def test_getdoc_inherited_decorated_method():
|
|
class Foo:
|
|
def meth(self):
|
|
"""
|
|
docstring
|
|
indented text
|
|
"""
|
|
|
|
class Bar(Foo):
|
|
@functools.lru_cache()
|
|
def meth(self):
|
|
# inherited and decorated method
|
|
pass
|
|
|
|
assert inspect.getdoc(Bar.meth, getattr, False, Bar, "meth") is None
|
|
assert inspect.getdoc(Bar.meth, getattr, True, Bar, "meth") == Foo.meth.__doc__
|
|
|
|
|
|
def test_is_builtin_class_method():
|
|
class MyInt(int):
|
|
def my_method(self):
|
|
pass
|
|
|
|
assert inspect.is_builtin_class_method(MyInt, 'to_bytes')
|
|
assert inspect.is_builtin_class_method(MyInt, '__init__')
|
|
assert not inspect.is_builtin_class_method(MyInt, 'my_method')
|
|
assert not inspect.is_builtin_class_method(MyInt, 'does_not_exist')
|
|
assert not inspect.is_builtin_class_method(4, 'still does not crash')
|
|
|
|
class ObjectWithMroAttr:
|
|
def __init__(self, mro_attr):
|
|
self.__mro__ = mro_attr
|
|
|
|
assert not inspect.is_builtin_class_method(ObjectWithMroAttr([1, 2, 3]), 'still does not crash')
|