|
1 | | -import unittest |
2 | 1 | import gc |
3 | | -from sys import getrefcount |
| 2 | +import sys |
| 3 | +import unittest |
4 | 4 | from test.support import import_helper |
5 | 5 |
|
6 | 6 | _testcapi = import_helper.import_module('_testcapi') |
@@ -131,9 +131,9 @@ def check_tuple_from_pair(self, from_pair): |
131 | 131 | class Temp: |
132 | 132 | pass |
133 | 133 | temp = Temp() |
134 | | - temp_rc = getrefcount(temp) |
| 134 | + temp_rc = sys.getrefcount(temp) |
135 | 135 | self.assertEqual(from_pair(temp, temp), (temp, temp)) |
136 | | - self.assertEqual(getrefcount(temp), temp_rc) |
| 136 | + self.assertEqual(sys.getrefcount(temp), temp_rc) |
137 | 137 |
|
138 | 138 | self._not_tracked(from_pair(1, 2)) |
139 | 139 | self._not_tracked(from_pair(None, None)) |
@@ -299,30 +299,71 @@ def test_tuple_set_item(self): |
299 | 299 |
|
300 | 300 | def test__tuple_resize(self): |
301 | 301 | # Test _PyTuple_Resize() |
302 | | - resize = _testcapi._tuple_resize |
| 302 | + _resize = _testcapi._tuple_resize |
303 | 303 | checknull = _testcapi._check_tuple_item_is_NULL |
304 | 304 |
|
305 | | - a = () |
306 | | - b = resize(a, 0, False) |
307 | | - self.assertEqual(len(a), 0) |
308 | | - self.assertEqual(len(b), 0) |
309 | | - b = resize(a, 2, False) |
310 | | - self.assertEqual(len(a), 0) |
311 | | - self.assertEqual(len(b), 2) |
312 | | - self.assertTrue(checknull(b, 0)) |
313 | | - self.assertTrue(checknull(b, 1)) |
314 | | - |
315 | | - a = ([1], [2], [3]) |
316 | | - b = resize(a, 3) |
| 305 | + def assert_is_fresh_copy(result, refcnt, is_new_obj): |
| 306 | + self.assertEqual(refcnt, 1) |
| 307 | + self.assertTrue(is_new_obj) |
| 308 | + self.assertFalse(sys._is_immortal(result)) |
| 309 | + |
| 310 | + def resize(tup, new_size, new=True, compute_hash=False): |
| 311 | + if isinstance(tup, tuple): |
| 312 | + old_size = len(tup) |
| 313 | + else: |
| 314 | + old_size = 0 |
| 315 | + result, refcnt, is_new_obj = _resize(tup, new_size, new, |
| 316 | + compute_hash=compute_hash) |
| 317 | + if new_size == old_size: |
| 318 | + # Return the same object unchanged |
| 319 | + self.assertFalse(is_new_obj) |
| 320 | + elif old_size == 0: |
| 321 | + assert_is_fresh_copy(result, refcnt, is_new_obj) |
| 322 | + elif new_size == 0: |
| 323 | + # check that the result is the empty bytes string singleton |
| 324 | + self.assertEqual(result, ()) |
| 325 | + self.assertTrue(sys._is_immortal(result)) |
| 326 | + self.assertTrue(is_new_obj) |
| 327 | + elif not new: |
| 328 | + # Make sure that the result is a fresh object. |
| 329 | + assert_is_fresh_copy(result, refcnt, is_new_obj) |
| 330 | + else: |
| 331 | + # An in-place resize can return the same memory address, or |
| 332 | + # not. 'is_new_obj' cannot be tested. |
| 333 | + self.assertEqual(refcnt, 1) |
| 334 | + self.assertFalse(sys._is_immortal(result)) |
| 335 | + |
| 336 | + return result |
| 337 | + |
| 338 | + # Resizing the empty tuple singleton always create a new tuple |
| 339 | + for compute_hash in (False, True): |
| 340 | + with self.subTest(compute_hash=compute_hash): |
| 341 | + a = () |
| 342 | + b = resize(a, 0, False, compute_hash) |
| 343 | + self.assertEqual(len(a), 0) |
| 344 | + self.assertEqual(len(b), 0) |
| 345 | + b = resize(a, 2, False, compute_hash) |
| 346 | + self.assertEqual(len(a), 0) |
| 347 | + self.assertEqual(len(b), 2) |
| 348 | + self.assertTrue(checknull(b, 0)) |
| 349 | + self.assertTrue(checknull(b, 1)) |
| 350 | + |
| 351 | + a = tuple(object() for _ in range(3)) |
| 352 | + b = resize(a, 3, True) |
317 | 353 | self.assertEqual(b, a) |
318 | | - b = resize(a, 2) |
| 354 | + b = resize(a, 2, True) |
319 | 355 | self.assertEqual(b, a[:2]) |
320 | | - b = resize(a, 5) |
| 356 | + b = resize(a, 5, True) |
321 | 357 | self.assertEqual(len(b), 5) |
322 | 358 | self.assertEqual(b[:3], a) |
323 | 359 | self.assertTrue(checknull(b, 3)) |
324 | 360 | self.assertTrue(checknull(b, 4)) |
325 | 361 |
|
| 362 | + # gh-161226: Disallow resizing a tuple if the hash value is cached |
| 363 | + a = tuple(object() for _ in range(3)) |
| 364 | + with self.assertRaises(SystemError): |
| 365 | + resize(a, 3, True, compute_hash=True) |
| 366 | + |
326 | 367 | a = () |
327 | 368 | self.assertRaises(MemoryError, resize, a, PY_SSIZE_T_MAX) |
328 | 369 | self.assertRaises(SystemError, resize, a, -1) |
|
0 commit comments