| import unittest | |
| from test.support import (cpython_only, is_wasi, requires_limited_api, Py_DEBUG, | |
| set_recursion_limit, skip_on_s390x, | |
| skip_emscripten_stack_overflow, | |
| skip_wasi_stack_overflow, skip_if_sanitizer, | |
| skip_if_huge_c_stack, import_helper) | |
| try: | |
| import _testcapi | |
| except ImportError: | |
| _testcapi = None | |
| try: | |
| import _testlimitedcapi | |
| except ImportError: | |
| _testlimitedcapi = None | |
| try: | |
| import _testinternalcapi | |
| except ImportError: | |
| _testinternalcapi = None | |
| import struct | |
| import collections | |
| import itertools | |
| import gc | |
| import contextlib | |
| import types | |
| class BadStr(str): | |
| def __eq__(self, other): | |
| return True | |
| def __hash__(self): | |
| # Guaranteed different hash | |
| return str.__hash__(self) ^ 3 | |
| 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) | |
| def test_kwargs_order_preserved(self): | |
| # PEP 468: Preserving Keyword Argument Order | |
| def fn(**kw): | |
| return list(kw) | |
| self.assertEqual(fn(b=1, a=2, c=3), ['b', 'a', 'c']) | |
| self.assertEqual(fn(c=3, a=2, b=1), ['c', 'a', 'b']) | |
| # Unpacked mappings are merged in place, keeping their own order. | |
| self.assertEqual(fn(z=0, **{'x': 1, 'a': 2}, y=3), | |
| ['z', 'x', 'a', 'y']) | |
| self.assertEqual(fn(**{'b': 1}, **{'a': 2}), ['b', 'a']) | |
| # Named parameters are removed from **kwargs without reordering | |
| # the rest. | |
| def fn2(a, c=None, **kw): | |
| return list(kw) | |
| self.assertEqual(fn2(d=1, a=2, b=3, c=4, e=5), ['d', 'b', 'e']) | |
| def test_kwargs_order_preserved_in_methods(self): | |
| # PEP 468: Preserving Keyword Argument Order | |
| class C: | |
| def __init__(self, **kw): | |
| self.init_kw = list(kw) | |
| def meth(self, **kw): | |
| return list(kw) | |
| @classmethod | |
| def cmeth(cls, **kw): | |
| return list(kw) | |
| @staticmethod | |
| def smeth(**kw): | |
| return list(kw) | |
| c = C(b=1, a=2, c=3) | |
| self.assertEqual(c.init_kw, ['b', 'a', 'c']) | |
| self.assertEqual(c.meth(b=1, a=2, c=3), ['b', 'a', 'c']) | |
| self.assertEqual(C.cmeth(b=1, a=2, c=3), ['b', 'a', 'c']) | |
| self.assertEqual(C.smeth(b=1, a=2, c=3), ['b', 'a', 'c']) | |
| def test_kwargs_order_preserved_in_c_functions(self): | |
| # PEP 468: Preserving Keyword Argument Order | |
| self.assertEqual(list(dict(b=1, a=2, c=3)), ['b', 'a', 'c']) | |
| self.assertEqual(list(dict(**{'b': 1}, a=2)), ['b', 'a']) | |
| self.assertEqual(list(collections.OrderedDict(b=1, a=2, c=3)), | |
| ['b', 'a', 'c']) | |
| def test_frames_are_popped_after_failed_calls(self): | |
| # GH-93252: stuff blows up if we don't pop the new frame after | |
| # recovering from failed calls: | |
| def f(): | |
| pass | |
| class C: | |
| def m(self): | |
| pass | |
| callables = [f, C.m, [].__len__] | |
| for c in callables: | |
| for _ in range(1000): | |
| try: | |
| c(None) | |
| except TypeError: | |
| pass | |
| # BOOM! | |
| @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) | |
| def test_varargs3(self): | |
| msg = r"^from_bytes\(\) takes at most 2 positional arguments \(3 given\)" | |
| self.assertRaisesRegex(TypeError, msg, int.from_bytes, b'a', 'little', False) | |
| def test_varargs1min(self): | |
| msg = (r"get\(\) takes at least 1 argument \(0 given\)|" | |
| r"get expected at least 1 argument, got 0") | |
| self.assertRaisesRegex(TypeError, msg, {}.get) | |
| msg = r"expected 1 argument, got 0" | |
| self.assertRaisesRegex(TypeError, msg, {}.__delattr__) | |
| def test_varargs2min(self): | |
| msg = r"getattr expected at least 2 arguments, got 0" | |
| self.assertRaisesRegex(TypeError, msg, getattr) | |
| def test_varargs1max(self): | |
| msg = (r"input\(\) takes at most 1 argument \(2 given\)|" | |
| r"input expected at most 1 argument, got 2") | |
| self.assertRaisesRegex(TypeError, msg, input, 1, 2) | |
| def test_varargs2max(self): | |
| msg = (r"get\(\) takes at most 2 arguments \(3 given\)|" | |
| r"get expected at most 2 arguments, got 3") | |
| self.assertRaisesRegex(TypeError, msg, {}.get, 1, 2, 3) | |
| 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) | |
| 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) | |
| def test_varargs10_kw(self): | |
| msg = r"^deque[.]index\(\) takes no keyword arguments$" | |
| self.assertRaisesRegex(TypeError, msg, collections.deque().index, x=2) | |
| 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_varargs15_kw(self): | |
| msg = r"^ImportError\(\) takes at most 3 keyword arguments \(4 given\)$" | |
| self.assertRaisesRegex(TypeError, msg, | |
| ImportError, 0, name=1, path=2, name_from=3, 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\(\) got an unexpected keyword argument 'foo'$" | |
| self.assertRaisesRegex(TypeError, msg, | |
| print, 0, sep=1, end=2, file=3, flush=4, foo=5) | |
| def test_varargs18_kw(self): | |
| # _PyArg_UnpackKeywords() with varpos | |
| msg = r"invalid keyword argument for print\(\)$" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| print(0, 1, **{BadStr('foo'): ','}) | |
| def test_varargs19_kw(self): | |
| # _PyArg_UnpackKeywords() | |
| msg = r"invalid keyword argument for round\(\)$" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| round(1.75, **{BadStr('foo'): 1}) | |
| 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) | |
| def test_object_not_callable(self): | |
| msg = r"^'object' object is not callable$" | |
| self.assertRaisesRegex(TypeError, msg, object()) | |
| def test_module_not_callable_no_suggestion_0(self): | |
| msg = r"^'module' object is not callable$" | |
| self.assertRaisesRegex(TypeError, msg, types.ModuleType("mod")) | |
| def test_module_not_callable_no_suggestion_1(self): | |
| msg = r"^'module' object is not callable$" | |
| mod = types.ModuleType("mod") | |
| mod.mod = 42 | |
| self.assertRaisesRegex(TypeError, msg, mod) | |
| def test_module_not_callable_no_suggestion_2(self): | |
| msg = r"^'module' object is not callable$" | |
| mod = types.ModuleType("mod") | |
| del mod.__name__ | |
| self.assertRaisesRegex(TypeError, msg, mod) | |
| def test_module_not_callable_no_suggestion_3(self): | |
| msg = r"^'module' object is not callable$" | |
| mod = types.ModuleType("mod") | |
| mod.__name__ = 42 | |
| self.assertRaisesRegex(TypeError, msg, mod) | |
| def test_module_not_callable_suggestion(self): | |
| msg = r"^'module' object is not callable\. Did you mean: 'mod\.mod\(\.\.\.\)'\?$" | |
| mod = types.ModuleType("mod") | |
| mod.mod = lambda: ... | |
| self.assertRaisesRegex(TypeError, msg, mod) | |
| @unittest.skipIf(_testcapi is None, "requires _testcapi") | |
| 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 | |
| ] | |
| # 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 callables are added later | |
| ] | |
| # Add all the calling conventions and variants of C callables | |
| if _testcapi: | |
| _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) | |
| @unittest.skipIf(_testcapi is None, "requires _testcapi") | |
| def test_vectorcall_dict(self): | |
| # Test PyObject_VectorcallDict() | |
| 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) | |
| @unittest.skipIf(_testcapi is None, "requires _testcapi") | |
| 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(optimize=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 | |
| @unittest.skipIf(_testcapi is None, "requires _testcapi") | |
| 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) | |
| # Mutable heap types 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) | |
| # Mutable heap types should inherit Py_TPFLAGS_HAVE_VECTORCALL, | |
| # but should lose it when __call__ is overridden | |
| class MethodDescriptorHeap(_testcapi.MethodDescriptorBase): | |
| pass | |
| self.assertTrue(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) | |
| MethodDescriptorHeap.__call__ = print | |
| self.assertFalse(MethodDescriptorHeap.__flags__ & Py_TPFLAGS_HAVE_VECTORCALL) | |
| # Mutable heap types should not inherit Py_TPFLAGS_HAVE_VECTORCALL if | |
| # they define __call__ directly | |
| class MethodDescriptorHeap(_testcapi.MethodDescriptorBase): | |
| def __call__(self): | |
| 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_override_on_mutable_class(self): | |
| """Setting __call__ should disable vectorcall""" | |
| TestType = _testcapi.make_vectorcall_class() | |
| instance = TestType() | |
| self.assertEqual(instance(), "tp_call") | |
| instance.set_vectorcall(TestType) | |
| self.assertEqual(instance(), "vectorcall") # assume vectorcall is used | |
| TestType.__call__ = lambda self: "custom" | |
| self.assertEqual(instance(), "custom") | |
| def test_vectorcall_override_with_subclass(self): | |
| """Setting __call__ on a superclass should disable vectorcall""" | |
| SuperType = _testcapi.make_vectorcall_class() | |
| class DerivedType(SuperType): | |
| pass | |
| instance = DerivedType() | |
| # Derived types with its own vectorcall should be unaffected | |
| UnaffectedType1 = _testcapi.make_vectorcall_class(DerivedType) | |
| UnaffectedType2 = _testcapi.make_vectorcall_class(SuperType) | |
| # Aside: Quickly check that the C helper actually made derived types | |
| self.assertIsSubclass(UnaffectedType1, DerivedType) | |
| self.assertIsSubclass(UnaffectedType2, SuperType) | |
| # Initial state: tp_call | |
| self.assertEqual(instance(), "tp_call") | |
| self.assertEqual(_testcapi.has_vectorcall_flag(SuperType), True) | |
| self.assertEqual(_testcapi.has_vectorcall_flag(DerivedType), True) | |
| self.assertEqual(_testcapi.has_vectorcall_flag(UnaffectedType1), True) | |
| self.assertEqual(_testcapi.has_vectorcall_flag(UnaffectedType2), True) | |
| # Setting the vectorcall function | |
| instance.set_vectorcall(SuperType) | |
| self.assertEqual(instance(), "vectorcall") | |
| self.assertEqual(_testcapi.has_vectorcall_flag(SuperType), True) | |
| self.assertEqual(_testcapi.has_vectorcall_flag(DerivedType), True) | |
| self.assertEqual(_testcapi.has_vectorcall_flag(UnaffectedType1), True) | |
| self.assertEqual(_testcapi.has_vectorcall_flag(UnaffectedType2), True) | |
| # Setting __call__ should remove vectorcall from all subclasses | |
| SuperType.__call__ = lambda self: "custom" | |
| self.assertEqual(instance(), "custom") | |
| self.assertEqual(_testcapi.has_vectorcall_flag(SuperType), False) | |
| self.assertEqual(_testcapi.has_vectorcall_flag(DerivedType), False) | |
| self.assertEqual(_testcapi.has_vectorcall_flag(UnaffectedType1), True) | |
| self.assertEqual(_testcapi.has_vectorcall_flag(UnaffectedType2), True) | |
| 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_setvectorcall(self): | |
| from _testcapi import function_setvectorcall | |
| _testinternalcapi = import_helper.import_module("_testinternalcapi") | |
| def f(num): return num + 1 | |
| assert_equal = self.assertEqual | |
| num = 10 | |
| assert_equal(11, f(num)) | |
| function_setvectorcall(f) | |
| for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): | |
| assert_equal("overridden", f(num)) | |
| def test_setvectorcall_load_attr_specialization_skip(self): | |
| from _testcapi import function_setvectorcall | |
| _testinternalcapi = import_helper.import_module("_testinternalcapi") | |
| class X: | |
| def __getattribute__(self, attr): | |
| return attr | |
| assert_equal = self.assertEqual | |
| x = X() | |
| assert_equal("a", x.a) | |
| function_setvectorcall(X.__getattribute__) | |
| # make sure specialization doesn't trigger | |
| # when vectorcall is overridden | |
| for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): | |
| assert_equal("overridden", x.a) | |
| def test_setvectorcall_load_attr_specialization_deopt(self): | |
| from _testcapi import function_setvectorcall | |
| _testinternalcapi = import_helper.import_module("_testinternalcapi") | |
| class X: | |
| def __getattribute__(self, attr): | |
| return attr | |
| def get_a(x): | |
| return x.a | |
| assert_equal = self.assertEqual | |
| x = X() | |
| # trigger LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN specialization | |
| for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): | |
| assert_equal("a", get_a(x)) | |
| function_setvectorcall(X.__getattribute__) | |
| # make sure specialized LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN | |
| # gets deopted due to overridden vectorcall | |
| for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): | |
| assert_equal("overridden", get_a(x)) | |
| @requires_limited_api | |
| def test_vectorcall_limited_incoming(self): | |
| from _testcapi import pyobject_vectorcall | |
| for cls in (_testlimitedcapi.LimitedVectorCallClass, | |
| _testlimitedcapi.LimitedRelativeVectorCallClass): | |
| with self.subTest(cls=cls): | |
| obj = cls() | |
| self.assertEqual( | |
| pyobject_vectorcall(obj, (), ()), | |
| "vectorcall called") | |
| @requires_limited_api | |
| def test_vectorcall_limited_outgoing(self): | |
| from _testlimitedcapi import call_vectorcall | |
| args_captured = [] | |
| kwargs_captured = [] | |
| def f(*args, **kwargs): | |
| args_captured.append(args) | |
| kwargs_captured.append(kwargs) | |
| return "success" | |
| self.assertEqual(call_vectorcall(f), "success") | |
| self.assertEqual(args_captured, [("foo",)]) | |
| self.assertEqual(kwargs_captured, [{"baz": "bar"}]) | |
| @requires_limited_api | |
| def test_vectorcall_limited_outgoing_method(self): | |
| from _testlimitedcapi import call_vectorcall_method | |
| args_captured = [] | |
| kwargs_captured = [] | |
| class TestInstance: | |
| def f(self, *args, **kwargs): | |
| args_captured.append(args) | |
| kwargs_captured.append(kwargs) | |
| return "success" | |
| self.assertEqual(call_vectorcall_method(TestInstance()), "success") | |
| self.assertEqual(args_captured, [("foo",)]) | |
| self.assertEqual(kwargs_captured, [{"baz": "bar"}]) | |
| class A: | |
| def method_two_args(arg1=None, arg2=None): | |
| pass | |
| def method_one_arg(arg1): | |
| pass | |
| def method_zero_arg(): | |
| pass | |
| @classmethod | |
| def classmethod_one_arg(arg1): | |
| pass | |
| @staticmethod | |
| def static_no_args(): | |
| pass | |
| @staticmethod | |
| def static_positional_only_one_arg(arg1, /): | |
| pass | |
| @staticmethod | |
| def static_one_arg(arg1): | |
| pass | |
| def method_one_arg_named_self(self): | |
| pass | |
| def method_one_arg_named_cls(cls): | |
| pass | |
| def method_two_arg_second_param_self(arg1, self): | |
| pass | |
| class AMeta(type): | |
| def method_one_arg(arg1): | |
| pass | |
| def method_two_arg(arg1, arg2): | |
| pass | |
| @classmethod | |
| def classmethod_one_arg(arg1): | |
| pass | |
| @staticmethod | |
| def static_one_arg(arg1): | |
| pass | |
| class AClassWithMetaclass(metaclass=AMeta): | |
| pass | |
| @cpython_only | |
| class TestIncorrectNumberOfPositionalArgs(unittest.TestCase): | |
| def test_too_many_positional_with_defaults_suggests_missing_self(self): | |
| msg = r"\.method_two_args\(\) takes from 0 to 2 positional arguments but 3 were given\. Did you forget the 'self' parameter in the function definition\?" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| A().method_two_args("woof", "loud") | |
| def test_too_many_positional_but_missing_self_no_args(self): | |
| msg = r"takes 0 positional arguments but 1 was given\. Did you forget the 'self' parameter in the function definition\?" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| A().method_zero_arg() | |
| def test_too_many_positional_but_missing_self(self): | |
| msg = r"\.method_one_arg\(\) takes 1 positional argument but 2 were given\. Did you forget the 'self' parameter in the function definition\?" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| A().method_one_arg("quiet") | |
| def test_too_many_positional(self): | |
| msg = r"takes 0 positional arguments but 1 was given" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| A.static_no_args("oops it's an arg") | |
| def test_positional_only_passed_as_keyword(self): | |
| msg = r"\.static_positional_only_one_arg\(\) got some positional-only arguments passed as keyword arguments: 'arg1'" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| A.static_positional_only_one_arg(arg1="ball") | |
| def test_unexpected_keyword(self): | |
| msg = r"method_two_args\(\) got an unexpected keyword argument 'bad'" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| A().method_two_args(bad="x") | |
| def test_multiple_values(self): | |
| msg = r"\.method_two_args\(\) got multiple values for argument 'arg1'" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| A().method_two_args("quiet", "low", arg1="oops") | |
| def test_unbound_method_with_self_keeps_missing_argument_error(self): | |
| msg = r"\.method_one_arg\(\) missing 1 required positional argument: 'arg1'" | |
| with self.assertRaisesRegex(TypeError, msg): | |
| A.method_one_arg() | |
| def test_classmethod_missing_cls_does_not_suggest_missing_self(self): | |
| msg = r"\.classmethod_one_arg\(\) takes 1 positional argument but 2 were given" | |
| with self.assertRaisesRegex(TypeError, msg) as cm: | |
| A.classmethod_one_arg("arg1") | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_classmethod_missing_cls_via_instance_does_not_suggest_missing_self(self): | |
| msg = r"\.classmethod_one_arg\(\) takes 1 positional argument but 2 were given" | |
| with self.assertRaisesRegex(TypeError, msg) as cm: | |
| A().classmethod_one_arg("poodle") | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_staticmethod_too_many_args_does_not_suggest_missing_self(self): | |
| msg = r"\.static_one_arg\(\) takes 1 positional argument but 2 were given" | |
| with self.assertRaisesRegex(TypeError, msg) as cm: | |
| A.static_one_arg(1, 2) | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_staticmethod_too_many_args_via_instance_does_not_suggest_missing_self(self): | |
| msg = r"\.static_one_arg\(\) takes 1 positional argument but 2 were given" | |
| with self.assertRaisesRegex(TypeError, msg) as cm: | |
| A().static_one_arg(1, 2) | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_metaclass_missing_receiver_does_not_suggest_missing_self(self): | |
| msg = r"AMeta\.method_one_arg\(\) takes 1 positional argument but 2 were given" | |
| with self.assertRaisesRegex(TypeError, msg) as cm: | |
| AClassWithMetaclass.method_one_arg("standard") | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_metaclass_method_too_many_args_does_not_suggest_missing_self(self): | |
| msg = r"AMeta\.method_two_arg\(\) takes 2 positional arguments but 3 were given" | |
| with self.assertRaisesRegex(TypeError, msg) as cm: | |
| AClassWithMetaclass.method_two_arg("trail", "river") | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_metaclass_classmethod_does_not_suggest_missing_self(self): | |
| msg = r"AMeta\.classmethod_one_arg\(\) takes 1 positional argument but 2 were given" | |
| with self.assertRaisesRegex(TypeError, msg) as cm: | |
| AClassWithMetaclass.classmethod_one_arg("standard") | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_metaclass_staticmethod_does_not_suggest_missing_self(self): | |
| msg = r"AMeta\.static_one_arg\(\) takes 1 positional argument but 2 were given" | |
| with self.assertRaisesRegex(TypeError, msg) as cm: | |
| AClassWithMetaclass.static_one_arg("show", "working") | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_more_than_two_surplus_args_does_not_suggest(self): | |
| with self.assertRaises(TypeError) as cm: | |
| A().method_one_arg("arg1", "arg2", "arg3") | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_when_arg_named_self_does_not_suggest(self): | |
| with self.assertRaises(TypeError) as cm: | |
| A().method_one_arg_named_self("arg1") | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_when_arg_named_cls_does_not_suggest(self): | |
| with self.assertRaises(TypeError) as cm: | |
| A().method_one_arg_named_cls("arg1") | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| def test_when_second_param_is_self(self): | |
| with self.assertRaises(TypeError) as cm: | |
| A().method_two_arg_second_param_self() | |
| self.assertNotIn("Did you forget", str(cm.exception)) | |
| @cpython_only | |
| class TestErrorMessagesSuggestions(unittest.TestCase): | |
| @contextlib.contextmanager | |
| def check_suggestion_includes(self, message): | |
| with self.assertRaises(TypeError) as cm: | |
| yield | |
| self.assertIn(f"Did you mean '{message}'?", str(cm.exception)) | |
| @contextlib.contextmanager | |
| def check_suggestion_not_present(self): | |
| with self.assertRaises(TypeError) as cm: | |
| yield | |
| self.assertNotIn("Did you mean", str(cm.exception)) | |
| def test_unexpected_keyword_suggestion_valid_positions(self): | |
| def foo(blech=None, /, aaa=None, *args, late1=None): | |
| pass | |
| cases = [ | |
| ("blach", None), | |
| ("aa", "aaa"), | |
| ("orgs", None), | |
| ("late11", "late1"), | |
| ] | |
| for keyword, suggestion in cases: | |
| with self.subTest(keyword): | |
| ctx = self.check_suggestion_includes(suggestion) if suggestion else self.check_suggestion_not_present() | |
| with ctx: | |
| foo(**{keyword:None}) | |
| def test_unexpected_keyword_suggestion_kinds(self): | |
| def substitution(noise=None, more_noise=None, a = None, blech = None): | |
| pass | |
| def elimination(noise = None, more_noise = None, a = None, blch = None): | |
| pass | |
| def addition(noise = None, more_noise = None, a = None, bluchin = None): | |
| pass | |
| def substitution_over_elimination(blach = None, bluc = None): | |
| pass | |
| def substitution_over_addition(blach = None, bluchi = None): | |
| pass | |
| def elimination_over_addition(bluc = None, blucha = None): | |
| pass | |
| def case_change_over_substitution(BLuch=None, Luch = None, fluch = None): | |
| pass | |
| for func, suggestion in [ | |
| (addition, "bluchin"), | |
| (substitution, "blech"), | |
| (elimination, "blch"), | |
| (addition, "bluchin"), | |
| (substitution_over_elimination, "blach"), | |
| (substitution_over_addition, "blach"), | |
| (elimination_over_addition, "bluc"), | |
| (case_change_over_substitution, "BLuch"), | |
| ]: | |
| with self.subTest(suggestion): | |
| with self.check_suggestion_includes(suggestion): | |
| func(bluch=None) | |
| def test_unexpected_keyword_suggestion_via_getargs(self): | |
| with self.check_suggestion_includes("maxsplit"): | |
| "foo".split(maxsplt=1) | |
| self.assertRaisesRegex( | |
| TypeError, r"split\(\) got an unexpected keyword argument 'blech'$", | |
| "foo".split, blech=1 | |
| ) | |
| with self.check_suggestion_not_present(): | |
| "foo".split(blech=1) | |
| with self.check_suggestion_not_present(): | |
| "foo".split(more_noise=1, maxsplt=1) | |
| # Also test the vgetargskeywords path | |
| with self.check_suggestion_includes("name"): | |
| ImportError(namez="oops") | |
| self.assertRaisesRegex( | |
| TypeError, r"ImportError\(\) got an unexpected keyword argument 'blech'$", | |
| ImportError, blech=1 | |
| ) | |
| with self.check_suggestion_not_present(): | |
| ImportError(blech=1) | |
| with self.check_suggestion_not_present(): | |
| ImportError(blech=1, namez="oops") | |
| @cpython_only | |
| class TestRecursion(unittest.TestCase): | |
| def test_margin_is_sufficient(self): | |
| def get_sp(): | |
| return _testinternalcapi.get_stack_pointer() | |
| this_sp = _testinternalcapi.get_stack_pointer() | |
| lower_sp = _testcapi.pyobject_vectorcall(get_sp, (), ()) | |
| if _testcapi._Py_STACK_GROWS_DOWN: | |
| self.assertLess(lower_sp, this_sp) | |
| safe_margin = this_sp - lower_sp | |
| else: | |
| self.assertGreater(lower_sp, this_sp) | |
| safe_margin = lower_sp - this_sp | |
| # Add an (arbitrary) extra 25% for safety | |
| safe_margin = safe_margin * 5 / 4 | |
| self.assertLess(safe_margin, _testinternalcapi.get_stack_margin()) | |
| @skip_on_s390x | |
| @unittest.skipIf(is_wasi and Py_DEBUG, "requires deep stack") | |
| @skip_if_sanitizer("requires deep stack", thread=True) | |
| @unittest.skipIf(_testcapi is None, "requires _testcapi") | |
| @skip_if_huge_c_stack(90_000) | |
| @skip_emscripten_stack_overflow() | |
| @skip_wasi_stack_overflow() | |
| def test_super_deep(self): | |
| def recurse(n): | |
| if n: | |
| recurse(n-1) | |
| def py_recurse(n, m): | |
| if n: | |
| py_recurse(n-1, m) | |
| else: | |
| c_py_recurse(m-1) | |
| def c_recurse(n): | |
| if n: | |
| _testcapi.pyobject_vectorcall(c_recurse, (n-1,), ()) | |
| def c_py_recurse(m): | |
| if m: | |
| _testcapi.pyobject_vectorcall(py_recurse, (1000, m), ()) | |
| with set_recursion_limit(100_000): | |
| recurse(90_000) | |
| with self.assertRaises(RecursionError): | |
| recurse(101_000) | |
| c_recurse(50) | |
| with self.assertRaises(RecursionError): | |
| c_recurse(90_000) | |
| c_py_recurse(50) | |
| with self.assertRaises(RecursionError): | |
| c_py_recurse(100_000) | |
| def test_recursion_with_kwargs(self): | |
| # GH-137883: The interpreter forgot to check the recursion limit when | |
| # calling with keywords. | |
| def recurse_kw(a=0): | |
| recurse_kw(a=0) | |
| with self.assertRaises(RecursionError): | |
| recurse_kw() | |
| class TestFunctionWithManyArgs(unittest.TestCase): | |
| def test_function_with_many_args(self): | |
| for N in (10, 500, 1000): | |
| with self.subTest(N=N): | |
| args = ",".join([f"a{i}" for i in range(N)]) | |
| src = f"def f({args}) : return a{N//2}" | |
| l = {} | |
| exec(src, {}, l) | |
| self.assertEqual(l['f'](*range(N)), N//2) | |
| @unittest.skipIf(_testcapi is None, 'need _testcapi') | |
| class TestCAPI(unittest.TestCase): | |
| def test_cfunction_call(self): | |
| def func(*args, **kwargs): | |
| return (args, kwargs) | |
| # PyCFunction_Call() was removed in Python 3.13 API, but was kept in | |
| # the stable ABI. | |
| def PyCFunction_Call(func, *args, **kwargs): | |
| if kwargs: | |
| return _testcapi.pycfunction_call(func, args, kwargs) | |
| else: | |
| return _testcapi.pycfunction_call(func, args) | |
| self.assertEqual(PyCFunction_Call(func), ((), {})) | |
| self.assertEqual(PyCFunction_Call(func, 1, 2, 3), ((1, 2, 3), {})) | |
| self.assertEqual(PyCFunction_Call(func, "arg", num=5), (("arg",), {'num': 5})) | |
| if __name__ == "__main__": | |
| unittest.main() |