Skip to content

RunnerScriptConstructor refers to wrong inputs.parameters as an inline template #630

@elliotgunton

Description

@elliotgunton

Pre-bug-report checklist

1. This bug can be reproduced using YAML

2. This bug occurs when...

  • running Hera code without submitting to Argo (e.g. when exporting to YAML)
  • running Hera code and submitting to Argo

Bug report

Describe the bug
A clear and concise description of what the bug is:
Use a "runner script" constructor, and make the script an inline template using the workaround in #628:

@script(constructor="runner")
def print_message(message: str):
    print(message)

with Workflow(
    name="hera-inline-test",
    entrypoint="d",
) as w:
    with DAG(name="d"):
        task = print_message(arguments={"message": "test"})
        task.inline = w.templates.pop()
        task.template = None

You can then see in the templates generated yaml:

  templates:
  - dag:
      tasks:
      - arguments:
          parameters:
          - name: message
            value: test
        inline:
          inputs:
            parameters:
            - name: message
          name: print-message
          script:
            args:
            - -m
            - hera.workflows.runner
            - -e
            - herainlinetest.workflow_template:print_message
            command:
            - python
            image: # <private image>
            source: '{{inputs.parameters}}'
        name: print-message
    name: d

The line source: '{{inputs.parameters}}' is actually referring to the dag's {{inputs.parameters}} which doesn't exist, so when submitted, the task errors out with a pydantic error as the parameters aren't present.

We can work around this by constructing the list of parameters via the names themselves, e.g. using a custom script constructor:

class InlineTemplateRunnerScriptConstructor(RunnerScriptConstructor):
    """A workaround constructor to allow our runner script to be an inline template."""

    def generate_source(self, instance: Script) -> str:
        """Convert inputs to a json string containing a list of dicts.

        Each dict contains the keys "name" and "value" with corresponding values
        for each input parameter in `instance`.
        """
        inputs = instance._build_inputs()
        if inputs is None or not inputs.parameters:
            return ""

        input_params = []
        for param in inputs.parameters:
            input_params.append(
                {"name": param.name, "value": f"{{{{inputs.parameters.{param.name}}}}}"}
            )

        return json.dumps(input_params)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions