nextpipe 0.6.2.dev6__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/PKG-INFO +1 -1
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/examples/complex-workflow-csv.md +4 -4
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/examples/multifile-workflow.md +2 -3
- nextpipe-0.7.0/nextpipe/__about__.py +1 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe/flow.py +144 -126
- nextpipe-0.6.2.dev6/nextpipe/__about__.py +0 -1
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/.github/workflows/lint.yml +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/.github/workflows/release.yml +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/.github/workflows/test.yml +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/.gitignore +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/.markdownlint.jsonc +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/.nextmv/bump_requirements.sh +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/.prettierrc.yml +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/.readthedocs.yaml +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/LICENSE.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/README.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/RELEASE.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/examples/basic-chained-workflow.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/examples/complex-workflow.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/examples/ensemble-workflow.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/examples/fanout-workflow.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/active-plan.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/api-keys.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/app-diagram.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/apps-orchestrate-full.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/apps-orchestrate.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/innovator.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/logo-180.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/new-run-orchestrate.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/nextmv-favicon.svg +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/plans.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/pushed-app.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/routing-ortools-pyvroom-selected.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/routing-selected.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/run-metadata.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/run-summary.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/run-workflow.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/secrets-collection-orchestrate.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/images/workflow-diagram.png +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/index.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/reference/config.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/reference/decorators.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/reference/echo-multi.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/reference/echo.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/reference/flow.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/reference/graph.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/reference/schema.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/reference/threads.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/reference/uplink.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/reference/utils.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/requirements.txt +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/docs/tutorials/orchestrate-multiple-models.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/mkdocs.yml +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe/__init__.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe/config.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe/constants.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe/decorators.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe/graph.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe/schema.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe/threads.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe/uplink.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe/utils.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/nextpipe.code-workspace +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/pyproject.toml +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/.gitignore +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/README.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/__init__.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo/.gitignore +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo/README.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo/app.yaml +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo/main.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo/requirements.txt +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo-multi/.gitignore +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo-multi/README.md +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo-multi/app.yaml +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo-multi/inputs/data.csv +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo-multi/inputs/input.xlsx +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo-multi/main.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/apps/echo-multi/requirements.txt +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/deploy/app.yaml +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/deploy/main.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/deploy/requirements.txt +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/appapp.json +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/appapp.json.golden +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/appapp.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/chain.json +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/chain.json.golden +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/chain.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/complex.json +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/complex.json.golden +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/complex.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/fail.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/foreach-2-pred.json +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/foreach-2-pred.json.golden +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/foreach-2-pred.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/foreach.json +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/foreach.json.golden +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/foreach.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/inputs/data.csv +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/inputs/input.xlsx +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/pipelines/multifile.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/test_graph.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/test_integration.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/test_schema.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/test_threads.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/test_uplink.py +0 -0
- {nextpipe-0.6.2.dev6 → nextpipe-0.7.0}/tests/test_version.py +0 -0
|
@@ -24,7 +24,7 @@ import csv
|
|
|
24
24
|
import json
|
|
25
25
|
from typing import Any
|
|
26
26
|
|
|
27
|
-
import nextmv
|
|
27
|
+
import nextmv
|
|
28
28
|
import requests
|
|
29
29
|
|
|
30
30
|
from nextpipe import FlowSpec, app, log, needs, repeat, step
|
|
@@ -112,9 +112,9 @@ class Workflow(FlowSpec):
|
|
|
112
112
|
@needs(predecessors=[solve_nextroute, solve_vroom, solve_ortools])
|
|
113
113
|
@step
|
|
114
114
|
def pick_best(
|
|
115
|
-
nextroute_results: list[nextmv.
|
|
116
|
-
vroom_result: nextmv.
|
|
117
|
-
ortools_result: nextmv.
|
|
115
|
+
nextroute_results: list[nextmv.RunResult],
|
|
116
|
+
vroom_result: nextmv.RunResult,
|
|
117
|
+
ortools_result: nextmv.RunResult,
|
|
118
118
|
):
|
|
119
119
|
"""Pick the best solution based on the result value."""
|
|
120
120
|
|
|
@@ -19,7 +19,6 @@ import os
|
|
|
19
19
|
import shutil
|
|
20
20
|
|
|
21
21
|
import nextmv
|
|
22
|
-
import nextmv.cloud
|
|
23
22
|
|
|
24
23
|
from nextpipe import FlowSpec, app, log, needs, step
|
|
25
24
|
|
|
@@ -76,7 +75,7 @@ class Flow(FlowSpec):
|
|
|
76
75
|
)
|
|
77
76
|
@needs(predecessors=[transform])
|
|
78
77
|
@step
|
|
79
|
-
def solve2(result: nextmv.
|
|
78
|
+
def solve2(result: nextmv.RunResult):
|
|
80
79
|
"""Runs another multi-file model."""
|
|
81
80
|
pass
|
|
82
81
|
|
|
@@ -85,7 +84,7 @@ class Flow(FlowSpec):
|
|
|
85
84
|
# via 'result.output'.
|
|
86
85
|
@needs(predecessors=[solve2])
|
|
87
86
|
@step
|
|
88
|
-
def prepare_output(result: nextmv.
|
|
87
|
+
def prepare_output(result: nextmv.RunResult):
|
|
89
88
|
"""Transforms the result for the next step."""
|
|
90
89
|
# Extract the path to the output files.
|
|
91
90
|
result_path = result.output
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "v0.7.0"
|
|
@@ -896,6 +896,138 @@ class Runner:
|
|
|
896
896
|
description = f"This run was started by the nextpipe parent: {wf_app_id} (run-id: {wf_run_id})"
|
|
897
897
|
return name, description
|
|
898
898
|
|
|
899
|
+
@staticmethod
|
|
900
|
+
def __prepare_app_run_args(
|
|
901
|
+
node: FlowNode,
|
|
902
|
+
inputs: list[object],
|
|
903
|
+
app_step: decorators.App,
|
|
904
|
+
) -> tuple[dict[str, Any], nextmv.PollingOptions, bool]:
|
|
905
|
+
"""
|
|
906
|
+
Prepare arguments for running an app step.
|
|
907
|
+
|
|
908
|
+
Returns
|
|
909
|
+
-------
|
|
910
|
+
tuple[dict[str, Any], nextmv.PollingOptions, bool]
|
|
911
|
+
A tuple of run kwargs, polling options, and whether the input is dir mode.
|
|
912
|
+
"""
|
|
913
|
+
|
|
914
|
+
app_run_name, app_run_description = Runner.__determine_sub_run_info(app_step.app_id, node.id)
|
|
915
|
+
|
|
916
|
+
# TODO: We only support one predecessor for app steps for now. This may
|
|
917
|
+
# change in the future. We may want to support multiple predecessors for
|
|
918
|
+
# app steps. However, we need to think about how to handle the input and
|
|
919
|
+
# how to expose control over the input to the user.
|
|
920
|
+
if len(inputs) > 1:
|
|
921
|
+
raise Exception(f"App steps cannot have more than one predecessor, but {node.id} has {len(inputs)}")
|
|
922
|
+
|
|
923
|
+
if isinstance(inputs[0], schema.AppRunConfig):
|
|
924
|
+
app_run_config: schema.AppRunConfig = inputs[0]
|
|
925
|
+
input_data = app_run_config.input
|
|
926
|
+
if app_run_config.name:
|
|
927
|
+
app_run_name = app_run_config.name
|
|
928
|
+
if app_run_config.description:
|
|
929
|
+
app_run_description = app_run_config.description
|
|
930
|
+
# AppRunConfig options take precedence over decorator options.
|
|
931
|
+
options = app_step.options | app_run_config.get_options()
|
|
932
|
+
elif isinstance(inputs[0], nextmv.RunResult):
|
|
933
|
+
run_result: nextmv.RunResult = inputs[0]
|
|
934
|
+
input_data = run_result.output
|
|
935
|
+
options = app_step.options
|
|
936
|
+
else:
|
|
937
|
+
input_data = inputs[0]
|
|
938
|
+
options = app_step.options
|
|
939
|
+
|
|
940
|
+
is_dir_mode = isinstance(input_data, str) and os.path.isdir(input_data)
|
|
941
|
+
|
|
942
|
+
polling_options = copy.deepcopy(app_step.polling_options)
|
|
943
|
+
polling_options.initial_delay = random.uniform(0, 5)
|
|
944
|
+
if polling_options.stop is None:
|
|
945
|
+
polling_options.stop = lambda: node.cancel
|
|
946
|
+
|
|
947
|
+
run_kwargs: dict[str, Any] = {
|
|
948
|
+
"options": options,
|
|
949
|
+
"name": app_run_name,
|
|
950
|
+
"description": app_run_description,
|
|
951
|
+
}
|
|
952
|
+
if is_dir_mode:
|
|
953
|
+
run_kwargs["input_dir_path"] = input_data
|
|
954
|
+
else:
|
|
955
|
+
run_kwargs["input"] = input_data
|
|
956
|
+
if app_step.run_configuration is not None:
|
|
957
|
+
run_kwargs["configuration"] = app_step.run_configuration
|
|
958
|
+
|
|
959
|
+
return run_kwargs, polling_options, is_dir_mode
|
|
960
|
+
|
|
961
|
+
@staticmethod
|
|
962
|
+
def __execute_app_run(
|
|
963
|
+
node: FlowNode,
|
|
964
|
+
app: Application,
|
|
965
|
+
app_step: decorators.App,
|
|
966
|
+
client: Client,
|
|
967
|
+
update_dag: callable,
|
|
968
|
+
run_kwargs: dict[str, Any],
|
|
969
|
+
polling_options: nextmv.PollingOptions,
|
|
970
|
+
is_dir_mode: bool,
|
|
971
|
+
temp_dir: str,
|
|
972
|
+
) -> nextmv.RunResult:
|
|
973
|
+
"""
|
|
974
|
+
Execute an app run or return bypass output wrapped as a run result.
|
|
975
|
+
"""
|
|
976
|
+
|
|
977
|
+
if app_step.bypass_result is None:
|
|
978
|
+
run_id = app.new_run(**run_kwargs)
|
|
979
|
+
console_url = f"{client.console_url}/app/{app_step.app_id}/run/{run_id}?view=details"
|
|
980
|
+
utils.log_internal(f"Started app step {node.id} run, find it at {console_url}")
|
|
981
|
+
node.run_id = run_id
|
|
982
|
+
update_dag() # Update node with run_id before polling
|
|
983
|
+
|
|
984
|
+
if app_step.emit_live_logs:
|
|
985
|
+
utils.log_internal(f"Emitting live logs for app step {node.id} ...")
|
|
986
|
+
app.run_logs_with_polling(
|
|
987
|
+
run_id=run_id,
|
|
988
|
+
verbose=True,
|
|
989
|
+
log_func=lambda msg: utils.log(message=msg.log, step_name=node.id),
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
return app.run_result_with_polling(run_id=run_id, polling_options=polling_options, output_dir_path=temp_dir)
|
|
993
|
+
|
|
994
|
+
utils.log_internal(f"Bypassing app step {node.id} run, using bypass result")
|
|
995
|
+
|
|
996
|
+
result = nextmv.RunResult(
|
|
997
|
+
id="bypassed-run",
|
|
998
|
+
name="bypassed-run",
|
|
999
|
+
description="This run was bypassed using a user-supplied result.",
|
|
1000
|
+
user_email="unavailable",
|
|
1001
|
+
metadata=nextmv.Metadata(
|
|
1002
|
+
application_id=app_step.app_id or "unavailable",
|
|
1003
|
+
application_instance_id=app_step.instance_id or "unavailable",
|
|
1004
|
+
application_version_id="unavailable",
|
|
1005
|
+
created_at=datetime.datetime.now(),
|
|
1006
|
+
duration=0.0,
|
|
1007
|
+
error="",
|
|
1008
|
+
input_size=0.0,
|
|
1009
|
+
output_size=0.0,
|
|
1010
|
+
format=nextmv.Format(
|
|
1011
|
+
format_input=nextmv.FormatInput(
|
|
1012
|
+
input_type=nextmv.InputFormat.MULTI_FILE if is_dir_mode else nextmv.InputFormat.JSON,
|
|
1013
|
+
),
|
|
1014
|
+
),
|
|
1015
|
+
status_v2=nextmv.StatusV2.succeeded,
|
|
1016
|
+
),
|
|
1017
|
+
)
|
|
1018
|
+
result.output = app_step.bypass_result
|
|
1019
|
+
|
|
1020
|
+
if isinstance(app_step.bypass_result, str) and os.path.isdir(app_step.bypass_result):
|
|
1021
|
+
src = pathlib.Path(app_step.bypass_result)
|
|
1022
|
+
dst = pathlib.Path(temp_dir)
|
|
1023
|
+
for item in src.iterdir():
|
|
1024
|
+
if item.is_file():
|
|
1025
|
+
shutil.copy2(item, dst / item.name)
|
|
1026
|
+
elif item.is_dir():
|
|
1027
|
+
shutil.copytree(item, dst / item.name)
|
|
1028
|
+
|
|
1029
|
+
return result
|
|
1030
|
+
|
|
899
1031
|
@staticmethod
|
|
900
1032
|
def __run_step(
|
|
901
1033
|
node: FlowNode,
|
|
@@ -935,72 +1067,7 @@ class Runner:
|
|
|
935
1067
|
if node.parent.definition.is_app():
|
|
936
1068
|
app_step: decorators.App = node.parent.definition.app
|
|
937
1069
|
|
|
938
|
-
|
|
939
|
-
app_run_name, app_run_description = Runner.__determine_sub_run_info(app_step.app_id, node.id)
|
|
940
|
-
|
|
941
|
-
# Prepare the input for the app
|
|
942
|
-
# TODO: We only support one predecessor for app steps for now. This may
|
|
943
|
-
# change in the future. We may want to support multiple predecessors for
|
|
944
|
-
# app steps. However, we need to think about how to handle the input and
|
|
945
|
-
# how to expose control over the input to the user.
|
|
946
|
-
if len(inputs) > 1:
|
|
947
|
-
raise Exception(f"App steps cannot have more than one predecessor, but {node.id} has {len(inputs)}")
|
|
948
|
-
if isinstance(inputs[0], schema.AppRunConfig):
|
|
949
|
-
# If the input is AppRunConfig, unwrap it.
|
|
950
|
-
app_run_config: schema.AppRunConfig = inputs[0]
|
|
951
|
-
input = app_run_config.input
|
|
952
|
-
if app_run_config.name:
|
|
953
|
-
app_run_name = app_run_config.name
|
|
954
|
-
if app_run_config.description:
|
|
955
|
-
app_run_description = app_run_config.description
|
|
956
|
-
app_run_options = app_run_config.get_options()
|
|
957
|
-
# Merge the options from the app decorator with the options from the
|
|
958
|
-
# AppRunConfig. AppRunConfig options take precedence.
|
|
959
|
-
options = app_step.options | app_run_options
|
|
960
|
-
elif isinstance(inputs[0], nextmv.RunResult):
|
|
961
|
-
# If the input is a RunResult, we use its output as input.
|
|
962
|
-
run_result: nextmv.RunResult = inputs[0]
|
|
963
|
-
input = run_result.output
|
|
964
|
-
options = app_step.options
|
|
965
|
-
else:
|
|
966
|
-
# If the input is not AppRunConfig, we use it directly.
|
|
967
|
-
input = inputs[0]
|
|
968
|
-
options = app_step.options
|
|
969
|
-
|
|
970
|
-
# Detect dir mode / multi-file direct input
|
|
971
|
-
is_dir_mode = False
|
|
972
|
-
if isinstance(input, str) and os.path.isdir(input):
|
|
973
|
-
is_dir_mode = True
|
|
974
|
-
|
|
975
|
-
# Modify the polling options set for the step (by default or by the
|
|
976
|
-
# user) so that the initial delay is randomized and the stopping
|
|
977
|
-
# callback is configured as the node being cancelled if the user
|
|
978
|
-
# doesn't want to override it.
|
|
979
|
-
polling_options = copy.deepcopy(app_step.polling_options)
|
|
980
|
-
delay = random.uniform(0, 5) # For lack of a better idea...
|
|
981
|
-
polling_options.initial_delay = delay
|
|
982
|
-
if polling_options.stop is None:
|
|
983
|
-
polling_options.stop = lambda: node.cancel
|
|
984
|
-
|
|
985
|
-
run_args = (
|
|
986
|
-
[], # No nameless arguments
|
|
987
|
-
{ # We use the named arguments to pass the user arguments to the run function
|
|
988
|
-
"options": options,
|
|
989
|
-
"name": app_run_name,
|
|
990
|
-
"description": app_run_description,
|
|
991
|
-
},
|
|
992
|
-
)
|
|
993
|
-
|
|
994
|
-
# Prepare input argument. We need to use 'input_dir_path' when dealing with a
|
|
995
|
-
# directory input (e.g., multi-file input).
|
|
996
|
-
if is_dir_mode:
|
|
997
|
-
run_args[1]["input_dir_path"] = input
|
|
998
|
-
else:
|
|
999
|
-
run_args[1]["input"] = input
|
|
1000
|
-
|
|
1001
|
-
# Apply run configuration if given.
|
|
1002
|
-
if app_step.run_configuration is not None:
|
|
1003
|
-
run_args[1]["configuration"] = app_step.run_configuration
|
|
1070
|
+
run_kwargs, polling_options, is_dir_mode = Runner.__prepare_app_run_args(node, inputs, app_step)
|
|
1004
1071
|
|
|
1005
1072
|
# Prepare the application itself.
|
|
1006
1073
|
app = Application(
|
|
@@ -1015,66 +1082,17 @@ class Runner:
|
|
|
1015
1082
|
|
|
1016
1083
|
# Run the application
|
|
1017
1084
|
try:
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
app.run_logs_with_polling(
|
|
1030
|
-
run_id=run_id,
|
|
1031
|
-
verbose=True,
|
|
1032
|
-
log_func=lambda msg: utils.log(message=msg.log, step_name=node.id),
|
|
1033
|
-
)
|
|
1034
|
-
|
|
1035
|
-
# Wait for run to finish and get the result.
|
|
1036
|
-
result = app.run_result_with_polling(
|
|
1037
|
-
run_id=run_id, polling_options=polling_options, output_dir_path=temp_dir
|
|
1038
|
-
)
|
|
1039
|
-
else:
|
|
1040
|
-
utils.log_internal(f"Bypassing app step {node.id} run, using bypass result")
|
|
1041
|
-
|
|
1042
|
-
# We wrap the bypass result in a dummy RunResult to ensure consistent behavior.
|
|
1043
|
-
result = nextmv.RunResult(
|
|
1044
|
-
id="bypassed-run",
|
|
1045
|
-
name="bypassed-run",
|
|
1046
|
-
description="This run was bypassed using a user-supplied result.",
|
|
1047
|
-
user_email="unavailable",
|
|
1048
|
-
metadata=nextmv.Metadata(
|
|
1049
|
-
application_id="unavailable",
|
|
1050
|
-
application_instance_id="unavailable",
|
|
1051
|
-
application_version_id="unavailable",
|
|
1052
|
-
created_at=datetime.datetime.now(),
|
|
1053
|
-
duration=0.0,
|
|
1054
|
-
error="",
|
|
1055
|
-
input_size=0.0,
|
|
1056
|
-
output_size=0.0,
|
|
1057
|
-
format=nextmv.Format(
|
|
1058
|
-
format_input=nextmv.FormatInput(
|
|
1059
|
-
input_type=nextmv.InputFormat.MULTI_FILE
|
|
1060
|
-
if is_dir_mode
|
|
1061
|
-
else nextmv.InputFormat.JSON,
|
|
1062
|
-
),
|
|
1063
|
-
),
|
|
1064
|
-
status_v2=nextmv.StatusV2.succeeded,
|
|
1065
|
-
),
|
|
1066
|
-
)
|
|
1067
|
-
result.output = app_step.bypass_result
|
|
1068
|
-
|
|
1069
|
-
# If the user supplied a bypass result, we need to write it to the temp dir if it's in dir mode
|
|
1070
|
-
if isinstance(app_step.bypass_result, str) and os.path.isdir(app_step.bypass_result):
|
|
1071
|
-
src = pathlib.Path(app_step.bypass_result)
|
|
1072
|
-
dst = pathlib.Path(temp_dir)
|
|
1073
|
-
for item in src.iterdir():
|
|
1074
|
-
if item.is_file():
|
|
1075
|
-
shutil.copy2(item, dst / item.name)
|
|
1076
|
-
elif item.is_dir():
|
|
1077
|
-
shutil.copytree(item, dst / item.name)
|
|
1085
|
+
result = Runner.__execute_app_run(
|
|
1086
|
+
node=node,
|
|
1087
|
+
app=app,
|
|
1088
|
+
app_step=app_step,
|
|
1089
|
+
client=client,
|
|
1090
|
+
update_dag=update_dag,
|
|
1091
|
+
run_kwargs=run_kwargs,
|
|
1092
|
+
polling_options=polling_options,
|
|
1093
|
+
is_dir_mode=is_dir_mode,
|
|
1094
|
+
temp_dir=temp_dir,
|
|
1095
|
+
)
|
|
1078
1096
|
|
|
1079
1097
|
finally: # Make sure we clean up temp dir on failure too
|
|
1080
1098
|
# If the temp dir is empty, remove it
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "v0.6.2-dev.6"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|