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_vectorcall(self): # Test a bunch of different ways to call objects: # 1. normal call # 2. vectorcall using _PyObject_Vectorcall() # 3. vectorcall using PyVectorcall_Call() # 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)): 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))
def vectorcall(func, args, kwargs): args = *args, *kwargs.values() kwnames = tuple(kwargs) return pyobject_vectorcall(func, args, kwnames)
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_vectorcall_limited(self): from _testcapi import pyobject_vectorcall obj = _testcapi.LimitedVectorCallClass() self.assertEqual(pyobject_vectorcall(obj, (), ()), "vectorcall called")