Repository navigation
Track Unpickler payload references during cyclic GC - #9288
Conversation
Traverse input callbacks, buffer iterators, memo slots and memo proxy owners. Clear outgoing payload edges through deferred drops and retire successful reinitialization state outside the state lock. Retain stream and buffers payload-cycle regressions in the existing pickle snippet. Assisted-by: OpenAI Codex:model-version-unavailable
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
📒 Files selected for processing: 2
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c9708f3e45
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
|
|
||
| # Unpickler payloads can retain a cycle through their memo proxy. | ||
| class UnpicklerCycleStream: |
There was a problem hiding this comment.
This newly added regression block changes test logic in an existing test file, but the repository's test-modification policy says test logic, data, and assertions must never be modified and limits acceptable test-file changes to adding or removing expectedFailure decorators; revert this block rather than landing an unsupported test-file edit.
AGENTS.md reference: AGENTS.md:L278-L282
Useful? React with 👍 / 👎.
| return weakref.ref(holder) | ||
|
|
||
|
|
||
| for kind in ("stream", "buffers"): |
There was a problem hiding this comment.
Run the required validation suites
For this interpreter change and the added extra_tests/snippets code, the commit's validation explicitly says the workspace/C-API suites and full pytest gate were not run; repository policy requires the workspace test command, C-API cargo test, and pytest -v in extra_tests (plus relevant interpreter-behavior tests) before completion, so run those gates before landing.
AGENTS.md reference: AGENTS.md:L35-L35
Useful? React with 👍 / 👎.
Track Unpickler payload references during cyclic GC
Summary
Split the remaining Unpickler GC/lifetime closure from the Python 3.15 upgrade work. This changes
crates/stdlib/src/pickle.rs(+68/-17) on main basebcfad3554and appends two nonduplicative payload-cycle regressions to the existingextra_tests/snippets/stdlib_pickle.py(+41). It preserves the existing allocation/copy handling and private-name lookup.read,readline, andpeekslots, the buffer iterator, and every occupied memo slot, including sparse keys and repeated identities held by distinct strong slots.No Lib changes, new snippet files, Rust Python-behavior tests, collector changes, or CPython-only decorator changes are included.
Bounded validation
Source-bound candidate built with the main Ruff pin
9290a884; compiler/codegen/VM/stdlib/pylib/root were explicitly invalidated and actually recompiled. All tracked source hashes and symlink targets matched the reviewed source after building. Prior sealed binaries were preserved.An external review harness ran each mode in a separate fresh process, with a fixed 20-second deadline, 512 MiB sampled RSS, 6 GiB address-space limit, core dumps disabled, and resource reserve guards:
read/readline/peek/buffers/memo/proxycounts are 1/1/1/1/2/1. Main baseline reports 0/0/0/0/0/0.Two existing canonical CPython 3.15.0 compatibility methods,
test_priming_unpickler_memoandtest_reusing_unpickler_objects, pass on baseline, candidate and CPython, two tests and zero skips each. Their unchanged methods and setup were AST-selected into an external harness with_picklebindings and onlyio,_pickle, andunittestglobals. No broad pickle test-module import or whole-suite execution occurred.There were no crashes, hangs, guard stops, automatic retries, or weakened assertions. Inputs were locally serialized ordinary values; no malformed streams, stress/OOM, native-memory probes, reentrant finalizers or concurrent mutation were tested.
The existing pickle snippet now retains the stream and buffers cycle regressions. Canonical persistent-load reference-cycle and buffer-content tests do not cover these payload cycles. Only the new definitions and loop body were AST-selected and validated, one case per fresh guarded process: baseline fails both release assertions, while CPython and candidate pass both. The complete pre-existing snippet was not executed because its unrelated oversized-length cases are outside this local gate. Its required full pytest gate remains unrun.
CPython 3.15.0 oracle qualification
The pinned oracle does not pass every harness assertion. Its direct edge counts are 1/1/1/1/0/1, failing the two-slot memo assertion. A separately bounded
memo.copy()observation returns an empty dict immediately after assignment on CPython, whereas main RustPython preserves keys0and17with both holder identities.Source inspection of pinned
_pickle.cexplains this: the dict memo setter writes values into the old memo, then cleans that memo and installs the newly allocated empty array. Consequently the oracle's memo-cycle pass is vacuous for the intended cycle. We preserve this discrepancy rather than weaken the Rust ownership invariant or claim full oracle parity. Oracle stream/buffers/reinit modes pass independently.Safety and scope qualifications
__init__path only. Failure-path locals can still drop under the state lock; no reentrant-finalizer claim is made.Additional gates
Passed under the same build resource guard:
cargo check --locked --offline -p rustpython-stdlib --features threading(128s).cargo check --locked --offline -p rustpython-stdlib(non-threaded, default features; 54s).cargo clippy --locked --offline -p rustpython-stdlib --all-targets --features threading -- -D warnings(218s).prek run --files crates/stdlib/src/pickle.rs(4s; no source changes).All 3,631 source manifest entries matched after the native gates. The subsequently approved snippet-only addition is the sole manifest difference from the source-bound binary. Native source and sealed binary remain unchanged. Normal hooks were then rerun on both changed files and passed in 2s without rewrites; the two added cases were validated against that same sealed binary.
The required broader workspace/CAPI runtime suites and release pickle-module command have not been run and are not waived. This bounded validation is not a collector/threading soundness proof.
AI assistance: OpenAI assistant assisted with source review, implementation preparation and bounded validation.