Repository navigation
Keep a bytearray exported while it formats with % - #9147
Conversation
bytearray.__mod__ copied the format and ran the arguments' __repr__,
__str__, __bytes__ and __index__ without holding an export, so a callback
could clear or extend the format and formatting went on with the old
copy:
fmt = bytearray(b"%a end")
class S:
def __repr__(self):
fmt.clear()
return "E"
fmt % S() # bytearray(b'E end'), fmt left empty
CPython holds a buffer on the format for the whole call, so the resize
fails with BufferError and the format is left as it was. Taking an export
for the duration of __mod__ does the same here, the way bytearray.join
already does for its separator.
This lets test_mod_concurrent_mutation in test_bytes run again.
Assisted-by: Claude Code:claude-opus-5-5
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. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
📦 Library DependenciesThe following Lib/ modules were modified. Here are their dependencies: :4: SyntaxWarning: invalid decimal literal dependencies:
dependent tests: (17 tests)
[ ] lib: cpython/Lib/asyncio dependencies:
dependent tests: (8 tests)
[ ] test: cpython/Lib/test/test_bytes.py (TODO: 14) dependencies: dependent tests: (no tests depend on bytes) Legend:
|
Summary
test_mod_concurrent_mutationintest_byteswas still skipped as a segfault. #8524 fixed the crash by formatting a copy of the bytearray, but nothing stopped a conversion callback from resizing the format meanwhile, so the test'sBufferErrornever came:Here this returned
bytearray(b'E end')and leftfmtempty. CPython 3.15 raisesBufferError: Existing exports of data: object cannot be re-sizedandfmtkeepsb"%a end".__mod__now holds an export for the whole call, which is whatjoinalready does for its separator. The format is still copied, so one thing still differs: an in-place change that keeps the length (e.g.fmt[4] = ord("r")from inside__bytes__) is not seen, where CPython formats the changed buffer. No CPython test covers that, and fixing it means not copying, so I left it.With the skip removed, the test fails before the change (
BufferError not raised by __mod__) and passes after. Nine variants of this case match CPython 3.15.0.test_bytespasses 3 runs out of 3, andtest_memoryview,test_bufferandtest_formatpass too. Workspacecargo testwith the AGENTS.md excludes,clippy -Dwarnings,cargo fmt --checkand the prek hooks (includingcheck_redundant_patches) are clean. Linux release build only.Written with Claude Code (claude-opus-5-5) as a tool, and I went over the change and the results before opening this.
Summary by CodeRabbit