mirror of
https://github.com/python/cpython.git
synced 2026-07-29 13:16:59 -04:00
[3.15] gh-85943: Fix BytesWarning in the struct format cache under -bb (GH-153627) (GH-153834)
Normalize bytes format strings to str before using them as the cache key,
so that equal str and bytes formats no longer collide and get compared.
(cherry picked from commit 190d2ffbb9)
Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Serhiy Storchaka
Claude Opus 4.8
parent
38c3f112c3
commit
21ab8eb5a8
@@ -181,6 +181,16 @@ class StructTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
||||
self.assertGreaterEqual(struct.calcsize('n'), struct.calcsize('i'))
|
||||
self.assertGreaterEqual(struct.calcsize('n'), struct.calcsize('P'))
|
||||
|
||||
def test_cache_bytes_vs_str_bb(self):
|
||||
# Mixing str and bytes formats must not raise BytesWarning under -bb.
|
||||
code = (
|
||||
'import struct\n'
|
||||
'struct.calcsize(b"!d"); struct.calcsize("!d")\n'
|
||||
'struct.calcsize(">d"); struct.calcsize(b">d")\n'
|
||||
'struct.Struct(b"i"); struct.Struct("i")\n'
|
||||
)
|
||||
assert_python_ok('-bb', '-c', code)
|
||||
|
||||
def test_integers(self):
|
||||
# Integer tests (bBhHiIlLqQnN).
|
||||
import binascii
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Fix :mod:`struct` functions raising :exc:`BytesWarning` under the ``-bb``
|
||||
command line option when a :class:`str` format is used after an equal
|
||||
:class:`bytes` format (or vice versa). The internal format cache no longer
|
||||
mixes :class:`str` and :class:`bytes` keys.
|
||||
+22
-4
@@ -2613,7 +2613,8 @@ static PyType_Spec PyStructType_spec = {
|
||||
static int
|
||||
cache_struct_converter(PyObject *module, PyObject *fmt, PyStructObject **ptr)
|
||||
{
|
||||
PyObject * s_object;
|
||||
PyObject *s_object;
|
||||
PyObject *key;
|
||||
_structmodulestate *state = get_struct_state(module);
|
||||
|
||||
if (fmt == NULL) {
|
||||
@@ -2621,24 +2622,41 @@ cache_struct_converter(PyObject *module, PyObject *fmt, PyStructObject **ptr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (PyDict_GetItemRef(state->cache, fmt, &s_object) < 0) {
|
||||
/* Use a str cache key: an equal str and bytes would collide and be
|
||||
compared, raising BytesWarning under -bb. */
|
||||
if (PyBytes_Check(fmt)) {
|
||||
key = PyUnicode_DecodeASCII(PyBytes_AS_STRING(fmt),
|
||||
PyBytes_GET_SIZE(fmt), "surrogateescape");
|
||||
if (key == NULL) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
key = Py_NewRef(fmt);
|
||||
}
|
||||
|
||||
if (PyDict_GetItemRef(state->cache, key, &s_object) < 0) {
|
||||
Py_DECREF(key);
|
||||
return 0;
|
||||
}
|
||||
if (s_object != NULL) {
|
||||
Py_DECREF(key);
|
||||
*ptr = PyStructObject_CAST(s_object);
|
||||
return Py_CLEANUP_SUPPORTED;
|
||||
}
|
||||
|
||||
s_object = PyObject_CallOneArg(state->PyStructType, fmt);
|
||||
s_object = PyObject_CallOneArg(state->PyStructType, key);
|
||||
if (s_object != NULL) {
|
||||
if (PyDict_GET_SIZE(state->cache) >= MAXCACHE)
|
||||
PyDict_Clear(state->cache);
|
||||
/* Attempt to cache the result */
|
||||
if (PyDict_SetItem(state->cache, fmt, s_object) == -1)
|
||||
if (PyDict_SetItem(state->cache, key, s_object) == -1)
|
||||
PyErr_Clear();
|
||||
Py_DECREF(key);
|
||||
*ptr = (PyStructObject *)s_object;
|
||||
return Py_CLEANUP_SUPPORTED;
|
||||
}
|
||||
Py_DECREF(key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user