Skip to content

io.TextIoWrapper.reconfigure to always flush#7281

Merged
youknowone merged 2 commits intoRustPython:mainfrom
ShaharNaveh:io-reconfigure-flush
Mar 1, 2026
Merged

io.TextIoWrapper.reconfigure to always flush#7281
youknowone merged 2 commits intoRustPython:mainfrom
ShaharNaveh:io-reconfigure-flush

Conversation

@ShaharNaveh
Copy link
Contributor

@ShaharNaveh ShaharNaveh commented Feb 28, 2026

fix #6588

Summary by CodeRabbit

  • Bug Fixes

    • Improved I/O buffer flushing behavior during reconfiguration to unconditionally flush all pending data, ensuring more consistent I/O operations when encoding or other parameters change.
  • Tests

    • Added test cases validating error handling and behavior during encoding reconfiguration scenarios.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 28, 2026

📝 Walkthrough

Walkthrough

The PR fixes a panic in TextIOWrapper's reentrant reconfigure call by unconditionally flushing pending data after handling encoding/error/newline changes, rather than conditionally based on a flag. A test case reproducing the reentrant scenario is added.

Changes

Cohort / File(s) Summary
Reconfigure Flush Logic
crates/vm/src/stdlib/io.rs
Removed flush_on_reconfigure flag and reworked reconfigure flow to unconditionally flush any pending data and underlying buffer after handling configuration changes, eliminating conditional flush behavior.
Reentrancy Test Coverage
extra_tests/snippets/stdlib_io.py
Added import of TextIOWrapper from io module and created Gh6588 test class that reproduces the reentrant writelines scenario by reconfiguring TextIOWrapper encoding during a write operation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • #6565 — Modifies the same io.rs flush and reconfigure behavior with buffering/flush adjustments (flush-before-read and related flush paths).

Poem

🐰 Flush it all away, no flags to delay,
Reconfigure flows on a clearer path today,
Reentrancy's panic now stays at bay,
One unconditional flush saves the day! 💧

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR fails to address the core requirements: preventing Rust panics and detecting/rejecting reentrant calls to TextIOWrapper.reconfigure. The changes only unconditionally flush data but do not implement mutex locking improvements or reentrant call detection to prevent panics as required by issue #6588.
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 accurately describes the main change: making TextIOWrapper.reconfigure always flush, which is directly reflected in the code changes.
Out of Scope Changes check ✅ Passed The code changes are minimal and directly related to the PR objective of ensuring TextIOWrapper.reconfigure always flushes, with no extraneous modifications.

✏️ 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.

@ShaharNaveh ShaharNaveh marked this pull request as ready for review March 1, 2026 00:48
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@extra_tests/snippets/stdlib_io.py`:
- Line 2: Remove the unused imports BytesIO and StringIO from the import
statement in stdlib_io.py; edit the line that currently imports BufferedReader,
BytesIO, FileIO, RawIOBase, StringIO, TextIOWrapper and delete BytesIO and
StringIO so only actually used symbols (e.g., BufferedReader, FileIO, RawIOBase,
TextIOWrapper) remain, resolving the F401 lint warnings.
- Around line 63-86: The test fails early with AttributeError because Gh6588
lacks a flush method, so TextIOWrapper.writelines() raises instead of exercising
the reentrant reconfigure-path; update the Gh6588 class to implement a
flush(self) method (e.g., forward to self.textio.flush() or a no-op) so
TextIOWrapper (created as textio via TextIOWrapper(raw, ...)) can call flush and
the test will exercise the reentrant call in write/reconfigure rather than
short-circuiting on missing flush.

ℹ️ 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 53fca08.

📒 Files selected for processing (2)
  • crates/vm/src/stdlib/io.rs
  • extra_tests/snippets/stdlib_io.py

@@ -1,5 +1,5 @@
import os
from io import BufferedReader, BytesIO, FileIO, RawIOBase, StringIO
from io import BufferedReader, BytesIO, FileIO, RawIOBase, StringIO, TextIOWrapper
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused BytesIO and StringIO imports.

Line 2 introduces unused imports that are already flagged by static analysis (F401), which adds noise to linting.

Proposed fix
-from io import BufferedReader, BytesIO, FileIO, RawIOBase, StringIO, TextIOWrapper
+from io import BufferedReader, FileIO, RawIOBase, TextIOWrapper
🧰 Tools
🪛 Flake8 (7.3.0)

[error] 2-2: 'io.BytesIO' imported but unused

(F401)


[error] 2-2: 'io.StringIO' imported but unused

(F401)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extra_tests/snippets/stdlib_io.py` at line 2, Remove the unused imports
BytesIO and StringIO from the import statement in stdlib_io.py; edit the line
that currently imports BufferedReader, BytesIO, FileIO, RawIOBase, StringIO,
TextIOWrapper and delete BytesIO and StringIO so only actually used symbols
(e.g., BufferedReader, FileIO, RawIOBase, TextIOWrapper) remain, resolving the
F401 lint warnings.

Comment on lines +63 to +86
class Gh6588:
def __init__(self):
self.textio = None
self.closed = False

def writable(self):
return True

def readable(self):
return False

def seekable(self):
return False

def write(self, data):
self.textio.reconfigure(encoding="utf-8")
return len(data)


raw = Gh6588()
textio = TextIOWrapper(raw, encoding="utf-8", write_through=True)
raw.textio = textio
with assert_raises(AttributeError):
textio.writelines(["x"])
Copy link
Contributor

@coderabbitai coderabbitai bot Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This regression test currently validates missing flush, not reentrancy handling.

Gh6588 has no flush, so Line 86 can fail with AttributeError before validating the reentrant-lock path from issue #6588. This can let the panic/reentrancy bug regress undetected.

Proposed test adjustment to exercise reentrant `reconfigure` path
 class Gh6588:
@@
     def write(self, data):
         self.textio.reconfigure(encoding="utf-8")
         return len(data)
+
+    def flush(self):
+        self.textio.reconfigure(encoding="utf-8")
@@
-with assert_raises(AttributeError):
+with assert_raises(RuntimeError):
     textio.writelines(["x"])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extra_tests/snippets/stdlib_io.py` around lines 63 - 86, The test fails early
with AttributeError because Gh6588 lacks a flush method, so
TextIOWrapper.writelines() raises instead of exercising the reentrant
reconfigure-path; update the Gh6588 class to implement a flush(self) method
(e.g., forward to self.textio.flush() or a no-op) so TextIOWrapper (created as
textio via TextIOWrapper(raw, ...)) can call flush and the test will exercise
the reentrant call in write/reconfigure rather than short-circuiting on missing
flush.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ShaharNaveh is this comment correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ShaharNaveh is this comment correct?

No, the entire point of the test is to align the behavior to be like cpython and to get an AttributeError

@youknowone youknowone merged commit ca390dc into RustPython:main Mar 1, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Panic during reentrant writelines via TextIOWrapper.reconfigure

2 participants