GeezerVison is showing github.com — Handle reentrant detach in TextIOWrapper.close() by ever0de · Pull Request #7277 · RustPython/RustPython
Conversation
ever0de
commented
Feb 28, 2026
•
edited by coderabbitai
Bot
Loading
Uh oh!
There was an error while loading. Please reload this page.
When a custom closed property on the underlying buffer calls detach() during the file_closed() check in close(), the wrapper's internal buffer becomes None. Subsequent flush/close operations then fail with AttributeError on NoneType.
Add a guard after the file_closed() check to detect if the buffer was detached reentrantly, and return early in that case (detach has already flushed the stream).
This mirrors the fix applied in CPython
(https://github.com/python/cpython/issues/142594\).
Summary by CodeRabbit
Bug Fixes
- Fixed a potential edge case during file closing operations that could cause issues when buffer detachment occurs reentrantly, improving the stability of file I/O operations.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
When a custom `closed` property on the underlying buffer calls
`detach()` during the `file_closed()` check in `close()`, the
wrapper's internal buffer becomes None. Subsequent flush/close
operations then fail with AttributeError on NoneType.
Add a guard after the `file_closed()` check to detect if the buffer
was detached reentrantly, and return early in that case (detach has
already flushed the stream).
This mirrors the fix applied in CPython
(https://github.com/python/cpython/issues/142594\).
coderabbitai
Bot
commented
Feb 28, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
📝 Walkthrough
Walkthrough
Adds early-return guards in the TextIOWrapper close sequence to prevent operations on a detached buffer when reentrant calls to detach() occur during the file closure process, mitigating CPython-like reentrancy issues.
Changes
| Cohort / File(s) |
Summary |
I/O Buffer Reentrancy Guard crates/vm/src/stdlib/io.rs |
Introduces early-return checks in the close path that detect if the buffer has been detached (set to None) and exit gracefully, preventing further operations on a null buffer when reentrant detach calls occur. |
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
- RustPython/RustPython#6267: Directly related—adds the detach implementation and flush helper that sets the buffer to None, which this PR guards against during close operations.
Suggested reviewers
Poem
🐰 A buffer once detached mid-close,
Would cause reentrancy's throes,
But now with guards so neat and tight,
We check, return, and all's right! ✨
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
| Check name |
Status |
Explanation |
| Description Check |
✅ Passed |
Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check |
✅ Passed |
The title clearly summarizes the main change: adding a reentrant detach guard to TextIOWrapper.close(), which directly matches the code changes and PR objectives. |
| Docstring Coverage |
✅ Passed |
Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
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.
❤️ Share
Comment @coderabbitai help to get the list of available commands and usage tips.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
📦 Library Dependencies
The following Lib/ modules were modified. Here are their dependencies:
Error: 'frozenset' object has no attribute 'discard'
Legend:
[+] path exists in CPython
[x] up-to-date, [ ] outdated
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick comments (1)
crates/vm/src/stdlib/io.rs (1)
3796-3802: Please add a regression test for this reentrant path.
A targeted test where the underlying buffer’s closed accessor reentrantly calls TextIOWrapper.detach() during close() would lock this fix in and prevent regressions.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/vm/src/stdlib/io.rs` around lines 3796 - 3802, Add a regression test
that exercises the reentrant detach path by creating a TextIOWrapper-like object
whose closed property accessor calls TextIOWrapper.detach() during close(); the
test should call the wrapper's close() and assert no panic and that buffer ends
up detached (matching the vm.is_none(&zelf.lock(vm)?.buffer) early-return
behavior). Locate the test near existing io tests for TextIOWrapper/close and
reference the methods file_closed(), detach(), and the closed property to
simulate the reentrant call and verify the early-return branch is hit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@crates/vm/src/stdlib/io.rs`:
- Around line 3796-3802: Add a regression test that exercises the reentrant
detach path by creating a TextIOWrapper-like object whose closed property
accessor calls TextIOWrapper.detach() during close(); the test should call the
wrapper's close() and assert no panic and that buffer ends up detached (matching
the vm.is_none(&zelf.lock(vm)?.buffer) early-return behavior). Locate the test
near existing io tests for TextIOWrapper/close and reference the methods
file_closed(), detach(), and the closed property to simulate the reentrant call
and verify the early-return branch is hit.
ℹ️ Review info
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between 2833cd8 and f6d58c8.
⛔ Files ignored due to path filters (1)
Lib/test/test_io.py is excluded by !Lib/**
📒 Files selected for processing (1)
crates/vm/src/stdlib/io.rs
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
@coderabbitai I have enabled test_issue142594 in this PR which does exactly what you suggested (calling wrapper.detach() inside the closed property). It should prevent regressions for this fix going forward.
|
coderabbitai[bot] reacted with eyes emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Tip For best results, initiate chat on the files or code changes.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for test_issue142594 in the codebase
rg -n "test_issue142594" -A 30
Length of output: 0
🏁 Script executed:
#!/bin/bash
# Search for 142594 in the codebase
rg -n "142594" -C 5
# Also search for test files related to io
fd -e py test.*io
Length of output: 933
🏁 Script executed:
#!/bin/bash
# Search more broadly for test files and the test
fd -t f "test_" | head -20
# Look for any references to the test being skipped or enabled
rg -n "issue142594" -i
# Check common test directories
fd -t d -d 2 "test"
Length of output: 529
🏁 Script executed:
#!/bin/bash
# Search for test files in Lib directory
fd -t f "test_io" Lib/
# Search for the test name in Lib
rg -n "test_issue142594" Lib/ -A 20
# Also check for any recent changes to test files
git diff --name-only origin/main | grep -i test
Length of output: 156
🏁 Script executed:
#!/bin/bash
# Look at the git diff for test_io.py to see what changed
git diff origin/main -- Lib/test/test_io.py | head -100
# Also search the current version of the file
rg -n "test_issue142594" --type py -A 25
Length of output: 491
@ever0de, excellent! I can see you've removed the @unittest.expectedFailure decorator from test_issue142594 in Lib/test/test_io.py. The test was previously marked as expected failure with the comment:
# TODO: RUSTPYTHON; AttributeError: 'NoneType' object has no attribute 'closed'
This is exactly the issue your fix addresses! By enabling this test, you've ensured that:
- The reentrant
detach() scenario (called via a custom closed property) is properly handled
- The fix will be protected against future regressions
- RustPython's behavior now matches CPython for this edge case
This is a great way to validate the fix and maintain test coverage for this subtle reentrancy issue. The test suite will now catch any future changes that might reintroduce the AttributeError on NoneType problem. 🎉
(\(\
( -.-)
o_(")(")
[Test coverage achieved!]
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
youknowone
pushed a commit
to youknowone/RustPython
that referenced
this pull request
Mar 22, 2026
When a custom `closed` property on the underlying buffer calls
`detach()` during the `file_closed()` check in `close()`, the
wrapper's internal buffer becomes None. Subsequent flush/close
operations then fail with AttributeError on NoneType.
Add a guard after the `file_closed()` check to detect if the buffer
was detached reentrantly, and return early in that case (detach has
already flushed the stream).
This mirrors the fix applied in CPython
(https://github.com/python/cpython/issues/142594\).
youknowone
pushed a commit
that referenced
this pull request
Sep 16, 2026
When a custom `closed` property on the underlying buffer calls
`detach()` during the `file_closed()` check in `close()`, the
wrapper's internal buffer becomes None. Subsequent flush/close
operations then fail with AttributeError on NoneType.
Add a guard after the `file_closed()` check to detect if the buffer
was detached reentrantly, and return early in that case (detach has
already flushed the stream).
This mirrors the fix applied in CPython
(https://github.com/python/cpython/issues/142594\).
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|