Skip to content

Commit d9ed649

Browse files
jdemeyerpganssle
authored andcommitted
Add tests for calling from C
Adapted by Petr Viktorin from #14795 All bugs are Petr's :)
1 parent 381981a commit d9ed649

File tree

2 files changed

+53
-46
lines changed

2 files changed

+53
-46
lines changed

Lib/test/test_call.py

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ def test_oldargs1_2_kw(self):
299299

300300
@cpython_only
301301
class TestCallingConventions(unittest.TestCase):
302-
"""Test calling using the various C calling conventions (METH_*)
302+
"""Test calling using various C calling conventions (METH_*) from Python
303303
304304
Subclasses test several kinds of functions (module-level, methods,
305305
class methods static methods) using these attributes:
@@ -503,14 +503,15 @@ def static_method():
503503

504504
PYTHON_INSTANCE = PythonClass()
505505

506-
507-
IGNORE_RESULT = object()
508-
506+
NULL_OR_EMPTY = object()
509507

510508
@cpython_only
511509
class FastCallTests(unittest.TestCase):
510+
"""Test calling using various callables from C
511+
"""
512+
512513
# Test calls with positional arguments
513-
CALLS_POSARGS = (
514+
CALLS_POSARGS = [
514515
# (func, args: tuple, result)
515516

516517
# Python function with 2 arguments
@@ -529,31 +530,11 @@ class FastCallTests(unittest.TestCase):
529530
(PYTHON_INSTANCE.class_method, (), "classmethod"),
530531
(PYTHON_INSTANCE.static_method, (), "staticmethod"),
531532

532-
# C function: METH_NOARGS
533-
(globals, (), IGNORE_RESULT),
534-
535-
# C function: METH_O
536-
(id, ("hello",), IGNORE_RESULT),
537-
538-
# C function: METH_VARARGS
539-
(dir, (1,), IGNORE_RESULT),
540-
541-
# C function: METH_VARARGS | METH_KEYWORDS
542-
(min, (5, 9), 5),
543-
544-
# C function: METH_FASTCALL
545-
(divmod, (1000, 33), (30, 10)),
546-
547-
# C type static method: METH_FASTCALL | METH_CLASS
548-
(int.from_bytes, (b'\x01\x00', 'little'), 1),
549-
550-
# bpo-30524: Test that calling a C type static method with no argument
551-
# doesn't crash (ignore the result): METH_FASTCALL | METH_CLASS
552-
(datetime.datetime.now, (), IGNORE_RESULT),
553-
)
533+
# C callables are added later
534+
]
554535

555536
# Test calls with positional and keyword arguments
556-
CALLS_KWARGS = (
537+
CALLS_KWARGS = [
557538
# (func, args: tuple, kwargs: dict, result)
558539

559540
# Python function with 2 arguments
@@ -564,17 +545,51 @@ class FastCallTests(unittest.TestCase):
564545
(PYTHON_INSTANCE.method, (1,), {'arg2': 2}, [1, 2]),
565546
(PYTHON_INSTANCE.method, (), {'arg1': 1, 'arg2': 2}, [1, 2]),
566547

567-
# C function: METH_VARARGS | METH_KEYWORDS
568-
(max, ([],), {'default': 9}, 9),
569-
570-
# C type static method: METH_FASTCALL | METH_CLASS
571-
(int.from_bytes, (b'\x01\x00',), {'byteorder': 'little'}, 1),
572-
(int.from_bytes, (), {'bytes': b'\x01\x00', 'byteorder': 'little'}, 1),
573-
)
548+
# C callables are added later
549+
]
550+
551+
# Add all the calling conventions and variants of C callables
552+
_instance = _testcapi.MethInstance()
553+
for obj, expected_self in (
554+
(_testcapi, _testcapi), # module-level function
555+
(_instance, _instance), # bound method
556+
(_testcapi.MethClass, _testcapi.MethClass), # class method on class
557+
(_testcapi.MethClass(), _testcapi.MethClass), # class method on inst.
558+
(_testcapi.MethStatic, None), # static method
559+
):
560+
CALLS_POSARGS.extend([
561+
(obj.meth_varargs, (1, 2), (expected_self, (1, 2))),
562+
(obj.meth_varargs_keywords,
563+
(1, 2), (expected_self, (1, 2), NULL_OR_EMPTY)),
564+
(obj.meth_fastcall, (1, 2), (expected_self, (1, 2))),
565+
(obj.meth_fastcall, (), (expected_self, ())),
566+
(obj.meth_fastcall_keywords,
567+
(1, 2), (expected_self, (1, 2), NULL_OR_EMPTY)),
568+
(obj.meth_fastcall_keywords,
569+
(), (expected_self, (), NULL_OR_EMPTY)),
570+
(obj.meth_noargs, (), expected_self),
571+
(obj.meth_o, (123, ), (expected_self, 123)),
572+
])
573+
574+
CALLS_KWARGS.extend([
575+
(obj.meth_varargs_keywords,
576+
(1, 2), {'x': 'y'}, (expected_self, (1, 2), {'x': 'y'})),
577+
(obj.meth_varargs_keywords,
578+
(), {'x': 'y'}, (expected_self, (), {'x': 'y'})),
579+
(obj.meth_varargs_keywords,
580+
(1, 2), {}, (expected_self, (1, 2), NULL_OR_EMPTY)),
581+
(obj.meth_fastcall_keywords,
582+
(1, 2), {'x': 'y'}, (expected_self, (1, 2), {'x': 'y'})),
583+
(obj.meth_fastcall_keywords,
584+
(), {'x': 'y'}, (expected_self, (), {'x': 'y'})),
585+
(obj.meth_fastcall_keywords,
586+
(1, 2), {}, (expected_self, (1, 2), NULL_OR_EMPTY)),
587+
])
574588

575589
def check_result(self, result, expected):
576-
if expected is IGNORE_RESULT:
577-
return
590+
if isinstance(expected, tuple) and expected[-1] is NULL_OR_EMPTY:
591+
if result[-1] in ({}, None):
592+
expected = (*expected[:-1], result[-1])
578593
self.assertEqual(result, expected)
579594

580595
def test_fastcall(self):
@@ -599,19 +614,11 @@ def test_vectorcall_dict(self):
599614
result = _testcapi.pyobject_fastcalldict(func, args, None)
600615
self.check_result(result, expected)
601616

602-
# kwargs={}
603-
result = _testcapi.pyobject_fastcalldict(func, args, {})
604-
self.check_result(result, expected)
605-
606617
if not args:
607618
# args=NULL, nargs=0, kwargs=NULL
608619
result = _testcapi.pyobject_fastcalldict(func, None, None)
609620
self.check_result(result, expected)
610621

611-
# args=NULL, nargs=0, kwargs={}
612-
result = _testcapi.pyobject_fastcalldict(func, None, {})
613-
self.check_result(result, expected)
614-
615622
for func, args, kwargs, expected in self.CALLS_KWARGS:
616623
with self.subTest(func=func, args=args, kwargs=kwargs):
617624
result = _testcapi.pyobject_fastcalldict(func, args, kwargs)

Modules/_testcapimodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5203,7 +5203,7 @@ meth_varargs(PyObject* self, PyObject* args)
52035203
static PyObject*
52045204
meth_varargs_keywords(PyObject* self, PyObject* args, PyObject* kwargs)
52055205
{
5206-
return Py_BuildValue("NOO", _null_to_none(self), args, kwargs);
5206+
return Py_BuildValue("NOO", _null_to_none(self), args, _null_to_none(kwargs));
52075207
}
52085208

52095209
static PyObject*

0 commit comments

Comments
 (0)