diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index c595e8cf14f1e15..042cad03ce80e79 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -116,6 +116,14 @@ def test_bytes(self): for s in [b"", b"Andr\xe8 Previn", b"abc", b" "*10000]: self.helper(s) + @support.cpython_only + def test_bytes_singleton(self): + for version in range(marshal.version + 1): + for sample in [b"", b"x"]: + new = marshal.loads(marshal.dumps(sample, version)) + self.assertIs(new, sample) + + class ExceptionTestCase(unittest.TestCase): def test_exceptions(self): new = marshal.loads(marshal.dumps(StopIteration)) diff --git a/Misc/NEWS.d/next/Library/2026-09-13-07-14-35.gh-issue-128509.9F2GCE.rst b/Misc/NEWS.d/next/Library/2026-09-13-07-14-35.gh-issue-128509.9F2GCE.rst new file mode 100644 index 000000000000000..7699242c8c647ea --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-13-07-14-35.gh-issue-128509.9F2GCE.rst @@ -0,0 +1,2 @@ +:func:`marshal.load` and :func:`marshal.loads` can now get 1-byte string +singletons. Patch by Victor Stinner. diff --git a/Python/marshal.c b/Python/marshal.c index 1897d700c055bd3..420c3ee115a7377 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -1388,16 +1388,12 @@ r_object(RFILE *p) } break; } - v = PyBytes_FromStringAndSize((char *)NULL, n); - if (v == NULL) - break; ptr = r_string(n, p); if (ptr == NULL) { - Py_DECREF(v); break; } - memcpy(PyBytes_AS_STRING(v), ptr, n); - retval = v; + // Get a singleton for 1-byte string + retval = PyBytes_FromStringAndSize(ptr, n); // can be NULL R_REF(retval); break; }