Skip to content

Commit ecffa9b

Browse files
committed
gh-157710: Add MemoryError tests to PyUnicodeWriter (#157853)
Add a test changing the buffer kind multiple times. Add assertions to _PyUnicodeWriter_InitWithBuffer(). (cherry picked from commit 6b97452)
1 parent 6d9d6aa commit ecffa9b

3 files changed

Lines changed: 55 additions & 0 deletions

File tree

‎Lib/test/support/__init__.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3453,3 +3453,22 @@ def built_with_c_assertions():
34533453
return Py_DEBUG
34543454
else:
34553455
return bool(_testlimitedcapi._py_getbuiltwithassert())
3456+
3457+
3458+
@contextlib.contextmanager
3459+
def inject_memory_error_cm(start=0, stop=0):
3460+
"""
3461+
Similar to inject_memory_error() but can be used as a context manager.
3462+
3463+
Raise SkipTest if the _testcapi extension module is missing
3464+
"""
3465+
try:
3466+
import _testcapi
3467+
except ImportError:
3468+
raise unittest.SkipTest("_testcapi required")
3469+
3470+
try:
3471+
_testcapi.set_nomemory(start, stop)
3472+
yield
3473+
finally:
3474+
_testcapi.remove_mem_hooks()

‎Lib/test/test_capi/test_unicode.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,6 +2111,39 @@ def test_singletons(self):
21112111
writer.write_substring(ch + 'xxx', 0, 1)
21122112
self.assertIs(writer.finish(), ch)
21132113

2114+
@support.nomemtest
2115+
def test_memory_error(self):
2116+
# Inject MemoryError in PyUnicodeWriter_WriteStr()
2117+
writer = self.create_writer(0)
2118+
writer.write_str("start")
2119+
with self.assertRaises(MemoryError):
2120+
with support.inject_memory_error_cm():
2121+
# Resize the internal str object
2122+
writer.write_str("s" * 1024)
2123+
writer.write_str(" end")
2124+
self.assertEqual(writer.finish(), "start end")
2125+
2126+
# Inject MemoryError in PyUnicodeWriter_Finish()
2127+
writer = self.create_writer(1024)
2128+
writer.write_str("abc")
2129+
with self.assertRaises(MemoryError):
2130+
with support.inject_memory_error_cm():
2131+
# Need to truncate the internal str object
2132+
writer.finish()
2133+
2134+
def test_change_kind(self):
2135+
writer = self.create_writer(0)
2136+
# Create an ASCII buffer
2137+
writer.write_str('ascii ')
2138+
# Change the buffer to UCS1
2139+
writer.write_str('latin1:\xe9 ')
2140+
# Change the buffer to UCS2
2141+
writer.write_str('ucs2:\u20ac ')
2142+
# Change the buffer to UCS4
2143+
writer.write_str('ucs4:\U0010ffff')
2144+
self.assertEqual(writer.finish(),
2145+
'ascii latin1:\xe9 ucs2:\u20ac ucs4:\U0010ffff')
2146+
21142147

21152148
# Test PyUnicodeWriter_Format()
21162149
@unittest.skipIf(ctypes is None, 'need ctypes')

‎Objects/unicode_writer.c‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,13 @@ void PyUnicodeWriter_Discard(PyUnicodeWriter *writer)
199199
void
200200
_PyUnicodeWriter_InitWithBuffer(_PyUnicodeWriter *writer, PyObject *buffer)
201201
{
202+
assert(PyUnstable_Object_IsUniquelyReferenced(buffer));
203+
202204
memset(writer, 0, sizeof(*writer));
203205
writer->buffer = buffer;
204206
_PyUnicodeWriter_Update(writer);
205207
writer->min_length = writer->size;
208+
assert(_PyUnicodeWriter_CanWrite(writer));
206209
}
207210

208211

0 commit comments

Comments
 (0)