diff options
Diffstat (limited to 'Lib/test/test_call.py')
-rw-r--r-- | Lib/test/test_call.py | 722 |
1 files changed, 93 insertions, 629 deletions
diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index d178aa4..d06c2c9 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -1,670 +1,134 @@ -import datetime import unittest -from test.support import cpython_only -try: - import _testcapi -except ImportError: - _testcapi = None -import struct -import collections -import itertools -import gc +from test import test_support +# The test cases here cover several paths through the function calling +# code. They depend on the METH_XXX flag that is used to define a C +# function, which can't be verified from Python. If the METH_XXX decl +# for a C function changes, these tests may not cover the right paths. -class FunctionCalls(unittest.TestCase): - - def test_kwargs_order(self): - # bpo-34320: **kwargs should preserve order of passed OrderedDict - od = collections.OrderedDict([('a', 1), ('b', 2)]) - od.move_to_end('a') - expected = list(od.items()) - - def fn(**kw): - return kw - - res = fn(**od) - self.assertIsInstance(res, dict) - self.assertEqual(list(res.items()), expected) - - -@cpython_only -class CFunctionCallsErrorMessages(unittest.TestCase): +class CFunctionCalls(unittest.TestCase): def test_varargs0(self): - msg = r"__contains__\(\) takes exactly one argument \(0 given\)" - self.assertRaisesRegex(TypeError, msg, {}.__contains__) + self.assertRaises(TypeError, {}.has_key) - def test_varargs2(self): - msg = r"__contains__\(\) takes exactly one argument \(2 given\)" - self.assertRaisesRegex(TypeError, msg, {}.__contains__, 0, 1) - - def test_varargs3(self): - msg = r"^from_bytes\(\) takes exactly 2 positional arguments \(3 given\)" - self.assertRaisesRegex(TypeError, msg, int.from_bytes, b'a', 'little', False) + def test_varargs1(self): + with test_support.check_py3k_warnings(): + {}.has_key(0) - def test_varargs1min(self): - msg = r"get expected at least 1 argument, got 0" - self.assertRaisesRegex(TypeError, msg, {}.get) + def test_varargs2(self): + self.assertRaises(TypeError, {}.has_key, 0, 1) - msg = r"expected 1 argument, got 0" - self.assertRaisesRegex(TypeError, msg, {}.__delattr__) + def test_varargs0_ext(self): + try: + {}.has_key(*()) + except TypeError: + pass - def test_varargs2min(self): - msg = r"getattr expected at least 2 arguments, got 0" - self.assertRaisesRegex(TypeError, msg, getattr) + def test_varargs1_ext(self): + with test_support.check_py3k_warnings(): + {}.has_key(*(0,)) - def test_varargs1max(self): - msg = r"input expected at most 1 argument, got 2" - self.assertRaisesRegex(TypeError, msg, input, 1, 2) + def test_varargs2_ext(self): + try: + with test_support.check_py3k_warnings(): + {}.has_key(*(1, 2)) + except TypeError: + pass + else: + raise RuntimeError - def test_varargs2max(self): - msg = r"get expected at most 2 arguments, got 3" - self.assertRaisesRegex(TypeError, msg, {}.get, 1, 2, 3) + def test_varargs0_kw(self): + self.assertRaises(TypeError, {}.has_key, x=2) def test_varargs1_kw(self): - msg = r"__contains__\(\) takes no keyword arguments" - self.assertRaisesRegex(TypeError, msg, {}.__contains__, x=2) + self.assertRaises(TypeError, {}.has_key, x=2) def test_varargs2_kw(self): - msg = r"__contains__\(\) takes no keyword arguments" - self.assertRaisesRegex(TypeError, msg, {}.__contains__, x=2, y=2) - - def test_varargs3_kw(self): - msg = r"bool\(\) takes no keyword arguments" - self.assertRaisesRegex(TypeError, msg, bool, x=2) - - def test_varargs4_kw(self): - msg = r"^list[.]index\(\) takes no keyword arguments$" - self.assertRaisesRegex(TypeError, msg, [].index, x=2) - - def test_varargs5_kw(self): - msg = r"^hasattr\(\) takes no keyword arguments$" - self.assertRaisesRegex(TypeError, msg, hasattr, x=2) - - def test_varargs6_kw(self): - msg = r"^getattr\(\) takes no keyword arguments$" - self.assertRaisesRegex(TypeError, msg, getattr, x=2) - - def test_varargs7_kw(self): - msg = r"^next\(\) takes no keyword arguments$" - self.assertRaisesRegex(TypeError, msg, next, x=2) - - def test_varargs8_kw(self): - msg = r"^_struct[.]pack\(\) takes no keyword arguments$" - self.assertRaisesRegex(TypeError, msg, struct.pack, x=2) - - def test_varargs9_kw(self): - msg = r"^_struct[.]pack_into\(\) takes no keyword arguments$" - self.assertRaisesRegex(TypeError, msg, struct.pack_into, x=2) + self.assertRaises(TypeError, {}.has_key, x=2, y=2) - def test_varargs10_kw(self): - msg = r"^deque[.]index\(\) takes no keyword arguments$" - self.assertRaisesRegex(TypeError, msg, collections.deque().index, x=2) + def test_oldargs0_0(self): + {}.keys() - def test_varargs11_kw(self): - msg = r"^Struct[.]pack\(\) takes no keyword arguments$" - self.assertRaisesRegex(TypeError, msg, struct.Struct.pack, struct.Struct(""), x=2) - - def test_varargs12_kw(self): - msg = r"^staticmethod\(\) takes no keyword arguments$" - self.assertRaisesRegex(TypeError, msg, staticmethod, func=id) - - def test_varargs13_kw(self): - msg = r"^classmethod\(\) takes no keyword arguments$" - self.assertRaisesRegex(TypeError, msg, classmethod, func=id) - - def test_varargs14_kw(self): - msg = r"^product\(\) takes at most 1 keyword argument \(2 given\)$" - self.assertRaisesRegex(TypeError, msg, - itertools.product, 0, repeat=1, foo=2) + def test_oldargs0_1(self): + self.assertRaises(TypeError, {}.keys, 0) - def test_varargs15_kw(self): - msg = r"^ImportError\(\) takes at most 2 keyword arguments \(3 given\)$" - self.assertRaisesRegex(TypeError, msg, - ImportError, 0, name=1, path=2, foo=3) + def test_oldargs0_2(self): + self.assertRaises(TypeError, {}.keys, 0, 1) - def test_varargs16_kw(self): - msg = r"^min\(\) takes at most 2 keyword arguments \(3 given\)$" - self.assertRaisesRegex(TypeError, msg, - min, 0, default=1, key=2, foo=3) + def test_oldargs0_0_ext(self): + {}.keys(*()) - def test_varargs17_kw(self): - msg = r"^print\(\) takes at most 4 keyword arguments \(5 given\)$" - self.assertRaisesRegex(TypeError, msg, - print, 0, sep=1, end=2, file=3, flush=4, foo=5) + def test_oldargs0_1_ext(self): + try: + {}.keys(*(0,)) + except TypeError: + pass + else: + raise RuntimeError - def test_oldargs0_1(self): - msg = r"keys\(\) takes no arguments \(1 given\)" - self.assertRaisesRegex(TypeError, msg, {}.keys, 0) + def test_oldargs0_2_ext(self): + try: + {}.keys(*(1, 2)) + except TypeError: + pass + else: + raise RuntimeError - def test_oldargs0_2(self): - msg = r"keys\(\) takes no arguments \(2 given\)" - self.assertRaisesRegex(TypeError, msg, {}.keys, 0, 1) + def test_oldargs0_0_kw(self): + try: + {}.keys(x=2) + except TypeError: + pass + else: + raise RuntimeError def test_oldargs0_1_kw(self): - msg = r"keys\(\) takes no keyword arguments" - self.assertRaisesRegex(TypeError, msg, {}.keys, x=2) + self.assertRaises(TypeError, {}.keys, x=2) def test_oldargs0_2_kw(self): - msg = r"keys\(\) takes no keyword arguments" - self.assertRaisesRegex(TypeError, msg, {}.keys, x=2, y=2) + self.assertRaises(TypeError, {}.keys, x=2, y=2) def test_oldargs1_0(self): - msg = r"count\(\) takes exactly one argument \(0 given\)" - self.assertRaisesRegex(TypeError, msg, [].count) - - def test_oldargs1_2(self): - msg = r"count\(\) takes exactly one argument \(2 given\)" - self.assertRaisesRegex(TypeError, msg, [].count, 1, 2) + self.assertRaises(TypeError, [].count) - def test_oldargs1_0_kw(self): - msg = r"count\(\) takes no keyword arguments" - self.assertRaisesRegex(TypeError, msg, [].count, x=2) - - def test_oldargs1_1_kw(self): - msg = r"count\(\) takes no keyword arguments" - self.assertRaisesRegex(TypeError, msg, [].count, {}, x=2) + def test_oldargs1_1(self): + [].count(1) - def test_oldargs1_2_kw(self): - msg = r"count\(\) takes no keyword arguments" - self.assertRaisesRegex(TypeError, msg, [].count, x=2, y=2) - - - -class TestCallingConventions(unittest.TestCase): - """Test calling using various C calling conventions (METH_*) from Python - - Subclasses test several kinds of functions (module-level, methods, - class methods static methods) using these attributes: - obj: the object that contains tested functions (as attributes) - expected_self: expected "self" argument to the C function - - The base class tests module-level functions. - """ - - def setUp(self): - self.obj = self.expected_self = _testcapi - - def test_varargs(self): - self.assertEqual( - self.obj.meth_varargs(1, 2, 3), - (self.expected_self, (1, 2, 3)), - ) - - def test_varargs_ext(self): - self.assertEqual( - self.obj.meth_varargs(*(1, 2, 3)), - (self.expected_self, (1, 2, 3)), - ) - - def test_varargs_error_kw(self): - msg = r"meth_varargs\(\) takes no keyword arguments" - self.assertRaisesRegex( - TypeError, msg, lambda: self.obj.meth_varargs(k=1), - ) - - def test_varargs_keywords(self): - self.assertEqual( - self.obj.meth_varargs_keywords(1, 2, a=3, b=4), - (self.expected_self, (1, 2), {'a': 3, 'b': 4}) - ) - - def test_varargs_keywords_ext(self): - self.assertEqual( - self.obj.meth_varargs_keywords(*[1, 2], **{'a': 3, 'b': 4}), - (self.expected_self, (1, 2), {'a': 3, 'b': 4}) - ) - - def test_o(self): - self.assertEqual(self.obj.meth_o(1), (self.expected_self, 1)) - - def test_o_ext(self): - self.assertEqual(self.obj.meth_o(*[1]), (self.expected_self, 1)) - - def test_o_error_no_arg(self): - msg = r"meth_o\(\) takes exactly one argument \(0 given\)" - self.assertRaisesRegex(TypeError, msg, self.obj.meth_o) - - def test_o_error_two_args(self): - msg = r"meth_o\(\) takes exactly one argument \(2 given\)" - self.assertRaisesRegex( - TypeError, msg, lambda: self.obj.meth_o(1, 2), - ) - - def test_o_error_ext(self): - msg = r"meth_o\(\) takes exactly one argument \(3 given\)" - self.assertRaisesRegex( - TypeError, msg, lambda: self.obj.meth_o(*(1, 2, 3)), - ) - - def test_o_error_kw(self): - msg = r"meth_o\(\) takes no keyword arguments" - self.assertRaisesRegex( - TypeError, msg, lambda: self.obj.meth_o(k=1), - ) - - def test_o_error_arg_kw(self): - msg = r"meth_o\(\) takes no keyword arguments" - self.assertRaisesRegex( - TypeError, msg, lambda: self.obj.meth_o(k=1), - ) - - def test_noargs(self): - self.assertEqual(self.obj.meth_noargs(), self.expected_self) - - def test_noargs_ext(self): - self.assertEqual(self.obj.meth_noargs(*[]), self.expected_self) - - def test_noargs_error_arg(self): - msg = r"meth_noargs\(\) takes no arguments \(1 given\)" - self.assertRaisesRegex( - TypeError, msg, lambda: self.obj.meth_noargs(1), - ) - - def test_noargs_error_arg2(self): - msg = r"meth_noargs\(\) takes no arguments \(2 given\)" - self.assertRaisesRegex( - TypeError, msg, lambda: self.obj.meth_noargs(1, 2), - ) - - def test_noargs_error_ext(self): - msg = r"meth_noargs\(\) takes no arguments \(3 given\)" - self.assertRaisesRegex( - TypeError, msg, lambda: self.obj.meth_noargs(*(1, 2, 3)), - ) - - def test_noargs_error_kw(self): - msg = r"meth_noargs\(\) takes no keyword arguments" - self.assertRaisesRegex( - TypeError, msg, lambda: self.obj.meth_noargs(k=1), - ) - - def test_fastcall(self): - self.assertEqual( - self.obj.meth_fastcall(1, 2, 3), - (self.expected_self, (1, 2, 3)), - ) - - def test_fastcall_ext(self): - self.assertEqual( - self.obj.meth_fastcall(*(1, 2, 3)), - (self.expected_self, (1, 2, 3)), - ) - - def test_fastcall_error_kw(self): - msg = r"meth_fastcall\(\) takes no keyword arguments" - self.assertRaisesRegex( - TypeError, msg, lambda: self.obj.meth_fastcall(k=1), - ) - - def test_fastcall_keywords(self): - self.assertEqual( - self.obj.meth_fastcall_keywords(1, 2, a=3, b=4), - (self.expected_self, (1, 2), {'a': 3, 'b': 4}) - ) - - def test_fastcall_keywords_ext(self): - self.assertEqual( - self.obj.meth_fastcall_keywords(*(1, 2), **{'a': 3, 'b': 4}), - (self.expected_self, (1, 2), {'a': 3, 'b': 4}) - ) - - -class TestCallingConventionsInstance(TestCallingConventions): - """Test calling instance methods using various calling conventions""" - - def setUp(self): - self.obj = self.expected_self = _testcapi.MethInstance() - - -class TestCallingConventionsClass(TestCallingConventions): - """Test calling class methods using various calling conventions""" - - def setUp(self): - self.obj = self.expected_self = _testcapi.MethClass - - -class TestCallingConventionsClassInstance(TestCallingConventions): - """Test calling class methods on instance""" - - def setUp(self): - self.obj = _testcapi.MethClass() - self.expected_self = _testcapi.MethClass - - -class TestCallingConventionsStatic(TestCallingConventions): - """Test calling static methods using various calling conventions""" - - def setUp(self): - self.obj = _testcapi.MethStatic() - self.expected_self = None - - -def pyfunc(arg1, arg2): - return [arg1, arg2] - - -def pyfunc_noarg(): - return "noarg" - - -class PythonClass: - def method(self, arg1, arg2): - return [arg1, arg2] - - def method_noarg(self): - return "noarg" - - @classmethod - def class_method(cls): - return "classmethod" - - @staticmethod - def static_method(): - return "staticmethod" - - -PYTHON_INSTANCE = PythonClass() - -NULL_OR_EMPTY = object() - -class FastCallTests(unittest.TestCase): - """Test calling using various callables from C - """ - - # Test calls with positional arguments - CALLS_POSARGS = [ - # (func, args: tuple, result) - - # Python function with 2 arguments - (pyfunc, (1, 2), [1, 2]), - - # Python function without argument - (pyfunc_noarg, (), "noarg"), - - # Python class methods - (PythonClass.class_method, (), "classmethod"), - (PythonClass.static_method, (), "staticmethod"), - - # Python instance methods - (PYTHON_INSTANCE.method, (1, 2), [1, 2]), - (PYTHON_INSTANCE.method_noarg, (), "noarg"), - (PYTHON_INSTANCE.class_method, (), "classmethod"), - (PYTHON_INSTANCE.static_method, (), "staticmethod"), - - # C callables are added later - ] + def test_oldargs1_2(self): + self.assertRaises(TypeError, [].count, 1, 2) - # Test calls with positional and keyword arguments - CALLS_KWARGS = [ - # (func, args: tuple, kwargs: dict, result) + def test_oldargs1_0_ext(self): + try: + [].count(*()) + except TypeError: + pass + else: + raise RuntimeError - # Python function with 2 arguments - (pyfunc, (1,), {'arg2': 2}, [1, 2]), - (pyfunc, (), {'arg1': 1, 'arg2': 2}, [1, 2]), + def test_oldargs1_1_ext(self): + [].count(*(1,)) - # Python instance methods - (PYTHON_INSTANCE.method, (1,), {'arg2': 2}, [1, 2]), - (PYTHON_INSTANCE.method, (), {'arg1': 1, 'arg2': 2}, [1, 2]), + def test_oldargs1_2_ext(self): + try: + [].count(*(1, 2)) + except TypeError: + pass + else: + raise RuntimeError - # C callables are added later - ] + def test_oldargs1_0_kw(self): + self.assertRaises(TypeError, [].count, x=2) - # Add all the calling conventions and variants of C callables - _instance = _testcapi.MethInstance() - for obj, expected_self in ( - (_testcapi, _testcapi), # module-level function - (_instance, _instance), # bound method - (_testcapi.MethClass, _testcapi.MethClass), # class method on class - (_testcapi.MethClass(), _testcapi.MethClass), # class method on inst. - (_testcapi.MethStatic, None), # static method - ): - CALLS_POSARGS.extend([ - (obj.meth_varargs, (1, 2), (expected_self, (1, 2))), - (obj.meth_varargs_keywords, - (1, 2), (expected_self, (1, 2), NULL_OR_EMPTY)), - (obj.meth_fastcall, (1, 2), (expected_self, (1, 2))), - (obj.meth_fastcall, (), (expected_self, ())), - (obj.meth_fastcall_keywords, - (1, 2), (expected_self, (1, 2), NULL_OR_EMPTY)), - (obj.meth_fastcall_keywords, - (), (expected_self, (), NULL_OR_EMPTY)), - (obj.meth_noargs, (), expected_self), - (obj.meth_o, (123, ), (expected_self, 123)), - ]) - - CALLS_KWARGS.extend([ - (obj.meth_varargs_keywords, - (1, 2), {'x': 'y'}, (expected_self, (1, 2), {'x': 'y'})), - (obj.meth_varargs_keywords, - (), {'x': 'y'}, (expected_self, (), {'x': 'y'})), - (obj.meth_varargs_keywords, - (1, 2), {}, (expected_self, (1, 2), NULL_OR_EMPTY)), - (obj.meth_fastcall_keywords, - (1, 2), {'x': 'y'}, (expected_self, (1, 2), {'x': 'y'})), - (obj.meth_fastcall_keywords, - (), {'x': 'y'}, (expected_self, (), {'x': 'y'})), - (obj.meth_fastcall_keywords, - (1, 2), {}, (expected_self, (1, 2), NULL_OR_EMPTY)), - ]) - - def check_result(self, result, expected): - if isinstance(expected, tuple) and expected[-1] is NULL_OR_EMPTY: - if result[-1] in ({}, None): - expected = (*expected[:-1], result[-1]) - self.assertEqual(result, expected) - - def test_fastcall(self): - # Test _PyObject_FastCall() - - for func, args, expected in self.CALLS_POSARGS: - with self.subTest(func=func, args=args): - result = _testcapi.pyobject_fastcall(func, args) - self.check_result(result, expected) - - if not args: - # args=NULL, nargs=0 - result = _testcapi.pyobject_fastcall(func, None) - self.check_result(result, expected) - - def test_vectorcall_dict(self): - # Test _PyObject_FastCallDict() - - for func, args, expected in self.CALLS_POSARGS: - with self.subTest(func=func, args=args): - # kwargs=NULL - result = _testcapi.pyobject_fastcalldict(func, args, None) - self.check_result(result, expected) - - if not args: - # args=NULL, nargs=0, kwargs=NULL - result = _testcapi.pyobject_fastcalldict(func, None, None) - self.check_result(result, expected) - - for func, args, kwargs, expected in self.CALLS_KWARGS: - with self.subTest(func=func, args=args, kwargs=kwargs): - result = _testcapi.pyobject_fastcalldict(func, args, kwargs) - self.check_result(result, expected) - - def test_vectorcall(self): - # Test _PyObject_Vectorcall() - - for func, args, expected in self.CALLS_POSARGS: - with self.subTest(func=func, args=args): - # kwnames=NULL - result = _testcapi.pyobject_vectorcall(func, args, None) - self.check_result(result, expected) - - # kwnames=() - result = _testcapi.pyobject_vectorcall(func, args, ()) - self.check_result(result, expected) - - if not args: - # kwnames=NULL - result = _testcapi.pyobject_vectorcall(func, None, None) - self.check_result(result, expected) - - # kwnames=() - result = _testcapi.pyobject_vectorcall(func, None, ()) - self.check_result(result, expected) - - for func, args, kwargs, expected in self.CALLS_KWARGS: - with self.subTest(func=func, args=args, kwargs=kwargs): - kwnames = tuple(kwargs.keys()) - args = args + tuple(kwargs.values()) - result = _testcapi.pyobject_vectorcall(func, args, kwnames) - self.check_result(result, expected) - - def test_fastcall_clearing_dict(self): - # Test bpo-36907: the point of the test is just checking that this - # does not crash. - class IntWithDict: - __slots__ = ["kwargs"] - def __init__(self, **kwargs): - self.kwargs = kwargs - def __index__(self): - self.kwargs.clear() - gc.collect() - return 0 - x = IntWithDict(dont_inherit=IntWithDict()) - # We test the argument handling of "compile" here, the compilation - # itself is not relevant. When we pass flags=x below, x.__index__() is - # 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_oldargs1_1_kw(self): + self.assertRaises(TypeError, [].count, {}, x=2) - 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) + def test_oldargs1_2_kw(self): + self.assertRaises(TypeError, [].count, x=2, y=2) - # 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' - - class SuperBase: - def __call__(self, *args): - return super().__call__(*args) - - class MethodDescriptorSuper(SuperBase, _testcapi.MethodDescriptorBase): - def __call__(self, *args): - return super().__call__(*args) - - calls += [ - (dict.update, ({},), {"key":True}, None), - ({}.update, ({},), {"key":True}, None), - (MethodDescriptorHeap(), (0,), {}, True), - (MethodDescriptorOverridden(), (0,), {}, 'new'), - (MethodDescriptorSuper(), (0,), {}, True), - ] - - 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)) +def test_main(): + test_support.run_unittest(CFunctionCalls) if __name__ == "__main__": - unittest.main() + test_main() |