dv-flow-mgr 1.0.0.14389547107a1__py3-none-any.whl → 1.0.0.14420805951a1__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.
@@ -394,6 +394,10 @@ class PackageLoader(object):
394
394
  else:
395
395
  print("Warning: file %s is not a fragment" % file)
396
396
 
397
+ def getTask(self, name) -> Task:
398
+ task = self._findTask(name)
399
+ return task
400
+
397
401
  def _loadTasks(self, pkg, taskdefs : List[TaskDef], basedir : str):
398
402
  self._log.debug("--> _loadTasks %s" % pkg.name)
399
403
  # Declare first
@@ -538,10 +542,16 @@ class PackageLoader(object):
538
542
  self._pkg_s[-1].pop_scope()
539
543
 
540
544
  def _findTaskType(self, name):
541
- return self._pkg_s[-1].find(name)
545
+ if len(self._pkg_s):
546
+ return self._pkg_s[-1].find(name)
547
+ else:
548
+ return self._loader_scope.find(name)
542
549
 
543
550
  def _findTask(self, name):
544
- return self._pkg_s[-1].find(name)
551
+ if len(self._pkg_s):
552
+ return self._pkg_s[-1].find(name)
553
+ else:
554
+ return self._loader_scope.find(name)
545
555
 
546
556
 
547
557
  def _getScopeFullname(self, leaf=None):
@@ -25,6 +25,7 @@ import logging
25
25
  from typing import Callable, Any, Dict, List, Union
26
26
  from .package import Package
27
27
  from .package_def import PackageDef, PackageSpec
28
+ from .package_loader import PackageLoader
28
29
  from .ext_rgy import ExtRgy
29
30
  from .task import Task
30
31
  from .task_def import RundirE
@@ -60,6 +61,7 @@ class TaskGraphBuilder(object):
60
61
  """The Task-Graph Builder knows how to discover packages and construct task graphs"""
61
62
  root_pkg : Package
62
63
  rundir : str
64
+ loader : PackageLoader = None
63
65
  marker_l : Callable = lambda *args, **kwargs: None
64
66
  _pkg_m : Dict[PackageSpec,Package] = dc.field(default_factory=dict)
65
67
  _pkg_spec_s : List[PackageDef] = dc.field(default_factory=list)
@@ -71,6 +73,7 @@ class TaskGraphBuilder(object):
71
73
  _ns_scope_s : List[TaskNamespaceScope] = dc.field(default_factory=list)
72
74
  _compound_task_ctxt_s : List[CompoundTaskCtxt] = dc.field(default_factory=list)
73
75
  _task_rundir_s : List[List[str]] = dc.field(default_factory=list)
76
+ _task_node_s : List[TaskNode] = dc.field(default_factory=list)
74
77
  _uses_count : int = 0
75
78
 
76
79
  _log : logging.Logger = None
@@ -78,7 +81,7 @@ class TaskGraphBuilder(object):
78
81
  def __post_init__(self):
79
82
  # Initialize the overrides from the global registry
80
83
  self._log = logging.getLogger(type(self).__name__)
81
- self._shell_m.update(ExtRgy._inst._shell_m)
84
+ self._shell_m.update(ExtRgy.inst()._shell_m)
82
85
  self._task_rundir_s.append([])
83
86
 
84
87
  if self.root_pkg is not None:
@@ -203,28 +206,24 @@ class TaskGraphBuilder(object):
203
206
 
204
207
  def mkTaskGraph(self, task : str, rundir=None) -> TaskNode:
205
208
  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
209
 
218
-
219
210
  def mkTaskNode(self, task_t, name=None, srcdir=None, needs=None, **kwargs):
220
211
  self._log.debug("--> mkTaskNode: %s" % task_t)
221
212
 
222
213
  if task_t in self._task_m.keys():
223
214
  task = self._task_m[task_t]
215
+ elif self.loader is not None:
216
+ task = self.loader.getTask(task_t)
217
+
218
+ if task is None:
219
+ raise Exception("task_t (%s) not present" % str(task_t))
224
220
  else:
225
221
  raise Exception("task_t (%s) not present" % str(task_t))
226
222
 
227
- ret = self._mkTaskNode(task)
223
+ ret = self._mkTaskNode(
224
+ task,
225
+ name=name,
226
+ srcdir=srcdir)
228
227
 
229
228
  if needs is not None:
230
229
  for need in needs:
@@ -250,38 +249,60 @@ class TaskGraphBuilder(object):
250
249
  break
