Hera Python SDK
repository·main·Indexed 21 days ago
https://github.com/argoproj-labs/heraHera is a Python SDK designed to make Argo Workflows simple and intuitive. It allows developers to construct and submit Workflows entirely in Python, turning Python functions into containerized templates using the @script decorator. The SDK supports orchestration via DAGs and Steps, and provides integrations for authentication with Argo Workflows clusters, optional CLI tools, and async client support.
What's inside Hera
- Hera is a Python library designed to construct and submit Argo Workflows. It provides a Pythonic interface to the Argo API, allowing you to express any Argo Workflow configuration that is possible in YAML directly in Python. It uses a combination of hand-written custom classes for ease of use and auto-generated classes from the Argo Workflows OpenAPI specification to ensure full feature coverage.
Use template-level lifecycle hooks
mainLifecycle hooks at the template level allow you to execute logic based on the status of aSteporTask. Common triggers include when a template reaches aRunningorSucceededstatus. These are used to manage dependencies and cleanup within the workflow lifecycle.Choose between Inline and Runner Scripts
mainHera provides two ways to define scripts. Runner scripts are the recommended path because they offer a stronger feature set. However, because they involve building images, the iterative development process can be more cumbersome than inline scripts. It is recommended to set up a CI/CD solution to manage the runner script lifecycle.Implement recursion in Argo Workflows
mainIndividual
SteporTaskobjects can specify theStepsorDAGtemplate they belong to, enabling recursive template invocations.Warning: You must implement a break condition. Do not make the first
SteporTaskthe parentStepsorDAGwithout a condition, as this will cause the Argo controller to expand the definition indefinitely, potentially crashing the cluster.Use the `result` output parameter to capture stdout
mainIn Hera, when a function decorated with
@script()is called within aStepscontext, it returns aStepobject. This object has aresultproperty that captures the entirestdoutof the template. This can be passed directly as an argument to subsequent steps.Warning: The
resultvalue includes the entire stdout, including all log lines. For more predictable behavior and to avoid capturing log noise, it is recommended to use named output parameters instead.@script() def hello(message: str): print(f"Hello {message}") @script() def repeat_back(message: str): print(f"You just said: '{message}'") with Workflow(generate_name="get-result-", entrypoint="steps") as w: with Steps(name="steps"): hello_step = hello(arguments={"message": "world!"}) repeat_back(arguments={"message": hello_step.result}) w.create()How DAGs work in Hera
mainDAGs (Directed Acyclic Graphs) are composed of
Tasks. UnlikeSteps, which use context managers for parallelism, DAGs require you to explicitly specify dependencies between tasks using the right-shift (>>) operator. This operator tells Argo which tasks must complete before a subsequent task begins. Tasks without defined dependencies will start immediately upon Workflow execution.Key differences from Steps:
- Explicit Dependencies: You must use
>>to link tasks. - Flexibility: You can define tasks anywhere and incrementally build the graph by adding dependencies across multiple lines or even importing tasks from other files.
from hera.workflows import DAG, Workflow, script @script() def echo(message): print(message) with Workflow(generate_name="dag-diamond-", entrypoint="diamond") as w: with DAG(name="diamond"): A = echo(name="A", arguments={"message": "A"}) B = echo(name="B", arguments={"message": "B"}) C = echo(name="C", arguments={"message": "C"}) D = echo(name="D", arguments={"message": "D"}) A >> [B, C] >> D- Explicit Dependencies: You must use
How Hera and Argo Workflows work together
mainHera is a Python-first SDK designed to make Argo Workflows intuitive. It follows a pattern where orchestration logic (the structure of the workflow) is kept separate from the business logic (the code inside your functions).
By using the
@scriptdecorator, your Python functions are automatically converted into containerized templates that run on Kubernetes, providing full access to Argo Workflows' capabilities while allowing you to write native Python code.How Inline vs Runner Script Templates differ
mainHera provides two ways to transform Python functions into Argo Workflow templates using the
@scriptdecorator:Feature Inline Script ( @script())Runner Script ( @script(constructor="runner", image="..."))Implementation Function body is dumped into the sourcefield of the YAML.Code is executed via the hera.workflows.runnermodule inside a container.Imports Must be inside the function body. Can be module-level (standard Python style). Complexity Simple, good for prototyping. Powerful, better for production and complex data. Dependencies Relies on the container image having the required libs. Requires a custom OCI image containing your code/dependencies. How decorators work in Hera
mainHera decorators are members of the
Workflow(orWorkflowTemplate) class. They are used to link Python functions to specific Argo Workflow template types. Instead of using context managers, you declare aWorkflowTemplateobject and use its methods as decorators to define templates.Key Concepts:
- Template Declaration: You must declare a
WorkflowTemplateorWorkflowinstance upfront (e.g.,w = WorkflowTemplate(name="my-template")). - Input/Output Handling: To declare inputs and outputs for your templates, you must use the
InputandOutputPydantic classes fromhera.workflows. This allows Hera to deduce the template schema from the function signature. - Local Execution: The
dagandstepsdecorators allow you to run and test functions locally in pure Python. However, Argo-specific features like expressions orwith_itemsloops will not function during local execution.
from hera.workflows import Input, Output, WorkflowTemplate w = WorkflowTemplate(name="my-template") class MyInput(Input): user: str class MyOutput(Output): my_str: str @w.script() def hello_world(my_input: MyInput) -> MyOutput: output = MyOutput() output.my_str = f"Hello {my_input.user}" return output- Template Declaration: You must declare a
Use RunnerScriptConstructor for complex Python logic
mainThe
RunnerScriptConstructoruses the Hera Runner to execute your function. This allows you to write Python code in a standard way, solving the limitations of inline scripts.Advantages
- Standard Imports: Imports can be placed anywhere in your package.
- Modular Code: The decorated function can call other functions defined in your package.
- Advanced Types: Supports any serializable class, including Pydantic models, for both inputs and outputs.
- Clean YAML: Instead of dumping large code blocks into the Workflow YAML, it uses the
hera.workflows.runnermodule to call your function via a module path (module:function).
Requirements
- You must build a custom Docker image containing your source code, its dependencies, and
heraitself. - The image must be accessible to your Argo cluster before submitting the Workflow.
from hera.workflows import RunnerScriptConstructor from pydantic.v1 import BaseModel class Input(BaseModel): a: int b: str = "foo" class Output(BaseModel): output: List[Input] @script(constructor=RunnerScriptConstructor(), image="my-code-image:v1") def my_function(input: Input) -> Output: return Output(output=[input]) with Workflow( generate_name="hello-world-", entrypoint="my_function", arguments={"input": Input(a=42)}, ) as w: my_function()Arrange execution with Steps
mainThe
Stepstemplate is used to run a sequence of templates.- Sequential execution: By default, templates within a
Stepscontext run one after another. - Parallel execution: You can run templates in parallel by using the
.parallel()context manager within aStepsblock. - Invocating: You can invoke templates using
Step()or by calling a@scriptdecorated function directly within theStepscontext.
from hera.workflows import Container, Parameter, Step, Steps, Workflow with Workflow( generate_name="steps-", entrypoint="hello-hello-hello", ) as w: whalesay = Container( name="whalesay", inputs=[Parameter(name="message")], image="docker/whalesay", command=["cowsay"], args=["{{inputs.parameters.message}}"], ) with Steps(name="hello-hello-hello") as s: Step( name="hello1", template="whalesay", arguments={"message": "hello1"}, ) with s.parallel(): Step( name="hello2a", template="whalesay", arguments={"message": "hello2a"}, ) Step( name="hello2b", template="whalesay", arguments={"message": "hello2b"}, )- Sequential execution: By default, templates within a
Define task dependencies using rshift syntax
mainIn a
DAG, you can define the execution order of tasks using the>>(rshift) operator. This allows you to create complex dependency chains, including parallel execution.Example patterns:
A >> B: Task A must complete before Task B starts.A >> [B, C]: Task A must complete before both B and C start (B and C run in parallel).[B, C] >> D: Both B and C must complete before D starts.
A >> [B, C] >> D