nextpipe 0.1.0.dev4__tar.gz → 0.1.0.dev5__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 (43) hide show
  1. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/PKG-INFO +9 -4
  2. nextpipe-0.1.0.dev5/README.md +7 -0
  3. nextpipe-0.1.0.dev5/examples/README.md +3 -0
  4. nextpipe-0.1.0.dev5/examples/apps/echo/.gitignore +1 -0
  5. nextpipe-0.1.0.dev5/examples/apps/echo/README.md +17 -0
  6. nextpipe-0.1.0.dev5/examples/apps/echo/app.yaml +6 -0
  7. nextpipe-0.1.0.dev5/examples/apps/echo/main.py +11 -0
  8. nextpipe-0.1.0.dev5/examples/apps/echo/requirements.txt +1 -0
  9. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-chain/README.md +4 -0
  10. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-complex/README.md +7 -0
  11. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-ensemble/README.md +6 -1
  12. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-ensemble/main.py +7 -1
  13. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-preprocess/README.md +7 -0
  14. nextpipe-0.1.0.dev5/nextpipe/__about__.py +1 -0
  15. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/nextpipe/decorators.py +14 -0
  16. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/nextpipe/flow.py +84 -60
  17. nextpipe-0.1.0.dev5/nextpipe/schema.py +50 -0
  18. nextpipe-0.1.0.dev5/nextpipe/threads.py +75 -0
  19. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/pyproject.toml +2 -1
  20. nextpipe-0.1.0.dev4/README.md +0 -3
  21. nextpipe-0.1.0.dev4/nextpipe/__about__.py +0 -1
  22. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/.github/workflows/python-lint.yml +0 -0
  23. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/.github/workflows/python-test.yml +0 -0
  24. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/.github/workflows/release.yml +0 -0
  25. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/.gitignore +0 -0
  26. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/.prettierrc.yml +0 -0
  27. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/LICENSE.md +0 -0
  28. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-chain/app.yaml +0 -0
  29. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-chain/main.py +0 -0
  30. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-chain/requirements.txt +0 -0
  31. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-complex/app.yaml +0 -0
  32. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-complex/main.py +0 -0
  33. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-complex/requirements.txt +0 -0
  34. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-ensemble/app.yaml +0 -0
  35. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-ensemble/requirements.txt +0 -0
  36. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-preprocess/app.yaml +0 -0
  37. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-preprocess/main.py +0 -0
  38. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/examples/pipeline-preprocess/requirements.txt +0 -0
  39. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/nextpipe/__init__.py +0 -0
  40. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/nextpipe/utils.py +0 -0
  41. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/nextpipe.code-workspace +0 -0
  42. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/tests/__init__.py +0 -0
  43. {nextpipe-0.1.0.dev4 → nextpipe-0.1.0.dev5}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: nextpipe
3
- Version: 0.1.0.dev4
3
+ Version: 0.1.0.dev5
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
@@ -104,7 +104,8 @@ Classifier: Programming Language :: Python :: 3.10
104
104
  Classifier: Programming Language :: Python :: 3.11
105
105
  Classifier: Programming Language :: Python :: 3.12
106
106
  Requires-Python: >=3.8
107
- Requires-Dist: nextmv>=0.12.1.dev2
107
+ Requires-Dist: dataclasses-json>=0.6.7
108
+ Requires-Dist: nextmv>=0.13.1
108
109
  Requires-Dist: pathos>=0.3.2
109
110
  Requires-Dist: requests>=2.31.0
110
111
  Provides-Extra: dev
@@ -113,4 +114,8 @@ Description-Content-Type: text/markdown
113
114
 
114
115
  # nextpipe
115
116
 
