diff options
author | Petr Viktorin <encukou@gmail.com> | 2019-06-02 21:52:20 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-06-02 21:52:20 (GMT) |
commit | fb9423fd0a85f06affb8c3a8f25dd598a649aa42 (patch) | |
tree | e44d7ac1b3483da0f95a9f95e6907c1d5036a242 /Lib | |
parent | e1179a5096fb12297ececd7a1c79969aa5747e28 (diff) | |
download | cpython-fb9423fd0a85f06affb8c3a8f25dd598a649aa42.zip cpython-fb9423fd0a85f06affb8c3a8f25dd598a649aa42.tar.gz cpython-fb9423fd0a85f06affb8c3a8f25dd598a649aa42.tar.bz2 |
bpo-36974: Make tp_call=PyVectorcall_Call work for inherited types (GH-13699)
When inheriting a heap subclass from a vectorcall class that sets
`.tp_call=PyVectorcall_Call` (as recommended in PEP 590), the subclass does
not inherit `_Py_TPFLAGS_HAVE_VECTORCALL`, and thus `PyVectorcall_Call` does
not work for it.
This attempts to solve the issue by:
* always inheriting `tp_vectorcall_offset` unless `tp_call` is overridden
in the subclass
* inheriting _Py_TPFLAGS_HAVE_VECTORCALL for static types, unless `tp_call`
is overridden
* making `PyVectorcall_Call` ignore `_Py_TPFLAGS_HAVE_VECTORCALL`
This means it'll be ever more important to only call `PyVectorcall_Call`
on classes that support vectorcall. In `PyVectorcall_Call`'s intended role
as `tp_call` filler, that's not a problem.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/test_capi.py | 30 |
1 files changed, 25 insertions, 5 deletions
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index fabc821..88bda05 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -515,9 +515,10 @@ class TestPEP590(unittest.TestCase): def test_vectorcall(self): # Test a bunch of different ways to call objects: - # 1. normal call - # 2. vectorcall using _PyObject_Vectorcall() - # 3. vectorcall using PyVectorcall_Call() + # 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 @@ -543,18 +544,37 @@ class TestPEP590(unittest.TestCase): 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, pyvectorcall_call(func, args)) 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, pyvectorcall_call(func, args, kwargs)) self.assertEqual(expected, meth(*args1, **kwargs)) self.assertEqual(expected, wrapped(*args, **kwargs)) |