Skip to content

Commit 13b7173

Browse files
committed
Merge branch '3.15' into inject_memerror_15
2 parents b3ebdcc + adc1127 commit 13b7173

6 files changed

Lines changed: 85 additions & 10 deletions

File tree

‎Doc/c-api/unicode.rst‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,6 +1826,9 @@ object.
18261826
The instance must be destroyed by :c:func:`PyUnicodeWriter_Finish` on
18271827
success, or :c:func:`PyUnicodeWriter_Discard` on error.
18281828
1829+
The API is **not thread safe**. To share a writer with multiple threads, a
1830+
critical section or a lock is needed.
1831+
18291832
.. c:function:: PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length)
18301833
18311834
Create a Unicode writer instance.

‎Include/internal/pycore_unicodeobject.h‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,31 @@ _PyUnicode_EnsureUnicode(PyObject *obj)
104104
return 0;
105105
}
106106

107+
#ifndef NDEBUG
108+
static inline int
109+
_PyUnicodeWriter_CanWrite(_PyUnicodeWriter *writer)
110+
{
111+
// Code adapted from _PyUnicode_IsModifiable()
112+
assert(!writer->readonly);
113+
PyObject *buffer = writer->buffer;
114+
assert(buffer != NULL);
115+
// Do not use _PyObject_IsUniquelyReferenced(): the caller can have its own
116+
// lock to prevent a writer being used by two theads at the same time.
117+
assert(Py_REFCNT(buffer) == 1);
118+
assert(PyUnstable_Unicode_GET_CACHED_HASH(buffer) == -1);
119+
assert(!PyUnicode_CHECK_INTERNED(buffer));
120+
assert(!_Py_IsImmortal(buffer));
121+
return 1;
122+
}
123+
#endif
124+
107125
static inline int
108126
_PyUnicodeWriter_WriteCharInline(_PyUnicodeWriter *writer, Py_UCS4 ch)
109127
{
110128
assert(ch <= _Py_MAX_UNICODE);
111129
if (_PyUnicodeWriter_Prepare(writer, 1, ch) < 0)
112130
return -1;
131+
assert(_PyUnicodeWriter_CanWrite(writer));
113132
PyUnicode_WRITE(writer->kind, writer->data, writer->pos, ch);
114133
writer->pos++;
115134
return 0;

‎Lib/test/test_capi/test_unicode.py‎

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2101,10 +2101,8 @@ def test_substring_empty(self):
21012101
self.assertEqual(writer.finish(), '')
21022102

21032103
def test_singletons(self):
2104-
for size in (0, 123):
2105-
with self.subTest(size=size):
2106-
writer = self.create_writer(size)
2107-
self.assertIs(writer.finish(), '')
2104+
writer = self.create_writer(5)
2105+
self.assertIs(writer.finish(), '')
21082106

21092107
for ch in range(256):
21102108
with self.subTest(ch=ch):
@@ -2113,6 +2111,39 @@ def test_singletons(self):
21132111
writer.write_substring(ch + 'xxx', 0, 1)
21142112
self.assertIs(writer.finish(), ch)
21152113

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+
21162147

21172148
# Test PyUnicodeWriter_Format()
21182149
@unittest.skipIf(ctypes is None, 'need ctypes')

‎Objects/longobject.c‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,7 @@ long_to_decimal_string_internal(PyObject *aa,
22202220
Py_DECREF(scratch);
22212221
return -1;
22222222
}
2223+
assert(_PyUnicodeWriter_CanWrite(writer));
22232224
}
22242225
else if (bytes_writer) {
22252226
*bytes_str = PyBytesWriter_GrowAndUpdatePointer(bytes_writer, strlen,
@@ -2390,8 +2391,10 @@ long_format_binary(PyObject *aa, int base, int alternate,
23902391
}
23912392

23922393
if (writer) {
2393-
if (_PyUnicodeWriter_Prepare(writer, sz, 'x') == -1)
2394+
if (_PyUnicodeWriter_Prepare(writer, sz, 'x') == -1) {
23942395
return -1;
2396+
}
2397+
assert(_PyUnicodeWriter_CanWrite(writer));
23952398
}
23962399
else if (bytes_writer) {
23972400
*bytes_str = PyBytesWriter_GrowAndUpdatePointer(bytes_writer, sz,

‎Objects/unicode_writer.c‎

Lines changed: 10 additions & 1 deletion
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

@@ -350,6 +353,8 @@ _PyUnicodeWriter_WriteStr(_PyUnicodeWriter *writer, PyObject *str)
350353
if (_PyUnicodeWriter_PrepareInternal(writer, len, maxchar) == -1)
351354
return -1;
352355
}
356+
357+
assert(_PyUnicodeWriter_CanWrite(writer));
353358
_PyUnicode_FastCopyCharacters(writer->buffer, writer->pos,
354359
str, 0, len);
355360
writer->pos += len;
@@ -428,6 +433,7 @@ _PyUnicodeWriter_WriteSubstring(_PyUnicodeWriter *writer, PyObject *str,
428433
if (_PyUnicodeWriter_Prepare(writer, len, maxchar) < 0) {
429434
return -1;
430435
}
436+
assert(_PyUnicodeWriter_CanWrite(writer));
431437

432438
_PyUnicode_FastCopyCharacters(writer->buffer, writer->pos,
433439
str, start, len);
@@ -485,8 +491,10 @@ _PyUnicodeWriter_WriteASCIIString(_PyUnicodeWriter *writer,
485491
return 0;
486492
}
487493

488-
if (_PyUnicodeWriter_Prepare(writer, len, 127) == -1)
494+
if (_PyUnicodeWriter_Prepare(writer, len, 127) == -1) {
489495
return -1;
496+
}
497+
assert(_PyUnicodeWriter_CanWrite(writer));
490498

491499
switch (writer->kind)
492500
{
@@ -591,6 +599,7 @@ _PyUnicodeWriter_WriteLatin1String(_PyUnicodeWriter *writer,
591599
maxchar = ucs1lib_find_max_char((const Py_UCS1*)str, (const Py_UCS1*)str + len);
592600
if (_PyUnicodeWriter_Prepare(writer, len, maxchar) == -1)
593601
return -1;
602+
assert(_PyUnicodeWriter_CanWrite(writer));
594603
unicode_write_cstr(writer->buffer, writer->pos, str, len);
595604
writer->pos += len;
596605
return 0;

‎Objects/unicodeobject.c‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1743,18 +1743,21 @@ unicode_is_singleton(PyObject *unicode)
17431743
}
17441744
#endif
17451745

1746+
// If this function is updated, update also _PyUnicodeWriter_CanWrite().
17461747
int
17471748
_PyUnicode_IsModifiable(PyObject *unicode)
17481749
{
17491750
assert(_PyUnicode_CHECK(unicode));
1751+
if (!PyUnicode_CheckExact(unicode))
1752+
return 0;
1753+
// On Free Threading, this test fails if called from a thread other
1754+
// than the one which created the str object.
17501755
if (!_PyObject_IsUniquelyReferenced(unicode))
17511756
return 0;
17521757
if (PyUnicode_HASH(unicode) != -1)
17531758
return 0;
17541759
if (PyUnicode_CHECK_INTERNED(unicode))
17551760
return 0;
1756-
if (!PyUnicode_CheckExact(unicode))
1757-
return 0;
17581761
#ifdef Py_DEBUG
17591762
/* singleton refcount is greater than 1 */
17601763
assert(!unicode_is_singleton(unicode));
@@ -2006,6 +2009,7 @@ PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *pub_writer,
20062009
if (_PyUnicodeWriter_Prepare(writer, size - num_surrogates, maxchar) < 0) {
20072010
return -1;
20082011
}
2012+
assert(_PyUnicodeWriter_CanWrite(writer));
20092013

20102014
int kind = writer->kind;
20112015
void *data = (Py_UCS1*)writer->data + writer->pos * kind;
@@ -2303,6 +2307,7 @@ PyUnicodeWriter_WriteUCS4(PyUnicodeWriter *pub_writer,
23032307
if (_PyUnicodeWriter_Prepare(writer, size, maxchar) < 0) {
23042308
return -1;
23052309
}
2310+
assert(_PyUnicodeWriter_CanWrite(writer));
23062311

23072312
int kind = writer->kind;
23082313
void *data = (Py_UCS1*)writer->data + writer->pos * kind;
@@ -2589,8 +2594,10 @@ unicode_fromformat_write_str(_PyUnicodeWriter *writer, PyObject *str,
25892594
else
25902595
maxchar = writer->maxchar;
25912596

2592-
if (_PyUnicodeWriter_Prepare(writer, arglen, maxchar) == -1)
2597+
if (_PyUnicodeWriter_Prepare(writer, arglen, maxchar) == -1) {
25932598
return -1;
2599+
}
2600+
assert(_PyUnicodeWriter_CanWrite(writer));
25942601

25952602
fill = Py_MAX(width - length, 0);
25962603
if (fill && !(flags & F_LJUST)) {
@@ -2880,8 +2887,10 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
28802887
Py_ssize_t spacepad = Py_MAX(width - precision - sign, 0);
28812888
Py_ssize_t zeropad = Py_MAX(precision - len, 0);
28822889

2883-
if (_PyUnicodeWriter_Prepare(writer, width, 127) == -1)
2890+
if (_PyUnicodeWriter_Prepare(writer, width, 127) == -1) {
28842891
return NULL;
2892+
}
2893+
assert(_PyUnicodeWriter_CanWrite(writer));
28852894

28862895
if (spacepad && !(flags & F_LJUST)) {
28872896
if (PyUnicode_Fill(writer->buffer, writer->pos, spacepad, ' ') == -1)
@@ -5408,6 +5417,7 @@ _PyUnicode_DecodeUTF8Writer(_PyUnicodeWriter *writer,
54085417
if (_PyUnicodeWriter_Prepare(writer, size, 127) < 0) {
54095418
return -1;
54105419
}
5420+
assert(_PyUnicodeWriter_CanWrite(writer));
54115421

54125422
const char *starts = s;
54135423
const char *end = s + size;

0 commit comments

Comments
 (0)