Merge pull request #7632 from eric-wieser/better-inspect-failure

Do not crash if `is_builtin_class_method` is passed a non-type
This commit is contained in:
Takeshi KOMIYA 2020-05-09 14:56:40 +09:00 committed by GitHub
commit e49fc778e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 6 deletions

View File

@ -389,12 +389,23 @@ def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
Why this function needed? CPython implements int.__init__ by Descriptor
but PyPy implements it by pure Python code.
"""
classes = [c for c in inspect.getmro(obj) if attr_name in c.__dict__]
cls = classes[0] if classes else object
if not hasattr(builtins, safe_getattr(cls, '__name__', '')):
try:
mro = inspect.getmro(obj)
except AttributeError:
# no __mro__, assume the object has no methods as we know them
return False
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls
try:
cls = next(c for c in mro if attr_name in safe_getattr(c, '__dict__', {}))
except StopIteration:
return False
try:
name = safe_getattr(cls, '__name__')
except AttributeError:
return False
return getattr(builtins, name, None) is cls
def signature(subject: Callable, bound_method: bool = False) -> inspect.Signature:

View File

@ -18,7 +18,7 @@ from inspect import Parameter
import pytest
from sphinx.util import inspect
from sphinx.util.inspect import stringify_signature
from sphinx.util.inspect import stringify_signature, is_builtin_class_method
def test_signature():
@ -579,3 +579,21 @@ def test_getdoc_inherited_decorated_method():
assert inspect.getdoc(Bar.meth, getattr, False, Bar, "meth") is None
assert inspect.getdoc(Bar.meth, getattr, True, Bar, "meth") == "docstring."
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')