251
250
  return task
252
251
 
253
- def _mkTaskNode(self, task : Task, hierarchical=False):
252
+ def _mkTaskNode(self, task : Task, name=None, srcdir=None, params=None, hierarchical=False):
254
253
 
255
254
  if not hierarchical:
256
255
  self._task_rundir_s.append([])
257
256
 
258
257
  # Determine how to build this node
259
- if task.subtasks is not None and len(task.subtasks):
260
- ret = self._mkTaskCompoundNode(task)
258
+ if self._isCompound(task):
259
+ ret = self._mkTaskCompoundNode(
260
+ task,
261
+ name=name,
262
+ srcdir=srcdir,
263
+ params=params,
264
+ hierarchical=hierarchical)
261
265
  else:
262
- ret = self._mkTaskLeafNode(task)
266
+ ret = self._mkTaskLeafNode(
267
+ task,
268
+ name=name,
269
+ srcdir=srcdir,
270
+ params=params,
271
+ hierarchical=hierarchical)
263
272
 
264
273
  if not hierarchical:
265
274
  self._task_rundir_s.pop()
266
275
 
267
276
  return ret
268
277
 
278
+ def _isCompound(self, task):
279
+ if task.subtasks is not None and len(task.subtasks):
280
+ return True
281
+ elif task.uses is not None:
282
+ return self._isCompound(task.uses)
283
+
269
284
  def _getTaskNode(self, name):
270
285
  if name in self._task_node_m.keys():
271
286
  return self._task_node_m[name]
272
287
  else:
273
288
  return self.mkTaskNode(name)
274
289
 
275
- def _mkTaskLeafNode(self, task : Task, name=None) -> TaskNode:
290
+ def _mkTaskLeafNode(self, task : Task, name=None, srcdir=None, params=None, hierarchical=False) -> TaskNode:
276
291
  self._log.debug("--> _mkTaskLeafNode %s" % task.name)
277
- srcdir = os.path.dirname(task.srcinfo.file)
278
-
279
- if task.rundir == RundirE.Unique:
280
- self.enter_rundir(task.name)
281
292
 
282
293
  if name is None:
283
294
  name = task.name
284
295
 
296
+ if srcdir is None:
297
+ srcdir = os.path.dirname(task.srcinfo.file)
298
+
299
+ if params is None:
300
+ params = task.paramT()
301
+
302
+ if task.rundir == RundirE.Unique:
303
+ self.enter_rundir(name)
304
+
305
+
285
306
  callable = None
286
307
  if task.run is not None:
287
308
  shell = task.shell if task.shell is not None else "shell"
@@ -296,13 +317,16 @@ class TaskGraphBuilder(object):
296
317
  node = TaskNodeLeaf(
297
318
  name=name,
298
319
  srcdir=srcdir,
299
- params=task.paramT(),
320
+ params=params,
300
321
  passthrough=task.passthrough,
301
322
  consumes=task.consumes,
302
323
  task=callable(task.run))
303
324
  self._task_node_m[name] = node
304
325
  node.rundir = self.get_rundir()
305
326
 
327
+ if len(self._task_node_s):
328
+ node.parent = self._task_node_s[-1]
329
+
306
330
  # Now, link up the needs
307
331
  self._log.debug("--> processing needs")
308
332
  self._gatherNeeds(task, node)
@@ -314,23 +338,46 @@ class TaskGraphBuilder(object):
314
338
  self._log.debug("<-- _mkTaskLeafNode %s" % task.name)
315
339
  return node
316
340
 
317
- def _mkTaskCompoundNode(self, task : Task, name=None) -> TaskNode:
341
+ def _mkTaskCompoundNode(self, task : Task, name=None, srcdir=None, params=None, hierarchical=False) -> TaskNode:
318
342
  self._log.debug("--> _mkTaskCompoundNode %s" % task.name)
319
- srcdir = os.path.dirname(task.srcinfo.file)
320
343
 
321
344
  if name is None:
322
345
  name = task.name
323
346
 
347
+ if srcdir is None:
348
+ srcdir = os.path.dirname(task.srcinfo.file)
349
+
350
+ if params is None:
351
+ params = task.paramT()
352
+
324
353
  if task.rundir == RundirE.Unique:
325
- self.enter_rundir(task.name)
354
+ self.enter_rundir(name)
355
+
356
+ if task.uses is not None:
357
+ # This is a compound task that is based on
358
+ # another. Create the base implementation
359
+ node = self._mkTaskNode(
360
+ task.uses,
361
+ name=name,
362
+ srcdir=srcdir,
363
+ params=params,
364
+ hierarchical=True)
365
+
366
+ if not isinstance(node, TaskNodeCompound):
367
+ # TODO: need to enclose the leaf node in a compound wrapper
368
+ raise Exception("Task %s is not compound" % task.uses)
369
+ else:
370
+ # Node represents the terminal node of the sub-DAG
371
+ node = TaskNodeCompound(
372
+ name=name,
373
+ srcdir=srcdir,
374
+ params=params)
375
+
376
+ if len(self._task_node_s):
377
+ node.parent = self._task_node_s[-1]
326
378
 
327
- # Node represents the terminal node of the sub-DAG
328
- node = TaskNodeCompound(
329
- name=name,
330
- srcdir=srcdir,
331
- params=task.paramT()
332
- )
333
379
  self._task_node_m[name] = node
380
+ self._task_node_s.append(node)
334
381
 
335
382
  node.rundir = self.get_rundir()
336
383
 
@@ -339,8 +386,13 @@ class TaskGraphBuilder(object):
339
386
  node.input.rundir = self.get_rundir()
340
387
  self.leave_rundir()
341
388
 
342
- self._log.debug("--> processing needs")
343
- self._gatherNeeds(task, node.input)
389
+ self._log.debug("--> processing needs (%s)" % task.name)
390
+ for need in task.needs:
391
+ need_n = self._getTaskNode(need.name)
392
+ self._log.debug("Add need %s" % need_n.name)
393
+ if need_n is None:
394
+ raise Exception("Failed to find need %s" % need.name)
395
+ node.input.needs.append((need_n, False))
344
396
  self._log.debug("<-- processing needs")
345
397
 
346
398
  # TODO: handle strategy
@@ -349,11 +401,17 @@ class TaskGraphBuilder(object):
349
401
  # For now, build out local tasks and link up the needs
350
402
  tasks = []
351
403
  for t in task.subtasks:
352
- nn = self._mkTaskNode(t, True)
353
- tasks.append((t, self._getTaskNode(t.name)))
404
+ nn = self._mkTaskNode(t, hierarchical=True)
405
+ node.tasks.append(nn)
406
+ # tasks.append((t, self._getTaskNode(t.name)))
407
+ tasks.append((t, nn))
408
+
409
+ # Pop the node stack, since we're done constructing the body
410
+ self._task_node_s.pop()
354
411
 
355
412
  # Fill in 'needs'
356
413
  for t, tn in tasks:
414
+ self._log.debug("Process node %s" % t.name)
357
415
 
358
416
  referenced = None
359
417
  for tt in task.subtasks:
@@ -362,7 +420,9 @@ class TaskGraphBuilder(object):
362
420
  break
363
421
 
364
422
  refs_internal = None
365
- for nn,_ in tn.needs:
423
+ # Assess how this task is connected to others in the compound node
424
+ for nn,_ in tn.first.needs:
425
+ self._log.debug("Need: %s" % nn.name)
366
426
  for _,tnn in tasks:
367
427
  if nn == tnn:
368
428
  refs_internal = tnn
@@ -376,7 +436,7 @@ class TaskGraphBuilder(object):
376
436
  self._log.debug("Node %s doesn't reference any internal node" % t.name)
377
437
  tn.needs.append((node.input, False))
378
438
  else:
379
- self._log.debug("Node references internal node %s" % refs_internal.name)
439
+ self._log.debug("Node %s references internal node %s" % (t.name, refs_internal.name))
380
440
 
381
441
  if referenced is not None:
382
442
  # Add this task as a dependency of the output
@@ -403,34 +463,6 @@ class TaskGraphBuilder(object):
403
463
  node.needs.append((need_n, False))
404
464
  self._log.debug("<-- _gatherNeeds %s" % task_t.name)
405
465
 
