aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/test/test_capi
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_capi')
-rw-r--r--Lib/test/test_capi/test_bytearray.py6
-rw-r--r--Lib/test/test_capi/test_bytes.py8
-rw-r--r--Lib/test/test_capi/test_misc.py2
-rw-r--r--Lib/test/test_capi/test_object.py3
-rw-r--r--Lib/test/test_capi/test_opt.py270
5 files changed, 283 insertions, 6 deletions
diff --git a/Lib/test/test_capi/test_bytearray.py b/Lib/test/test_capi/test_bytearray.py
index dfa98de9f00..52565ea34c6 100644
--- a/Lib/test/test_capi/test_bytearray.py
+++ b/Lib/test/test_capi/test_bytearray.py
@@ -66,6 +66,7 @@ class CAPITest(unittest.TestCase):
# Test PyByteArray_FromObject()
fromobject = _testlimitedcapi.bytearray_fromobject
+ self.assertEqual(fromobject(b''), bytearray(b''))
self.assertEqual(fromobject(b'abc'), bytearray(b'abc'))
self.assertEqual(fromobject(bytearray(b'abc')), bytearray(b'abc'))
self.assertEqual(fromobject(ByteArraySubclass(b'abc')), bytearray(b'abc'))
@@ -115,6 +116,7 @@ class CAPITest(unittest.TestCase):
self.assertEqual(concat(b'abc', bytearray(b'def')), bytearray(b'abcdef'))
self.assertEqual(concat(bytearray(b'abc'), b''), bytearray(b'abc'))
self.assertEqual(concat(b'', bytearray(b'def')), bytearray(b'def'))
+ self.assertEqual(concat(bytearray(b''), bytearray(b'')), bytearray(b''))
self.assertEqual(concat(memoryview(b'xabcy')[1:4], b'def'),
bytearray(b'abcdef'))
self.assertEqual(concat(b'abc', memoryview(b'xdefy')[1:4]),
@@ -150,6 +152,10 @@ class CAPITest(unittest.TestCase):
self.assertEqual(resize(ba, 0), 0)
self.assertEqual(ba, bytearray())
+ ba = bytearray(b'')
+ self.assertEqual(resize(ba, 0), 0)
+ self.assertEqual(ba, bytearray())
+
ba = ByteArraySubclass(b'abcdef')
self.assertEqual(resize(ba, 3), 0)
self.assertEqual(ba, bytearray(b'abc'))
diff --git a/Lib/test/test_capi/test_bytes.py b/Lib/test/test_capi/test_bytes.py
index 5b61c733815..bc820bd68d9 100644
--- a/Lib/test/test_capi/test_bytes.py
+++ b/Lib/test/test_capi/test_bytes.py
@@ -22,6 +22,7 @@ class CAPITest(unittest.TestCase):
# Test PyBytes_Check()
check = _testlimitedcapi.bytes_check
self.assertTrue(check(b'abc'))
+ self.assertTrue(check(b''))
self.assertFalse(check('abc'))
self.assertFalse(check(bytearray(b'abc')))
self.assertTrue(check(BytesSubclass(b'abc')))
@@ -36,6 +37,7 @@ class CAPITest(unittest.TestCase):
# Test PyBytes_CheckExact()
check = _testlimitedcapi.bytes_checkexact
self.assertTrue(check(b'abc'))
+ self.assertTrue(check(b''))
self.assertFalse(check('abc'))
self.assertFalse(check(bytearray(b'abc')))
self.assertFalse(check(BytesSubclass(b'abc')))
@@ -79,6 +81,7 @@ class CAPITest(unittest.TestCase):
# Test PyBytes_FromObject()
fromobject = _testlimitedcapi.bytes_fromobject
+ self.assertEqual(fromobject(b''), b'')
self.assertEqual(fromobject(b'abc'), b'abc')
self.assertEqual(fromobject(bytearray(b'abc')), b'abc')
self.assertEqual(fromobject(BytesSubclass(b'abc')), b'abc')
@@ -108,6 +111,7 @@ class CAPITest(unittest.TestCase):
self.assertEqual(asstring(b'abc', 4), b'abc\0')
self.assertEqual(asstring(b'abc\0def', 8), b'abc\0def\0')
+ self.assertEqual(asstring(b'', 1), b'\0')
self.assertRaises(TypeError, asstring, 'abc', 0)
self.assertRaises(TypeError, asstring, object(), 0)
@@ -120,6 +124,7 @@ class CAPITest(unittest.TestCase):
self.assertEqual(asstringandsize(b'abc', 4), (b'abc\0', 3))
self.assertEqual(asstringandsize(b'abc\0def', 8), (b'abc\0def\0', 7))
+ self.assertEqual(asstringandsize(b'', 1), (b'\0', 0))
self.assertEqual(asstringandsize_null(b'abc', 4), b'abc\0')
self.assertRaises(ValueError, asstringandsize_null, b'abc\0def', 8)
self.assertRaises(TypeError, asstringandsize, 'abc', 0)
@@ -134,6 +139,7 @@ class CAPITest(unittest.TestCase):
# Test PyBytes_Repr()
bytes_repr = _testlimitedcapi.bytes_repr
+ self.assertEqual(bytes_repr(b'', 0), r"""b''""")
self.assertEqual(bytes_repr(b'''abc''', 0), r"""b'abc'""")
self.assertEqual(bytes_repr(b'''abc''', 1), r"""b'abc'""")
self.assertEqual(bytes_repr(b'''a'b"c"d''', 0), r"""b'a\'b"c"d'""")
@@ -163,6 +169,7 @@ class CAPITest(unittest.TestCase):
self.assertEqual(concat(b'', bytearray(b'def')), b'def')
self.assertEqual(concat(memoryview(b'xabcy')[1:4], b'def'), b'abcdef')
self.assertEqual(concat(b'abc', memoryview(b'xdefy')[1:4]), b'abcdef')
+ self.assertEqual(concat(b'', b''), b'')
self.assertEqual(concat(b'abc', b'def', True), b'abcdef')
self.assertEqual(concat(b'abc', bytearray(b'def'), True), b'abcdef')
@@ -192,6 +199,7 @@ class CAPITest(unittest.TestCase):
"""Test PyBytes_DecodeEscape()"""
decodeescape = _testlimitedcapi.bytes_decodeescape
+ self.assertEqual(decodeescape(b''), b'')
self.assertEqual(decodeescape(b'abc'), b'abc')
self.assertEqual(decodeescape(br'\t\n\r\x0b\x0c\x00\\\'\"'),
b'''\t\n\r\v\f\0\\'"''')
diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py
index a597f23a992..f74694a7a74 100644
--- a/Lib/test/test_capi/test_misc.py
+++ b/Lib/test/test_capi/test_misc.py
@@ -412,10 +412,12 @@ class CAPITest(unittest.TestCase):
L = MyList((L,))
@support.requires_resource('cpu')
+ @support.skip_emscripten_stack_overflow()
def test_trashcan_python_class1(self):
self.do_test_trashcan_python_class(list)
@support.requires_resource('cpu')
+ @support.skip_emscripten_stack_overflow()
def test_trashcan_python_class2(self):
from _testcapi import MyList
self.do_test_trashcan_python_class(MyList)
diff --git a/Lib/test/test_capi/test_object.py b/Lib/test/test_capi/test_object.py
index 127862546b1..d4056727d07 100644
--- a/Lib/test/test_capi/test_object.py
+++ b/Lib/test/test_capi/test_object.py
@@ -180,7 +180,7 @@ class IsUniquelyReferencedTest(unittest.TestCase):
self.assertTrue(_testcapi.is_uniquely_referenced(object()))
self.assertTrue(_testcapi.is_uniquely_referenced([]))
# Immortals
- self.assertFalse(_testcapi.is_uniquely_referenced("spanish inquisition"))
+ self.assertFalse(_testcapi.is_uniquely_referenced(()))
self.assertFalse(_testcapi.is_uniquely_referenced(42))
# CRASHES is_uniquely_referenced(NULL)
@@ -221,6 +221,7 @@ class CAPITest(unittest.TestCase):
"""
self.check_negative_refcount(code)
+ @support.requires_resource('cpu')
def test_decref_delayed(self):
# gh-130519: Test that _PyObject_XDecRefDelayed() and QSBR code path
# handles destructors that are possibly re-entrant or trigger a GC.
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index 651148336f7..cb6eae48414 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -1280,8 +1280,8 @@ class TestUopsOptimization(unittest.TestCase):
self.assertIsNotNone(ex)
self.assertEqual(res, TIER2_THRESHOLD * 6 + 1)
call = opnames.index("_CALL_BUILTIN_FAST")
- load_attr_top = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", 0, call)
- load_attr_bottom = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", call)
+ load_attr_top = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", 0, call)
+ load_attr_bottom = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", call)
self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1)
self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2)
@@ -1303,8 +1303,8 @@ class TestUopsOptimization(unittest.TestCase):
self.assertIsNotNone(ex)
self.assertEqual(res, TIER2_THRESHOLD * 2)
call = opnames.index("_CALL_BUILTIN_FAST_WITH_KEYWORDS")
- load_attr_top = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", 0, call)
- load_attr_bottom = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", call)
+ load_attr_top = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", 0, call)
+ load_attr_bottom = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", call)
self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1)
self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2)
@@ -1925,6 +1925,50 @@ class TestUopsOptimization(unittest.TestCase):
self.assertNotIn("_GUARD_NOS_INT", uops)
self.assertNotIn("_GUARD_TOS_INT", uops)
+ def test_get_len_with_const_tuple(self):
+ def testfunc(n):
+ x = 0.0
+ for _ in range(n):
+ match (1, 2, 3, 4):
+ case [_, _, _, _]:
+ x += 1.0
+ return x
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(int(res), TIER2_THRESHOLD)
+ uops = get_opnames(ex)
+ self.assertNotIn("_GUARD_NOS_INT", uops)
+ self.assertNotIn("_GET_LEN", uops)
+ self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_get_len_with_non_const_tuple(self):
+ def testfunc(n):
+ x = 0.0
+ for _ in range(n):
+ match object(), object():
+ case [_, _]:
+ x += 1.0
+ return x
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(int(res), TIER2_THRESHOLD)
+ uops = get_opnames(ex)
+ self.assertNotIn("_GUARD_NOS_INT", uops)
+ self.assertNotIn("_GET_LEN", uops)
+ self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_get_len_with_non_tuple(self):
+ def testfunc(n):
+ x = 0.0
+ for _ in range(n):
+ match [1, 2, 3, 4]:
+ case [_, _, _, _]:
+ x += 1.0
+ return x
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(int(res), TIER2_THRESHOLD)
+ uops = get_opnames(ex)
+ self.assertNotIn("_GUARD_NOS_INT", uops)
+ self.assertIn("_GET_LEN", uops)
+
def test_binary_op_subscr_tuple_int(self):
def testfunc(n):
x = 0
@@ -1955,9 +1999,225 @@ class TestUopsOptimization(unittest.TestCase):
self.assertEqual(res, TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
- self.assertIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_CALL_ISINSTANCE", uops)
self.assertNotIn("_GUARD_THIRD_NULL", uops)
self.assertNotIn("_GUARD_CALLABLE_ISINSTANCE", uops)
+ self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_call_list_append(self):
+ def testfunc(n):
+ a = []
+ for i in range(n):
+ a.append(i)
+ return sum(a)
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, sum(range(TIER2_THRESHOLD)))
+ uops = get_opnames(ex)
+ self.assertIn("_CALL_LIST_APPEND", uops)
+ # We should remove these in the future
+ self.assertIn("_GUARD_NOS_LIST", uops)
+ self.assertIn("_GUARD_CALLABLE_LIST_APPEND", uops)
+
+ def test_call_isinstance_is_true(self):
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ y = isinstance(42, int)
+ if y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertNotIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
+ self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_call_isinstance_is_false(self):
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ y = isinstance(42, str)
+ if not y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertNotIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertNotIn("_GUARD_IS_FALSE_POP", uops)
+ self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_call_isinstance_subclass(self):
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ y = isinstance(True, int)
+ if y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertNotIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertNotIn("_GUARD_IS_TRUE_POP", uops)
+ self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops)
+ self.assertNotIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_call_isinstance_unknown_object(self):
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ # The optimizer doesn't know the return type here:
+ bar = eval("42")
+ # This will only narrow to bool:
+ y = isinstance(bar, int)
+ if y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertIn("_GUARD_IS_TRUE_POP", uops)
+
+ def test_call_isinstance_tuple_of_classes(self):
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ # A tuple of classes is currently not optimized,
+ # so this is only narrowed to bool:
+ y = isinstance(42, (int, str))
+ if y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertIn("_GUARD_IS_TRUE_POP", uops)
+
+ def test_call_isinstance_metaclass(self):
+ class EvenNumberMeta(type):
+ def __instancecheck__(self, number):
+ return number % 2 == 0
+
+ class EvenNumber(metaclass=EvenNumberMeta):
+ pass
+
+ def testfunc(n):
+ x = 0
+ for _ in range(n):
+ # Only narrowed to bool
+ y = isinstance(42, EvenNumber)
+ if y:
+ x += 1
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertIn("_CALL_ISINSTANCE", uops)
+ self.assertNotIn("_TO_BOOL_BOOL", uops)
+ self.assertIn("_GUARD_IS_TRUE_POP", uops)
+
+ def test_set_type_version_sets_type(self):
+ class C:
+ A = 1
+
+ def testfunc(n):
+ x = 0
+ c = C()
+ for _ in range(n):
+ x += c.A # Guarded.
+ x += type(c).A # Unguarded!
+ return x
+
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, 2 * TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertIn("_GUARD_TYPE_VERSION", uops)
+ self.assertNotIn("_CHECK_ATTR_CLASS", uops)
+
+ def test_load_small_int(self):
+ def testfunc(n):
+ x = 0
+ for i in range(n):
+ x += 1
+ return x
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertNotIn("_LOAD_SMALL_INT", uops)
+ self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
+
+ def test_cached_attributes(self):
+ class C:
+ A = 1
+ def m(self):
+ return 1
+ class D:
+ __slots__ = ()
+ A = 1
+ def m(self):
+ return 1
+ class E(Exception):
+ def m(self):
+ return 1
+ def f(n):
+ x = 0
+ c = C()
+ d = D()
+ e = E()
+ for _ in range(n):
+ x += C.A # _LOAD_ATTR_CLASS
+ x += c.A # _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES
+ x += d.A # _LOAD_ATTR_NONDESCRIPTOR_NO_DICT
+ x += c.m() # _LOAD_ATTR_METHOD_WITH_VALUES
+ x += d.m() # _LOAD_ATTR_METHOD_NO_DICT
+ x += e.m() # _LOAD_ATTR_METHOD_LAZY_DICT
+ return x
+
+ res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD)
+ self.assertEqual(res, 6 * TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertNotIn("_LOAD_ATTR_CLASS", uops)
+ self.assertNotIn("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", uops)
+ self.assertNotIn("_LOAD_ATTR_NONDESCRIPTOR_NO_DICT", uops)
+ self.assertNotIn("_LOAD_ATTR_METHOD_WITH_VALUES", uops)
+ self.assertNotIn("_LOAD_ATTR_METHOD_NO_DICT", uops)
+ self.assertNotIn("_LOAD_ATTR_METHOD_LAZY_DICT", uops)
def global_identity(x):