Add FailureHandler and CompletionHandler base classes for dependency hooks#299
Add FailureHandler and CompletionHandler base classes for dependency hooks#299chrisguidry merged 10 commits intomainfrom
Conversation
…hooks Introduces two new abstract base classes that let dependencies control post-execution flow: - `FailureHandler` - controls what happens when a task fails. `Retry` now inherits from this and implements `handle_failure()` to schedule retries. - `CompletionHandler` - controls what happens after task completion. `Perpetual` now inherits from this and implements `on_complete()` to schedule the next execution. This follows the pattern established by `Runtime` for `Timeout`: the Worker delegates control to specialized dependencies rather than knowing the details itself. The Worker's `_retry_if_requested()` and `_perpetuate_if_requested()` methods are removed (~50 lines), with that logic now living in the dependencies. Also consolidates the duplicate `ms()`/`_ms()` duration formatting functions into a single `format_duration()` in `_base.py`, which fixed the remaining coverage gap. Co-Authored-By: Claude Opus 4.5 <[email protected]>
|
📚 Documentation has been built for this PR! You can download the documentation directly here: |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #299 +/- ##
========================================
Coverage 99.63% 99.64%
========================================
Files 96 98 +2
Lines 9608 9766 +158
Branches 463 468 +5
========================================
+ Hits 9573 9731 +158
Misses 20 20
Partials 15 15
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3faac76684
ℹ️ 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".
Refactors FailureHandler.handle_failure() and CompletionHandler.on_complete() to accept (execution, outcome) instead of multiple parameters. TaskOutcome bundles duration, result, and exception into a clean dataclass. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Gives developers flexibility to schedule the next execution using whatever
they have handy - a timedelta ("run again in 5 minutes") or an absolute
datetime ("run at midnight").
For Retry, keeps in_() as a backwards-compatible alias for after().
Closes #297
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
When a FailureHandler returns True (e.g., Retry schedules a retry), we should not call mark_as_failed() since that would publish a FAILED state that callers using get_result() could observe. Also fixes stricken tasks (e.g., from run_at_most) to be properly marked as cancelled so their keys get TTL. Adds tests verifying: - Retrying tasks have SCHEDULED state, not FAILED - Exhausted retries properly mark tasks as FAILED Thanks to Codex for catching this semantic issue. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Now that stricken tasks are properly marked as CANCELLED, get_result() needs to handle this state. It now raises asyncio.CancelledError when the task was cancelled. Adds test for get_result() on cancelled tasks. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Verifies that a Perpetual task that fails still gets rescheduled for its next execution. After running 3 times (all failures), the state correctly shows FAILED from the last execution. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Verifies that a task can have both Retry and Perpetual dependencies. On failure, Retry handles it first (if attempts remain). When retries are exhausted, Perpetual reschedules the next run with attempt reset. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Verifies that when a task calls perpetual.after() to set a custom delay and then fails, the next perpetual run still uses that delay. Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add ExecutionCancelled exception for get_result() on cancelled tasks, avoiding conflation with asyncio.CancelledError semantics - Split test_dependencies_core.py into focused test files: - test_dependency_uniqueness.py (single=True constraint tests) - test_handler_semantics.py (FailureHandler/CompletionHandler behavior) - test_dependencies_core.py (core dependency injection tests) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Summary
Introduces two new abstract base classes that let dependencies control post-execution flow:
FailureHandler- controls what happens when a task fails.Retrynow inherits from this and implementshandle_failure()to schedule retries.CompletionHandler- controls what happens after task completion.Perpetualnow inherits from this and implementson_complete()to schedule the next execution.This follows the pattern established by
RuntimeforTimeout: the Worker delegates control to specialized dependencies rather than knowing the details itself. The Worker's_retry_if_requested()and_perpetuate_if_requested()methods are removed (~50 lines), with that logic now living in the dependencies.The dependency hierarchy now looks like:
All three have
single = Truebecause only one thing can control each aspect.Also adds
after(timedelta)andat(datetime)methods to bothPerpetualandRetry, giving developers flexibility to schedule the next execution using whatever they have handy. ForRetry, the oldin_()method is kept as a backwards-compatible alias.Closes #297
🤖 Generated with Claude Code