mirror of
https://github.com/python/cpython.git
synced 2026-07-28 04:42:09 -04:00
gh-85943: Fix BytesWarning in the struct format cache under -bb (GH-153627)
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. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -182,6 +182,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
@@ -2642,7 +2642,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) {
|
||||
@@ -2650,24 +2651,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