116
- Framework for Decision Pipeline modeling and execution
117
+ Framework for Decision Pipeline modeling and execution.
118
+
119
+ ## Examples
120
+
121
+ You can find examples of how to use `nextpipe` in the [examples](./examples) directory.
@@ -0,0 +1,7 @@
1
+ # nextpipe
2
+
3
+ Framework for Decision Pipeline modeling and execution.
4
+
5
+ ## Examples
6
+
7
+ You can find examples of how to use `nextpipe` in the [examples](./examples) directory.
@@ -0,0 +1,3 @@
1
+ # Examples
2
+
3
+ This directory contains examples of how to use `nextpipe`. Each example typically makes use of other apps that get executed in the example pipeline. If these apps are not already available via [community-apps](https://github.com/nextmv-io/community-apps) (via CLI: `nextmv community clone -a <app-id>`), you can find them in the `./apps` directory.
@@ -0,0 +1 @@
1
+ *.json
@@ -0,0 +1,17 @@
1
+ # JSON echo
2
+
3
+ This is a sample app that reads JSON from the input and writes it to the output.
4
+ Furthermore, it adds a statistics section to the output.
5
+
6
+ ## Usage
7
+
8
+ ```bash
9
+ echo '{"hello": "world!"}' | python main.py
10
+ ```
11
+
12
+ ## Push to Nextmv
13
+
14
+ ```bash
15
+ nextmv app create -a echo -n "JSON echo" -d "This is a sample app that reads JSON from the input and writes it to the output."
16
+ nextmv app push -a echo
17
+ ```
@@ -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,11 @@
1
+ import time
2
+
3
+ import nextmv
4
+
5
+ before = time.time()
6
+ input = nextmv.load_local()
7
+ output = nextmv.Output(
8
+ solution={"echo": input.data},
9
+ statistics={"run": {"duration": time.time() - before}},
10
+ )
11
+ nextmv.write_local(output)
@@ -0,0 +1 @@
1
+ nextmv==0.13.1
@@ -13,6 +13,10 @@ graph TD
13
13
  enhance(enhance)
14
14
  ```
15
15
 
16
+ ## Pre-requisites
17
+
18
+ - Push the echo app as described in the [echo app README](../apps/echo/README.md)
19
+
16
20
  ## Usage
17
21
 
18
22
  ```bash
@@ -29,6 +29,13 @@ graph TD
29
29
  pick_best(pick_best)
30
30
  ```
31
31
 
32
+ ## Pre-requisites
33
+
34
+ - Subscribe to the following marketplace apps and name them as follows:
35
+ - _Nextmv Routing_: `routing-nextroute`
36
+ - _OR-Tools Routing_: `routing-ortools`
37
+ - _PyVroom Routing_: `routing-pyvroom`
38
+
32
39
  ## Usage
33
40
 
34
41
  ```bash
@@ -21,9 +21,14 @@ graph TD
21
21
  pick_best(pick_best)
22
22
  ```
23
23
 
24
+ ## Pre-requisites
25
+
26
+ - Subscribe to the following marketplace apps and name them as follows:
27
+ - _Nextmv Routing_: `routing-nextroute`
28
+
24
29
  ## Usage
25
30
 
26
31
  ```bash
27
32
  nextmv app push -a <app-id>
28
- cat /path/to/routing/input.json | nextmv app run -a <app-id> -e "8c16gb12h"
33
+ cat /path/to/routing/input.json | nextmv app run -a <app-id> -e "8c16gb12h" -o 'instance=v171-5s'
29
34
  ```
@@ -5,12 +5,18 @@ import nextmv
5
5
 
6
6
  from nextpipe import FlowSpec, app, needs, repeat, step
7
7
 
8
+ # Define the options for the workflow
9
+ parameters = [
10
+ nextmv.Parameter("instance", str, "devint", "App instance to use. Default is devint.", False),
11
+ ]
12
+ options = nextmv.Options(*parameters)
13
+
8
14
 
9
15
  # >>> Workflow definition
10
16
  class Flow(FlowSpec):
11
17
  @app(
12
18
  app_id="routing-nextroute",
13
- instance_id="v171-5s",
19
+ instance_id=options.instance,
14
20
  parameters={"model.constraints.enable.cluster": True},
15
21
  )
16
22
  @repeat(repetitions=3)
@@ -31,6 +31,13 @@ graph TD
31
31
  pick_best(pick_best)
32
32
  ```
33
33
 
34
+ ## Pre-requisites
35
+
36
+ - Subscribe to the following marketplace apps and name them as follows:
37
+ - _Nextmv Routing_: `routing-nextroute`
38
+ - _OR-Tools Routing_: `routing-ortools`
39
+ - _PyVroom Routing_: `routing-pyvroom`
40
+
34
41
  ## Usage
35
42
 
36
43
  ```bash
@@ -0,0 +1 @@
1
+ __version__ = "v0.1.0.dev5"
@@ -21,6 +21,8 @@ class Step:
21
21
  def __init__(self, function: callable):
22
22
  self.function = function
23
23
  self.type = StepType.DEFAULT
24
+ self.state = "pending"
25
+ self.run_ids = []
24
26
  self._inputs = {}
25
27
  self._output = None
26
28
 
@@ -37,6 +39,12 @@ class Step:
37
39
  def get_name(self):
38
40
  return self.function.__name__
39
41
 
42
+ def set_state(self, state: str):
43
+ self.state = state
44
+
45
+ def get_state(self):
46
+ return self.state
47
+
40
48
  def is_needs(self):
41
49
  return hasattr(self, "needs")
42
50
 
@@ -49,6 +57,12 @@ class Step:
49
57
  def is_app(self):
50
58
  return self.type == StepType.APP
51
59
 
60
+ def set_run_ids(self, run_ids: List[str]):
61
+ self.run_ids = run_ids
62
+
63
+ def get_run_ids(self):
64
+ return self.run_ids
65
+
52
66
 
53
67
  class Needs:
54
68
  def __init__(self, predecessors: List[callable]):
@@ -4,26 +4,16 @@ import collections
4
4
  import inspect
5
5
  import io
6
6
  import time
7
+ from importlib.metadata import version
7
8
  from typing import List, Optional, Union
8
9
 
9
- import nextmv
10
10
  from nextmv.cloud import Application, Client, StatusV2
11
- from pathos.multiprocessing import ProcessingPool as Pool
12
11
 
13
- from . import utils
14
-
15
-
16
- class FlowParameter(nextmv.Parameter):
17
- """
18
- Parameter that is used in a `FlowSpec`.
19
- """
20
-
21
- def __init__(self, name, type, default=None, description=None, required=False):
22
- super().__init__(name, type, default, description, required)
12
+ from . import decorators, schema, threads, utils
23
13
 
24
14
 
25
15
  class DAGNode:
26
- def __init__(self, step_function, step_definition, docstring):
16
+ def __init__(self, step_function: callable, step_definition: decorators.Step, docstring: str):
27
17
  self.step_function = step_function
28
18
  self.step = step_definition
29
19
  self.docstring = docstring
@@ -89,45 +79,51 @@ class FlowSpec:
89
79
 
90
80
  # Run the nodes in parallel
91
81
  tasks = {}
92
- with Pool(8) as pool:
93
- while open_nodes:
94
- while True:
95
- # Get the first node from the open nodes which has all its predecessors done
96
- node = next(
97
- iter(
98
- filter(
99
- lambda n: all(p in closed_nodes for p in n.predecessors),
100
- open_nodes,
101
- )
102
- ),
103
- None,
104
- )
105
- if node is None:
106
- # No more nodes to run at this point. Wait for the remaining tasks to finish.
107
- break
108
- open_nodes.remove(node)
109
- # Run the node asynchronously
110
- tasks[node] = pool.apipe(
111
- self.__run_node,
112
- node,
113
- self._get_inputs(node),
114
- self.client,
115
- )
116
-
117
- # Wait until at least one task is done
118
- task_done = False
119
- while not task_done:
120
- time.sleep(0.1)
121
- # Check if any tasks are done, if not, keep waiting
122
- for node, task in list(tasks.items()):
123
- if task.ready():
124
- # Remove task and mark successors as ready by adding them to the open list.
125
- result = task.get()
126
- self.set_result(node, result)
127
- del tasks[node]
128
- task_done = True
129
- closed_nodes.add(node)
130
- open_nodes.update(node.successors)
82
+ pool = threads.Pool(8)
83
+ while open_nodes:
84
+ while True:
85
+ # Get the first node from the open nodes which has all its predecessors done
86
+ node = next(
87
+ iter(
88
+ filter(
89
+ lambda n: all(p in closed_nodes for p in n.predecessors),
90
+ open_nodes,
91
+ )
92
+ ),
93
+ None,
94
+ )
95
+ if node is None:
96
+ # No more nodes to run at this point. Wait for the remaining tasks to finish.
97
+ break
98
+ open_nodes.remove(node)
99
+ # Skip the node if it is optional and the condition is not met
100
+ if node.step.skip():
101
+ utils.log(f"Skipping node {node.step.get_name()}")
102
+ node.step.set_state("skipped")
103
+ utils.log("NEXTPIPE_DAG_UPDATE=" + self.graph._persist_dag_update(node))
104
+ continue
105
+ # Run the node asynchronously
106
+ job = threads.Job(self.__run_node, (node, self._get_inputs(node), self.client))
107
+ pool.run(job)
108
+ tasks[node] = job
109
+ node.step.set_state("running")
110
+ utils.log("NEXTPIPE_DAG_UPDATE=" + self.graph._persist_dag_update(node))
111
+
112
+ # Wait until at least one task is done
113
+ task_done = False
114
+ while not task_done:
115
+ time.sleep(0.1)
116
+ # Check if any tasks are done, if not, keep waiting
117
+ for node, job in list(tasks.items()):
118
+ if job.done:
119
+ # Remove task and mark successors as ready by adding them to the open list.
120
+ self.set_result(node, job.result)
121
+ node.step.set_state("succeeded")
122
+ utils.log("NEXTPIPE_DAG_UPDATE=" + self.graph._persist_dag_update(node))
123
+ del tasks[node]
124
+ task_done = True
125
+ closed_nodes.add(node)
126
+ open_nodes.update(node.successors)
131
127
 
132
128
  def set_result(self, step: callable, result: object):
133
129
  self.results[step.step] = result
@@ -146,11 +142,6 @@ class FlowSpec:
146
142
  def __run_node(node: DAGNode, inputs: List[object], client: Client) -> Union[List[object], object, None]:
147
143
  utils.log(f"Running node {node.step.get_name()}")
148
144
 
149
- # Skip the node if it is optional and the condition is not met
150
- if node.step.skip():
151
- utils.log(f"Skipping node {node.step.get_name()}")
152
- return
153
-
154
145
  # Run the step
155
146
  if node.step.is_app():
156
147
  app_step = node.step.app
@@ -176,6 +167,7 @@ class FlowSpec:
176
167
  app = Application(client=client, id=app_step.app_id, default_instance_id=app_step.instance_id)
177
168
  # Run the app (or multiple runs if it is a repeat step)
178
169
  run_ids = [app.new_run(*i[0], **i[1]) for i in inputs]
170
+ node.step.set_run_ids(run_ids)
179
171
  outputs = utils.wait_for_runs(app=app, run_ids=run_ids)
180
172
  # Check if all runs were successful
181
173
  for output in outputs:
@@ -202,11 +194,14 @@ class FlowGraph:
202
194
  def __init__(self, flow_spec):
203
195
  self.flow_spec = flow_spec
204
196
  self.__create_graph(flow_spec)
197
+ self.__debug_print_head()
205
198
  self.__debug_print_graph()
199
+ # Print the DAG in persistence format
200
+ utils.log("NEXTPIPE_DAG=" + self._persist_dag())
206
201
  # Create a Mermaid diagram of the graph and log it
207
- mermaid = self.__to_mermaid()
202
+ mermaid = self._to_mermaid()
208
203
  utils.log(mermaid)
209
- mermaid_url = f'https://mermaid.ink/svg/{base64.b64encode(mermaid.encode("utf8")).decode("ascii")}?theme=dark'
204
+ mermaid_url = f"https://mermaid.ink/svg/{base64.b64encode(mermaid.encode('utf8')).decode('ascii')}?theme=dark"
210
205
  utils.log(f"Mermaid URL: {mermaid_url}")
211
206
 
212
207
  def __create_graph(self, flow_spec):
@@ -250,7 +245,30 @@ class FlowGraph:
250
245
  if cycle:
251
246
  raise Exception(f"Cycle detected in the flow graph, cycle nodes: {cycle_nodes}")
252
247
 
253
- def __to_mermaid(self):
248
+ def _persist_dag(self) -> str:
249
+ dto = schema.DAGDTO(
250
+ nodes=[
251
+ schema.NodeDTO(
252
+ id=node.step.get_name(),
253
+ app_id=node.step.app.app_id if node.step.is_app() else "",
254
+ step_name=node.step.get_name(),
255
+ docs=node.docstring,
256
+ successors=[s.step.get_name() for s in node.successors],
257
+ )
258
+ for node in self.nodes
259
+ ]
260
+ )
261
+ return schema.serialize_dag(dto)
262
+
263
+ def _persist_dag_update(self, node: DAGNode) -> str:
264
+ dto = schema.NodeUpdateDTO(
265
+ node_id=node.step.get_name(),
266
+ state=node.step.get_state(),
267
+ run_ids=node.step.get_run_ids(),
268
+ )
269
+ return schema.serialize_node_update(dto)
270
+
271
+ def _to_mermaid(self):
254
272
  """Convert the graph to a Mermaid diagram."""
255
273
  out = io.StringIO()
256
274
  out.write("graph TD\n")
@@ -272,6 +290,12 @@ class FlowGraph:
272
290
  out.write(f" {node_name} --> {successor.step.get_name()}\n")
273
291
  return out.getvalue()
274
292
 
293
+ def __debug_print_head(self):
294
+ utils.log(f"Flow: {self.flow_spec.__name__}")
295
+ utils.log(f"nextpipe: {version('nextpipe')}")
296
+ utils.log(f"nextmv: {version('nextmv')}")
297
+ utils.log("Flow graph nodes:")
298
+
275
299
  def __debug_print_graph(self):
276
300
  for node in self.nodes:
277
301
  utils.log("Node:")
@@ -0,0 +1,50 @@
1
+ import base64
2
+ import dataclasses
3
+ from typing import List
4
+
5
+ import dataclasses_json
6
+
7
+
8
+ @dataclasses_json.dataclass_json
9
+ @dataclasses.dataclass
10
+ class NodeUpdateDTO:
11
+ node_id: str
12
+ """The ID of the node to update."""
13
+ state: str
14
+ """The state of the node."""
15
+ run_ids: List[str] = dataclasses.field(default_factory=list)
16
+ """The ID of the associated run (if any)."""
17
+
18
+
19
+ @dataclasses_json.dataclass_json
20
+ @dataclasses.dataclass
21
+ class NodeDTO:
22
+ id: str
23
+ """The ID of the node."""
24
+ app_id: str
25
+ """The ID of the app this step represents (if any)."""
26
+ step_name: str
27
+ """The name of the step (func)."""
28
+ docs: str
29
+ """The doc string of the step."""
30
+ successors: List[str]
31
+ """The IDs of the nodes that depend on this node."""
32
+
33
+
34
+ @dataclasses_json.dataclass_json
35
+ @dataclasses.dataclass
36
+ class DAGDTO:
37
+ nodes: List[NodeDTO]
38
+ """The nodes in the DAG."""
39
+
40
+
41
+ def serialize_dag(dag: DAGDTO) -> str:
42
+ """Serialize the DAG for transmission."""
43
+ j_str = dag.to_json(separators=(",", ":"))
44
+ return base64.b64encode(j_str.encode()).decode()
45
+
46
+
47
+ def serialize_node_update(node_update: NodeUpdateDTO) -> str:
48
+ """Serialize the node update for transmission."""
49
+ j_str = node_update.to_json(separators=(",", ":"))
50
+ return base64.b64encode(j_str.encode()).decode()
@@ -0,0 +1,75 @@
1
+ import threading
2
+ import time
3
+ from typing import Callable, Optional
4
+
5
+
6
+ class Job:
7
+ def __init__(self, target: Callable, args: Optional[tuple] = None):
8
+ self.target = target
9
+ self.args = args
10
+ self.done = False
11
+ self.result = None
12
+
13
+ def run(self):
14
+ if self.args:
15
+ self.result = self.target(*self.args)
16
+ else:
17
+ self.result = self.target()
18
+ self.done = True
19
+
20
+
21
+ class Pool:
22
+ def __init__(self, max_threads: int):
23
+ self.max_threads = max_threads
24
+ self.counter = 0 # Used to assign unique IDs to threads
25
+ self.waiting = {}
26
+ self.running = {}
27
+ self.lock = threading.Lock()
28
+ self.cond = threading.Condition(self.lock)
29
+
30
+ def run(self, job: Job) -> None:
31
+ with self.lock:
32
+ self.counter += 1
33
+ thread_id = self.counter
34
+ self.waiting[thread_id] = job
35
+
36
+ def worker(job: Job, thread_id: int):
37
+ try:
38
+ job.run()
39
+ finally:
40
+ with self.lock:
41
+ self.running.pop(thread_id, None)
42
+ self.cond.notify_all() # Notify others that a thread is available
43
+
44
+ while True:
45
+ with self.lock:
46
+ if len(self.running) < self.max_threads:
47
+ # Move job from waiting to running
48
+ thread = threading.Thread(target=worker, args=(job, thread_id))
49
+ self.running[thread_id] = thread
50
+ self.waiting.pop(thread_id, None)
51
+ thread.start()
52
+ break # Successfully assigned the job to a thread
53
+ else:
54
+ self.cond.wait() # Wait until a thread is available
55
+
56
+ def join(self) -> None:
57
+ with self.cond:
58
+ while self.waiting or self.running:
59
+ self.cond.wait() # Wait until all jobs are finished
60
+
61
+
62
+ def test_pool():
63
+ def target(*args):
64
+ print(f"Running job with args: {args}")
65
+ time.sleep(0.5) # Simulate work
66
+
67
+ pool = Pool(2)
68
+ for i in range(1, 7): # Submit 6 jobs
69
+ pool.run(Job(target, (i,)))
70
+ pool.join()
71
+ print("All jobs completed.")
72
+
73
+
74
+ if __name__ == "__main__":
75
+ test_pool()
@@ -18,7 +18,8 @@ classifiers = [
18
18
  dependencies = [
19
19
  "requests>=2.31.0",
20
20
  "pathos>=0.3.2",
21
- "nextmv>=0.12.1.dev2",
21
+ "nextmv>=0.13.1",
22
+ "dataclasses-json>=0.6.7",
22
23
  ]
23
24
  description = "Framework for Decision Pipeline modeling and execution"
24
25
  dynamic = [
@@ -1,3 +0,0 @@
1
- # nextpipe
2
-
3
- Framework for Decision Pipeline modeling and execution
@@ -1 +0,0 @@
1
- __version__ = "v0.1.0.dev4"
File without changes
File without changes