diff --git a/docs/examples/workflows/dag-with-script-output-param-passing.md b/docs/examples/workflows/dag-with-script-output-param-passing.md new file mode 100644 index 000000000..26ede674d --- /dev/null +++ b/docs/examples/workflows/dag-with-script-output-param-passing.md @@ -0,0 +1,98 @@ +# Dag-With-Script-Output-Param-Passing + + + + + + +=== "Hera" + + ```python linenums="1" + from hera.workflows import ( + DAG, + Parameter, + Task, + Workflow, + models as m, + script, + ) + + + @script(outputs=[Parameter(name="a", value_from=m.ValueFrom(path="/test"))]) + def out(): + with open("/test", "w") as f_out: + f_out.write("test") + + + @script() + def in_(a): + print(a) + + + with Workflow(generate_name="script-output-param-passing-", entrypoint="d") as w: + with DAG(name="d"): + t1: Task = out() + t2 = in_(arguments=t1.get_parameter("a")) + t1 >> t2 + ``` + +=== "YAML" + + ```yaml linenums="1" + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + generateName: script-output-param-passing- + spec: + entrypoint: d + templates: + - dag: + tasks: + - name: out + template: out + - arguments: + parameters: + - name: a + value: '{{tasks.out.outputs.parameters.a}}' + depends: out + name: in- + template: in- + name: d + - name: out + outputs: + parameters: + - name: a + valueFrom: + path: /test + script: + command: + - python + image: python:3.8 + source: "import os\nimport sys\nsys.path.append(os.getcwd())\nwith open(\"/test\"\ + , \"w\") as f_out:\n f_out.write(\"test\")\n" + - inputs: + parameters: + - name: a + name: in- + script: + command: + - python + image: python:3.8 + source: 'import os + + import sys + + sys.path.append(os.getcwd()) + + import json + + try: a = json.loads(r''''''{{inputs.parameters.a}}'''''') + + except: a = r''''''{{inputs.parameters.a}}'''''' + + + print(a) + + ' + ``` + diff --git a/docs/examples/workflows/upstream/retry_script.md b/docs/examples/workflows/upstream/retry_script.md new file mode 100644 index 000000000..7c111b3c3 --- /dev/null +++ b/docs/examples/workflows/upstream/retry_script.md @@ -0,0 +1,55 @@ +# Retry Script + +> Note: This example is a replication of an Argo Workflow example in Hera. The upstream example can be [found here](https://github.com/argoproj/argo-workflows/blob/master/examples/retry-script.yaml). + + + + +=== "Hera" + + ```python linenums="1" + from hera.workflows import RetryStrategy, Workflow, script + + + @script(image="python:alpine3.6", retry_strategy=RetryStrategy(limit=10), add_cwd_to_sys_path=False) + def retry_script(): + import random + import sys + + exit_code = random.choice([0, 1, 1]) + sys.exit(exit_code) + + + with Workflow(generate_name="retry-script-", entrypoint="retry-script") as w: + retry_script() + ``` + +=== "YAML" + + ```yaml linenums="1" + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + generateName: retry-script- + spec: + entrypoint: retry-script + templates: + - name: retry-script + retryStrategy: + limit: '10' + script: + command: + - python + image: python:alpine3.6 + source: 'import random + + import sys + + + exit_code = random.choice([0, 1, 1]) + + sys.exit(exit_code) + + ' + ``` + diff --git a/examples/workflows/dag-with-script-output-param-passing.py b/examples/workflows/dag-with-script-output-param-passing.py new file mode 100644 index 000000000..a1de2fd31 --- /dev/null +++ b/examples/workflows/dag-with-script-output-param-passing.py @@ -0,0 +1,26 @@ +from hera.workflows import ( + DAG, + Parameter, + Task, + Workflow, + models as m, + script, +) + + +@script(outputs=[Parameter(name="a", value_from=m.ValueFrom(path="/test"))]) +def out(): + with open("/test", "w") as f_out: + f_out.write("test") + + +@script() +def in_(a): + print(a) + + +with Workflow(generate_name="script-output-param-passing-", entrypoint="d") as w: + with DAG(name="d"): + t1: Task = out() + t2 = in_(arguments=t1.get_parameter("a")) + t1 >> t2 diff --git a/examples/workflows/dag-with-script-output-param-passing.yaml b/examples/workflows/dag-with-script-output-param-passing.yaml new file mode 100644 index 000000000..8bbe8f294 --- /dev/null +++ b/examples/workflows/dag-with-script-output-param-passing.yaml @@ -0,0 +1,55 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: script-output-param-passing- +spec: + entrypoint: d + templates: + - dag: + tasks: + - name: out + template: out + - arguments: + parameters: + - name: a + value: '{{tasks.out.outputs.parameters.a}}' + depends: out + name: in- + template: in- + name: d + - name: out + outputs: + parameters: + - name: a + valueFrom: + path: /test + script: + command: + - python + image: python:3.8 + source: "import os\nimport sys\nsys.path.append(os.getcwd())\nwith open(\"/test\"\ + , \"w\") as f_out:\n f_out.write(\"test\")\n" + - inputs: + parameters: + - name: a + name: in- + script: + command: + - python + image: python:3.8 + source: 'import os + + import sys + + sys.path.append(os.getcwd()) + + import json + + try: a = json.loads(r''''''{{inputs.parameters.a}}'''''') + + except: a = r''''''{{inputs.parameters.a}}'''''' + + + print(a) + + ' diff --git a/examples/workflows/upstream/retry-script.upstream.yaml b/examples/workflows/upstream/retry-script.upstream.yaml new file mode 100644 index 000000000..92bc489d0 --- /dev/null +++ b/examples/workflows/upstream/retry-script.upstream.yaml @@ -0,0 +1,20 @@ +# This example demonstrates the use of retries for a single script. +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: retry-script- +spec: + entrypoint: retry-script + templates: + - name: retry-script + retryStrategy: + limit: "10" + script: + image: python:alpine3.6 + command: ["python"] + # fail with a 66% probability + source: | + import random; + import sys; + exit_code = random.choice([0, 1, 1]); + sys.exit(exit_code) diff --git a/examples/workflows/upstream/retry-script.yaml b/examples/workflows/upstream/retry-script.yaml new file mode 100644 index 000000000..b62e1b099 --- /dev/null +++ b/examples/workflows/upstream/retry-script.yaml @@ -0,0 +1,24 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Workflow +metadata: + generateName: retry-script- +spec: + entrypoint: retry-script + templates: + - name: retry-script + retryStrategy: + limit: '10' + script: + command: + - python + image: python:alpine3.6 + source: 'import random + + import sys + + + exit_code = random.choice([0, 1, 1]) + + sys.exit(exit_code) + + ' diff --git a/examples/workflows/upstream/retry_script.py b/examples/workflows/upstream/retry_script.py new file mode 100644 index 000000000..9fb59a327 --- /dev/null +++ b/examples/workflows/upstream/retry_script.py @@ -0,0 +1,14 @@ +from hera.workflows import RetryStrategy, Workflow, script + + +@script(image="python:alpine3.6", retry_strategy=RetryStrategy(limit=10), add_cwd_to_sys_path=False) +def retry_script(): + import random + import sys + + exit_code = random.choice([0, 1, 1]) + sys.exit(exit_code) + + +with Workflow(generate_name="retry-script-", entrypoint="retry-script") as w: + retry_script() diff --git a/src/hera/workflows/retry_strategy.py b/src/hera/workflows/retry_strategy.py index f277e45ac..dc79bffea 100644 --- a/src/hera/workflows/retry_strategy.py +++ b/src/hera/workflows/retry_strategy.py @@ -36,7 +36,7 @@ class RetryStrategy(_BaseModel): affinity: Optional[RetryAffinity] = None backoff: Optional[Backoff] = None expression: Optional[str] = None - limit: Optional[Union[int, str]] = None + limit: Optional[Union[str, int, IntOrString]] = None retry_policy: Optional[Union[str, RetryPolicy]] = None @validator("retry_policy", pre=True) @@ -52,7 +52,7 @@ def _convert_limit(cls, v): if v is None or isinstance(v, IntOrString): return v - return IntOrString(__root__=str(v)) # int or str + return str(v) # int or str def build(self) -> _ModelRetryStrategy: return _ModelRetryStrategy( diff --git a/src/hera/workflows/task.py b/src/hera/workflows/task.py index d7b2c20bd..5a1ef2900 100644 --- a/src/hera/workflows/task.py +++ b/src/hera/workflows/task.py @@ -146,7 +146,7 @@ def finished_at(self) -> str: def result(self) -> str: return f"{{{{tasks.{self.name}.outputs.result}}}}" - def get_parameters_as(self, name): + def get_parameters_as(self, name: str) -> Parameter: """Gets all the output parameters from this task""" return Parameter(name=name, value=f"{{{{tasks.{self.name}.outputs.parameters}}}}") @@ -181,13 +181,9 @@ def get_parameter(self, name: str) -> Parameter: obj = next((output for output in parameters if output.name == name), None) if obj is not None: - obj.value = f"{{{{tasks.{self.name}.outputs.parameters.{name}}}}}" return Parameter( name=obj.name, - value=obj.value, - value_from=obj.value_from, - global_name=obj.global_name, - description=obj.description, + value=f"{{{{tasks.{self.name}.outputs.parameters.{name}}}}}", ) raise KeyError(f"No output parameter named `{name}` found")