bpo-29418: Implement inspect.ismethodwrapper and fix inspect.isroutine for cases where methodwrapper is given (GH-19261)

Automerge-Triggered-By: GH:isidentical
This commit is contained in:
Hakan Çelik 2022-02-16 15:46:20 +03:00 committed by GitHub
parent 3954fcb83f
commit 562c13f573
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 12 deletions

View file

@ -429,6 +429,14 @@ attributes:
Return ``True`` if the object is a built-in function or a bound built-in method. Return ``True`` if the object is a built-in function or a bound built-in method.
.. function:: ismethodwrapper(object)
Return ``True`` if the type of object is a :class:`~types.MethodWrapperType`.
These are instances of :class:`~types.MethodWrapperType`, such as :meth:`~object().__str__`,
:meth:`~object().__eq__` and :meth:`~object().__repr__`
.. function:: isroutine(object) .. function:: isroutine(object)
Return ``True`` if the object is a user-defined or built-in function or method. Return ``True`` if the object is a user-defined or built-in function or method.

View file

@ -121,6 +121,7 @@ __all__ = [
"ismemberdescriptor", "ismemberdescriptor",
"ismethod", "ismethod",
"ismethoddescriptor", "ismethoddescriptor",
"ismethodwrapper",
"ismodule", "ismodule",
"isroutine", "isroutine",
"istraceback", "istraceback",
@ -509,12 +510,17 @@ def isbuiltin(object):
__self__ instance to which a method is bound, or None""" __self__ instance to which a method is bound, or None"""
return isinstance(object, types.BuiltinFunctionType) return isinstance(object, types.BuiltinFunctionType)
def ismethodwrapper(object):
"""Return true if the object is a method wrapper."""
return isinstance(object, types.MethodWrapperType)
def isroutine(object): def isroutine(object):
"""Return true if the object is any kind of function or method.""" """Return true if the object is any kind of function or method."""
return (isbuiltin(object) return (isbuiltin(object)
or isfunction(object) or isfunction(object)
or ismethod(object) or ismethod(object)
or ismethoddescriptor(object)) or ismethoddescriptor(object)
or ismethodwrapper(object))
def isabstract(object): def isabstract(object):
"""Return true if the object is an abstract base class (ABC).""" """Return true if the object is an abstract base class (ABC)."""
@ -1887,13 +1893,9 @@ def getcoroutinelocals(coroutine):
############################################################################### ###############################################################################
_WrapperDescriptor = type(type.__call__) _NonUserDefinedCallables = (types.WrapperDescriptorType,
_MethodWrapper = type(all.__call__) types.MethodWrapperType,
_ClassMethodWrapper = type(int.__dict__['from_bytes']) types.ClassMethodDescriptorType,
_NonUserDefinedCallables = (_WrapperDescriptor,
_MethodWrapper,
_ClassMethodWrapper,
types.BuiltinFunctionType) types.BuiltinFunctionType)
@ -2533,7 +2535,7 @@ def _signature_from_callable(obj, *,
elif not isinstance(obj, _NonUserDefinedCallables): elif not isinstance(obj, _NonUserDefinedCallables):
# An object with __call__ # An object with __call__
# We also check that the 'obj' is not an instance of # We also check that the 'obj' is not an instance of
# _WrapperDescriptor or _MethodWrapper to avoid # types.WrapperDescriptorType or types.MethodWrapperType to avoid
# infinite recursion (and even potential segfault) # infinite recursion (and even potential segfault)
call = _signature_get_user_defined_method(type(obj), '__call__') call = _signature_get_user_defined_method(type(obj), '__call__')
if call is not None: if call is not None:

View file

@ -44,7 +44,8 @@ from test.test_import import _ready_to_import
# isbuiltin, isroutine, isgenerator, isgeneratorfunction, getmembers, # isbuiltin, isroutine, isgenerator, isgeneratorfunction, getmembers,
# getdoc, getfile, getmodule, getsourcefile, getcomments, getsource, # getdoc, getfile, getmodule, getsourcefile, getcomments, getsource,
# getclasstree, getargvalues, formatargvalues, # getclasstree, getargvalues, formatargvalues,
# currentframe, stack, trace, isdatadescriptor # currentframe, stack, trace, isdatadescriptor,
# ismethodwrapper
# NOTE: There are some additional tests relating to interaction with # NOTE: There are some additional tests relating to interaction with
# zipimport in the test_zipimport_support test module. # zipimport in the test_zipimport_support test module.
@ -93,7 +94,8 @@ class IsTestBase(unittest.TestCase):
inspect.ismodule, inspect.istraceback, inspect.ismodule, inspect.istraceback,
inspect.isgenerator, inspect.isgeneratorfunction, inspect.isgenerator, inspect.isgeneratorfunction,
inspect.iscoroutine, inspect.iscoroutinefunction, inspect.iscoroutine, inspect.iscoroutinefunction,
inspect.isasyncgen, inspect.isasyncgenfunction]) inspect.isasyncgen, inspect.isasyncgenfunction,
inspect.ismethodwrapper])
def istest(self, predicate, exp): def istest(self, predicate, exp):
obj = eval(exp) obj = eval(exp)
@ -169,6 +171,14 @@ class TestPredicates(IsTestBase):
self.istest(inspect.ismemberdescriptor, 'datetime.timedelta.days') self.istest(inspect.ismemberdescriptor, 'datetime.timedelta.days')
else: else:
self.assertFalse(inspect.ismemberdescriptor(datetime.timedelta.days)) self.assertFalse(inspect.ismemberdescriptor(datetime.timedelta.days))
self.istest(inspect.ismethodwrapper, "object().__str__")
self.istest(inspect.ismethodwrapper, "object().__eq__")
self.istest(inspect.ismethodwrapper, "object().__repr__")
self.assertFalse(inspect.ismethodwrapper(type))
self.assertFalse(inspect.ismethodwrapper(int))
self.assertFalse(inspect.ismethodwrapper(type("AnyClass", (), {})))
def test_iscoroutine(self): def test_iscoroutine(self):
async_gen_coro = async_generator_function_example(1) async_gen_coro = async_generator_function_example(1)
@ -241,8 +251,38 @@ class TestPredicates(IsTestBase):
coro.close(); gen_coro.close() # silence warnings coro.close(); gen_coro.close() # silence warnings
def test_isroutine(self): def test_isroutine(self):
self.assertTrue(inspect.isroutine(mod.spam)) # method
self.assertTrue(inspect.isroutine(git.argue))
self.assertTrue(inspect.isroutine(mod.custom_method))
self.assertTrue(inspect.isroutine([].count)) self.assertTrue(inspect.isroutine([].count))
# function
self.assertTrue(inspect.isroutine(mod.spam))
self.assertTrue(inspect.isroutine(mod.StupidGit.abuse))
# slot-wrapper
self.assertTrue(inspect.isroutine(object.__init__))
self.assertTrue(inspect.isroutine(object.__str__))
self.assertTrue(inspect.isroutine(object.__lt__))
self.assertTrue(inspect.isroutine(int.__lt__))
# method-wrapper
self.assertTrue(inspect.isroutine(object().__init__))
self.assertTrue(inspect.isroutine(object().__str__))
self.assertTrue(inspect.isroutine(object().__lt__))
self.assertTrue(inspect.isroutine((42).__lt__))
# method-descriptor
self.assertTrue(inspect.isroutine(str.join))
self.assertTrue(inspect.isroutine(list.append))
self.assertTrue(inspect.isroutine(''.join))
self.assertTrue(inspect.isroutine([].append))
# object
self.assertFalse(inspect.isroutine(object))
self.assertFalse(inspect.isroutine(object()))
self.assertFalse(inspect.isroutine(str()))
# module
self.assertFalse(inspect.isroutine(mod))
# type
self.assertFalse(inspect.isroutine(type))
self.assertFalse(inspect.isroutine(int))
self.assertFalse(inspect.isroutine(type('some_class', (), {})))
def test_isclass(self): def test_isclass(self):
self.istest(inspect.isclass, 'mod.StupidGit') self.istest(inspect.isclass, 'mod.StupidGit')

View file

@ -0,0 +1 @@
Implement :func:`inspect.ismethodwrapper` and fix :func:`inspect.isroutine` for cases where methodwrapper is given. Patch by Hakan Çelik.