Hera Python SDK

repository·main·Indexed 21 days ago

https://github.com/argoproj-labs/hera

Hera 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.

Tokens
49.1K
Snippets
139
Records
175
Agent score
75%

What's inside Hera

  1. What is Hera

    main
    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.
  2. Choose between Inline and Runner Scripts

    main
    Hera 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.
  3. Implement recursion in Argo Workflows

    main

    Individual Step or Task objects can specify the Steps or DAG template they belong to, enabling recursive template invocations.

    Warning: You must implement a break condition. Do not make the first Step or Task the parent Steps or DAG without a condition, as this will cause the Argo controller to expand the definition indefinitely, potentially crashing the cluster.

  4. Use the `result` output parameter to capture stdout

    main

    In Hera, when a function decorated with @script() is called within a Steps context, it returns a Step object. This object has a result property that captures the entire stdout of the template. This can be passed directly as an argument to subsequent steps.

    Warning: The result value 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()
  5. How DAGs work in Hera

    main

    DAGs (Directed Acyclic Graphs) are composed of Tasks. Unlike Steps, 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
  6. How Hera and Argo Workflows work together

    main

    Hera 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 @script decorator, 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.

  7. How Inline vs Runner Script Templates differ

    main

    Hera provides two ways to transform Python functions into Argo Workflow templates using the @script decorator:

    FeatureInline Script (@script())Runner Script (@script(constructor="runner", image="..."))
    ImplementationFunction body is dumped into the source field of the YAML.Code is executed via the hera.workflows.runner module inside a container.
    ImportsMust be inside the function body.Can be module-level (standard Python style).
    ComplexitySimple, good for prototyping.Powerful, better for production and complex data.
    DependenciesRelies on the container image having the required libs.Requires a custom OCI image containing your code/dependencies.
  8. How decorators work in Hera

    main

    Hera decorators are members of the Workflow (or WorkflowTemplate) class. They are used to link Python functions to specific Argo Workflow template types. Instead of using context managers, you declare a WorkflowTemplate object and use its methods as decorators to define templates.

    Key Concepts:

    • Template Declaration: You must declare a WorkflowTemplate or Workflow instance upfront (e.g., w = WorkflowTemplate(name="my-template")).
    • Input/Output Handling: To declare inputs and outputs for your templates, you must use the Input and Output Pydantic classes from hera.workflows. This allows Hera to deduce the template schema from the function signature.
    • Local Execution: The dag and steps decorators allow you to run and test functions locally in pure Python. However, Argo-specific features like expressions or with_items loops 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
  9. Use RunnerScriptConstructor for complex Python logic

    main

    The RunnerScriptConstructor uses 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.runner module 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 hera itself.
    • 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()
  10. Arrange execution with Steps

    main

    The Steps template is used to run a sequence of templates.

    • Sequential execution: By default, templates within a Steps context run one after another.
    • Parallel execution: You can run templates in parallel by using the .parallel() context manager within a Steps block.
    • Invocating: You can invoke templates using Step() or by calling a @script decorated function directly within the Steps context.
    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"},
                )
  11. Define task dependencies using rshift syntax

    main

    In 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