Skip to content

Commit 41d6c4f

Browse files
Add an example for a CLI app also used as a Hera script (#1536)
**Pull Request Checklist** - [x] Related to #1530 - [x] Tests added - [x] Documentation/examples added - [x] [Good commit messages](https://cbea.ms/git-commit/) and/or PR title **Description of PR** Currently, a user may want to reuse a CLI app with Hera. `click` is a popular choice but garbles the underlying function, rendering it unusable with the `script` decorator (and not worth first-party support at this time). Cappa, used for the `hera` CLI itself, does not interfere with the function, so it is usable with the script decorator. This PR adds a use-case/integration example for reusing a CLI app with Hera. Also pointing out in a warning box that `click` will not ordinarily work with the `script` decorator (requiring the `Script` class). cc @DanCardin sharing the cappa love 🫶 Signed-off-by: Elliot Gunton <[email protected]> Co-authored-by: Sambhav Kothari <[email protected]>
1 parent d04fc2d commit 41d6c4f

File tree

4 files changed

+143
-0
lines changed

4 files changed

+143
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Reusing Cli App
2+
3+
4+
5+
This example shows how a CLI application built with [Cappa](https://github.com/DanCardin/cappa) can also be used as a `script` function.
6+
7+
!!! Warning
8+
Using the `script` decorator is not possible with other CLI libraries like [`click`](https://click.palletsprojects.com/en/stable/), and will require use of the `Script` class and setting `source=function_name`.
9+
[See this issue discussion](https://github.com/argoproj-labs/hera/issues/1530#issuecomment-3616082722) for more details.
10+
11+
12+
=== "Hera"
13+
14+
```python linenums="1"
15+
import cappa
16+
17+
from hera.workflows import Workflow, script
18+
19+
20+
@script()
21+
def hello(count: int, name: str):
22+
"""Simple program that greets NAME for a total of COUNT times."""
23+
for _ in range(count):
24+
print(f"Hello {name}!")
25+
26+
27+
with Workflow(
28+
generate_name="cli-example-",
29+
entrypoint="hello",
30+
arguments={"count": 3, "name": "Hera"},
31+
) as w:
32+
hello()
33+
34+
35+
if __name__ == "__main__":
36+
cappa.invoke(hello)
37+
```
38+
39+
=== "YAML"
40+
41+
```yaml linenums="1"
42+
apiVersion: argoproj.io/v1alpha1
43+
kind: Workflow
44+
metadata:
45+
generateName: cli-example-
46+
spec:
47+
entrypoint: hello
48+
templates:
49+
- name: hello
50+
inputs:
51+
parameters:
52+
- name: count
53+
- name: name
54+
script:
55+
image: python:3.10
56+
source: |-
57+
import os
58+
import sys
59+
sys.path.append(os.getcwd())
60+
import json
61+
try: count = json.loads(r'''{{inputs.parameters.count}}''')
62+
except: count = r'''{{inputs.parameters.count}}'''
63+
try: name = json.loads(r'''{{inputs.parameters.name}}''')
64+
except: name = r'''{{inputs.parameters.name}}'''
65+
66+
"""Simple program that greets NAME for a total of COUNT times."""
67+
for _ in range(count):
68+
print(f'Hello {name}!')
69+
command:
70+
- python
71+
arguments:
72+
parameters:
73+
- name: count
74+
value: '3'
75+
- name: name
76+
value: Hera
77+
```
78+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
apiVersion: argoproj.io/v1alpha1
2+
kind: Workflow
3+
metadata:
4+
generateName: cli-example-
5+
spec:
6+
entrypoint: hello
7+
templates:
8+
- name: hello
9+
inputs:
10+
parameters:
11+
- name: count
12+
- name: name
13+
script:
14+
image: python:3.10
15+
source: |-
16+
import os
17+
import sys
18+
sys.path.append(os.getcwd())
19+
import json
20+
try: count = json.loads(r'''{{inputs.parameters.count}}''')
21+
except: count = r'''{{inputs.parameters.count}}'''
22+
try: name = json.loads(r'''{{inputs.parameters.name}}''')
23+
except: name = r'''{{inputs.parameters.name}}'''
24+
25+
"""Simple program that greets NAME for a total of COUNT times."""
26+
for _ in range(count):
27+
print(f'Hello {name}!')
28+
command:
29+
- python
30+
arguments:
31+
parameters:
32+
- name: count
33+
value: '3'
34+
- name: name
35+
value: Hera
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""This example shows how a CLI application built with [Cappa](https://github.com/DanCardin/cappa) can also be used as a `script` function.
2+
3+
!!! Warning
4+
Using the `script` decorator is not possible with other CLI libraries like [`click`](https://click.palletsprojects.com/en/stable/), and will require use of the `Script` class and setting `source=function_name`.
5+
[See this issue discussion](https://github.com/argoproj-labs/hera/issues/1530#issuecomment-3616082722) for more details.
6+
"""
7+
8+
import cappa
9+
10+
from hera.workflows import Workflow, script
11+
12+
13+
@script()
14+
def hello(count: int, name: str):
15+
"""Simple program that greets NAME for a total of COUNT times."""
16+
for _ in range(count):
17+
print(f"Hello {name}!")
18+
19+
20+
with Workflow(
21+
generate_name="cli-example-",
22+
entrypoint="hello",
23+
arguments={"count": 3, "name": "Hera"},
24+
) as w:
25+
hello()
26+
27+
28+
if __name__ == "__main__":
29+
cappa.invoke(hello)

tests/submissions/test_examples.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"new_decorators_auto_template_refs", # Uses TemplateRefs to non-existent WorkflowTemplates
2323
"template_refs", # Also uses TemplateRefs - the WorkflowTemplates are in the file though
2424
"parquet_pandas",
25+
"reusing_cli_app",
2526
}
2627
SKIP_SUBMISSION_EXAMPLES = SKIP_LINT_EXAMPLES.union(
2728
{

0 commit comments

Comments
 (0)