summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/inspect.rst8
-rw-r--r--Lib/inspect.py20
-rw-r--r--Lib/test/test_inspect.py46
-rw-r--r--Misc/NEWS.d/next/Library/2020-03-31-20-53-11.bpo-29418.8Qa9cQ.rst1
4 files changed, 63 insertions, 12 deletions
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index 711f510..7a6dc9c 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -429,6 +429,14 @@ attributes:
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)
Return ``True`` if the object is a user-defined or built-in function or method.
diff --git a/Lib/inspect.py b/Lib/inspect.py
index eb45f81..9c1283a 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -121,6 +121,7 @@ __all__ = [
"ismemberdescriptor",
"ismethod",
"ismethoddescriptor",
+ "ismethodwrapper",
"ismodule",
"isroutine",
"istraceback",
@@ -509,12 +510,17 @@ def isbuiltin(object):
__self__ instance to which a method is bound, or None"""
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):
"""Return true if the object is any kind of function or method."""
return (isbuiltin(object)
or isfunction(object)
or ismethod(object)
- or ismethoddescriptor(object))
+ or ismethoddescriptor(object)
+ or ismethodwrapper(object))
def isabstract(object):
"""Return true if the object is an abstract base class (ABC)."""
@@ -1887,13 +1893,9 @@ def getcoroutinelocals(coroutine):
###############################################################################
-_WrapperDescriptor = type(type.__call__)
-_MethodWrapper = type(all.__call__)
-_ClassMethodWrapper = type(int.__dict__['from_bytes'])
-
-_NonUserDefinedCallables = (_WrapperDescriptor,
- _MethodWrapper,
- _ClassMethodWrapper,
+_NonUserDefinedCallables = (types.WrapperDescriptorType,
+ types.MethodWrapperType,
+ types.ClassMethodDescriptorType,
types.BuiltinFunctionType)
@@ -2533,7 +2535,7 @@ def _signature_from_callable(obj, *,
elif not isinstance(obj, _NonUserDefinedCallables):
# An object with __call__
# 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)
call = _signature_get_user_defined_method(type(obj), '__call__')
if call is not None:
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index 29589a7..9e3c770 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -44,7 +44,8 @@ from test.test_import import _ready_to_import
# isbuiltin, isroutine, isgenerator, isgeneratorfunction, getmembers,
# getdoc, getfile, getmodule, getsourcefile, getcomments, getsource,
# getclasstree, getargvalues, formatargvalues,
-# currentframe, stack, trace, isdatadescriptor
+# currentframe, stack, trace, isdatadescriptor,
+# ismethodwrapper
# NOTE: There are some additional tests relating to interaction with
# zipimport in the test_zipimport_support test module.
@@ -93,7 +94,8 @@ class IsTestBase(unittest.TestCase):
inspect.ismodule, inspect.istraceback,
inspect.isgenerator, inspect.isgeneratorfunction,
inspect.iscoroutine, inspect.iscoroutinefunction,
- inspect.isasyncgen, inspect.isasyncgenfunction])
+ inspect.isasyncgen, inspect.isasyncgenfunction,
+ inspect.ismethodwrapper])
def istest(self, predicate, exp):
obj = eval(exp)
@@ -169,6 +171,14 @@ class TestPredicates(IsTestBase):
self.istest(inspect.ismemberdescriptor, 'datetime.timedelta.days')
else:
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):
async_gen_coro = async_generator_function_example(1)
@@ -241,8 +251,38 @@ class TestPredicates(IsTestBase):
coro.close(); gen_coro.close() # silence warnings
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))
+ # 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):
self.istest(inspect.isclass, 'mod.StupidGit')
diff --git a/Misc/NEWS.d/next/Library/2020-03-31-20-53-11.bpo-29418.8Qa9cQ.rst b/Misc/NEWS.d/next/Library/2020-03-31-20-53-11.bpo-29418.8Qa9cQ.rst
new file mode 100644
index 0000000..b188ac3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-03-31-20-53-11.bpo-29418.8Qa9cQ.rst
@@ -0,0 +1 @@
+Implement :func:`inspect.ismethodwrapper` and fix :func:`inspect.isroutine` for cases where methodwrapper is given. Patch by Hakan Çelik.