Skip to content

Conversation

@youknowone
Copy link
Member

@youknowone youknowone commented Dec 28, 2025

Summary by CodeRabbit

  • New Features

    • Per-field conditional compilation for struct initialization
    • Expanded non-blocking I/O behavior with coordinated closing support
  • Bug Fixes

    • Improved buffered I/O state consistency and edge-case handling during concurrent close/read/write
    • Reduced deadlock/reentrancy risk when accessing underlying streams
  • API Changes

    • I/O serialization method renamed and error messages updated
    • Read/write/readinto now expose non-blocking (Option-wrapped) results; added closing and raw-access helpers

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 28, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Per-field #[cfg(...)] handling was added to generated struct-sequence initializers, and the I/O subsystem gained atomic closing coordination, non-blocking semantics (Option-wrapped reads/writes), and API updates (renamed __reduce____getstate__) with buffering and flush/rewind adjustments.

Changes

Cohort / File(s) Summary
Struct Sequence Code Generation
crates/derive-impl/src/pystructseq.rs
try_from_elements now initializes each field with per-field cfg_attrs awareness; visible/skipped field inits are conditionally compiled per field, falling back to previous logic when no cfg present.
I/O Subsystem — Core File
crates/vm/src/stdlib/io.rs
Large changes to buffered and raw I/O: added AtomicBool closing flags, closing() accessors, get_raw_unlocked(), flush/rewind adjustments, and coordinated closing checks across buffered types.
I/O Subsystem — API Signatures
crates/vm/src/stdlib/io.rs
Replaced __reduce__ with __getstate__ and updated pickle error messages. FileIO read/readinto/write signatures now return Option-wrapped results to represent non-blocking EAGAIN semantics.
I/O Subsystem — Non-blocking & Buffering Behavior
crates/vm/src/stdlib/io.rs
Read/write/readinto flows updated for non-blocking paths (returning None on would-block), partial buffering now maps to BlockingIOError with EAGAIN semantics, and pre-read flush/rewind added to keep buffers consistent.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant TextIO as TextIOWrapper
    participant Buf as BufferedReader/Writer
    participant Raw as FileIO
    participant Kernel as OS/Kernel

    rect rgb(235,245,255)
    Note over Client,Kernel: Non-blocking read path (high-level)
    end

    Client->>TextIO: read(size, non_blocking=True)
    TextIO->>Buf: read(size, non_blocking=True)
    alt data in buffer
        Buf-->>TextIO: Option<Some(bytes)>
        TextIO-->>Client: Some(bytes)
    else need raw I/O
        Buf->>Raw: read(size, non_blocking=True)
        Raw->>Kernel: sys_read(non-blocking)
        alt kernel returns bytes
            Kernel->>Raw: bytes
            Raw-->>Buf: Option<Some(bytes)>
            Buf-->>TextIO: Some(bytes)
            TextIO-->>Client: Some(bytes)
        else EAGAIN
            Kernel->>Raw: EAGAIN
            Raw-->>Buf: Option<None>
            Buf-->>TextIO: None
            TextIO-->>Client: None
        end
    end
Loading
sequenceDiagram
    participant Writer
    participant BufW as BufferedWriter
    participant Buffer as LocalBuffer
    participant Raw as FileIO
    participant Closer as AtomicCloser

    rect rgb(255,245,235)
    Note over Writer,Closer: Write path with closing coordination
    end

    Writer->>BufW: write(data)
    BufW->>Closer: check closing()
    alt closing true
        BufW->>Closer: spin-wait / short yield
        Closer-->>BufW: still closing -> error
        BufW-->>Writer: raise value/error (write to closed)
    else closing false
        BufW->>Buffer: append data
        BufW->>Raw: flush()
        alt raw write succeeds
            Raw-->>BufW: Ok(n)
            BufW-->>Writer: Some(n)
        else EAGAIN / BlockingIOError
            Raw-->>BufW: BlockingIOError (EAGAIN)
            BufW->>Buffer: shift buffer, update positions
            BufW-->>Writer: raise BlockingIOError (EAGAIN)
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through fields with tiny cfg bows,
I guarded streams where the wild wind blows,
Atomic flags hold the closing tight,
Non-blocking bytes dance in moonlight,
sniff — code and carrots, both just right.

Pre-merge checks and finishing touches

✅ 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 reflects the main changes in the PR: upgrading test_io from Python 3.13.11 and fixing multiple IO-related issues across buffered I/O, file I/O, and text I/O implementations.
Docstring Coverage ✅ Passed Docstring coverage is 96.55% which is sufficient. The required threshold is 80.00%.

📜 Recent review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e54c2db and f19db85.

⛔ Files ignored due to path filters (1)
  • Lib/test/test_io.py is excluded by !Lib/**
📒 Files selected for processing (2)
  • crates/derive-impl/src/pystructseq.rs
  • crates/vm/src/stdlib/io.rs

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.

@youknowone youknowone force-pushed the io branch 2 times, most recently from 9fd139d to e54c2db Compare December 28, 2025 10:19
@youknowone youknowone marked this pull request as ready for review December 28, 2025 10:50
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: 1

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3600b66 and e54c2db.

⛔ Files ignored due to path filters (1)
  • Lib/test/test_io.py is excluded by !Lib/**
📒 Files selected for processing (2)
  • crates/derive-impl/src/pystructseq.rs
  • crates/vm/src/stdlib/io.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • crates/derive-impl/src/pystructseq.rs
  • crates/vm/src/stdlib/io.rs
🧬 Code graph analysis (1)
crates/derive-impl/src/pystructseq.rs (4)
crates/vm/src/stdlib/ctypes/function.rs (2)
  • f (987-987)
  • None (1687-1687)
crates/stdlib/src/ssl/compat.rs (2)
  • v (1435-1435)
  • v (1717-1717)
crates/vm/src/vm/context.rs (1)
  • none (365-367)
crates/vm/src/types/structseq.rs (2)
  • try_from_elements (172-174)
  • Self (284-287)

Comment on lines 2009 to 2021
fn write(&self, obj: ArgBytesLike, vm: &VirtualMachine) -> PyResult<usize> {
// Check if close() is in progress (Issue #31976)
// If closing, wait for close() to complete by spinning until raw is closed
if self.writer().closing().load(Ordering::Acquire) {
// Wait for close() to complete - loop until raw.closed is True
loop {
let raw = {
let data = self.writer().lock(vm)?;
match &data.raw {
Some(raw) => raw.to_owned(),
None => break, // detached
}
};
if file_closed(&raw, vm)? {
break;
}
// Yield to other threads
std::thread::yield_now();
}
return Err(vm.new_value_error("write to closed file".to_owned()));
}
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

Potential unbounded spin-wait in write when closing.

The spin-wait loop has no timeout or iteration limit. If close() stalls or raw.closed never becomes True, write() could hang indefinitely. While yield_now() prevents busy-waiting, it doesn't guarantee forward progress.

Consider adding a maximum iteration count or timeout to prevent potential deadlocks, or document the assumption that close() will always complete in finite time.

@youknowone youknowone merged commit 476b75d into RustPython:main Dec 28, 2025
12 of 13 checks passed
@youknowone youknowone deleted the io branch December 28, 2025 11:36
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.

1 participant