406
- def getTaskCtor(self, spec : Union[str,'TaskSpec'], pkg : PackageDef = None) -> 'TaskNodeCtor':
407
- from .task_def import TaskSpec
408
- if type(spec) == str:
409
- spec = TaskSpec(spec)
410
-
411
- self._log.debug("--> getTaskCtor %s" % spec.name)
412
- spec_e = spec.name.split(".")
413
- task_name = spec_e[-1]
414
-
415
- # if len(spec_e) == 1:
416
- # # Just have a task name. Use the current package
417
- # if len(self._pkg_s) == 0:
418
- # raise Exception("No package context for task %s" % spec.name)
419
- # pkg = self._pkg_s[-1]
420
- # else:
421
- # pkg_name = ".".join(spec_e[0:-1])
422
-
423
- # try:
424
- # pkg = self.getPackage(PackageSpec(pkg_name))
425
- # except Exception as e:
426
- # self._log.critical("Failed to find package %s while looking for task %s" % (pkg_name, spec.name))
427
- # raise e
428
-
429
- ctor = pkg.getTaskCtor(task_name)
430
-
431
- self._log.debug("<-- getTaskCtor %s" % spec.name)
432
- return ctor
433
-
434
466
  def error(self, msg, loc=None):
435
467
  if loc is not None:
436
468
  marker = TaskMarker(msg=msg, severity=SeverityE.Error, loc=loc)
@@ -441,217 +473,3 @@ class TaskGraphBuilder(object):
441
473
  def marker(self, marker):
442
474
  self.marker_l(marker)
443
475
 
