Skip to content

Commit

Permalink
Make steps callable, add dag/steps callable examples (#715)
Browse files Browse the repository at this point in the history
**Pull Request Checklist**
- [x] Fixes #714 
- [x] Tests added
- [x] Documentation/examples added
- [x] [Good commit messages](https://cbea.ms/git-commit/) and/or PR
title

**Description of PR**
Currently, Steps aren't callable as a template under another DAG/Steps
context, while DAGs themselves are. This PR adds the
`CallableTemplateMixin` to Steps and adds examples for both Steps and
dags to show the callable usage.

---------

Signed-off-by: Elliot Gunton <[email protected]>
  • Loading branch information
elliotgunton authored Jul 19, 2023
1 parent 5cfc2ba commit 6aebe34
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 2 deletions.
100 changes: 100 additions & 0 deletions docs/examples/workflows/callable_dag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Callable Dag






=== "Hera"

```python linenums="1"
from hera.workflows import DAG, Parameter, Workflow, script


@script()
def hello(name: str):
print("Hello, {name}!".format(name=name))


with Workflow(
generate_name="callable-dag-",
entrypoint="calling-dag",
) as w:
with DAG(name="my-dag", inputs=Parameter(name="my-dag-input")) as my_dag:
hello(name="hello-1", arguments={"name": "hello-1-{{inputs.parameters.my-dag-input}}"})
hello(name="hello-2", arguments={"name": "hello-2-{{inputs.parameters.my-dag-input}}"})

with DAG(name="calling-dag") as d:
t1 = my_dag(name="call-1", arguments={"my-dag-input": "call-1"})
t2 = my_dag(name="call-2", arguments={"my-dag-input": "call-2"})
t1 >> t2
```

=== "YAML"

```yaml linenums="1"
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: callable-dag-
spec:
entrypoint: calling-dag
templates:
- dag:
tasks:
- arguments:
parameters:
- name: name
value: hello-1-{{inputs.parameters.my-dag-input}}
name: hello-1
template: hello
- arguments:
parameters:
- name: name
value: hello-2-{{inputs.parameters.my-dag-input}}
name: hello-2
template: hello
inputs:
parameters:
- name: my-dag-input
name: my-dag
- inputs:
parameters:
- name: name
name: hello
script:
command:
- python
image: python:3.8
source: 'import os

import sys

sys.path.append(os.getcwd())

import json

try: name = json.loads(r''''''{{inputs.parameters.name}}'''''')

except: name = r''''''{{inputs.parameters.name}}''''''


print(''Hello, {name}!''.format(name=name))'
- dag:
tasks:
- arguments:
parameters:
- name: my-dag-input
value: call-1
name: call-1
template: my-dag
- arguments:
parameters:
- name: my-dag-input
value: call-2
depends: call-1
name: call-2
template: my-dag
name: calling-dag
```

96 changes: 96 additions & 0 deletions docs/examples/workflows/callable_steps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Callable Steps






=== "Hera"

```python linenums="1"
from hera.workflows import Parameter, Steps, Workflow, script


@script()
def hello(name: str):
print("Hello, {name}!".format(name=name))


with Workflow(
generate_name="callable-steps-",
entrypoint="calling-steps",
) as w:
with Steps(name="my-steps", inputs=Parameter(name="my-step-input")) as my_steps:
hello(name="hello-1", arguments={"name": "hello-1-{{inputs.parameters.my-step-input}}"})
hello(name="hello-2", arguments={"name": "hello-2-{{inputs.parameters.my-step-input}}"})

with Steps(name="calling-steps") as s:
my_steps(name="call-1", arguments={"my-step-input": "call-1"})
my_steps(name="call-2", arguments={"my-step-input": "call-2"})
```

=== "YAML"

```yaml linenums="1"
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: callable-steps-
spec:
entrypoint: calling-steps
templates:
- inputs:
parameters:
- name: my-step-input
name: my-steps
steps:
- - arguments:
parameters:
- name: name
value: hello-1-{{inputs.parameters.my-step-input}}
name: hello-1
template: hello
- - arguments:
parameters:
- name: name
value: hello-2-{{inputs.parameters.my-step-input}}
name: hello-2
template: hello
- inputs:
parameters:
- name: name
name: hello
script:
command:
- python
image: python:3.8
source: 'import os

import sys

sys.path.append(os.getcwd())

import json

try: name = json.loads(r''''''{{inputs.parameters.name}}'''''')

except: name = r''''''{{inputs.parameters.name}}''''''


print(''Hello, {name}!''.format(name=name))'
- name: calling-steps
steps:
- - arguments:
parameters:
- name: my-step-input
value: call-1
name: call-1
template: my-steps
- - arguments:
parameters:
- name: my-step-input
value: call-2
name: call-2
template: my-steps
```

63 changes: 63 additions & 0 deletions examples/workflows/callable-dag.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: callable-dag-
spec:
entrypoint: calling-dag
templates:
- dag:
tasks:
- arguments:
parameters:
- name: name
value: hello-1-{{inputs.parameters.my-dag-input}}
name: hello-1
template: hello
- arguments:
parameters:
- name: name
value: hello-2-{{inputs.parameters.my-dag-input}}
name: hello-2
template: hello
inputs:
parameters:
- name: my-dag-input
name: my-dag
- inputs:
parameters:
- name: name
name: hello
script:
command:
- python
image: python:3.8
source: 'import os
import sys
sys.path.append(os.getcwd())
import json
try: name = json.loads(r''''''{{inputs.parameters.name}}'''''')
except: name = r''''''{{inputs.parameters.name}}''''''
print(''Hello, {name}!''.format(name=name))'
- dag:
tasks:
- arguments:
parameters:
- name: my-dag-input
value: call-1
name: call-1
template: my-dag
- arguments:
parameters:
- name: my-dag-input
value: call-2
depends: call-1
name: call-2
template: my-dag
name: calling-dag
60 changes: 60 additions & 0 deletions examples/workflows/callable-steps.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: callable-steps-
spec:
entrypoint: calling-steps
templates:
- inputs:
parameters:
- name: my-step-input
name: my-steps
steps:
- - arguments:
parameters:
- name: name
value: hello-1-{{inputs.parameters.my-step-input}}
name: hello-1
template: hello
- - arguments:
parameters:
- name: name
value: hello-2-{{inputs.parameters.my-step-input}}
name: hello-2
template: hello
- inputs:
parameters:
- name: name
name: hello
script:
command:
- python
image: python:3.8
source: 'import os
import sys
sys.path.append(os.getcwd())
import json
try: name = json.loads(r''''''{{inputs.parameters.name}}'''''')
except: name = r''''''{{inputs.parameters.name}}''''''
print(''Hello, {name}!''.format(name=name))'
- name: calling-steps
steps:
- - arguments:
parameters:
- name: my-step-input
value: call-1
name: call-1
template: my-steps
- - arguments:
parameters:
- name: my-step-input
value: call-2
name: call-2
template: my-steps
20 changes: 20 additions & 0 deletions examples/workflows/callable_dag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from hera.workflows import DAG, Parameter, Workflow, script


@script()
def hello(name: str):
print("Hello, {name}!".format(name=name))


with Workflow(
generate_name="callable-dag-",
entrypoint="calling-dag",
) as w:
with DAG(name="my-dag", inputs=Parameter(name="my-dag-input")) as my_dag:
hello(name="hello-1", arguments={"name": "hello-1-{{inputs.parameters.my-dag-input}}"})
hello(name="hello-2", arguments={"name": "hello-2-{{inputs.parameters.my-dag-input}}"})

with DAG(name="calling-dag") as d:
t1 = my_dag(name="call-1", arguments={"my-dag-input": "call-1"})
t2 = my_dag(name="call-2", arguments={"my-dag-input": "call-2"})
t1 >> t2
19 changes: 19 additions & 0 deletions examples/workflows/callable_steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from hera.workflows import Parameter, Steps, Workflow, script


@script()
def hello(name: str):
print("Hello, {name}!".format(name=name))


with Workflow(
generate_name="callable-steps-",
entrypoint="calling-steps",
) as w:
with Steps(name="my-steps", inputs=Parameter(name="my-step-input")) as my_steps:
hello(name="hello-1", arguments={"name": "hello-1-{{inputs.parameters.my-step-input}}"})
hello(name="hello-2", arguments={"name": "hello-2-{{inputs.parameters.my-step-input}}"})

with Steps(name="calling-steps") as s:
my_steps(name="call-1", arguments={"my-step-input": "call-1"})
my_steps(name="call-2", arguments={"my-step-input": "call-2"})
7 changes: 6 additions & 1 deletion src/hera/workflows/dag.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
from hera.workflows.task import Task


class DAG(IOMixin, TemplateMixin, CallableTemplateMixin, ContextMixin):
class DAG(
IOMixin,
TemplateMixin,
CallableTemplateMixin,
ContextMixin,
):
"""A DAG template invocator is used to define Task dependencies as an acyclic graph.
DAG implements the contextmanager interface so allows usage of `with`, under which any
Expand Down
4 changes: 3 additions & 1 deletion src/hera/workflows/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from hera.workflows._mixins import (
ArgumentsMixin,
CallableTemplateMixin,
ContextMixin,
IOMixin,
ItemMixin,
Expand Down Expand Up @@ -103,9 +104,10 @@ def _build_step(self) -> List[_ModelWorkflowStep]:


class Steps(
ContextMixin,
IOMixin,
TemplateMixin,
CallableTemplateMixin,
ContextMixin,
):
"""A Steps template invocator is used to define a sequence of steps which can run sequentially or in parallel.
Expand Down

0 comments on commit 6aebe34

Please sign in to comment.