Skip to content

Commit 5effd10

Browse files
bpo-37191: Move TestPEP590 from test_capi to test_call (GH-13892)
(cherry picked from commit 740a84d) Co-authored-by: Victor Stinner <[email protected]>
1 parent 3576266 commit 5effd10

File tree

2 files changed

+123
-115
lines changed

2 files changed

+123
-115
lines changed

Lib/test/test_call.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,5 +475,128 @@ def __index__(self):
475475
# called, which changes the keywords dict.
476476
compile("pass", "", "exec", x, **x.kwargs)
477477

478+
479+
Py_TPFLAGS_HAVE_VECTORCALL = 1 << 11
480+
Py_TPFLAGS_METHOD_DESCRIPTOR = 1 << 17
481+
482+
483+
def testfunction(self):
484+
"""some doc"""
485+
return self
486+
487+
488+
def testfunction_kw(self, *, kw):
489+
"""some doc"""
490+
return self
491+
492+
493+
class TestPEP590(unittest.TestCase):
494+
495+
def test_method_descriptor_flag(self):
496+
import functools
497+
cached = functools.lru_cache(1)(testfunction)
498+
499+
self.assertFalse(type(repr).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
500+
self.assertTrue(type(list.append).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
501+
self.assertTrue(type(list.__add__).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
502+
self.assertTrue(type(testfunction).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
503+
self.assertTrue(type(cached).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
504+
505+
self.assertTrue(_testcapi.MethodDescriptorBase.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
506+
self.assertTrue(_testcapi.MethodDescriptorDerived.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
507+
self.assertFalse(_testcapi.MethodDescriptorNopGet.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
508+
509+
# Heap type should not inherit Py_TPFLAGS_METHOD_DESCRIPTOR
510+
class MethodDescriptorHeap(_testcapi.MethodDescriptorBase):
511+
pass
512+
self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
513+
514+
def test_vectorcall_flag(self):
515+
self.assertTrue(_testcapi.MethodDescriptorBase.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
516+
self.assertTrue(_testcapi.MethodDescriptorDerived.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
517+
self.assertFalse(_testcapi.MethodDescriptorNopGet.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
518+
self.assertTrue(_testcapi.MethodDescriptor2.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
519+
520+
# Heap type should not inherit Py_TPFLAGS_HAVE_VECTORCALL
521+
class MethodDescriptorHeap(_testcapi.MethodDescriptorBase):
522+
pass
523+
self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
524+
525+
def test_vectorcall_override(self):
526+
# Check that tp_call can correctly override vectorcall.
527+
# MethodDescriptorNopGet implements tp_call but it inherits from
528+
# MethodDescriptorBase, which implements vectorcall. Since
529+
# MethodDescriptorNopGet returns the args tuple when called, we check
530+
# additionally that no new tuple is created for this call.
531+
args = tuple(range(5))
532+
f = _testcapi.MethodDescriptorNopGet()
533+
self.assertIs(f(*args), args)
534+
535+
def test_vectorcall(self):
536+
# Test a bunch of different ways to call objects:
537+
# 1. vectorcall using PyVectorcall_Call()
538+
# (only for objects that support vectorcall directly)
539+
# 2. normal call
540+
# 3. vectorcall using _PyObject_Vectorcall()
541+
# 4. call as bound method
542+
# 5. call using functools.partial
543+
544+
# A list of (function, args, kwargs, result) calls to test
545+
calls = [(len, (range(42),), {}, 42),
546+
(list.append, ([], 0), {}, None),
547+
([].append, (0,), {}, None),
548+
(sum, ([36],), {"start":6}, 42),
549+
(testfunction, (42,), {}, 42),
550+
(testfunction_kw, (42,), {"kw":None}, 42),
551+
(_testcapi.MethodDescriptorBase(), (0,), {}, True),
552+
(_testcapi.MethodDescriptorDerived(), (0,), {}, True),
553+
(_testcapi.MethodDescriptor2(), (0,), {}, False)]
554+
555+
from _testcapi import pyobject_vectorcall, pyvectorcall_call
556+
from types import MethodType
557+
from functools import partial
558+
559+
def vectorcall(func, args, kwargs):
560+
args = *args, *kwargs.values()
561+
kwnames = tuple(kwargs)
562+
return pyobject_vectorcall(func, args, kwnames)
563+
564+
for (func, args, kwargs, expected) in calls:
565+
with self.subTest(str(func)):
566+
if not kwargs:
567+
self.assertEqual(expected, pyvectorcall_call(func, args))
568+
self.assertEqual(expected, pyvectorcall_call(func, args, kwargs))
569+
570+
# Add derived classes (which do not support vectorcall directly,
571+
# but do support all other ways of calling).
572+
573+
class MethodDescriptorHeap(_testcapi.MethodDescriptorBase):
574+
pass
575+
576+
class MethodDescriptorOverridden(_testcapi.MethodDescriptorBase):
577+
def __call__(self, n):
578+
return 'new'
579+
580+
calls += [
581+
(MethodDescriptorHeap(), (0,), {}, True),
582+
(MethodDescriptorOverridden(), (0,), {}, 'new'),
583+
]
584+
585+
for (func, args, kwargs, expected) in calls:
586+
with self.subTest(str(func)):
587+
args1 = args[1:]
588+
meth = MethodType(func, args[0])
589+
wrapped = partial(func)
590+
if not kwargs:
591+
self.assertEqual(expected, func(*args))
592+
self.assertEqual(expected, pyobject_vectorcall(func, args, None))
593+
self.assertEqual(expected, meth(*args1))
594+
self.assertEqual(expected, wrapped(*args))
595+
self.assertEqual(expected, func(*args, **kwargs))
596+
self.assertEqual(expected, vectorcall(func, args, kwargs))
597+
self.assertEqual(expected, meth(*args1, **kwargs))
598+
self.assertEqual(expected, wrapped(*args, **kwargs))
599+
600+
478601
if __name__ == "__main__":
479602
unittest.main()

Lib/test/test_capi.py

Lines changed: 0 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,11 @@
2727
# Were we compiled --with-pydebug or with #define Py_DEBUG?
2828
Py_DEBUG = hasattr(sys, 'gettotalrefcount')
2929

30-
Py_TPFLAGS_HAVE_VECTORCALL = 1 << 11
31-
Py_TPFLAGS_METHOD_DESCRIPTOR = 1 << 17
32-
3330

3431
def testfunction(self):
3532
"""some doc"""
3633
return self
3734

38-
def testfunction_kw(self, *, kw):
39-
"""some doc"""
40-
return self
41-
4235

4336
class InstanceMethod:
4437
id = _testcapi.instancemethod(id)
@@ -471,114 +464,6 @@ def test_pendingcalls_non_threaded(self):
471464
self.pendingcalls_wait(l, n)
472465

473466

474-
class TestPEP590(unittest.TestCase):
475-
476-
def test_method_descriptor_flag(self):
477-
import functools
478-
cached = functools.lru_cache(1)(testfunction)
479-
480-
self.assertFalse(type(repr).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
481-
self.assertTrue(type(list.append).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
482-
self.assertTrue(type(list.__add__).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
483-
self.assertTrue(type(testfunction).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
484-
self.assertTrue(type(cached).__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
485-
486-
self.assertTrue(_testcapi.MethodDescriptorBase.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
487-
self.assertTrue(_testcapi.MethodDescriptorDerived.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
488-
self.assertFalse(_testcapi.MethodDescriptorNopGet.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
489-
490-
# Heap type should not inherit Py_TPFLAGS_METHOD_DESCRIPTOR
491-
class MethodDescriptorHeap(_testcapi.MethodDescriptorBase):
492-
pass
493-
self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_METHOD_DESCRIPTOR)
494-
495-
def test_vectorcall_flag(self):
496-
self.assertTrue(_testcapi.MethodDescriptorBase.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
497-
self.assertTrue(_testcapi.MethodDescriptorDerived.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
498-
self.assertFalse(_testcapi.MethodDescriptorNopGet.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
499-
self.assertTrue(_testcapi.MethodDescriptor2.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
500-
501-
# Heap type should not inherit Py_TPFLAGS_HAVE_VECTORCALL
502-
class MethodDescriptorHeap(_testcapi.MethodDescriptorBase):
503-
pass
504-
self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL)
505-
506-
def test_vectorcall_override(self):
507-
# Check that tp_call can correctly override vectorcall.
508-
# MethodDescriptorNopGet implements tp_call but it inherits from
509-
# MethodDescriptorBase, which implements vectorcall. Since
510-
# MethodDescriptorNopGet returns the args tuple when called, we check
511-
# additionally that no new tuple is created for this call.
512-
args = tuple(range(5))
513-
f = _testcapi.MethodDescriptorNopGet()
514-
self.assertIs(f(*args), args)
515-
516-
def test_vectorcall(self):
517-
# Test a bunch of different ways to call objects:
518-
# 1. vectorcall using PyVectorcall_Call()
519-
# (only for objects that support vectorcall directly)
520-
# 2. normal call
521-
# 3. vectorcall using _PyObject_Vectorcall()
522-
# 4. call as bound method
523-
# 5. call using functools.partial
524-
525-
# A list of (function, args, kwargs, result) calls to test
526-
calls = [(len, (range(42),), {}, 42),
527-
(list.append, ([], 0), {}, None),
528-
([].append, (0,), {}, None),
529-
(sum, ([36],), {"start":6}, 42),
530-
(testfunction, (42,), {}, 42),
531-
(testfunction_kw, (42,), {"kw":None}, 42),
532-
(_testcapi.MethodDescriptorBase(), (0,), {}, True),
533-
(_testcapi.MethodDescriptorDerived(), (0,), {}, True),
534-
(_testcapi.MethodDescriptor2(), (0,), {}, False)]
535-
536-
from _testcapi import pyobject_vectorcall, pyvectorcall_call
537-
from types import MethodType
538-
from functools import partial
539-
540-
def vectorcall(func, args, kwargs):
541-
args = *args, *kwargs.values()
542-
kwnames = tuple(kwargs)
543-
return pyobject_vectorcall(func, args, kwnames)
544-
545-
for (func, args, kwargs, expected) in calls:
546-
with self.subTest(str(func)):
547-
if not kwargs:
548-
self.assertEqual(expected, pyvectorcall_call(func, args))
549-
self.assertEqual(expected, pyvectorcall_call(func, args, kwargs))
550-
551-
# Add derived classes (which do not support vectorcall directly,
552-
# but do support all other ways of calling).
553-
554-
class MethodDescriptorHeap(_testcapi.MethodDescriptorBase):
555-
pass
556-
557-
class MethodDescriptorOverridden(_testcapi.MethodDescriptorBase):
558-
def __call__(self, n):
559-
return 'new'
560-
561-
calls += [
562-
(MethodDescriptorHeap(), (0,), {}, True),
563-
(MethodDescriptorOverridden(), (0,), {}, 'new'),
564-
]
565-
566-
for (func, args, kwargs, expected) in calls:
567-
with self.subTest(str(func)):
568-
args1 = args[1:]
569-
meth = MethodType(func, args[0])
570-
wrapped = partial(func)
571-
if not kwargs:
572-
self.assertEqual(expected, func(*args))
573-
self.assertEqual(expected, pyobject_vectorcall(func, args, None))
574-
self.assertEqual(expected, meth(*args1))
575-
self.assertEqual(expected, wrapped(*args))
576-
self.assertEqual(expected, func(*args, **kwargs))
577-
self.assertEqual(expected, vectorcall(func, args, kwargs))
578-
self.assertEqual(expected, meth(*args1, **kwargs))
579-
self.assertEqual(expected, wrapped(*args, **kwargs))
580-
581-
582467
class SubinterpreterTest(unittest.TestCase):
583468

584469
def test_subinterps(self):

0 commit comments

Comments
 (0)