mantatech-sdk 0.5b0.dev65__py3-none-any.whl

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 (54) hide show
  1. manta/__init__.light.py +22 -0
  2. manta/__init__.py +83 -0
  3. manta/__main__.py +21 -0
  4. manta/apis/__init__.py +7 -0
  5. manta/apis/async_user_api.py +6458 -0
  6. manta/apis/graph.py +498 -0
  7. manta/apis/module.py +316 -0
  8. manta/apis/results.py +251 -0
  9. manta/apis/swarm.py +206 -0
  10. manta/apis/user_api.py +1016 -0
  11. manta/cli/__init__.py +1 -0
  12. manta/cli/commands/__init__.py +1 -0
  13. manta/cli/commands/base_handler.py +229 -0
  14. manta/cli/commands/doc.py +192 -0
  15. manta/cli/commands/install.py +346 -0
  16. manta/cli/commands/sdk.py +9 -0
  17. manta/cli/commands/sdk_cluster.py +211 -0
  18. manta/cli/commands/sdk_config.py +347 -0
  19. manta/cli/commands/sdk_globals.py +280 -0
  20. manta/cli/commands/sdk_logs.py +174 -0
  21. manta/cli/commands/sdk_main.py +167 -0
  22. manta/cli/commands/sdk_module.py +516 -0
  23. manta/cli/commands/sdk_nodes.py +168 -0
  24. manta/cli/commands/sdk_original.py +3873 -0
  25. manta/cli/commands/sdk_results.py +265 -0
  26. manta/cli/commands/sdk_swarm.py +454 -0
  27. manta/cli/commands/sdk_user.py +234 -0
  28. manta/cli/commands/status.py +292 -0
  29. manta/cli/component_detector.py +112 -0
  30. manta/cli/config_manager.py +445 -0
  31. manta/cli/main.py +265 -0
  32. manta/cli/utils/__init__.py +27 -0
  33. manta/cli/utils/converters.py +140 -0
  34. manta/clients/cluster_management_client.py +486 -0
  35. manta/clients/local_client.py +149 -0
  36. manta/clients/module_management_client.py +217 -0
  37. manta/clients/swarm_management_client.py +562 -0
  38. manta/clients/user_management_client.py +395 -0
  39. manta/clients/world_client.py +195 -0
  40. manta/light/__init__.py +31 -0
  41. manta/light/globals.py +245 -0
  42. manta/light/local.py +407 -0
  43. manta/light/logging_config.py +39 -0
  44. manta/light/path.py +116 -0
  45. manta/light/results.py +236 -0
  46. manta/light/task.py +100 -0
  47. manta/light/utils.py +217 -0
  48. manta/light/world.py +177 -0
  49. mantatech_sdk-0.5b0.dev65.dist-info/METADATA +1039 -0
  50. mantatech_sdk-0.5b0.dev65.dist-info/RECORD +54 -0
  51. mantatech_sdk-0.5b0.dev65.dist-info/WHEEL +5 -0
  52. mantatech_sdk-0.5b0.dev65.dist-info/entry_points.txt +2 -0
  53. mantatech_sdk-0.5b0.dev65.dist-info/licenses/LICENSE +683 -0
  54. mantatech_sdk-0.5b0.dev65.dist-info/top_level.txt +1 -0
