Skip to content

[3.15] gh-129724: gh-156504: Make marshal output independent of reference counts (GH-159192) - #159290

Draft
gpshead wants to merge 1 commit into
python:3.15from
gpshead:backport-48f18f0-3.15
Draft

gpshead wants to merge 1 commit into
python:3.15from
gpshead:backport-48f18f0-3.15

Conversation

@gpshead

@gpshead gpshead commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

(cherry picked from commit 48f18f0)

Manual backport notes

A pure cherry-pick conflicted because 3.15 does not have the marshal error handling changes of gh-156942 and gh-155907, nor gh-148653.

  • Python/marshal.c: keeps the WFERR_* error codes of this branch; the new allocation failures use WFERR_NOMEMORY.
  • PyMarshal_WriteLongToFile() and PyMarshal_WriteObjectToFile() still do not report a failure to write to the file, as before on this branch.
  • PyMarshal_WriteObjectToFile() now goes through the same code as marshal.dumps(), so when the object cannot be marshalled it writes nothing and sets ValueError. Before, it wrote partial data and usually returned without an error.
  • Recursive tuples are not detected on this branch (Crash: marshal.loads SIGSEGV on self-referencing TYPE_TUPLE with FLAG_REF #148653), so test_shared_recursion uses a recursive slice.
  • Lib/test/test_capi/test_marshal.py does not exist here: its new cases are in CAPI_TestCase of Lib/test/test_marshal.py, and the _testcapi helper returns the error instead of asserting there is none.
  • Doc/c-api/marshal.rst: the notes about unreported write errors no longer say "In Python 3.15 and earlier"; the versionchanged entry describes the change.
  • Programs/test_frozenmain.h is regenerated.

…ent of reference counts (pythonGH-159192)

The marshal writer decided whether an object can be referenced later
(FLAG_REF) from its reference count, so the output for the same value
depended on what else in the process held a reference to its parts, and
differed between the default and the free-threaded builds' view of the
same objects.  This is the remaining cause of pythongh-129724 and the cause of
pythongh-156504.

FLAG_REF is now set exactly on the objects which are written more than
once in the dump.  The writer still makes one pass: it records the
position of the type byte of each object it remembers and of each
TYPE_REF, and when the dump is complete it sets the flags and renumbers
the references.  A reference count of one is only used to skip the
hashtable.  Interned strings are no longer flagged unconditionally.

The reader is unchanged and the format version is not bumped: both the
old and the new output are loaded by old and new readers.

PyMarshal_WriteObjectToFile() now builds the data in memory and writes
it with one fwrite(), since the flags are only known at the end.
Nothing is written if the object cannot be marshalled.

(cherry picked from commit 48f18f0)
@read-the-docs-community

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type-bug An unexpected behavior, bug, or error

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant