mirror of
https://github.com/hatchet-dev/hatchet.git
synced 2026-01-01 06:11:02 -06:00
* feat: first pass at dependency injection * feat: add DI example + tests * feat: finish up tests * feat: docs * chore: gen * chore: lint * chore: changelog + ver * fix: split up paragraphs * refactor: improve impl * chore: gen * feat: inject input + ctx into deps * chore: gen * [Python] Feat: More use of `logger.exception` (#2069) * feat: add more instances of `logger.exception` * chore: ver * chore: changelog * fix: one more error log * chore: gen * chore: gen * chore: lint * fix: improve shutdown * chore: changelog * unwind: exit handler * feat: task run error * feat: improve error serde with more context * chore: changelog * fix: changelog * chore: gen * fix: rm celpy + lark dep, casing issues * chore: changelog * fix: respect log levels over the API * fix: changelog * refactor: rename log forwarder * fix: circular import
84 lines
2.1 KiB
Python
84 lines
2.1 KiB
Python
import json
|
|
from datetime import timedelta
|
|
|
|
from hatchet_sdk import Context, EmptyModel, Hatchet
|
|
from hatchet_sdk.exceptions import TaskRunError
|
|
|
|
hatchet = Hatchet(debug=False)
|
|
|
|
ERROR_TEXT = "step1 failed"
|
|
|
|
# > OnFailure Step
|
|
# This workflow will fail because the step will throw an error
|
|
# we define an onFailure step to handle this case
|
|
|
|
on_failure_wf = hatchet.workflow(name="OnFailureWorkflow")
|
|
|
|
|
|
@on_failure_wf.task(execution_timeout=timedelta(seconds=1))
|
|
def step1(input: EmptyModel, ctx: Context) -> None:
|
|
# 👀 this step will always raise an exception
|
|
raise Exception(ERROR_TEXT)
|
|
|
|
|
|
# 👀 After the workflow fails, this special step will run
|
|
@on_failure_wf.on_failure_task()
|
|
def on_failure(input: EmptyModel, ctx: Context) -> dict[str, str]:
|
|
# 👀 we can do things like perform cleanup logic
|
|
# or notify a user here
|
|
|
|
# 👀 Fetch the errors from upstream step runs from the context
|
|
print(ctx.task_run_errors)
|
|
|
|
return {"status": "success"}
|
|
|
|
|
|
|
|
|
|
# > OnFailure With Details
|
|
# We can access the failure details in the onFailure step
|
|
# via the context method
|
|
|
|
on_failure_wf_with_details = hatchet.workflow(name="OnFailureWorkflowWithDetails")
|
|
|
|
|
|
# ... defined as above
|
|
@on_failure_wf_with_details.task(execution_timeout=timedelta(seconds=1))
|
|
def details_step1(input: EmptyModel, ctx: Context) -> None:
|
|
raise Exception(ERROR_TEXT)
|
|
|
|
|
|
# 👀 After the workflow fails, this special step will run
|
|
@on_failure_wf_with_details.on_failure_task()
|
|
def details_on_failure(input: EmptyModel, ctx: Context) -> dict[str, str | None]:
|
|
error = ctx.get_task_run_error(details_step1)
|
|
|
|
if not error:
|
|
return {"status": "unexpected success"}
|
|
|
|
# 👀 we can access the failure details here
|
|
assert isinstance(error, TaskRunError)
|
|
|
|
if "step1 failed" in error.exc:
|
|
return {
|
|
"status": "success",
|
|
"failed_run_external_id": error.task_run_external_id,
|
|
}
|
|
|
|
raise Exception("unexpected failure")
|
|
|
|
|
|
|
|
|
|
def main() -> None:
|
|
worker = hatchet.worker(
|
|
"on-failure-worker",
|
|
slots=4,
|
|
workflows=[on_failure_wf, on_failure_wf_with_details],
|
|
)
|
|
worker.start()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|