|
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import array |
| 8 | +import contextlib |
8 | 9 | import operator |
9 | 10 | import os |
10 | 11 | import re |
@@ -48,6 +49,19 @@ def __index__(self): |
48 | 49 | return self.value |
49 | 50 |
|
50 | 51 |
|
| 52 | +@contextlib.contextmanager |
| 53 | +def inject_memory_error(testcase, start): |
| 54 | + # Raise SkipTest if _testcapi extension module is missing |
| 55 | + _testcapi = import_helper.import_module('_testcapi') |
| 56 | + |
| 57 | + with testcase.assertRaises(MemoryError): |
| 58 | + try: |
| 59 | + _testcapi.set_nomemory(start) |
| 60 | + yield |
| 61 | + finally: |
| 62 | + _testcapi.remove_mem_hooks() |
| 63 | + |
| 64 | + |
51 | 65 | class BaseBytesTest: |
52 | 66 |
|
53 | 67 | def assertTypedEqual(self, actual, expected): |
@@ -1116,13 +1130,14 @@ def test_translate(self): |
1116 | 1130 | self.assertRaises(ValueError, b.translate, bytes(range(255))) |
1117 | 1131 |
|
1118 | 1132 | c = b.translate(rosetta, b'hello') |
1119 | | - self.assertEqual(b, b'hello') |
1120 | | - self.assertIsInstance(c, self.type2test) |
| 1133 | + self.assertEqual(c, b'') |
| 1134 | + self.assertEqual(type(c), self.type2test) |
1121 | 1135 |
|
1122 | 1136 | c = b.translate(rosetta) |
1123 | 1137 | d = b.translate(rosetta, b'') |
1124 | | - self.assertEqual(c, d) |
1125 | 1138 | self.assertEqual(c, b'helle') |
| 1139 | + self.assertEqual(type(c), self.type2test) |
| 1140 | + self.assertEqual(d, b'helle') |
1126 | 1141 |
|
1127 | 1142 | c = b.translate(rosetta, b'l') |
1128 | 1143 | self.assertEqual(c, b'hee') |
@@ -1555,6 +1570,36 @@ def test_resize(self): |
1555 | 1570 | self.assertRaises(MemoryError, bytearray().resize, sys.maxsize) |
1556 | 1571 | self.assertRaises(MemoryError, bytearray(1000).resize, sys.maxsize) |
1557 | 1572 |
|
| 1573 | + @support.nomemtest |
| 1574 | + def test_resize_error(self): |
| 1575 | + # gh-157242: If bytearray.resize() fails (MemoryError), |
| 1576 | + # the bytearray must be left unchanged. |
| 1577 | + |
| 1578 | + offset = 3 |
| 1579 | + for logical_offset in (False, True): |
| 1580 | + with self.subTest(logical_offset=logical_offset): |
| 1581 | + # grow bytearray |
| 1582 | + ba = bytearray(b'0123456789') |
| 1583 | + if logical_offset: |
| 1584 | + expected = ba[offset:] |
| 1585 | + del ba[:offset] |
| 1586 | + else: |
| 1587 | + expected = ba.copy() |
| 1588 | + with inject_memory_error(self, 0): |
| 1589 | + ba.resize(1024) |
| 1590 | + self.assertEqual(ba, expected) |
| 1591 | + |
| 1592 | + # shrink bytearray |
| 1593 | + ba = bytearray(b'0123456789') |
| 1594 | + if logical_offset: |
| 1595 | + expected = ba[offset:] |
| 1596 | + del ba[:offset] |
| 1597 | + else: |
| 1598 | + expected = ba.copy() |
| 1599 | + with inject_memory_error(self, 0): |
| 1600 | + ba.resize(1) |
| 1601 | + self.assertEqual(ba, expected) |
| 1602 | + |
1558 | 1603 | def test_take_bytes(self): |
1559 | 1604 | ba = bytearray(b'ab') |
1560 | 1605 | self.assertEqual(ba.take_bytes(), b'ab') |
@@ -1619,6 +1664,29 @@ def test_take_bytes(self): |
1619 | 1664 | self.assertEqual(ba, bytearray(b'A')) |
1620 | 1665 | self.assertEqual(ord(b'c'), ord('c')) |
1621 | 1666 |
|
| 1667 | + @support.nomemtest |
| 1668 | + def test_take_bytes_error(self): |
| 1669 | + # gh-157242: If bytearray.take_bytes() fails (MemoryError), |
| 1670 | + # the bytearray must be left unchanged. |
| 1671 | + |
| 1672 | + for logical_offset, to_take, mem_errors in ( |
| 1673 | + (True, 5, (0, 1)), |
| 1674 | + (False, 5, (0, 1)), |
| 1675 | + (True, None, (0,)), |
| 1676 | + ): |
| 1677 | + for mem_error in mem_errors: |
| 1678 | + with self.subTest(logical_offset=logical_offset, |
| 1679 | + to_take=to_take, mem_error=mem_error): |
| 1680 | + ba = bytearray(b'0123456789') |
| 1681 | + if logical_offset: |
| 1682 | + expected = ba[3:] |
| 1683 | + del ba[:3] |
| 1684 | + else: |
| 1685 | + expected = ba.copy() |
| 1686 | + with inject_memory_error(self, mem_error): |
| 1687 | + ba.take_bytes(to_take) |
| 1688 | + self.assertEqual(ba, expected) |
| 1689 | + |
1622 | 1690 | @support.cpython_only # tests an implementation detail |
1623 | 1691 | def test_take_bytes_optimization(self): |
1624 | 1692 | # Validate optimization around taking lots of little chunks out of a |
|
0 commit comments