GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Handle reentrant detach in TextIOWrapper.close() by ever0de · Pull Request #7277 · RustPython/RustPython

Conversation

@ever0de

@ever0de ever0de commented Feb 28, 2026 •
edited by coderabbitai Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

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.

All reactions
@ever0de
Handle reentrant detach in TextIOWrapper.close()
f6d58c8
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

coderabbitai Bot commented Feb 28, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor
📝 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

  • youknowone

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
  • X
  • Mastodon
  • Reddit
  • LinkedIn

Comment @coderabbitai help to get the list of available commands and usage tips.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@github-actions

github-actions Bot commented Feb 28, 2026

Copy link
Copy Markdown
Contributor

📦 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
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

coderabbitai[bot]
coderabbitai Bot reviewed Feb 28, 2026
View reviewed changes

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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.

All reactions
@ever0de

ever0de commented Feb 28, 2026

Copy link
Copy Markdown
Contributor Author

@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
All reactions
  • 👀 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@coderabbitai

coderabbitai Bot commented Feb 28, 2026

Copy link
Copy Markdown
Contributor

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:

  1. The reentrant detach() scenario (called via a custom closed property) is properly handled
  2. The fix will be protected against future regressions
  3. 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!]
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

youknowone
youknowone approved these changes Mar 1, 2026
View reviewed changes
@youknowone
youknowone merged commit be39a67 into RustPython:main Mar 1, 2026
14 checks passed

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
@ever0de
Handle reentrant detach in TextIOWrapper.close() (RustPython#7277)
757ff94
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
@ever0de
Handle reentrant detach in TextIOWrapper.close() (#7277)
62e1c11
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\).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@coderabbitai coderabbitai[bot] coderabbitai[bot] left review comments
@youknowone youknowone youknowone approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

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.

2 participants

@ever0de @youknowone
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.