dv-flow-mgr 1.0.0.14370893826a1__py3-none-any.whl → 1.0.0.14392445998a1__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.
- dv_flow/mgr/package_loader.py +1 -1
- dv_flow/mgr/task_graph_builder.py +96 -315
- dv_flow/mgr/task_graph_dot_writer.py +90 -30
- dv_flow/mgr/task_node.py +5 -0
- dv_flow/mgr/task_node_compound.py +6 -0
- dv_flow/mgr/task_node_leaf.py +4 -2
- {dv_flow_mgr-1.0.0.14370893826a1.dist-info → dv_flow_mgr-1.0.0.14392445998a1.dist-info}/METADATA +1 -1
- {dv_flow_mgr-1.0.0.14370893826a1.dist-info → dv_flow_mgr-1.0.0.14392445998a1.dist-info}/RECORD +12 -12
- {dv_flow_mgr-1.0.0.14370893826a1.dist-info → dv_flow_mgr-1.0.0.14392445998a1.dist-info}/WHEEL +0 -0
- {dv_flow_mgr-1.0.0.14370893826a1.dist-info → dv_flow_mgr-1.0.0.14392445998a1.dist-info}/entry_points.txt +0 -0
- {dv_flow_mgr-1.0.0.14370893826a1.dist-info → dv_flow_mgr-1.0.0.14392445998a1.dist-info}/licenses/LICENSE +0 -0
- {dv_flow_mgr-1.0.0.14370893826a1.dist-info → dv_flow_mgr-1.0.0.14392445998a1.dist-info}/top_level.txt +0 -0
dv_flow/mgr/package_loader.py
CHANGED
@@ -71,6 +71,7 @@ class TaskGraphBuilder(object):
|
|
71
71
|
_ns_scope_s : List[TaskNamespaceScope] = dc.field(default_factory=list)
|
72
72
|
_compound_task_ctxt_s : List[CompoundTaskCtxt] = dc.field(default_factory=list)
|
73
73
|
_task_rundir_s : List[List[str]] = dc.field(default_factory=list)
|
74
|
+
_task_node_s : List[TaskNode] = dc.field(default_factory=list)
|
74
75
|
_uses_count : int = 0
|
75
76
|
|
76
77
|
_log : logging.Logger = None
|
@@ -203,51 +204,7 @@ class TaskGraphBuilder(object):
|
|
203
204
|
|
204
205
|
def mkTaskGraph(self, task : str, rundir=None) -> TaskNode:
|
205
206
|
return self.mkTaskNode(task, rundir=rundir)
|
206
|
-
# self._task_node_m.clear()
|
207
|
-
|
208
|
-
# if rundir is not None:
|
209
|
-
# self._rundir_s.append(rundir)
|
210
|
-
|
211
|
-
# ret = self._mkTaskGraph(task)
|
212
|
-
|
213
|
-
# if rundir is not None:
|
214
|
-
# self._rundir_s.pop()
|
215
|
-
|
216
|
-
# return ret
|
217
|
-
|
218
|
-
def _mkTaskGraph(self, task : str) -> TaskNode:
|
219
|
-
if task in self.root_pkg.task_m.keys():
|
220
|
-
task_t = self.root_pkg.task_m[task]
|
221
|
-
else:
|
222
|
-
pass
|
223
|
-
|
224
|
-
if task_t is None:
|
225
|
-
raise Exception("Failed to find task %s" % task)
|
226
|
-
|
227
|
-
ctor = self._getTaskCtor(task_t)
|
228
|
-
|
229
|
-
params = ctor.mkTaskParams()
|
230
|
-
|
231
|
-
needs = []
|
232
|
-
|
233
|
-
for need in task_t.needs:
|
234
|
-
need_n = self.findTask(need.name)
|
235
|
-
if need_n is None:
|
236
|
-
raise Exception("Failed to find need %s" % need.name)
|
237
|
-
needs.append(need_n)
|
238
|
-
|
239
|
-
task = ctor.mkTaskNode(
|
240
|
-
builder=self,
|
241
|
-
params=params,
|
242
|
-
name=task,
|
243
|
-
needs=needs)
|
244
|
-
task.rundir = self.get_rundir(task.name)
|
245
|
-
# task.rundir = rundir
|
246
207
|
|
247
|
-
self._task_node_m[task.name] = task
|
248
|
-
|
249
|
-
return task
|
250
|
-
|
251
208
|
def mkTaskNode(self, task_t, name=None, srcdir=None, needs=None, **kwargs):
|
252
209
|
self._log.debug("--> mkTaskNode: %s" % task_t)
|
253
210
|
|
@@ -282,37 +239,59 @@ class TaskGraphBuilder(object):
|
|
282
239
|
break
|
283
240
|
return task
|
284
241
|
|
285
|
-
def _mkTaskNode(self, task : Task, hierarchical=False):
|
242
|
+
def _mkTaskNode(self, task : Task, name=None, srcdir=None, params=None, hierarchical=False):
|
286
243
|
|
287
244
|
if not hierarchical:
|
288
245
|
self._task_rundir_s.append([])
|
289
246
|
|
290
247
|
# Determine how to build this node
|
291
|
-
if
|
292
|
-
ret = self._mkTaskCompoundNode(
|
248
|
+
if self._isCompound(task):
|
249
|
+
ret = self._mkTaskCompoundNode(
|
250
|
+
task,
|
251
|
+
name=name,
|
252
|
+
srcdir=srcdir,
|
253
|
+
params=params,
|
254
|
+
hierarchical=hierarchical)
|
293
255
|
else:
|
294
|
-
ret = self._mkTaskLeafNode(
|
256
|
+
ret = self._mkTaskLeafNode(
|
257
|
+
task,
|
258
|
+
name=name,
|
259
|
+
srcdir=srcdir,
|
260
|
+
params=params,
|
261
|
+
hierarchical=hierarchical)
|
295
262
|
|
296
263
|
if not hierarchical:
|
297
264
|
self._task_rundir_s.pop()
|
298
265
|
|
299
266
|
return ret
|
300
267
|
|
268
|
+
def _isCompound(self, task):
|
269
|
+
if task.subtasks is not None and len(task.subtasks):
|
270
|
+
return True
|
271
|
+
elif task.uses is not None:
|
272
|
+
return self._isCompound(task.uses)
|
273
|
+
|
301
274
|
def _getTaskNode(self, name):
|
302
275
|
if name in self._task_node_m.keys():
|
303
276
|
return self._task_node_m[name]
|
304
277
|
else:
|
305
278
|
return self.mkTaskNode(name)
|
306
279
|
|
307
|
-
def _mkTaskLeafNode(self, task : Task, name=None) -> TaskNode:
|
280
|
+
def _mkTaskLeafNode(self, task : Task, name=None, srcdir=None, params=None, hierarchical=False) -> TaskNode:
|
308
281
|
self._log.debug("--> _mkTaskLeafNode %s" % task.name)
|
309
|
-
|
282
|
+
|
283
|
+
if name is None:
|
284
|
+
name = task.name
|
285
|
+
|
286
|
+
if srcdir is None:
|
287
|
+
srcdir = os.path.dirname(task.srcinfo.file)
|
288
|
+
|
289
|
+
if params is None:
|
290
|
+
params = task.paramT()
|
310
291
|
|
311
292
|
if task.rundir == RundirE.Unique:
|
312
293
|
self.enter_rundir(task.name)
|
313
294
|
|
314
|
-
if name is None:
|
315
|
-
name = task.name
|
316
295
|
|
317
296
|
callable = None
|
318
297
|
if task.run is not None:
|
@@ -328,19 +307,19 @@ class TaskGraphBuilder(object):
|
|
328
307
|
node = TaskNodeLeaf(
|
329
308
|
name=name,
|
330
309
|
srcdir=srcdir,
|
331
|
-
params=
|
310
|
+
params=params,
|
332
311
|
passthrough=task.passthrough,
|
333
312
|
consumes=task.consumes,
|
334
313
|
task=callable(task.run))
|
335
314
|
self._task_node_m[name] = node
|
336
315
|
node.rundir = self.get_rundir()
|
337
316
|
|
317
|
+
if len(self._task_node_s):
|
318
|
+
node.parent = self._task_node_s[-1]
|
319
|
+
|
338
320
|
# Now, link up the needs
|
339
321
|
self._log.debug("--> processing needs")
|
340
|
-
|
341
|
-
self._log.debug("-- need %s" % n.name)
|
342
|
-
nn = self._getTaskNode(n.name)
|
343
|
-
node.needs.append((nn, False))
|
322
|
+
self._gatherNeeds(task, node)
|
344
323
|
self._log.debug("<-- processing needs")
|
345
324
|
|
346
325
|
if task.rundir == RundirE.Unique:
|
@@ -349,23 +328,46 @@ class TaskGraphBuilder(object):
|
|
349
328
|
self._log.debug("<-- _mkTaskLeafNode %s" % task.name)
|
350
329
|
return node
|
351
330
|
|
352
|
-
def _mkTaskCompoundNode(self, task : Task, name=None) -> TaskNode:
|
331
|
+
def _mkTaskCompoundNode(self, task : Task, name=None, srcdir=None, params=None, hierarchical=False) -> TaskNode:
|
353
332
|
self._log.debug("--> _mkTaskCompoundNode %s" % task.name)
|
354
|
-
srcdir = os.path.dirname(task.srcinfo.file)
|
355
333
|
|
356
334
|
if name is None:
|
357
335
|
name = task.name
|
358
336
|
|
337
|
+
if srcdir is None:
|
338
|
+
srcdir = os.path.dirname(task.srcinfo.file)
|
339
|
+
|
340
|
+
if params is None:
|
341
|
+
params = task.paramT()
|
342
|
+
|
359
343
|
if task.rundir == RundirE.Unique:
|
360
344
|
self.enter_rundir(task.name)
|
361
345
|
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
346
|
+
if task.uses is not None:
|
347
|
+
# This is a compound task that is based on
|
348
|
+
# another. Create the base implementation
|
349
|
+
node = self._mkTaskNode(
|
350
|
+
task.uses,
|
351
|
+
name=name,
|
352
|
+
srcdir=srcdir,
|
353
|
+
params=params,
|
354
|
+
hierarchical=True)
|
355
|
+
|
356
|
+
if not isinstance(node, TaskNodeCompound):
|
357
|
+
# TODO: need to enclose the leaf node in a compound wrapper
|
358
|
+
raise Exception("Task %s is not compound" % task.uses)
|
359
|
+
else:
|
360
|
+
# Node represents the terminal node of the sub-DAG
|
361
|
+
node = TaskNodeCompound(
|
362
|
+
name=name,
|
363
|
+
srcdir=srcdir,
|
364
|
+
params=params)
|
365
|
+
|
366
|
+
if len(self._task_node_s):
|
367
|
+
node.parent = self._task_node_s[-1]
|
368
|
+
|
368
369
|
self._task_node_m[name] = node
|
370
|
+
self._task_node_s.append(node)
|
369
371
|
|
370
372
|
node.rundir = self.get_rundir()
|
371
373
|
|
@@ -374,11 +376,13 @@ class TaskGraphBuilder(object):
|
|
374
376
|
node.input.rundir = self.get_rundir()
|
375
377
|
self.leave_rundir()
|
376
378
|
|
377
|
-
self._log.debug("--> processing needs")
|
379
|
+
self._log.debug("--> processing needs (%s)" % task.name)
|
378
380
|
for need in task.needs:
|
379
|
-
self.
|
380
|
-
|
381
|
-
|
381
|
+
need_n = self._getTaskNode(need.name)
|
382
|
+
self._log.debug("Add need %s" % need_n.name)
|
383
|
+
if need_n is None:
|
384
|
+
raise Exception("Failed to find need %s" % need.name)
|
385
|
+
node.input.needs.append((need_n, False))
|
382
386
|
self._log.debug("<-- processing needs")
|
383
387
|
|
384
388
|
# TODO: handle strategy
|
@@ -387,11 +391,17 @@ class TaskGraphBuilder(object):
|
|
387
391
|
# For now, build out local tasks and link up the needs
|
388
392
|
tasks = []
|
389
393
|
for t in task.subtasks:
|
390
|
-
nn = self._mkTaskNode(t, True)
|
391
|
-
tasks.append(
|
394
|
+
nn = self._mkTaskNode(t, hierarchical=True)
|
395
|
+
node.tasks.append(nn)
|
396
|
+
# tasks.append((t, self._getTaskNode(t.name)))
|
397
|
+
tasks.append((t, nn))
|
398
|
+
|
399
|
+
# Pop the node stack, since we're done constructing the body
|
400
|
+
self._task_node_s.pop()
|
392
401
|
|
393
402
|
# Fill in 'needs'
|
394
403
|
for t, tn in tasks:
|
404
|
+
self._log.debug("Process node %s" % t.name)
|
395
405
|
|
396
406
|
referenced = None
|
397
407
|
for tt in task.subtasks:
|
@@ -400,7 +410,9 @@ class TaskGraphBuilder(object):
|
|
400
410
|
break
|
401
411
|
|
402
412
|
refs_internal = None
|
403
|
-
|
413
|
+
# Assess how this task is connected to others in the compound node
|
414
|
+
for nn,_ in tn.first.needs:
|
415
|
+
self._log.debug("Need: %s" % nn.name)
|
404
416
|
for _,tnn in tasks:
|
405
417
|
if nn == tnn:
|
406
418
|
refs_internal = tnn
|
@@ -414,7 +426,7 @@ class TaskGraphBuilder(object):
|
|
414
426
|
self._log.debug("Node %s doesn't reference any internal node" % t.name)
|
415
427
|
tn.needs.append((node.input, False))
|
416
428
|
else:
|
417
|
-
self._log.debug("Node references internal node %s" % refs_internal.name)
|
429
|
+
self._log.debug("Node %s references internal node %s" % (t.name, refs_internal.name))
|
418
430
|
|
419
431
|
if referenced is not None:
|
420
432
|
# Add this task as a dependency of the output
|
@@ -429,35 +441,18 @@ class TaskGraphBuilder(object):
|
|
429
441
|
|
430
442
|
return node
|
431
443
|
|
444
|
+
def _gatherNeeds(self, task_t, node):
|
445
|
+
self._log.debug("--> _gatherNeeds %s" % task_t.name)
|
446
|
+
if task_t.uses is not None:
|
447
|
+
self._gatherNeeds(task_t.uses, node)
|
448
|
+
|
449
|
+
for need in task_t.needs:
|
450
|
+
need_n = self._getTaskNode(need.name)
|
451
|
+
if need_n is None:
|
452
|
+
raise Exception("Failed to find need %s" % need.name)
|
453
|
+
node.needs.append((need_n, False))
|
454
|
+
self._log.debug("<-- _gatherNeeds %s" % task_t.name)
|
432
455
|
|
433
|
-
def getTaskCtor(self, spec : Union[str,'TaskSpec'], pkg : PackageDef = None) -> 'TaskNodeCtor':
|
434
|
-
from .task_def import TaskSpec
|
435
|
-
if type(spec) == str:
|
436
|
-
spec = TaskSpec(spec)
|
437
|
-
|
438
|
-
self._log.debug("--> getTaskCtor %s" % spec.name)
|
439
|
-
spec_e = spec.name.split(".")
|
440
|
-
task_name = spec_e[-1]
|
441
|
-
|
442
|
-
# if len(spec_e) == 1:
|
443
|
-
# # Just have a task name. Use the current package
|
444
|
-
# if len(self._pkg_s) == 0:
|
445
|
-
# raise Exception("No package context for task %s" % spec.name)
|
446
|
-
# pkg = self._pkg_s[-1]
|
447
|
-
# else:
|
448
|
-
# pkg_name = ".".join(spec_e[0:-1])
|
449
|
-
|
450
|
-
# try:
|
451
|
-
# pkg = self.getPackage(PackageSpec(pkg_name))
|
452
|
-
# except Exception as e:
|
453
|
-
# self._log.critical("Failed to find package %s while looking for task %s" % (pkg_name, spec.name))
|
454
|
-
# raise e
|
455
|
-
|
456
|
-
ctor = pkg.getTaskCtor(task_name)
|
457
|
-
|
458
|
-
self._log.debug("<-- getTaskCtor %s" % spec.name)
|
459
|
-
return ctor
|
460
|
-
|
461
456
|
def error(self, msg, loc=None):
|
462
457
|
if loc is not None:
|
463
458
|
marker = TaskMarker(msg=msg, severity=SeverityE.Error, loc=loc)
|
@@ -468,217 +463,3 @@ class TaskGraphBuilder(object):
|
|
468
463
|
def marker(self, marker):
|
469
464
|
self.marker_l(marker)
|
470
465
|
|
471
|
-
def _getTaskCtor(self, task : Task) -> TaskNodeCtor:
|
472
|
-
if task in self._task_ctor_m.keys():
|
473
|
-
ctor = self._task_ctor_m[task]
|
474
|
-
else:
|
475
|
-
ctor = self._mkTaskCtor(task)
|
476
|
-
self._task_ctor_m[task] = ctor
|
477
|
-
return ctor
|
478
|
-
|
479
|
-
def _mkTaskCtor(self, task):
|
480
|
-
srcdir = os.path.dirname(task.srcinfo.file)
|
481
|
-
self._log.debug("--> mkTaskCtor %s (srcdir: %s)" % (task.name, srcdir))
|
482
|
-
|
483
|
-
if len(task.subtasks) > 0:
|
484
|
-
self._log.debug("Task has a body")
|
485
|
-
# Compound task
|
486
|
-
self._log.debug("Task specifies sub-task implementation")
|
487
|
-
ctor = self._mkCompoundTaskCtor(task)
|
488
|
-
else:
|
489
|
-
self._log.debug("Task doesn't specify a body")
|
490
|
-
# Shell task or 'null'
|
491
|
-
ctor = self._mkLeafTaskCtor(task)
|
492
|
-
|
493
|
-
if ctor is None:
|
494
|
-
raise Exception()
|
495
|
-
|
496
|
-
return ctor
|
497
|
-
|
498
|
-
def _mkLeafTaskCtor(self, task) -> TaskNodeCtor:
|
499
|
-
self._log.debug("--> _mkLeafTaskCtor")
|
500
|
-
srcdir = os.path.dirname(task.srcinfo.file)
|
501
|
-
base_ctor_t : TaskNodeCtor = None
|
502
|
-
ctor_t : TaskNodeCtor = None
|
503
|
-
base_params = None
|
504
|
-
callable = None
|
505
|
-
# fullname = self.name + "." + task.name
|
506
|
-
# rundir = task.rundir
|
507
|
-
|
508
|
-
# TODO: should we have the ctor look this up itself?
|
509
|
-
# Want to confirm that the value can be found.
|
510
|
-
# Defer final resolution until actual graph building (post-config)
|
511
|
-
if task.uses is not None:
|
512
|
-
self._log.debug("Uses: %s" % task.uses.name)
|
513
|
-
|
514
|
-
base_ctor_t = self._getTaskCtor(task.uses)
|
515
|
-
|
516
|
-
if base_ctor_t is None:
|
517
|
-
self._log.error("Failed to load task ctor %s" % task.uses)
|
518
|
-
# base_params = base_ctor_t.mkTaskParams()
|
519
|
-
else:
|
520
|
-
self._log.debug("No 'uses' specified %s" % task.name)
|
521
|
-
|
522
|
-
self._log.debug("%d needs" % len(task.needs))
|
523
|
-
|
524
|
-
# Determine the implementation constructor first
|
525
|
-
if task.run is not None:
|
526
|
-
shell = task.shell if task.shell is not None else "shell"
|
527
|
-
|
528
|
-
if shell in self._shell_m.keys():
|
529
|
-
self._log.debug("Use shell implementation")
|
530
|
-
callable = self._shell_m[shell]
|
531
|
-
else:
|
532
|
-
self._log.debug("Shell %s not found" % shell)
|
533
|
-
raise Exception("Shell %s not found" % shell)
|
534
|
-
|
535
|
-
# if taskdef.body.pytask is not None:
|
536
|
-
# # Built-in impl
|
537
|
-
# # Now, lookup the class
|
538
|
-
# self._log.debug("Use PyTask implementation")
|
539
|
-
# last_dot = taskdef.body.pytask.rfind('.')
|
540
|
-
# clsname = taskdef.body.pytask[last_dot+1:]
|
541
|
-
# modname = taskdef.body.pytask[:last_dot]
|
542
|
-
|
543
|
-
# try:
|
544
|
-
# if modname not in sys.modules:
|
545
|
-
# if srcdir not in sys.path:
|
546
|
-
# sys.path.append(srcdir)
|
547
|
-
# mod = importlib.import_module(modname)
|
548
|
-
# else:
|
549
|
-
# mod = sys.modules[modname]
|
550
|
-
# except ModuleNotFoundError as e:
|
551
|
-
# raise Exception("Failed to import module %s (_basedir=%s): %s" % (
|
552
|
-
# modname, self._basedir, str(e)))
|
553
|
-
|
554
|
-
# if not hasattr(mod, clsname):
|
555
|
-
# raise Exception("Method %s not found in module %s" % (clsname, modname))
|
556
|
-
# callable = getattr(mod, clsname)
|
557
|
-
# elif taskdef.body.run is not None:
|
558
|
-
# callable = self._getRunCallable(taskdef)
|
559
|
-
else:
|
560
|
-
# TODO: use null task
|
561
|
-
pass
|
562
|
-
|
563
|
-
# Determine if we need to use a new
|
564
|
-
if task.paramT is None:
|
565
|
-
raise Exception()
|
566
|
-
paramT = task.paramT
|
567
|
-
needs = []
|
568
|
-
|
569
|
-
# TODO:
|
570
|
-
rundir : RundirE = task.rundir
|
571
|
-
|
572
|
-
if callable is not None:
|
573
|
-
ctor_t = TaskNodeCtorTask(
|
574
|
-
name=task.name,
|
575
|
-
srcdir=srcdir,
|
576
|
-
paramT=task.paramT, # TODO: need to determine the parameter type
|
577
|
-
passthrough=task.passthrough,
|
578
|
-
consumes=task.consumes,
|
579
|
-
needs=needs, # TODO: need to determine the needs
|
580
|
-
rundir=rundir,
|
581
|
-
task=callable)
|
582
|
-
elif base_ctor_t is not None:
|
583
|
-
# Use the existing (base) to create the implementation
|
584
|
-
ctor_t = TaskNodeCtorProxy(
|
585
|
-
name=task.name,
|
586
|
-
srcdir=srcdir,
|
587
|
-
paramT=task.paramT, # TODO: need to determine the parameter type
|
588
|
-
passthrough=task.passthrough,
|
589
|
-
consumes=task.consumes,
|
590
|
-
needs=needs,
|
591
|
-
rundir=rundir,
|
592
|
-
uses=base_ctor_t)
|
593
|
-
else:
|
594
|
-
self._log.debug("Use 'Null' as the class implementation")
|
595
|
-
ctor_t = TaskNodeCtorTask(
|
596
|
-
name=task.name,
|
597
|
-
srcdir=srcdir,
|
598
|
-
paramT=paramT,
|
599
|
-
passthrough=task.passthrough,
|
600
|
-
consumes=task.consumes,
|
601
|
-
needs=needs,
|
602
|
-
rundir=rundir,
|
603
|
-
task=TaskNull)
|
604
|
-
|
605
|
-
self._log.debug("<-- mkTaskCtor %s" % task.name)
|
606
|
-
return ctor_t
|
607
|
-
|
608
|
-
def _getRunCallable(self, task):
|
609
|
-
self._log.debug("--> _getRunCallable %s" % task.name)
|
610
|
-
callable = None
|
611
|
-
if task.run is not None and task.shell == "python":
|
612
|
-
# Evaluate a Python script
|
613
|
-
pass
|
614
|
-
else:
|
615
|
-
# run a shell script
|
616
|
-
shell = None
|
617
|
-
body = task.run.strip()
|
618
|
-
|
619
|
-
callable = ShellCallable(body=body, shell=shell)
|
620
|
-
pass
|
621
|
-
return callable
|
622
|
-
|
623
|
-
def _mkCompoundTaskCtor(self, task) -> TaskNodeCtor:
|
624
|
-
self._log.debug("--> _mkCompoundTaskCtor %s" % task.name)
|
625
|
-
srcdir = os.path.dirname(task.srcinfo.file)
|
626
|
-
base_ctor_t : TaskNodeCtor = None
|
627
|
-
ctor_t : TaskNodeCtor = None
|
628
|
-
base_params = None
|
629
|
-
callable = None
|
630
|
-
|
631
|
-
# fullname = self._getScopeFullname()
|
632
|
-
fullname = task.name
|
633
|
-
|
634
|
-
if task.uses is not None:
|
635
|
-
self._log.debug("Uses: %s" % task.uses)
|
636
|
-
base_ctor_t = task.uses.ctor
|
637
|
-
base_params = base_ctor_t.mkTaskParams()
|
638
|
-
|
639
|
-
if base_ctor_t is None:
|
640
|
-
self._log.error("Failed to load task ctor %s" % task.uses)
|
641
|
-
|
642
|
-
# TODO: should build during loading
|
643
|
-
# passthrough, consumes, needs = self._getPTConsumesNeeds(taskdef, base_ctor_t)
|
644
|
-
passthrough = []
|
645
|
-
consumes = []
|
646
|
-
needs = []
|
647
|
-
|
648
|
-
# Determine if we need to use a new
|
649
|
-
# paramT = self._getParamT(taskdef, base_params)
|
650
|
-
paramT = task.paramT
|
651
|
-
|
652
|
-
if base_ctor_t is not None:
|
653
|
-
ctor_t = TaskNodeCtorCompoundProxy(
|
654
|
-
name=fullname,
|
655
|
-
srcdir=srcdir,
|
656
|
-
paramT=paramT,
|
657
|
-
passthrough=passthrough,
|
658
|
-
consumes=consumes,
|
659
|
-
needs=needs,
|
660
|
-
task=task,
|
661
|
-
uses=base_ctor_t)
|
662
|
-
else:
|
663
|
-
self._log.debug("No 'uses' specified")
|
664
|
-
ctor_t = TaskNodeCtorCompound(
|
665
|
-
name=fullname,
|
666
|
-
srcdir=srcdir,
|
667
|
-
paramT=paramT,
|
668
|
-
passthrough=passthrough,
|
669
|
-
consumes=consumes,
|
670
|
-
needs=needs,
|
671
|
-
task=task)
|
672
|
-
|
673
|
-
for st in task.subtasks:
|
674
|
-
ctor = self._getTaskCtor(st)
|
675
|
-
if ctor is None:
|
676
|
-
raise Exception("ctor for %s is None" % st.name)
|
677
|
-
ctor_t.tasks.append(st)
|
678
|
-
|
679
|
-
# for t in task.subtasks:
|
680
|
-
# ctor_t.tasks.append(self._mkTaskCtor(t, srcdir))
|
681
|
-
|
682
|
-
|
683
|
-
self._log.debug("<-- mkCompoundTaskCtor %s (%d)" % (task.name, len(ctor_t.tasks)))
|
684
|
-
return ctor_t
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import dataclasses as dc
|
2
2
|
import logging
|
3
3
|
import sys
|
4
|
-
from typing import ClassVar, Dict, TextIO
|
4
|
+
from typing import ClassVar, Dict, Set, TextIO
|
5
5
|
from .task_node import TaskNode
|
6
6
|
from .task_node_compound import TaskNodeCompound
|
7
7
|
|
@@ -10,6 +10,7 @@ class TaskGraphDotWriter(object):
|
|
10
10
|
fp : TextIO = dc.field(default=None)
|
11
11
|
_ind : str = ""
|
12
12
|
_node_id_m : Dict[TaskNode, str] = dc.field(default_factory=dict)
|
13
|
+
_processed_needs : Set[TaskNode] = dc.field(default_factory=set)
|
13
14
|
_node_id : int = 1
|
14
15
|
_cluster_id : int = 1
|
15
16
|
_log : ClassVar = logging.getLogger("TaskGraphDotWriter")
|
@@ -22,48 +23,107 @@ class TaskGraphDotWriter(object):
|
|
22
23
|
else:
|
23
24
|
self.fp = open(filename, "w")
|
24
25
|
self.println("digraph G {")
|
25
|
-
|
26
|
+
# First, build-out all nodes
|
27
|
+
self.build_node(node)
|
28
|
+
self.process_needs(node)
|
26
29
|
self.println("}")
|
27
30
|
|
28
31
|
self.fp.close()
|
29
32
|
self._log.debug("<-- TaskGraphDotWriter::write")
|
30
33
|
|
31
|
-
def
|
32
|
-
self._log.debug("-->
|
33
|
-
node_id = self._node_id
|
34
|
-
self._node_id += 1
|
35
|
-
node_name = "n%d" % self._node_id
|
36
|
-
self._node_id_m[node] = node_name
|
34
|
+
def build_node(self, node):
|
35
|
+
self._log.debug("--> build_node %s (%d)" % (node.name, len(node.needs),))
|
37
36
|
|
38
37
|
if isinstance(node, TaskNodeCompound):
|
39
|
-
self.
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
self.
|
45
|
-
self.process_node(node.input)
|
46
|
-
|
47
|
-
self.println("%s[label=\"%s.out\"];" % (
|
48
|
-
node_name,
|
49
|
-
node.name))
|
38
|
+
self._log.debug("-- compound node")
|
39
|
+
# Find the root and build out any expanded sub-nodes
|
40
|
+
root = node
|
41
|
+
while root.parent is not None:
|
42
|
+
root = root.parent
|
43
|
+
self.build_compound_node(root)
|
50
44
|
else:
|
51
|
-
|
52
|
-
|
53
|
-
|
45
|
+
# Leaf node
|
46
|
+
self._log.debug("-- leaf node")
|
47
|
+
node_id = self._node_id
|
48
|
+
self._node_id += 1
|
49
|
+
node_name = "n%d" % node_id
|
50
|
+
self._node_id_m[node] = node_name
|
51
|
+
self.println("%s[label=\"%s\"];" % (node_name, node.name))
|
52
|
+
self._log.debug("<-- build_node %s (%d)" % (node.name, len(node.needs),))
|
53
|
+
|
54
|
+
def process_needs(self, node):
|
55
|
+
self._log.debug("--> process_needs %s (%d)" % (node.name, len(node.needs),))
|
56
|
+
|
57
|
+
# if isinstance(node, TaskNodeCompound):
|
58
|
+
# self.println("subgraph cluster_%d {" % self._cluster_id)
|
59
|
+
# self._cluster_id += 1
|
60
|
+
# self.inc_ind()
|
61
|
+
# self.println("label=\"%s\";" % node.name)
|
62
|
+
# self.println("color=blue;")
|
63
|
+
# self.println("style=dashed;")
|
64
|
+
# self.process_node(node.input)
|
54
65
|
|
55
|
-
|
56
|
-
|
57
|
-
|
66
|
+
# self.println("%s[label=\"%s.out\"];" % (
|
67
|
+
# node_name,
|
68
|
+
# node.name))
|
69
|
+
# else:
|
70
|
+
# self.println("%s[label=\"%s\"];" % (
|
71
|
+
# node_name,
|
72
|
+
# node.name))
|
73
|
+
|
74
|
+
for dep,_ in node.needs:
|
75
|
+
if dep not in self._node_id_m.keys():
|
76
|
+
self.build_node(dep)
|
77
|
+
if dep not in self._node_id_m.keys():
|
78
|
+
self._log.error("Dep-node not built: %s" % dep.name)
|
79
|
+
if node not in self._node_id_m.keys():
|
80
|
+
self.build_node(node)
|
81
|
+
if node not in self._node_id_m.keys():
|
82
|
+
self._log.error("Dep-node not built: %s" % node.name)
|
58
83
|
self.println("%s -> %s;" % (
|
59
|
-
self._node_id_m[dep
|
84
|
+
self._node_id_m[dep],
|
60
85
|
self._node_id_m[node]))
|
86
|
+
if dep not in self._processed_needs:
|
87
|
+
self._processed_needs.add(dep)
|
88
|
+
self.process_needs(dep)
|
61
89
|
|
62
|
-
|
63
|
-
|
64
|
-
|
90
|
+
self._log.debug("<-- process_needs %s (%d)" % (node.name, len(node.needs),))
|
91
|
+
|
92
|
+
def build_compound_node(self, node):
|
93
|
+
"""Hierarchical build of a compound root node"""
|
94
|
+
|
95
|
+
self._log.debug("--> build_compound_node %s (%d)" % (node.name, len(node.tasks),))
|
96
|
+
|
97
|
+
id = self._cluster_id
|
98
|
+
self._cluster_id += 1
|
99
|
+
self.println("subgraph cluster_%d {" % id)
|
100
|
+
self.inc_ind()
|
101
|
+
self.println("label=\"%s\";" % node.name)
|
102
|
+
self.println("color=blue;")
|
103
|
+
self.println("style=dashed;")
|
104
|
+
|
105
|
+
task_node_id = self._node_id
|
106
|
+
self._node_id += 1
|
107
|
+
task_node_name = "n%d" % task_node_id
|
108
|
+
self.println("%s[label=\"%s\"];" % (task_node_name, node.name))
|
109
|
+
self._node_id_m[node] = task_node_name
|
110
|
+
|
111
|
+
for n in node.tasks:
|
112
|
+
if isinstance(n, TaskNodeCompound):
|
113
|
+
# Recurse
|
114
|
+
self.build_compound_node(n)
|
115
|
+
else:
|
116
|
+
# Leaf node
|
117
|
+
node_id = self._node_id
|
118
|
+
self._node_id += 1
|
119
|
+
node_name = "n%d" % node_id
|
120
|
+
self._node_id_m[n] = node_name
|
121
|
+
leaf_name = n.name[n.name.rfind(".") + 1:]
|
122
|
+
self.println("%s[label=\"%s\"];" % (node_name, leaf_name))
|
123
|
+
self.dec_ind()
|
124
|
+
self.println("}")
|
65
125
|
|
66
|
-
self._log.debug("<--
|
126
|
+
self._log.debug("<-- build_compound_node %s (%d)" % (node.name, len(node.tasks),))
|
67
127
|
|
68
128
|
def println(self, l):
|
69
129
|
self.fp.write("%s%s\n" % (self._ind, l))
|
dv_flow/mgr/task_node.py
CHANGED
@@ -56,6 +56,7 @@ class TaskNode(object):
|
|
56
56
|
start : float = dc.field(default=None)
|
57
57
|
end : float = dc.field(default=None)
|
58
58
|
save_exec_data : bool = dc.field(default=True)
|
59
|
+
parent : 'TaskNode' = dc.field(default=None)
|
59
60
|
|
60
61
|
_log : ClassVar = logging.getLogger("TaskNode")
|
61
62
|
|
@@ -69,6 +70,10 @@ class TaskNode(object):
|
|
69
70
|
if not isinstance(need, tuple):
|
70
71
|
self.needs[i] = (need, False)
|
71
72
|
|
73
|
+
@property
|
74
|
+
def first(self):
|
75
|
+
return self
|
76
|
+
|
72
77
|
async def do_run(self,
|
73
78
|
runner,
|
74
79
|
rundir,
|
@@ -46,8 +46,14 @@ class TaskNodeCompound(TaskNode):
|
|
46
46
|
srcdir=self.srcdir,
|
47
47
|
params=NullParams())
|
48
48
|
self.input.task = null_run
|
49
|
+
self.tasks.append(self.input)
|
50
|
+
|
49
51
|
return super().__post_init__()
|
50
52
|
|
53
|
+
@property
|
54
|
+
def first(self):
|
55
|
+
return self.input
|
56
|
+
|
51
57
|
async def do_run(self,
|
52
58
|
runner : TaskRunner,
|
53
59
|
rundir,
|
dv_flow/mgr/task_node_leaf.py
CHANGED
@@ -98,7 +98,7 @@ class TaskNodeLeaf(TaskNode):
|
|
98
98
|
else:
|
99
99
|
self._log.debug("consumes(unknown): %s" % str(self.consumes))
|
100
100
|
|
101
|
-
for name,field in self.params.model_fields.items():
|
101
|
+
for name,field in type(self.params).model_fields.items():
|
102
102
|
value = getattr(self.params, name)
|
103
103
|
if type(value) == str:
|
104
104
|
if value.find("${{") != -1:
|
@@ -170,7 +170,9 @@ class TaskNodeLeaf(TaskNode):
|
|
170
170
|
for need,block in self.needs:
|
171
171
|
if not block:
|
172
172
|
output.extend(need.output.output)
|
173
|
-
|
173
|
+
elif self.consumes == ConsumesE.All:
|
174
|
+
self._log.debug("All inputs are consumed, so not propagating any")
|
175
|
+
elif isinstance(self.consumes, list):
|
174
176
|
# Filter out parameter sets that were consumed
|
175
177
|
self._log.debug("Propagating non-consumed input parameters to output")
|
176
178
|
self._log.debug("consumes: %s" % str(self.consumes))
|
{dv_flow_mgr-1.0.0.14370893826a1.dist-info → dv_flow_mgr-1.0.0.14392445998a1.dist-info}/RECORD
RENAMED
@@ -18,7 +18,7 @@ dv_flow/mgr/out,sha256=d8GGBi3J43fhdLBlnsUbzBfRe0TD0QTP3nOTz54l2bI,200
|
|
18
18
|
dv_flow/mgr/package.py,sha256=PvOuNfuy-SlaiMDzT5SU48OlPJrhS9vpNCgX06KNyTo,2336
|
19
19
|
dv_flow/mgr/package_def.py,sha256=-UyeFb_0Sj16RtS2vxtIxTfl-oW7adJjM1I3ZSjcRpc,5729
|
20
20
|
dv_flow/mgr/package_import_spec.py,sha256=aZMpnS9a5NFY76_pYXEuO3-Mkc_xFzy73fdrUe_54Dc,1760
|
21
|
-
dv_flow/mgr/package_loader.py,sha256=
|
21
|
+
dv_flow/mgr/package_loader.py,sha256=Z80md4ZhJk6TPVlvYAWlnH1aJvATlB9skIyb1pZ1P8U,25075
|
22
22
|
dv_flow/mgr/param.py,sha256=kkxMRGf6mPjSZJsjgLKH2vJL62Sn0ZESvjBLkEYOp20,1386
|
23
23
|
dv_flow/mgr/param_def.py,sha256=hOBBRLiXJ5DakXkhrLCBAQ9GPlgq-QS52r0aflmIgbg,1832
|
24
24
|
dv_flow/mgr/param_ref_eval.py,sha256=5yH37oIX6f2qmk7GfRgNT5qZx0jm3CJFgB9lLDZZ1yQ,1981
|
@@ -31,11 +31,11 @@ dv_flow/mgr/srcinfo.py,sha256=xrfp-relVr7hYNbxOjY5jqX4H0nNMm9HK5AAM6_Piyk,366
|
|
31
31
|
dv_flow/mgr/task.py,sha256=Xe1sCCTN4yJwhB2Aw_rywdlmVPY2CDnuwZisULuOXhw,2036
|
32
32
|
dv_flow/mgr/task_data.py,sha256=lN7Iq8YTitEMGG4rZqYQi6Ri2HuPgBQ5oGQbW-63T8c,12436
|
33
33
|
dv_flow/mgr/task_def.py,sha256=VAjkFay7KxXfZoloZfbiPr6WhURztV2Dxm6-REMQweo,4912
|
34
|
-
dv_flow/mgr/task_graph_builder.py,sha256=
|
35
|
-
dv_flow/mgr/task_graph_dot_writer.py,sha256=
|
34
|
+
dv_flow/mgr/task_graph_builder.py,sha256=h0jiGY4VQI-wLJevawHl40wTILiMulA2eZpi0jFJ82A,16739
|
35
|
+
dv_flow/mgr/task_graph_dot_writer.py,sha256=fZlNR-ZUFsI5FpH1KFCWJcSpyLSKUh8IhHO9Eavc60c,4925
|
36
36
|
dv_flow/mgr/task_listener_log.py,sha256=QH7hAVO1agYJoOx16XqjWv1c-jk5zb6p6A-hic7OMU0,4150
|
37
|
-
dv_flow/mgr/task_node.py,sha256=
|
38
|
-
dv_flow/mgr/task_node_compound.py,sha256=
|
37
|
+
dv_flow/mgr/task_node.py,sha256=hPlCcped3nCNgOYj4oysVcxu4cHOIUCBUqAevOr20Lc,5086
|
38
|
+
dv_flow/mgr/task_node_compound.py,sha256=kmhfcHeqNwYE-XIRh8dsdU_zvLpAzHwVTdr4S3eAtN8,3080
|
39
39
|
dv_flow/mgr/task_node_ctor.py,sha256=YsoVMX5WbpbzcHvEK7ps_ZRV-J7MZ3F8NNozQw7vbog,4418
|
40
40
|
dv_flow/mgr/task_node_ctor_compound.py,sha256=290JdcTnL3b3Gv7s_wRLjdM02ezKhc9QnxZE0mv72i8,4379
|
41
41
|
dv_flow/mgr/task_node_ctor_compound_proxy.py,sha256=D8x54nD8Pd-2-_mr1syhqVeSFfIVf100ldi3bdzmSfI,2073
|
@@ -43,7 +43,7 @@ dv_flow/mgr/task_node_ctor_def_base.py,sha256=_8QQHKDkONio_ve0Z409yxC0AMO8ocNBPD
|
|
43
43
|
dv_flow/mgr/task_node_ctor_proxy.py,sha256=ViOFJ64JM4-CGFZNl89BghFuKSQ66kZVqSj4v2PA6VA,1906
|
44
44
|
dv_flow/mgr/task_node_ctor_task.py,sha256=d49g90TyPCMFR8BuWWqp4ym-MW5vGSdDR0V47Ru28JY,2232
|
45
45
|
dv_flow/mgr/task_node_ctor_wrapper.py,sha256=Nb1CVcPHZofnb-iLWDHQWAxlTOdbRrnR9DdSxY8yOec,3626
|
46
|
-
dv_flow/mgr/task_node_leaf.py,sha256=
|
46
|
+
dv_flow/mgr/task_node_leaf.py,sha256=QZru3SisLZwykpfATJPo8qQJbRwArw0GBvRlLtLB8bs,8658
|
47
47
|
dv_flow/mgr/task_output.py,sha256=ZwyvwnYj_gHOEFAEOH3m24Xfc4Cn77hb1j7LkX8_3C4,1086
|
48
48
|
dv_flow/mgr/task_params_ctor.py,sha256=qlrzibGAFHmbqOu88jEoh1wOSFHu68Gwdgc259-50e8,1915
|
49
49
|
dv_flow/mgr/task_run_ctxt.py,sha256=w1EOG9R16j0eT8fNcOCqlfyFrw277onsSur607eNprY,2811
|
@@ -66,9 +66,9 @@ dv_flow/mgr/util/__main__.py,sha256=F0LXpCDpYTPalSo0dc1h_qZkip5v1AZYYh-vcYbh5s0,
|
|
66
66
|
dv_flow/mgr/util/util.py,sha256=cBNt3JJ0PGLlUQFTtBLi12i2j_9gNgSBtKdwS3VfF5Y,1566
|
67
67
|
dv_flow/mgr/util/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
68
68
|
dv_flow/mgr/util/cmds/cmd_schema.py,sha256=IJzZdxCSEgIQ79LpYiM7UqJ9RJ-7yraqmBN2XVgAgXA,1752
|
69
|
-
dv_flow_mgr-1.0.0.
|
70
|
-
dv_flow_mgr-1.0.0.
|
71
|
-
dv_flow_mgr-1.0.0.
|
72
|
-
dv_flow_mgr-1.0.0.
|
73
|
-
dv_flow_mgr-1.0.0.
|
74
|
-
dv_flow_mgr-1.0.0.
|
69
|
+
dv_flow_mgr-1.0.0.14392445998a1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
70
|
+
dv_flow_mgr-1.0.0.14392445998a1.dist-info/METADATA,sha256=B60agWgNKhS9gxExLemROgmyWl_S8SpiF2OqDjCyAKg,13336
|
71
|
+
dv_flow_mgr-1.0.0.14392445998a1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
72
|
+
dv_flow_mgr-1.0.0.14392445998a1.dist-info/entry_points.txt,sha256=1roy8wAFM48LabOvr6jiOw0MUs-qE8X3Vf8YykPazxk,50
|
73
|
+
dv_flow_mgr-1.0.0.14392445998a1.dist-info/top_level.txt,sha256=amfVTkggzYPtWwLqNmRukfz1Buu0pGS2SrYBBLhXm04,8
|
74
|
+
dv_flow_mgr-1.0.0.14392445998a1.dist-info/RECORD,,
|
{dv_flow_mgr-1.0.0.14370893826a1.dist-info → dv_flow_mgr-1.0.0.14392445998a1.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|