[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:
Miss Islington (bot)
2026-07-17 08:23:01 +00:00
committed by GitHub
co-authored by Serhiy Storchaka Claude Opus 4.8
parent 38c3f112c3
commit 21ab8eb5a8
3 changed files with 36 additions and 4 deletions
+10
View File
@@ -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
View File
@@ -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;
}