manta/apis/graph.py ADDED
@@ -0,0 +1,498 @@
1
+ import uuid
2
+ from dataclasses import dataclass, field
3
+ from enum import Enum
4
+ from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Union
5
+ from uuid import UUID
6
+
7
+ from manta_common.conversions import ID, u64x, x2u64
8
+ from .module import Module, Scheduling
9
+
10
+ __all__ = ["Graph", "Edge", "Condition", "merge", "Driver", "Task"]
11
+
12
+
13
+ class Condition(Enum):
14
+ FINISHED = "FINISHED"
15
+ RESULT = "RESULT"
16
+
17
+
18
+ class Driver(Enum):
19
+ HOST = "host"
20
+ BRIDGE = "bridge"
21
+ OVERLAY = "overlay"
22
+
23
+
24
+ @dataclass(unsafe_hash=True)
25
+ class Edge:
26
+ """
27
+ Edge between two module IDs
28
+
29
+ Attributes
30
+ ----------
31
+ start : UUID
32
+ Head Module ID
33
+ end : UUID
34
+ Tail Module ID
35
+ condition : Condition
36
+ Condition for going from head module to tail module
37
+ """
38
+
39
+ start: UUID
40
+ end: UUID
41
+ condition: Condition
42
+
43
+
44
+ @dataclass
45
+ class Connection:
46
+ """
47
+ Class which holds connection before and after a node in the `Graph`
48
+
49
+ Attributes
50
+ ----------
51
+ previous : Set[Edge]
52
+ Downstream edges
53
+ next : Set[Edge]
54
+ Upstream edges
55
+ """
56
+
57
+ previous: Set[Edge] = field(default_factory=set)
58
+ next: Set[Edge] = field(default_factory=set)
59
+
60
+
61
+ class Graph:
62
+ """
63
+ The :code:`Graph` is a task graph where a task is described by a
64
+ :class:`Task <manta.graph.Task>` in order to be sent to
65
+ the Manager
66
+
67
+ Parameters
68
+ ----------
69
+ task_table : Optional[Dict[UUID, Task]]
70
+ Table for descriptions of :class:`Task <manta.graph.Task>`
71
+ connectivity_table : Optional[Dict[UUID, Connection]]
72
+ Table for connectivities between :class:`Task <manta.graph.Task>`
73
+ """
74
+
75
+ __slots__ = ["task_table", "connectivity_table"]
76
+
77
+ def __init__(
78
+ self,
79
+ task_table: Optional[Dict[UUID, "Task"]] = None,
80
+ connectivity_table: Optional[Dict[UUID, Connection]] = None,
81
+ ):
82
+ self.connectivity_table = connectivity_table or {}
83
+ self.task_table = task_table or {}
84
+
85
+ def add_to_task_table(self, uuid: UUID, task: "Task"):
86
+ """
87
+ Add a task description associated to its ID
88
+
89
+ Parameters
90
+ ----------
91
+ uuid : UUID
92
+ ID of the task
93
+ task : Task
94
+ Description of the task
95
+
96
+ Raises
97
+ ------
98
+ KeyError
99
+ If the ID already exists, it cannot be added twice.
100
+ """
101
+ if uuid in self.task_table:
102
+ raise KeyError(f"{uuid} already in the task table.")
103
+ self.task_table[uuid] = task
104
+
105
+ def add_to_connectivity_table(self, edge: Edge):
106
+ """
107
+ Add an edge
108
+
109
+ Parameters
110
+ ----------
111
+ edge : Edge
112
+ Edge between two IDs
113
+
114
+ Raises
115
+ ------
116
+ KeyError
117
+ If one of the IDs into the edge is not in the task table
118
+ """
119
+ # Check if IDs are associated to a module
120
+ if edge.start not in self.task_table:
121
+ raise KeyError(f"{edge.start} missing in the task table.")
122
+ if edge.end not in self.task_table:
123
+ raise KeyError(f"{edge.end} missing in the task table.")
124
+
125
+ # Set connection if they don't exist in the connectivity table
126
+ connection_start = self.connectivity_table.setdefault(edge.start, Connection())
127
+ connection_end = self.connectivity_table.setdefault(edge.end, Connection())
128
+
129
+ # Add edges to them
130
+ connection_start.next.add(edge)
131
+ connection_end.previous.add(edge)
132
+
133
+ def check_networks(self, networks: Dict[str, str]):
134
+ """
135
+ Check if all network names are found in :code:`networks`
136
+
137
+ Parameters
138
+ ----------
139
+ networks : Dict[str, str]
140
+ Networks from the :code:`Swarm`
141
+
142
+ Raises
143
+ ------
144
+ RuntimeError
145
+ Network name in a :code:`Task` was not found in :code:`networks`
146
+ """
147
+ for task in self.task_table.values():
148
+ if (network := task.network) and network not in networks:
149
+ raise RuntimeError(
150
+ f"Network {network} not found in declared swarm networks"
151
+ )
152
+
153
+ def starting_uuids(self) -> Iterator[UUID]:
154
+ """
155
+ Yield the starting UUIDs of the graph
156
+
157
+ Returns
158
+ -------
159
+ Iterator[UUID]
160
+ IDs of starting nodes in the graph
161
+ """
162
+ for task_uuid, connection in self.connectivity_table.items():
163
+ if len(connection.previous) == 0:
164
+ yield task_uuid
165
+
166
+ def propagation(
167
+ self, acc: list, uuid: UUID, visited_uuids: set, order: int
168
+ ) -> Tuple[List[Dict[str, Any]], set]:
169
+ """
170
+ Recursive function which explore a UUID in the graph and returns a succession
171
+ of tasks ordered by the definition of the graph
172
+
173
+ Parameters
174
+ ----------
175
+ acc : list
176
+ Output
177
+ uuid : UUID
178
+ UUID to explore
179
+ visited_uuids : set
180
+ Already visited uuids
181
+ order : int
182
+ Depth variable
183
+
184
+ Returns
185
+ -------
186
+ Tuple[list, set]
187
+ :code:`(acc, visited_uuids)` updated
188
+ """
189
+ connection = self.connectivity_table[uuid]
190
+ task = self.task_table[uuid]
191
+
192
+ task_dict = {
193
+ "task_id": ID(uuid.hex).oid,
194
+ "module": task.module,
195
+ "scheduling": task.scheduling.to_proto(),
196
+ "previous_tasks": [ID(edge.start.hex).oid for edge in connection.previous],
197
+ "next_tasks": [ID(edge.end.hex).oid for edge in connection.next],
198
+ "conditions": [str(edge.condition) for edge in connection.next],
199
+ "order": order,
200
+ "network": task.network,
201
+ "alias": task.alias,
202
+ }
203
+
204
+ acc.append(task_dict)
205
+
206
+ next_uuids = [edge.end for edge in connection.next]
207
+ visited_uuids.add(uuid)
208
+ if len(next_uuids) != 0:
209
+ for uuid in next_uuids:
210
+ if uuid not in visited_uuids:
211
+ acc, visited_uuids = self.propagation(
212
+ acc, uuid, visited_uuids, order + 1
213
+ )
214
+ return acc, visited_uuids
215
+
216
+ def to_proto(self) -> List[Dict[str, Any]]:
217
+ """
218
+ Make the graph as a protobuf structure
219
+
220
+ Returns
221
+ -------
222
+ List[Dict[str, Any]]
223
+ List of tasks
224
+ """
225
+ acc: List[Dict[str, Any]] = []
226
+ visited_uuids = set()
227
+
228
+ if len(self.connectivity_table) == 0:
229
+ assert len(self.task_table) == 1, (
230
+ "Swarm without connectivity must have only one task."
231
+ )
232
+ main_uuid = next(iter(self.task_table))
233
+ self.connectivity_table[main_uuid] = Connection()
234
+
235
+ for starting_uuid in self.starting_uuids():
236
+ acc, visited_uuids = self.propagation(acc, starting_uuid, visited_uuids, 0)
237
+
238
+ return acc
239
+
240
+ def __str__(self) -> str:
241
+ """
242
+ Organize the graph structure into a string by showing the UUIDs of the
243
+ tasks and their connections.
244
+
245
+ It will print the UUID of each task and how they are connected through the
246
+ `connectivity_table`, showing both the previous and next connections. Additionally,
247
+ it will display the associated network for each task (if applicable).
248
+ """
249
+
250
+ def alias_or_uuid(uuid):
251
+ task = self.task_table[uuid]
252
+ return str(task.alias or uuid)
253
+
254
+ if len(self.connectivity_table) == 0:
255
+ return "Graph()"
256
+
257
+ string = "Graph(\n"
258
+ for task_uuid, connection in self.connectivity_table.items():
259
+ string += (
260
+ f" {alias_or_uuid(task_uuid)!r}: (\n"
261
+ f" previous : {[alias_or_uuid(edge.start) for edge in connection.previous]},\n"
262
+ f" next : {[alias_or_uuid(edge.end) for edge in connection.next]},\n"
263
+ " ),\n"
264
+ )
265
+ string += ")"
266
+ return string
267
+
268
+
269
+ def merge(graph_list: List[Graph]) -> Graph:
270
+ """
271
+ Merge a list of graphs into one graph
272
+
273
+ Parameters
274
+ ----------
275
+ graph_list : List[Graph]
276
+ List of graphs to be merged
277
+
278
+ Returns
279
+ -------
280
+ Graph
281
+ Merged graph
282
+
283
+ Raises
284
+ ------
285
+ ValueError
286
+ If two graphs contain the same :code`TaskID`
287
+ """
288
+ task_table = {}
289
+ connectivity_table = {}
290
+ for graph in graph_list:
291
+ # if task_table.keys() & graph.task_table.keys():
292
+ # print(task_table.keys())
293
+ # print(graph.task_table.keys())
294
+ # raise ValueError("Not allow to merge two graphs with same `IDs`")
295
+ task_table.update(graph.task_table)
296
+ connectivity_table.update(graph.connectivity_table)
297
+
298
+ # print(task_table)
299
+ # print(connectivity_table)
300
+
301
+ return Graph(task_table, connectivity_table)
302
+
303
+
304
+ def fold(graph_list: List[Graph]) -> Graph:
305
+ """
306
+ Like :code:`merge` function, it generates a graph from a list
307
+ of graphs. However similarities, such as same `TaskIDs` are
308
+ put together
309
+
310
+ Parameters
311
+ ----------
312
+ graph_list : List[Graph]
313
+ List of graphs to be merged
314
+
315
+ Returns
316
+ -------
317
+ Graph
318
+ Merged graph based on similarities
319
+ """
320
+ task_table = {}
321
+ connectivity_table = {}
322
+ for graph in graph_list:
323
+ task_table.update(graph.task_table)
324
+ for edge, connection in graph.connectivity_table.items():
325
+ local_connection = connectivity_table.setdefault(edge, Connection())
326
+ local_connection.previous |= connection.previous
327
+ local_connection.next |= connection.next
328
+
329
+ return Graph(task_table, connectivity_table)
330
+
331
+
332
+ class Task:
333
+ """
334
+ Class which holds the description of a task
335
+
336
+ Parameters
337
+ ----------
338
+ python_program : Union[str, Path]
339
+ Python program code
340
+ image : str
341
+ Image name of the container in which the program will be executed
342
+ method : str
343
+ Currently, only :code:`"any"` is available
344
+ fixed : bool
345
+ If :code:`True`, the chosen nodes must remain the same over the execution of the :code:`Swarm`
346
+ excepted_ids : Optional[list]
347
+ List of ids that must not be chosen for this module
348
+ specified_ids : Optional[list]
349
+ List of ids that must be chosen for this module
350
+ maximum : Optional[Union[int, float]]
351
+ Maximum of ids that can be chosen for this module.
352
+ If it is a float, it is a percentage between :code:`0.0` and :code:`1.0`
353
+ network : Optional[str]
354
+ It indicates in which network the task will be executed.
355
+ Network name must exist in :code:`Swarm.networks`.
356
+ gpu : bool
357
+ If :code:`True`, the module will be executed on GPU
358
+
359
+ Attributes
360
+ ----------
361
+ module : Module
362
+ Description of a task
363
+ graph : Graph
364
+ Hold the connectivity with previous tasks
365
+ uuid : UUID
366
+ ID of the task
367
+
368
+ Examples
369
+ --------
370
+
371
+ >>> aggregator = Task(
372
+ ... module=Module("modules/aggregator.py", "manta-demo:latest"),
373
+ ... method="any",
374
+ ... fixed=False,
375
+ ... maximum=1,
376
+ ... alias="aggregator",
377
+ ... )
378
+ >>> worker = Task(module="worker_module_id")
379
+ """
380
+
381
+ def __init__(
382
+ self,
383
+ module: Module,
384
+ method: str = "all",
385
+ fixed: bool = False,
386
+ excepted_ids: Optional[list] = None,
387
+ specified_ids: Optional[list] = None,
388
+ maximum: Optional[Union[int, float]] = -1,
389
+ network: Optional[str] = None,
390
+ gpu: bool = False,
391
+ graph: Optional[Graph] = None,
392
+ alias: Optional[str] = None,
393
+ ):
394
+ self.module = module
395
+ self.scheduling = Scheduling(
396
+ method=method,
397
+ fixed=fixed,
398
+ excepted_ids=excepted_ids,
399
+ specified_ids=specified_ids,
400
+ maximum=maximum,
401
+ gpu=gpu,
402
+ )
403
+ self.network = network
404
+ self.alias = alias or self.module.name
405
+
406
+ self.graph = Graph() if graph is None else graph
407
+ self.uuid = uuid.uuid4()
408
+
409
+ # If this task has been initialized with module data,
410
+ # immediately register it in the graph
411
+ self.take_in(self.module)
412
+
413
+ def take_in(self, module: Module):
414
+ """
415
+ Add the module definition of the task to its graph
416
+
417
+ Parameters
418
+ ----------
419
+ module : Module
420
+ Module of a task
421
+ """
422
+ if module.name is not None:
423
+ self.uuid = uuid.UUID(hex=u64x(x2u64(module.name)))
424
+ self.graph.add_to_task_table(self.uuid, self)
425
+
426
+ def add_edge(self, uuid: uuid.UUID, condition: Condition):
427
+ """
428
+ Add an edge to its graph where it is executed by the Manager
429
+ given the :code:`Condition`
430
+
431
+ Parameters
432
+ ----------
433
+ uuid : uuid.UUID
434
+ ID of the task
435
+ condition : Condition
436
+ Condition which tells to the Manager when next task should be executed
437
+ """
438
+ self.graph.add_to_connectivity_table(Edge(uuid, self.uuid, condition))
439
+
440
+ def __call__(
441
+ self,
442
+ task: Optional[Union[List["Task"], "Task"]] = None,
443
+ condition: Condition = Condition.FINISHED,
444
+ ) -> "Task":
445
+ """
446
+ Update its own graph with connections to other modules
447
+
448
+ Parameters
449
+ ----------
450
+ task : Optional[Union[List[Task], Task]]
451
+ One or multiple tasks to connect with itself
452
+ condition : Condition
453
+ Condition which tells to the Manager when next task should be executed
454
+
455
+ Returns
456
+ -------
457
+ Task
458
+ Return itself with updated graph
459
+
460
+ Examples
461
+ --------
462
+
463
+ >>> aggregator = Task(
464
+ ... Module(
465
+ ... "modules/aggregator.py",
466
+ ... "manta-demo:latest",
467
+ ... ),
468
+ ... method="any",
469
+ ... fixed=False,
470
+ ... maximum=1,
471
+ ... alias="aggregator",
472
+ ... )
473
+ >>> worker = Task(
474
+ ... Module(
475
+ ... "modules/worker",
476
+ ... "manta-demo:latest",
477
+ ... ),
478
+ ... method="all",
479
+ ... fixed=False,
480
+ ... maximum=-1,
481
+ ... alias="worker",
482
+ ... )
483
+ >>> m = aggregator(worker) # start worker and follow by aggregator
484
+ """
485
+ if task is None:
486
+ return self
487
+ elif isinstance(task, Task):
488
+ self.graph = merge([self.graph, task.graph])
489
+ self.add_edge(task.uuid, condition)
490
+ else:
491
+ # Handle list of tasks
492
+ self.graph = merge([self.graph] + [t.graph for t in task])
493
+ for t in task:
494
+ self.add_edge(t.uuid, condition)
495
+ return self
496
+
497
+ def __str__(self): # pragma: no cover
498
+ return f"Task({self.uuid})"