444
- def _getTaskCtor(self, task : Task) -> TaskNodeCtor:
445
- if task in self._task_ctor_m.keys():
446
- ctor = self._task_ctor_m[task]
447
- else:
448
- ctor = self._mkTaskCtor(task)
449
- self._task_ctor_m[task] = ctor
450
- return ctor
451
-
452
- def _mkTaskCtor(self, task):
453
- srcdir = os.path.dirname(task.srcinfo.file)
454
- self._log.debug("--> mkTaskCtor %s (srcdir: %s)" % (task.name, srcdir))
455
-
456
- if len(task.subtasks) > 0:
457
- self._log.debug("Task has a body")
458
- # Compound task
459
- self._log.debug("Task specifies sub-task implementation")
460
- ctor = self._mkCompoundTaskCtor(task)
461
- else:
462
- self._log.debug("Task doesn't specify a body")
463
- # Shell task or 'null'
464
- ctor = self._mkLeafTaskCtor(task)
465
-
466
- if ctor is None:
467
- raise Exception()
468
-
469
- return ctor
470
-
471
- def _mkLeafTaskCtor(self, task) -> TaskNodeCtor:
472
- self._log.debug("--> _mkLeafTaskCtor")
473
- srcdir = os.path.dirname(task.srcinfo.file)
474
- base_ctor_t : TaskNodeCtor = None
475
- ctor_t : TaskNodeCtor = None
476
- base_params = None
477
- callable = None
478
- # fullname = self.name + "." + task.name
479
- # rundir = task.rundir
480
-
481
- # TODO: should we have the ctor look this up itself?
482
- # Want to confirm that the value can be found.
483
- # Defer final resolution until actual graph building (post-config)
484
- if task.uses is not None:
485
- self._log.debug("Uses: %s" % task.uses.name)
486
-
487
- base_ctor_t = self._getTaskCtor(task.uses)
488
-
489
- if base_ctor_t is None:
490
- self._log.error("Failed to load task ctor %s" % task.uses)
491
- # base_params = base_ctor_t.mkTaskParams()
492
- else:
493
- self._log.debug("No 'uses' specified %s" % task.name)
494
-
495
- self._log.debug("%d needs" % len(task.needs))
496
-
497
- # Determine the implementation constructor first
498
- if task.run is not None:
499
- shell = task.shell if task.shell is not None else "shell"
500
-
501
- if shell in self._shell_m.keys():
502
- self._log.debug("Use shell implementation")
503
- callable = self._shell_m[shell]
504
- else:
505
- self._log.debug("Shell %s not found" % shell)
506
- raise Exception("Shell %s not found" % shell)
507
-
508
- # if taskdef.body.pytask is not None:
509
- # # Built-in impl
510
- # # Now, lookup the class
511
- # self._log.debug("Use PyTask implementation")
512
- # last_dot = taskdef.body.pytask.rfind('.')
513
- # clsname = taskdef.body.pytask[last_dot+1:]
514
- # modname = taskdef.body.pytask[:last_dot]
515
-
516
- # try:
517
- # if modname not in sys.modules:
518
- # if srcdir not in sys.path:
519
- # sys.path.append(srcdir)
520
- # mod = importlib.import_module(modname)
521
- # else:
522
- # mod = sys.modules[modname]
523
- # except ModuleNotFoundError as e:
524
- # raise Exception("Failed to import module %s (_basedir=%s): %s" % (
525
- # modname, self._basedir, str(e)))
526
-
527
- # if not hasattr(mod, clsname):
528
- # raise Exception("Method %s not found in module %s" % (clsname, modname))
529
- # callable = getattr(mod, clsname)
530
- # elif taskdef.body.run is not None:
531
- # callable = self._getRunCallable(taskdef)
532
- else:
533
- # TODO: use null task
534
- pass
535
-
536
- # Determine if we need to use a new
537
- if task.paramT is None:
538
- raise Exception()
539
- paramT = task.paramT
540
- needs = []
541
-
542
- # TODO:
543
- rundir : RundirE = task.rundir
544
-
545
- if callable is not None:
546
- ctor_t = TaskNodeCtorTask(
547
- name=task.name,
548
- srcdir=srcdir,
549
- paramT=task.paramT, # TODO: need to determine the parameter type
550
- passthrough=task.passthrough,
551
- consumes=task.consumes,
552
- needs=needs, # TODO: need to determine the needs
553
- rundir=rundir,
554
- task=callable)
555
- elif base_ctor_t is not None:
556
- # Use the existing (base) to create the implementation
557
- ctor_t = TaskNodeCtorProxy(
558
- name=task.name,
559
- srcdir=srcdir,
560
- paramT=task.paramT, # TODO: need to determine the parameter type
561
- passthrough=task.passthrough,
562
- consumes=task.consumes,
563
- needs=needs,
564
- rundir=rundir,
565
- uses=base_ctor_t)
566
- else:
567
- self._log.debug("Use 'Null' as the class implementation")
568
- ctor_t = TaskNodeCtorTask(
569
- name=task.name,
570
- srcdir=srcdir,
571
- paramT=paramT,
572
- passthrough=task.passthrough,
573
- consumes=task.consumes,
574
- needs=needs,
575
- rundir=rundir,
576
- task=TaskNull)
577
-
578
- self._log.debug("<-- mkTaskCtor %s" % task.name)
579
- return ctor_t
580
-
581
- def _getRunCallable(self, task):
582
- self._log.debug("--> _getRunCallable %s" % task.name)
583
- callable = None
584
- if task.run is not None and task.shell == "python":
585
- # Evaluate a Python script
586
- pass
587
- else:
588
- # run a shell script
589
- shell = None
590
- body = task.run.strip()
591
-
592
- callable = ShellCallable(body=body, shell=shell)
593
- pass
594
- return callable
595
-
596
- def _mkCompoundTaskCtor(self, task) -> TaskNodeCtor:
597
- self._log.debug("--> _mkCompoundTaskCtor %s" % task.name)
598
- srcdir = os.path.dirname(task.srcinfo.file)
599
- base_ctor_t : TaskNodeCtor = None
600
- ctor_t : TaskNodeCtor = None
601
- base_params = None
602
- callable = None
603
-
604
- # fullname = self._getScopeFullname()
605
- fullname = task.name
606
-
607
- if task.uses is not None:
608
- self._log.debug("Uses: %s" % task.uses)
609
- base_ctor_t = task.uses.ctor
610
- base_params = base_ctor_t.mkTaskParams()
611
-
612
- if base_ctor_t is None:
613
- self._log.error("Failed to load task ctor %s" % task.uses)
614
-
615
- # TODO: should build during loading
616
- # passthrough, consumes, needs = self._getPTConsumesNeeds(taskdef, base_ctor_t)
617
- passthrough = []
618
- consumes = []
619
- needs = []
620
-
621
- # Determine if we need to use a new
622
- # paramT = self._getParamT(taskdef, base_params)
623
- paramT = task.paramT
624
-
625
- if base_ctor_t is not None:
626
- ctor_t = TaskNodeCtorCompoundProxy(
627
- name=fullname,
628
- srcdir=srcdir,
629
- paramT=paramT,
630
- passthrough=passthrough,
631
- consumes=consumes,
632
- needs=needs,
633
- task=task,
634
- uses=base_ctor_t)
635
- else:
636
- self._log.debug("No 'uses' specified")
637
- ctor_t = TaskNodeCtorCompound(
638
- name=fullname,
639
- srcdir=srcdir,
640
- paramT=paramT,
641
- passthrough=passthrough,
642
- consumes=consumes,
643
- needs=needs,
644
- task=task)
645
-
646
- for st in task.subtasks:
647
- ctor = self._getTaskCtor(st)
648
- if ctor is None:
649
- raise Exception("ctor for %s is None" % st.name)
650
- ctor_t.tasks.append(st)
651
-
652
- # for t in task.subtasks:
653
- # ctor_t.tasks.append(self._mkTaskCtor(t, srcdir))
654
-
655
-
656
- self._log.debug("<-- mkCompoundTaskCtor %s (%d)" % (task.name, len(ctor_t.tasks)))
657
- 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
- self.process_node(node)
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 process_node(self, node):
32
- self._log.debug("--> process_node %s (%d)" % (node.name, len(node.needs),))
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.println("subgraph cluster_%d {" % self._cluster_id)
40
- self._cluster_id += 1
41
- self.inc_ind()
42
- self.println("label=\"%s\";" % node.name)
43
- self.println("color=blue;")
44
- self.println("style=dashed;")
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
- self.println("%s[label=\"%s\"];" % (
52
- node_name,
53
- node.name))
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
- for dep in node.needs:
56
- if dep[0] not in self._node_id_m.keys():
57
- self.process_node(dep[0])
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[0]],
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
- if isinstance(node, TaskNodeCompound):
63
- self.dec_ind()
64
- self.println("}")
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("<-- process_node %s (%d)" % (node.name, len(node.needs),))
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,
@@ -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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dv-flow-mgr
3
- Version: 1.0.0.14389547107a1
3
+ Version: 1.0.0.14420805951a1
4
4
  Summary: DV Flow Manager is a build system for silicon design
