Hypothesis fault-injection for the Rust pump's FD lifecycle (#74) - #244
Hypothesis fault-injection for the Rust pump's FD lifecycle (#74)#244leynos wants to merge 2 commits into
Conversation
The Rust inter-stage pump hands the raw pipe descriptors to native code under several partial-failure paths — FD extraction, reader-transport pause/resume, and blocking-mode switch/restore — that had no isolated seam for fault injection. Introduce two seams in a new cuprum/_pipeline_stream_fds.py module (the FD lifecycle also lifts _pipeline_streams back under the 400-line cap): - _BlockingModeGuard: an FD-state object that switches the descriptor pair to blocking mode, capturing prior state, and restores it — with the partial-failure rollback preserved. - _paused_reader: a context manager wrapping _pause_reader_transport so the resume can't be skipped on any exit (return, exception, cancel). _run_rust_pump is refactored (via _pump_over_raw_fds) to drive these, preserving behaviour; existing backend-selection tests are repointed to the new module and still pass. Add cuprum/unittests/test_pipeline_streams_fd_lifecycle.py with Hypothesis fault-injection covering the four #74 hazards: - leaked blocking state: a round-trip property plus an injected toggle-failure asserting no descriptor is left switched; - missing resume: _paused_reader resumes once on both exits and skips resume when the transport can't pause; - wrong fallback: a blocking-toggle failure returns the Python-fallback signal and still resumes the reader; - swallowed unexpected errors: _surface_unexpected_pipe_failures raises the first non-pipe exception and suppresses broken-pipe/reset. Regenerate the maturin wheel-manifest snapshot for the two new files. Closes #74 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Reviewer's GuideRefactors the Rust pump FD lifecycle management into a dedicated module with reusable blocking/pause guards, updates the Rust-pump dispatch path to use these abstractions, and adds focused property- and fault-injection tests around FD blocking state, reader pause/resume, and error surfacing behavior. Sequence diagram for _pump_over_raw_fds FD lifecycle and fallbacksequenceDiagram
participant Pump as _pump_over_raw_fds
participant Reader as asyncio_StreamReader
participant Writer as asyncio_StreamWriter
participant Guard as _BlockingModeGuard
participant Rust as rust_pump_stream
Pump->>Reader: _paused_reader(reader)
activate Reader
Pump->>Pump: _drain_reader_buffer(reader, writer)
Pump->>Guard: _BlockingModeGuard.engage(reader_fd, writer_fd)
alt [OSError from engage]
Guard-->>Pump: OSError
Pump-->>Pump: return False
else [engage ok]
Pump->>Rust: loop.run_in_executor(None, rust_pump_stream, reader_fd, writer_fd)
Pump->>Guard: restore()
Pump-->>Pump: return True
end
deactivate Reader
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 904477a19b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The abstraction policy in AGENTS.md requires a new abstraction's scope and reuse policy to be recorded in the project documentation, and _pipeline_stream_fds appeared nowhere under docs/. Add a "Raw descriptor lifecycle" subsection to design-doc 13.6, which already covers the asyncio/Rust hand-off this module serves. It states the partial-failure paths the module exists to contain, what _BlockingModeGuard and _paused_reader each guarantee, the single consumer, and the reuse policy: further descriptor-lifecycle concerns for this hand-off belong here, but the seams are not a general-purpose descriptor utility. cuprum-design.md is already indexed by docs/contents.md. Also add the worked usage example AGENTS.md requires to _pump_over_raw_fds, covering both the handled and fall-back outcomes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
The Rust inter-stage pump takes over the raw pipe descriptors from asyncio for the duration of a transfer, across several partial-failure paths — FD extraction, reader-transport pause/resume, and blocking-mode switch/restore — with no isolated seam for fault injection (#74).
Seams
New module
cuprum/_pipeline_stream_fds.py(extracting the FD lifecycle also lifts_pipeline_streams.pyback under the 400-line health cap: 300 + 166):_BlockingModeGuard— the FD-state object.engageswitches the descriptor pair to blocking mode capturing prior state (rolling back a partial change on failure);restorereturns them to that state._paused_reader— a context manager wrapping_pause_reader_transportso the resume cannot be skipped on any exit path (normal return, exception, or cancellation)._run_rust_pumpis refactored (via_pump_over_raw_fds) to drive these. Behaviour is preserved — the existingtest_pipeline_stream_backend_selection.pysuite (repointed to the new module) still passes, including the pause→drain→restore→resume ordering and the writer-toggle rollback tests.Fault-injection tests
cuprum/unittests/test_pipeline_streams_fd_lifecycle.pycovers the four hazards #74 names:_paused_readerresumes exactly once on normal and exception exit; skips resume when the transport can't pause or pausing raisesFalse) and still resumes the reader_surface_unexpected_pipe_failuresraises the first non-pipe exception and suppressesBrokenPipeError/ConnectionResetErrorValidation
Full gates green:
make check-fmt,make lint(ruff, interrogate 100%, pylint 10.00/10 — both modules under the line cap),make test(762 passed / 47 skipped; Rust nextest 57/57). Wheel-manifest snapshot regenerated for the two new files.Closes #74
🤖 Generated with Claude Code
Summary by Sourcery
Isolate the Rust pump’s raw file-descriptor lifecycle behind dedicated helpers and add targeted fault-injection tests for pause/resume and blocking-mode behaviour.
Enhancements:
Tests: