nextpipe 0.1.0.dev6__tar.gz → 0.1.0.dev8__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.
Files changed (80) hide show
  1. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/.github/workflows/test.yml +4 -2
  2. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/PKG-INFO +2 -2
  3. nextpipe-0.1.0.dev8/examples/apps/echo/input.json +1 -0
  4. {nextpipe-0.1.0.dev6/examples/pipeline-complex → nextpipe-0.1.0.dev8/examples/pipeline-chain}/app.yaml +2 -3
  5. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-chain/main.py +0 -5
  6. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-complex/README.md +1 -1
  7. {nextpipe-0.1.0.dev6/examples/pipeline-ensemble → nextpipe-0.1.0.dev8/examples/pipeline-complex}/app.yaml +2 -3
  8. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-complex/main.py +26 -16
  9. nextpipe-0.1.0.dev8/examples/pipeline-ensemble/app.yaml +6 -0
  10. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-ensemble/main.py +0 -5
  11. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-foreach/README.md +1 -1
  12. nextpipe-0.1.0.dev8/examples/pipeline-foreach/app.yaml +6 -0
  13. nextpipe-0.1.0.dev8/examples/pipeline-foreach/main.py +58 -0
  14. nextpipe-0.1.0.dev8/examples/pipeline-preprocess/app.yaml +6 -0
  15. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-preprocess/main.py +0 -5
  16. nextpipe-0.1.0.dev8/nextpipe/__about__.py +1 -0
  17. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/nextpipe/config.py +21 -1
  18. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/nextpipe/flow.py +2 -2
  19. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/nextpipe/uplink.py +2 -2
  20. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/nextpipe/utils.py +3 -3
  21. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/pyproject.toml +1 -1
  22. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/pipelines/chain.py +0 -5
  23. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/pipelines/complex.py +0 -5
  24. nextpipe-0.1.0.dev8/tests/pipelines/foreach-2-pred.json +3 -0
  25. nextpipe-0.1.0.dev8/tests/pipelines/foreach-2-pred.json.golden +65 -0
  26. nextpipe-0.1.0.dev8/tests/pipelines/foreach-2-pred.py +58 -0
  27. nextpipe-0.1.0.dev8/tests/pipelines/foreach.json +3 -0
  28. nextpipe-0.1.0.dev8/tests/pipelines/foreach.json.golden +56 -0
  29. nextpipe-0.1.0.dev6/examples/pipeline-foreach/main.py → nextpipe-0.1.0.dev8/tests/pipelines/foreach.py +0 -5
  30. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/test_integration.py +36 -4
  31. nextpipe-0.1.0.dev6/examples/pipeline-foreach/app.yaml +0 -7
  32. nextpipe-0.1.0.dev6/examples/pipeline-preprocess/app.yaml +0 -7
  33. nextpipe-0.1.0.dev6/nextpipe/__about__.py +0 -1
  34. nextpipe-0.1.0.dev6/tests/deploy/app.yaml +0 -7
  35. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/.github/workflows/lint.yml +0 -0
  36. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/.github/workflows/release.yml +0 -0
  37. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/.gitignore +0 -0
  38. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/.prettierrc.yml +0 -0
  39. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/LICENSE.md +0 -0
  40. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/README.md +0 -0
  41. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/README.md +0 -0
  42. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/apps/echo/.gitignore +0 -0
  43. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/apps/echo/README.md +0 -0
  44. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/apps/echo/app.yaml +0 -0
  45. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/apps/echo/main.py +0 -0
  46. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/apps/echo/requirements.txt +0 -0
  47. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-chain/README.md +0 -0
  48. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-chain/requirements.txt +0 -0
  49. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-complex/requirements.txt +0 -0
  50. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-ensemble/README.md +0 -0
  51. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-ensemble/requirements.txt +0 -0
  52. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-foreach/requirements.txt +0 -0
  53. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-preprocess/README.md +0 -0
  54. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/examples/pipeline-preprocess/requirements.txt +0 -0
  55. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/nextpipe/__init__.py +0 -0
  56. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/nextpipe/decorators.py +0 -0
  57. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/nextpipe/graph.py +0 -0
  58. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/nextpipe/schema.py +0 -0
  59. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/nextpipe/threads.py +0 -0
  60. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/nextpipe.code-workspace +0 -0
  61. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/.gitignore +0 -0
  62. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/README.md +0 -0
  63. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/__init__.py +0 -0
  64. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/apps/echo/.gitignore +0 -0
  65. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/apps/echo/README.md +0 -0
  66. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/apps/echo/app.yaml +0 -0
  67. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/apps/echo/main.py +0 -0
  68. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/apps/echo/requirements.txt +0 -0
  69. {nextpipe-0.1.0.dev6/examples/pipeline-chain → nextpipe-0.1.0.dev8/tests/deploy}/app.yaml +0 -0
  70. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/deploy/main.py +0 -0
  71. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/deploy/requirements.txt +0 -0
  72. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/pipelines/chain.json +0 -0
  73. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/pipelines/chain.json.golden +0 -0
  74. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/pipelines/complex.json +0 -0
  75. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/pipelines/complex.json.golden +0 -0
  76. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/pipelines/fail.py +0 -0
  77. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/test_graph.py +0 -0
  78. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/test_threads.py +0 -0
  79. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/test_uplink.py +0 -0
  80. {nextpipe-0.1.0.dev6 → nextpipe-0.1.0.dev8}/tests/test_version.py +0 -0
@@ -5,7 +5,7 @@ jobs:
5
5
  runs-on: ubuntu-latest
6
6
  strategy:
7
7
  matrix:
8
- python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
8
+ python-version: ["3.9", "3.11", "3.13"]
9
9
  steps:
10
10
  - name: git clone
11
11
  uses: actions/checkout@v4
@@ -23,4 +23,6 @@ jobs:
23
23
  - name: unit tests
24
24
  env:
25
25
  NEXTMV_API_KEY_NEXTPIPE: ${{ secrets.NEXTMV_API_KEY_NEXTPIPE }}
26
- run: python -m unittest
26
+ run: |
27
+ export NEXTMV_API_KEY=$NEXTMV_API_KEY_NEXTPIPE
28
+ python -m unittest
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nextpipe
3
- Version: 0.1.0.dev6
3
+ Version: 0.1.0.dev8
4
4
  Summary: Framework for Decision Pipeline modeling and execution
5
5
  Project-URL: Homepage, https://www.nextmv.io
6
6
  Project-URL: Documentation, https://www.nextmv.io/docs
@@ -109,7 +109,7 @@ Requires-Dist: nextmv>=0.13.1
109
109
  Requires-Dist: pathos>=0.3.2
110
110
  Requires-Dist: requests>=2.31.0
111
111
  Provides-Extra: dev
112
- Requires-Dist: goldie>=0.1.6; extra == 'dev'
112
+ Requires-Dist: goldie>=0.1.7; extra == 'dev'
113
113
  Requires-Dist: ruff>=0.6.4; extra == 'dev'
114
114
  Description-Content-Type: text/markdown
115
115
 
@@ -0,0 +1 @@
1
+ { "hello": "world!" }
@@ -1,7 +1,6 @@
1
1
  type: python
2
2
  runtime: ghcr.io/nextmv-io/runtime/python:3.11
3
3
  files:
4
- - main.py
5
- - key.json
4
+ - main.py
6
5
  python:
7
- pip-requirements: requirements.txt
6
+ pip-requirements: requirements.txt
@@ -1,5 +1,4 @@
1
1
  import json
2
- import os
3
2
 
4
3
  import nextmv
5
4
 
@@ -31,10 +30,6 @@ class Flow(FlowSpec):
31
30
 
32
31
 
33
32
  def main():
34
- # Read API key from file (until secrets management support)
35
- with open("key.json") as f:
36
- os.environ["NEXTMV_API_KEY"] = json.load(f)["nextmv_api_key"]
37
-
38
33
  # Load input data
39
34
  input = nextmv.load_local()
40
35
 
@@ -40,5 +40,5 @@ graph LR
40
40
 
41
41
  ```bash
42
42
  nextmv app push -a <app-id>
43
- echo '{}' | nextmv app run -a <app-id>
43
+ curl "https://gist.githubusercontent.com/merschformann/a90959b87d1360b604e4a9f6457340ca/raw/661e631376bdf78a07548a3cd136c1fc6e47c639/muenster.json" | nextmv app run -a <app-id>
44
44
  ```
@@ -1,7 +1,6 @@
1
1
  type: python
2
2
  runtime: ghcr.io/nextmv-io/runtime/python:3.11
3
3
  files:
4
- - main.py
5
- - key.json
4
+ - main.py
6
5
  python:
7
- pip-requirements: requirements.txt
6
+ pip-requirements: requirements.txt
@@ -1,8 +1,7 @@
1
+ import copy
1
2
  import json
2
- import os
3
3
 
4
4
  import nextmv
5
- import requests
6
5
 
7
6
  from nextpipe import FlowSpec, app, needs, repeat, step
8
7
 
@@ -10,29 +9,39 @@ from nextpipe import FlowSpec, app, needs, repeat, step
10
9
  # >>> Workflow definition
11
10
  class Flow(FlowSpec):
12
11
  @step
13
- def fetch_data(_):
14
- """Fetches data from the database."""
15
- file_url = "https://gist.githubusercontent.com/merschformann/a90959b87d1360b604e4a9f6457340ca/raw/661e631376bdf78a07548a3cd136c1fc6e47c639/muenster.json"
16
- response = requests.get(file_url)
17
- return response.json()
12
+ def prepare(input: dict):
13
+ """Prepares the data."""
14
+ return input
18
15
 
19
- @repeat(repetitions=3)
16
+ @needs(predecessors=[prepare])
17
+ @step
18
+ def convert(input: dict):
19
+ """Converts the data."""
20
+ clone = copy.deepcopy(input)
21
+ if "defaults" in clone and "stops" in clone["defaults"] and "quantity" in clone["defaults"]["stops"]:
22
+ clone["defaults"]["stops"]["quantity"] *= -1
23
+ for stop in clone["stops"]:
24
+ if "quantity" in stop:
25
+ stop["quantity"] *= -1
26
+ return clone
27
+
28
+ @repeat(repetitions=2)
20
29
  @app(app_id="routing-nextroute")
21
- @needs(predecessors=[fetch_data])
30
+ @needs(predecessors=[prepare])
22
31
  @step
23
32
  def run_nextroute():
24
33
  """Runs the model."""
25
34
  pass
26
35
 
27
36
  @app(app_id="routing-ortools")
28
- @needs(predecessors=[fetch_data])
37
+ @needs(predecessors=[convert])
29
38
  @step
30
39
  def run_ortools():
31
40
  """Runs the model."""
32
41
  pass
33
42
 
34
43
  @app(app_id="routing-pyvroom")
35
- @needs(predecessors=[fetch_data])
44
+ @needs(predecessors=[convert])
36
45
  @step
37
46
  def run_pyvroom():
38
47
  """Runs the model."""
@@ -56,16 +65,17 @@ class Flow(FlowSpec):
56
65
  values.sort()
57
66
  nextmv.log(f"Values: {values}")
58
67
 
59
- return results[best_solution_idx]
68
+ # For test stability reasons, we always return the or-tools result
69
+ _ = results.pop(best_solution_idx)
70
+ return result_ortools
60
71
 
61
72
 
62
73
  def main():
63
- # Read API key from file (until secrets management support)
64
- with open("key.json") as f:
65
- os.environ["NEXTMV_API_KEY"] = json.load(f)["nextmv_api_key"]
74
+ # Load input data
75
+ input = nextmv.load_local()
66
76
 
67
77
  # Run workflow
68
- flow = Flow("DecisionFlow", None)
78
+ flow = Flow("DecisionFlow", input.data)
69
79
  flow.run()
70
80
  result = flow.get_result(flow.pick_best)
71
81
  print(json.dumps(result))
@@ -0,0 +1,6 @@
1
+ type: python
2
+ runtime: ghcr.io/nextmv-io/runtime/python:3.11
3
+ files:
4
+ - main.py
5
+ python:
6
+ pip-requirements: requirements.txt
@@ -1,5 +1,4 @@
1
1
  import json
2
- import os
3
2
 
4
3
  import nextmv
5
4
 
@@ -37,10 +36,6 @@ class Flow(FlowSpec):
37
36
 
38
37
 
39
38
  def main():
40
- # Read API key from file (until secrets management support)
41
- with open("key.json") as f:
42
- os.environ["NEXTMV_API_KEY"] = json.load(f)["nextmv_api_key"]
43
-
44
39
  # Load input data
45
40
  input = nextmv.load_local()
46
41
 
@@ -16,7 +16,7 @@ graph LR
16
16
 
17
17
  ## Pre-requisites
18
18
 
19
- - Push the echo app as described in the [echo app README](../apps/echo/README.md)`
19
+ - Push the echo app as described in the [echo app README](../apps/echo/README.md)
20
20
 
21
21
  ## Usage
22
22
 
@@ -0,0 +1,6 @@
1
+ type: python
2
+ runtime: ghcr.io/nextmv-io/runtime/python:3.11
3
+ files:
4
+ - main.py
5
+ python:
6
+ pip-requirements: requirements.txt
@@ -0,0 +1,58 @@
1
+ import copy
2
+ import json
3
+
4
+ import nextmv
5
+ import nextmv.cloud
6
+
7
+ from nextpipe import AppOption, AppRunConfig, FlowSpec, app, foreach, join, needs, step
8
+
9
+
10
+ class Flow(FlowSpec):
11
+ @foreach() # Run the successor step for each item in the result list of this step
12
+ @step
13
+ def fanout(data: dict):
14
+ """
15
+ Creates 3 copies of the input and configures them for 3 different app parameters.
16
+ """
17
+ inputs = [copy.deepcopy(data) for _ in range(3)]
18
+ run_configs = [AppRunConfig(input, [AppOption("param", i)]) for i, input in enumerate(inputs)]
19
+ return run_configs
20
+
21
+ @step
22
+ def stats(data: dict):
23
+ """
24
+ Calculates some statistics to put on the output as well.
25
+ """
26
+ return {"stats": {"count": len(data)}}
27
+
28
+ @app(app_id="echo")
29
+ @needs(predecessors=[fanout])
30
+ @step
31
+ def solve():
32
+ """
33
+ Runs the model.
34
+ """
35
+ pass
36
+
37
+ @needs(predecessors=[solve, stats])
38
+ @join() # Collect the results from the previous 'foreach' step and combine them into a list passed as the arg
39
+ @step
40
+ def merge(results: list):
41
+ """Merges the results."""
42
+ return results
43
+
44
+
45
+ def main():
46
+ # Load input data
47
+ input = nextmv.load_local()
48
+
49
+ # Run workflow
50
+ flow = Flow("DecisionFlow", input.data)
51
+ flow.run()
52
+
53
+ # Write out the result
54
+ print(json.dumps(flow.get_result(flow.merge)))
55
+
56
+
57
+ if __name__ == "__main__":
58
+ main()
@@ -0,0 +1,6 @@
1
+ type: python
2
+ runtime: ghcr.io/nextmv-io/runtime/python:3.11
3
+ files:
4
+ - main.py
5
+ python:
6
+ pip-requirements: requirements.txt
@@ -1,6 +1,5 @@
1
1
  import csv
2
2
  import json
3
- import os
4
3
 
5
4
  import requests
6
5
  from nextmv.logger import log
@@ -90,10 +89,6 @@ class Flow(FlowSpec):
90
89
 
91
90
 
92
91
  def main():
93
- # Read API key from file (until secrets management support)
94
- with open("key.json") as f:
95
- os.environ["NEXTMV_API_KEY"] = json.load(f)["nextmv_api_key"]
96
-
97
92
  # Run workflow
98
93
  flow = Flow("DecisionFlow", None)
99
94
  flow.run()
@@ -0,0 +1 @@
1
+ __version__ = "v0.1.0.dev8"
@@ -1,8 +1,24 @@
1
- from dataclasses import dataclass
1
+ from dataclasses import dataclass, field
2
2
 
3
3
  from dataclasses_json import dataclass_json
4
4
 
5
5
 
6
+ @dataclass_json
7
+ @dataclass
8
+ class AppPollingOptions:
9
+ """Options for polling the platform for the status of an app."""
10
+
11
+ timeout: float = 1800
12
+ """
13
+ Timeout in seconds for polling the platform.
14
+ This is used for example when waiting for results of an app run.
15
+ """
16
+ max_backoff: float = 30
17
+ """
18
+ Maximum backoff time in seconds.
19
+ """
20
+
21
+
6
22
  @dataclass_json
7
23
  @dataclass
8
24
  class Configuration:
@@ -20,3 +36,7 @@ class Configuration:
20
36
  of inputs used when a step has multiple predecessors which are themselves repeated or
21
37
  foreach steps.
22
38
  """
39
+ app_polling: AppPollingOptions = field(default_factory=AppPollingOptions)
40
+ """
41
+ Options for polling the platform for the status of an app.
42
+ """
@@ -288,8 +288,8 @@ class Runner:
288
288
  inputs = [list(item) for item in product(*predecessor_inputs.values())]
289
289
  # If the steps is a 'join' step, we need to combine the inputs from all predecessors.
290
290
  if step.definition.is_join():
291
- # Unwrap the inputs from the list of lists.
292
- inputs = [item[0] for item in inputs]
291
+ # Make sure that we only pass one list as the input.
292
+ inputs = [[inputs]]
293
293
  # If the step is a 'repeat' step, repeat the inputs for each repetition.
294
294
  if step.definition.is_repeat():
295
295
  inputs = inputs * step.definition.get_repetitions()
@@ -12,7 +12,7 @@ from nextpipe.utils import log
12
12
 
13
13
  FAILED_UPDATES_THRESHOLD = 10
14
14
 
15
- ENV_APPLICATION_ID = "NEXTMV_APPLICATION_ID"
15
+ ENV_APP_ID = "NEXTMV_APP_ID"
16
16
  ENV_RUN_ID = "NEXTMV_RUN_ID"
17
17
 
18
18
 
@@ -96,7 +96,7 @@ class UplinkClient:
96
96
  if config is None:
97
97
  # Load config from environment
98
98
  config = UplinkConfig(
99
- application_id=os.environ.get(ENV_APPLICATION_ID),
99
+ application_id=os.environ.get(ENV_APP_ID),
100
100
  run_id=os.environ.get(ENV_RUN_ID),
101
101
  )
102
102
  self.config = config
@@ -36,15 +36,15 @@ _INFINITE_TIMEOUT = sys.maxsize
36
36
  def wait_for_runs(
37
37
  app: Application,
38
38
  run_ids: list[str],
39
- timeout: int = _INFINITE_TIMEOUT,
40
- max_backoff: int = 30,
39
+ timeout: float = _INFINITE_TIMEOUT,
40
+ max_backoff: float = 30,
41
41
  ) -> list[RunResult]:
42
42
  """
43
43
  Wait until all runs with the given IDs are finished.
44
44
  """
45
45
  # Wait until all runs are finished or the timeout is reached
46
46
  missing = set(run_ids)
47
- backoff = 1
47
+ backoff = 2
48
48
  start_time = time.time()
49
49
  while missing and time.time() - start_time < timeout:
50
50
  time.sleep(backoff)
@@ -72,5 +72,5 @@ path = "nextpipe/__about__.py"
72
72
  [project.optional-dependencies]
73
73
  dev = [
74
74
  "ruff>=0.6.4",
75
- "goldie>=0.1.6",
75
+ "goldie>=0.1.7",
76
76
  ]
@@ -1,5 +1,4 @@
1
1
  import json
2
- import os
3
2
 
4
3
  import nextmv
5
4
 
@@ -31,10 +30,6 @@ class Flow(FlowSpec):
31
30
 
32
31
 
33
32
  def main():
34
- # Read API key from file (until secrets management support)
35
- with open("key.json") as f:
36
- os.environ["NEXTMV_API_KEY"] = json.load(f)["nextmv_api_key"]
37
-
38
33
  # Load input data
39
34
  input = nextmv.load_local()
40
35
 
@@ -1,5 +1,4 @@
1
1
  import json
2
- import os
3
2
 
4
3
  import nextmv
5
4
 
@@ -59,10 +58,6 @@ class Flow(FlowSpec):
59
58
 
60
59
 
61
60
  def main():
62
- # Read API key from file (until secrets management support)
63
- with open("key.json") as f:
64
- os.environ["NEXTMV_API_KEY"] = json.load(f)["nextmv_api_key"]
65
-
66
61
  # Load input data
67
62
  input = nextmv.load_local()
68
63
 
@@ -0,0 +1,3 @@
1
+ {
2
+ "hello": "world!"
3
+ }
@@ -0,0 +1,65 @@
1
+ [
2
+ {
3
+ "solution": {
4
+ "echo": {
5
+ "data": {
6
+ "hello": "world!"
7
+ },
8
+ "args": [
9
+ "-param=0"
10
+ ]
11
+ }
12
+ },
13
+ "statistics": {
14
+ "run": {
15
+ "duration": 0.123
16
+ }
17
+ },
18
+ "assets": [],
19
+ "stats": {
20
+ "count": 1
21
+ }
22
+ },
23
+ {
24
+ "solution": {
25
+ "echo": {
26
+ "data": {
27
+ "hello": "world!"
28
+ },
29
+ "args": [
30
+ "-param=1"
31
+ ]
32
+ }
33
+ },
34
+ "statistics": {
35
+ "run": {
36
+ "duration": 0.123
37
+ }
38
+ },
39
+ "assets": [],
40
+ "stats": {
41
+ "count": 1
42
+ }
43
+ },
44
+ {
45
+ "solution": {
46
+ "echo": {
47
+ "data": {
48
+ "hello": "world!"
49
+ },
50
+ "args": [
51
+ "-param=2"
52
+ ]
53
+ }
54
+ },
55
+ "statistics": {
56
+ "run": {
57
+ "duration": 0.123
58
+ }
59
+ },
60
+ "assets": [],
61
+ "stats": {
62
+ "count": 1
63
+ }
64
+ }
65
+ ]
@@ -0,0 +1,58 @@
1
+ import copy
2
+ import json
3
+
4
+ import nextmv
5
+ import nextmv.cloud
6
+
7
+ from nextpipe import AppOption, AppRunConfig, FlowSpec, app, foreach, join, needs, step
8
+
9
+
10
+ class Flow(FlowSpec):
11
+ @foreach() # Run the successor step for each item in the result list of this step
12
+ @step
13
+ def fanout(data: dict):
14
+ """
15
+ Creates 3 copies of the input and configures them for 3 different app parameters.
16
+ """
17
+ inputs = [copy.deepcopy(data) for _ in range(3)]
18
+ run_configs = [AppRunConfig(input, [AppOption("param", i)]) for i, input in enumerate(inputs)]
19
+ return run_configs
20
+
21
+ @step
22
+ def stats(data: dict):
23
+ """
24
+ Calculates some statistics to put on the output as well.
25
+ """
26
+ return {"stats": {"count": len(data)}}
27
+
28
+ @app(app_id="echo")
29
+ @needs(predecessors=[fanout])
30
+ @step
31
+ def solve():
32
+ """
33
+ Runs the model.
34
+ """
35
+ pass
36
+
37
+ @needs(predecessors=[solve, stats])
38
+ @join() # Collect the results from the previous 'foreach' step and combine them into a list passed as the arg
39
+ @step
40
+ def merge(results: list):
41
+ """Merges the results."""
42
+ return results
43
+
44
+
45
+ def main():
46
+ # Load input data
47
+ input = nextmv.load_local()
48
+
49
+ # Run workflow
50
+ flow = Flow("DecisionFlow", input.data)
51
+ flow.run()
52
+
53
+ # Write out the result
54
+ print(json.dumps(flow.get_result(flow.merge)))
55
+
56
+
57
+ if __name__ == "__main__":
58
+ main()
@@ -0,0 +1,3 @@
1
+ {
2
+ "hello": "world!"
3
+ }
@@ -0,0 +1,56 @@
1
+ [
2
+ {
3
+ "solution": {
4
+ "echo": {
5
+ "data": {
6
+ "hello": "world!"
7
+ },
8
+ "args": [
9
+ "-param=0"
10
+ ]
11
+ }
12
+ },
13
+ "statistics": {
14
+ "run": {
15
+ "duration": 0.123
16
+ }
17
+ },
18
+ "assets": []
19
+ },
20
+ {
21
+ "solution": {
22
+ "echo": {
23
+ "data": {
24
+ "hello": "world!"
25
+ },
26
+ "args": [
27
+ "-param=1"
28
+ ]
29
+ }
30
+ },
31
+ "statistics": {
32
+ "run": {
33
+ "duration": 0.123
34
+ }
35
+ },
36
+ "assets": []
37
+ },
38
+ {
39
+ "solution": {
40
+ "echo": {
41
+ "data": {
42
+ "hello": "world!"
43
+ },
44
+ "args": [
45
+ "-param=2"
46
+ ]
47
+ }
48
+ },
49
+ "statistics": {
50
+ "run": {
51
+ "duration": 0.123
52
+ }
53
+ },
54
+ "assets": []
55
+ }
56
+ ]
@@ -1,6 +1,5 @@
1
1
  import copy
2
2
  import json
3
- import os
4
3
 
5
4
  import nextmv
6
5
  import nextmv.cloud
@@ -37,10 +36,6 @@ class Flow(FlowSpec):
37
36
 
38
37
 
39
38
  def main():
40
- # Read API key from file (until secrets management support)
41
- with open("key.json") as f:
42
- os.environ["NEXTMV_API_KEY"] = json.load(f)["nextmv_api_key"]
43
-
44
39
  # Load input data
45
40
  input = nextmv.load_local()
46
41
 
@@ -2,7 +2,6 @@ import os
2
2
  import os.path
3
3
  import random
4
4
  import unittest
5
- from dataclasses import replace
6
5
 
7
6
  import goldie
8
7
  from nextmv import cloud
@@ -91,9 +90,42 @@ class TestExample(unittest.TestCase):
91
90
  configuration=config,
92
91
  )
93
92
 
93
+ # FOREACH
94
+ config.comparison_configuration.json_processing_config = goldie.ConfigProcessJson(
95
+ replacements=[
96
+ goldie.JsonReplacement(path="$[0].statistics.run.duration", value=0.123),
97
+ goldie.JsonReplacement(path="$[1].statistics.run.duration", value=0.123),
98
+ goldie.JsonReplacement(path="$[2].statistics.run.duration", value=0.123),
99
+ ],
100
+ )
101
+ goldie.run_file_unittest(
102
+ test=self,
103
+ td=goldie.TestDefinition(
104
+ input_file=os.path.join(path, "foreach.json"),
105
+ extra_args=[("pipeline", os.path.join(path, "foreach.py"))],
106
+ ),
107
+ configuration=config,
108
+ )
109
+
110
+ # FOREACH 2 PREDECESSORS
111
+ config.comparison_configuration.json_processing_config = goldie.ConfigProcessJson(
112
+ replacements=[
113
+ goldie.JsonReplacement(path="$[0][0].statistics.run.duration", value=0.123),
114
+ goldie.JsonReplacement(path="$[1][0].statistics.run.duration", value=0.123),
115
+ goldie.JsonReplacement(path="$[2][0].statistics.run.duration", value=0.123),
116
+ ],
117
+ )
118
+ goldie.run_file_unittest(
119
+ test=self,
120
+ td=goldie.TestDefinition(
121
+ input_file=os.path.join(path, "foreach-2-pred.json"),
122
+ extra_args=[("pipeline", os.path.join(path, "foreach-2-pred.py"))],
123
+ ),
124
+ configuration=config,
125
+ )
126
+
94
127
  # COMPLEX
95
- config_complex = replace(config)
96
- config_complex.comparison_configuration.json_processing_config = goldie.ConfigProcessJson(
128
+ config.comparison_configuration.json_processing_config = goldie.ConfigProcessJson(
97
129
  replacements=[
98
130
  goldie.JsonReplacement(path="$.statistics.result.duration", value="0.123"),
99
131
  goldie.JsonReplacement(path="$.statistics.run.duration", value="0.123"),
@@ -105,7 +137,7 @@ class TestExample(unittest.TestCase):
105
137
  input_file=os.path.join(path, "complex.json"),
106
138
  extra_args=[("pipeline", os.path.join(path, "complex.py"))],
107
139
  ),
108
- configuration=config_complex,
140
+ configuration=config,
109
141
  )
110
142
 
111
143
 
@@ -1,7 +0,0 @@
1
- type: python
2
- runtime: ghcr.io/nextmv-io/runtime/python:3.11
3
- files:
4
- - main.py
5
- - key.json
6
- python:
7
- pip-requirements: requirements.txt
@@ -1,7 +0,0 @@
1
- type: python
2
- runtime: ghcr.io/nextmv-io/runtime/python:3.11
3
- files:
4
- - main.py
5
- - key.json
6
- python:
7
- pip-requirements: requirements.txt
@@ -1 +0,0 @@
1
- __version__ = "v0.1.0.dev6"
@@ -1,7 +0,0 @@
1
- type: python
2
- runtime: ghcr.io/nextmv-io/runtime/python:3.11
3
- files:
4
- - main.py
5
- - key.json
6
- python:
7
- pip-requirements: requirements.txt
File without changes
File without changes
File without changes