5
5
  Author-email: Matthew Ballance <matt.ballance@gmail.com>
6
6
  License: Apache License
@@ -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=Z80md4ZhJk6TPVlvYAWlnH1aJvATlB9skIyb1pZ1P8U,25075
21
+ dv_flow/mgr/package_loader.py,sha256=xNdN9aNVRDzSeU2YDMEk6rWPfNphQniehg1UdW91w-k,25361
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=CSrrubqfCeBEKh4dMa69RP2Nhw2QZrY6AlNIC0RNe9g,23524
35
- dv_flow/mgr/task_graph_dot_writer.py,sha256=GxqiYwQJbFgUQdnPCS9vpIYmYFbSXwnXCSbGmjbxC3M,2418
34
+ dv_flow/mgr/task_graph_builder.py,sha256=dFyC4L4D6v4dnybfbeIAVhyG-W9l4J3STcU75d3A1bM,17059
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=JXq2QimCZKxfhhkdhM-HKk4JbxpIlpW65xUJ0hVlQc0,4981
38
- dv_flow/mgr/task_node_compound.py,sha256=mNu4nf9hVqu2698ue5fpE3FeAOkvJH0Ke2W9V0G3-As,2975
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=oMossZQ-m0wz3_HxcPdrifFDLKD9XV1m_Cow4Ugp6Uo,8652
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.14389547107a1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
70
- dv_flow_mgr-1.0.0.14389547107a1.dist-info/METADATA,sha256=ybdQvgxa9XlrLujXOVPoOx_byLezEkvmAABXjcPwMlU,13336
71
- dv_flow_mgr-1.0.0.14389547107a1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
72
- dv_flow_mgr-1.0.0.14389547107a1.dist-info/entry_points.txt,sha256=1roy8wAFM48LabOvr6jiOw0MUs-qE8X3Vf8YykPazxk,50
73
- dv_flow_mgr-1.0.0.14389547107a1.dist-info/top_level.txt,sha256=amfVTkggzYPtWwLqNmRukfz1Buu0pGS2SrYBBLhXm04,8
74
- dv_flow_mgr-1.0.0.14389547107a1.dist-info/RECORD,,
69
+ dv_flow_mgr-1.0.0.14420805951a1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
70
+ dv_flow_mgr-1.0.0.14420805951a1.dist-info/METADATA,sha256=6nR-XRqIxfeAWGiU_oChpiwxuot00gCqe8EWvIpotrg,13336
71
+ dv_flow_mgr-1.0.0.14420805951a1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
72
+ dv_flow_mgr-1.0.0.14420805951a1.dist-info/entry_points.txt,sha256=1roy8wAFM48LabOvr6jiOw0MUs-qE8X3Vf8YykPazxk,50
73
+ dv_flow_mgr-1.0.0.14420805951a1.dist-info/top_level.txt,sha256=amfVTkggzYPtWwLqNmRukfz1Buu0pGS2SrYBBLhXm04,8
74
+ dv_flow_mgr-1.0.0.14420805951a1.dist-info/RECORD,,