From f883b7f8ee3209b52863fc662343c8cd81abdc59 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 10 Nov 2022 08:54:37 +0200 Subject: bpo-34272: Reorganize C API tests. (GH-8551) Move some C API tests into Lib/test/test_capi/. --- Lib/test/test_capi.py | 2037 -------------------- Lib/test/test_capi/__init__.py | 5 + Lib/test/test_capi/__main__.py | 3 + Lib/test/test_capi/test_getargs.py | 1297 +++++++++++++ Lib/test/test_capi/test_misc.py | 2037 ++++++++++++++++++++ Lib/test/test_capi/test_structmembers.py | 145 ++ Lib/test/test_getargs2.py | 1297 ------------- Lib/test/test_structmembers.py | 145 -- .../Tests/2018-07-29-15-59-51.bpo-34272.lVX2uR.rst | 1 + 9 files changed, 3488 insertions(+), 3479 deletions(-) delete mode 100644 Lib/test/test_capi.py create mode 100644 Lib/test/test_capi/__init__.py create mode 100644 Lib/test/test_capi/__main__.py create mode 100644 Lib/test/test_capi/test_getargs.py create mode 100644 Lib/test/test_capi/test_misc.py create mode 100644 Lib/test/test_capi/test_structmembers.py delete mode 100644 Lib/test/test_getargs2.py delete mode 100644 Lib/test/test_structmembers.py create mode 100644 Misc/NEWS.d/next/Tests/2018-07-29-15-59-51.bpo-34272.lVX2uR.rst diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py deleted file mode 100644 index ea4c9de..0000000 --- a/Lib/test/test_capi.py +++ /dev/null @@ -1,2037 +0,0 @@ -# Run the _testcapi module tests (tests for the Python/C API): by defn, -# these are all functions _testcapi exports whose name begins with 'test_'. - -from collections import OrderedDict -from contextlib import contextmanager, ExitStack -import _thread -import importlib.machinery -import importlib.util -import os -import pickle -import random -import re -import subprocess -import sys -import textwrap -import threading -import time -import unittest -import warnings -import weakref -from test import support -from test.support import MISSING_C_DOCSTRINGS -from test.support import catch_unraisable_exception -from test.support import import_helper -from test.support import threading_helper -from test.support import warnings_helper -from test.support.script_helper import assert_python_failure, assert_python_ok -try: - import _posixsubprocess -except ImportError: - _posixsubprocess = None -try: - import _testmultiphase -except ImportError: - _testmultiphase = None - -# Skip this test if the _testcapi module isn't available. -_testcapi = import_helper.import_module('_testcapi') - -import _testinternalcapi - - -def decode_stderr(err): - return err.decode('utf-8', 'replace').replace('\r', '') - - -def testfunction(self): - """some doc""" - return self - - -class InstanceMethod: - id = _testcapi.instancemethod(id) - testfunction = _testcapi.instancemethod(testfunction) - -class CAPITest(unittest.TestCase): - - def test_instancemethod(self): - inst = InstanceMethod() - self.assertEqual(id(inst), inst.id()) - self.assertTrue(inst.testfunction() is inst) - self.assertEqual(inst.testfunction.__doc__, testfunction.__doc__) - self.assertEqual(InstanceMethod.testfunction.__doc__, testfunction.__doc__) - - InstanceMethod.testfunction.attribute = "test" - self.assertEqual(testfunction.attribute, "test") - self.assertRaises(AttributeError, setattr, inst.testfunction, "attribute", "test") - - @support.requires_subprocess() - def test_no_FatalError_infinite_loop(self): - with support.SuppressCrashReport(): - p = subprocess.Popen([sys.executable, "-c", - 'import _testcapi;' - '_testcapi.crash_no_current_thread()'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True) - (out, err) = p.communicate() - self.assertEqual(out, '') - # This used to cause an infinite loop. - msg = ("Fatal Python error: PyThreadState_Get: " - "the function must be called with the GIL held, " - "after Python initialization and before Python finalization, " - "but the GIL is released " - "(the current Python thread state is NULL)") - self.assertTrue(err.rstrip().startswith(msg), - err) - - def test_memoryview_from_NULL_pointer(self): - self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer) - - def test_exception(self): - raised_exception = ValueError("5") - new_exc = TypeError("TEST") - try: - raise raised_exception - except ValueError as e: - orig_sys_exception = sys.exception() - orig_exception = _testcapi.set_exception(new_exc) - new_sys_exception = sys.exception() - new_exception = _testcapi.set_exception(orig_exception) - reset_sys_exception = sys.exception() - - self.assertEqual(orig_exception, e) - - self.assertEqual(orig_exception, raised_exception) - self.assertEqual(orig_sys_exception, orig_exception) - self.assertEqual(reset_sys_exception, orig_exception) - self.assertEqual(new_exception, new_exc) - self.assertEqual(new_sys_exception, new_exception) - else: - self.fail("Exception not raised") - - def test_exc_info(self): - raised_exception = ValueError("5") - new_exc = TypeError("TEST") - try: - raise raised_exception - except ValueError as e: - tb = e.__traceback__ - orig_sys_exc_info = sys.exc_info() - orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None) - new_sys_exc_info = sys.exc_info() - new_exc_info = _testcapi.set_exc_info(*orig_exc_info) - reset_sys_exc_info = sys.exc_info() - - self.assertEqual(orig_exc_info[1], e) - - self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb)) - self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info) - self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info) - self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None)) - self.assertSequenceEqual(new_sys_exc_info, new_exc_info) - else: - self.assertTrue(False) - - @unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.') - def test_seq_bytes_to_charp_array(self): - # Issue #15732: crash in _PySequence_BytesToCharpArray() - class Z(object): - def __len__(self): - return 1 - self.assertRaises(TypeError, _posixsubprocess.fork_exec, - 1,Z(),3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) - # Issue #15736: overflow in _PySequence_BytesToCharpArray() - class Z(object): - def __len__(self): - return sys.maxsize - def __getitem__(self, i): - return b'x' - self.assertRaises(MemoryError, _posixsubprocess.fork_exec, - 1,Z(),3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) - - @unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.') - def test_subprocess_fork_exec(self): - class Z(object): - def __len__(self): - return 1 - - # Issue #15738: crash in subprocess_fork_exec() - self.assertRaises(TypeError, _posixsubprocess.fork_exec, - Z(),[b'1'],3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) - - @unittest.skipIf(MISSING_C_DOCSTRINGS, - "Signature information for builtins requires docstrings") - def test_docstring_signature_parsing(self): - - self.assertEqual(_testcapi.no_docstring.__doc__, None) - self.assertEqual(_testcapi.no_docstring.__text_signature__, None) - - self.assertEqual(_testcapi.docstring_empty.__doc__, None) - self.assertEqual(_testcapi.docstring_empty.__text_signature__, None) - - self.assertEqual(_testcapi.docstring_no_signature.__doc__, - "This docstring has no signature.") - self.assertEqual(_testcapi.docstring_no_signature.__text_signature__, None) - - self.assertEqual(_testcapi.docstring_with_invalid_signature.__doc__, - "docstring_with_invalid_signature($module, /, boo)\n" - "\n" - "This docstring has an invalid signature." - ) - self.assertEqual(_testcapi.docstring_with_invalid_signature.__text_signature__, None) - - self.assertEqual(_testcapi.docstring_with_invalid_signature2.__doc__, - "docstring_with_invalid_signature2($module, /, boo)\n" - "\n" - "--\n" - "\n" - "This docstring also has an invalid signature." - ) - self.assertEqual(_testcapi.docstring_with_invalid_signature2.__text_signature__, None) - - self.assertEqual(_testcapi.docstring_with_signature.__doc__, - "This docstring has a valid signature.") - self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "($module, /, sig)") - - self.assertEqual(_testcapi.docstring_with_signature_but_no_doc.__doc__, None) - self.assertEqual(_testcapi.docstring_with_signature_but_no_doc.__text_signature__, - "($module, /, sig)") - - self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__doc__, - "\nThis docstring has a valid signature and some extra newlines.") - self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__, - "($module, /, parameter)") - - def test_c_type_with_matrix_multiplication(self): - M = _testcapi.matmulType - m1 = M() - m2 = M() - self.assertEqual(m1 @ m2, ("matmul", m1, m2)) - self.assertEqual(m1 @ 42, ("matmul", m1, 42)) - self.assertEqual(42 @ m1, ("matmul", 42, m1)) - o = m1 - o @= m2 - self.assertEqual(o, ("imatmul", m1, m2)) - o = m1 - o @= 42 - self.assertEqual(o, ("imatmul", m1, 42)) - o = 42 - o @= m1 - self.assertEqual(o, ("matmul", 42, m1)) - - def test_c_type_with_ipow(self): - # When the __ipow__ method of a type was implemented in C, using the - # modulo param would cause segfaults. - o = _testcapi.ipowType() - self.assertEqual(o.__ipow__(1), (1, None)) - self.assertEqual(o.__ipow__(2, 2), (2, 2)) - - def test_return_null_without_error(self): - # Issue #23571: A function must not return NULL without setting an - # error - if support.Py_DEBUG: - code = textwrap.dedent(""" - import _testcapi - from test import support - - with support.SuppressCrashReport(): - _testcapi.return_null_without_error() - """) - rc, out, err = assert_python_failure('-c', code) - err = decode_stderr(err) - self.assertRegex(err, - r'Fatal Python error: _Py_CheckFunctionResult: ' - r'a function returned NULL without setting an exception\n' - r'Python runtime state: initialized\n' - r'SystemError: ' - r'returned NULL without setting an exception\n' - r'\n' - r'Current thread.*:\n' - r' File .*", line 6 in \n') - else: - with self.assertRaises(SystemError) as cm: - _testcapi.return_null_without_error() - self.assertRegex(str(cm.exception), - 'return_null_without_error.* ' - 'returned NULL without setting an exception') - - def test_return_result_with_error(self): - # Issue #23571: A function must not return a result with an error set - if support.Py_DEBUG: - code = textwrap.dedent(""" - import _testcapi - from test import support - - with support.SuppressCrashReport(): - _testcapi.return_result_with_error() - """) - rc, out, err = assert_python_failure('-c', code) - err = decode_stderr(err) - self.assertRegex(err, - r'Fatal Python error: _Py_CheckFunctionResult: ' - r'a function returned a result with an exception set\n' - r'Python runtime state: initialized\n' - r'ValueError\n' - r'\n' - r'The above exception was the direct cause ' - r'of the following exception:\n' - r'\n' - r'SystemError: ' - r'returned a result with an exception set\n' - r'\n' - r'Current thread.*:\n' - r' File .*, line 6 in \n') - else: - with self.assertRaises(SystemError) as cm: - _testcapi.return_result_with_error() - self.assertRegex(str(cm.exception), - 'return_result_with_error.* ' - 'returned a result with an exception set') - - def test_getitem_with_error(self): - # Test _Py_CheckSlotResult(). Raise an exception and then calls - # PyObject_GetItem(): check that the assertion catches the bug. - # PyObject_GetItem() must not be called with an exception set. - code = textwrap.dedent(""" - import _testcapi - from test import support - - with support.SuppressCrashReport(): - _testcapi.getitem_with_error({1: 2}, 1) - """) - rc, out, err = assert_python_failure('-c', code) - err = decode_stderr(err) - if 'SystemError: ' not in err: - self.assertRegex(err, - r'Fatal Python error: _Py_CheckSlotResult: ' - r'Slot __getitem__ of type dict succeeded ' - r'with an exception set\n' - r'Python runtime state: initialized\n' - r'ValueError: bug\n' - r'\n' - r'Current thread .* \(most recent call first\):\n' - r' File .*, line 6 in \n' - r'\n' - r'Extension modules: _testcapi \(total: 1\)\n') - else: - # Python built with NDEBUG macro defined: - # test _Py_CheckFunctionResult() instead. - self.assertIn('returned a result with an exception set', err) - - def test_buildvalue_N(self): - _testcapi.test_buildvalue_N() - - def test_set_nomemory(self): - code = """if 1: - import _testcapi - - class C(): pass - - # The first loop tests both functions and that remove_mem_hooks() - # can be called twice in a row. The second loop checks a call to - # set_nomemory() after a call to remove_mem_hooks(). The third - # loop checks the start and stop arguments of set_nomemory(). - for outer_cnt in range(1, 4): - start = 10 * outer_cnt - for j in range(100): - if j == 0: - if outer_cnt != 3: - _testcapi.set_nomemory(start) - else: - _testcapi.set_nomemory(start, start + 1) - try: - C() - except MemoryError as e: - if outer_cnt != 3: - _testcapi.remove_mem_hooks() - print('MemoryError', outer_cnt, j) - _testcapi.remove_mem_hooks() - break - """ - rc, out, err = assert_python_ok('-c', code) - lines = out.splitlines() - for i, line in enumerate(lines, 1): - self.assertIn(b'MemoryError', out) - *_, count = line.split(b' ') - count = int(count) - self.assertLessEqual(count, i*5) - self.assertGreaterEqual(count, i*5-2) - - def test_mapping_keys_values_items(self): - class Mapping1(dict): - def keys(self): - return list(super().keys()) - def values(self): - return list(super().values()) - def items(self): - return list(super().items()) - class Mapping2(dict): - def keys(self): - return tuple(super().keys()) - def values(self): - return tuple(super().values()) - def items(self): - return tuple(super().items()) - dict_obj = {'foo': 1, 'bar': 2, 'spam': 3} - - for mapping in [{}, OrderedDict(), Mapping1(), Mapping2(), - dict_obj, OrderedDict(dict_obj), - Mapping1(dict_obj), Mapping2(dict_obj)]: - self.assertListEqual(_testcapi.get_mapping_keys(mapping), - list(mapping.keys())) - self.assertListEqual(_testcapi.get_mapping_values(mapping), - list(mapping.values())) - self.assertListEqual(_testcapi.get_mapping_items(mapping), - list(mapping.items())) - - def test_mapping_keys_values_items_bad_arg(self): - self.assertRaises(AttributeError, _testcapi.get_mapping_keys, None) - self.assertRaises(AttributeError, _testcapi.get_mapping_values, None) - self.assertRaises(AttributeError, _testcapi.get_mapping_items, None) - - class BadMapping: - def keys(self): - return None - def values(self): - return None - def items(self): - return None - bad_mapping = BadMapping() - self.assertRaises(TypeError, _testcapi.get_mapping_keys, bad_mapping) - self.assertRaises(TypeError, _testcapi.get_mapping_values, bad_mapping) - self.assertRaises(TypeError, _testcapi.get_mapping_items, bad_mapping) - - def test_mapping_has_key(self): - dct = {'a': 1} - self.assertTrue(_testcapi.mapping_has_key(dct, 'a')) - self.assertFalse(_testcapi.mapping_has_key(dct, 'b')) - - class SubDict(dict): - pass - - dct2 = SubDict({'a': 1}) - self.assertTrue(_testcapi.mapping_has_key(dct2, 'a')) - self.assertFalse(_testcapi.mapping_has_key(dct2, 'b')) - - def test_sequence_set_slice(self): - # Correct case: - data = [1, 2, 3, 4, 5] - data_copy = data.copy() - - _testcapi.sequence_set_slice(data, 1, 3, [8, 9]) - data_copy[1:3] = [8, 9] - self.assertEqual(data, data_copy) - self.assertEqual(data, [1, 8, 9, 4, 5]) - - # Custom class: - class Custom: - def __setitem__(self, index, value): - self.index = index - self.value = value - - c = Custom() - _testcapi.sequence_set_slice(c, 0, 5, 'abc') - self.assertEqual(c.index, slice(0, 5)) - self.assertEqual(c.value, 'abc') - - # Immutable sequences must raise: - bad_seq1 = (1, 2, 3, 4) - with self.assertRaises(TypeError): - _testcapi.sequence_set_slice(bad_seq1, 1, 3, (8, 9)) - self.assertEqual(bad_seq1, (1, 2, 3, 4)) - - bad_seq2 = 'abcd' - with self.assertRaises(TypeError): - _testcapi.sequence_set_slice(bad_seq2, 1, 3, 'xy') - self.assertEqual(bad_seq2, 'abcd') - - # Not a sequence: - with self.assertRaises(TypeError): - _testcapi.sequence_set_slice(None, 1, 3, 'xy') - - mapping = {1: 'a', 2: 'b', 3: 'c'} - with self.assertRaises(TypeError): - _testcapi.sequence_set_slice(mapping, 1, 3, 'xy') - self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'}) - - def test_sequence_del_slice(self): - # Correct case: - data = [1, 2, 3, 4, 5] - data_copy = data.copy() - - _testcapi.sequence_del_slice(data, 1, 3) - del data_copy[1:3] - self.assertEqual(data, data_copy) - self.assertEqual(data, [1, 4, 5]) - - # Custom class: - class Custom: - def __delitem__(self, index): - self.index = index - - c = Custom() - _testcapi.sequence_del_slice(c, 0, 5) - self.assertEqual(c.index, slice(0, 5)) - - # Immutable sequences must raise: - bad_seq1 = (1, 2, 3, 4) - with self.assertRaises(TypeError): - _testcapi.sequence_del_slice(bad_seq1, 1, 3) - self.assertEqual(bad_seq1, (1, 2, 3, 4)) - - bad_seq2 = 'abcd' - with self.assertRaises(TypeError): - _testcapi.sequence_del_slice(bad_seq2, 1, 3) - self.assertEqual(bad_seq2, 'abcd') - - # Not a sequence: - with self.assertRaises(TypeError): - _testcapi.sequence_del_slice(None, 1, 3) - - mapping = {1: 'a', 2: 'b', 3: 'c'} - with self.assertRaises(TypeError): - _testcapi.sequence_del_slice(mapping, 1, 3) - self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'}) - - @unittest.skipUnless(hasattr(_testcapi, 'negative_refcount'), - 'need _testcapi.negative_refcount') - def test_negative_refcount(self): - # bpo-35059: Check that Py_DECREF() reports the correct filename - # when calling _Py_NegativeRefcount() to abort Python. - code = textwrap.dedent(""" - import _testcapi - from test import support - - with support.SuppressCrashReport(): - _testcapi.negative_refcount() - """) - rc, out, err = assert_python_failure('-c', code) - self.assertRegex(err, - br'_testcapimodule\.c:[0-9]+: ' - br'_Py_NegativeRefcount: Assertion failed: ' - br'object has negative ref count') - - def test_trashcan_subclass(self): - # bpo-35983: Check that the trashcan mechanism for "list" is NOT - # activated when its tp_dealloc is being called by a subclass - from _testcapi import MyList - L = None - for i in range(1000): - L = MyList((L,)) - - @support.requires_resource('cpu') - def test_trashcan_python_class1(self): - self.do_test_trashcan_python_class(list) - - @support.requires_resource('cpu') - def test_trashcan_python_class2(self): - from _testcapi import MyList - self.do_test_trashcan_python_class(MyList) - - def do_test_trashcan_python_class(self, base): - # Check that the trashcan mechanism works properly for a Python - # subclass of a class using the trashcan (this specific test assumes - # that the base class "base" behaves like list) - class PyList(base): - # Count the number of PyList instances to verify that there is - # no memory leak - num = 0 - def __init__(self, *args): - __class__.num += 1 - super().__init__(*args) - def __del__(self): - __class__.num -= 1 - - for parity in (0, 1): - L = None - # We need in the order of 2**20 iterations here such that a - # typical 8MB stack would overflow without the trashcan. - for i in range(2**20): - L = PyList((L,)) - L.attr = i - if parity: - # Add one additional nesting layer - L = (L,) - self.assertGreater(PyList.num, 0) - del L - self.assertEqual(PyList.num, 0) - - def test_heap_ctype_doc_and_text_signature(self): - self.assertEqual(_testcapi.HeapDocCType.__doc__, "somedoc") - self.assertEqual(_testcapi.HeapDocCType.__text_signature__, "(arg1, arg2)") - - def test_null_type_doc(self): - self.assertEqual(_testcapi.NullTpDocType.__doc__, None) - - def test_subclass_of_heap_gc_ctype_with_tpdealloc_decrefs_once(self): - class HeapGcCTypeSubclass(_testcapi.HeapGcCType): - def __init__(self): - self.value2 = 20 - super().__init__() - - subclass_instance = HeapGcCTypeSubclass() - type_refcnt = sys.getrefcount(HeapGcCTypeSubclass) - - # Test that subclass instance was fully created - self.assertEqual(subclass_instance.value, 10) - self.assertEqual(subclass_instance.value2, 20) - - # Test that the type reference count is only decremented once - del subclass_instance - self.assertEqual(type_refcnt - 1, sys.getrefcount(HeapGcCTypeSubclass)) - - def test_subclass_of_heap_gc_ctype_with_del_modifying_dunder_class_only_decrefs_once(self): - class A(_testcapi.HeapGcCType): - def __init__(self): - self.value2 = 20 - super().__init__() - - class B(A): - def __init__(self): - super().__init__() - - def __del__(self): - self.__class__ = A - A.refcnt_in_del = sys.getrefcount(A) - B.refcnt_in_del = sys.getrefcount(B) - - subclass_instance = B() - type_refcnt = sys.getrefcount(B) - new_type_refcnt = sys.getrefcount(A) - - # Test that subclass instance was fully created - self.assertEqual(subclass_instance.value, 10) - self.assertEqual(subclass_instance.value2, 20) - - del subclass_instance - - # Test that setting __class__ modified the reference counts of the types - if support.Py_DEBUG: - # gh-89373: In debug mode, _Py_Dealloc() keeps a strong reference - # to the type while calling tp_dealloc() - self.assertEqual(type_refcnt, B.refcnt_in_del) - else: - self.assertEqual(type_refcnt - 1, B.refcnt_in_del) - self.assertEqual(new_type_refcnt + 1, A.refcnt_in_del) - - # Test that the original type already has decreased its refcnt - self.assertEqual(type_refcnt - 1, sys.getrefcount(B)) - - # Test that subtype_dealloc decref the newly assigned __class__ only once - self.assertEqual(new_type_refcnt, sys.getrefcount(A)) - - def test_heaptype_with_dict(self): - inst = _testcapi.HeapCTypeWithDict() - inst.foo = 42 - self.assertEqual(inst.foo, 42) - self.assertEqual(inst.dictobj, inst.__dict__) - self.assertEqual(inst.dictobj, {"foo": 42}) - - inst = _testcapi.HeapCTypeWithDict() - self.assertEqual({}, inst.__dict__) - - def test_heaptype_with_managed_dict(self): - inst = _testcapi.HeapCTypeWithManagedDict() - inst.foo = 42 - self.assertEqual(inst.foo, 42) - self.assertEqual(inst.__dict__, {"foo": 42}) - - inst = _testcapi.HeapCTypeWithManagedDict() - self.assertEqual({}, inst.__dict__) - - a = _testcapi.HeapCTypeWithManagedDict() - b = _testcapi.HeapCTypeWithManagedDict() - a.b = b - b.a = a - del a, b - - def test_sublclassing_managed_dict(self): - - class C(_testcapi.HeapCTypeWithManagedDict): - pass - - i = C() - i.spam = i - del i - - def test_heaptype_with_negative_dict(self): - inst = _testcapi.HeapCTypeWithNegativeDict() - inst.foo = 42 - self.assertEqual(inst.foo, 42) - self.assertEqual(inst.dictobj, inst.__dict__) - self.assertEqual(inst.dictobj, {"foo": 42}) - - inst = _testcapi.HeapCTypeWithNegativeDict() - self.assertEqual({}, inst.__dict__) - - def test_heaptype_with_weakref(self): - inst = _testcapi.HeapCTypeWithWeakref() - ref = weakref.ref(inst) - self.assertEqual(ref(), inst) - self.assertEqual(inst.weakreflist, ref) - - def test_heaptype_with_managed_weakref(self): - inst = _testcapi.HeapCTypeWithManagedWeakref() - ref = weakref.ref(inst) - self.assertEqual(ref(), inst) - - def test_sublclassing_managed_weakref(self): - - class C(_testcapi.HeapCTypeWithManagedWeakref): - pass - - inst = C() - ref = weakref.ref(inst) - self.assertEqual(ref(), inst) - - def test_sublclassing_managed_both(self): - - class C1(_testcapi.HeapCTypeWithManagedWeakref, _testcapi.HeapCTypeWithManagedDict): - pass - - class C2(_testcapi.HeapCTypeWithManagedDict, _testcapi.HeapCTypeWithManagedWeakref): - pass - - for cls in (C1, C2): - inst = cls() - ref = weakref.ref(inst) - self.assertEqual(ref(), inst) - inst.spam = inst - del inst - ref = weakref.ref(cls()) - self.assertIs(ref(), None) - - def test_heaptype_with_buffer(self): - inst = _testcapi.HeapCTypeWithBuffer() - b = bytes(inst) - self.assertEqual(b, b"1234") - - def test_c_subclass_of_heap_ctype_with_tpdealloc_decrefs_once(self): - subclass_instance = _testcapi.HeapCTypeSubclass() - type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclass) - - # Test that subclass instance was fully created - self.assertEqual(subclass_instance.value, 10) - self.assertEqual(subclass_instance.value2, 20) - - # Test that the type reference count is only decremented once - del subclass_instance - self.assertEqual(type_refcnt - 1, sys.getrefcount(_testcapi.HeapCTypeSubclass)) - - def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_once(self): - subclass_instance = _testcapi.HeapCTypeSubclassWithFinalizer() - type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer) - new_type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclass) - - # Test that subclass instance was fully created - self.assertEqual(subclass_instance.value, 10) - self.assertEqual(subclass_instance.value2, 20) - - # The tp_finalize slot will set __class__ to HeapCTypeSubclass - del subclass_instance - - # Test that setting __class__ modified the reference counts of the types - if support.Py_DEBUG: - # gh-89373: In debug mode, _Py_Dealloc() keeps a strong reference - # to the type while calling tp_dealloc() - self.assertEqual(type_refcnt, _testcapi.HeapCTypeSubclassWithFinalizer.refcnt_in_del) - else: - self.assertEqual(type_refcnt - 1, _testcapi.HeapCTypeSubclassWithFinalizer.refcnt_in_del) - self.assertEqual(new_type_refcnt + 1, _testcapi.HeapCTypeSubclass.refcnt_in_del) - - # Test that the original type already has decreased its refcnt - self.assertEqual(type_refcnt - 1, sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer)) - - # Test that subtype_dealloc decref the newly assigned __class__ only once - self.assertEqual(new_type_refcnt, sys.getrefcount(_testcapi.HeapCTypeSubclass)) - - def test_heaptype_with_setattro(self): - obj = _testcapi.HeapCTypeSetattr() - self.assertEqual(obj.pvalue, 10) - obj.value = 12 - self.assertEqual(obj.pvalue, 12) - del obj.value - self.assertEqual(obj.pvalue, 0) - - def test_heaptype_with_custom_metaclass(self): - self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclass, type)) - self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclassCustomNew, type)) - - t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclass) - self.assertIsInstance(t, type) - self.assertEqual(t.__name__, "HeapCTypeViaMetaclass") - self.assertIs(type(t), _testcapi.HeapCTypeMetaclass) - - msg = "Metaclasses with custom tp_new are not supported." - with self.assertRaisesRegex(TypeError, msg): - t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclassCustomNew) - - def test_multiple_inheritance_ctypes_with_weakref_or_dict(self): - - with self.assertRaises(TypeError): - class Both1(_testcapi.HeapCTypeWithWeakref, _testcapi.HeapCTypeWithDict): - pass - with self.assertRaises(TypeError): - class Both2(_testcapi.HeapCTypeWithDict, _testcapi.HeapCTypeWithWeakref): - pass - - def test_multiple_inheritance_ctypes_with_weakref_or_dict_and_other_builtin(self): - - with self.assertRaises(TypeError): - class C1(_testcapi.HeapCTypeWithDict, list): - pass - - with self.assertRaises(TypeError): - class C2(_testcapi.HeapCTypeWithWeakref, list): - pass - - class C3(_testcapi.HeapCTypeWithManagedDict, list): - pass - class C4(_testcapi.HeapCTypeWithManagedWeakref, list): - pass - - inst = C3() - inst.append(0) - str(inst.__dict__) - - inst = C4() - inst.append(0) - str(inst.__weakref__) - - for cls in (_testcapi.HeapCTypeWithManagedDict, _testcapi.HeapCTypeWithManagedWeakref): - for cls2 in (_testcapi.HeapCTypeWithDict, _testcapi.HeapCTypeWithWeakref): - class S(cls, cls2): - pass - class B1(C3, cls): - pass - class B2(C4, cls): - pass - - def test_pytype_fromspec_with_repeated_slots(self): - for variant in range(2): - with self.subTest(variant=variant): - with self.assertRaises(SystemError): - _testcapi.create_type_from_repeated_slots(variant) - - @warnings_helper.ignore_warnings(category=DeprecationWarning) - def test_immutable_type_with_mutable_base(self): - # Add deprecation warning here so it's removed in 3.14 - warnings._deprecated( - 'creating immutable classes with mutable bases', remove=(3, 14)) - - class MutableBase: - def meth(self): - return 'original' - - with self.assertWarns(DeprecationWarning): - ImmutableSubclass = _testcapi.make_immutable_type_with_base( - MutableBase) - instance = ImmutableSubclass() - - self.assertEqual(instance.meth(), 'original') - - # Cannot override the static type's method - with self.assertRaisesRegex( - TypeError, - "cannot set 'meth' attribute of immutable type"): - ImmutableSubclass.meth = lambda self: 'overridden' - self.assertEqual(instance.meth(), 'original') - - # Can change the method on the mutable base - MutableBase.meth = lambda self: 'changed' - self.assertEqual(instance.meth(), 'changed') - - - def test_pynumber_tobase(self): - from _testcapi import pynumber_tobase - small_number = 123 - large_number = 2**64 - class IDX: - def __init__(self, val): - self.val = val - def __index__(self): - return self.val - - test_cases = ((2, '0b1111011', '0b10000000000000000000000000000000000000000000000000000000000000000'), - (8, '0o173', '0o2000000000000000000000'), - (10, '123', '18446744073709551616'), - (16, '0x7b', '0x10000000000000000')) - for base, small_target, large_target in test_cases: - with self.subTest(base=base, st=small_target, lt=large_target): - # Test for small number - self.assertEqual(pynumber_tobase(small_number, base), small_target) - self.assertEqual(pynumber_tobase(-small_number, base), '-' + small_target) - self.assertEqual(pynumber_tobase(IDX(small_number), base), small_target) - # Test for large number(out of range of a longlong,i.e.[-2**63, 2**63-1]) - self.assertEqual(pynumber_tobase(large_number, base), large_target) - self.assertEqual(pynumber_tobase(-large_number, base), '-' + large_target) - self.assertEqual(pynumber_tobase(IDX(large_number), base), large_target) - self.assertRaises(TypeError, pynumber_tobase, IDX(123.0), 10) - self.assertRaises(TypeError, pynumber_tobase, IDX('123'), 10) - self.assertRaises(TypeError, pynumber_tobase, 123.0, 10) - self.assertRaises(TypeError, pynumber_tobase, '123', 10) - self.assertRaises(SystemError, pynumber_tobase, 123, 0) - - def check_fatal_error(self, code, expected, not_expected=()): - with support.SuppressCrashReport(): - rc, out, err = assert_python_failure('-sSI', '-c', code) - - err = decode_stderr(err) - self.assertIn('Fatal Python error: test_fatal_error: MESSAGE\n', - err) - - match = re.search(r'^Extension modules:(.*) \(total: ([0-9]+)\)$', - err, re.MULTILINE) - if not match: - self.fail(f"Cannot find 'Extension modules:' in {err!r}") - modules = set(match.group(1).strip().split(', ')) - total = int(match.group(2)) - - for name in expected: - self.assertIn(name, modules) - for name in not_expected: - self.assertNotIn(name, modules) - self.assertEqual(len(modules), total) - - @support.requires_subprocess() - def test_fatal_error(self): - # By default, stdlib extension modules are ignored, - # but not test modules. - expected = ('_testcapi',) - not_expected = ('sys',) - code = 'import _testcapi, sys; _testcapi.fatal_error(b"MESSAGE")' - self.check_fatal_error(code, expected, not_expected) - - # Mark _testcapi as stdlib module, but not sys - expected = ('sys',) - not_expected = ('_testcapi',) - code = textwrap.dedent(''' - import _testcapi, sys - sys.stdlib_module_names = frozenset({"_testcapi"}) - _testcapi.fatal_error(b"MESSAGE") - ''') - self.check_fatal_error(code, expected) - - def test_pyobject_repr_from_null(self): - s = _testcapi.pyobject_repr_from_null() - self.assertEqual(s, '') - - def test_pyobject_str_from_null(self): - s = _testcapi.pyobject_str_from_null() - self.assertEqual(s, '') - - def test_pyobject_bytes_from_null(self): - s = _testcapi.pyobject_bytes_from_null() - self.assertEqual(s, b'') - - def test_Py_CompileString(self): - # Check that Py_CompileString respects the coding cookie - _compile = _testcapi.Py_CompileString - code = b"# -*- coding: latin1 -*-\nprint('\xc2\xa4')\n" - result = _compile(code) - expected = compile(code, "", "exec") - self.assertEqual(result.co_consts, expected.co_consts) - - def test_export_symbols(self): - # bpo-44133: Ensure that the "Py_FrozenMain" and - # "PyThread_get_thread_native_id" symbols are exported by the Python - # (directly by the binary, or via by the Python dynamic library). - ctypes = import_helper.import_module('ctypes') - names = [] - - # Test if the PY_HAVE_THREAD_NATIVE_ID macro is defined - if hasattr(_thread, 'get_native_id'): - names.append('PyThread_get_thread_native_id') - - # Python/frozenmain.c fails to build on Windows when the symbols are - # missing: - # - PyWinFreeze_ExeInit - # - PyWinFreeze_ExeTerm - # - PyInitFrozenExtensions - if os.name != 'nt': - names.append('Py_FrozenMain') - - for name in names: - with self.subTest(name=name): - self.assertTrue(hasattr(ctypes.pythonapi, name)) - - def test_clear_managed_dict(self): - - class C: - def __init__(self): - self.a = 1 - - c = C() - _testcapi.clear_managed_dict(c) - self.assertEqual(c.__dict__, {}) - c = C() - self.assertEqual(c.__dict__, {'a':1}) - _testcapi.clear_managed_dict(c) - self.assertEqual(c.__dict__, {}) - - def test_eval_get_func_name(self): - def function_example(): ... - - class A: - def method_example(self): ... - - self.assertEqual(_testcapi.eval_get_func_name(function_example), - "function_example") - self.assertEqual(_testcapi.eval_get_func_name(A.method_example), - "method_example") - self.assertEqual(_testcapi.eval_get_func_name(A().method_example), - "method_example") - self.assertEqual(_testcapi.eval_get_func_name(sum), "sum") # c function - self.assertEqual(_testcapi.eval_get_func_name(A), "type") - - def test_eval_get_func_desc(self): - def function_example(): ... - - class A: - def method_example(self): ... - - self.assertEqual(_testcapi.eval_get_func_desc(function_example), - "()") - self.assertEqual(_testcapi.eval_get_func_desc(A.method_example), - "()") - self.assertEqual(_testcapi.eval_get_func_desc(A().method_example), - "()") - self.assertEqual(_testcapi.eval_get_func_desc(sum), "()") # c function - self.assertEqual(_testcapi.eval_get_func_desc(A), " object") - - def test_function_get_code(self): - import types - - def some(): - pass - - code = _testcapi.function_get_code(some) - self.assertIsInstance(code, types.CodeType) - self.assertEqual(code, some.__code__) - - with self.assertRaises(SystemError): - _testcapi.function_get_code(None) # not a function - - def test_function_get_globals(self): - def some(): - pass - - globals_ = _testcapi.function_get_globals(some) - self.assertIsInstance(globals_, dict) - self.assertEqual(globals_, some.__globals__) - - with self.assertRaises(SystemError): - _testcapi.function_get_globals(None) # not a function - - def test_function_get_module(self): - def some(): - pass - - module = _testcapi.function_get_module(some) - self.assertIsInstance(module, str) - self.assertEqual(module, some.__module__) - - with self.assertRaises(SystemError): - _testcapi.function_get_module(None) # not a function - - def test_function_get_defaults(self): - def some( - pos_only1, pos_only2='p', - /, - zero=0, optional=None, - *, - kw1, - kw2=True, - ): - pass - - defaults = _testcapi.function_get_defaults(some) - self.assertEqual(defaults, ('p', 0, None)) - self.assertEqual(defaults, some.__defaults__) - - with self.assertRaises(SystemError): - _testcapi.function_get_defaults(None) # not a function - - def test_function_set_defaults(self): - def some( - pos_only1, pos_only2='p', - /, - zero=0, optional=None, - *, - kw1, - kw2=True, - ): - pass - - old_defaults = ('p', 0, None) - self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) - self.assertEqual(some.__defaults__, old_defaults) - - with self.assertRaises(SystemError): - _testcapi.function_set_defaults(some, 1) # not tuple or None - self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) - self.assertEqual(some.__defaults__, old_defaults) - - with self.assertRaises(SystemError): - _testcapi.function_set_defaults(1, ()) # not a function - self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) - self.assertEqual(some.__defaults__, old_defaults) - - new_defaults = ('q', 1, None) - _testcapi.function_set_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) - self.assertEqual(some.__defaults__, new_defaults) - - # Empty tuple is fine: - new_defaults = () - _testcapi.function_set_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) - self.assertEqual(some.__defaults__, new_defaults) - - class tuplesub(tuple): ... # tuple subclasses must work - - new_defaults = tuplesub(((1, 2), ['a', 'b'], None)) - _testcapi.function_set_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) - self.assertEqual(some.__defaults__, new_defaults) - - # `None` is special, it sets `defaults` to `NULL`, - # it needs special handling in `_testcapi`: - _testcapi.function_set_defaults(some, None) - self.assertEqual(_testcapi.function_get_defaults(some), None) - self.assertEqual(some.__defaults__, None) - - def test_function_get_kw_defaults(self): - def some( - pos_only1, pos_only2='p', - /, - zero=0, optional=None, - *, - kw1, - kw2=True, - ): - pass - - defaults = _testcapi.function_get_kw_defaults(some) - self.assertEqual(defaults, {'kw2': True}) - self.assertEqual(defaults, some.__kwdefaults__) - - with self.assertRaises(SystemError): - _testcapi.function_get_kw_defaults(None) # not a function - - def test_function_set_kw_defaults(self): - def some( - pos_only1, pos_only2='p', - /, - zero=0, optional=None, - *, - kw1, - kw2=True, - ): - pass - - old_defaults = {'kw2': True} - self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) - self.assertEqual(some.__kwdefaults__, old_defaults) - - with self.assertRaises(SystemError): - _testcapi.function_set_kw_defaults(some, 1) # not dict or None - self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) - self.assertEqual(some.__kwdefaults__, old_defaults) - - with self.assertRaises(SystemError): - _testcapi.function_set_kw_defaults(1, {}) # not a function - self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) - self.assertEqual(some.__kwdefaults__, old_defaults) - - new_defaults = {'kw2': (1, 2, 3)} - _testcapi.function_set_kw_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) - self.assertEqual(some.__kwdefaults__, new_defaults) - - # Empty dict is fine: - new_defaults = {} - _testcapi.function_set_kw_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) - self.assertEqual(some.__kwdefaults__, new_defaults) - - class dictsub(dict): ... # dict subclasses must work - - new_defaults = dictsub({'kw2': None}) - _testcapi.function_set_kw_defaults(some, new_defaults) - self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) - self.assertEqual(some.__kwdefaults__, new_defaults) - - # `None` is special, it sets `kwdefaults` to `NULL`, - # it needs special handling in `_testcapi`: - _testcapi.function_set_kw_defaults(some, None) - self.assertEqual(_testcapi.function_get_kw_defaults(some), None) - self.assertEqual(some.__kwdefaults__, None) - - -class TestPendingCalls(unittest.TestCase): - - def pendingcalls_submit(self, l, n): - def callback(): - #this function can be interrupted by thread switching so let's - #use an atomic operation - l.append(None) - - for i in range(n): - time.sleep(random.random()*0.02) #0.01 secs on average - #try submitting callback until successful. - #rely on regular interrupt to flush queue if we are - #unsuccessful. - while True: - if _testcapi._pending_threadfunc(callback): - break - - def pendingcalls_wait(self, l, n, context = None): - #now, stick around until l[0] has grown to 10 - count = 0 - while len(l) != n: - #this busy loop is where we expect to be interrupted to - #run our callbacks. Note that callbacks are only run on the - #main thread - if False and support.verbose: - print("(%i)"%(len(l),),) - for i in range(1000): - a = i*i - if context and not context.event.is_set(): - continue - count += 1 - self.assertTrue(count < 10000, - "timeout waiting for %i callbacks, got %i"%(n, len(l))) - if False and support.verbose: - print("(%i)"%(len(l),)) - - @threading_helper.requires_working_threading() - def test_pendingcalls_threaded(self): - - #do every callback on a separate thread - n = 32 #total callbacks - threads = [] - class foo(object):pass - context = foo() - context.l = [] - context.n = 2 #submits per thread - context.nThreads = n // context.n - context.nFinished = 0 - context.lock = threading.Lock() - context.event = threading.Event() - - threads = [threading.Thread(target=self.pendingcalls_thread, - args=(context,)) - for i in range(context.nThreads)] - with threading_helper.start_threads(threads): - self.pendingcalls_wait(context.l, n, context) - - def pendingcalls_thread(self, context): - try: - self.pendingcalls_submit(context.l, context.n) - finally: - with context.lock: - context.nFinished += 1 - nFinished = context.nFinished - if False and support.verbose: - print("finished threads: ", nFinished) - if nFinished == context.nThreads: - context.event.set() - - def test_pendingcalls_non_threaded(self): - #again, just using the main thread, likely they will all be dispatched at - #once. It is ok to ask for too many, because we loop until we find a slot. - #the loop can be interrupted to dispatch. - #there are only 32 dispatch slots, so we go for twice that! - l = [] - n = 64 - self.pendingcalls_submit(l, n) - self.pendingcalls_wait(l, n) - - -class SubinterpreterTest(unittest.TestCase): - - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_subinterps(self): - import builtins - r, w = os.pipe() - code = """if 1: - import sys, builtins, pickle - with open({:d}, "wb") as f: - pickle.dump(id(sys.modules), f) - pickle.dump(id(builtins), f) - """.format(w) - with open(r, "rb") as f: - ret = support.run_in_subinterp(code) - self.assertEqual(ret, 0) - self.assertNotEqual(pickle.load(f), id(sys.modules)) - self.assertNotEqual(pickle.load(f), id(builtins)) - - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_subinterps_recent_language_features(self): - r, w = os.pipe() - code = """if 1: - import pickle - with open({:d}, "wb") as f: - - @(lambda x:x) # Py 3.9 - def noop(x): return x - - a = (b := f'1{{2}}3') + noop('x') # Py 3.8 (:=) / 3.6 (f'') - - async def foo(arg): return await arg # Py 3.5 - - pickle.dump(dict(a=a, b=b), f) - """.format(w) - - with open(r, "rb") as f: - ret = support.run_in_subinterp(code) - self.assertEqual(ret, 0) - self.assertEqual(pickle.load(f), {'a': '123x', 'b': '123'}) - - def test_py_config_isoloated_per_interpreter(self): - # A config change in one interpreter must not leak to out to others. - # - # This test could verify ANY config value, it just happens to have been - # written around the time of int_max_str_digits. Refactoring is okay. - code = """if 1: - import sys, _testinternalcapi - - # Any config value would do, this happens to be the one being - # double checked at the time this test was written. - config = _testinternalcapi.get_config() - config['int_max_str_digits'] = 55555 - _testinternalcapi.set_config(config) - sub_value = _testinternalcapi.get_config()['int_max_str_digits'] - assert sub_value == 55555, sub_value - """ - before_config = _testinternalcapi.get_config() - assert before_config['int_max_str_digits'] != 55555 - self.assertEqual(support.run_in_subinterp(code), 0, - 'subinterp code failure, check stderr.') - after_config = _testinternalcapi.get_config() - self.assertIsNot( - before_config, after_config, - "Expected get_config() to return a new dict on each call") - self.assertEqual(before_config, after_config, - "CAUTION: Tests executed after this may be " - "running under an altered config.") - # try:...finally: calling set_config(before_config) not done - # as that results in sys.argv, sys.path, and sys.warnoptions - # "being modified by test_capi" per test.regrtest. So if this - # test fails, assume that the environment in this process may - # be altered and suspect. - - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_configured_settings(self): - """ - The config with which an interpreter is created corresponds - 1-to-1 with the new interpreter's settings. This test verifies - that they match. - """ - import json - - THREADS = 1<<10 - DAEMON_THREADS = 1<<11 - FORK = 1<<15 - EXEC = 1<<16 - - features = ['fork', 'exec', 'threads', 'daemon_threads'] - kwlist = [f'allow_{n}' for n in features] - for config, expected in { - (True, True, True, True): FORK | EXEC | THREADS | DAEMON_THREADS, - (False, False, False, False): 0, - (False, False, True, False): THREADS, - }.items(): - kwargs = dict(zip(kwlist, config)) - expected = { - 'feature_flags': expected, - } - with self.subTest(config): - r, w = os.pipe() - script = textwrap.dedent(f''' - import _testinternalcapi, json, os - settings = _testinternalcapi.get_interp_settings() - with os.fdopen({w}, "w") as stdin: - json.dump(settings, stdin) - ''') - with os.fdopen(r) as stdout: - support.run_in_subinterp_with_config(script, **kwargs) - out = stdout.read() - settings = json.loads(out) - - self.assertEqual(settings, expected) - - def test_mutate_exception(self): - """ - Exceptions saved in global module state get shared between - individual module instances. This test checks whether or not - a change in one interpreter's module gets reflected into the - other ones. - """ - import binascii - - support.run_in_subinterp("import binascii; binascii.Error.foobar = 'foobar'") - - self.assertFalse(hasattr(binascii.Error, "foobar")) - - @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") - def test_module_state_shared_in_global(self): - """ - bpo-44050: Extension module state should be shared between interpreters - when it doesn't support sub-interpreters. - """ - r, w = os.pipe() - self.addCleanup(os.close, r) - self.addCleanup(os.close, w) - - script = textwrap.dedent(f""" - import importlib.machinery - import importlib.util - import os - - fullname = '_test_module_state_shared' - origin = importlib.util.find_spec('_testmultiphase').origin - loader = importlib.machinery.ExtensionFileLoader(fullname, origin) - spec = importlib.util.spec_from_loader(fullname, loader) - module = importlib.util.module_from_spec(spec) - attr_id = str(id(module.Error)).encode() - - os.write({w}, attr_id) - """) - exec(script) - main_attr_id = os.read(r, 100) - - ret = support.run_in_subinterp(script) - self.assertEqual(ret, 0) - subinterp_attr_id = os.read(r, 100) - self.assertEqual(main_attr_id, subinterp_attr_id) - - -class TestThreadState(unittest.TestCase): - - @threading_helper.reap_threads - @threading_helper.requires_working_threading() - def test_thread_state(self): - # some extra thread-state tests driven via _testcapi - def target(): - idents = [] - - def callback(): - idents.append(threading.get_ident()) - - _testcapi._test_thread_state(callback) - a = b = callback - time.sleep(1) - # Check our main thread is in the list exactly 3 times. - self.assertEqual(idents.count(threading.get_ident()), 3, - "Couldn't find main thread correctly in the list") - - target() - t = threading.Thread(target=target) - t.start() - t.join() - - @threading_helper.reap_threads - @threading_helper.requires_working_threading() - def test_gilstate_ensure_no_deadlock(self): - # See https://github.com/python/cpython/issues/96071 - code = textwrap.dedent(f""" - import _testcapi - - def callback(): - print('callback called') - - _testcapi._test_thread_state(callback) - """) - ret = assert_python_ok('-X', 'tracemalloc', '-c', code) - self.assertIn(b'callback called', ret.out) - - -class Test_testcapi(unittest.TestCase): - locals().update((name, getattr(_testcapi, name)) - for name in dir(_testcapi) - if name.startswith('test_') and not name.endswith('_code')) - - # Suppress warning from PyUnicode_FromUnicode(). - @warnings_helper.ignore_warnings(category=DeprecationWarning) - def test_widechar(self): - _testcapi.test_widechar() - - def test_version_api_data(self): - self.assertEqual(_testcapi.Py_Version, sys.hexversion) - - -class Test_testinternalcapi(unittest.TestCase): - locals().update((name, getattr(_testinternalcapi, name)) - for name in dir(_testinternalcapi) - if name.startswith('test_')) - - -@support.requires_subprocess() -class PyMemDebugTests(unittest.TestCase): - PYTHONMALLOC = 'debug' - # '0x04c06e0' or '04C06E0' - PTR_REGEX = r'(?:0x)?[0-9a-fA-F]+' - - def check(self, code): - with support.SuppressCrashReport(): - out = assert_python_failure( - '-c', code, - PYTHONMALLOC=self.PYTHONMALLOC, - # FreeBSD: instruct jemalloc to not fill freed() memory - # with junk byte 0x5a, see JEMALLOC(3) - MALLOC_CONF="junk:false", - ) - stderr = out.err - return stderr.decode('ascii', 'replace') - - def test_buffer_overflow(self): - out = self.check('import _testcapi; _testcapi.pymem_buffer_overflow()') - regex = (r"Debug memory block at address p={ptr}: API 'm'\n" - r" 16 bytes originally requested\n" - r" The [0-9] pad bytes at p-[0-9] are FORBIDDENBYTE, as expected.\n" - r" The [0-9] pad bytes at tail={ptr} are not all FORBIDDENBYTE \(0x[0-9a-f]{{2}}\):\n" - r" at tail\+0: 0x78 \*\*\* OUCH\n" - r" at tail\+1: 0xfd\n" - r" at tail\+2: 0xfd\n" - r" .*\n" - r"( The block was made by call #[0-9]+ to debug malloc/realloc.\n)?" - r" Data at p: cd cd cd .*\n" - r"\n" - r"Enable tracemalloc to get the memory block allocation traceback\n" - r"\n" - r"Fatal Python error: _PyMem_DebugRawFree: bad trailing pad byte") - regex = regex.format(ptr=self.PTR_REGEX) - regex = re.compile(regex, flags=re.DOTALL) - self.assertRegex(out, regex) - - def test_api_misuse(self): - out = self.check('import _testcapi; _testcapi.pymem_api_misuse()') - regex = (r"Debug memory block at address p={ptr}: API 'm'\n" - r" 16 bytes originally requested\n" - r" The [0-9] pad bytes at p-[0-9] are FORBIDDENBYTE, as expected.\n" - r" The [0-9] pad bytes at tail={ptr} are FORBIDDENBYTE, as expected.\n" - r"( The block was made by call #[0-9]+ to debug malloc/realloc.\n)?" - r" Data at p: cd cd cd .*\n" - r"\n" - r"Enable tracemalloc to get the memory block allocation traceback\n" - r"\n" - r"Fatal Python error: _PyMem_DebugRawFree: bad ID: Allocated using API 'm', verified using API 'r'\n") - regex = regex.format(ptr=self.PTR_REGEX) - self.assertRegex(out, regex) - - def check_malloc_without_gil(self, code): - out = self.check(code) - expected = ('Fatal Python error: _PyMem_DebugMalloc: ' - 'Python memory allocator called without holding the GIL') - self.assertIn(expected, out) - - def test_pymem_malloc_without_gil(self): - # Debug hooks must raise an error if PyMem_Malloc() is called - # without holding the GIL - code = 'import _testcapi; _testcapi.pymem_malloc_without_gil()' - self.check_malloc_without_gil(code) - - def test_pyobject_malloc_without_gil(self): - # Debug hooks must raise an error if PyObject_Malloc() is called - # without holding the GIL - code = 'import _testcapi; _testcapi.pyobject_malloc_without_gil()' - self.check_malloc_without_gil(code) - - def check_pyobject_is_freed(self, func_name): - code = textwrap.dedent(f''' - import gc, os, sys, _testcapi - # Disable the GC to avoid crash on GC collection - gc.disable() - try: - _testcapi.{func_name}() - # Exit immediately to avoid a crash while deallocating - # the invalid object - os._exit(0) - except _testcapi.error: - os._exit(1) - ''') - assert_python_ok( - '-c', code, - PYTHONMALLOC=self.PYTHONMALLOC, - MALLOC_CONF="junk:false", - ) - - def test_pyobject_null_is_freed(self): - self.check_pyobject_is_freed('check_pyobject_null_is_freed') - - def test_pyobject_uninitialized_is_freed(self): - self.check_pyobject_is_freed('check_pyobject_uninitialized_is_freed') - - def test_pyobject_forbidden_bytes_is_freed(self): - self.check_pyobject_is_freed('check_pyobject_forbidden_bytes_is_freed') - - def test_pyobject_freed_is_freed(self): - self.check_pyobject_is_freed('check_pyobject_freed_is_freed') - - -class PyMemMallocDebugTests(PyMemDebugTests): - PYTHONMALLOC = 'malloc_debug' - - -@unittest.skipUnless(support.with_pymalloc(), 'need pymalloc') -class PyMemPymallocDebugTests(PyMemDebugTests): - PYTHONMALLOC = 'pymalloc_debug' - - -@unittest.skipUnless(support.Py_DEBUG, 'need Py_DEBUG') -class PyMemDefaultTests(PyMemDebugTests): - # test default allocator of Python compiled in debug mode - PYTHONMALLOC = '' - - -@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") -class Test_ModuleStateAccess(unittest.TestCase): - """Test access to module start (PEP 573)""" - - # The C part of the tests lives in _testmultiphase, in a module called - # _testmultiphase_meth_state_access. - # This module has multi-phase initialization, unlike _testcapi. - - def setUp(self): - fullname = '_testmultiphase_meth_state_access' # XXX - origin = importlib.util.find_spec('_testmultiphase').origin - loader = importlib.machinery.ExtensionFileLoader(fullname, origin) - spec = importlib.util.spec_from_loader(fullname, loader) - module = importlib.util.module_from_spec(spec) - loader.exec_module(module) - self.module = module - - def test_subclass_get_module(self): - """PyType_GetModule for defining_class""" - class StateAccessType_Subclass(self.module.StateAccessType): - pass - - instance = StateAccessType_Subclass() - self.assertIs(instance.get_defining_module(), self.module) - - def test_subclass_get_module_with_super(self): - class StateAccessType_Subclass(self.module.StateAccessType): - def get_defining_module(self): - return super().get_defining_module() - - instance = StateAccessType_Subclass() - self.assertIs(instance.get_defining_module(), self.module) - - def test_state_access(self): - """Checks methods defined with and without argument clinic - - This tests a no-arg method (get_count) and a method with - both a positional and keyword argument. - """ - - a = self.module.StateAccessType() - b = self.module.StateAccessType() - - methods = { - 'clinic': a.increment_count_clinic, - 'noclinic': a.increment_count_noclinic, - } - - for name, increment_count in methods.items(): - with self.subTest(name): - self.assertEqual(a.get_count(), b.get_count()) - self.assertEqual(a.get_count(), 0) - - increment_count() - self.assertEqual(a.get_count(), b.get_count()) - self.assertEqual(a.get_count(), 1) - - increment_count(3) - self.assertEqual(a.get_count(), b.get_count()) - self.assertEqual(a.get_count(), 4) - - increment_count(-2, twice=True) - self.assertEqual(a.get_count(), b.get_count()) - self.assertEqual(a.get_count(), 0) - - with self.assertRaises(TypeError): - increment_count(thrice=3) - - with self.assertRaises(TypeError): - increment_count(1, 2, 3) - - def test_get_module_bad_def(self): - # PyType_GetModuleByDef fails gracefully if it doesn't - # find what it's looking for. - # see bpo-46433 - instance = self.module.StateAccessType() - with self.assertRaises(TypeError): - instance.getmodulebydef_bad_def() - - def test_get_module_static_in_mro(self): - # Here, the class PyType_GetModuleByDef is looking for - # appears in the MRO after a static type (Exception). - # see bpo-46433 - class Subclass(BaseException, self.module.StateAccessType): - pass - self.assertIs(Subclass().get_defining_module(), self.module) - - -SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100 - -class Test_Pep523API(unittest.TestCase): - - def do_test(self, func): - calls = [] - start = SUFFICIENT_TO_DEOPT_AND_SPECIALIZE - count = start + SUFFICIENT_TO_DEOPT_AND_SPECIALIZE - for i in range(count): - if i == start: - _testinternalcapi.set_eval_frame_record(calls) - func() - _testinternalcapi.set_eval_frame_default() - self.assertEqual(len(calls), SUFFICIENT_TO_DEOPT_AND_SPECIALIZE) - for name in calls: - self.assertEqual(name, func.__name__) - - def test_pep523_with_specialization_simple(self): - def func1(): - pass - self.do_test(func1) - - def test_pep523_with_specialization_with_default(self): - def func2(x=None): - pass - self.do_test(func2) - - -class TestDictWatchers(unittest.TestCase): - # types of watchers testcapimodule can add: - EVENTS = 0 # appends dict events as strings to global event list - ERROR = 1 # unconditionally sets and signals a RuntimeException - SECOND = 2 # always appends "second" to global event list - - def add_watcher(self, kind=EVENTS): - return _testcapi.add_dict_watcher(kind) - - def clear_watcher(self, watcher_id): - _testcapi.clear_dict_watcher(watcher_id) - - @contextmanager - def watcher(self, kind=EVENTS): - wid = self.add_watcher(kind) - try: - yield wid - finally: - self.clear_watcher(wid) - - def assert_events(self, expected): - actual = _testcapi.get_dict_watcher_events() - self.assertEqual(actual, expected) - - def watch(self, wid, d): - _testcapi.watch_dict(wid, d) - - def unwatch(self, wid, d): - _testcapi.unwatch_dict(wid, d) - - def test_set_new_item(self): - d = {} - with self.watcher() as wid: - self.watch(wid, d) - d["foo"] = "bar" - self.assert_events(["new:foo:bar"]) - - def test_set_existing_item(self): - d = {"foo": "bar"} - with self.watcher() as wid: - self.watch(wid, d) - d["foo"] = "baz" - self.assert_events(["mod:foo:baz"]) - - def test_clone(self): - d = {} - d2 = {"foo": "bar"} - with self.watcher() as wid: - self.watch(wid, d) - d.update(d2) - self.assert_events(["clone"]) - - def test_no_event_if_not_watched(self): - d = {} - with self.watcher() as wid: - d["foo"] = "bar" - self.assert_events([]) - - def test_del(self): - d = {"foo": "bar"} - with self.watcher() as wid: - self.watch(wid, d) - del d["foo"] - self.assert_events(["del:foo"]) - - def test_pop(self): - d = {"foo": "bar"} - with self.watcher() as wid: - self.watch(wid, d) - d.pop("foo") - self.assert_events(["del:foo"]) - - def test_clear(self): - d = {"foo": "bar"} - with self.watcher() as wid: - self.watch(wid, d) - d.clear() - self.assert_events(["clear"]) - - def test_dealloc(self): - d = {"foo": "bar"} - with self.watcher() as wid: - self.watch(wid, d) - del d - self.assert_events(["dealloc"]) - - def test_unwatch(self): - d = {} - with self.watcher() as wid: - self.watch(wid, d) - d["foo"] = "bar" - self.unwatch(wid, d) - d["hmm"] = "baz" - self.assert_events(["new:foo:bar"]) - - def test_error(self): - d = {} - with self.watcher(kind=self.ERROR) as wid: - self.watch(wid, d) - with catch_unraisable_exception() as cm: - d["foo"] = "bar" - self.assertIs(cm.unraisable.object, d) - self.assertEqual(str(cm.unraisable.exc_value), "boom!") - self.assert_events([]) - - def test_two_watchers(self): - d1 = {} - d2 = {} - with self.watcher() as wid1: - with self.watcher(kind=self.SECOND) as wid2: - self.watch(wid1, d1) - self.watch(wid2, d2) - d1["foo"] = "bar" - d2["hmm"] = "baz" - self.assert_events(["new:foo:bar", "second"]) - - def test_watch_non_dict(self): - with self.watcher() as wid: - with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"): - self.watch(wid, 1) - - def test_watch_out_of_range_watcher_id(self): - d = {} - with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"): - self.watch(-1, d) - with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"): - self.watch(8, d) # DICT_MAX_WATCHERS = 8 - - def test_watch_unassigned_watcher_id(self): - d = {} - with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"): - self.watch(1, d) - - def test_unwatch_non_dict(self): - with self.watcher() as wid: - with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"): - self.unwatch(wid, 1) - - def test_unwatch_out_of_range_watcher_id(self): - d = {} - with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"): - self.unwatch(-1, d) - with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"): - self.unwatch(8, d) # DICT_MAX_WATCHERS = 8 - - def test_unwatch_unassigned_watcher_id(self): - d = {} - with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"): - self.unwatch(1, d) - - def test_clear_out_of_range_watcher_id(self): - with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"): - self.clear_watcher(-1) - with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"): - self.clear_watcher(8) # DICT_MAX_WATCHERS = 8 - - def test_clear_unassigned_watcher_id(self): - with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"): - self.clear_watcher(1) - - -class TestTypeWatchers(unittest.TestCase): - # types of watchers testcapimodule can add: - TYPES = 0 # appends modified types to global event list - ERROR = 1 # unconditionally sets and signals a RuntimeException - WRAP = 2 # appends modified type wrapped in list to global event list - - # duplicating the C constant - TYPE_MAX_WATCHERS = 8 - - def add_watcher(self, kind=TYPES): - return _testcapi.add_type_watcher(kind) - - def clear_watcher(self, watcher_id): - _testcapi.clear_type_watcher(watcher_id) - - @contextmanager - def watcher(self, kind=TYPES): - wid = self.add_watcher(kind) - try: - yield wid - finally: - self.clear_watcher(wid) - - def assert_events(self, expected): - actual = _testcapi.get_type_modified_events() - self.assertEqual(actual, expected) - - def watch(self, wid, t): - _testcapi.watch_type(wid, t) - - def unwatch(self, wid, t): - _testcapi.unwatch_type(wid, t) - - def test_watch_type(self): - class C: pass - with self.watcher() as wid: - self.watch(wid, C) - C.foo = "bar" - self.assert_events([C]) - - def test_event_aggregation(self): - class C: pass - with self.watcher() as wid: - self.watch(wid, C) - C.foo = "bar" - C.bar = "baz" - # only one event registered for both modifications - self.assert_events([C]) - - def test_lookup_resets_aggregation(self): - class C: pass - with self.watcher() as wid: - self.watch(wid, C) - C.foo = "bar" - # lookup resets type version tag - self.assertEqual(C.foo, "bar") - C.bar = "baz" - # both events registered - self.assert_events([C, C]) - - def test_unwatch_type(self): - class C: pass - with self.watcher() as wid: - self.watch(wid, C) - C.foo = "bar" - self.assertEqual(C.foo, "bar") - self.assert_events([C]) - self.unwatch(wid, C) - C.bar = "baz" - self.assert_events([C]) - - def test_clear_watcher(self): - class C: pass - # outer watcher is unused, it's just to keep events list alive - with self.watcher() as _: - with self.watcher() as wid: - self.watch(wid, C) - C.foo = "bar" - self.assertEqual(C.foo, "bar") - self.assert_events([C]) - C.bar = "baz" - # Watcher on C has been cleared, no new event - self.assert_events([C]) - - def test_watch_type_subclass(self): - class C: pass - class D(C): pass - with self.watcher() as wid: - self.watch(wid, D) - C.foo = "bar" - self.assert_events([D]) - - def test_error(self): - class C: pass - with self.watcher(kind=self.ERROR) as wid: - self.watch(wid, C) - with catch_unraisable_exception() as cm: - C.foo = "bar" - self.assertIs(cm.unraisable.object, C) - self.assertEqual(str(cm.unraisable.exc_value), "boom!") - self.assert_events([]) - - def test_two_watchers(self): - class C1: pass - class C2: pass - with self.watcher() as wid1: - with self.watcher(kind=self.WRAP) as wid2: - self.assertNotEqual(wid1, wid2) - self.watch(wid1, C1) - self.watch(wid2, C2) - C1.foo = "bar" - C2.hmm = "baz" - self.assert_events([C1, [C2]]) - - def test_watch_non_type(self): - with self.watcher() as wid: - with self.assertRaisesRegex(ValueError, r"Cannot watch non-type"): - self.watch(wid, 1) - - def test_watch_out_of_range_watcher_id(self): - class C: pass - with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"): - self.watch(-1, C) - with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"): - self.watch(self.TYPE_MAX_WATCHERS, C) - - def test_watch_unassigned_watcher_id(self): - class C: pass - with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"): - self.watch(1, C) - - def test_unwatch_non_type(self): - with self.watcher() as wid: - with self.assertRaisesRegex(ValueError, r"Cannot watch non-type"): - self.unwatch(wid, 1) - - def test_unwatch_out_of_range_watcher_id(self): - class C: pass - with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"): - self.unwatch(-1, C) - with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"): - self.unwatch(self.TYPE_MAX_WATCHERS, C) - - def test_unwatch_unassigned_watcher_id(self): - class C: pass - with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"): - self.unwatch(1, C) - - def test_clear_out_of_range_watcher_id(self): - with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"): - self.clear_watcher(-1) - with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"): - self.clear_watcher(self.TYPE_MAX_WATCHERS) - - def test_clear_unassigned_watcher_id(self): - with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"): - self.clear_watcher(1) - - def test_no_more_ids_available(self): - contexts = [self.watcher() for i in range(self.TYPE_MAX_WATCHERS)] - with ExitStack() as stack: - for ctx in contexts: - stack.enter_context(ctx) - with self.assertRaisesRegex(RuntimeError, r"no more type watcher IDs"): - self.add_watcher() - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_capi/__init__.py b/Lib/test/test_capi/__init__.py new file mode 100644 index 0000000..4b16ecc --- /dev/null +++ b/Lib/test/test_capi/__init__.py @@ -0,0 +1,5 @@ +import os +from test.support import load_package_tests + +def load_tests(*args): + return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_capi/__main__.py b/Lib/test/test_capi/__main__.py new file mode 100644 index 0000000..05d0177 --- /dev/null +++ b/Lib/test/test_capi/__main__.py @@ -0,0 +1,3 @@ +import unittest + +unittest.main('test.test_capi') diff --git a/Lib/test/test_capi/test_getargs.py b/Lib/test/test_capi/test_getargs.py new file mode 100644 index 0000000..1d5c7fb --- /dev/null +++ b/Lib/test/test_capi/test_getargs.py @@ -0,0 +1,1297 @@ +import unittest +import math +import string +import sys +from test import support +from test.support import import_helper +from test.support import warnings_helper +# Skip this test if the _testcapi module isn't available. +_testcapi = import_helper.import_module('_testcapi') +from _testcapi import getargs_keywords, getargs_keyword_only + +# > How about the following counterproposal. This also changes some of +# > the other format codes to be a little more regular. +# > +# > Code C type Range check +# > +# > b unsigned char 0..UCHAR_MAX +# > h signed short SHRT_MIN..SHRT_MAX +# > B unsigned char none ** +# > H unsigned short none ** +# > k * unsigned long none +# > I * unsigned int 0..UINT_MAX +# +# +# > i int INT_MIN..INT_MAX +# > l long LONG_MIN..LONG_MAX +# +# > K * unsigned long long none +# > L long long LLONG_MIN..LLONG_MAX +# +# > Notes: +# > +# > * New format codes. +# > +# > ** Changed from previous "range-and-a-half" to "none"; the +# > range-and-a-half checking wasn't particularly useful. +# +# Plus a C API or two, e.g. PyLong_AsUnsignedLongMask() -> +# unsigned long and PyLong_AsUnsignedLongLongMask() -> unsigned +# long long (if that exists). + +LARGE = 0x7FFFFFFF +VERY_LARGE = 0xFF0000121212121212121242 + +from _testcapi import UCHAR_MAX, USHRT_MAX, UINT_MAX, ULONG_MAX, INT_MAX, \ + INT_MIN, LONG_MIN, LONG_MAX, PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, \ + SHRT_MIN, SHRT_MAX, FLT_MIN, FLT_MAX, DBL_MIN, DBL_MAX + +DBL_MAX_EXP = sys.float_info.max_exp +INF = float('inf') +NAN = float('nan') + +# fake, they are not defined in Python's header files +LLONG_MAX = 2**63-1 +LLONG_MIN = -2**63 +ULLONG_MAX = 2**64-1 + +class Index: + def __index__(self): + return 99 + +class IndexIntSubclass(int): + def __index__(self): + return 99 + +class BadIndex: + def __index__(self): + return 1.0 + +class BadIndex2: + def __index__(self): + return True + +class BadIndex3(int): + def __index__(self): + return True + + +class Int: + def __int__(self): + return 99 + +class IntSubclass(int): + def __int__(self): + return 99 + +class BadInt: + def __int__(self): + return 1.0 + +class BadInt2: + def __int__(self): + return True + +class BadInt3(int): + def __int__(self): + return True + + +class Float: + def __float__(self): + return 4.25 + +class FloatSubclass(float): + pass + +class FloatSubclass2(float): + def __float__(self): + return 4.25 + +class BadFloat: + def __float__(self): + return 687 + +class BadFloat2: + def __float__(self): + return FloatSubclass(4.25) + +class BadFloat3(float): + def __float__(self): + return FloatSubclass(4.25) + + +class Complex: + def __complex__(self): + return 4.25+0.5j + +class ComplexSubclass(complex): + pass + +class ComplexSubclass2(complex): + def __complex__(self): + return 4.25+0.5j + +class BadComplex: + def __complex__(self): + return 1.25 + +class BadComplex2: + def __complex__(self): + return ComplexSubclass(4.25+0.5j) + +class BadComplex3(complex): + def __complex__(self): + return ComplexSubclass(4.25+0.5j) + + +class TupleSubclass(tuple): + pass + +class DictSubclass(dict): + pass + + +class Unsigned_TestCase(unittest.TestCase): + def test_b(self): + from _testcapi import getargs_b + # b returns 'unsigned char', and does range checking (0 ... UCHAR_MAX) + self.assertRaises(TypeError, getargs_b, 3.14) + self.assertEqual(99, getargs_b(Index())) + self.assertEqual(0, getargs_b(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_b, BadIndex()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_b(BadIndex2())) + self.assertEqual(0, getargs_b(BadIndex3())) + self.assertRaises(TypeError, getargs_b, Int()) + self.assertEqual(0, getargs_b(IntSubclass())) + self.assertRaises(TypeError, getargs_b, BadInt()) + self.assertRaises(TypeError, getargs_b, BadInt2()) + self.assertEqual(0, getargs_b(BadInt3())) + + self.assertRaises(OverflowError, getargs_b, -1) + self.assertEqual(0, getargs_b(0)) + self.assertEqual(UCHAR_MAX, getargs_b(UCHAR_MAX)) + self.assertRaises(OverflowError, getargs_b, UCHAR_MAX + 1) + + self.assertEqual(42, getargs_b(42)) + self.assertRaises(OverflowError, getargs_b, VERY_LARGE) + + def test_B(self): + from _testcapi import getargs_B + # B returns 'unsigned char', no range checking + self.assertRaises(TypeError, getargs_B, 3.14) + self.assertEqual(99, getargs_B(Index())) + self.assertEqual(0, getargs_B(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_B, BadIndex()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_B(BadIndex2())) + self.assertEqual(0, getargs_B(BadIndex3())) + self.assertRaises(TypeError, getargs_B, Int()) + self.assertEqual(0, getargs_B(IntSubclass())) + self.assertRaises(TypeError, getargs_B, BadInt()) + self.assertRaises(TypeError, getargs_B, BadInt2()) + self.assertEqual(0, getargs_B(BadInt3())) + + self.assertEqual(UCHAR_MAX, getargs_B(-1)) + self.assertEqual(0, getargs_B(0)) + self.assertEqual(UCHAR_MAX, getargs_B(UCHAR_MAX)) + self.assertEqual(0, getargs_B(UCHAR_MAX+1)) + + self.assertEqual(42, getargs_B(42)) + self.assertEqual(UCHAR_MAX & VERY_LARGE, getargs_B(VERY_LARGE)) + + def test_H(self): + from _testcapi import getargs_H + # H returns 'unsigned short', no range checking + self.assertRaises(TypeError, getargs_H, 3.14) + self.assertEqual(99, getargs_H(Index())) + self.assertEqual(0, getargs_H(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_H, BadIndex()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_H(BadIndex2())) + self.assertEqual(0, getargs_H(BadIndex3())) + self.assertRaises(TypeError, getargs_H, Int()) + self.assertEqual(0, getargs_H(IntSubclass())) + self.assertRaises(TypeError, getargs_H, BadInt()) + self.assertRaises(TypeError, getargs_H, BadInt2()) + self.assertEqual(0, getargs_H(BadInt3())) + + self.assertEqual(USHRT_MAX, getargs_H(-1)) + self.assertEqual(0, getargs_H(0)) + self.assertEqual(USHRT_MAX, getargs_H(USHRT_MAX)) + self.assertEqual(0, getargs_H(USHRT_MAX+1)) + + self.assertEqual(42, getargs_H(42)) + + self.assertEqual(VERY_LARGE & USHRT_MAX, getargs_H(VERY_LARGE)) + + def test_I(self): + from _testcapi import getargs_I + # I returns 'unsigned int', no range checking + self.assertRaises(TypeError, getargs_I, 3.14) + self.assertEqual(99, getargs_I(Index())) + self.assertEqual(0, getargs_I(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_I, BadIndex()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_I(BadIndex2())) + self.assertEqual(0, getargs_I(BadIndex3())) + self.assertRaises(TypeError, getargs_I, Int()) + self.assertEqual(0, getargs_I(IntSubclass())) + self.assertRaises(TypeError, getargs_I, BadInt()) + self.assertRaises(TypeError, getargs_I, BadInt2()) + self.assertEqual(0, getargs_I(BadInt3())) + + self.assertEqual(UINT_MAX, getargs_I(-1)) + self.assertEqual(0, getargs_I(0)) + self.assertEqual(UINT_MAX, getargs_I(UINT_MAX)) + self.assertEqual(0, getargs_I(UINT_MAX+1)) + + self.assertEqual(42, getargs_I(42)) + + self.assertEqual(VERY_LARGE & UINT_MAX, getargs_I(VERY_LARGE)) + + def test_k(self): + from _testcapi import getargs_k + # k returns 'unsigned long', no range checking + # it does not accept float, or instances with __int__ + self.assertRaises(TypeError, getargs_k, 3.14) + self.assertRaises(TypeError, getargs_k, Index()) + self.assertEqual(0, getargs_k(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_k, BadIndex()) + self.assertRaises(TypeError, getargs_k, BadIndex2()) + self.assertEqual(0, getargs_k(BadIndex3())) + self.assertRaises(TypeError, getargs_k, Int()) + self.assertEqual(0, getargs_k(IntSubclass())) + self.assertRaises(TypeError, getargs_k, BadInt()) + self.assertRaises(TypeError, getargs_k, BadInt2()) + self.assertEqual(0, getargs_k(BadInt3())) + + self.assertEqual(ULONG_MAX, getargs_k(-1)) + self.assertEqual(0, getargs_k(0)) + self.assertEqual(ULONG_MAX, getargs_k(ULONG_MAX)) + self.assertEqual(0, getargs_k(ULONG_MAX+1)) + + self.assertEqual(42, getargs_k(42)) + + self.assertEqual(VERY_LARGE & ULONG_MAX, getargs_k(VERY_LARGE)) + +class Signed_TestCase(unittest.TestCase): + def test_h(self): + from _testcapi import getargs_h + # h returns 'short', and does range checking (SHRT_MIN ... SHRT_MAX) + self.assertRaises(TypeError, getargs_h, 3.14) + self.assertEqual(99, getargs_h(Index())) + self.assertEqual(0, getargs_h(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_h, BadIndex()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_h(BadIndex2())) + self.assertEqual(0, getargs_h(BadIndex3())) + self.assertRaises(TypeError, getargs_h, Int()) + self.assertEqual(0, getargs_h(IntSubclass())) + self.assertRaises(TypeError, getargs_h, BadInt()) + self.assertRaises(TypeError, getargs_h, BadInt2()) + self.assertEqual(0, getargs_h(BadInt3())) + + self.assertRaises(OverflowError, getargs_h, SHRT_MIN-1) + self.assertEqual(SHRT_MIN, getargs_h(SHRT_MIN)) + self.assertEqual(SHRT_MAX, getargs_h(SHRT_MAX)) + self.assertRaises(OverflowError, getargs_h, SHRT_MAX+1) + + self.assertEqual(42, getargs_h(42)) + self.assertRaises(OverflowError, getargs_h, VERY_LARGE) + + def test_i(self): + from _testcapi import getargs_i + # i returns 'int', and does range checking (INT_MIN ... INT_MAX) + self.assertRaises(TypeError, getargs_i, 3.14) + self.assertEqual(99, getargs_i(Index())) + self.assertEqual(0, getargs_i(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_i, BadIndex()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_i(BadIndex2())) + self.assertEqual(0, getargs_i(BadIndex3())) + self.assertRaises(TypeError, getargs_i, Int()) + self.assertEqual(0, getargs_i(IntSubclass())) + self.assertRaises(TypeError, getargs_i, BadInt()) + self.assertRaises(TypeError, getargs_i, BadInt2()) + self.assertEqual(0, getargs_i(BadInt3())) + + self.assertRaises(OverflowError, getargs_i, INT_MIN-1) + self.assertEqual(INT_MIN, getargs_i(INT_MIN)) + self.assertEqual(INT_MAX, getargs_i(INT_MAX)) + self.assertRaises(OverflowError, getargs_i, INT_MAX+1) + + self.assertEqual(42, getargs_i(42)) + self.assertRaises(OverflowError, getargs_i, VERY_LARGE) + + def test_l(self): + from _testcapi import getargs_l + # l returns 'long', and does range checking (LONG_MIN ... LONG_MAX) + self.assertRaises(TypeError, getargs_l, 3.14) + self.assertEqual(99, getargs_l(Index())) + self.assertEqual(0, getargs_l(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_l, BadIndex()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_l(BadIndex2())) + self.assertEqual(0, getargs_l(BadIndex3())) + self.assertRaises(TypeError, getargs_l, Int()) + self.assertEqual(0, getargs_l(IntSubclass())) + self.assertRaises(TypeError, getargs_l, BadInt()) + self.assertRaises(TypeError, getargs_l, BadInt2()) + self.assertEqual(0, getargs_l(BadInt3())) + + self.assertRaises(OverflowError, getargs_l, LONG_MIN-1) + self.assertEqual(LONG_MIN, getargs_l(LONG_MIN)) + self.assertEqual(LONG_MAX, getargs_l(LONG_MAX)) + self.assertRaises(OverflowError, getargs_l, LONG_MAX+1) + + self.assertEqual(42, getargs_l(42)) + self.assertRaises(OverflowError, getargs_l, VERY_LARGE) + + def test_n(self): + from _testcapi import getargs_n + # n returns 'Py_ssize_t', and does range checking + # (PY_SSIZE_T_MIN ... PY_SSIZE_T_MAX) + self.assertRaises(TypeError, getargs_n, 3.14) + self.assertEqual(99, getargs_n(Index())) + self.assertEqual(0, getargs_n(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_n, BadIndex()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_n(BadIndex2())) + self.assertEqual(0, getargs_n(BadIndex3())) + self.assertRaises(TypeError, getargs_n, Int()) + self.assertEqual(0, getargs_n(IntSubclass())) + self.assertRaises(TypeError, getargs_n, BadInt()) + self.assertRaises(TypeError, getargs_n, BadInt2()) + self.assertEqual(0, getargs_n(BadInt3())) + + self.assertRaises(OverflowError, getargs_n, PY_SSIZE_T_MIN-1) + self.assertEqual(PY_SSIZE_T_MIN, getargs_n(PY_SSIZE_T_MIN)) + self.assertEqual(PY_SSIZE_T_MAX, getargs_n(PY_SSIZE_T_MAX)) + self.assertRaises(OverflowError, getargs_n, PY_SSIZE_T_MAX+1) + + self.assertEqual(42, getargs_n(42)) + self.assertRaises(OverflowError, getargs_n, VERY_LARGE) + + +class LongLong_TestCase(unittest.TestCase): + def test_L(self): + from _testcapi import getargs_L + # L returns 'long long', and does range checking (LLONG_MIN + # ... LLONG_MAX) + self.assertRaises(TypeError, getargs_L, 3.14) + self.assertRaises(TypeError, getargs_L, "Hello") + self.assertEqual(99, getargs_L(Index())) + self.assertEqual(0, getargs_L(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_L, BadIndex()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(1, getargs_L(BadIndex2())) + self.assertEqual(0, getargs_L(BadIndex3())) + self.assertRaises(TypeError, getargs_L, Int()) + self.assertEqual(0, getargs_L(IntSubclass())) + self.assertRaises(TypeError, getargs_L, BadInt()) + self.assertRaises(TypeError, getargs_L, BadInt2()) + self.assertEqual(0, getargs_L(BadInt3())) + + self.assertRaises(OverflowError, getargs_L, LLONG_MIN-1) + self.assertEqual(LLONG_MIN, getargs_L(LLONG_MIN)) + self.assertEqual(LLONG_MAX, getargs_L(LLONG_MAX)) + self.assertRaises(OverflowError, getargs_L, LLONG_MAX+1) + + self.assertEqual(42, getargs_L(42)) + self.assertRaises(OverflowError, getargs_L, VERY_LARGE) + + def test_K(self): + from _testcapi import getargs_K + # K return 'unsigned long long', no range checking + self.assertRaises(TypeError, getargs_K, 3.14) + self.assertRaises(TypeError, getargs_K, Index()) + self.assertEqual(0, getargs_K(IndexIntSubclass())) + self.assertRaises(TypeError, getargs_K, BadIndex()) + self.assertRaises(TypeError, getargs_K, BadIndex2()) + self.assertEqual(0, getargs_K(BadIndex3())) + self.assertRaises(TypeError, getargs_K, Int()) + self.assertEqual(0, getargs_K(IntSubclass())) + self.assertRaises(TypeError, getargs_K, BadInt()) + self.assertRaises(TypeError, getargs_K, BadInt2()) + self.assertEqual(0, getargs_K(BadInt3())) + + self.assertEqual(ULLONG_MAX, getargs_K(ULLONG_MAX)) + self.assertEqual(0, getargs_K(0)) + self.assertEqual(0, getargs_K(ULLONG_MAX+1)) + + self.assertEqual(42, getargs_K(42)) + + self.assertEqual(VERY_LARGE & ULLONG_MAX, getargs_K(VERY_LARGE)) + + +class Float_TestCase(unittest.TestCase): + def assertEqualWithSign(self, actual, expected): + self.assertEqual(actual, expected) + self.assertEqual(math.copysign(1, actual), math.copysign(1, expected)) + + def test_f(self): + from _testcapi import getargs_f + self.assertEqual(getargs_f(4.25), 4.25) + self.assertEqual(getargs_f(4), 4.0) + self.assertRaises(TypeError, getargs_f, 4.25+0j) + self.assertEqual(getargs_f(Float()), 4.25) + self.assertEqual(getargs_f(FloatSubclass(7.5)), 7.5) + self.assertEqual(getargs_f(FloatSubclass2(7.5)), 7.5) + self.assertRaises(TypeError, getargs_f, BadFloat()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_f(BadFloat2()), 4.25) + self.assertEqual(getargs_f(BadFloat3(7.5)), 7.5) + self.assertEqual(getargs_f(Index()), 99.0) + self.assertRaises(TypeError, getargs_f, Int()) + + for x in (FLT_MIN, -FLT_MIN, FLT_MAX, -FLT_MAX, INF, -INF): + self.assertEqual(getargs_f(x), x) + if FLT_MAX < DBL_MAX: + self.assertEqual(getargs_f(DBL_MAX), INF) + self.assertEqual(getargs_f(-DBL_MAX), -INF) + if FLT_MIN > DBL_MIN: + self.assertEqualWithSign(getargs_f(DBL_MIN), 0.0) + self.assertEqualWithSign(getargs_f(-DBL_MIN), -0.0) + self.assertEqualWithSign(getargs_f(0.0), 0.0) + self.assertEqualWithSign(getargs_f(-0.0), -0.0) + r = getargs_f(NAN) + self.assertNotEqual(r, r) + + @support.requires_IEEE_754 + def test_f_rounding(self): + from _testcapi import getargs_f + self.assertEqual(getargs_f(3.40282356e38), FLT_MAX) + self.assertEqual(getargs_f(-3.40282356e38), -FLT_MAX) + + def test_d(self): + from _testcapi import getargs_d + self.assertEqual(getargs_d(4.25), 4.25) + self.assertEqual(getargs_d(4), 4.0) + self.assertRaises(TypeError, getargs_d, 4.25+0j) + self.assertEqual(getargs_d(Float()), 4.25) + self.assertEqual(getargs_d(FloatSubclass(7.5)), 7.5) + self.assertEqual(getargs_d(FloatSubclass2(7.5)), 7.5) + self.assertRaises(TypeError, getargs_d, BadFloat()) + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_d(BadFloat2()), 4.25) + self.assertEqual(getargs_d(BadFloat3(7.5)), 7.5) + self.assertEqual(getargs_d(Index()), 99.0) + self.assertRaises(TypeError, getargs_d, Int()) + + for x in (DBL_MIN, -DBL_MIN, DBL_MAX, -DBL_MAX, INF, -INF): + self.assertEqual(getargs_d(x), x) + self.assertRaises(OverflowError, getargs_d, 1< 1 + self.assertEqual(getargs_c(b'a'), 97) + self.assertEqual(getargs_c(bytearray(b'a')), 97) + self.assertRaises(TypeError, getargs_c, memoryview(b'a')) + self.assertRaises(TypeError, getargs_c, 's') + self.assertRaises(TypeError, getargs_c, 97) + self.assertRaises(TypeError, getargs_c, None) + + def test_y(self): + from _testcapi import getargs_y + self.assertRaises(TypeError, getargs_y, 'abc\xe9') + self.assertEqual(getargs_y(b'bytes'), b'bytes') + self.assertRaises(ValueError, getargs_y, b'nul:\0') + self.assertRaises(TypeError, getargs_y, bytearray(b'bytearray')) + self.assertRaises(TypeError, getargs_y, memoryview(b'memoryview')) + self.assertRaises(TypeError, getargs_y, None) + + def test_y_star(self): + from _testcapi import getargs_y_star + self.assertRaises(TypeError, getargs_y_star, 'abc\xe9') + self.assertEqual(getargs_y_star(b'bytes'), b'bytes') + self.assertEqual(getargs_y_star(b'nul:\0'), b'nul:\0') + self.assertEqual(getargs_y_star(bytearray(b'bytearray')), b'bytearray') + self.assertEqual(getargs_y_star(memoryview(b'memoryview')), b'memoryview') + self.assertRaises(TypeError, getargs_y_star, None) + + def test_y_hash(self): + from _testcapi import getargs_y_hash + self.assertRaises(TypeError, getargs_y_hash, 'abc\xe9') + self.assertEqual(getargs_y_hash(b'bytes'), b'bytes') + self.assertEqual(getargs_y_hash(b'nul:\0'), b'nul:\0') + self.assertRaises(TypeError, getargs_y_hash, bytearray(b'bytearray')) + self.assertRaises(TypeError, getargs_y_hash, memoryview(b'memoryview')) + self.assertRaises(TypeError, getargs_y_hash, None) + + def test_w_star(self): + # getargs_w_star() modifies first and last byte + from _testcapi import getargs_w_star + self.assertRaises(TypeError, getargs_w_star, 'abc\xe9') + self.assertRaises(TypeError, getargs_w_star, b'bytes') + self.assertRaises(TypeError, getargs_w_star, b'nul:\0') + self.assertRaises(TypeError, getargs_w_star, memoryview(b'bytes')) + buf = bytearray(b'bytearray') + self.assertEqual(getargs_w_star(buf), b'[ytearra]') + self.assertEqual(buf, bytearray(b'[ytearra]')) + buf = bytearray(b'memoryview') + self.assertEqual(getargs_w_star(memoryview(buf)), b'[emoryvie]') + self.assertEqual(buf, bytearray(b'[emoryvie]')) + self.assertRaises(TypeError, getargs_w_star, None) + + +class String_TestCase(unittest.TestCase): + def test_C(self): + from _testcapi import getargs_C + self.assertRaises(TypeError, getargs_C, 'abc') # len > 1 + self.assertEqual(getargs_C('a'), 97) + self.assertEqual(getargs_C('\u20ac'), 0x20ac) + self.assertEqual(getargs_C('\U0001f40d'), 0x1f40d) + self.assertRaises(TypeError, getargs_C, b'a') + self.assertRaises(TypeError, getargs_C, bytearray(b'a')) + self.assertRaises(TypeError, getargs_C, memoryview(b'a')) + self.assertRaises(TypeError, getargs_C, 97) + self.assertRaises(TypeError, getargs_C, None) + + def test_s(self): + from _testcapi import getargs_s + self.assertEqual(getargs_s('abc\xe9'), b'abc\xc3\xa9') + self.assertRaises(ValueError, getargs_s, 'nul:\0') + self.assertRaises(TypeError, getargs_s, b'bytes') + self.assertRaises(TypeError, getargs_s, bytearray(b'bytearray')) + self.assertRaises(TypeError, getargs_s, memoryview(b'memoryview')) + self.assertRaises(TypeError, getargs_s, None) + + def test_s_star(self): + from _testcapi import getargs_s_star + self.assertEqual(getargs_s_star('abc\xe9'), b'abc\xc3\xa9') + self.assertEqual(getargs_s_star('nul:\0'), b'nul:\0') + self.assertEqual(getargs_s_star(b'bytes'), b'bytes') + self.assertEqual(getargs_s_star(bytearray(b'bytearray')), b'bytearray') + self.assertEqual(getargs_s_star(memoryview(b'memoryview')), b'memoryview') + self.assertRaises(TypeError, getargs_s_star, None) + + def test_s_hash(self): + from _testcapi import getargs_s_hash + self.assertEqual(getargs_s_hash('abc\xe9'), b'abc\xc3\xa9') + self.assertEqual(getargs_s_hash('nul:\0'), b'nul:\0') + self.assertEqual(getargs_s_hash(b'bytes'), b'bytes') + self.assertRaises(TypeError, getargs_s_hash, bytearray(b'bytearray')) + self.assertRaises(TypeError, getargs_s_hash, memoryview(b'memoryview')) + self.assertRaises(TypeError, getargs_s_hash, None) + + def test_s_hash_int(self): + # "s#" without PY_SSIZE_T_CLEAN defined. + from _testcapi import getargs_s_hash_int + from _testcapi import getargs_s_hash_int2 + buf = bytearray([1, 2]) + self.assertRaises(SystemError, getargs_s_hash_int, buf, "abc") + self.assertRaises(SystemError, getargs_s_hash_int, buf, x=42) + self.assertRaises(SystemError, getargs_s_hash_int, buf, x="abc") + self.assertRaises(SystemError, getargs_s_hash_int2, buf, ("abc",)) + self.assertRaises(SystemError, getargs_s_hash_int2, buf, x=42) + self.assertRaises(SystemError, getargs_s_hash_int2, buf, x="abc") + buf.append(3) # still mutable -- not locked by a buffer export + # getargs_s_hash_int(buf) may not raise SystemError because skipitem() + # is not called. But it is an implementation detail. + # getargs_s_hash_int(buf) + # getargs_s_hash_int2(buf) + + def test_z(self): + from _testcapi import getargs_z + self.assertEqual(getargs_z('abc\xe9'), b'abc\xc3\xa9') + self.assertRaises(ValueError, getargs_z, 'nul:\0') + self.assertRaises(TypeError, getargs_z, b'bytes') + self.assertRaises(TypeError, getargs_z, bytearray(b'bytearray')) + self.assertRaises(TypeError, getargs_z, memoryview(b'memoryview')) + self.assertIsNone(getargs_z(None)) + + def test_z_star(self): + from _testcapi import getargs_z_star + self.assertEqual(getargs_z_star('abc\xe9'), b'abc\xc3\xa9') + self.assertEqual(getargs_z_star('nul:\0'), b'nul:\0') + self.assertEqual(getargs_z_star(b'bytes'), b'bytes') + self.assertEqual(getargs_z_star(bytearray(b'bytearray')), b'bytearray') + self.assertEqual(getargs_z_star(memoryview(b'memoryview')), b'memoryview') + self.assertIsNone(getargs_z_star(None)) + + def test_z_hash(self): + from _testcapi import getargs_z_hash + self.assertEqual(getargs_z_hash('abc\xe9'), b'abc\xc3\xa9') + self.assertEqual(getargs_z_hash('nul:\0'), b'nul:\0') + self.assertEqual(getargs_z_hash(b'bytes'), b'bytes') + self.assertRaises(TypeError, getargs_z_hash, bytearray(b'bytearray')) + self.assertRaises(TypeError, getargs_z_hash, memoryview(b'memoryview')) + self.assertIsNone(getargs_z_hash(None)) + + def test_es(self): + from _testcapi import getargs_es + self.assertEqual(getargs_es('abc\xe9'), b'abc\xc3\xa9') + self.assertEqual(getargs_es('abc\xe9', 'latin1'), b'abc\xe9') + self.assertRaises(UnicodeEncodeError, getargs_es, 'abc\xe9', 'ascii') + self.assertRaises(LookupError, getargs_es, 'abc\xe9', 'spam') + self.assertRaises(TypeError, getargs_es, b'bytes', 'latin1') + self.assertRaises(TypeError, getargs_es, bytearray(b'bytearray'), 'latin1') + self.assertRaises(TypeError, getargs_es, memoryview(b'memoryview'), 'latin1') + self.assertRaises(TypeError, getargs_es, None, 'latin1') + self.assertRaises(TypeError, getargs_es, 'nul:\0', 'latin1') + + def test_et(self): + from _testcapi import getargs_et + self.assertEqual(getargs_et('abc\xe9'), b'abc\xc3\xa9') + self.assertEqual(getargs_et('abc\xe9', 'latin1'), b'abc\xe9') + self.assertRaises(UnicodeEncodeError, getargs_et, 'abc\xe9', 'ascii') + self.assertRaises(LookupError, getargs_et, 'abc\xe9', 'spam') + self.assertEqual(getargs_et(b'bytes', 'latin1'), b'bytes') + self.assertEqual(getargs_et(bytearray(b'bytearray'), 'latin1'), b'bytearray') + self.assertRaises(TypeError, getargs_et, memoryview(b'memoryview'), 'latin1') + self.assertRaises(TypeError, getargs_et, None, 'latin1') + self.assertRaises(TypeError, getargs_et, 'nul:\0', 'latin1') + self.assertRaises(TypeError, getargs_et, b'nul:\0', 'latin1') + self.assertRaises(TypeError, getargs_et, bytearray(b'nul:\0'), 'latin1') + + def test_es_hash(self): + from _testcapi import getargs_es_hash + self.assertEqual(getargs_es_hash('abc\xe9'), b'abc\xc3\xa9') + self.assertEqual(getargs_es_hash('abc\xe9', 'latin1'), b'abc\xe9') + self.assertRaises(UnicodeEncodeError, getargs_es_hash, 'abc\xe9', 'ascii') + self.assertRaises(LookupError, getargs_es_hash, 'abc\xe9', 'spam') + self.assertRaises(TypeError, getargs_es_hash, b'bytes', 'latin1') + self.assertRaises(TypeError, getargs_es_hash, bytearray(b'bytearray'), 'latin1') + self.assertRaises(TypeError, getargs_es_hash, memoryview(b'memoryview'), 'latin1') + self.assertRaises(TypeError, getargs_es_hash, None, 'latin1') + self.assertEqual(getargs_es_hash('nul:\0', 'latin1'), b'nul:\0') + + buf = bytearray(b'x'*8) + self.assertEqual(getargs_es_hash('abc\xe9', 'latin1', buf), b'abc\xe9') + self.assertEqual(buf, bytearray(b'abc\xe9\x00xxx')) + buf = bytearray(b'x'*5) + self.assertEqual(getargs_es_hash('abc\xe9', 'latin1', buf), b'abc\xe9') + self.assertEqual(buf, bytearray(b'abc\xe9\x00')) + buf = bytearray(b'x'*4) + self.assertRaises(ValueError, getargs_es_hash, 'abc\xe9', 'latin1', buf) + self.assertEqual(buf, bytearray(b'x'*4)) + buf = bytearray() + self.assertRaises(ValueError, getargs_es_hash, 'abc\xe9', 'latin1', buf) + + def test_et_hash(self): + from _testcapi import getargs_et_hash + self.assertEqual(getargs_et_hash('abc\xe9'), b'abc\xc3\xa9') + self.assertEqual(getargs_et_hash('abc\xe9', 'latin1'), b'abc\xe9') + self.assertRaises(UnicodeEncodeError, getargs_et_hash, 'abc\xe9', 'ascii') + self.assertRaises(LookupError, getargs_et_hash, 'abc\xe9', 'spam') + self.assertEqual(getargs_et_hash(b'bytes', 'latin1'), b'bytes') + self.assertEqual(getargs_et_hash(bytearray(b'bytearray'), 'latin1'), b'bytearray') + self.assertRaises(TypeError, getargs_et_hash, memoryview(b'memoryview'), 'latin1') + self.assertRaises(TypeError, getargs_et_hash, None, 'latin1') + self.assertEqual(getargs_et_hash('nul:\0', 'latin1'), b'nul:\0') + self.assertEqual(getargs_et_hash(b'nul:\0', 'latin1'), b'nul:\0') + self.assertEqual(getargs_et_hash(bytearray(b'nul:\0'), 'latin1'), b'nul:\0') + + buf = bytearray(b'x'*8) + self.assertEqual(getargs_et_hash('abc\xe9', 'latin1', buf), b'abc\xe9') + self.assertEqual(buf, bytearray(b'abc\xe9\x00xxx')) + buf = bytearray(b'x'*5) + self.assertEqual(getargs_et_hash('abc\xe9', 'latin1', buf), b'abc\xe9') + self.assertEqual(buf, bytearray(b'abc\xe9\x00')) + buf = bytearray(b'x'*4) + self.assertRaises(ValueError, getargs_et_hash, 'abc\xe9', 'latin1', buf) + self.assertEqual(buf, bytearray(b'x'*4)) + buf = bytearray() + self.assertRaises(ValueError, getargs_et_hash, 'abc\xe9', 'latin1', buf) + + @support.requires_legacy_unicode_capi + def test_u(self): + from _testcapi import getargs_u + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_u('abc\xe9'), 'abc\xe9') + with self.assertWarns(DeprecationWarning): + self.assertRaises(ValueError, getargs_u, 'nul:\0') + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_u, b'bytes') + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_u, bytearray(b'bytearray')) + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_u, memoryview(b'memoryview')) + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_u, None) + + @support.requires_legacy_unicode_capi + def test_u_hash(self): + from _testcapi import getargs_u_hash + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_u_hash('abc\xe9'), 'abc\xe9') + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_u_hash('nul:\0'), 'nul:\0') + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_u_hash, b'bytes') + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_u_hash, bytearray(b'bytearray')) + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_u_hash, memoryview(b'memoryview')) + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_u_hash, None) + + @support.requires_legacy_unicode_capi + def test_Z(self): + from _testcapi import getargs_Z + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_Z('abc\xe9'), 'abc\xe9') + with self.assertWarns(DeprecationWarning): + self.assertRaises(ValueError, getargs_Z, 'nul:\0') + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_Z, b'bytes') + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_Z, bytearray(b'bytearray')) + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_Z, memoryview(b'memoryview')) + with self.assertWarns(DeprecationWarning): + self.assertIsNone(getargs_Z(None)) + + @support.requires_legacy_unicode_capi + def test_Z_hash(self): + from _testcapi import getargs_Z_hash + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_Z_hash('abc\xe9'), 'abc\xe9') + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_Z_hash('nul:\0'), 'nul:\0') + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_Z_hash, b'bytes') + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_Z_hash, bytearray(b'bytearray')) + with self.assertWarns(DeprecationWarning): + self.assertRaises(TypeError, getargs_Z_hash, memoryview(b'memoryview')) + with self.assertWarns(DeprecationWarning): + self.assertIsNone(getargs_Z_hash(None)) + + +class Object_TestCase(unittest.TestCase): + def test_S(self): + from _testcapi import getargs_S + obj = b'bytes' + self.assertIs(getargs_S(obj), obj) + self.assertRaises(TypeError, getargs_S, bytearray(b'bytearray')) + self.assertRaises(TypeError, getargs_S, 'str') + self.assertRaises(TypeError, getargs_S, None) + self.assertRaises(TypeError, getargs_S, memoryview(obj)) + + def test_Y(self): + from _testcapi import getargs_Y + obj = bytearray(b'bytearray') + self.assertIs(getargs_Y(obj), obj) + self.assertRaises(TypeError, getargs_Y, b'bytes') + self.assertRaises(TypeError, getargs_Y, 'str') + self.assertRaises(TypeError, getargs_Y, None) + self.assertRaises(TypeError, getargs_Y, memoryview(obj)) + + def test_U(self): + from _testcapi import getargs_U + obj = 'str' + self.assertIs(getargs_U(obj), obj) + self.assertRaises(TypeError, getargs_U, b'bytes') + self.assertRaises(TypeError, getargs_U, bytearray(b'bytearray')) + self.assertRaises(TypeError, getargs_U, None) + + +# Bug #6012 +class Test6012(unittest.TestCase): + def test(self): + self.assertEqual(_testcapi.argparsing("Hello", "World"), 1) + + +class SkipitemTest(unittest.TestCase): + + # u, and Z raises DeprecationWarning + @warnings_helper.ignore_warnings(category=DeprecationWarning) + def test_skipitem(self): + """ + If this test failed, you probably added a new "format unit" + in Python/getargs.c, but neglected to update our poor friend + skipitem() in the same file. (If so, shame on you!) + + With a few exceptions**, this function brute-force tests all + printable ASCII*** characters (32 to 126 inclusive) as format units, + checking to see that PyArg_ParseTupleAndKeywords() return consistent + errors both when the unit is attempted to be used and when it is + skipped. If the format unit doesn't exist, we'll get one of two + specific error messages (one for used, one for skipped); if it does + exist we *won't* get that error--we'll get either no error or some + other error. If we get the specific "does not exist" error for one + test and not for the other, there's a mismatch, and the test fails. + + ** Some format units have special funny semantics and it would + be difficult to accommodate them here. Since these are all + well-established and properly skipped in skipitem() we can + get away with not testing them--this test is really intended + to catch *new* format units. + + *** Python C source files must be ASCII. Therefore it's impossible + to have non-ASCII format units. + + """ + empty_tuple = () + tuple_1 = (0,) + dict_b = {'b':1} + keywords = ["a", "b"] + + for i in range(32, 127): + c = chr(i) + + # skip parentheses, the error reporting is inconsistent about them + # skip 'e', it's always a two-character code + # skip '|' and '$', they don't represent arguments anyway + if c in '()e|$': + continue + + # test the format unit when not skipped + format = c + "i" + try: + _testcapi.parse_tuple_and_keywords(tuple_1, dict_b, + format, keywords) + when_not_skipped = False + except SystemError as e: + s = "argument 1 (impossible)" + when_not_skipped = (str(e) == s) + except TypeError: + when_not_skipped = False + + # test the format unit when skipped + optional_format = "|" + format + try: + _testcapi.parse_tuple_and_keywords(empty_tuple, dict_b, + optional_format, keywords) + when_skipped = False + except SystemError as e: + s = "impossible: '{}'".format(format) + when_skipped = (str(e) == s) + + message = ("test_skipitem_parity: " + "detected mismatch between convertsimple and skipitem " + "for format unit '{}' ({}), not skipped {}, skipped {}".format( + c, i, when_skipped, when_not_skipped)) + self.assertIs(when_skipped, when_not_skipped, message) + + def test_skipitem_with_suffix(self): + parse = _testcapi.parse_tuple_and_keywords + empty_tuple = () + tuple_1 = (0,) + dict_b = {'b':1} + keywords = ["a", "b"] + + supported = ('s#', 's*', 'z#', 'z*', 'y#', 'y*', 'w#', 'w*') + for c in string.ascii_letters: + for c2 in '#*': + f = c + c2 + with self.subTest(format=f): + optional_format = "|" + f + "i" + if f in supported: + parse(empty_tuple, dict_b, optional_format, keywords) + else: + with self.assertRaisesRegex(SystemError, + 'impossible'): + parse(empty_tuple, dict_b, optional_format, keywords) + + for c in map(chr, range(32, 128)): + f = 'e' + c + optional_format = "|" + f + "i" + with self.subTest(format=f): + if c in 'st': + parse(empty_tuple, dict_b, optional_format, keywords) + else: + with self.assertRaisesRegex(SystemError, + 'impossible'): + parse(empty_tuple, dict_b, optional_format, keywords) + + +class ParseTupleAndKeywords_Test(unittest.TestCase): + + def test_parse_tuple_and_keywords(self): + # Test handling errors in the parse_tuple_and_keywords helper itself + self.assertRaises(TypeError, _testcapi.parse_tuple_and_keywords, + (), {}, 42, []) + self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, + (), {}, '', 42) + self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, + (), {}, '', [''] * 42) + self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, + (), {}, '', [42]) + + def test_bad_use(self): + # Test handling invalid format and keywords in + # PyArg_ParseTupleAndKeywords() + self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, + (1,), {}, '||O', ['a']) + self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, + (1, 2), {}, '|O|O', ['a', 'b']) + self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, + (), {'a': 1}, '$$O', ['a']) + self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, + (), {'a': 1, 'b': 2}, '$O$O', ['a', 'b']) + self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, + (), {'a': 1}, '$|O', ['a']) + self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, + (), {'a': 1, 'b': 2}, '$O|O', ['a', 'b']) + self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, + (1,), {}, '|O', ['a', 'b']) + self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, + (1,), {}, '|OO', ['a']) + self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, + (), {}, '|$O', ['']) + self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, + (), {}, '|OO', ['a', '']) + + def test_positional_only(self): + parse = _testcapi.parse_tuple_and_keywords + + parse((1, 2, 3), {}, 'OOO', ['', '', 'a']) + parse((1, 2), {'a': 3}, 'OOO', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + r'function takes at least 2 positional arguments \(1 given\)'): + parse((1,), {'a': 3}, 'OOO', ['', '', 'a']) + parse((1,), {}, 'O|OO', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + r'function takes at least 1 positional argument \(0 given\)'): + parse((), {}, 'O|OO', ['', '', 'a']) + parse((1, 2), {'a': 3}, 'OO$O', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + r'function takes exactly 2 positional arguments \(1 given\)'): + parse((1,), {'a': 3}, 'OO$O', ['', '', 'a']) + parse((1,), {}, 'O|O$O', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + r'function takes at least 1 positional argument \(0 given\)'): + parse((), {}, 'O|O$O', ['', '', 'a']) + with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'): + parse((1,), {}, 'O|$OO', ['', '', 'a']) + with self.assertRaisesRegex(SystemError, 'Empty keyword'): + parse((1,), {}, 'O|OO', ['', 'a', '']) + + +class Test_testcapi(unittest.TestCase): + locals().update((name, getattr(_testcapi, name)) + for name in dir(_testcapi) + if name.startswith('test_') and name.endswith('_code')) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py new file mode 100644 index 0000000..ea4c9de --- /dev/null +++ b/Lib/test/test_capi/test_misc.py @@ -0,0 +1,2037 @@ +# Run the _testcapi module tests (tests for the Python/C API): by defn, +# these are all functions _testcapi exports whose name begins with 'test_'. + +from collections import OrderedDict +from contextlib import contextmanager, ExitStack +import _thread +import importlib.machinery +import importlib.util +import os +import pickle +import random +import re +import subprocess +import sys +import textwrap +import threading +import time +import unittest +import warnings +import weakref +from test import support +from test.support import MISSING_C_DOCSTRINGS +from test.support import catch_unraisable_exception +from test.support import import_helper +from test.support import threading_helper +from test.support import warnings_helper +from test.support.script_helper import assert_python_failure, assert_python_ok +try: + import _posixsubprocess +except ImportError: + _posixsubprocess = None +try: + import _testmultiphase +except ImportError: + _testmultiphase = None + +# Skip this test if the _testcapi module isn't available. +_testcapi = import_helper.import_module('_testcapi') + +import _testinternalcapi + + +def decode_stderr(err): + return err.decode('utf-8', 'replace').replace('\r', '') + + +def testfunction(self): + """some doc""" + return self + + +class InstanceMethod: + id = _testcapi.instancemethod(id) + testfunction = _testcapi.instancemethod(testfunction) + +class CAPITest(unittest.TestCase): + + def test_instancemethod(self): + inst = InstanceMethod() + self.assertEqual(id(inst), inst.id()) + self.assertTrue(inst.testfunction() is inst) + self.assertEqual(inst.testfunction.__doc__, testfunction.__doc__) + self.assertEqual(InstanceMethod.testfunction.__doc__, testfunction.__doc__) + + InstanceMethod.testfunction.attribute = "test" + self.assertEqual(testfunction.attribute, "test") + self.assertRaises(AttributeError, setattr, inst.testfunction, "attribute", "test") + + @support.requires_subprocess() + def test_no_FatalError_infinite_loop(self): + with support.SuppressCrashReport(): + p = subprocess.Popen([sys.executable, "-c", + 'import _testcapi;' + '_testcapi.crash_no_current_thread()'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True) + (out, err) = p.communicate() + self.assertEqual(out, '') + # This used to cause an infinite loop. + msg = ("Fatal Python error: PyThreadState_Get: " + "the function must be called with the GIL held, " + "after Python initialization and before Python finalization, " + "but the GIL is released " + "(the current Python thread state is NULL)") + self.assertTrue(err.rstrip().startswith(msg), + err) + + def test_memoryview_from_NULL_pointer(self): + self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer) + + def test_exception(self): + raised_exception = ValueError("5") + new_exc = TypeError("TEST") + try: + raise raised_exception + except ValueError as e: + orig_sys_exception = sys.exception() + orig_exception = _testcapi.set_exception(new_exc) + new_sys_exception = sys.exception() + new_exception = _testcapi.set_exception(orig_exception) + reset_sys_exception = sys.exception() + + self.assertEqual(orig_exception, e) + + self.assertEqual(orig_exception, raised_exception) + self.assertEqual(orig_sys_exception, orig_exception) + self.assertEqual(reset_sys_exception, orig_exception) + self.assertEqual(new_exception, new_exc) + self.assertEqual(new_sys_exception, new_exception) + else: + self.fail("Exception not raised") + + def test_exc_info(self): + raised_exception = ValueError("5") + new_exc = TypeError("TEST") + try: + raise raised_exception + except ValueError as e: + tb = e.__traceback__ + orig_sys_exc_info = sys.exc_info() + orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None) + new_sys_exc_info = sys.exc_info() + new_exc_info = _testcapi.set_exc_info(*orig_exc_info) + reset_sys_exc_info = sys.exc_info() + + self.assertEqual(orig_exc_info[1], e) + + self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb)) + self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info) + self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info) + self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None)) + self.assertSequenceEqual(new_sys_exc_info, new_exc_info) + else: + self.assertTrue(False) + + @unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.') + def test_seq_bytes_to_charp_array(self): + # Issue #15732: crash in _PySequence_BytesToCharpArray() + class Z(object): + def __len__(self): + return 1 + self.assertRaises(TypeError, _posixsubprocess.fork_exec, + 1,Z(),3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) + # Issue #15736: overflow in _PySequence_BytesToCharpArray() + class Z(object): + def __len__(self): + return sys.maxsize + def __getitem__(self, i): + return b'x' + self.assertRaises(MemoryError, _posixsubprocess.fork_exec, + 1,Z(),3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) + + @unittest.skipUnless(_posixsubprocess, '_posixsubprocess required for this test.') + def test_subprocess_fork_exec(self): + class Z(object): + def __len__(self): + return 1 + + # Issue #15738: crash in subprocess_fork_exec() + self.assertRaises(TypeError, _posixsubprocess.fork_exec, + Z(),[b'1'],3,(1, 2),5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23) + + @unittest.skipIf(MISSING_C_DOCSTRINGS, + "Signature information for builtins requires docstrings") + def test_docstring_signature_parsing(self): + + self.assertEqual(_testcapi.no_docstring.__doc__, None) + self.assertEqual(_testcapi.no_docstring.__text_signature__, None) + + self.assertEqual(_testcapi.docstring_empty.__doc__, None) + self.assertEqual(_testcapi.docstring_empty.__text_signature__, None) + + self.assertEqual(_testcapi.docstring_no_signature.__doc__, + "This docstring has no signature.") + self.assertEqual(_testcapi.docstring_no_signature.__text_signature__, None) + + self.assertEqual(_testcapi.docstring_with_invalid_signature.__doc__, + "docstring_with_invalid_signature($module, /, boo)\n" + "\n" + "This docstring has an invalid signature." + ) + self.assertEqual(_testcapi.docstring_with_invalid_signature.__text_signature__, None) + + self.assertEqual(_testcapi.docstring_with_invalid_signature2.__doc__, + "docstring_with_invalid_signature2($module, /, boo)\n" + "\n" + "--\n" + "\n" + "This docstring also has an invalid signature." + ) + self.assertEqual(_testcapi.docstring_with_invalid_signature2.__text_signature__, None) + + self.assertEqual(_testcapi.docstring_with_signature.__doc__, + "This docstring has a valid signature.") + self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "($module, /, sig)") + + self.assertEqual(_testcapi.docstring_with_signature_but_no_doc.__doc__, None) + self.assertEqual(_testcapi.docstring_with_signature_but_no_doc.__text_signature__, + "($module, /, sig)") + + self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__doc__, + "\nThis docstring has a valid signature and some extra newlines.") + self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__, + "($module, /, parameter)") + + def test_c_type_with_matrix_multiplication(self): + M = _testcapi.matmulType + m1 = M() + m2 = M() + self.assertEqual(m1 @ m2, ("matmul", m1, m2)) + self.assertEqual(m1 @ 42, ("matmul", m1, 42)) + self.assertEqual(42 @ m1, ("matmul", 42, m1)) + o = m1 + o @= m2 + self.assertEqual(o, ("imatmul", m1, m2)) + o = m1 + o @= 42 + self.assertEqual(o, ("imatmul", m1, 42)) + o = 42 + o @= m1 + self.assertEqual(o, ("matmul", 42, m1)) + + def test_c_type_with_ipow(self): + # When the __ipow__ method of a type was implemented in C, using the + # modulo param would cause segfaults. + o = _testcapi.ipowType() + self.assertEqual(o.__ipow__(1), (1, None)) + self.assertEqual(o.__ipow__(2, 2), (2, 2)) + + def test_return_null_without_error(self): + # Issue #23571: A function must not return NULL without setting an + # error + if support.Py_DEBUG: + code = textwrap.dedent(""" + import _testcapi + from test import support + + with support.SuppressCrashReport(): + _testcapi.return_null_without_error() + """) + rc, out, err = assert_python_failure('-c', code) + err = decode_stderr(err) + self.assertRegex(err, + r'Fatal Python error: _Py_CheckFunctionResult: ' + r'a function returned NULL without setting an exception\n' + r'Python runtime state: initialized\n' + r'SystemError: ' + r'returned NULL without setting an exception\n' + r'\n' + r'Current thread.*:\n' + r' File .*", line 6 in \n') + else: + with self.assertRaises(SystemError) as cm: + _testcapi.return_null_without_error() + self.assertRegex(str(cm.exception), + 'return_null_without_error.* ' + 'returned NULL without setting an exception') + + def test_return_result_with_error(self): + # Issue #23571: A function must not return a result with an error set + if support.Py_DEBUG: + code = textwrap.dedent(""" + import _testcapi + from test import support + + with support.SuppressCrashReport(): + _testcapi.return_result_with_error() + """) + rc, out, err = assert_python_failure('-c', code) + err = decode_stderr(err) + self.assertRegex(err, + r'Fatal Python error: _Py_CheckFunctionResult: ' + r'a function returned a result with an exception set\n' + r'Python runtime state: initialized\n' + r'ValueError\n' + r'\n' + r'The above exception was the direct cause ' + r'of the following exception:\n' + r'\n' + r'SystemError: ' + r'returned a result with an exception set\n' + r'\n' + r'Current thread.*:\n' + r' File .*, line 6 in \n') + else: + with self.assertRaises(SystemError) as cm: + _testcapi.return_result_with_error() + self.assertRegex(str(cm.exception), + 'return_result_with_error.* ' + 'returned a result with an exception set') + + def test_getitem_with_error(self): + # Test _Py_CheckSlotResult(). Raise an exception and then calls + # PyObject_GetItem(): check that the assertion catches the bug. + # PyObject_GetItem() must not be called with an exception set. + code = textwrap.dedent(""" + import _testcapi + from test import support + + with support.SuppressCrashReport(): + _testcapi.getitem_with_error({1: 2}, 1) + """) + rc, out, err = assert_python_failure('-c', code) + err = decode_stderr(err) + if 'SystemError: ' not in err: + self.assertRegex(err, + r'Fatal Python error: _Py_CheckSlotResult: ' + r'Slot __getitem__ of type dict succeeded ' + r'with an exception set\n' + r'Python runtime state: initialized\n' + r'ValueError: bug\n' + r'\n' + r'Current thread .* \(most recent call first\):\n' + r' File .*, line 6 in \n' + r'\n' + r'Extension modules: _testcapi \(total: 1\)\n') + else: + # Python built with NDEBUG macro defined: + # test _Py_CheckFunctionResult() instead. + self.assertIn('returned a result with an exception set', err) + + def test_buildvalue_N(self): + _testcapi.test_buildvalue_N() + + def test_set_nomemory(self): + code = """if 1: + import _testcapi + + class C(): pass + + # The first loop tests both functions and that remove_mem_hooks() + # can be called twice in a row. The second loop checks a call to + # set_nomemory() after a call to remove_mem_hooks(). The third + # loop checks the start and stop arguments of set_nomemory(). + for outer_cnt in range(1, 4): + start = 10 * outer_cnt + for j in range(100): + if j == 0: + if outer_cnt != 3: + _testcapi.set_nomemory(start) + else: + _testcapi.set_nomemory(start, start + 1) + try: + C() + except MemoryError as e: + if outer_cnt != 3: + _testcapi.remove_mem_hooks() + print('MemoryError', outer_cnt, j) + _testcapi.remove_mem_hooks() + break + """ + rc, out, err = assert_python_ok('-c', code) + lines = out.splitlines() + for i, line in enumerate(lines, 1): + self.assertIn(b'MemoryError', out) + *_, count = line.split(b' ') + count = int(count) + self.assertLessEqual(count, i*5) + self.assertGreaterEqual(count, i*5-2) + + def test_mapping_keys_values_items(self): + class Mapping1(dict): + def keys(self): + return list(super().keys()) + def values(self): + return list(super().values()) + def items(self): + return list(super().items()) + class Mapping2(dict): + def keys(self): + return tuple(super().keys()) + def values(self): + return tuple(super().values()) + def items(self): + return tuple(super().items()) + dict_obj = {'foo': 1, 'bar': 2, 'spam': 3} + + for mapping in [{}, OrderedDict(), Mapping1(), Mapping2(), + dict_obj, OrderedDict(dict_obj), + Mapping1(dict_obj), Mapping2(dict_obj)]: + self.assertListEqual(_testcapi.get_mapping_keys(mapping), + list(mapping.keys())) + self.assertListEqual(_testcapi.get_mapping_values(mapping), + list(mapping.values())) + self.assertListEqual(_testcapi.get_mapping_items(mapping), + list(mapping.items())) + + def test_mapping_keys_values_items_bad_arg(self): + self.assertRaises(AttributeError, _testcapi.get_mapping_keys, None) + self.assertRaises(AttributeError, _testcapi.get_mapping_values, None) + self.assertRaises(AttributeError, _testcapi.get_mapping_items, None) + + class BadMapping: + def keys(self): + return None + def values(self): + return None + def items(self): + return None + bad_mapping = BadMapping() + self.assertRaises(TypeError, _testcapi.get_mapping_keys, bad_mapping) + self.assertRaises(TypeError, _testcapi.get_mapping_values, bad_mapping) + self.assertRaises(TypeError, _testcapi.get_mapping_items, bad_mapping) + + def test_mapping_has_key(self): + dct = {'a': 1} + self.assertTrue(_testcapi.mapping_has_key(dct, 'a')) + self.assertFalse(_testcapi.mapping_has_key(dct, 'b')) + + class SubDict(dict): + pass + + dct2 = SubDict({'a': 1}) + self.assertTrue(_testcapi.mapping_has_key(dct2, 'a')) + self.assertFalse(_testcapi.mapping_has_key(dct2, 'b')) + + def test_sequence_set_slice(self): + # Correct case: + data = [1, 2, 3, 4, 5] + data_copy = data.copy() + + _testcapi.sequence_set_slice(data, 1, 3, [8, 9]) + data_copy[1:3] = [8, 9] + self.assertEqual(data, data_copy) + self.assertEqual(data, [1, 8, 9, 4, 5]) + + # Custom class: + class Custom: + def __setitem__(self, index, value): + self.index = index + self.value = value + + c = Custom() + _testcapi.sequence_set_slice(c, 0, 5, 'abc') + self.assertEqual(c.index, slice(0, 5)) + self.assertEqual(c.value, 'abc') + + # Immutable sequences must raise: + bad_seq1 = (1, 2, 3, 4) + with self.assertRaises(TypeError): + _testcapi.sequence_set_slice(bad_seq1, 1, 3, (8, 9)) + self.assertEqual(bad_seq1, (1, 2, 3, 4)) + + bad_seq2 = 'abcd' + with self.assertRaises(TypeError): + _testcapi.sequence_set_slice(bad_seq2, 1, 3, 'xy') + self.assertEqual(bad_seq2, 'abcd') + + # Not a sequence: + with self.assertRaises(TypeError): + _testcapi.sequence_set_slice(None, 1, 3, 'xy') + + mapping = {1: 'a', 2: 'b', 3: 'c'} + with self.assertRaises(TypeError): + _testcapi.sequence_set_slice(mapping, 1, 3, 'xy') + self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'}) + + def test_sequence_del_slice(self): + # Correct case: + data = [1, 2, 3, 4, 5] + data_copy = data.copy() + + _testcapi.sequence_del_slice(data, 1, 3) + del data_copy[1:3] + self.assertEqual(data, data_copy) + self.assertEqual(data, [1, 4, 5]) + + # Custom class: + class Custom: + def __delitem__(self, index): + self.index = index + + c = Custom() + _testcapi.sequence_del_slice(c, 0, 5) + self.assertEqual(c.index, slice(0, 5)) + + # Immutable sequences must raise: + bad_seq1 = (1, 2, 3, 4) + with self.assertRaises(TypeError): + _testcapi.sequence_del_slice(bad_seq1, 1, 3) + self.assertEqual(bad_seq1, (1, 2, 3, 4)) + + bad_seq2 = 'abcd' + with self.assertRaises(TypeError): + _testcapi.sequence_del_slice(bad_seq2, 1, 3) + self.assertEqual(bad_seq2, 'abcd') + + # Not a sequence: + with self.assertRaises(TypeError): + _testcapi.sequence_del_slice(None, 1, 3) + + mapping = {1: 'a', 2: 'b', 3: 'c'} + with self.assertRaises(TypeError): + _testcapi.sequence_del_slice(mapping, 1, 3) + self.assertEqual(mapping, {1: 'a', 2: 'b', 3: 'c'}) + + @unittest.skipUnless(hasattr(_testcapi, 'negative_refcount'), + 'need _testcapi.negative_refcount') + def test_negative_refcount(self): + # bpo-35059: Check that Py_DECREF() reports the correct filename + # when calling _Py_NegativeRefcount() to abort Python. + code = textwrap.dedent(""" + import _testcapi + from test import support + + with support.SuppressCrashReport(): + _testcapi.negative_refcount() + """) + rc, out, err = assert_python_failure('-c', code) + self.assertRegex(err, + br'_testcapimodule\.c:[0-9]+: ' + br'_Py_NegativeRefcount: Assertion failed: ' + br'object has negative ref count') + + def test_trashcan_subclass(self): + # bpo-35983: Check that the trashcan mechanism for "list" is NOT + # activated when its tp_dealloc is being called by a subclass + from _testcapi import MyList + L = None + for i in range(1000): + L = MyList((L,)) + + @support.requires_resource('cpu') + def test_trashcan_python_class1(self): + self.do_test_trashcan_python_class(list) + + @support.requires_resource('cpu') + def test_trashcan_python_class2(self): + from _testcapi import MyList + self.do_test_trashcan_python_class(MyList) + + def do_test_trashcan_python_class(self, base): + # Check that the trashcan mechanism works properly for a Python + # subclass of a class using the trashcan (this specific test assumes + # that the base class "base" behaves like list) + class PyList(base): + # Count the number of PyList instances to verify that there is + # no memory leak + num = 0 + def __init__(self, *args): + __class__.num += 1 + super().__init__(*args) + def __del__(self): + __class__.num -= 1 + + for parity in (0, 1): + L = None + # We need in the order of 2**20 iterations here such that a + # typical 8MB stack would overflow without the trashcan. + for i in range(2**20): + L = PyList((L,)) + L.attr = i + if parity: + # Add one additional nesting layer + L = (L,) + self.assertGreater(PyList.num, 0) + del L + self.assertEqual(PyList.num, 0) + + def test_heap_ctype_doc_and_text_signature(self): + self.assertEqual(_testcapi.HeapDocCType.__doc__, "somedoc") + self.assertEqual(_testcapi.HeapDocCType.__text_signature__, "(arg1, arg2)") + + def test_null_type_doc(self): + self.assertEqual(_testcapi.NullTpDocType.__doc__, None) + + def test_subclass_of_heap_gc_ctype_with_tpdealloc_decrefs_once(self): + class HeapGcCTypeSubclass(_testcapi.HeapGcCType): + def __init__(self): + self.value2 = 20 + super().__init__() + + subclass_instance = HeapGcCTypeSubclass() + type_refcnt = sys.getrefcount(HeapGcCTypeSubclass) + + # Test that subclass instance was fully created + self.assertEqual(subclass_instance.value, 10) + self.assertEqual(subclass_instance.value2, 20) + + # Test that the type reference count is only decremented once + del subclass_instance + self.assertEqual(type_refcnt - 1, sys.getrefcount(HeapGcCTypeSubclass)) + + def test_subclass_of_heap_gc_ctype_with_del_modifying_dunder_class_only_decrefs_once(self): + class A(_testcapi.HeapGcCType): + def __init__(self): + self.value2 = 20 + super().__init__() + + class B(A): + def __init__(self): + super().__init__() + + def __del__(self): + self.__class__ = A + A.refcnt_in_del = sys.getrefcount(A) + B.refcnt_in_del = sys.getrefcount(B) + + subclass_instance = B() + type_refcnt = sys.getrefcount(B) + new_type_refcnt = sys.getrefcount(A) + + # Test that subclass instance was fully created + self.assertEqual(subclass_instance.value, 10) + self.assertEqual(subclass_instance.value2, 20) + + del subclass_instance + + # Test that setting __class__ modified the reference counts of the types + if support.Py_DEBUG: + # gh-89373: In debug mode, _Py_Dealloc() keeps a strong reference + # to the type while calling tp_dealloc() + self.assertEqual(type_refcnt, B.refcnt_in_del) + else: + self.assertEqual(type_refcnt - 1, B.refcnt_in_del) + self.assertEqual(new_type_refcnt + 1, A.refcnt_in_del) + + # Test that the original type already has decreased its refcnt + self.assertEqual(type_refcnt - 1, sys.getrefcount(B)) + + # Test that subtype_dealloc decref the newly assigned __class__ only once + self.assertEqual(new_type_refcnt, sys.getrefcount(A)) + + def test_heaptype_with_dict(self): + inst = _testcapi.HeapCTypeWithDict() + inst.foo = 42 + self.assertEqual(inst.foo, 42) + self.assertEqual(inst.dictobj, inst.__dict__) + self.assertEqual(inst.dictobj, {"foo": 42}) + + inst = _testcapi.HeapCTypeWithDict() + self.assertEqual({}, inst.__dict__) + + def test_heaptype_with_managed_dict(self): + inst = _testcapi.HeapCTypeWithManagedDict() + inst.foo = 42 + self.assertEqual(inst.foo, 42) + self.assertEqual(inst.__dict__, {"foo": 42}) + + inst = _testcapi.HeapCTypeWithManagedDict() + self.assertEqual({}, inst.__dict__) + + a = _testcapi.HeapCTypeWithManagedDict() + b = _testcapi.HeapCTypeWithManagedDict() + a.b = b + b.a = a + del a, b + + def test_sublclassing_managed_dict(self): + + class C(_testcapi.HeapCTypeWithManagedDict): + pass + + i = C() + i.spam = i + del i + + def test_heaptype_with_negative_dict(self): + inst = _testcapi.HeapCTypeWithNegativeDict() + inst.foo = 42 + self.assertEqual(inst.foo, 42) + self.assertEqual(inst.dictobj, inst.__dict__) + self.assertEqual(inst.dictobj, {"foo": 42}) + + inst = _testcapi.HeapCTypeWithNegativeDict() + self.assertEqual({}, inst.__dict__) + + def test_heaptype_with_weakref(self): + inst = _testcapi.HeapCTypeWithWeakref() + ref = weakref.ref(inst) + self.assertEqual(ref(), inst) + self.assertEqual(inst.weakreflist, ref) + + def test_heaptype_with_managed_weakref(self): + inst = _testcapi.HeapCTypeWithManagedWeakref() + ref = weakref.ref(inst) + self.assertEqual(ref(), inst) + + def test_sublclassing_managed_weakref(self): + + class C(_testcapi.HeapCTypeWithManagedWeakref): + pass + + inst = C() + ref = weakref.ref(inst) + self.assertEqual(ref(), inst) + + def test_sublclassing_managed_both(self): + + class C1(_testcapi.HeapCTypeWithManagedWeakref, _testcapi.HeapCTypeWithManagedDict): + pass + + class C2(_testcapi.HeapCTypeWithManagedDict, _testcapi.HeapCTypeWithManagedWeakref): + pass + + for cls in (C1, C2): + inst = cls() + ref = weakref.ref(inst) + self.assertEqual(ref(), inst) + inst.spam = inst + del inst + ref = weakref.ref(cls()) + self.assertIs(ref(), None) + + def test_heaptype_with_buffer(self): + inst = _testcapi.HeapCTypeWithBuffer() + b = bytes(inst) + self.assertEqual(b, b"1234") + + def test_c_subclass_of_heap_ctype_with_tpdealloc_decrefs_once(self): + subclass_instance = _testcapi.HeapCTypeSubclass() + type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclass) + + # Test that subclass instance was fully created + self.assertEqual(subclass_instance.value, 10) + self.assertEqual(subclass_instance.value2, 20) + + # Test that the type reference count is only decremented once + del subclass_instance + self.assertEqual(type_refcnt - 1, sys.getrefcount(_testcapi.HeapCTypeSubclass)) + + def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_once(self): + subclass_instance = _testcapi.HeapCTypeSubclassWithFinalizer() + type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer) + new_type_refcnt = sys.getrefcount(_testcapi.HeapCTypeSubclass) + + # Test that subclass instance was fully created + self.assertEqual(subclass_instance.value, 10) + self.assertEqual(subclass_instance.value2, 20) + + # The tp_finalize slot will set __class__ to HeapCTypeSubclass + del subclass_instance + + # Test that setting __class__ modified the reference counts of the types + if support.Py_DEBUG: + # gh-89373: In debug mode, _Py_Dealloc() keeps a strong reference + # to the type while calling tp_dealloc() + self.assertEqual(type_refcnt, _testcapi.HeapCTypeSubclassWithFinalizer.refcnt_in_del) + else: + self.assertEqual(type_refcnt - 1, _testcapi.HeapCTypeSubclassWithFinalizer.refcnt_in_del) + self.assertEqual(new_type_refcnt + 1, _testcapi.HeapCTypeSubclass.refcnt_in_del) + + # Test that the original type already has decreased its refcnt + self.assertEqual(type_refcnt - 1, sys.getrefcount(_testcapi.HeapCTypeSubclassWithFinalizer)) + + # Test that subtype_dealloc decref the newly assigned __class__ only once + self.assertEqual(new_type_refcnt, sys.getrefcount(_testcapi.HeapCTypeSubclass)) + + def test_heaptype_with_setattro(self): + obj = _testcapi.HeapCTypeSetattr() + self.assertEqual(obj.pvalue, 10) + obj.value = 12 + self.assertEqual(obj.pvalue, 12) + del obj.value + self.assertEqual(obj.pvalue, 0) + + def test_heaptype_with_custom_metaclass(self): + self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclass, type)) + self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclassCustomNew, type)) + + t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclass) + self.assertIsInstance(t, type) + self.assertEqual(t.__name__, "HeapCTypeViaMetaclass") + self.assertIs(type(t), _testcapi.HeapCTypeMetaclass) + + msg = "Metaclasses with custom tp_new are not supported." + with self.assertRaisesRegex(TypeError, msg): + t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclassCustomNew) + + def test_multiple_inheritance_ctypes_with_weakref_or_dict(self): + + with self.assertRaises(TypeError): + class Both1(_testcapi.HeapCTypeWithWeakref, _testcapi.HeapCTypeWithDict): + pass + with self.assertRaises(TypeError): + class Both2(_testcapi.HeapCTypeWithDict, _testcapi.HeapCTypeWithWeakref): + pass + + def test_multiple_inheritance_ctypes_with_weakref_or_dict_and_other_builtin(self): + + with self.assertRaises(TypeError): + class C1(_testcapi.HeapCTypeWithDict, list): + pass + + with self.assertRaises(TypeError): + class C2(_testcapi.HeapCTypeWithWeakref, list): + pass + + class C3(_testcapi.HeapCTypeWithManagedDict, list): + pass + class C4(_testcapi.HeapCTypeWithManagedWeakref, list): + pass + + inst = C3() + inst.append(0) + str(inst.__dict__) + + inst = C4() + inst.append(0) + str(inst.__weakref__) + + for cls in (_testcapi.HeapCTypeWithManagedDict, _testcapi.HeapCTypeWithManagedWeakref): + for cls2 in (_testcapi.HeapCTypeWithDict, _testcapi.HeapCTypeWithWeakref): + class S(cls, cls2): + pass + class B1(C3, cls): + pass + class B2(C4, cls): + pass + + def test_pytype_fromspec_with_repeated_slots(self): + for variant in range(2): + with self.subTest(variant=variant): + with self.assertRaises(SystemError): + _testcapi.create_type_from_repeated_slots(variant) + + @warnings_helper.ignore_warnings(category=DeprecationWarning) + def test_immutable_type_with_mutable_base(self): + # Add deprecation warning here so it's removed in 3.14 + warnings._deprecated( + 'creating immutable classes with mutable bases', remove=(3, 14)) + + class MutableBase: + def meth(self): + return 'original' + + with self.assertWarns(DeprecationWarning): + ImmutableSubclass = _testcapi.make_immutable_type_with_base( + MutableBase) + instance = ImmutableSubclass() + + self.assertEqual(instance.meth(), 'original') + + # Cannot override the static type's method + with self.assertRaisesRegex( + TypeError, + "cannot set 'meth' attribute of immutable type"): + ImmutableSubclass.meth = lambda self: 'overridden' + self.assertEqual(instance.meth(), 'original') + + # Can change the method on the mutable base + MutableBase.meth = lambda self: 'changed' + self.assertEqual(instance.meth(), 'changed') + + + def test_pynumber_tobase(self): + from _testcapi import pynumber_tobase + small_number = 123 + large_number = 2**64 + class IDX: + def __init__(self, val): + self.val = val + def __index__(self): + return self.val + + test_cases = ((2, '0b1111011', '0b10000000000000000000000000000000000000000000000000000000000000000'), + (8, '0o173', '0o2000000000000000000000'), + (10, '123', '18446744073709551616'), + (16, '0x7b', '0x10000000000000000')) + for base, small_target, large_target in test_cases: + with self.subTest(base=base, st=small_target, lt=large_target): + # Test for small number + self.assertEqual(pynumber_tobase(small_number, base), small_target) + self.assertEqual(pynumber_tobase(-small_number, base), '-' + small_target) + self.assertEqual(pynumber_tobase(IDX(small_number), base), small_target) + # Test for large number(out of range of a longlong,i.e.[-2**63, 2**63-1]) + self.assertEqual(pynumber_tobase(large_number, base), large_target) + self.assertEqual(pynumber_tobase(-large_number, base), '-' + large_target) + self.assertEqual(pynumber_tobase(IDX(large_number), base), large_target) + self.assertRaises(TypeError, pynumber_tobase, IDX(123.0), 10) + self.assertRaises(TypeError, pynumber_tobase, IDX('123'), 10) + self.assertRaises(TypeError, pynumber_tobase, 123.0, 10) + self.assertRaises(TypeError, pynumber_tobase, '123', 10) + self.assertRaises(SystemError, pynumber_tobase, 123, 0) + + def check_fatal_error(self, code, expected, not_expected=()): + with support.SuppressCrashReport(): + rc, out, err = assert_python_failure('-sSI', '-c', code) + + err = decode_stderr(err) + self.assertIn('Fatal Python error: test_fatal_error: MESSAGE\n', + err) + + match = re.search(r'^Extension modules:(.*) \(total: ([0-9]+)\)$', + err, re.MULTILINE) + if not match: + self.fail(f"Cannot find 'Extension modules:' in {err!r}") + modules = set(match.group(1).strip().split(', ')) + total = int(match.group(2)) + + for name in expected: + self.assertIn(name, modules) + for name in not_expected: + self.assertNotIn(name, modules) + self.assertEqual(len(modules), total) + + @support.requires_subprocess() + def test_fatal_error(self): + # By default, stdlib extension modules are ignored, + # but not test modules. + expected = ('_testcapi',) + not_expected = ('sys',) + code = 'import _testcapi, sys; _testcapi.fatal_error(b"MESSAGE")' + self.check_fatal_error(code, expected, not_expected) + + # Mark _testcapi as stdlib module, but not sys + expected = ('sys',) + not_expected = ('_testcapi',) + code = textwrap.dedent(''' + import _testcapi, sys + sys.stdlib_module_names = frozenset({"_testcapi"}) + _testcapi.fatal_error(b"MESSAGE") + ''') + self.check_fatal_error(code, expected) + + def test_pyobject_repr_from_null(self): + s = _testcapi.pyobject_repr_from_null() + self.assertEqual(s, '') + + def test_pyobject_str_from_null(self): + s = _testcapi.pyobject_str_from_null() + self.assertEqual(s, '') + + def test_pyobject_bytes_from_null(self): + s = _testcapi.pyobject_bytes_from_null() + self.assertEqual(s, b'') + + def test_Py_CompileString(self): + # Check that Py_CompileString respects the coding cookie + _compile = _testcapi.Py_CompileString + code = b"# -*- coding: latin1 -*-\nprint('\xc2\xa4')\n" + result = _compile(code) + expected = compile(code, "", "exec") + self.assertEqual(result.co_consts, expected.co_consts) + + def test_export_symbols(self): + # bpo-44133: Ensure that the "Py_FrozenMain" and + # "PyThread_get_thread_native_id" symbols are exported by the Python + # (directly by the binary, or via by the Python dynamic library). + ctypes = import_helper.import_module('ctypes') + names = [] + + # Test if the PY_HAVE_THREAD_NATIVE_ID macro is defined + if hasattr(_thread, 'get_native_id'): + names.append('PyThread_get_thread_native_id') + + # Python/frozenmain.c fails to build on Windows when the symbols are + # missing: + # - PyWinFreeze_ExeInit + # - PyWinFreeze_ExeTerm + # - PyInitFrozenExtensions + if os.name != 'nt': + names.append('Py_FrozenMain') + + for name in names: + with self.subTest(name=name): + self.assertTrue(hasattr(ctypes.pythonapi, name)) + + def test_clear_managed_dict(self): + + class C: + def __init__(self): + self.a = 1 + + c = C() + _testcapi.clear_managed_dict(c) + self.assertEqual(c.__dict__, {}) + c = C() + self.assertEqual(c.__dict__, {'a':1}) + _testcapi.clear_managed_dict(c) + self.assertEqual(c.__dict__, {}) + + def test_eval_get_func_name(self): + def function_example(): ... + + class A: + def method_example(self): ... + + self.assertEqual(_testcapi.eval_get_func_name(function_example), + "function_example") + self.assertEqual(_testcapi.eval_get_func_name(A.method_example), + "method_example") + self.assertEqual(_testcapi.eval_get_func_name(A().method_example), + "method_example") + self.assertEqual(_testcapi.eval_get_func_name(sum), "sum") # c function + self.assertEqual(_testcapi.eval_get_func_name(A), "type") + + def test_eval_get_func_desc(self): + def function_example(): ... + + class A: + def method_example(self): ... + + self.assertEqual(_testcapi.eval_get_func_desc(function_example), + "()") + self.assertEqual(_testcapi.eval_get_func_desc(A.method_example), + "()") + self.assertEqual(_testcapi.eval_get_func_desc(A().method_example), + "()") + self.assertEqual(_testcapi.eval_get_func_desc(sum), "()") # c function + self.assertEqual(_testcapi.eval_get_func_desc(A), " object") + + def test_function_get_code(self): + import types + + def some(): + pass + + code = _testcapi.function_get_code(some) + self.assertIsInstance(code, types.CodeType) + self.assertEqual(code, some.__code__) + + with self.assertRaises(SystemError): + _testcapi.function_get_code(None) # not a function + + def test_function_get_globals(self): + def some(): + pass + + globals_ = _testcapi.function_get_globals(some) + self.assertIsInstance(globals_, dict) + self.assertEqual(globals_, some.__globals__) + + with self.assertRaises(SystemError): + _testcapi.function_get_globals(None) # not a function + + def test_function_get_module(self): + def some(): + pass + + module = _testcapi.function_get_module(some) + self.assertIsInstance(module, str) + self.assertEqual(module, some.__module__) + + with self.assertRaises(SystemError): + _testcapi.function_get_module(None) # not a function + + def test_function_get_defaults(self): + def some( + pos_only1, pos_only2='p', + /, + zero=0, optional=None, + *, + kw1, + kw2=True, + ): + pass + + defaults = _testcapi.function_get_defaults(some) + self.assertEqual(defaults, ('p', 0, None)) + self.assertEqual(defaults, some.__defaults__) + + with self.assertRaises(SystemError): + _testcapi.function_get_defaults(None) # not a function + + def test_function_set_defaults(self): + def some( + pos_only1, pos_only2='p', + /, + zero=0, optional=None, + *, + kw1, + kw2=True, + ): + pass + + old_defaults = ('p', 0, None) + self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) + self.assertEqual(some.__defaults__, old_defaults) + + with self.assertRaises(SystemError): + _testcapi.function_set_defaults(some, 1) # not tuple or None + self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) + self.assertEqual(some.__defaults__, old_defaults) + + with self.assertRaises(SystemError): + _testcapi.function_set_defaults(1, ()) # not a function + self.assertEqual(_testcapi.function_get_defaults(some), old_defaults) + self.assertEqual(some.__defaults__, old_defaults) + + new_defaults = ('q', 1, None) + _testcapi.function_set_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) + self.assertEqual(some.__defaults__, new_defaults) + + # Empty tuple is fine: + new_defaults = () + _testcapi.function_set_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) + self.assertEqual(some.__defaults__, new_defaults) + + class tuplesub(tuple): ... # tuple subclasses must work + + new_defaults = tuplesub(((1, 2), ['a', 'b'], None)) + _testcapi.function_set_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_defaults(some), new_defaults) + self.assertEqual(some.__defaults__, new_defaults) + + # `None` is special, it sets `defaults` to `NULL`, + # it needs special handling in `_testcapi`: + _testcapi.function_set_defaults(some, None) + self.assertEqual(_testcapi.function_get_defaults(some), None) + self.assertEqual(some.__defaults__, None) + + def test_function_get_kw_defaults(self): + def some( + pos_only1, pos_only2='p', + /, + zero=0, optional=None, + *, + kw1, + kw2=True, + ): + pass + + defaults = _testcapi.function_get_kw_defaults(some) + self.assertEqual(defaults, {'kw2': True}) + self.assertEqual(defaults, some.__kwdefaults__) + + with self.assertRaises(SystemError): + _testcapi.function_get_kw_defaults(None) # not a function + + def test_function_set_kw_defaults(self): + def some( + pos_only1, pos_only2='p', + /, + zero=0, optional=None, + *, + kw1, + kw2=True, + ): + pass + + old_defaults = {'kw2': True} + self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) + self.assertEqual(some.__kwdefaults__, old_defaults) + + with self.assertRaises(SystemError): + _testcapi.function_set_kw_defaults(some, 1) # not dict or None + self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) + self.assertEqual(some.__kwdefaults__, old_defaults) + + with self.assertRaises(SystemError): + _testcapi.function_set_kw_defaults(1, {}) # not a function + self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults) + self.assertEqual(some.__kwdefaults__, old_defaults) + + new_defaults = {'kw2': (1, 2, 3)} + _testcapi.function_set_kw_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) + self.assertEqual(some.__kwdefaults__, new_defaults) + + # Empty dict is fine: + new_defaults = {} + _testcapi.function_set_kw_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) + self.assertEqual(some.__kwdefaults__, new_defaults) + + class dictsub(dict): ... # dict subclasses must work + + new_defaults = dictsub({'kw2': None}) + _testcapi.function_set_kw_defaults(some, new_defaults) + self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults) + self.assertEqual(some.__kwdefaults__, new_defaults) + + # `None` is special, it sets `kwdefaults` to `NULL`, + # it needs special handling in `_testcapi`: + _testcapi.function_set_kw_defaults(some, None) + self.assertEqual(_testcapi.function_get_kw_defaults(some), None) + self.assertEqual(some.__kwdefaults__, None) + + +class TestPendingCalls(unittest.TestCase): + + def pendingcalls_submit(self, l, n): + def callback(): + #this function can be interrupted by thread switching so let's + #use an atomic operation + l.append(None) + + for i in range(n): + time.sleep(random.random()*0.02) #0.01 secs on average + #try submitting callback until successful. + #rely on regular interrupt to flush queue if we are + #unsuccessful. + while True: + if _testcapi._pending_threadfunc(callback): + break + + def pendingcalls_wait(self, l, n, context = None): + #now, stick around until l[0] has grown to 10 + count = 0 + while len(l) != n: + #this busy loop is where we expect to be interrupted to + #run our callbacks. Note that callbacks are only run on the + #main thread + if False and support.verbose: + print("(%i)"%(len(l),),) + for i in range(1000): + a = i*i + if context and not context.event.is_set(): + continue + count += 1 + self.assertTrue(count < 10000, + "timeout waiting for %i callbacks, got %i"%(n, len(l))) + if False and support.verbose: + print("(%i)"%(len(l),)) + + @threading_helper.requires_working_threading() + def test_pendingcalls_threaded(self): + + #do every callback on a separate thread + n = 32 #total callbacks + threads = [] + class foo(object):pass + context = foo() + context.l = [] + context.n = 2 #submits per thread + context.nThreads = n // context.n + context.nFinished = 0 + context.lock = threading.Lock() + context.event = threading.Event() + + threads = [threading.Thread(target=self.pendingcalls_thread, + args=(context,)) + for i in range(context.nThreads)] + with threading_helper.start_threads(threads): + self.pendingcalls_wait(context.l, n, context) + + def pendingcalls_thread(self, context): + try: + self.pendingcalls_submit(context.l, context.n) + finally: + with context.lock: + context.nFinished += 1 + nFinished = context.nFinished + if False and support.verbose: + print("finished threads: ", nFinished) + if nFinished == context.nThreads: + context.event.set() + + def test_pendingcalls_non_threaded(self): + #again, just using the main thread, likely they will all be dispatched at + #once. It is ok to ask for too many, because we loop until we find a slot. + #the loop can be interrupted to dispatch. + #there are only 32 dispatch slots, so we go for twice that! + l = [] + n = 64 + self.pendingcalls_submit(l, n) + self.pendingcalls_wait(l, n) + + +class SubinterpreterTest(unittest.TestCase): + + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_subinterps(self): + import builtins + r, w = os.pipe() + code = """if 1: + import sys, builtins, pickle + with open({:d}, "wb") as f: + pickle.dump(id(sys.modules), f) + pickle.dump(id(builtins), f) + """.format(w) + with open(r, "rb") as f: + ret = support.run_in_subinterp(code) + self.assertEqual(ret, 0) + self.assertNotEqual(pickle.load(f), id(sys.modules)) + self.assertNotEqual(pickle.load(f), id(builtins)) + + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_subinterps_recent_language_features(self): + r, w = os.pipe() + code = """if 1: + import pickle + with open({:d}, "wb") as f: + + @(lambda x:x) # Py 3.9 + def noop(x): return x + + a = (b := f'1{{2}}3') + noop('x') # Py 3.8 (:=) / 3.6 (f'') + + async def foo(arg): return await arg # Py 3.5 + + pickle.dump(dict(a=a, b=b), f) + """.format(w) + + with open(r, "rb") as f: + ret = support.run_in_subinterp(code) + self.assertEqual(ret, 0) + self.assertEqual(pickle.load(f), {'a': '123x', 'b': '123'}) + + def test_py_config_isoloated_per_interpreter(self): + # A config change in one interpreter must not leak to out to others. + # + # This test could verify ANY config value, it just happens to have been + # written around the time of int_max_str_digits. Refactoring is okay. + code = """if 1: + import sys, _testinternalcapi + + # Any config value would do, this happens to be the one being + # double checked at the time this test was written. + config = _testinternalcapi.get_config() + config['int_max_str_digits'] = 55555 + _testinternalcapi.set_config(config) + sub_value = _testinternalcapi.get_config()['int_max_str_digits'] + assert sub_value == 55555, sub_value + """ + before_config = _testinternalcapi.get_config() + assert before_config['int_max_str_digits'] != 55555 + self.assertEqual(support.run_in_subinterp(code), 0, + 'subinterp code failure, check stderr.') + after_config = _testinternalcapi.get_config() + self.assertIsNot( + before_config, after_config, + "Expected get_config() to return a new dict on each call") + self.assertEqual(before_config, after_config, + "CAUTION: Tests executed after this may be " + "running under an altered config.") + # try:...finally: calling set_config(before_config) not done + # as that results in sys.argv, sys.path, and sys.warnoptions + # "being modified by test_capi" per test.regrtest. So if this + # test fails, assume that the environment in this process may + # be altered and suspect. + + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_configured_settings(self): + """ + The config with which an interpreter is created corresponds + 1-to-1 with the new interpreter's settings. This test verifies + that they match. + """ + import json + + THREADS = 1<<10 + DAEMON_THREADS = 1<<11 + FORK = 1<<15 + EXEC = 1<<16 + + features = ['fork', 'exec', 'threads', 'daemon_threads'] + kwlist = [f'allow_{n}' for n in features] + for config, expected in { + (True, True, True, True): FORK | EXEC | THREADS | DAEMON_THREADS, + (False, False, False, False): 0, + (False, False, True, False): THREADS, + }.items(): + kwargs = dict(zip(kwlist, config)) + expected = { + 'feature_flags': expected, + } + with self.subTest(config): + r, w = os.pipe() + script = textwrap.dedent(f''' + import _testinternalcapi, json, os + settings = _testinternalcapi.get_interp_settings() + with os.fdopen({w}, "w") as stdin: + json.dump(settings, stdin) + ''') + with os.fdopen(r) as stdout: + support.run_in_subinterp_with_config(script, **kwargs) + out = stdout.read() + settings = json.loads(out) + + self.assertEqual(settings, expected) + + def test_mutate_exception(self): + """ + Exceptions saved in global module state get shared between + individual module instances. This test checks whether or not + a change in one interpreter's module gets reflected into the + other ones. + """ + import binascii + + support.run_in_subinterp("import binascii; binascii.Error.foobar = 'foobar'") + + self.assertFalse(hasattr(binascii.Error, "foobar")) + + @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") + def test_module_state_shared_in_global(self): + """ + bpo-44050: Extension module state should be shared between interpreters + when it doesn't support sub-interpreters. + """ + r, w = os.pipe() + self.addCleanup(os.close, r) + self.addCleanup(os.close, w) + + script = textwrap.dedent(f""" + import importlib.machinery + import importlib.util + import os + + fullname = '_test_module_state_shared' + origin = importlib.util.find_spec('_testmultiphase').origin + loader = importlib.machinery.ExtensionFileLoader(fullname, origin) + spec = importlib.util.spec_from_loader(fullname, loader) + module = importlib.util.module_from_spec(spec) + attr_id = str(id(module.Error)).encode() + + os.write({w}, attr_id) + """) + exec(script) + main_attr_id = os.read(r, 100) + + ret = support.run_in_subinterp(script) + self.assertEqual(ret, 0) + subinterp_attr_id = os.read(r, 100) + self.assertEqual(main_attr_id, subinterp_attr_id) + + +class TestThreadState(unittest.TestCase): + + @threading_helper.reap_threads + @threading_helper.requires_working_threading() + def test_thread_state(self): + # some extra thread-state tests driven via _testcapi + def target(): + idents = [] + + def callback(): + idents.append(threading.get_ident()) + + _testcapi._test_thread_state(callback) + a = b = callback + time.sleep(1) + # Check our main thread is in the list exactly 3 times. + self.assertEqual(idents.count(threading.get_ident()), 3, + "Couldn't find main thread correctly in the list") + + target() + t = threading.Thread(target=target) + t.start() + t.join() + + @threading_helper.reap_threads + @threading_helper.requires_working_threading() + def test_gilstate_ensure_no_deadlock(self): + # See https://github.com/python/cpython/issues/96071 + code = textwrap.dedent(f""" + import _testcapi + + def callback(): + print('callback called') + + _testcapi._test_thread_state(callback) + """) + ret = assert_python_ok('-X', 'tracemalloc', '-c', code) + self.assertIn(b'callback called', ret.out) + + +class Test_testcapi(unittest.TestCase): + locals().update((name, getattr(_testcapi, name)) + for name in dir(_testcapi) + if name.startswith('test_') and not name.endswith('_code')) + + # Suppress warning from PyUnicode_FromUnicode(). + @warnings_helper.ignore_warnings(category=DeprecationWarning) + def test_widechar(self): + _testcapi.test_widechar() + + def test_version_api_data(self): + self.assertEqual(_testcapi.Py_Version, sys.hexversion) + + +class Test_testinternalcapi(unittest.TestCase): + locals().update((name, getattr(_testinternalcapi, name)) + for name in dir(_testinternalcapi) + if name.startswith('test_')) + + +@support.requires_subprocess() +class PyMemDebugTests(unittest.TestCase): + PYTHONMALLOC = 'debug' + # '0x04c06e0' or '04C06E0' + PTR_REGEX = r'(?:0x)?[0-9a-fA-F]+' + + def check(self, code): + with support.SuppressCrashReport(): + out = assert_python_failure( + '-c', code, + PYTHONMALLOC=self.PYTHONMALLOC, + # FreeBSD: instruct jemalloc to not fill freed() memory + # with junk byte 0x5a, see JEMALLOC(3) + MALLOC_CONF="junk:false", + ) + stderr = out.err + return stderr.decode('ascii', 'replace') + + def test_buffer_overflow(self): + out = self.check('import _testcapi; _testcapi.pymem_buffer_overflow()') + regex = (r"Debug memory block at address p={ptr}: API 'm'\n" + r" 16 bytes originally requested\n" + r" The [0-9] pad bytes at p-[0-9] are FORBIDDENBYTE, as expected.\n" + r" The [0-9] pad bytes at tail={ptr} are not all FORBIDDENBYTE \(0x[0-9a-f]{{2}}\):\n" + r" at tail\+0: 0x78 \*\*\* OUCH\n" + r" at tail\+1: 0xfd\n" + r" at tail\+2: 0xfd\n" + r" .*\n" + r"( The block was made by call #[0-9]+ to debug malloc/realloc.\n)?" + r" Data at p: cd cd cd .*\n" + r"\n" + r"Enable tracemalloc to get the memory block allocation traceback\n" + r"\n" + r"Fatal Python error: _PyMem_DebugRawFree: bad trailing pad byte") + regex = regex.format(ptr=self.PTR_REGEX) + regex = re.compile(regex, flags=re.DOTALL) + self.assertRegex(out, regex) + + def test_api_misuse(self): + out = self.check('import _testcapi; _testcapi.pymem_api_misuse()') + regex = (r"Debug memory block at address p={ptr}: API 'm'\n" + r" 16 bytes originally requested\n" + r" The [0-9] pad bytes at p-[0-9] are FORBIDDENBYTE, as expected.\n" + r" The [0-9] pad bytes at tail={ptr} are FORBIDDENBYTE, as expected.\n" + r"( The block was made by call #[0-9]+ to debug malloc/realloc.\n)?" + r" Data at p: cd cd cd .*\n" + r"\n" + r"Enable tracemalloc to get the memory block allocation traceback\n" + r"\n" + r"Fatal Python error: _PyMem_DebugRawFree: bad ID: Allocated using API 'm', verified using API 'r'\n") + regex = regex.format(ptr=self.PTR_REGEX) + self.assertRegex(out, regex) + + def check_malloc_without_gil(self, code): + out = self.check(code) + expected = ('Fatal Python error: _PyMem_DebugMalloc: ' + 'Python memory allocator called without holding the GIL') + self.assertIn(expected, out) + + def test_pymem_malloc_without_gil(self): + # Debug hooks must raise an error if PyMem_Malloc() is called + # without holding the GIL + code = 'import _testcapi; _testcapi.pymem_malloc_without_gil()' + self.check_malloc_without_gil(code) + + def test_pyobject_malloc_without_gil(self): + # Debug hooks must raise an error if PyObject_Malloc() is called + # without holding the GIL + code = 'import _testcapi; _testcapi.pyobject_malloc_without_gil()' + self.check_malloc_without_gil(code) + + def check_pyobject_is_freed(self, func_name): + code = textwrap.dedent(f''' + import gc, os, sys, _testcapi + # Disable the GC to avoid crash on GC collection + gc.disable() + try: + _testcapi.{func_name}() + # Exit immediately to avoid a crash while deallocating + # the invalid object + os._exit(0) + except _testcapi.error: + os._exit(1) + ''') + assert_python_ok( + '-c', code, + PYTHONMALLOC=self.PYTHONMALLOC, + MALLOC_CONF="junk:false", + ) + + def test_pyobject_null_is_freed(self): + self.check_pyobject_is_freed('check_pyobject_null_is_freed') + + def test_pyobject_uninitialized_is_freed(self): + self.check_pyobject_is_freed('check_pyobject_uninitialized_is_freed') + + def test_pyobject_forbidden_bytes_is_freed(self): + self.check_pyobject_is_freed('check_pyobject_forbidden_bytes_is_freed') + + def test_pyobject_freed_is_freed(self): + self.check_pyobject_is_freed('check_pyobject_freed_is_freed') + + +class PyMemMallocDebugTests(PyMemDebugTests): + PYTHONMALLOC = 'malloc_debug' + + +@unittest.skipUnless(support.with_pymalloc(), 'need pymalloc') +class PyMemPymallocDebugTests(PyMemDebugTests): + PYTHONMALLOC = 'pymalloc_debug' + + +@unittest.skipUnless(support.Py_DEBUG, 'need Py_DEBUG') +class PyMemDefaultTests(PyMemDebugTests): + # test default allocator of Python compiled in debug mode + PYTHONMALLOC = '' + + +@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") +class Test_ModuleStateAccess(unittest.TestCase): + """Test access to module start (PEP 573)""" + + # The C part of the tests lives in _testmultiphase, in a module called + # _testmultiphase_meth_state_access. + # This module has multi-phase initialization, unlike _testcapi. + + def setUp(self): + fullname = '_testmultiphase_meth_state_access' # XXX + origin = importlib.util.find_spec('_testmultiphase').origin + loader = importlib.machinery.ExtensionFileLoader(fullname, origin) + spec = importlib.util.spec_from_loader(fullname, loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) + self.module = module + + def test_subclass_get_module(self): + """PyType_GetModule for defining_class""" + class StateAccessType_Subclass(self.module.StateAccessType): + pass + + instance = StateAccessType_Subclass() + self.assertIs(instance.get_defining_module(), self.module) + + def test_subclass_get_module_with_super(self): + class StateAccessType_Subclass(self.module.StateAccessType): + def get_defining_module(self): + return super().get_defining_module() + + instance = StateAccessType_Subclass() + self.assertIs(instance.get_defining_module(), self.module) + + def test_state_access(self): + """Checks methods defined with and without argument clinic + + This tests a no-arg method (get_count) and a method with + both a positional and keyword argument. + """ + + a = self.module.StateAccessType() + b = self.module.StateAccessType() + + methods = { + 'clinic': a.increment_count_clinic, + 'noclinic': a.increment_count_noclinic, + } + + for name, increment_count in methods.items(): + with self.subTest(name): + self.assertEqual(a.get_count(), b.get_count()) + self.assertEqual(a.get_count(), 0) + + increment_count() + self.assertEqual(a.get_count(), b.get_count()) + self.assertEqual(a.get_count(), 1) + + increment_count(3) + self.assertEqual(a.get_count(), b.get_count()) + self.assertEqual(a.get_count(), 4) + + increment_count(-2, twice=True) + self.assertEqual(a.get_count(), b.get_count()) + self.assertEqual(a.get_count(), 0) + + with self.assertRaises(TypeError): + increment_count(thrice=3) + + with self.assertRaises(TypeError): + increment_count(1, 2, 3) + + def test_get_module_bad_def(self): + # PyType_GetModuleByDef fails gracefully if it doesn't + # find what it's looking for. + # see bpo-46433 + instance = self.module.StateAccessType() + with self.assertRaises(TypeError): + instance.getmodulebydef_bad_def() + + def test_get_module_static_in_mro(self): + # Here, the class PyType_GetModuleByDef is looking for + # appears in the MRO after a static type (Exception). + # see bpo-46433 + class Subclass(BaseException, self.module.StateAccessType): + pass + self.assertIs(Subclass().get_defining_module(), self.module) + + +SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100 + +class Test_Pep523API(unittest.TestCase): + + def do_test(self, func): + calls = [] + start = SUFFICIENT_TO_DEOPT_AND_SPECIALIZE + count = start + SUFFICIENT_TO_DEOPT_AND_SPECIALIZE + for i in range(count): + if i == start: + _testinternalcapi.set_eval_frame_record(calls) + func() + _testinternalcapi.set_eval_frame_default() + self.assertEqual(len(calls), SUFFICIENT_TO_DEOPT_AND_SPECIALIZE) + for name in calls: + self.assertEqual(name, func.__name__) + + def test_pep523_with_specialization_simple(self): + def func1(): + pass + self.do_test(func1) + + def test_pep523_with_specialization_with_default(self): + def func2(x=None): + pass + self.do_test(func2) + + +class TestDictWatchers(unittest.TestCase): + # types of watchers testcapimodule can add: + EVENTS = 0 # appends dict events as strings to global event list + ERROR = 1 # unconditionally sets and signals a RuntimeException + SECOND = 2 # always appends "second" to global event list + + def add_watcher(self, kind=EVENTS): + return _testcapi.add_dict_watcher(kind) + + def clear_watcher(self, watcher_id): + _testcapi.clear_dict_watcher(watcher_id) + + @contextmanager + def watcher(self, kind=EVENTS): + wid = self.add_watcher(kind) + try: + yield wid + finally: + self.clear_watcher(wid) + + def assert_events(self, expected): + actual = _testcapi.get_dict_watcher_events() + self.assertEqual(actual, expected) + + def watch(self, wid, d): + _testcapi.watch_dict(wid, d) + + def unwatch(self, wid, d): + _testcapi.unwatch_dict(wid, d) + + def test_set_new_item(self): + d = {} + with self.watcher() as wid: + self.watch(wid, d) + d["foo"] = "bar" + self.assert_events(["new:foo:bar"]) + + def test_set_existing_item(self): + d = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + d["foo"] = "baz" + self.assert_events(["mod:foo:baz"]) + + def test_clone(self): + d = {} + d2 = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + d.update(d2) + self.assert_events(["clone"]) + + def test_no_event_if_not_watched(self): + d = {} + with self.watcher() as wid: + d["foo"] = "bar" + self.assert_events([]) + + def test_del(self): + d = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + del d["foo"] + self.assert_events(["del:foo"]) + + def test_pop(self): + d = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + d.pop("foo") + self.assert_events(["del:foo"]) + + def test_clear(self): + d = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + d.clear() + self.assert_events(["clear"]) + + def test_dealloc(self): + d = {"foo": "bar"} + with self.watcher() as wid: + self.watch(wid, d) + del d + self.assert_events(["dealloc"]) + + def test_unwatch(self): + d = {} + with self.watcher() as wid: + self.watch(wid, d) + d["foo"] = "bar" + self.unwatch(wid, d) + d["hmm"] = "baz" + self.assert_events(["new:foo:bar"]) + + def test_error(self): + d = {} + with self.watcher(kind=self.ERROR) as wid: + self.watch(wid, d) + with catch_unraisable_exception() as cm: + d["foo"] = "bar" + self.assertIs(cm.unraisable.object, d) + self.assertEqual(str(cm.unraisable.exc_value), "boom!") + self.assert_events([]) + + def test_two_watchers(self): + d1 = {} + d2 = {} + with self.watcher() as wid1: + with self.watcher(kind=self.SECOND) as wid2: + self.watch(wid1, d1) + self.watch(wid2, d2) + d1["foo"] = "bar" + d2["hmm"] = "baz" + self.assert_events(["new:foo:bar", "second"]) + + def test_watch_non_dict(self): + with self.watcher() as wid: + with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"): + self.watch(wid, 1) + + def test_watch_out_of_range_watcher_id(self): + d = {} + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"): + self.watch(-1, d) + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"): + self.watch(8, d) # DICT_MAX_WATCHERS = 8 + + def test_watch_unassigned_watcher_id(self): + d = {} + with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"): + self.watch(1, d) + + def test_unwatch_non_dict(self): + with self.watcher() as wid: + with self.assertRaisesRegex(ValueError, r"Cannot watch non-dictionary"): + self.unwatch(wid, 1) + + def test_unwatch_out_of_range_watcher_id(self): + d = {} + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"): + self.unwatch(-1, d) + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"): + self.unwatch(8, d) # DICT_MAX_WATCHERS = 8 + + def test_unwatch_unassigned_watcher_id(self): + d = {} + with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"): + self.unwatch(1, d) + + def test_clear_out_of_range_watcher_id(self): + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID -1"): + self.clear_watcher(-1) + with self.assertRaisesRegex(ValueError, r"Invalid dict watcher ID 8"): + self.clear_watcher(8) # DICT_MAX_WATCHERS = 8 + + def test_clear_unassigned_watcher_id(self): + with self.assertRaisesRegex(ValueError, r"No dict watcher set for ID 1"): + self.clear_watcher(1) + + +class TestTypeWatchers(unittest.TestCase): + # types of watchers testcapimodule can add: + TYPES = 0 # appends modified types to global event list + ERROR = 1 # unconditionally sets and signals a RuntimeException + WRAP = 2 # appends modified type wrapped in list to global event list + + # duplicating the C constant + TYPE_MAX_WATCHERS = 8 + + def add_watcher(self, kind=TYPES): + return _testcapi.add_type_watcher(kind) + + def clear_watcher(self, watcher_id): + _testcapi.clear_type_watcher(watcher_id) + + @contextmanager + def watcher(self, kind=TYPES): + wid = self.add_watcher(kind) + try: + yield wid + finally: + self.clear_watcher(wid) + + def assert_events(self, expected): + actual = _testcapi.get_type_modified_events() + self.assertEqual(actual, expected) + + def watch(self, wid, t): + _testcapi.watch_type(wid, t) + + def unwatch(self, wid, t): + _testcapi.unwatch_type(wid, t) + + def test_watch_type(self): + class C: pass + with self.watcher() as wid: + self.watch(wid, C) + C.foo = "bar" + self.assert_events([C]) + + def test_event_aggregation(self): + class C: pass + with self.watcher() as wid: + self.watch(wid, C) + C.foo = "bar" + C.bar = "baz" + # only one event registered for both modifications + self.assert_events([C]) + + def test_lookup_resets_aggregation(self): + class C: pass + with self.watcher() as wid: + self.watch(wid, C) + C.foo = "bar" + # lookup resets type version tag + self.assertEqual(C.foo, "bar") + C.bar = "baz" + # both events registered + self.assert_events([C, C]) + + def test_unwatch_type(self): + class C: pass + with self.watcher() as wid: + self.watch(wid, C) + C.foo = "bar" + self.assertEqual(C.foo, "bar") + self.assert_events([C]) + self.unwatch(wid, C) + C.bar = "baz" + self.assert_events([C]) + + def test_clear_watcher(self): + class C: pass + # outer watcher is unused, it's just to keep events list alive + with self.watcher() as _: + with self.watcher() as wid: + self.watch(wid, C) + C.foo = "bar" + self.assertEqual(C.foo, "bar") + self.assert_events([C]) + C.bar = "baz" + # Watcher on C has been cleared, no new event + self.assert_events([C]) + + def test_watch_type_subclass(self): + class C: pass + class D(C): pass + with self.watcher() as wid: + self.watch(wid, D) + C.foo = "bar" + self.assert_events([D]) + + def test_error(self): + class C: pass + with self.watcher(kind=self.ERROR) as wid: + self.watch(wid, C) + with catch_unraisable_exception() as cm: + C.foo = "bar" + self.assertIs(cm.unraisable.object, C) + self.assertEqual(str(cm.unraisable.exc_value), "boom!") + self.assert_events([]) + + def test_two_watchers(self): + class C1: pass + class C2: pass + with self.watcher() as wid1: + with self.watcher(kind=self.WRAP) as wid2: + self.assertNotEqual(wid1, wid2) + self.watch(wid1, C1) + self.watch(wid2, C2) + C1.foo = "bar" + C2.hmm = "baz" + self.assert_events([C1, [C2]]) + + def test_watch_non_type(self): + with self.watcher() as wid: + with self.assertRaisesRegex(ValueError, r"Cannot watch non-type"): + self.watch(wid, 1) + + def test_watch_out_of_range_watcher_id(self): + class C: pass + with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"): + self.watch(-1, C) + with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"): + self.watch(self.TYPE_MAX_WATCHERS, C) + + def test_watch_unassigned_watcher_id(self): + class C: pass + with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"): + self.watch(1, C) + + def test_unwatch_non_type(self): + with self.watcher() as wid: + with self.assertRaisesRegex(ValueError, r"Cannot watch non-type"): + self.unwatch(wid, 1) + + def test_unwatch_out_of_range_watcher_id(self): + class C: pass + with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"): + self.unwatch(-1, C) + with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"): + self.unwatch(self.TYPE_MAX_WATCHERS, C) + + def test_unwatch_unassigned_watcher_id(self): + class C: pass + with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"): + self.unwatch(1, C) + + def test_clear_out_of_range_watcher_id(self): + with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID -1"): + self.clear_watcher(-1) + with self.assertRaisesRegex(ValueError, r"Invalid type watcher ID 8"): + self.clear_watcher(self.TYPE_MAX_WATCHERS) + + def test_clear_unassigned_watcher_id(self): + with self.assertRaisesRegex(ValueError, r"No type watcher set for ID 1"): + self.clear_watcher(1) + + def test_no_more_ids_available(self): + contexts = [self.watcher() for i in range(self.TYPE_MAX_WATCHERS)] + with ExitStack() as stack: + for ctx in contexts: + stack.enter_context(ctx) + with self.assertRaisesRegex(RuntimeError, r"no more type watcher IDs"): + self.add_watcher() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_capi/test_structmembers.py b/Lib/test/test_capi/test_structmembers.py new file mode 100644 index 0000000..07d2f62 --- /dev/null +++ b/Lib/test/test_capi/test_structmembers.py @@ -0,0 +1,145 @@ +import unittest +from test.support import import_helper +from test.support import warnings_helper + +# Skip this test if the _testcapi module isn't available. +import_helper.import_module('_testcapi') +from _testcapi import _test_structmembersType, \ + CHAR_MAX, CHAR_MIN, UCHAR_MAX, \ + SHRT_MAX, SHRT_MIN, USHRT_MAX, \ + INT_MAX, INT_MIN, UINT_MAX, \ + LONG_MAX, LONG_MIN, ULONG_MAX, \ + LLONG_MAX, LLONG_MIN, ULLONG_MAX, \ + PY_SSIZE_T_MAX, PY_SSIZE_T_MIN + +ts=_test_structmembersType(False, # T_BOOL + 1, # T_BYTE + 2, # T_UBYTE + 3, # T_SHORT + 4, # T_USHORT + 5, # T_INT + 6, # T_UINT + 7, # T_LONG + 8, # T_ULONG + 23, # T_PYSSIZET + 9.99999,# T_FLOAT + 10.1010101010, # T_DOUBLE + "hi" # T_STRING_INPLACE + ) + +class ReadWriteTests(unittest.TestCase): + + def test_bool(self): + ts.T_BOOL = True + self.assertEqual(ts.T_BOOL, True) + ts.T_BOOL = False + self.assertEqual(ts.T_BOOL, False) + self.assertRaises(TypeError, setattr, ts, 'T_BOOL', 1) + + def test_byte(self): + ts.T_BYTE = CHAR_MAX + self.assertEqual(ts.T_BYTE, CHAR_MAX) + ts.T_BYTE = CHAR_MIN + self.assertEqual(ts.T_BYTE, CHAR_MIN) + ts.T_UBYTE = UCHAR_MAX + self.assertEqual(ts.T_UBYTE, UCHAR_MAX) + + def test_short(self): + ts.T_SHORT = SHRT_MAX + self.assertEqual(ts.T_SHORT, SHRT_MAX) + ts.T_SHORT = SHRT_MIN + self.assertEqual(ts.T_SHORT, SHRT_MIN) + ts.T_USHORT = USHRT_MAX + self.assertEqual(ts.T_USHORT, USHRT_MAX) + + def test_int(self): + ts.T_INT = INT_MAX + self.assertEqual(ts.T_INT, INT_MAX) + ts.T_INT = INT_MIN + self.assertEqual(ts.T_INT, INT_MIN) + ts.T_UINT = UINT_MAX + self.assertEqual(ts.T_UINT, UINT_MAX) + + def test_long(self): + ts.T_LONG = LONG_MAX + self.assertEqual(ts.T_LONG, LONG_MAX) + ts.T_LONG = LONG_MIN + self.assertEqual(ts.T_LONG, LONG_MIN) + ts.T_ULONG = ULONG_MAX + self.assertEqual(ts.T_ULONG, ULONG_MAX) + + def test_py_ssize_t(self): + ts.T_PYSSIZET = PY_SSIZE_T_MAX + self.assertEqual(ts.T_PYSSIZET, PY_SSIZE_T_MAX) + ts.T_PYSSIZET = PY_SSIZE_T_MIN + self.assertEqual(ts.T_PYSSIZET, PY_SSIZE_T_MIN) + + @unittest.skipUnless(hasattr(ts, "T_LONGLONG"), "long long not present") + def test_longlong(self): + ts.T_LONGLONG = LLONG_MAX + self.assertEqual(ts.T_LONGLONG, LLONG_MAX) + ts.T_LONGLONG = LLONG_MIN + self.assertEqual(ts.T_LONGLONG, LLONG_MIN) + + ts.T_ULONGLONG = ULLONG_MAX + self.assertEqual(ts.T_ULONGLONG, ULLONG_MAX) + + ## make sure these will accept a plain int as well as a long + ts.T_LONGLONG = 3 + self.assertEqual(ts.T_LONGLONG, 3) + ts.T_ULONGLONG = 4 + self.assertEqual(ts.T_ULONGLONG, 4) + + def test_bad_assignments(self): + integer_attributes = [ + 'T_BOOL', + 'T_BYTE', 'T_UBYTE', + 'T_SHORT', 'T_USHORT', + 'T_INT', 'T_UINT', + 'T_LONG', 'T_ULONG', + 'T_PYSSIZET' + ] + if hasattr(ts, 'T_LONGLONG'): + integer_attributes.extend(['T_LONGLONG', 'T_ULONGLONG']) + + # issue8014: this produced 'bad argument to internal function' + # internal error + for nonint in None, 3.2j, "full of eels", {}, []: + for attr in integer_attributes: + self.assertRaises(TypeError, setattr, ts, attr, nonint) + + def test_inplace_string(self): + self.assertEqual(ts.T_STRING_INPLACE, "hi") + self.assertRaises(TypeError, setattr, ts, "T_STRING_INPLACE", "s") + self.assertRaises(TypeError, delattr, ts, "T_STRING_INPLACE") + + +class TestWarnings(unittest.TestCase): + + def test_byte_max(self): + with warnings_helper.check_warnings(('', RuntimeWarning)): + ts.T_BYTE = CHAR_MAX+1 + + def test_byte_min(self): + with warnings_helper.check_warnings(('', RuntimeWarning)): + ts.T_BYTE = CHAR_MIN-1 + + def test_ubyte_max(self): + with warnings_helper.check_warnings(('', RuntimeWarning)): + ts.T_UBYTE = UCHAR_MAX+1 + + def test_short_max(self): + with warnings_helper.check_warnings(('', RuntimeWarning)): + ts.T_SHORT = SHRT_MAX+1 + + def test_short_min(self): + with warnings_helper.check_warnings(('', RuntimeWarning)): + ts.T_SHORT = SHRT_MIN-1 + + def test_ushort_max(self): + with warnings_helper.check_warnings(('', RuntimeWarning)): + ts.T_USHORT = USHRT_MAX+1 + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py deleted file mode 100644 index 1d5c7fb..0000000 --- a/Lib/test/test_getargs2.py +++ /dev/null @@ -1,1297 +0,0 @@ -import unittest -import math -import string -import sys -from test import support -from test.support import import_helper -from test.support import warnings_helper -# Skip this test if the _testcapi module isn't available. -_testcapi = import_helper.import_module('_testcapi') -from _testcapi import getargs_keywords, getargs_keyword_only - -# > How about the following counterproposal. This also changes some of -# > the other format codes to be a little more regular. -# > -# > Code C type Range check -# > -# > b unsigned char 0..UCHAR_MAX -# > h signed short SHRT_MIN..SHRT_MAX -# > B unsigned char none ** -# > H unsigned short none ** -# > k * unsigned long none -# > I * unsigned int 0..UINT_MAX -# -# -# > i int INT_MIN..INT_MAX -# > l long LONG_MIN..LONG_MAX -# -# > K * unsigned long long none -# > L long long LLONG_MIN..LLONG_MAX -# -# > Notes: -# > -# > * New format codes. -# > -# > ** Changed from previous "range-and-a-half" to "none"; the -# > range-and-a-half checking wasn't particularly useful. -# -# Plus a C API or two, e.g. PyLong_AsUnsignedLongMask() -> -# unsigned long and PyLong_AsUnsignedLongLongMask() -> unsigned -# long long (if that exists). - -LARGE = 0x7FFFFFFF -VERY_LARGE = 0xFF0000121212121212121242 - -from _testcapi import UCHAR_MAX, USHRT_MAX, UINT_MAX, ULONG_MAX, INT_MAX, \ - INT_MIN, LONG_MIN, LONG_MAX, PY_SSIZE_T_MIN, PY_SSIZE_T_MAX, \ - SHRT_MIN, SHRT_MAX, FLT_MIN, FLT_MAX, DBL_MIN, DBL_MAX - -DBL_MAX_EXP = sys.float_info.max_exp -INF = float('inf') -NAN = float('nan') - -# fake, they are not defined in Python's header files -LLONG_MAX = 2**63-1 -LLONG_MIN = -2**63 -ULLONG_MAX = 2**64-1 - -class Index: - def __index__(self): - return 99 - -class IndexIntSubclass(int): - def __index__(self): - return 99 - -class BadIndex: - def __index__(self): - return 1.0 - -class BadIndex2: - def __index__(self): - return True - -class BadIndex3(int): - def __index__(self): - return True - - -class Int: - def __int__(self): - return 99 - -class IntSubclass(int): - def __int__(self): - return 99 - -class BadInt: - def __int__(self): - return 1.0 - -class BadInt2: - def __int__(self): - return True - -class BadInt3(int): - def __int__(self): - return True - - -class Float: - def __float__(self): - return 4.25 - -class FloatSubclass(float): - pass - -class FloatSubclass2(float): - def __float__(self): - return 4.25 - -class BadFloat: - def __float__(self): - return 687 - -class BadFloat2: - def __float__(self): - return FloatSubclass(4.25) - -class BadFloat3(float): - def __float__(self): - return FloatSubclass(4.25) - - -class Complex: - def __complex__(self): - return 4.25+0.5j - -class ComplexSubclass(complex): - pass - -class ComplexSubclass2(complex): - def __complex__(self): - return 4.25+0.5j - -class BadComplex: - def __complex__(self): - return 1.25 - -class BadComplex2: - def __complex__(self): - return ComplexSubclass(4.25+0.5j) - -class BadComplex3(complex): - def __complex__(self): - return ComplexSubclass(4.25+0.5j) - - -class TupleSubclass(tuple): - pass - -class DictSubclass(dict): - pass - - -class Unsigned_TestCase(unittest.TestCase): - def test_b(self): - from _testcapi import getargs_b - # b returns 'unsigned char', and does range checking (0 ... UCHAR_MAX) - self.assertRaises(TypeError, getargs_b, 3.14) - self.assertEqual(99, getargs_b(Index())) - self.assertEqual(0, getargs_b(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_b, BadIndex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(1, getargs_b(BadIndex2())) - self.assertEqual(0, getargs_b(BadIndex3())) - self.assertRaises(TypeError, getargs_b, Int()) - self.assertEqual(0, getargs_b(IntSubclass())) - self.assertRaises(TypeError, getargs_b, BadInt()) - self.assertRaises(TypeError, getargs_b, BadInt2()) - self.assertEqual(0, getargs_b(BadInt3())) - - self.assertRaises(OverflowError, getargs_b, -1) - self.assertEqual(0, getargs_b(0)) - self.assertEqual(UCHAR_MAX, getargs_b(UCHAR_MAX)) - self.assertRaises(OverflowError, getargs_b, UCHAR_MAX + 1) - - self.assertEqual(42, getargs_b(42)) - self.assertRaises(OverflowError, getargs_b, VERY_LARGE) - - def test_B(self): - from _testcapi import getargs_B - # B returns 'unsigned char', no range checking - self.assertRaises(TypeError, getargs_B, 3.14) - self.assertEqual(99, getargs_B(Index())) - self.assertEqual(0, getargs_B(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_B, BadIndex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(1, getargs_B(BadIndex2())) - self.assertEqual(0, getargs_B(BadIndex3())) - self.assertRaises(TypeError, getargs_B, Int()) - self.assertEqual(0, getargs_B(IntSubclass())) - self.assertRaises(TypeError, getargs_B, BadInt()) - self.assertRaises(TypeError, getargs_B, BadInt2()) - self.assertEqual(0, getargs_B(BadInt3())) - - self.assertEqual(UCHAR_MAX, getargs_B(-1)) - self.assertEqual(0, getargs_B(0)) - self.assertEqual(UCHAR_MAX, getargs_B(UCHAR_MAX)) - self.assertEqual(0, getargs_B(UCHAR_MAX+1)) - - self.assertEqual(42, getargs_B(42)) - self.assertEqual(UCHAR_MAX & VERY_LARGE, getargs_B(VERY_LARGE)) - - def test_H(self): - from _testcapi import getargs_H - # H returns 'unsigned short', no range checking - self.assertRaises(TypeError, getargs_H, 3.14) - self.assertEqual(99, getargs_H(Index())) - self.assertEqual(0, getargs_H(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_H, BadIndex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(1, getargs_H(BadIndex2())) - self.assertEqual(0, getargs_H(BadIndex3())) - self.assertRaises(TypeError, getargs_H, Int()) - self.assertEqual(0, getargs_H(IntSubclass())) - self.assertRaises(TypeError, getargs_H, BadInt()) - self.assertRaises(TypeError, getargs_H, BadInt2()) - self.assertEqual(0, getargs_H(BadInt3())) - - self.assertEqual(USHRT_MAX, getargs_H(-1)) - self.assertEqual(0, getargs_H(0)) - self.assertEqual(USHRT_MAX, getargs_H(USHRT_MAX)) - self.assertEqual(0, getargs_H(USHRT_MAX+1)) - - self.assertEqual(42, getargs_H(42)) - - self.assertEqual(VERY_LARGE & USHRT_MAX, getargs_H(VERY_LARGE)) - - def test_I(self): - from _testcapi import getargs_I - # I returns 'unsigned int', no range checking - self.assertRaises(TypeError, getargs_I, 3.14) - self.assertEqual(99, getargs_I(Index())) - self.assertEqual(0, getargs_I(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_I, BadIndex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(1, getargs_I(BadIndex2())) - self.assertEqual(0, getargs_I(BadIndex3())) - self.assertRaises(TypeError, getargs_I, Int()) - self.assertEqual(0, getargs_I(IntSubclass())) - self.assertRaises(TypeError, getargs_I, BadInt()) - self.assertRaises(TypeError, getargs_I, BadInt2()) - self.assertEqual(0, getargs_I(BadInt3())) - - self.assertEqual(UINT_MAX, getargs_I(-1)) - self.assertEqual(0, getargs_I(0)) - self.assertEqual(UINT_MAX, getargs_I(UINT_MAX)) - self.assertEqual(0, getargs_I(UINT_MAX+1)) - - self.assertEqual(42, getargs_I(42)) - - self.assertEqual(VERY_LARGE & UINT_MAX, getargs_I(VERY_LARGE)) - - def test_k(self): - from _testcapi import getargs_k - # k returns 'unsigned long', no range checking - # it does not accept float, or instances with __int__ - self.assertRaises(TypeError, getargs_k, 3.14) - self.assertRaises(TypeError, getargs_k, Index()) - self.assertEqual(0, getargs_k(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_k, BadIndex()) - self.assertRaises(TypeError, getargs_k, BadIndex2()) - self.assertEqual(0, getargs_k(BadIndex3())) - self.assertRaises(TypeError, getargs_k, Int()) - self.assertEqual(0, getargs_k(IntSubclass())) - self.assertRaises(TypeError, getargs_k, BadInt()) - self.assertRaises(TypeError, getargs_k, BadInt2()) - self.assertEqual(0, getargs_k(BadInt3())) - - self.assertEqual(ULONG_MAX, getargs_k(-1)) - self.assertEqual(0, getargs_k(0)) - self.assertEqual(ULONG_MAX, getargs_k(ULONG_MAX)) - self.assertEqual(0, getargs_k(ULONG_MAX+1)) - - self.assertEqual(42, getargs_k(42)) - - self.assertEqual(VERY_LARGE & ULONG_MAX, getargs_k(VERY_LARGE)) - -class Signed_TestCase(unittest.TestCase): - def test_h(self): - from _testcapi import getargs_h - # h returns 'short', and does range checking (SHRT_MIN ... SHRT_MAX) - self.assertRaises(TypeError, getargs_h, 3.14) - self.assertEqual(99, getargs_h(Index())) - self.assertEqual(0, getargs_h(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_h, BadIndex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(1, getargs_h(BadIndex2())) - self.assertEqual(0, getargs_h(BadIndex3())) - self.assertRaises(TypeError, getargs_h, Int()) - self.assertEqual(0, getargs_h(IntSubclass())) - self.assertRaises(TypeError, getargs_h, BadInt()) - self.assertRaises(TypeError, getargs_h, BadInt2()) - self.assertEqual(0, getargs_h(BadInt3())) - - self.assertRaises(OverflowError, getargs_h, SHRT_MIN-1) - self.assertEqual(SHRT_MIN, getargs_h(SHRT_MIN)) - self.assertEqual(SHRT_MAX, getargs_h(SHRT_MAX)) - self.assertRaises(OverflowError, getargs_h, SHRT_MAX+1) - - self.assertEqual(42, getargs_h(42)) - self.assertRaises(OverflowError, getargs_h, VERY_LARGE) - - def test_i(self): - from _testcapi import getargs_i - # i returns 'int', and does range checking (INT_MIN ... INT_MAX) - self.assertRaises(TypeError, getargs_i, 3.14) - self.assertEqual(99, getargs_i(Index())) - self.assertEqual(0, getargs_i(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_i, BadIndex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(1, getargs_i(BadIndex2())) - self.assertEqual(0, getargs_i(BadIndex3())) - self.assertRaises(TypeError, getargs_i, Int()) - self.assertEqual(0, getargs_i(IntSubclass())) - self.assertRaises(TypeError, getargs_i, BadInt()) - self.assertRaises(TypeError, getargs_i, BadInt2()) - self.assertEqual(0, getargs_i(BadInt3())) - - self.assertRaises(OverflowError, getargs_i, INT_MIN-1) - self.assertEqual(INT_MIN, getargs_i(INT_MIN)) - self.assertEqual(INT_MAX, getargs_i(INT_MAX)) - self.assertRaises(OverflowError, getargs_i, INT_MAX+1) - - self.assertEqual(42, getargs_i(42)) - self.assertRaises(OverflowError, getargs_i, VERY_LARGE) - - def test_l(self): - from _testcapi import getargs_l - # l returns 'long', and does range checking (LONG_MIN ... LONG_MAX) - self.assertRaises(TypeError, getargs_l, 3.14) - self.assertEqual(99, getargs_l(Index())) - self.assertEqual(0, getargs_l(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_l, BadIndex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(1, getargs_l(BadIndex2())) - self.assertEqual(0, getargs_l(BadIndex3())) - self.assertRaises(TypeError, getargs_l, Int()) - self.assertEqual(0, getargs_l(IntSubclass())) - self.assertRaises(TypeError, getargs_l, BadInt()) - self.assertRaises(TypeError, getargs_l, BadInt2()) - self.assertEqual(0, getargs_l(BadInt3())) - - self.assertRaises(OverflowError, getargs_l, LONG_MIN-1) - self.assertEqual(LONG_MIN, getargs_l(LONG_MIN)) - self.assertEqual(LONG_MAX, getargs_l(LONG_MAX)) - self.assertRaises(OverflowError, getargs_l, LONG_MAX+1) - - self.assertEqual(42, getargs_l(42)) - self.assertRaises(OverflowError, getargs_l, VERY_LARGE) - - def test_n(self): - from _testcapi import getargs_n - # n returns 'Py_ssize_t', and does range checking - # (PY_SSIZE_T_MIN ... PY_SSIZE_T_MAX) - self.assertRaises(TypeError, getargs_n, 3.14) - self.assertEqual(99, getargs_n(Index())) - self.assertEqual(0, getargs_n(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_n, BadIndex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(1, getargs_n(BadIndex2())) - self.assertEqual(0, getargs_n(BadIndex3())) - self.assertRaises(TypeError, getargs_n, Int()) - self.assertEqual(0, getargs_n(IntSubclass())) - self.assertRaises(TypeError, getargs_n, BadInt()) - self.assertRaises(TypeError, getargs_n, BadInt2()) - self.assertEqual(0, getargs_n(BadInt3())) - - self.assertRaises(OverflowError, getargs_n, PY_SSIZE_T_MIN-1) - self.assertEqual(PY_SSIZE_T_MIN, getargs_n(PY_SSIZE_T_MIN)) - self.assertEqual(PY_SSIZE_T_MAX, getargs_n(PY_SSIZE_T_MAX)) - self.assertRaises(OverflowError, getargs_n, PY_SSIZE_T_MAX+1) - - self.assertEqual(42, getargs_n(42)) - self.assertRaises(OverflowError, getargs_n, VERY_LARGE) - - -class LongLong_TestCase(unittest.TestCase): - def test_L(self): - from _testcapi import getargs_L - # L returns 'long long', and does range checking (LLONG_MIN - # ... LLONG_MAX) - self.assertRaises(TypeError, getargs_L, 3.14) - self.assertRaises(TypeError, getargs_L, "Hello") - self.assertEqual(99, getargs_L(Index())) - self.assertEqual(0, getargs_L(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_L, BadIndex()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(1, getargs_L(BadIndex2())) - self.assertEqual(0, getargs_L(BadIndex3())) - self.assertRaises(TypeError, getargs_L, Int()) - self.assertEqual(0, getargs_L(IntSubclass())) - self.assertRaises(TypeError, getargs_L, BadInt()) - self.assertRaises(TypeError, getargs_L, BadInt2()) - self.assertEqual(0, getargs_L(BadInt3())) - - self.assertRaises(OverflowError, getargs_L, LLONG_MIN-1) - self.assertEqual(LLONG_MIN, getargs_L(LLONG_MIN)) - self.assertEqual(LLONG_MAX, getargs_L(LLONG_MAX)) - self.assertRaises(OverflowError, getargs_L, LLONG_MAX+1) - - self.assertEqual(42, getargs_L(42)) - self.assertRaises(OverflowError, getargs_L, VERY_LARGE) - - def test_K(self): - from _testcapi import getargs_K - # K return 'unsigned long long', no range checking - self.assertRaises(TypeError, getargs_K, 3.14) - self.assertRaises(TypeError, getargs_K, Index()) - self.assertEqual(0, getargs_K(IndexIntSubclass())) - self.assertRaises(TypeError, getargs_K, BadIndex()) - self.assertRaises(TypeError, getargs_K, BadIndex2()) - self.assertEqual(0, getargs_K(BadIndex3())) - self.assertRaises(TypeError, getargs_K, Int()) - self.assertEqual(0, getargs_K(IntSubclass())) - self.assertRaises(TypeError, getargs_K, BadInt()) - self.assertRaises(TypeError, getargs_K, BadInt2()) - self.assertEqual(0, getargs_K(BadInt3())) - - self.assertEqual(ULLONG_MAX, getargs_K(ULLONG_MAX)) - self.assertEqual(0, getargs_K(0)) - self.assertEqual(0, getargs_K(ULLONG_MAX+1)) - - self.assertEqual(42, getargs_K(42)) - - self.assertEqual(VERY_LARGE & ULLONG_MAX, getargs_K(VERY_LARGE)) - - -class Float_TestCase(unittest.TestCase): - def assertEqualWithSign(self, actual, expected): - self.assertEqual(actual, expected) - self.assertEqual(math.copysign(1, actual), math.copysign(1, expected)) - - def test_f(self): - from _testcapi import getargs_f - self.assertEqual(getargs_f(4.25), 4.25) - self.assertEqual(getargs_f(4), 4.0) - self.assertRaises(TypeError, getargs_f, 4.25+0j) - self.assertEqual(getargs_f(Float()), 4.25) - self.assertEqual(getargs_f(FloatSubclass(7.5)), 7.5) - self.assertEqual(getargs_f(FloatSubclass2(7.5)), 7.5) - self.assertRaises(TypeError, getargs_f, BadFloat()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_f(BadFloat2()), 4.25) - self.assertEqual(getargs_f(BadFloat3(7.5)), 7.5) - self.assertEqual(getargs_f(Index()), 99.0) - self.assertRaises(TypeError, getargs_f, Int()) - - for x in (FLT_MIN, -FLT_MIN, FLT_MAX, -FLT_MAX, INF, -INF): - self.assertEqual(getargs_f(x), x) - if FLT_MAX < DBL_MAX: - self.assertEqual(getargs_f(DBL_MAX), INF) - self.assertEqual(getargs_f(-DBL_MAX), -INF) - if FLT_MIN > DBL_MIN: - self.assertEqualWithSign(getargs_f(DBL_MIN), 0.0) - self.assertEqualWithSign(getargs_f(-DBL_MIN), -0.0) - self.assertEqualWithSign(getargs_f(0.0), 0.0) - self.assertEqualWithSign(getargs_f(-0.0), -0.0) - r = getargs_f(NAN) - self.assertNotEqual(r, r) - - @support.requires_IEEE_754 - def test_f_rounding(self): - from _testcapi import getargs_f - self.assertEqual(getargs_f(3.40282356e38), FLT_MAX) - self.assertEqual(getargs_f(-3.40282356e38), -FLT_MAX) - - def test_d(self): - from _testcapi import getargs_d - self.assertEqual(getargs_d(4.25), 4.25) - self.assertEqual(getargs_d(4), 4.0) - self.assertRaises(TypeError, getargs_d, 4.25+0j) - self.assertEqual(getargs_d(Float()), 4.25) - self.assertEqual(getargs_d(FloatSubclass(7.5)), 7.5) - self.assertEqual(getargs_d(FloatSubclass2(7.5)), 7.5) - self.assertRaises(TypeError, getargs_d, BadFloat()) - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_d(BadFloat2()), 4.25) - self.assertEqual(getargs_d(BadFloat3(7.5)), 7.5) - self.assertEqual(getargs_d(Index()), 99.0) - self.assertRaises(TypeError, getargs_d, Int()) - - for x in (DBL_MIN, -DBL_MIN, DBL_MAX, -DBL_MAX, INF, -INF): - self.assertEqual(getargs_d(x), x) - self.assertRaises(OverflowError, getargs_d, 1< 1 - self.assertEqual(getargs_c(b'a'), 97) - self.assertEqual(getargs_c(bytearray(b'a')), 97) - self.assertRaises(TypeError, getargs_c, memoryview(b'a')) - self.assertRaises(TypeError, getargs_c, 's') - self.assertRaises(TypeError, getargs_c, 97) - self.assertRaises(TypeError, getargs_c, None) - - def test_y(self): - from _testcapi import getargs_y - self.assertRaises(TypeError, getargs_y, 'abc\xe9') - self.assertEqual(getargs_y(b'bytes'), b'bytes') - self.assertRaises(ValueError, getargs_y, b'nul:\0') - self.assertRaises(TypeError, getargs_y, bytearray(b'bytearray')) - self.assertRaises(TypeError, getargs_y, memoryview(b'memoryview')) - self.assertRaises(TypeError, getargs_y, None) - - def test_y_star(self): - from _testcapi import getargs_y_star - self.assertRaises(TypeError, getargs_y_star, 'abc\xe9') - self.assertEqual(getargs_y_star(b'bytes'), b'bytes') - self.assertEqual(getargs_y_star(b'nul:\0'), b'nul:\0') - self.assertEqual(getargs_y_star(bytearray(b'bytearray')), b'bytearray') - self.assertEqual(getargs_y_star(memoryview(b'memoryview')), b'memoryview') - self.assertRaises(TypeError, getargs_y_star, None) - - def test_y_hash(self): - from _testcapi import getargs_y_hash - self.assertRaises(TypeError, getargs_y_hash, 'abc\xe9') - self.assertEqual(getargs_y_hash(b'bytes'), b'bytes') - self.assertEqual(getargs_y_hash(b'nul:\0'), b'nul:\0') - self.assertRaises(TypeError, getargs_y_hash, bytearray(b'bytearray')) - self.assertRaises(TypeError, getargs_y_hash, memoryview(b'memoryview')) - self.assertRaises(TypeError, getargs_y_hash, None) - - def test_w_star(self): - # getargs_w_star() modifies first and last byte - from _testcapi import getargs_w_star - self.assertRaises(TypeError, getargs_w_star, 'abc\xe9') - self.assertRaises(TypeError, getargs_w_star, b'bytes') - self.assertRaises(TypeError, getargs_w_star, b'nul:\0') - self.assertRaises(TypeError, getargs_w_star, memoryview(b'bytes')) - buf = bytearray(b'bytearray') - self.assertEqual(getargs_w_star(buf), b'[ytearra]') - self.assertEqual(buf, bytearray(b'[ytearra]')) - buf = bytearray(b'memoryview') - self.assertEqual(getargs_w_star(memoryview(buf)), b'[emoryvie]') - self.assertEqual(buf, bytearray(b'[emoryvie]')) - self.assertRaises(TypeError, getargs_w_star, None) - - -class String_TestCase(unittest.TestCase): - def test_C(self): - from _testcapi import getargs_C - self.assertRaises(TypeError, getargs_C, 'abc') # len > 1 - self.assertEqual(getargs_C('a'), 97) - self.assertEqual(getargs_C('\u20ac'), 0x20ac) - self.assertEqual(getargs_C('\U0001f40d'), 0x1f40d) - self.assertRaises(TypeError, getargs_C, b'a') - self.assertRaises(TypeError, getargs_C, bytearray(b'a')) - self.assertRaises(TypeError, getargs_C, memoryview(b'a')) - self.assertRaises(TypeError, getargs_C, 97) - self.assertRaises(TypeError, getargs_C, None) - - def test_s(self): - from _testcapi import getargs_s - self.assertEqual(getargs_s('abc\xe9'), b'abc\xc3\xa9') - self.assertRaises(ValueError, getargs_s, 'nul:\0') - self.assertRaises(TypeError, getargs_s, b'bytes') - self.assertRaises(TypeError, getargs_s, bytearray(b'bytearray')) - self.assertRaises(TypeError, getargs_s, memoryview(b'memoryview')) - self.assertRaises(TypeError, getargs_s, None) - - def test_s_star(self): - from _testcapi import getargs_s_star - self.assertEqual(getargs_s_star('abc\xe9'), b'abc\xc3\xa9') - self.assertEqual(getargs_s_star('nul:\0'), b'nul:\0') - self.assertEqual(getargs_s_star(b'bytes'), b'bytes') - self.assertEqual(getargs_s_star(bytearray(b'bytearray')), b'bytearray') - self.assertEqual(getargs_s_star(memoryview(b'memoryview')), b'memoryview') - self.assertRaises(TypeError, getargs_s_star, None) - - def test_s_hash(self): - from _testcapi import getargs_s_hash - self.assertEqual(getargs_s_hash('abc\xe9'), b'abc\xc3\xa9') - self.assertEqual(getargs_s_hash('nul:\0'), b'nul:\0') - self.assertEqual(getargs_s_hash(b'bytes'), b'bytes') - self.assertRaises(TypeError, getargs_s_hash, bytearray(b'bytearray')) - self.assertRaises(TypeError, getargs_s_hash, memoryview(b'memoryview')) - self.assertRaises(TypeError, getargs_s_hash, None) - - def test_s_hash_int(self): - # "s#" without PY_SSIZE_T_CLEAN defined. - from _testcapi import getargs_s_hash_int - from _testcapi import getargs_s_hash_int2 - buf = bytearray([1, 2]) - self.assertRaises(SystemError, getargs_s_hash_int, buf, "abc") - self.assertRaises(SystemError, getargs_s_hash_int, buf, x=42) - self.assertRaises(SystemError, getargs_s_hash_int, buf, x="abc") - self.assertRaises(SystemError, getargs_s_hash_int2, buf, ("abc",)) - self.assertRaises(SystemError, getargs_s_hash_int2, buf, x=42) - self.assertRaises(SystemError, getargs_s_hash_int2, buf, x="abc") - buf.append(3) # still mutable -- not locked by a buffer export - # getargs_s_hash_int(buf) may not raise SystemError because skipitem() - # is not called. But it is an implementation detail. - # getargs_s_hash_int(buf) - # getargs_s_hash_int2(buf) - - def test_z(self): - from _testcapi import getargs_z - self.assertEqual(getargs_z('abc\xe9'), b'abc\xc3\xa9') - self.assertRaises(ValueError, getargs_z, 'nul:\0') - self.assertRaises(TypeError, getargs_z, b'bytes') - self.assertRaises(TypeError, getargs_z, bytearray(b'bytearray')) - self.assertRaises(TypeError, getargs_z, memoryview(b'memoryview')) - self.assertIsNone(getargs_z(None)) - - def test_z_star(self): - from _testcapi import getargs_z_star - self.assertEqual(getargs_z_star('abc\xe9'), b'abc\xc3\xa9') - self.assertEqual(getargs_z_star('nul:\0'), b'nul:\0') - self.assertEqual(getargs_z_star(b'bytes'), b'bytes') - self.assertEqual(getargs_z_star(bytearray(b'bytearray')), b'bytearray') - self.assertEqual(getargs_z_star(memoryview(b'memoryview')), b'memoryview') - self.assertIsNone(getargs_z_star(None)) - - def test_z_hash(self): - from _testcapi import getargs_z_hash - self.assertEqual(getargs_z_hash('abc\xe9'), b'abc\xc3\xa9') - self.assertEqual(getargs_z_hash('nul:\0'), b'nul:\0') - self.assertEqual(getargs_z_hash(b'bytes'), b'bytes') - self.assertRaises(TypeError, getargs_z_hash, bytearray(b'bytearray')) - self.assertRaises(TypeError, getargs_z_hash, memoryview(b'memoryview')) - self.assertIsNone(getargs_z_hash(None)) - - def test_es(self): - from _testcapi import getargs_es - self.assertEqual(getargs_es('abc\xe9'), b'abc\xc3\xa9') - self.assertEqual(getargs_es('abc\xe9', 'latin1'), b'abc\xe9') - self.assertRaises(UnicodeEncodeError, getargs_es, 'abc\xe9', 'ascii') - self.assertRaises(LookupError, getargs_es, 'abc\xe9', 'spam') - self.assertRaises(TypeError, getargs_es, b'bytes', 'latin1') - self.assertRaises(TypeError, getargs_es, bytearray(b'bytearray'), 'latin1') - self.assertRaises(TypeError, getargs_es, memoryview(b'memoryview'), 'latin1') - self.assertRaises(TypeError, getargs_es, None, 'latin1') - self.assertRaises(TypeError, getargs_es, 'nul:\0', 'latin1') - - def test_et(self): - from _testcapi import getargs_et - self.assertEqual(getargs_et('abc\xe9'), b'abc\xc3\xa9') - self.assertEqual(getargs_et('abc\xe9', 'latin1'), b'abc\xe9') - self.assertRaises(UnicodeEncodeError, getargs_et, 'abc\xe9', 'ascii') - self.assertRaises(LookupError, getargs_et, 'abc\xe9', 'spam') - self.assertEqual(getargs_et(b'bytes', 'latin1'), b'bytes') - self.assertEqual(getargs_et(bytearray(b'bytearray'), 'latin1'), b'bytearray') - self.assertRaises(TypeError, getargs_et, memoryview(b'memoryview'), 'latin1') - self.assertRaises(TypeError, getargs_et, None, 'latin1') - self.assertRaises(TypeError, getargs_et, 'nul:\0', 'latin1') - self.assertRaises(TypeError, getargs_et, b'nul:\0', 'latin1') - self.assertRaises(TypeError, getargs_et, bytearray(b'nul:\0'), 'latin1') - - def test_es_hash(self): - from _testcapi import getargs_es_hash - self.assertEqual(getargs_es_hash('abc\xe9'), b'abc\xc3\xa9') - self.assertEqual(getargs_es_hash('abc\xe9', 'latin1'), b'abc\xe9') - self.assertRaises(UnicodeEncodeError, getargs_es_hash, 'abc\xe9', 'ascii') - self.assertRaises(LookupError, getargs_es_hash, 'abc\xe9', 'spam') - self.assertRaises(TypeError, getargs_es_hash, b'bytes', 'latin1') - self.assertRaises(TypeError, getargs_es_hash, bytearray(b'bytearray'), 'latin1') - self.assertRaises(TypeError, getargs_es_hash, memoryview(b'memoryview'), 'latin1') - self.assertRaises(TypeError, getargs_es_hash, None, 'latin1') - self.assertEqual(getargs_es_hash('nul:\0', 'latin1'), b'nul:\0') - - buf = bytearray(b'x'*8) - self.assertEqual(getargs_es_hash('abc\xe9', 'latin1', buf), b'abc\xe9') - self.assertEqual(buf, bytearray(b'abc\xe9\x00xxx')) - buf = bytearray(b'x'*5) - self.assertEqual(getargs_es_hash('abc\xe9', 'latin1', buf), b'abc\xe9') - self.assertEqual(buf, bytearray(b'abc\xe9\x00')) - buf = bytearray(b'x'*4) - self.assertRaises(ValueError, getargs_es_hash, 'abc\xe9', 'latin1', buf) - self.assertEqual(buf, bytearray(b'x'*4)) - buf = bytearray() - self.assertRaises(ValueError, getargs_es_hash, 'abc\xe9', 'latin1', buf) - - def test_et_hash(self): - from _testcapi import getargs_et_hash - self.assertEqual(getargs_et_hash('abc\xe9'), b'abc\xc3\xa9') - self.assertEqual(getargs_et_hash('abc\xe9', 'latin1'), b'abc\xe9') - self.assertRaises(UnicodeEncodeError, getargs_et_hash, 'abc\xe9', 'ascii') - self.assertRaises(LookupError, getargs_et_hash, 'abc\xe9', 'spam') - self.assertEqual(getargs_et_hash(b'bytes', 'latin1'), b'bytes') - self.assertEqual(getargs_et_hash(bytearray(b'bytearray'), 'latin1'), b'bytearray') - self.assertRaises(TypeError, getargs_et_hash, memoryview(b'memoryview'), 'latin1') - self.assertRaises(TypeError, getargs_et_hash, None, 'latin1') - self.assertEqual(getargs_et_hash('nul:\0', 'latin1'), b'nul:\0') - self.assertEqual(getargs_et_hash(b'nul:\0', 'latin1'), b'nul:\0') - self.assertEqual(getargs_et_hash(bytearray(b'nul:\0'), 'latin1'), b'nul:\0') - - buf = bytearray(b'x'*8) - self.assertEqual(getargs_et_hash('abc\xe9', 'latin1', buf), b'abc\xe9') - self.assertEqual(buf, bytearray(b'abc\xe9\x00xxx')) - buf = bytearray(b'x'*5) - self.assertEqual(getargs_et_hash('abc\xe9', 'latin1', buf), b'abc\xe9') - self.assertEqual(buf, bytearray(b'abc\xe9\x00')) - buf = bytearray(b'x'*4) - self.assertRaises(ValueError, getargs_et_hash, 'abc\xe9', 'latin1', buf) - self.assertEqual(buf, bytearray(b'x'*4)) - buf = bytearray() - self.assertRaises(ValueError, getargs_et_hash, 'abc\xe9', 'latin1', buf) - - @support.requires_legacy_unicode_capi - def test_u(self): - from _testcapi import getargs_u - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_u('abc\xe9'), 'abc\xe9') - with self.assertWarns(DeprecationWarning): - self.assertRaises(ValueError, getargs_u, 'nul:\0') - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_u, b'bytes') - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_u, bytearray(b'bytearray')) - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_u, memoryview(b'memoryview')) - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_u, None) - - @support.requires_legacy_unicode_capi - def test_u_hash(self): - from _testcapi import getargs_u_hash - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_u_hash('abc\xe9'), 'abc\xe9') - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_u_hash('nul:\0'), 'nul:\0') - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_u_hash, b'bytes') - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_u_hash, bytearray(b'bytearray')) - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_u_hash, memoryview(b'memoryview')) - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_u_hash, None) - - @support.requires_legacy_unicode_capi - def test_Z(self): - from _testcapi import getargs_Z - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_Z('abc\xe9'), 'abc\xe9') - with self.assertWarns(DeprecationWarning): - self.assertRaises(ValueError, getargs_Z, 'nul:\0') - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_Z, b'bytes') - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_Z, bytearray(b'bytearray')) - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_Z, memoryview(b'memoryview')) - with self.assertWarns(DeprecationWarning): - self.assertIsNone(getargs_Z(None)) - - @support.requires_legacy_unicode_capi - def test_Z_hash(self): - from _testcapi import getargs_Z_hash - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_Z_hash('abc\xe9'), 'abc\xe9') - with self.assertWarns(DeprecationWarning): - self.assertEqual(getargs_Z_hash('nul:\0'), 'nul:\0') - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_Z_hash, b'bytes') - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_Z_hash, bytearray(b'bytearray')) - with self.assertWarns(DeprecationWarning): - self.assertRaises(TypeError, getargs_Z_hash, memoryview(b'memoryview')) - with self.assertWarns(DeprecationWarning): - self.assertIsNone(getargs_Z_hash(None)) - - -class Object_TestCase(unittest.TestCase): - def test_S(self): - from _testcapi import getargs_S - obj = b'bytes' - self.assertIs(getargs_S(obj), obj) - self.assertRaises(TypeError, getargs_S, bytearray(b'bytearray')) - self.assertRaises(TypeError, getargs_S, 'str') - self.assertRaises(TypeError, getargs_S, None) - self.assertRaises(TypeError, getargs_S, memoryview(obj)) - - def test_Y(self): - from _testcapi import getargs_Y - obj = bytearray(b'bytearray') - self.assertIs(getargs_Y(obj), obj) - self.assertRaises(TypeError, getargs_Y, b'bytes') - self.assertRaises(TypeError, getargs_Y, 'str') - self.assertRaises(TypeError, getargs_Y, None) - self.assertRaises(TypeError, getargs_Y, memoryview(obj)) - - def test_U(self): - from _testcapi import getargs_U - obj = 'str' - self.assertIs(getargs_U(obj), obj) - self.assertRaises(TypeError, getargs_U, b'bytes') - self.assertRaises(TypeError, getargs_U, bytearray(b'bytearray')) - self.assertRaises(TypeError, getargs_U, None) - - -# Bug #6012 -class Test6012(unittest.TestCase): - def test(self): - self.assertEqual(_testcapi.argparsing("Hello", "World"), 1) - - -class SkipitemTest(unittest.TestCase): - - # u, and Z raises DeprecationWarning - @warnings_helper.ignore_warnings(category=DeprecationWarning) - def test_skipitem(self): - """ - If this test failed, you probably added a new "format unit" - in Python/getargs.c, but neglected to update our poor friend - skipitem() in the same file. (If so, shame on you!) - - With a few exceptions**, this function brute-force tests all - printable ASCII*** characters (32 to 126 inclusive) as format units, - checking to see that PyArg_ParseTupleAndKeywords() return consistent - errors both when the unit is attempted to be used and when it is - skipped. If the format unit doesn't exist, we'll get one of two - specific error messages (one for used, one for skipped); if it does - exist we *won't* get that error--we'll get either no error or some - other error. If we get the specific "does not exist" error for one - test and not for the other, there's a mismatch, and the test fails. - - ** Some format units have special funny semantics and it would - be difficult to accommodate them here. Since these are all - well-established and properly skipped in skipitem() we can - get away with not testing them--this test is really intended - to catch *new* format units. - - *** Python C source files must be ASCII. Therefore it's impossible - to have non-ASCII format units. - - """ - empty_tuple = () - tuple_1 = (0,) - dict_b = {'b':1} - keywords = ["a", "b"] - - for i in range(32, 127): - c = chr(i) - - # skip parentheses, the error reporting is inconsistent about them - # skip 'e', it's always a two-character code - # skip '|' and '$', they don't represent arguments anyway - if c in '()e|$': - continue - - # test the format unit when not skipped - format = c + "i" - try: - _testcapi.parse_tuple_and_keywords(tuple_1, dict_b, - format, keywords) - when_not_skipped = False - except SystemError as e: - s = "argument 1 (impossible)" - when_not_skipped = (str(e) == s) - except TypeError: - when_not_skipped = False - - # test the format unit when skipped - optional_format = "|" + format - try: - _testcapi.parse_tuple_and_keywords(empty_tuple, dict_b, - optional_format, keywords) - when_skipped = False - except SystemError as e: - s = "impossible: '{}'".format(format) - when_skipped = (str(e) == s) - - message = ("test_skipitem_parity: " - "detected mismatch between convertsimple and skipitem " - "for format unit '{}' ({}), not skipped {}, skipped {}".format( - c, i, when_skipped, when_not_skipped)) - self.assertIs(when_skipped, when_not_skipped, message) - - def test_skipitem_with_suffix(self): - parse = _testcapi.parse_tuple_and_keywords - empty_tuple = () - tuple_1 = (0,) - dict_b = {'b':1} - keywords = ["a", "b"] - - supported = ('s#', 's*', 'z#', 'z*', 'y#', 'y*', 'w#', 'w*') - for c in string.ascii_letters: - for c2 in '#*': - f = c + c2 - with self.subTest(format=f): - optional_format = "|" + f + "i" - if f in supported: - parse(empty_tuple, dict_b, optional_format, keywords) - else: - with self.assertRaisesRegex(SystemError, - 'impossible'): - parse(empty_tuple, dict_b, optional_format, keywords) - - for c in map(chr, range(32, 128)): - f = 'e' + c - optional_format = "|" + f + "i" - with self.subTest(format=f): - if c in 'st': - parse(empty_tuple, dict_b, optional_format, keywords) - else: - with self.assertRaisesRegex(SystemError, - 'impossible'): - parse(empty_tuple, dict_b, optional_format, keywords) - - -class ParseTupleAndKeywords_Test(unittest.TestCase): - - def test_parse_tuple_and_keywords(self): - # Test handling errors in the parse_tuple_and_keywords helper itself - self.assertRaises(TypeError, _testcapi.parse_tuple_and_keywords, - (), {}, 42, []) - self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, - (), {}, '', 42) - self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, - (), {}, '', [''] * 42) - self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, - (), {}, '', [42]) - - def test_bad_use(self): - # Test handling invalid format and keywords in - # PyArg_ParseTupleAndKeywords() - self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, - (1,), {}, '||O', ['a']) - self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, - (1, 2), {}, '|O|O', ['a', 'b']) - self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, - (), {'a': 1}, '$$O', ['a']) - self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, - (), {'a': 1, 'b': 2}, '$O$O', ['a', 'b']) - self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, - (), {'a': 1}, '$|O', ['a']) - self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, - (), {'a': 1, 'b': 2}, '$O|O', ['a', 'b']) - self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, - (1,), {}, '|O', ['a', 'b']) - self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, - (1,), {}, '|OO', ['a']) - self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, - (), {}, '|$O', ['']) - self.assertRaises(SystemError, _testcapi.parse_tuple_and_keywords, - (), {}, '|OO', ['a', '']) - - def test_positional_only(self): - parse = _testcapi.parse_tuple_and_keywords - - parse((1, 2, 3), {}, 'OOO', ['', '', 'a']) - parse((1, 2), {'a': 3}, 'OOO', ['', '', 'a']) - with self.assertRaisesRegex(TypeError, - r'function takes at least 2 positional arguments \(1 given\)'): - parse((1,), {'a': 3}, 'OOO', ['', '', 'a']) - parse((1,), {}, 'O|OO', ['', '', 'a']) - with self.assertRaisesRegex(TypeError, - r'function takes at least 1 positional argument \(0 given\)'): - parse((), {}, 'O|OO', ['', '', 'a']) - parse((1, 2), {'a': 3}, 'OO$O', ['', '', 'a']) - with self.assertRaisesRegex(TypeError, - r'function takes exactly 2 positional arguments \(1 given\)'): - parse((1,), {'a': 3}, 'OO$O', ['', '', 'a']) - parse((1,), {}, 'O|O$O', ['', '', 'a']) - with self.assertRaisesRegex(TypeError, - r'function takes at least 1 positional argument \(0 given\)'): - parse((), {}, 'O|O$O', ['', '', 'a']) - with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'): - parse((1,), {}, 'O|$OO', ['', '', 'a']) - with self.assertRaisesRegex(SystemError, 'Empty keyword'): - parse((1,), {}, 'O|OO', ['', 'a', '']) - - -class Test_testcapi(unittest.TestCase): - locals().update((name, getattr(_testcapi, name)) - for name in dir(_testcapi) - if name.startswith('test_') and name.endswith('_code')) - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_structmembers.py b/Lib/test/test_structmembers.py deleted file mode 100644 index 07d2f62..0000000 --- a/Lib/test/test_structmembers.py +++ /dev/null @@ -1,145 +0,0 @@ -import unittest -from test.support import import_helper -from test.support import warnings_helper - -# Skip this test if the _testcapi module isn't available. -import_helper.import_module('_testcapi') -from _testcapi import _test_structmembersType, \ - CHAR_MAX, CHAR_MIN, UCHAR_MAX, \ - SHRT_MAX, SHRT_MIN, USHRT_MAX, \ - INT_MAX, INT_MIN, UINT_MAX, \ - LONG_MAX, LONG_MIN, ULONG_MAX, \ - LLONG_MAX, LLONG_MIN, ULLONG_MAX, \ - PY_SSIZE_T_MAX, PY_SSIZE_T_MIN - -ts=_test_structmembersType(False, # T_BOOL - 1, # T_BYTE - 2, # T_UBYTE - 3, # T_SHORT - 4, # T_USHORT - 5, # T_INT - 6, # T_UINT - 7, # T_LONG - 8, # T_ULONG - 23, # T_PYSSIZET - 9.99999,# T_FLOAT - 10.1010101010, # T_DOUBLE - "hi" # T_STRING_INPLACE - ) - -class ReadWriteTests(unittest.TestCase): - - def test_bool(self): - ts.T_BOOL = True - self.assertEqual(ts.T_BOOL, True) - ts.T_BOOL = False - self.assertEqual(ts.T_BOOL, False) - self.assertRaises(TypeError, setattr, ts, 'T_BOOL', 1) - - def test_byte(self): - ts.T_BYTE = CHAR_MAX - self.assertEqual(ts.T_BYTE, CHAR_MAX) - ts.T_BYTE = CHAR_MIN - self.assertEqual(ts.T_BYTE, CHAR_MIN) - ts.T_UBYTE = UCHAR_MAX - self.assertEqual(ts.T_UBYTE, UCHAR_MAX) - - def test_short(self): - ts.T_SHORT = SHRT_MAX - self.assertEqual(ts.T_SHORT, SHRT_MAX) - ts.T_SHORT = SHRT_MIN - self.assertEqual(ts.T_SHORT, SHRT_MIN) - ts.T_USHORT = USHRT_MAX - self.assertEqual(ts.T_USHORT, USHRT_MAX) - - def test_int(self): - ts.T_INT = INT_MAX - self.assertEqual(ts.T_INT, INT_MAX) - ts.T_INT = INT_MIN - self.assertEqual(ts.T_INT, INT_MIN) - ts.T_UINT = UINT_MAX - self.assertEqual(ts.T_UINT, UINT_MAX) - - def test_long(self): - ts.T_LONG = LONG_MAX - self.assertEqual(ts.T_LONG, LONG_MAX) - ts.T_LONG = LONG_MIN - self.assertEqual(ts.T_LONG, LONG_MIN) - ts.T_ULONG = ULONG_MAX - self.assertEqual(ts.T_ULONG, ULONG_MAX) - - def test_py_ssize_t(self): - ts.T_PYSSIZET = PY_SSIZE_T_MAX - self.assertEqual(ts.T_PYSSIZET, PY_SSIZE_T_MAX) - ts.T_PYSSIZET = PY_SSIZE_T_MIN - self.assertEqual(ts.T_PYSSIZET, PY_SSIZE_T_MIN) - - @unittest.skipUnless(hasattr(ts, "T_LONGLONG"), "long long not present") - def test_longlong(self): - ts.T_LONGLONG = LLONG_MAX - self.assertEqual(ts.T_LONGLONG, LLONG_MAX) - ts.T_LONGLONG = LLONG_MIN - self.assertEqual(ts.T_LONGLONG, LLONG_MIN) - - ts.T_ULONGLONG = ULLONG_MAX - self.assertEqual(ts.T_ULONGLONG, ULLONG_MAX) - - ## make sure these will accept a plain int as well as a long - ts.T_LONGLONG = 3 - self.assertEqual(ts.T_LONGLONG, 3) - ts.T_ULONGLONG = 4 - self.assertEqual(ts.T_ULONGLONG, 4) - - def test_bad_assignments(self): - integer_attributes = [ - 'T_BOOL', - 'T_BYTE', 'T_UBYTE', - 'T_SHORT', 'T_USHORT', - 'T_INT', 'T_UINT', - 'T_LONG', 'T_ULONG', - 'T_PYSSIZET' - ] - if hasattr(ts, 'T_LONGLONG'): - integer_attributes.extend(['T_LONGLONG', 'T_ULONGLONG']) - - # issue8014: this produced 'bad argument to internal function' - # internal error - for nonint in None, 3.2j, "full of eels", {}, []: - for attr in integer_attributes: - self.assertRaises(TypeError, setattr, ts, attr, nonint) - - def test_inplace_string(self): - self.assertEqual(ts.T_STRING_INPLACE, "hi") - self.assertRaises(TypeError, setattr, ts, "T_STRING_INPLACE", "s") - self.assertRaises(TypeError, delattr, ts, "T_STRING_INPLACE") - - -class TestWarnings(unittest.TestCase): - - def test_byte_max(self): - with warnings_helper.check_warnings(('', RuntimeWarning)): - ts.T_BYTE = CHAR_MAX+1 - - def test_byte_min(self): - with warnings_helper.check_warnings(('', RuntimeWarning)): - ts.T_BYTE = CHAR_MIN-1 - - def test_ubyte_max(self): - with warnings_helper.check_warnings(('', RuntimeWarning)): - ts.T_UBYTE = UCHAR_MAX+1 - - def test_short_max(self): - with warnings_helper.check_warnings(('', RuntimeWarning)): - ts.T_SHORT = SHRT_MAX+1 - - def test_short_min(self): - with warnings_helper.check_warnings(('', RuntimeWarning)): - ts.T_SHORT = SHRT_MIN-1 - - def test_ushort_max(self): - with warnings_helper.check_warnings(('', RuntimeWarning)): - ts.T_USHORT = USHRT_MAX+1 - - -if __name__ == "__main__": - unittest.main() diff --git a/Misc/NEWS.d/next/Tests/2018-07-29-15-59-51.bpo-34272.lVX2uR.rst b/Misc/NEWS.d/next/Tests/2018-07-29-15-59-51.bpo-34272.lVX2uR.rst new file mode 100644 index 0000000..479299e --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2018-07-29-15-59-51.bpo-34272.lVX2uR.rst @@ -0,0 +1 @@ +Some C API tests were moved into the new Lib/test/test_capi/ directory. -- cgit v0.12