nextpipe 0.7.2__tar.gz → 0.7.3__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.7.2 → nextpipe-0.7.3}/PKG-INFO +1 -1
- nextpipe-0.7.3/nextpipe/__about__.py +1 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe/decorators.py +12 -3
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe/flow.py +24 -10
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe/schema.py +54 -1
- nextpipe-0.7.3/tests/test_flow.py +104 -0
- nextpipe-0.7.3/tests/test_schema.py +69 -0
- nextpipe-0.7.2/nextpipe/__about__.py +0 -1
- nextpipe-0.7.2/tests/test_schema.py +0 -27
- {nextpipe-0.7.2 → nextpipe-0.7.3}/.github/workflows/lint.yml +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/.github/workflows/release.yml +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/.github/workflows/test.yml +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/.gitignore +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/.nextmv/bump_requirements.sh +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/.prettierrc.yml +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/.python-version +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/LICENSE.md +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/README.md +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/RELEASE.md +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe/__init__.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe/config.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe/constants.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe/graph.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe/threads.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe/uplink.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe/utils.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/nextpipe.code-workspace +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/pyproject.toml +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/.gitignore +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/README.md +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/__init__.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo/.gitignore +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo/README.md +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo/app.yaml +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo/main.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo/requirements.txt +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo-multi/.gitignore +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo-multi/README.md +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo-multi/app.yaml +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo-multi/inputs/data.csv +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo-multi/inputs/input.xlsx +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo-multi/main.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/apps/echo-multi/requirements.txt +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/deploy/app.yaml +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/deploy/main.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/deploy/requirements.txt +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/appapp.json +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/appapp.json.golden +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/appapp.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/chain.json +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/chain.json.golden +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/chain.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/complex.json +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/complex.json.golden +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/complex.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/fail.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/foreach-2-pred.json +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/foreach-2-pred.json.golden +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/foreach-2-pred.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/foreach.json +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/foreach.json.golden +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/foreach.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/inputs/data.csv +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/inputs/input.xlsx +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/pipelines/multifile.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/test_graph.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/test_integration.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/test_threads.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/test_uplink.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/tests/test_version.py +0 -0
- {nextpipe-0.7.2 → nextpipe-0.7.3}/uv.lock +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "v0.7.3"
|
|
@@ -798,12 +798,16 @@ class App:
|
|
|
798
798
|
The ID of the Nextmv Application to run.
|
|
799
799
|
instance_id : str, optional
|
|
800
800
|
The ID of the instance to run. Default is defined by the app on Platform.
|
|
801
|
+
A predecessor step may override this per run by returning an `AppRunConfig`
|
|
802
|
+
carrying its own `instance_id`.
|
|
801
803
|
options : dict[str, Any], optional
|
|
802
804
|
The options to pass to the application, by default None.
|
|
803
805
|
full_result : bool, optional
|
|
804
806
|
Whether to return the full result including metadata, by default False.
|
|
805
807
|
run_configuration : nextmv.RunConfiguration, optional
|
|
806
|
-
The configuration to apply when running the app, by default None.
|
|
808
|
+
The configuration to apply when running the app, by default None. A
|
|
809
|
+
predecessor step may override this per run by returning an
|
|
810
|
+
`AppRunConfig` carrying its own `run_configuration`.
|
|
807
811
|
polling_options : Optional[nextmv.PollingOptions], optional
|
|
808
812
|
Options for polling for the results of the app run, by default _DEFAULT_POLLING_OPTIONS.
|
|
809
813
|
bypass_result : typing.Any, optional
|
|
@@ -884,7 +888,9 @@ def app(
|
|
|
884
888
|
app_id : str
|
|
885
889
|
The ID of the application to run.
|
|
886
890
|
instance_id : str
|
|
887
|
-
The ID of the instance to run. Default is defined by the app on Platform.
|
|
891
|
+
The ID of the instance to run. Default is defined by the app on Platform. A
|
|
892
|
+
predecessor step may override this per run by returning an `AppRunConfig`
|
|
893
|
+
carrying its own `instance_id`.
|
|
888
894
|
options : dict[str, Any]
|
|
889
895
|
The options to pass to the application. This is a dictionary of
|
|
890
896
|
parameter names and values. The values must be JSON serializable.
|
|
@@ -894,7 +900,10 @@ def app(
|
|
|
894
900
|
this is set to `False`, only the output of the application will be
|
|
895
901
|
returned.
|
|
896
902
|
run_configuration : nextmv.RunConfiguration
|
|
897
|
-
The configuration to apply when running the app.
|
|
903
|
+
The configuration to apply when running the app. A predecessor step may
|
|
904
|
+
override this per run by returning an `AppRunConfig` carrying its own
|
|
905
|
+
`run_configuration`. In that case, the configuration of the `AppRunConfig`
|
|
906
|
+
replaces this one as a whole, i.e., no fields are inherited from it.
|
|
898
907
|
polling_options : Optional[nextmv.PollingOptions]
|
|
899
908
|
Options for polling for the results of the app run. This is used to
|
|
900
909
|
configure the polling behavior, such as the timeout and backoff
|
|
@@ -901,14 +901,15 @@ class Runner:
|
|
|
901
901
|
node: FlowNode,
|
|
902
902
|
inputs: list[object],
|
|
903
903
|
app_step: decorators.App,
|
|
904
|
-
) -> tuple[dict[str, Any], nextmv.PollingOptions, bool]:
|
|
904
|
+
) -> tuple[dict[str, Any], nextmv.PollingOptions, bool, str]:
|
|
905
905
|
"""
|
|
906
906
|
Prepare arguments for running an app step.
|
|
907
907
|
|
|
908
908
|
Returns
|
|
909
909
|
-------
|
|
910
|
-
tuple[dict[str, Any], nextmv.PollingOptions, bool]
|
|
911
|
-
A tuple of run kwargs, polling options,
|
|
910
|
+
tuple[dict[str, Any], nextmv.PollingOptions, bool, str]
|
|
911
|
+
A tuple of run kwargs, polling options, whether the input is dir mode, and
|
|
912
|
+
the ID of the instance to run.
|
|
912
913
|
"""
|
|
913
914
|
|
|
914
915
|
app_run_name, app_run_description = Runner.__determine_sub_run_info(app_step.app_id, node.id)
|
|
@@ -920,6 +921,9 @@ class Runner:
|
|
|
920
921
|
if len(inputs) > 1:
|
|
921
922
|
raise Exception(f"App steps cannot have more than one predecessor, but {node.id} has {len(inputs)}")
|
|
922
923
|
|
|
924
|
+
run_configuration = app_step.run_configuration
|
|
925
|
+
instance_id = app_step.instance_id
|
|
926
|
+
|
|
923
927
|
if isinstance(inputs[0], schema.AppRunConfig):
|
|
924
928
|
app_run_config: schema.AppRunConfig = inputs[0]
|
|
925
929
|
input_data = app_run_config.input
|
|
@@ -929,6 +933,12 @@ class Runner:
|
|
|
929
933
|
app_run_description = app_run_config.description
|
|
930
934
|
# AppRunConfig options take precedence over decorator options.
|
|
931
935
|
options = app_step.options | app_run_config.get_options()
|
|
936
|
+
# An AppRunConfig run configuration replaces the decorator one as a whole.
|
|
937
|
+
if app_run_config.run_configuration is not None:
|
|
938
|
+
run_configuration = app_run_config.run_configuration
|
|
939
|
+
# Same for the instance to run on.
|
|
940
|
+
if app_run_config.instance_id:
|
|
941
|
+
instance_id = app_run_config.instance_id
|
|
932
942
|
elif isinstance(inputs[0], nextmv.RunResult):
|
|
933
943
|
run_result: nextmv.RunResult = inputs[0]
|
|
934
944
|
input_data = run_result.output
|
|
@@ -953,10 +963,10 @@ class Runner:
|
|
|
953
963
|
run_kwargs["input_dir_path"] = input_data
|
|
954
964
|
else:
|
|
955
965
|
run_kwargs["input"] = input_data
|
|
956
|
-
if
|
|
957
|
-
run_kwargs["configuration"] =
|
|
966
|
+
if run_configuration is not None:
|
|
967
|
+
run_kwargs["configuration"] = run_configuration
|
|
958
968
|
|
|
959
|
-
return run_kwargs, polling_options, is_dir_mode
|
|
969
|
+
return run_kwargs, polling_options, is_dir_mode, instance_id
|
|
960
970
|
|
|
961
971
|
@staticmethod
|
|
962
972
|
def __execute_app_run(
|
|
@@ -969,6 +979,7 @@ class Runner:
|
|
|
969
979
|
polling_options: nextmv.PollingOptions,
|
|
970
980
|
is_dir_mode: bool,
|
|
971
981
|
temp_dir: str,
|
|
982
|
+
instance_id: str,
|
|
972
983
|
) -> nextmv.RunResult:
|
|
973
984
|
"""
|
|
974
985
|
Execute an app run or return bypass output wrapped as a run result.
|
|
@@ -1000,7 +1011,7 @@ class Runner:
|
|
|
1000
1011
|
user_email="unavailable",
|
|
1001
1012
|
metadata=nextmv.Metadata(
|
|
1002
1013
|
application_id=app_step.app_id or "unavailable",
|
|
1003
|
-
application_instance_id=
|
|
1014
|
+
application_instance_id=instance_id or "unavailable",
|
|
1004
1015
|
application_version_id="unavailable",
|
|
1005
1016
|
created_at=datetime.datetime.now(),
|
|
1006
1017
|
duration=0.0,
|
|
@@ -1067,15 +1078,17 @@ class Runner:
|
|
|
1067
1078
|
if node.parent.definition.is_app():
|
|
1068
1079
|
app_step: decorators.App = node.parent.definition.app
|
|
1069
1080
|
|
|
1070
|
-
run_kwargs, polling_options, is_dir_mode = Runner.__prepare_app_run_args(
|
|
1081
|
+
run_kwargs, polling_options, is_dir_mode, instance_id = Runner.__prepare_app_run_args(
|
|
1082
|
+
node, inputs, app_step
|
|
1083
|
+
)
|
|
1071
1084
|
|
|
1072
1085
|
# Prepare the application itself.
|
|
1073
1086
|
app = Application(
|
|
1074
1087
|
client=client,
|
|
1075
1088
|
id=app_step.app_id,
|
|
1076
1089
|
)
|
|
1077
|
-
if
|
|
1078
|
-
app.default_instance_id =
|
|
1090
|
+
if instance_id is not None and instance_id != "":
|
|
1091
|
+
app.default_instance_id = instance_id
|
|
1079
1092
|
|
|
1080
1093
|
# We always supply an output directory path in case of implicit multi-file output
|
|
1081
1094
|
temp_dir = tempfile.mkdtemp(prefix="nextpipe_output_")
|
|
@@ -1092,6 +1105,7 @@ class Runner:
|
|
|
1092
1105
|
polling_options=polling_options,
|
|
1093
1106
|
is_dir_mode=is_dir_mode,
|
|
1094
1107
|
temp_dir=temp_dir,
|
|
1108
|
+
instance_id=instance_id,
|
|
1095
1109
|
)
|
|
1096
1110
|
|
|
1097
1111
|
finally: # Make sure we clean up temp dir on failure too
|
|
@@ -14,11 +14,31 @@ AppRunConfig
|
|
|
14
14
|
from dataclasses import dataclass, field
|
|
15
15
|
from typing import Any
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
import nextmv
|
|
18
|
+
from dataclasses_json import config, dataclass_json
|
|
18
19
|
|
|
19
20
|
from . import utils
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
def _encode_run_configuration(
|
|
24
|
+
run_configuration: nextmv.RunConfiguration | None,
|
|
25
|
+
) -> dict[str, Any] | None:
|
|
26
|
+
"""Encode a run configuration for JSON serialization."""
|
|
27
|
+
|
|
28
|
+
return run_configuration.to_dict() if run_configuration is not None else None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _decode_run_configuration(
|
|
32
|
+
run_configuration: dict[str, Any] | nextmv.RunConfiguration | None,
|
|
33
|
+
) -> nextmv.RunConfiguration | None:
|
|
34
|
+
"""Decode a run configuration from its JSON representation."""
|
|
35
|
+
|
|
36
|
+
if run_configuration is None or isinstance(run_configuration, nextmv.RunConfiguration):
|
|
37
|
+
return run_configuration
|
|
38
|
+
|
|
39
|
+
return nextmv.RunConfiguration.from_dict(run_configuration)
|
|
40
|
+
|
|
41
|
+
|
|
22
42
|
@dataclass_json
|
|
23
43
|
@dataclass
|
|
24
44
|
class AppOption:
|
|
@@ -79,6 +99,16 @@ class AppRunConfig:
|
|
|
79
99
|
`AppOption` instances, or, simply as a dictionary of key-value pairs.
|
|
80
100
|
name : str, optional
|
|
81
101
|
Name for the run, by default None.
|
|
102
|
+
description : str, optional
|
|
103
|
+
Description for the run, by default None.
|
|
104
|
+
run_configuration : nextmv.RunConfiguration, optional
|
|
105
|
+
The configuration to apply when running the app, by default None. If given, it
|
|
106
|
+
replaces the `run_configuration` of the `app` decorator for this run. Note that
|
|
107
|
+
it replaces it as a whole, i.e., no fields are inherited from the decorator's
|
|
108
|
+
configuration.
|
|
109
|
+
instance_id : str, optional
|
|
110
|
+
The ID of the instance to run, by default None. If given, it replaces the
|
|
111
|
+
`instance_id` of the `app` decorator for this run.
|
|
82
112
|
|
|
83
113
|
Examples
|
|
84
114
|
--------
|
|
@@ -88,6 +118,17 @@ class AppRunConfig:
|
|
|
88
118
|
... options={"threads": 4},
|
|
89
119
|
... name="my-run"
|
|
90
120
|
... )
|
|
121
|
+
|
|
122
|
+
The run configuration and instance can be chosen per run, e.g., to pick an
|
|
123
|
+
execution class based on the size of the scenario at hand.
|
|
124
|
+
|
|
125
|
+
>>> import nextmv
|
|
126
|
+
>>> from nextpipe import AppRunConfig
|
|
127
|
+
>>> config = AppRunConfig(
|
|
128
|
+
... input={"data": [1, 2, 3]},
|
|
129
|
+
... run_configuration=nextmv.RunConfiguration(execution_class="8c16gb12h"),
|
|
130
|
+
... instance_id="tuned-for-large",
|
|
131
|
+
... )
|
|
91
132
|
"""
|
|
92
133
|
|
|
93
134
|
input: dict[str, Any] | str | Any
|
|
@@ -100,6 +141,18 @@ class AppRunConfig:
|
|
|
100
141
|
"""Name for the run."""
|
|
101
142
|
description: str | None = None
|
|
102
143
|
"""Description for the run."""
|
|
144
|
+
run_configuration: nextmv.RunConfiguration | None = field(
|
|
145
|
+
default=None,
|
|
146
|
+
metadata=config(
|
|
147
|
+
encoder=_encode_run_configuration,
|
|
148
|
+
decoder=_decode_run_configuration,
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
"""The configuration to apply when running the app. Replaces the `run_configuration`
|
|
152
|
+
of the `app` decorator as a whole, if given."""
|
|
153
|
+
instance_id: str | None = None
|
|
154
|
+
"""The ID of the instance to run. Replaces the `instance_id` of the `app` decorator,
|
|
155
|
+
if given."""
|
|
103
156
|
|
|
104
157
|
def get_options(self) -> dict[str, Any]:
|
|
105
158
|
"""
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
import nextmv
|
|
6
|
+
|
|
7
|
+
from nextpipe import AppRunConfig, decorators
|
|
8
|
+
from nextpipe.flow import Runner
|
|
9
|
+
|
|
10
|
+
# Add the parent directory to the sys.path to allow imports from the main package. This
|
|
11
|
+
# is meant to help VS Code testing features.
|
|
12
|
+
sys.path.append(os.path.dirname(sys.path[0]))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _FakeNode:
|
|
16
|
+
"""Minimal stand-in for a FlowNode, providing what run arg preparation needs."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, id: str = "solve_0"):
|
|
19
|
+
self.id = id
|
|
20
|
+
self.cancel = False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _prepare(inputs: list[object], app_step: decorators.App) -> tuple[dict, str]:
|
|
24
|
+
"""Prepare the run kwargs and instance ID for the given inputs and app step."""
|
|
25
|
+
|
|
26
|
+
run_kwargs, _, _, instance_id = Runner._Runner__prepare_app_run_args(_FakeNode(), inputs, app_step)
|
|
27
|
+
return run_kwargs, instance_id
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestPrepareAppRunArgs(unittest.TestCase):
|
|
31
|
+
def test_no_run_configuration(self):
|
|
32
|
+
app_step = decorators.App(app_id="echo")
|
|
33
|
+
run_kwargs, _ = _prepare([{"data": [1, 2, 3]}], app_step)
|
|
34
|
+
self.assertNotIn("configuration", run_kwargs)
|
|
35
|
+
|
|
36
|
+
def test_decorator_run_configuration(self):
|
|
37
|
+
app_step = decorators.App(
|
|
38
|
+
app_id="echo",
|
|
39
|
+
run_configuration=nextmv.RunConfiguration(execution_class="4c8gb12h"),
|
|
40
|
+
)
|
|
41
|
+
run_kwargs, _ = _prepare([{"data": [1, 2, 3]}], app_step)
|
|
42
|
+
self.assertEqual(run_kwargs["configuration"].execution_class, "4c8gb12h")
|
|
43
|
+
|
|
44
|
+
def test_app_run_config_run_configuration(self):
|
|
45
|
+
app_step = decorators.App(app_id="echo")
|
|
46
|
+
app_run_config = AppRunConfig(
|
|
47
|
+
input={"data": [1, 2, 3]},
|
|
48
|
+
run_configuration=nextmv.RunConfiguration(execution_class="8c16gb12h"),
|
|
49
|
+
)
|
|
50
|
+
run_kwargs, _ = _prepare([app_run_config], app_step)
|
|
51
|
+
self.assertEqual(run_kwargs["configuration"].execution_class, "8c16gb12h")
|
|
52
|
+
|
|
53
|
+
def test_app_run_config_run_configuration_replaces_decorator(self):
|
|
54
|
+
"""The run configuration of an AppRunConfig replaces the decorator one as a whole."""
|
|
55
|
+
|
|
56
|
+
app_step = decorators.App(
|
|
57
|
+
app_id="echo",
|
|
58
|
+
run_configuration=nextmv.RunConfiguration(
|
|
59
|
+
execution_class="4c8gb12h",
|
|
60
|
+
secrets_collection_id="some-secrets",
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
app_run_config = AppRunConfig(
|
|
64
|
+
input={"data": [1, 2, 3]},
|
|
65
|
+
run_configuration=nextmv.RunConfiguration(execution_class="8c16gb12h"),
|
|
66
|
+
)
|
|
67
|
+
run_kwargs, _ = _prepare([app_run_config], app_step)
|
|
68
|
+
self.assertEqual(run_kwargs["configuration"].execution_class, "8c16gb12h")
|
|
69
|
+
self.assertIsNone(run_kwargs["configuration"].secrets_collection_id)
|
|
70
|
+
|
|
71
|
+
def test_app_run_config_without_run_configuration_keeps_decorator(self):
|
|
72
|
+
app_step = decorators.App(
|
|
73
|
+
app_id="echo",
|
|
74
|
+
run_configuration=nextmv.RunConfiguration(execution_class="4c8gb12h"),
|
|
75
|
+
)
|
|
76
|
+
app_run_config = AppRunConfig(input={"data": [1, 2, 3]})
|
|
77
|
+
run_kwargs, _ = _prepare([app_run_config], app_step)
|
|
78
|
+
self.assertEqual(run_kwargs["configuration"].execution_class, "4c8gb12h")
|
|
79
|
+
|
|
80
|
+
def test_no_instance_id(self):
|
|
81
|
+
app_step = decorators.App(app_id="echo")
|
|
82
|
+
_, instance_id = _prepare([{"data": [1, 2, 3]}], app_step)
|
|
83
|
+
self.assertEqual(instance_id, "")
|
|
84
|
+
|
|
85
|
+
def test_decorator_instance_id(self):
|
|
86
|
+
app_step = decorators.App(app_id="echo", instance_id="decorator-instance")
|
|
87
|
+
_, instance_id = _prepare([{"data": [1, 2, 3]}], app_step)
|
|
88
|
+
self.assertEqual(instance_id, "decorator-instance")
|
|
89
|
+
|
|
90
|
+
def test_app_run_config_instance_id_replaces_decorator(self):
|
|
91
|
+
app_step = decorators.App(app_id="echo", instance_id="decorator-instance")
|
|
92
|
+
app_run_config = AppRunConfig(input={"data": [1, 2, 3]}, instance_id="per-run-instance")
|
|
93
|
+
_, instance_id = _prepare([app_run_config], app_step)
|
|
94
|
+
self.assertEqual(instance_id, "per-run-instance")
|
|
95
|
+
|
|
96
|
+
def test_app_run_config_without_instance_id_keeps_decorator(self):
|
|
97
|
+
app_step = decorators.App(app_id="echo", instance_id="decorator-instance")
|
|
98
|
+
app_run_config = AppRunConfig(input={"data": [1, 2, 3]})
|
|
99
|
+
_, instance_id = _prepare([app_run_config], app_step)
|
|
100
|
+
self.assertEqual(instance_id, "decorator-instance")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if __name__ == "__main__":
|
|
104
|
+
unittest.main()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
import nextmv
|
|
6
|
+
|
|
7
|
+
from nextpipe import AppOption, AppRunConfig
|
|
8
|
+
|
|
9
|
+
# Add the parent directory to the sys.path to allow imports from the main package. This
|
|
10
|
+
# is meant to help VS Code testing features.
|
|
11
|
+
sys.path.append(os.path.dirname(sys.path[0]))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestAppRunConfig(unittest.TestCase):
|
|
15
|
+
def test_options_dict(self):
|
|
16
|
+
config = AppRunConfig(input={"data": [1, 2, 3]}, options={"threads": 4, "verbose": True}, name="test-run")
|
|
17
|
+
options = config.get_options()
|
|
18
|
+
self.assertEqual(options["threads"], "4")
|
|
19
|
+
self.assertEqual(options["verbose"], "True")
|
|
20
|
+
|
|
21
|
+
def test_options_obj(self):
|
|
22
|
+
config = AppRunConfig(
|
|
23
|
+
input={"data": [1, 2, 3]},
|
|
24
|
+
options=[AppOption(name="threads", value=4), AppOption(name="verbose", value=True)],
|
|
25
|
+
name="test-run",
|
|
26
|
+
)
|
|
27
|
+
options = config.get_options()
|
|
28
|
+
self.assertEqual(options["threads"], "4")
|
|
29
|
+
self.assertTrue(options["verbose"], "True")
|
|
30
|
+
|
|
31
|
+
def test_run_configuration_default(self):
|
|
32
|
+
config = AppRunConfig(input={"data": [1, 2, 3]})
|
|
33
|
+
self.assertIsNone(config.run_configuration)
|
|
34
|
+
|
|
35
|
+
def test_run_configuration(self):
|
|
36
|
+
config = AppRunConfig(
|
|
37
|
+
input={"data": [1, 2, 3]},
|
|
38
|
+
run_configuration=nextmv.RunConfiguration(execution_class="8c16gb12h"),
|
|
39
|
+
)
|
|
40
|
+
self.assertEqual(config.run_configuration.execution_class, "8c16gb12h")
|
|
41
|
+
|
|
42
|
+
def test_run_configuration_json_round_trip(self):
|
|
43
|
+
config = AppRunConfig(
|
|
44
|
+
input={"data": [1, 2, 3]},
|
|
45
|
+
options={"threads": 4},
|
|
46
|
+
run_configuration=nextmv.RunConfiguration(
|
|
47
|
+
execution_class="8c16gb12h",
|
|
48
|
+
secrets_collection_id="some-secrets",
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
restored = AppRunConfig.from_json(config.to_json())
|
|
52
|
+
self.assertIsInstance(restored.run_configuration, nextmv.RunConfiguration)
|
|
53
|
+
self.assertEqual(restored.run_configuration.execution_class, "8c16gb12h")
|
|
54
|
+
self.assertEqual(restored.run_configuration.secrets_collection_id, "some-secrets")
|
|
55
|
+
|
|
56
|
+
def test_run_configuration_json_round_trip_none(self):
|
|
57
|
+
config = AppRunConfig(input={"data": [1, 2, 3]})
|
|
58
|
+
restored = AppRunConfig.from_json(config.to_json())
|
|
59
|
+
self.assertIsNone(restored.run_configuration)
|
|
60
|
+
|
|
61
|
+
def test_instance_id_default(self):
|
|
62
|
+
config = AppRunConfig(input={"data": [1, 2, 3]})
|
|
63
|
+
self.assertIsNone(config.instance_id)
|
|
64
|
+
|
|
65
|
+
def test_instance_id(self):
|
|
66
|
+
config = AppRunConfig(input={"data": [1, 2, 3]}, instance_id="tuned-for-large")
|
|
67
|
+
self.assertEqual(config.instance_id, "tuned-for-large")
|
|
68
|
+
restored = AppRunConfig.from_json(config.to_json())
|
|
69
|
+
self.assertEqual(restored.instance_id, "tuned-for-large")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "v0.7.2"
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
import unittest
|
|
4
|
-
|
|
5
|
-
from nextpipe import AppOption, AppRunConfig
|
|
6
|
-
|
|
7
|
-
# Add the parent directory to the sys.path to allow imports from the main package. This
|
|
8
|
-
# is meant to help VS Code testing features.
|
|
9
|
-
sys.path.append(os.path.dirname(sys.path[0]))
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class TestAppRunConfig(unittest.TestCase):
|
|
13
|
-
def test_options_dict(self):
|
|
14
|
-
config = AppRunConfig(input={"data": [1, 2, 3]}, options={"threads": 4, "verbose": True}, name="test-run")
|
|
15
|
-
options = config.get_options()
|
|
16
|
-
self.assertEqual(options["threads"], "4")
|
|
17
|
-
self.assertEqual(options["verbose"], "True")
|
|
18
|
-
|
|
19
|
-
def test_options_obj(self):
|
|
20
|
-
config = AppRunConfig(
|
|
21
|
-
input={"data": [1, 2, 3]},
|
|
22
|
-
options=[AppOption(name="threads", value=4), AppOption(name="verbose", value=True)],
|
|
23
|
-
name="test-run",
|
|
24
|
-
)
|
|
25
|
-
options = config.get_options()
|
|
26
|
-
self.assertEqual(options["threads"], "4")
|
|
27
|
-
self.assertTrue(options["verbose"], "True")
|
|
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
|