diff options
-rw-r--r-- | Doc/library/inspect.rst | 8 | ||||
-rw-r--r-- | Lib/inspect.py | 20 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 46 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-03-31-20-53-11.bpo-29418.8Qa9cQ.rst | 1 |
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. |