Skip to content

Commit

Permalink
New container decorator (#1068)
Browse files Browse the repository at this point in the history
**Pull Request Checklist**
- [x] Fixes #1065 
- [x] Tests added
- [x] Documentation/examples added
- [x] [Good commit messages](https://cbea.ms/git-commit/) and/or PR
title

**Description of PR**
Adds the container decorator, and the special `path` function on Outputs
Also adds template refs per #1097 

---------

Signed-off-by: Elliot Gunton <[email protected]>
Signed-off-by: Elliot Gunton <[email protected]>
  • Loading branch information
elliotgunton authored Jun 12, 2024
1 parent 5631c6a commit 73861b6
Show file tree
Hide file tree
Showing 9 changed files with 654 additions and 86 deletions.
71 changes: 71 additions & 0 deletions docs/examples/workflows/experimental/new_container_decorator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# New Container Decorator






=== "Hera"

```python linenums="1"
from typing_extensions import Annotated

from hera.shared import global_config
from hera.workflows import Input, Output, Parameter, WorkflowTemplate

global_config.experimental_features["decorator_syntax"] = True


# We start by defining our Workflow Template
w = WorkflowTemplate(name="my-template")


# This defines the template's inputs
class MyInput(Input):
user: str = "Hera"


class MyOutput(Output):
container_greeting: Annotated[
str,
Parameter(
name="container-greeting",
value_from={"path": "/tmp/hello_world.txt"},
),
]


@w.set_entrypoint
@w.container(command=["sh", "-c"], args=["echo Hello {{inputs.parameters.user}} | tee /tmp/hello_world.txt"])
def basic_hello_world(my_input: MyInput) -> MyOutput: ...
```

=== "YAML"

```yaml linenums="1"
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: my-template
spec:
entrypoint: basic-hello-world
templates:
- container:
args:
- echo Hello {{inputs.parameters.user}} | tee /tmp/hello_world.txt
command:
- sh
- -c
image: python:3.8
inputs:
parameters:
- default: Hera
name: user
name: basic-hello-world
outputs:
parameters:
- name: container-greeting
valueFrom:
path: /tmp/hello_world.txt
```

Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# New Decorators Auto Template Refs






=== "Hera"

```python linenums="1"
from pydantic import BaseModel
from typing_extensions import Annotated

from hera.shared import global_config
from hera.workflows import ClusterWorkflowTemplate, Input, Output, Parameter, Workflow, WorkflowTemplate

global_config.experimental_features["decorator_syntax"] = True

wt = WorkflowTemplate(name="my-workflow-template")
cwt = ClusterWorkflowTemplate(name="my-cluster-workflow-template")

w = Workflow(generate_name="my-workflow-")


class SetupConfig(BaseModel):
a_param: str


class SetupOutput(Output):
environment_parameter: str
an_annotated_parameter: Annotated[int, Parameter(name="dummy-param")] # use an annotated non-str
setup_config: Annotated[SetupConfig, Parameter(name="setup-config")] # use a pydantic BaseModel


@cwt.script()
def setup() -> SetupOutput:
return SetupOutput(
environment_parameter="linux",
an_annotated_parameter=42,
setup_config=SetupConfig(a_param="test"),
result="Setting things up",
)


class ConcatConfig(BaseModel):
reverse: bool


class ConcatInput(Input):
word_a: Annotated[str, Parameter(name="word_a", default="")]
word_b: str
concat_config: ConcatConfig = ConcatConfig(reverse=False)


@wt.script()
def concat(concat_input: ConcatInput) -> Output:
res = f"{concat_input.word_a} {concat_input.word_b}"
if concat_input.reverse:
res = res[::-1]
return Output(result=res)


class WorkerConfig(BaseModel):
param_1: str
param_2: str


class WorkerInput(Input):
value_a: str = "my default"
value_b: str
an_int_value: int = 42
a_basemodel: WorkerConfig = WorkerConfig(param_1="Hello", param_2="world")


class WorkerOutput(Output):
value: str


@w.set_entrypoint
@w.dag()
def worker(worker_input: WorkerInput) -> WorkerOutput:
setup_task = setup()
task_a = concat(
ConcatInput(
word_a=worker_input.value_a,
word_b=setup_task.environment_parameter + str(setup_task.an_annotated_parameter),
)
)
task_b = concat(ConcatInput(word_a=worker_input.value_b, word_b=setup_task.result))
final_task = concat(ConcatInput(word_a=task_a.result, word_b=task_b.result))

return WorkerOutput(value=final_task.result)
```

=== "YAML"

```yaml linenums="1"
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: my-workflow-
spec:
entrypoint: worker
templates:
- dag:
tasks:
- name: setup_task
templateRef:
clusterScope: true
name: my-cluster-workflow-template
template: setup
- arguments:
parameters:
- name: word_a
value: '{{inputs.parameters.value_a}}'
- name: word_b
value: '{{tasks.setup_task.outputs.parameters.environment_parameter}}{{tasks.setup_task.outputs.parameters.dummy-param}}'
- name: concat_config
value: '{"reverse": false}'
depends: setup_task
name: task_a
templateRef:
name: my-workflow-template
template: concat
- arguments:
parameters:
- name: word_a
value: '{{inputs.parameters.value_b}}'
- name: word_b
value: '{{tasks.setup_task.outputs.result}}'
- name: concat_config
value: '{"reverse": false}'
depends: setup_task
name: task_b
templateRef:
name: my-workflow-template
template: concat
- arguments:
parameters:
- name: word_a
value: '{{tasks.task_a.outputs.result}}'
- name: word_b
value: '{{tasks.task_b.outputs.result}}'
- name: concat_config
value: '{"reverse": false}'
depends: task_a && task_b
name: final_task
templateRef:
name: my-workflow-template
template: concat
inputs:
parameters:
- default: my default
name: value_a
- name: value_b
- default: '42'
name: an_int_value
- default: '{"param_1": "Hello", "param_2": "world"}'
name: a_basemodel
name: worker
outputs:
parameters:
- name: value
valueFrom:
parameter: '{{tasks.final_task.outputs.result}}'
```

24 changes: 24 additions & 0 deletions examples/workflows/experimental/new-container-decorator.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: my-template
spec:
entrypoint: basic-hello-world
templates:
- container:
args:
- echo Hello {{inputs.parameters.user}} | tee /tmp/hello_world.txt
command:
- sh
- -c
image: python:3.8
inputs:
parameters:
- default: Hera
name: user
name: basic-hello-world
outputs:
parameters:
- name: container-greeting
valueFrom:
path: /tmp/hello_world.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: my-workflow-
spec:
entrypoint: worker
templates:
- dag:
tasks:
- name: setup_task
templateRef:
clusterScope: true
name: my-cluster-workflow-template
template: setup
- arguments:
parameters:
- name: word_a
value: '{{inputs.parameters.value_a}}'
- name: word_b
value: '{{tasks.setup_task.outputs.parameters.environment_parameter}}{{tasks.setup_task.outputs.parameters.dummy-param}}'
- name: concat_config
value: '{"reverse": false}'
depends: setup_task
name: task_a
templateRef:
name: my-workflow-template
template: concat
- arguments:
parameters:
- name: word_a
value: '{{inputs.parameters.value_b}}'
- name: word_b
value: '{{tasks.setup_task.outputs.result}}'
- name: concat_config
value: '{"reverse": false}'
depends: setup_task
name: task_b
templateRef:
name: my-workflow-template
template: concat
- arguments:
parameters:
- name: word_a
value: '{{tasks.task_a.outputs.result}}'
- name: word_b
value: '{{tasks.task_b.outputs.result}}'
- name: concat_config
value: '{"reverse": false}'
depends: task_a && task_b
name: final_task
templateRef:
name: my-workflow-template
template: concat
inputs:
parameters:
- default: my default
name: value_a
- name: value_b
- default: '42'
name: an_int_value
- default: '{"param_1": "Hello", "param_2": "world"}'
name: a_basemodel
name: worker
outputs:
parameters:
- name: value
valueFrom:
parameter: '{{tasks.final_task.outputs.result}}'
30 changes: 30 additions & 0 deletions examples/workflows/experimental/new_container_decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing_extensions import Annotated

from hera.shared import global_config
from hera.workflows import Input, Output, Parameter, WorkflowTemplate

global_config.experimental_features["decorator_syntax"] = True


# We start by defining our Workflow Template
w = WorkflowTemplate(name="my-template")


# This defines the template's inputs
class MyInput(Input):
user: str = "Hera"


class MyOutput(Output):
container_greeting: Annotated[
str,
Parameter(
name="container-greeting",
value_from={"path": "/tmp/hello_world.txt"},
),
]


@w.set_entrypoint
@w.container(command=["sh", "-c"], args=["echo Hello {{inputs.parameters.user}} | tee /tmp/hello_world.txt"])
def basic_hello_world(my_input: MyInput) -> MyOutput: ...
Loading

0 comments on commit 73861b6

Please sign in to comment.