Skip to content

Commit 23c1ff3

Browse files
committed
Self review
1 parent 7e0dfeb commit 23c1ff3

6 files changed

Lines changed: 150 additions & 156 deletions

File tree

‎Doc/c-api/sys.rst‎

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -152,28 +152,29 @@ Operating System Utilities
152152
<c-preinit>` and so that the LC_CTYPE locale is properly configured: see
153153
the :c:func:`Py_PreInitialize` function.
154154
155-
Decode a byte string from the :term:`filesystem encoding and error handler`.
156-
If the error handler is :ref:`surrogateescape error handler
157-
<surrogateescape>`, undecodable bytes are decoded as characters in range
158-
U+DC80..U+DCFF; and if a byte sequence can be decoded as a surrogate
159-
character, the bytes are escaped using the surrogateescape error handler
160-
instead of decoding them.
155+
Decode a byte string from the :term:`filesystem encoding <filesystem
156+
encoding and error handler>` with the :ref:`surrogateescape error handler
157+
<surrogateescape>` error handler.
158+
159+
Undecodable bytes are decoded as characters in range U+DC80..U+DCFF. If a
160+
byte sequence can be decoded as a surrogate character, escape the bytes
161+
using the surrogateescape error handler instead of decoding them.
161162
162163
Return a pointer to a newly allocated wide character string, use
163164
:c:func:`PyMem_RawFree` to free the memory. If size is not ``NULL``, write
164165
the number of wide characters excluding the null character into ``*size``
165166
166-
Return ``NULL`` on decoding error or memory allocation error. If *size* is
167-
not ``NULL``, ``*size`` is set to ``(size_t)-1`` on memory error or set to
168-
``(size_t)-2`` on decoding error.
167+
On memory allocation failure, set *\*size* to ``(size_t)-1`` and return
168+
``NULL``.
169+
170+
On decode error, set *\*size* to ``(size_t)-2`` and return ``NULL``.
171+
Decoding errors should never happen, unless there is a bug in the C
172+
library.
169173
170174
The :term:`filesystem encoding and error handler` are selected by
171175
:c:func:`PyConfig_Read`: see :c:member:`~PyConfig.filesystem_encoding` and
172176
:c:member:`~PyConfig.filesystem_errors` members of :c:type:`PyConfig`.
173177
174-
Decoding errors should never happen, unless there is a bug in the C
175-
library.
176-
177178
Use the :c:func:`Py_EncodeLocale` function to encode the character string
178179
back to a byte string.
179180
@@ -195,17 +196,19 @@ Operating System Utilities
195196
196197
.. c:function:: char* Py_EncodeLocale(const wchar_t *text, size_t *error_pos)
197198
198-
Encode a wide character string to the :term:`filesystem encoding and error
199-
handler`. If the error handler is :ref:`surrogateescape error handler
200-
<surrogateescape>`, surrogate characters in the range U+DC80..U+DCFF are
201-
converted to bytes 0x80..0xFF.
199+
Encode a wide character string to the :term:`filesystem encoding <filesystem
200+
encoding and error handler>` with the :ref:`surrogateescape error handler
201+
<surrogateescape>`. Surrogate characters in the range U+DC80..U+DCFF are
202+
encoded to bytes 0x80..0xFF.
202203
203204
Return a pointer to a newly allocated byte string, use :c:func:`PyMem_Free`
204-
to free the memory. Return ``NULL`` on encoding error or memory allocation
205-
error.
205+
to free the memory.
206+
207+
On memory allocation failure, set *\*error_pos* to ``(size_t)-1`` and return
208+
``NULL``.
206209
207-
If error_pos is not ``NULL``, ``*error_pos`` is set to ``(size_t)-1`` on
208-
success, or set to the index of the invalid character on encoding error.
210+
On encoding error, set *\*error_pos* to the index of the first invalid
211+
character and return ``NULL``.
209212
210213
The :term:`filesystem encoding and error handler` are selected by
211214
:c:func:`PyConfig_Read`: see :c:member:`~PyConfig.filesystem_encoding` and

‎Lib/test/test_codecs.py‎

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4072,11 +4072,12 @@ class LocaleCodecTest(unittest.TestCase):
40724072
SURROGATES = "\uDC80\uDCFF"
40734073

40744074
def encode_locale_surrogateescape(self, text):
4075-
# Test Py_EncodeLocale(): use the "surrogateescape" error handler
4075+
# Test public Py_EncodeLocale() C API:
4076+
# use the "surrogateescape" error handler
40764077
return _testlimitedcapi.encode_locale(text)
40774078

4078-
def encode_locale_ex(self, text, errors="strict"):
4079-
# Test _Py_EncodeLocale()
4079+
def encode_locale(self, text, errors="strict"):
4080+
# Test the internal _Py_EncodeLocale() C API
40804081
return _testinternalcapi.encode_locale(text, 0, errors)
40814082

40824083
def check_encode_strings(self, errors):
@@ -4094,21 +4095,21 @@ def check_encode_strings(self, errors):
40944095
self.fail("failed to compute error_pos")
40954096

40964097
if errors == "surrogateescape":
4097-
with self.assertRaises(ValueError) as cm:
4098+
with self.assertRaises(RuntimeError) as cm:
40984099
self.encode_locale_surrogateescape(text)
4099-
errmsg = f"Py_EncodeLocale failed: error_pos={error_pos}"
4100+
errmsg = f"encode error: pos={error_pos}"
41004101
self.assertEqual(str(cm.exception), errmsg)
41014102

41024103
with self.assertRaises(RuntimeError) as cm:
4103-
self.encode_locale_ex(text, errors)
4104+
self.encode_locale(text, errors)
41044105
errmsg = f"encode error: pos={error_pos}"
41054106
self.assertEqual(str(cm.exception), errmsg)
41064107
else:
41074108
if errors in ("strict", "surrogateescape"):
41084109
encoded = self.encode_locale_surrogateescape(text)
41094110
self.assertEqual(encoded, expected)
41104111

4111-
encoded = self.encode_locale_ex(text, errors)
4112+
encoded = self.encode_locale(text, errors)
41124113
self.assertEqual(encoded, expected)
41134114

41144115
def test_encode_strict(self):
@@ -4119,7 +4120,7 @@ def test_encode_surrogateescape(self):
41194120

41204121
def test_encode_surrogatepass(self):
41214122
try:
4122-
self.encode_locale_ex('', 'surrogatepass')
4123+
self.encode_locale('', 'surrogatepass')
41234124
except ValueError as exc:
41244125
if str(exc) == 'unsupported error handler':
41254126
self.skipTest(f"{self.ENCODING!r} encoder doesn't support "
@@ -4131,15 +4132,16 @@ def test_encode_surrogatepass(self):
41314132

41324133
def test_encode_unsupported_error_handler(self):
41334134
with self.assertRaises(ValueError) as cm:
4134-
self.encode_locale_ex('', 'backslashreplace')
4135+
self.encode_locale('', 'backslashreplace')
41354136
self.assertEqual(str(cm.exception), 'unsupported error handler')
41364137

41374138
def decode_locale(self, encoded, errors="strict"):
4138-
# Test _Py_DecodeLocale()
4139+
# Test the internal _Py_DecodeLocale() C API
41394140
return _testinternalcapi.decode_locale(encoded, 0, errors)
41404141

41414142
def decode_locale_surrogateescape(self, encoded):
4142-
# Test Py_DecodeLocale(): use the "surrogateescape" error handler
4143+
# Test the public Py_DecodeLocale() C API:
4144+
# use the "surrogateescape" error handler
41434145
return _testlimitedcapi.decode_locale(encoded)
41444146

41454147
def check_decode_strings(self, errors):
@@ -4179,7 +4181,7 @@ def check_decode_strings(self, errors):
41794181
self.fail("failed to compute error_pos")
41804182

41814183
if errors == "surrogateescape":
4182-
with self.assertRaises(ValueError) as cm:
4184+
with self.assertRaises(RuntimeError) as cm:
41834185
self.decode_locale_surrogateescape(encoded)
41844186
errmsg = f"Py_DecodeLocale failed: error_pos={error_pos}"
41854187
self.assertEqual(str(cm.exception), errmsg)

‎Modules/_testinternalcapi.c‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,10 +1103,7 @@ encode_locale(PyObject *self, PyObject *args)
11031103
PyErr_SetString(PyExc_ValueError, "unsupported error handler");
11041104
break;
11051105
default:
1106-
assert(str == NULL);
1107-
assert(output_length == 0);
1108-
assert(error_pos == 0);
1109-
PyErr_SetString(PyExc_ValueError, "unknown error code");
1106+
PyErr_SetString(PyExc_SystemError, "unknown error code");
11101107
break;
11111108
}
11121109
return res;
@@ -1157,7 +1154,7 @@ decode_locale(PyObject *self, PyObject *args)
11571154
PyErr_SetString(PyExc_ValueError, "unsupported error handler");
11581155
break;
11591156
default:
1160-
PyErr_SetString(PyExc_ValueError, "unknown error code");
1157+
PyErr_SetString(PyExc_SystemError, "unknown error code");
11611158
break;
11621159
}
11631160
return res;

‎Modules/_testlimitedcapi/codec.c‎

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,28 @@ decode_locale(PyObject *Py_UNUSED(module), PyObject *arg)
2525
return NULL;
2626
}
2727

28-
size_t wstr_len = (size_t)-123;
29-
wchar_t *wstr = Py_DecodeLocale(str, &wstr_len);
28+
const size_t size_canary = (size_t)-123;
29+
size_t size = size_canary;
30+
wchar_t *wstr = Py_DecodeLocale(str, &size);
3031

3132
if (str == NULL) {
32-
if (wstr_len == (size_t)-1) {
33+
if (size == (size_t)-1) {
3334
PyErr_NoMemory();
3435
}
35-
else if (wstr_len == (size_t)-2) {
36-
PyErr_SetString(PyExc_ValueError, "decode error");
36+
else if (size == (size_t)-2) {
37+
PyErr_SetString(PyExc_RuntimeError, "decode error");
3738
}
3839
else {
3940
PyErr_Format(PyExc_SystemError,
4041
"unknown Py_DecodeLocale() return value: %zd",
41-
(Py_ssize_t)wstr_len);
42+
(Py_ssize_t)size);
4243
}
4344
return NULL;
4445
}
46+
assert(wstr != NULL);
47+
assert(size != size_canary);
4548

46-
PyObject *result = PyUnicode_FromWideChar(wstr, wstr_len);
49+
PyObject *result = PyUnicode_FromWideChar(wstr, size);
4750
PyMem_RawFree(wstr);
4851
return result;
4952
}
@@ -69,10 +72,14 @@ encode_locale(PyObject *Py_UNUSED(module), PyObject *arg)
6972
PyMem_Free(wstr);
7073

7174
if (str == NULL) {
72-
assert(error_pos != error_pos_canary);
73-
return PyErr_Format(PyExc_ValueError,
74-
"Py_EncodeLocale failed: error_pos=%zd",
75-
error_pos);
75+
if (error_pos == (size_t)-1) {
76+
return PyErr_NoMemory();
77+
}
78+
else {
79+
assert(error_pos != error_pos_canary);
80+
return PyErr_Format(PyExc_RuntimeError,
81+
"encode error: pos=%zd", error_pos);
82+
}
7683
}
7784
assert(error_pos == error_pos_canary);
7885

‎Objects/unicodeobject.c‎

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3773,12 +3773,12 @@ unicode_encode_locale(PyObject *unicode, _Py_error_handler error_handler,
37733773
if (res != 0) {
37743774
if (res == _Py_CODEC_ENCODE_ERROR) {
37753775
PyObject *exc;
3776-
assert(error_pos <= (PY_SSIZE_T_MAX - 1));
3776+
assert(error_pos <= (size_t)(PY_SSIZE_T_MAX - 1));
37773777
exc = PyObject_CallFunction(PyExc_UnicodeEncodeError, "sOnns",
37783778
"locale", unicode,
37793779
(Py_ssize_t)error_pos,
37803780
(Py_ssize_t)(error_pos+1),
3781-
"encoding error");
3781+
"encode error");
37823782
if (exc != NULL) {
37833783
PyCodec_StrictErrors(exc);
37843784
Py_DECREF(exc);
@@ -3989,12 +3989,12 @@ unicode_decode_locale(const char *str, Py_ssize_t len,
39893989
if (res != 0) {
39903990
if (res == _Py_CODEC_DECODE_ERROR) {
39913991
PyObject *exc;
3992-
assert(wlen <= (PY_SSIZE_T_MAX - 1));
3992+
assert(wlen <= (size_t)(PY_SSIZE_T_MAX - 1));
39933993
exc = PyObject_CallFunction(PyExc_UnicodeDecodeError, "sy#nns",
39943994
"locale", str, len,
39953995
(Py_ssize_t)wlen,
39963996
(Py_ssize_t)(wlen + 1),
3997-
"decoding error");
3997+
"decode error");
39983998
if (exc != NULL) {
39993999
PyCodec_StrictErrors(exc);
40004000
Py_DECREF(exc);
@@ -5495,24 +5495,20 @@ PyUnicode_DecodeUTF8Stateful(const char *s,
54955495
//
54965496
// On memory allocation failure, return _Py_CODEC_MEMORY_ERROR.
54975497
//
5498-
// On decoding error (if surrogateescape is zero), return
5499-
// _Py_CODEC_DECODE_ERROR. If wlen is non-NULL, write the start of the illegal
5500-
// byte sequence into *wlen.
5498+
// On decoding error (if errors is "strict"), return _Py_CODEC_DECODE_ERROR.
5499+
// If wlen is non-NULL, write the start of the illegal byte sequence into
5500+
// *wlen.
55015501
//
5502-
// Return _Py_CODEC_UNSUPPORTED_ERROR_HANDLER if 'errors' error handler is not
5502+
// Return _Py_CODEC_UNSUPPORTED_ERROR_HANDLER if errors error handler is not
55035503
// supported.
55045504
int
55055505
_Py_DecodeUTF8(const char *s, Py_ssize_t size, wchar_t **wstr, size_t *wlen,
55065506
_Py_error_handler errors)
55075507
{
5508+
assert(0 <= size);
55085509
assert(s != NULL);
55095510
assert(wstr != NULL);
55105511

5511-
const char *orig_s = s;
5512-
const char *e;
5513-
wchar_t *unicode;
5514-
Py_ssize_t outpos;
5515-
55165512
int surrogateescape = 0;
55175513
int surrogatepass = 0;
55185514
switch (errors)
@@ -5531,18 +5527,18 @@ _Py_DecodeUTF8(const char *s, Py_ssize_t size, wchar_t **wstr, size_t *wlen,
55315527

55325528
/* Note: size will always be longer than the resulting Unicode
55335529
character count */
5534-
if (PY_SSIZE_T_MAX / (Py_ssize_t)sizeof(wchar_t) - 1 < size) {
5530+
if ((size_t)PY_SSIZE_T_MAX / sizeof(wchar_t) - 1 < (size_t)size) {
55355531
return _Py_CODEC_MEMORY_ERROR;
55365532
}
5537-
5538-
unicode = PyMem_RawMalloc((size + 1) * sizeof(wchar_t));
5533+
wchar_t *unicode = PyMem_RawMalloc((size + 1) * sizeof(wchar_t));
55395534
if (!unicode) {
55405535
return _Py_CODEC_MEMORY_ERROR;
55415536
}
55425537

55435538
/* Unpack UTF-8 encoded data */
5544-
e = s + size;
5545-
outpos = 0;
5539+
const char *orig_s = s;
5540+
const char *e = s + size;
5541+
Py_ssize_t outpos = 0;
55465542
while (s < e) {
55475543
Py_UCS4 ch;
55485544
#if SIZEOF_WCHAR_T == 4

0 commit comments

Comments
 (0)