From 740a84de73ad8d02655de0a084036f4b7e49a01b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 7 Jun 2019 17:51:28 +0200 Subject: [PATCH] bpo-37191: Move TestPEP590 from test_capi to test_call (GH-13892) --- Lib/test/test_call.py | 123 ++++++++++++++++++++++++++++++++++++++++++ Lib/test/test_capi.py | 115 --------------------------------------- 2 files changed, 123 insertions(+), 115 deletions(-) diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 9f0a75b84a0..b4ab74903ed 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -475,5 +475,128 @@ class FastCallTests(unittest.TestCase): # called, which changes the keywords dict. compile("pass", "", "exec", x, **x.kwargs) + +Py_TPFLAGS_HAVE_VECTORCALL = 1 << 11 +Py_TPFLAGS_METHOD_DESCRIPTOR = 1 << 17 + + +def testfunction(self): + """some doc""" + return self + + +def testfunction_kw(self, *, kw): + """some doc""" + return self + + +class TestPEP590(unittest.TestCase): + + def test_method_descriptor_flag(self): + import functools + cached = functools.lru_cache(1)(testfunction) + + self.assertFalse(type(repr).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) + self.assertTrue(type(list.append).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) + self.assertTrue(type(list.__add__).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) + self.assertTrue(type(testfunction).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) + self.assertTrue(type(cached).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) + + self.assertTrue(_testcapi.MethodDescriptorBase.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) + self.assertTrue(_testcapi.MethodDescriptorDerived.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) + self.assertFalse(_testcapi.MethodDescriptorNopGet.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) + + # Heap type should not inherit Py_TPFLAGS_METHOD_DESCRIPTOR + class MethodDescriptorHeap(_testcapi.MethodDescriptorBase): + pass + self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) + + def test_vectorcall_flag(self): + self.assertTrue(_testcapi.MethodDescriptorBase.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) + self.assertTrue(_testcapi.MethodDescriptorDerived.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) + self.assertFalse(_testcapi.MethodDescriptorNopGet.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) + self.assertTrue(_testcapi.MethodDescriptor2.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) + + # Heap type should not inherit Py_TPFLAGS_HAVE_VECTORCALL + class MethodDescriptorHeap(_testcapi.MethodDescriptorBase): + pass + self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) + + def test_vectorcall_override(self): + # Check that tp_call can correctly override vectorcall. + # MethodDescriptorNopGet implements tp_call but it inherits from + # MethodDescriptorBase, which implements vectorcall. Since + # MethodDescriptorNopGet returns the args tuple when called, we check + # additionally that no new tuple is created for this call. + args = tuple(range(5)) + f = _testcapi.MethodDescriptorNopGet() + self.assertIs(f(*args), args) + + def test_vectorcall(self): + # Test a bunch of different ways to call objects: + # 1. vectorcall using PyVectorcall_Call() + # (only for objects that support vectorcall directly) + # 2. normal call + # 3. vectorcall using _PyObject_Vectorcall() + # 4. call as bound method + # 5. call using functools.partial + + # A list of (function, args, kwargs, result) calls to test + calls = [(len, (range(42),), {}, 42), + (list.append, ([], 0), {}, None), + ([].append, (0,), {}, None), + (sum, ([36],), {"start":6}, 42), + (testfunction, (42,), {}, 42), + (testfunction_kw, (42,), {"kw":None}, 42), + (_testcapi.MethodDescriptorBase(), (0,), {}, True), + (_testcapi.MethodDescriptorDerived(), (0,), {}, True), + (_testcapi.MethodDescriptor2(), (0,), {}, False)] + + from _testcapi import pyobject_vectorcall, pyvectorcall_call + from types import MethodType + from functools import partial + + def vectorcall(func, args, kwargs): + args = *args, *kwargs.values() + kwnames = tuple(kwargs) + return pyobject_vectorcall(func, args, kwnames) + + for (func, args, kwargs, expected) in calls: + with self.subTest(str(func)): + if not kwargs: + self.assertEqual(expected, pyvectorcall_call(func, args)) + self.assertEqual(expected, pyvectorcall_call(func, args, kwargs)) + + # Add derived classes (which do not support vectorcall directly, + # but do support all other ways of calling). + + class MethodDescriptorHeap(_testcapi.MethodDescriptorBase): + pass + + class MethodDescriptorOverridden(_testcapi.MethodDescriptorBase): + def __call__(self, n): + return 'new' + + calls += [ + (MethodDescriptorHeap(), (0,), {}, True), + (MethodDescriptorOverridden(), (0,), {}, 'new'), + ] + + for (func, args, kwargs, expected) in calls: + with self.subTest(str(func)): + args1 = args[1:] + meth = MethodType(func, args[0]) + wrapped = partial(func) + if not kwargs: + self.assertEqual(expected, func(*args)) + self.assertEqual(expected, pyobject_vectorcall(func, args, None)) + self.assertEqual(expected, meth(*args1)) + self.assertEqual(expected, wrapped(*args)) + self.assertEqual(expected, func(*args, **kwargs)) + self.assertEqual(expected, vectorcall(func, args, kwargs)) + self.assertEqual(expected, meth(*args1, **kwargs)) + self.assertEqual(expected, wrapped(*args, **kwargs)) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 6a20f479c53..45fabd59915 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -27,18 +27,11 @@ _testcapi = support.import_module('_testcapi') # Were we compiled --with-pydebug or with #define Py_DEBUG? Py_DEBUG = hasattr(sys, 'gettotalrefcount') -Py_TPFLAGS_HAVE_VECTORCALL = 1 << 11 -Py_TPFLAGS_METHOD_DESCRIPTOR = 1 << 17 - def testfunction(self): """some doc""" return self -def testfunction_kw(self, *, kw): - """some doc""" - return self - class InstanceMethod: id = _testcapi.instancemethod(id) @@ -471,114 +464,6 @@ class TestPendingCalls(unittest.TestCase): self.pendingcalls_wait(l, n) -class TestPEP590(unittest.TestCase): - - def test_method_descriptor_flag(self): - import functools - cached = functools.lru_cache(1)(testfunction) - - self.assertFalse(type(repr).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) - self.assertTrue(type(list.append).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) - self.assertTrue(type(list.__add__).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) - self.assertTrue(type(testfunction).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) - self.assertTrue(type(cached).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) - - self.assertTrue(_testcapi.MethodDescriptorBase.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) - self.assertTrue(_testcapi.MethodDescriptorDerived.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) - self.assertFalse(_testcapi.MethodDescriptorNopGet.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) - - # Heap type should not inherit Py_TPFLAGS_METHOD_DESCRIPTOR - class MethodDescriptorHeap(_testcapi.MethodDescriptorBase): - pass - self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR) - - def test_vectorcall_flag(self): - self.assertTrue(_testcapi.MethodDescriptorBase.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) - self.assertTrue(_testcapi.MethodDescriptorDerived.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) - self.assertFalse(_testcapi.MethodDescriptorNopGet.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) - self.assertTrue(_testcapi.MethodDescriptor2.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) - - # Heap type should not inherit Py_TPFLAGS_HAVE_VECTORCALL - class MethodDescriptorHeap(_testcapi.MethodDescriptorBase): - pass - self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) - - def test_vectorcall_override(self): - # Check that tp_call can correctly override vectorcall. - # MethodDescriptorNopGet implements tp_call but it inherits from - # MethodDescriptorBase, which implements vectorcall. Since - # MethodDescriptorNopGet returns the args tuple when called, we check - # additionally that no new tuple is created for this call. - args = tuple(range(5)) - f = _testcapi.MethodDescriptorNopGet() - self.assertIs(f(*args), args) - - def test_vectorcall(self): - # Test a bunch of different ways to call objects: - # 1. vectorcall using PyVectorcall_Call() - # (only for objects that support vectorcall directly) - # 2. normal call - # 3. vectorcall using _PyObject_Vectorcall() - # 4. call as bound method - # 5. call using functools.partial - - # A list of (function, args, kwargs, result) calls to test - calls = [(len, (range(42),), {}, 42), - (list.append, ([], 0), {}, None), - ([].append, (0,), {}, None), - (sum, ([36],), {"start":6}, 42), - (testfunction, (42,), {}, 42), - (testfunction_kw, (42,), {"kw":None}, 42), - (_testcapi.MethodDescriptorBase(), (0,), {}, True), - (_testcapi.MethodDescriptorDerived(), (0,), {}, True), - (_testcapi.MethodDescriptor2(), (0,), {}, False)] - - from _testcapi import pyobject_vectorcall, pyvectorcall_call - from types import MethodType - from functools import partial - - def vectorcall(func, args, kwargs): - args = *args, *kwargs.values() - kwnames = tuple(kwargs) - return pyobject_vectorcall(func, args, kwnames) - - for (func, args, kwargs, expected) in calls: - with self.subTest(str(func)): - if not kwargs: - self.assertEqual(expected, pyvectorcall_call(func, args)) - self.assertEqual(expected, pyvectorcall_call(func, args, kwargs)) - - # Add derived classes (which do not support vectorcall directly, - # but do support all other ways of calling). - - class MethodDescriptorHeap(_testcapi.MethodDescriptorBase): - pass - - class MethodDescriptorOverridden(_testcapi.MethodDescriptorBase): - def __call__(self, n): - return 'new' - - calls += [ - (MethodDescriptorHeap(), (0,), {}, True), - (MethodDescriptorOverridden(), (0,), {}, 'new'), - ] - - for (func, args, kwargs, expected) in calls: - with self.subTest(str(func)): - args1 = args[1:] - meth = MethodType(func, args[0]) - wrapped = partial(func) - if not kwargs: - self.assertEqual(expected, func(*args)) - self.assertEqual(expected, pyobject_vectorcall(func, args, None)) - self.assertEqual(expected, meth(*args1)) - self.assertEqual(expected, wrapped(*args)) - self.assertEqual(expected, func(*args, **kwargs)) - self.assertEqual(expected, vectorcall(func, args, kwargs)) - self.assertEqual(expected, meth(*args1, **kwargs)) - self.assertEqual(expected, wrapped(*args, **kwargs)) - - class SubinterpreterTest(unittest.TestCase): def test_subinterps(self):