Property tests for subprocess timeout & fail-fast reducers (#75) - #245
Property tests for subprocess timeout & fail-fast reducers (#75)#245leynos wants to merge 2 commits into
Conversation
The subprocess timeout and pipeline fail-fast paths are temporal and branch-heavy, with no isolated seam for their decisions. Extract two pure reducers so they can be property-tested without process state or a clock. _subprocess_timeout.py: _resolve_timeout_payload(exc, _TimeoutFallback) is the timeout-payload seam. A _SubprocessTimeoutError carries a payload that is used verbatim; a bare TimeoutError is resolved from a _TimeoutFallback (configured timeout, captured stdout/stderr, injected exit-time clock). Either branch yields a concrete timeout, so downstream TimeoutExpired reporting is consistent. _handle_subprocess_timeout now calls it and keeps the event-emit and raise side effects. _process_lifecycle.py: _stages_to_terminate(failure_index, done) selects which stages get a termination task — every stage except the failed one and any already-finished stage, each at most once. _terminate_pipeline_remaining_stages drives it (keeping the strict-length zip for scheduling). Add cuprum/unittests/test_subprocess_timeout_reducers.py with Hypothesis property tests proving: timeout-payload consistency (carried payload wins and ignores the fallback; bare timeouts resolve from the fallback; a missing configured timeout is an invariant error), the fail-fast selection is exactly the running non-failed stages with no double scheduling, and cleanup is idempotent (a second pass over settled stages selects nothing). Regenerate the maturin wheel-manifest snapshot for the new test file. Closes #75 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: 12 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 (4)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Reviewer's GuideExtracts two pure reducer helpers for subprocess timeout payload resolution and pipeline fail-fast termination selection, updates the timeout handler and termination logic to use them, and adds Hypothesis-based property tests to validate timeout payload consistency, no double-termination, and idempotent cleanup. Sequence diagram for updated subprocess timeout handlingsequenceDiagram
participant Caller
participant _handle_subprocess_timeout
participant _resolve_timeout_payload
participant _emit_exit_event
participant _raise_timeout_expired
participant _get_exit_code
Caller->>_handle_subprocess_timeout: exc, ctx
_handle_subprocess_timeout->>_resolve_timeout_payload: exc, _TimeoutFallback
_resolve_timeout_payload-->>_handle_subprocess_timeout: _SubprocessTimeoutDetails payload
_handle_subprocess_timeout->>_get_exit_code: ctx.process
_get_exit_code-->>_handle_subprocess_timeout: exit_code
_handle_subprocess_timeout->>_emit_exit_event: _SubprocessExitEvent
_handle_subprocess_timeout->>_raise_timeout_expired: _TimeoutContext, exc
_raise_timeout_expired-->>Caller: TimeoutExpired (exception)
Sequence diagram for updated fail-fast pipeline termination selectionsequenceDiagram
participant Caller
participant _terminate_pipeline_remaining_stages
participant _stages_to_terminate
participant asyncio_create_task as asyncio.create_task
participant _terminate_process_via_wait_task
participant asyncio_gather as asyncio.gather
Caller->>_terminate_pipeline_remaining_stages: processes, wait_tasks, failure_index, cancel_grace
_terminate_pipeline_remaining_stages->>_stages_to_terminate: failure_index, [wait_task.done()]
_stages_to_terminate-->>_terminate_pipeline_remaining_stages: list[int] targets
loop for each idx in targets
_terminate_pipeline_remaining_stages->>asyncio_create_task: _terminate_process_via_wait_task(process, wait_task, cancel_grace)
asyncio_create_task->>_terminate_process_via_wait_task: process, wait_task, cancel_grace
end
_terminate_pipeline_remaining_stages->>asyncio_gather: termination_tasks
asyncio_gather-->>Caller: termination completed
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
test_resolve_uses_carried_payload_for_subprocess_timeout_error took 7 @given arguments (threshold 4). Draw the captured details from a _timeout_details composite strategy (one argument) and use a fixed, distinctive fallback: configured_timeout=None would raise if the carried branch ever consulted it, so no raise plus == details proves the fallback is never touched. Code health 10.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
The subprocess timeout handler and the pipeline fail-fast cleanup are temporal and branch-heavy, with a risk of inconsistent timeout payloads, non-idempotent cleanup, and double-termination scheduling — and no isolated seam for their decisions (#75). This extracts two pure reducers and property-tests them without process state or a clock.
Seams
_resolve_timeout_payload(exc, _TimeoutFallback)(cuprum._subprocess_timeout) — the timeout-payload seam:_SubprocessTimeoutErrorcarries a payload captured on the stream-timeout path, used verbatim;TimeoutErroris resolved from a_TimeoutFallback(configured timeout, captured stdout/stderr, injected exit-time clock reading);timeout, so downstreamTimeoutExpiredreporting is consistent._handle_subprocess_timeoutnow calls it and keeps the exit-event emit and the raise._stages_to_terminate(failure_index, done)(cuprum._process_lifecycle) — the fail-fast selection: every stage except the failed one and any already-finished stage, each at most once._terminate_pipeline_remaining_stagesdrives it (keeping the strict-lengthzipfor scheduling).Tests
cuprum/unittests/test_subprocess_timeout_reducers.py(Hypothesis) proves the three #75 goals:Validation
Full gates green:
make check-fmt,make lint(ruff, interrogate 100%, pylint 10.00/10),make test(825 passed / 58 skipped incl. existing timeout & pipeline behaviour suites; Rust nextest 57/57). Wheel-manifest snapshot regenerated.Closes #75
🤖 Generated with Claude Code
Summary by Sourcery
Extract pure reducers for subprocess timeout payload resolution and fail-fast pipeline termination, and property-test their behaviour for consistency and idempotence.
New Features:
_TimeoutFallbackdata structure and_resolve_timeout_payloadreducer to unify subprocess timeout payload resolution._stages_to_terminatereducer to select pipeline stages for termination after fail-fast.Enhancements:
_handle_subprocess_timeoutto delegate payload resolution to a pure reducer for consistent timeout reporting._terminate_pipeline_remaining_stagesto use a precomputed termination target set, avoiding double-scheduling and ensuring idempotent cleanup.Tests: