2017-06-09 11:48:45 -03:00
|
|
|
import datetime
|
2001-05-29 13:26:20 -03:00
|
|
|
import unittest
|
2017-06-06 12:45:22 -03:00
|
|
|
from test.support import cpython_only
|
2017-06-09 11:48:45 -03:00
|
|
|
try:
|
|
|
|
import _testcapi
|
|
|
|
except ImportError:
|
|
|
|
_testcapi = None
|
2017-06-15 12:05:23 -03:00
|
|
|
import struct
|
|
|
|
import collections
|
2017-08-23 15:16:48 -03:00
|
|
|
import itertools
|
2019-05-22 08:09:35 -03:00
|
|
|
import gc
|
2001-05-29 13:26:20 -03:00
|
|
|
|
2018-09-26 00:59:00 -03:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
2001-05-29 13:26:20 -03:00
|
|
|
# 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 CFunctionCalls(unittest.TestCase):
|
|
|
|
|
|
|
|
def test_varargs0(self):
|
2006-08-18 19:13:04 -03:00
|
|
|
self.assertRaises(TypeError, {}.__contains__)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_varargs1(self):
|
2006-08-18 19:13:04 -03:00
|
|
|
{}.__contains__(0)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_varargs2(self):
|
2006-08-18 19:13:04 -03:00
|
|
|
self.assertRaises(TypeError, {}.__contains__, 0, 1)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_varargs0_ext(self):
|
|
|
|
try:
|
2006-08-18 19:13:04 -03:00
|
|
|
{}.__contains__(*())
|
2001-05-29 13:26:20 -03:00
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_varargs1_ext(self):
|
2006-08-18 19:13:04 -03:00
|
|
|
{}.__contains__(*(0,))
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_varargs2_ext(self):
|
|
|
|
try:
|
2006-08-18 19:13:04 -03:00
|
|
|
{}.__contains__(*(1, 2))
|
2001-05-29 13:26:20 -03:00
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
raise RuntimeError
|
|
|
|
|
|
|
|
def test_varargs1_kw(self):
|
2006-08-18 19:13:04 -03:00
|
|
|
self.assertRaises(TypeError, {}.__contains__, x=2)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_varargs2_kw(self):
|
2006-08-18 19:13:04 -03:00
|
|
|
self.assertRaises(TypeError, {}.__contains__, x=2, y=2)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_oldargs0_0(self):
|
|
|
|
{}.keys()
|
|
|
|
|
|
|
|
def test_oldargs0_1(self):
|
|
|
|
self.assertRaises(TypeError, {}.keys, 0)
|
|
|
|
|
|
|
|
def test_oldargs0_2(self):
|
|
|
|
self.assertRaises(TypeError, {}.keys, 0, 1)
|
|
|
|
|
|
|
|
def test_oldargs0_0_ext(self):
|
|
|
|
{}.keys(*())
|
|
|
|
|
|
|
|
def test_oldargs0_1_ext(self):
|
|
|
|
try:
|
|
|
|
{}.keys(*(0,))
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
raise RuntimeError
|
|
|
|
|
|
|
|
def test_oldargs0_2_ext(self):
|
|
|
|
try:
|
|
|
|
{}.keys(*(1, 2))
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
raise RuntimeError
|
|
|
|
|
|
|
|
def test_oldargs0_0_kw(self):
|
|
|
|
try:
|
|
|
|
{}.keys(x=2)
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
raise RuntimeError
|
|
|
|
|
|
|
|
def test_oldargs0_1_kw(self):
|
|
|
|
self.assertRaises(TypeError, {}.keys, x=2)
|
|
|
|
|
|
|
|
def test_oldargs0_2_kw(self):
|
|
|
|
self.assertRaises(TypeError, {}.keys, x=2, y=2)
|
|
|
|
|
|
|
|
def test_oldargs1_0(self):
|
2004-03-04 04:25:44 -04:00
|
|
|
self.assertRaises(TypeError, [].count)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_oldargs1_1(self):
|
2004-03-04 04:25:44 -04:00
|
|
|
[].count(1)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_oldargs1_2(self):
|
2004-03-04 04:25:44 -04:00
|
|
|
self.assertRaises(TypeError, [].count, 1, 2)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_oldargs1_0_ext(self):
|
|
|
|
try:
|
2004-03-04 04:25:44 -04:00
|
|
|
[].count(*())
|
2001-05-29 13:26:20 -03:00
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
raise RuntimeError
|
|
|
|
|
|
|
|
def test_oldargs1_1_ext(self):
|
2004-03-04 04:25:44 -04:00
|
|
|
[].count(*(1,))
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_oldargs1_2_ext(self):
|
|
|
|
try:
|
2004-03-04 04:25:44 -04:00
|
|
|
[].count(*(1, 2))
|
2001-05-29 13:26:20 -03:00
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
raise RuntimeError
|
|
|
|
|
|
|
|
def test_oldargs1_0_kw(self):
|
2004-03-04 04:25:44 -04:00
|
|
|
self.assertRaises(TypeError, [].count, x=2)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_oldargs1_1_kw(self):
|
2004-03-04 04:25:44 -04:00
|
|
|
self.assertRaises(TypeError, [].count, {}, x=2)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
|
|
|
def test_oldargs1_2_kw(self):
|
2004-03-04 04:25:44 -04:00
|
|
|
self.assertRaises(TypeError, [].count, x=2, y=2)
|
2001-05-29 13:26:20 -03:00
|
|
|
|
2001-09-20 18:33:42 -03:00
|
|
|
|
2017-06-06 12:45:22 -03:00
|
|
|
@cpython_only
|
|
|
|
class CFunctionCallsErrorMessages(unittest.TestCase):
|
|
|
|
|
|
|
|
def test_varargs0(self):
|
|
|
|
msg = r"__contains__\(\) takes exactly one argument \(0 given\)"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, {}.__contains__)
|
|
|
|
|
|
|
|
def test_varargs2(self):
|
|
|
|
msg = r"__contains__\(\) takes exactly one argument \(2 given\)"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, {}.__contains__, 0, 1)
|
|
|
|
|
2017-06-09 13:27:06 -03:00
|
|
|
def test_varargs3(self):
|
2019-03-13 18:03:22 -03:00
|
|
|
msg = r"^from_bytes\(\) takes exactly 2 positional arguments \(3 given\)"
|
2017-06-09 13:27:06 -03:00
|
|
|
self.assertRaisesRegex(TypeError, msg, int.from_bytes, b'a', 'little', False)
|
|
|
|
|
2018-12-21 10:45:13 -04:00
|
|
|
def test_varargs1min(self):
|
2018-07-22 17:13:26 -03:00
|
|
|
msg = r"get expected at least 1 argument, got 0"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, {}.get)
|
|
|
|
|
2018-12-21 10:45:13 -04:00
|
|
|
msg = r"expected 1 argument, got 0"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, {}.__delattr__)
|
|
|
|
|
|
|
|
def test_varargs2min(self):
|
2018-07-22 17:13:26 -03:00
|
|
|
msg = r"getattr expected at least 2 arguments, got 0"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, getattr)
|
|
|
|
|
2018-12-21 10:45:13 -04:00
|
|
|
def test_varargs1max(self):
|
2018-07-22 17:13:26 -03:00
|
|
|
msg = r"input expected at most 1 argument, got 2"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, input, 1, 2)
|
|
|
|
|
2018-12-21 10:45:13 -04:00
|
|
|
def test_varargs2max(self):
|
2018-07-22 17:13:26 -03:00
|
|
|
msg = r"get expected at most 2 arguments, got 3"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, {}.get, 1, 2, 3)
|
|
|
|
|
2017-06-06 12:45:22 -03:00
|
|
|
def test_varargs1_kw(self):
|
|
|
|
msg = r"__contains__\(\) takes no keyword arguments"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, {}.__contains__, x=2)
|
|
|
|
|
|
|
|
def test_varargs2_kw(self):
|
|
|
|
msg = r"__contains__\(\) takes no keyword arguments"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, {}.__contains__, x=2, y=2)
|
|
|
|
|
2017-06-10 01:51:48 -03:00
|
|
|
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"^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)
|
|
|
|
|
2017-06-15 12:05:23 -03:00
|
|
|
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"^pack\(\) takes no keyword arguments$"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, struct.pack, x=2)
|
|
|
|
|
|
|
|
def test_varargs9_kw(self):
|
|
|
|
msg = r"^pack_into\(\) takes no keyword arguments$"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, struct.pack_into, x=2)
|
|
|
|
|
|
|
|
def test_varargs10_kw(self):
|
|
|
|
msg = r"^index\(\) takes no keyword arguments$"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, collections.deque().index, x=2)
|
|
|
|
|
|
|
|
def test_varargs11_kw(self):
|
|
|
|
msg = r"^pack\(\) takes no keyword arguments$"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, struct.Struct.pack, struct.Struct(""), x=2)
|
|
|
|
|
2017-07-09 00:45:06 -03:00
|
|
|
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)
|
|
|
|
|
2017-08-23 15:16:48 -03:00
|
|
|
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_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_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_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)
|
|
|
|
|
2017-06-06 12:45:22 -03:00
|
|
|
def test_oldargs0_1(self):
|
|
|
|
msg = r"keys\(\) takes no arguments \(1 given\)"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, {}.keys, 0)
|
|
|
|
|
|
|
|
def test_oldargs0_2(self):
|
|
|
|
msg = r"keys\(\) takes no arguments \(2 given\)"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, {}.keys, 0, 1)
|
|
|
|
|
|
|
|
def test_oldargs0_1_kw(self):
|
|
|
|
msg = r"keys\(\) takes no keyword arguments"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, {}.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)
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
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_2_kw(self):
|
|
|
|
msg = r"count\(\) takes no keyword arguments"
|
|
|
|
self.assertRaisesRegex(TypeError, msg, [].count, x=2, y=2)
|
|
|
|
|
|
|
|
|
2017-06-09 11:48:45 -03:00
|
|
|
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()
|
|
|
|
|
|
|
|
|
|
|
|
IGNORE_RESULT = object()
|
|
|
|
|
|
|
|
|
|
|
|
@cpython_only
|
|
|
|
class FastCallTests(unittest.TestCase):
|
|
|
|
# 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 function: METH_NOARGS
|
|
|
|
(globals, (), IGNORE_RESULT),
|
|
|
|
|
|
|
|
# C function: METH_O
|
|
|
|
(id, ("hello",), IGNORE_RESULT),
|
|
|
|
|
|
|
|
# C function: METH_VARARGS
|
|
|
|
(dir, (1,), IGNORE_RESULT),
|
|
|
|
|
|
|
|
# C function: METH_VARARGS | METH_KEYWORDS
|
|
|
|
(min, (5, 9), 5),
|
|
|
|
|
|
|
|
# C function: METH_FASTCALL
|
|
|
|
(divmod, (1000, 33), (30, 10)),
|
|
|
|
|
|
|
|
# C type static method: METH_FASTCALL | METH_CLASS
|
|
|
|
(int.from_bytes, (b'\x01\x00', 'little'), 1),
|
|
|
|
|
|
|
|
# bpo-30524: Test that calling a C type static method with no argument
|
|
|
|
# doesn't crash (ignore the result): METH_FASTCALL | METH_CLASS
|
|
|
|
(datetime.datetime.now, (), IGNORE_RESULT),
|
|
|
|
)
|
|
|
|
|
|
|
|
# Test calls with positional and keyword arguments
|
|
|
|
CALLS_KWARGS = (
|
|
|
|
# (func, args: tuple, kwargs: dict, result)
|
|
|
|
|
|
|
|
# Python function with 2 arguments
|
|
|
|
(pyfunc, (1,), {'arg2': 2}, [1, 2]),
|
|
|
|
(pyfunc, (), {'arg1': 1, 'arg2': 2}, [1, 2]),
|
|
|
|
|
|
|
|
# Python instance methods
|
|
|
|
(PYTHON_INSTANCE.method, (1,), {'arg2': 2}, [1, 2]),
|
|
|
|
(PYTHON_INSTANCE.method, (), {'arg1': 1, 'arg2': 2}, [1, 2]),
|
|
|
|
|
|
|
|
# C function: METH_VARARGS | METH_KEYWORDS
|
|
|
|
(max, ([],), {'default': 9}, 9),
|
|
|
|
|
|
|
|
# C type static method: METH_FASTCALL | METH_CLASS
|
|
|
|
(int.from_bytes, (b'\x01\x00',), {'byteorder': 'little'}, 1),
|
|
|
|
(int.from_bytes, (), {'bytes': b'\x01\x00', 'byteorder': 'little'}, 1),
|
|
|
|
)
|
|
|
|
|
|
|
|
def check_result(self, result, expected):
|
|
|
|
if expected is IGNORE_RESULT:
|
|
|
|
return
|
|
|
|
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)
|
|
|
|
|
2019-05-29 15:31:52 -03:00
|
|
|
def test_vectorcall_dict(self):
|
2017-06-09 11:48:45 -03:00
|
|
|
# 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)
|
|
|
|
|
|
|
|
# kwargs={}
|
|
|
|
result = _testcapi.pyobject_fastcalldict(func, args, {})
|
|
|
|
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)
|
|
|
|
|
|
|
|
# args=NULL, nargs=0, kwargs={}
|
|
|
|
result = _testcapi.pyobject_fastcalldict(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):
|
|
|
|
result = _testcapi.pyobject_fastcalldict(func, args, kwargs)
|
|
|
|
self.check_result(result, expected)
|
|
|
|
|
2019-05-29 15:31:52 -03:00
|
|
|
def test_vectorcall(self):
|
|
|
|
# Test _PyObject_Vectorcall()
|
2017-06-09 11:48:45 -03:00
|
|
|
|
|
|
|
for func, args, expected in self.CALLS_POSARGS:
|
|
|
|
with self.subTest(func=func, args=args):
|
|
|
|
# kwnames=NULL
|
2019-05-29 15:31:52 -03:00
|
|
|
result = _testcapi.pyobject_vectorcall(func, args, None)
|
2017-06-09 11:48:45 -03:00
|
|
|
self.check_result(result, expected)
|
|
|
|
|
|
|
|
# kwnames=()
|
2019-05-29 15:31:52 -03:00
|
|
|
result = _testcapi.pyobject_vectorcall(func, args, ())
|
2017-06-09 11:48:45 -03:00
|
|
|
self.check_result(result, expected)
|
|
|
|
|
|
|
|
if not args:
|
|
|
|
# kwnames=NULL
|
2019-05-29 15:31:52 -03:00
|
|
|
result = _testcapi.pyobject_vectorcall(func, None, None)
|
2017-06-09 11:48:45 -03:00
|
|
|
self.check_result(result, expected)
|
|
|
|
|
|
|
|
# kwnames=()
|
2019-05-29 15:31:52 -03:00
|
|
|
result = _testcapi.pyobject_vectorcall(func, None, ())
|
2017-06-09 11:48:45 -03:00
|
|
|
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())
|
2019-05-29 15:31:52 -03:00
|
|
|
result = _testcapi.pyobject_vectorcall(func, args, kwnames)
|
2017-06-09 11:48:45 -03:00
|
|
|
self.check_result(result, expected)
|
|
|
|
|
2019-05-22 08:09:35 -03:00
|
|
|
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)
|
2017-06-09 11:48:45 -03:00
|
|
|
|
2019-06-07 13:13:16 -03:00
|
|
|
|
|
|
|
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_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)
|
|
|
|
|
|
|
|
# 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'
|
|
|
|
|
2019-06-25 05:19:16 -03:00
|
|
|
class SuperBase:
|
|
|
|
def __call__(self, *args):
|
|
|
|
return super().__call__(*args)
|
|
|
|
|
|
|
|
class MethodDescriptorSuper(SuperBase, _testcapi.MethodDescriptorBase):
|
|
|
|
def __call__(self, *args):
|
|
|
|
return super().__call__(*args)
|
|
|
|
|
2019-06-07 13:13:16 -03:00
|
|
|
calls += [
|
2019-07-23 07:39:51 -03:00
|
|
|
(dict.update, ({},), {"key":True}, None),
|
|
|
|
({}.update, ({},), {"key":True}, None),
|
2019-06-07 13:13:16 -03:00
|
|
|
(MethodDescriptorHeap(), (0,), {}, True),
|
|
|
|
(MethodDescriptorOverridden(), (0,), {}, 'new'),
|
2019-06-25 05:19:16 -03:00
|
|
|
(MethodDescriptorSuper(), (0,), {}, True),
|
2019-06-07 13:13:16 -03:00
|
|
|
]
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
|
|
|
|
2001-09-20 18:33:42 -03:00
|
|
|
if __name__ == "__main__":
|
2015-04-13 17:00:43 -03:00
|
|
|
unittest.main()
|