dv-flow-mgr 0.0.2.14182043984a1__py3-none-any.whl → 1.0.0.14370600369a1__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 (43) hide show
  1. dv_flow/mgr/__init__.py +2 -1
  2. dv_flow/mgr/cmds/cmd_graph.py +2 -3
  3. dv_flow/mgr/cmds/cmd_run.py +7 -9
  4. dv_flow/mgr/cmds/cmd_show.py +1 -2
  5. dv_flow/mgr/cond_def.py +16 -0
  6. dv_flow/mgr/config.py +7 -0
  7. dv_flow/mgr/config_def.py +33 -0
  8. dv_flow/mgr/exec_callable.py +88 -0
  9. dv_flow/mgr/{pkg_rgy.py → ext_rgy.py} +44 -35
  10. dv_flow/mgr/extend_def.py +21 -0
  11. dv_flow/mgr/fragment_def.py +4 -3
  12. dv_flow/mgr/need_def.py +6 -0
  13. dv_flow/mgr/null_callable.py +10 -0
  14. dv_flow/mgr/package.py +30 -6
  15. dv_flow/mgr/package_def.py +40 -444
  16. dv_flow/mgr/package_loader.py +701 -0
  17. dv_flow/mgr/param_def.py +2 -1
  18. dv_flow/mgr/parser.out +567 -0
  19. dv_flow/mgr/pytask_callable.py +25 -0
  20. dv_flow/mgr/root_package.py +9 -0
  21. dv_flow/mgr/shell_callable.py +14 -0
  22. dv_flow/mgr/srcinfo.py +15 -0
  23. dv_flow/mgr/std/flow.dv +25 -4
  24. dv_flow/mgr/task.py +68 -0
  25. dv_flow/mgr/task_def.py +36 -24
  26. dv_flow/mgr/task_graph_builder.py +497 -247
  27. dv_flow/mgr/task_listener_log.py +4 -0
  28. dv_flow/mgr/task_node_ctor.py +11 -3
  29. dv_flow/mgr/task_node_ctor_compound.py +21 -33
  30. dv_flow/mgr/task_node_leaf.py +25 -3
  31. dv_flow/mgr/task_params_ctor.py +0 -1
  32. dv_flow/mgr/task_run_ctxt.py +4 -0
  33. dv_flow/mgr/task_runner.py +2 -0
  34. dv_flow/mgr/util/cmds/cmd_schema.py +0 -2
  35. dv_flow/mgr/util/util.py +4 -3
  36. dv_flow/mgr/yaml_srcinfo_loader.py +55 -0
  37. {dv_flow_mgr-0.0.2.14182043984a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/METADATA +1 -1
  38. dv_flow_mgr-1.0.0.14370600369a1.dist-info/RECORD +74 -0
  39. dv_flow_mgr-0.0.2.14182043984a1.dist-info/RECORD +0 -59
  40. {dv_flow_mgr-0.0.2.14182043984a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/WHEEL +0 -0
  41. {dv_flow_mgr-0.0.2.14182043984a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/entry_points.txt +0 -0
  42. {dv_flow_mgr-0.0.2.14182043984a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/licenses/LICENSE +0 -0
  43. {dv_flow_mgr-0.0.2.14182043984a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/top_level.txt +0 -0
@@ -22,14 +22,26 @@
22
22
  import os
23
23
  import dataclasses as dc
24
24
  import logging
25
- from typing import Callable
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 .pkg_rgy import PkgRgy
28
+ from .ext_rgy import ExtRgy
29
+ from .task import Task
29
30
  from .task_def import RundirE
31
+ from .task_data import TaskMarker, TaskMarkerLoc, SeverityE
30
32
  from .task_node import TaskNode
31
33
  from .task_node_ctor import TaskNodeCtor
32
- from typing import Dict, List, Union
34
+ from .task_node_ctor_compound import TaskNodeCtorCompound
35
+ from .task_node_ctor_compound_proxy import TaskNodeCtorCompoundProxy
36
+ from .task_node_ctor_proxy import TaskNodeCtorProxy
37
+ from .task_node_ctor_task import TaskNodeCtorTask
38
+ from .task_node_ctor_wrapper import TaskNodeCtorWrapper
39
+ from .task_node_compound import TaskNodeCompound
40
+ from .task_node_leaf import TaskNodeLeaf
41
+ from .std.task_null import TaskNull
42
+ from .exec_callable import ExecCallable
43
+ from .null_callable import NullCallable
44
+ from .shell_callable import ShellCallable
33
45
 
34
46
  @dc.dataclass
35
47
  class TaskNamespaceScope(object):
@@ -46,85 +58,68 @@ class CompoundTaskCtxt(object):
46
58
  @dc.dataclass
47
59
  class TaskGraphBuilder(object):
48
60
  """The Task-Graph Builder knows how to discover packages and construct task graphs"""
49
- root_pkg : PackageDef
61
+ root_pkg : Package
50
62
  rundir : str
51
- pkg_rgy : PkgRgy = None
52
63
  marker_l : Callable = lambda *args, **kwargs: None
53
- _pkg_s : List[Package] = dc.field(default_factory=list)
54
64
  _pkg_m : Dict[PackageSpec,Package] = dc.field(default_factory=dict)
55
65
  _pkg_spec_s : List[PackageDef] = dc.field(default_factory=list)
56
- _task_m : Dict['TaskSpec',TaskNode] = dc.field(default_factory=dict)
66
+ _shell_m : Dict[str,Callable] = dc.field(default_factory=dict)
67
+ _task_m : Dict[str,Task] = dc.field(default_factory=dict)
68
+ _task_node_m : Dict['TaskSpec',TaskNode] = dc.field(default_factory=dict)
69
+ _task_ctor_m : Dict[Task,TaskNodeCtor] = dc.field(default_factory=dict)
57
70
  _override_m : Dict[str,str] = dc.field(default_factory=dict)
58
71
  _ns_scope_s : List[TaskNamespaceScope] = dc.field(default_factory=list)
59
72
  _compound_task_ctxt_s : List[CompoundTaskCtxt] = dc.field(default_factory=list)
60
- _rundir_s : List[str] = dc.field(default_factory=list)
73
+ _task_rundir_s : List[List[str]] = dc.field(default_factory=list)
61
74
  _uses_count : int = 0
62
75
 
63
- _logger : logging.Logger = None
76
+ _log : logging.Logger = None
64
77
 
65
78
  def __post_init__(self):
66
- if self.pkg_rgy is None:
67
- self.pkg_rgy = PkgRgy.inst().copy()
68
-
69
79
  # Initialize the overrides from the global registry
70
- self._override_m.update(self.pkg_rgy.getOverrides())
71
-
72
- self._logger = logging.getLogger(type(self).__name__)
80
+ self._log = logging.getLogger(type(self).__name__)
81
+ self._shell_m.update(ExtRgy._inst._shell_m)
82
+ self._task_rundir_s.append([])
73
83
 
74
84
  if self.root_pkg is not None:
75
- self._logger.debug("TaskGraphBuilder: root_pkg: %s" % str(self.root_pkg))
76
-
77
- # Register package definitions found during loading
78
- visited = set()
79
- self._registerPackages(self.root_pkg, visited)
80
-
81
- self._pkg_spec_s.append(self.root_pkg)
82
- pkg = self.root_pkg.mkPackage(self)
83
- self._pkg_spec_s.pop()
84
-
85
- # Allows us to find ourselves
86
- self._pkg_m[PackageSpec(self.root_pkg.name)] = pkg
87
-
88
- def loadPkg(self, pkgfile : str):
89
- pkg = PackageDef.load(pkgfile)
90
- visited = set()
91
- self._registerPackages(pkg, visited)
85
+ # Collect all the tasks
86
+ pkg_s = set()
87
+ self._addPackageTasks(self.root_pkg, pkg_s)
88
+
89
+ def _addPackageTasks(self, pkg, pkg_s):
90
+ if pkg not in pkg_s:
91
+ pkg_s.add(pkg)
92
+ for task in pkg.task_m.values():
93
+ self._addTask(task)
94
+ for subpkg in pkg.pkg_m.values():
95
+ self._addPackageTasks(subpkg, pkg_s)
96
+
97
+ def _addTask(self, task):
98
+ if task.name not in self._task_m.keys():
99
+ self._task_m[task.name] = task
100
+ for st in task.subtasks:
101
+ self._addTask(st)
92
102
 
93
103
  def addOverride(self, key : str, val : str):
94
104
  self._override_m[key] = val
95
105
 
96
- def _registerPackages(self, pkg : PackageDef, visited):
97
- self._logger.debug("Packages: %s" % str(pkg))
98
- if pkg.name not in visited:
99
- visited.add(pkg.name)
100
- self._logger.debug("Registering package %s" % pkg.name)
101
- self.pkg_rgy.registerPackage(pkg)
102
- for subpkg in pkg.subpkg_m.values():
103
- self._registerPackages(subpkg, visited)
104
-
105
-
106
- def push_package(self, pkg : Package, add=False):
107
- self._pkg_s.append(pkg)
108
- if add:
109
- self._pkg_m[PackageSpec(pkg.name, pkg.params)] = pkg
110
-
111
- def pop_package(self, pkg : Package):
112
- self._pkg_s.pop()
113
-
114
- def package(self):
115
- return self._pkg_s[-1]
106
+ def enter_package(self, pkg : PackageDef):
107
+ pass
116
108
 
117
109
  def enter_rundir(self, rundir : str):
118
- self._rundir_s.append(rundir)
110
+ self._log.debug("enter_rundir: %s (%d)" % (rundir, len(self._task_rundir_s[-1])))
111
+ self._task_rundir_s[-1].append(rundir)
119
112
 
120
113
  def get_rundir(self, rundir=None):
121
- ret = self._rundir_s.copy()
114
+ ret = self._task_rundir_s[-1].copy()
122
115
  if rundir is not None:
123
116
  ret.append(rundir)
117
+ self._log.debug("get_rundir: %s" % str(ret))
124
118
  return ret
125
119
 
126
120
  def leave_rundir(self):
127
- self._rundir_s.pop()
121
+ self._log.debug("leave_rundir")
122
+ self._task_rundir_s[-1].pop()
128
123
 
129
124
  def enter_uses(self):
130
125
  self._uses_count += 1
@@ -142,15 +137,6 @@ class TaskGraphBuilder(object):
142
137
  if rundir is None or rundir == RundirE.Unique:
143
138
  self._rundir_s.append(task.name)
144
139
 
145
- def get_name_prefix(self):
146
- if len(self._compound_task_ctxt_s) > 0:
147
- # Use the compound scope name
148
- name = ".".join(c.task.name for c in self._compound_task_ctxt_s)
149
- else:
150
- name = self._pkg_s[-1].name
151
-
152
- return name
153
-
154
140
  def enter_compound_uses(self):
155
141
  self._compound_task_ctxt_s[-1].uses_s.append({})
156
142
 
@@ -159,7 +145,7 @@ class TaskGraphBuilder(object):
159
145
  # Propagate the items up the stack, appending 'super' to
160
146
  # the names
161
147
  for k,v in self._compound_task_ctxt_s[-1].uses_s[-1].items():
162
- self._compound_task_ctxt_s[-1].uses[-2]["super.%s" % k] = v
148
+ self._compound_task_ctxt_s[-1].uses_s[-2]["super.%s" % k] = v
163
149
  else:
164
150
  # Propagate the items to the compound namespace, appending
165
151
  # 'super' to the names
@@ -171,18 +157,18 @@ class TaskGraphBuilder(object):
171
157
  return len(self._compound_task_ctxt_s) > 0 and len(self._compound_task_ctxt_s[-1].uses_s) != 0
172
158
 
173
159
  def addTask(self, name, task : TaskNode):
174
- self._logger.debug("--> addTask: %s" % name)
160
+ self._log.debug("--> addTask: %s" % name)
175
161
 
176
162
  if len(self._compound_task_ctxt_s) == 0:
177
- self._task_m[name] = task
163
+ self._task_node_m[name] = task
178
164
  else:
179
165
  if len(self._compound_task_ctxt_s[-1].uses_s) > 0:
180
166
  self._compound_task_ctxt_s[-1].uses_s[-1][name] = task
181
167
  else:
182
168
  self._compound_task_ctxt_s[-1].task_m[name] = task
183
- self._logger.debug("<-- addTask: %s" % name)
169
+ self._log.debug("<-- addTask: %s" % name)
184
170
 
185
- def findTask(self, name):
171
+ def findTask(self, name, create=True):
186
172
  task = None
187
173
 
188
174
  if len(self._compound_task_ctxt_s) > 0:
@@ -191,15 +177,22 @@ class TaskGraphBuilder(object):
191
177
  task = self._compound_task_ctxt_s[-1].uses_s[-1][name]
192
178
  if task is None and name in self._compound_task_ctxt_s[-1].task_m.keys():
193
179
  task = self._compound_task_ctxt_s[-1].task_m[name]
194
- if task is None and name in self._task_m.keys():
195
- task = self._task_m[name]
180
+ if task is None and name in self._task_node_m.keys():
181
+ task = self._task_node_m[name]
196
182
 
197
- if task is None:
183
+ if task is None and create:
184
+ if name in self.root_pkg.task_m.keys():
185
+ task = self.mkTaskGraph(name)
186
+ self._log.debug("Found task %s in root package" % name)
187
+ else:
188
+ raise Exception("Failed to find task %s" % name)
189
+ pass
198
190
  # Go search type definitions
191
+ pass
199
192
 
200
193
  # Check the current package
201
- if len(self._pkg_s) > 0 and name in self._pkg_s[-1].task_m.keys():
202
- task = self._pkg_s[-1].task_m[name]
194
+ # if len(self._pkg_s) > 0 and name in self._pkg_s[-1].task_m.keys():
195
+ # task = self._pkg_s[-1].task_m[name]
203
196
 
204
197
  return task
205
198
 
@@ -209,226 +202,483 @@ class TaskGraphBuilder(object):
209
202
  self._rundir_s.pop()
210
203
 
211
204
  def mkTaskGraph(self, task : str, rundir=None) -> TaskNode:
212
- self._pkg_s.clear()
213
- self._task_m.clear()
205
+ return self.mkTaskNode(task, rundir=rundir)
206
+ # self._task_node_m.clear()
214
207
 
215
- if rundir is not None:
216
- self._rundir_s.append(rundir)
208
+ # if rundir is not None:
209
+ # self._rundir_s.append(rundir)
217
210
 
218
- ret = self._mkTaskGraph(task)
211
+ # ret = self._mkTaskGraph(task)
219
212
 
220
- if rundir is not None:
221
- self._rundir_s.pop()
213
+ # if rundir is not None:
214
+ # self._rundir_s.pop()
222
215
 
223
- return ret
216
+ # return ret
224
217
 
225
218
  def _mkTaskGraph(self, task : str) -> TaskNode:
226
-
227
- elems = task.split(".")
228
-
229
- pkg_name = ".".join(elems[0:-1])
230
- task_name = elems[-1]
231
-
232
- if pkg_name == "":
233
- if len(self._pkg_spec_s) == 0:
234
- raise Exception("No package context for %s" % task)
235
- pkg_spec = self._pkg_spec_s[-1]
236
- pkg_name = pkg_spec.name
219
+ if task in self.root_pkg.task_m.keys():
220
+ task_t = self.root_pkg.task_m[task]
237
221
  else:
238
- pkg_spec = PackageSpec(pkg_name)
222
+ pass
239
223
 
240
- self._logger.debug("pkg_spec: %s" % str(pkg_spec))
241
- self._pkg_spec_s.append(pkg_spec)
242
- pkg = self.getPackage(pkg_spec)
243
-
244
- self._pkg_s.append(pkg)
224
+ if task_t is None:
225
+ raise Exception("Failed to find task %s" % task)
245
226
 
246
- ctor_t : TaskNodeCtor = pkg.getTaskCtor(task_name)
227
+ ctor = self._getTaskCtor(task_t)
247
228
 
248
- self._logger.debug("ctor_t: %s" % ctor_t.name)
229
+ params = ctor.mkTaskParams()
249
230
 
250
231
  needs = []
251
232
 
252
- for need_def in ctor_t.getNeeds():
253
- # Resolve the full name of the need
254
- need_fullname = self._resolveNeedRef(need_def)
255
- self._logger.debug("Searching for qualifed-name task %s" % need_fullname)
256
- if not need_fullname in self._task_m.keys():
257
- # Go back to the root from a rundir perspective
258
- rundir_s = self._rundir_s
259
- self._rundir_s = [need_fullname]
260
- need_t = self._mkTaskGraph(need_fullname)
261
- self._rundir_s = rundir_s
262
- self._task_m[need_fullname] = need_t
263
- needs.append(self._task_m[need_fullname])
264
-
265
- # The returned task should have all param references resolved
266
- params = ctor_t.mkTaskParams()
267
-
268
- if params is None:
269
- raise Exception("ctor %s returned None for params" % str(ctor_t))
270
-
271
- task = ctor_t.mkTaskNode(
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(
272
240
  builder=self,
273
241
  params=params,
274
242
  name=task,
275
243
  needs=needs)
244
+ task.rundir = self.get_rundir(task.name)
276
245
  # task.rundir = rundir
277
246
 
278
- self._task_m[task.name] = task
279
-
280
- self._pkg_s.pop()
281
- self._pkg_spec_s.pop()
247
+ self._task_node_m[task.name] = task
282
248
 
283
249
  return task
284
250
 
285
- def findTaskDef(self, name):
286
- pass
251
+ def mkTaskNode(self, task_t, name=None, srcdir=None, needs=None, **kwargs):
252
+ self._log.debug("--> mkTaskNode: %s" % task_t)
287
253
 
288
- def _resolveNeedRef(self, need_def) -> str:
289
- if need_def.find(".") == -1:
290
- # Need is a local task. Prefix to avoid ambiguity
291
- return self._pkg_s[-1].name + "." + need_def
254
+ if task_t in self._task_m.keys():
255
+ task = self._task_m[task_t]
292
256
  else:
293
- return need_def
294
-
295
- def getPackage(self, spec : PackageSpec) -> Package:
296
- # Obtain the active package definition
297
- self._logger.debug("--> getPackage: %s len: %d" % (spec.name, len(self._pkg_spec_s)))
298
- if len(self._pkg_spec_s) > 0:
299
- pkg_spec = self._pkg_spec_s[-1]
300
- if self.root_pkg is not None and self.root_pkg.name == pkg_spec.name:
301
- pkg_def = self.root_pkg
257
+ raise Exception("task_t (%s) not present" % str(task_t))
258
+
259
+ ret = self._mkTaskNode(task)
260
+
261
+ if needs is not None:
262
+ for need in needs:
263
+ ret.needs.append((need, False))
264
+
265
+ for k,v in kwargs.items():
266
+ if hasattr(ret.params, k):
267
+ setattr(ret.params, k, v)
302
268
  else:
303
- pkg_def = self.pkg_rgy.getPackage(pkg_spec.name)
269
+ raise Exception("Task %s parameters do not include %s" % (task.name, k))
270
+
271
+ self._log.debug("<-- mkTaskNode: %s" % task_t)
272
+ return ret
273
+
274
+ def _findTask(self, pkg, name):
275
+ task = None
276
+ if name in pkg.task_m.keys():
277
+ task = pkg.task_m[name]
304
278
  else:
305
- pkg_def = None
306
-
307
- # Need a stack to track which package we are currently in
308
- # Need a map to get a concrete package from a name with parameterization
309
-
310
- self._logger.debug("pkg_s: %d %s" % (
311
- len(self._pkg_s), (self._pkg_s[-1].name if len(self._pkg_s) else "<unknown>")))
312
-
313
- # First, check the active pkg_def to see if any aliases
314
- # Should be considered
315
- pkg_name = spec.name
316
- if pkg_def is not None:
317
- # Look for an import alias
318
- self._logger.debug("Search package %s for import alias %s" % (
319
- pkg_def.name, pkg_spec.name))
320
- for imp in pkg_def.imports:
321
- if type(imp) != str:
322
- self._logger.debug("imp: %s" % str(imp))
323
- if imp.alias is not None and imp.alias == spec.name:
324
- # Found the alias name. Just need to get an instance of this package
325
- self._logger.debug("Found alias %s -> %s" % (imp.alias, imp.name))
326
- pkg_name = imp.name
327
- break
279
+ for subpkg in pkg.pkg_m.values():
280
+ task = self._findTask(subpkg, name)
281
+ if task is not None:
282
+ break
283
+ return task
284
+
285
+ def _mkTaskNode(self, task : Task, hierarchical=False):
286
+
287
+ if not hierarchical:
288
+ self._task_rundir_s.append([])
328
289
 
329
- # Note: _pkg_m needs to be context specific, such that imports from
330
- # one package don't end up visible in another
331
- spec.name = pkg_name
332
-
333
- if spec in self._pkg_m.keys():
334
- self._logger.debug("Found cached package instance")
335
- pkg = self._pkg_m[spec]
336
- elif self.pkg_rgy.hasPackage(spec.name):
337
- self._logger.debug("Registry has a definition")
338
- p_def = self.pkg_rgy.getPackage(spec.name)
339
-
340
- self._pkg_spec_s.append(p_def)
341
- pkg = p_def.mkPackage(self)
342
- self._pkg_spec_s.pop()
343
- self._pkg_m[spec] = pkg
290
+ # Determine how to build this node
291
+ if task.subtasks is not None and len(task.subtasks):
292
+ ret = self._mkTaskCompoundNode(task)
344
293
  else:
345
- self.error("Failed to find package %s" % spec.name)
346
- raise Exception("Failed to find definition of package %s" % spec.name)
294
+ ret = self._mkTaskLeafNode(task)
347
295
 
348
- self._logger.debug("<-- getPackage: %s" % str(pkg))
296
+ if not hierarchical:
297
+ self._task_rundir_s.pop()
349
298
 
350
- return pkg
299
+ return ret
351
300
 
352
- def mkTaskNode(self, task_t, name=None, srcdir=None, needs=None, **kwargs):
353
- self._logger.debug("--> mkTaskNode: %s" % task_t)
354
-
355
- if task_t in self._override_m.keys():
356
- self._logger.debug("Overriding task %s with %s" % (task_t, self._override_m[task_t]))
357
- task_t = self._override_m[task_t]
301
+ def _getTaskNode(self, name):
302
+ if name in self._task_node_m.keys():
303
+ return self._task_node_m[name]
358
304
  else:
359
- dot_idx = task_t.rfind(".")
360
- if dot_idx != -1:
361
- pkg = task_t[0:dot_idx]
362
- tname = task_t[dot_idx+1:]
363
-
364
- if pkg in self._override_m.keys():
365
- self._logger.debug("Overriding package %s with %s" % (pkg, self._override_m[pkg]))
366
- task_t = self._override_m[pkg] + "." + tname
367
-
368
- dot_idx = task_t.rfind(".")
369
- pkg = task_t[0:dot_idx]
370
- self._pkg_s.append(self.getPackage(PackageSpec(pkg)))
371
-
372
- ctor = self.getTaskCtor(task_t)
373
- if ctor is not None:
374
- if needs is None:
375
- needs = []
376
- for need_def in ctor.getNeeds():
377
- # Resolve the full name of the need
378
- need_fullname = self._resolveNeedRef(need_def)
379
- self._logger.debug("Searching for qualifed-name task %s" % need_fullname)
380
- if not need_fullname in self._task_m.keys():
381
- rundir_s = self._rundir_s
382
- self._rundir_s = [need_fullname]
383
- need_t = self._mkTaskGraph(need_fullname)
384
- self._rundir_s = rundir_s
385
- self._task_m[need_fullname] = need_t
386
- needs.append(self._task_m[need_fullname])
387
-
388
- self._logger.debug("ctor: %s" % ctor.name)
389
- params = ctor.mkTaskParams(kwargs)
390
- ret = ctor.mkTaskNode(
391
- self,
392
- params=params,
393
- name=name,
394
- srcdir=srcdir,
395
- needs=needs)
396
- ret.rundir = self.get_rundir(name)
305
+ return self.mkTaskNode(name)
306
+
307
+ def _mkTaskLeafNode(self, task : Task, name=None) -> TaskNode:
308
+ self._log.debug("--> _mkTaskLeafNode %s" % task.name)
309
+ srcdir = os.path.dirname(task.srcinfo.file)
310
+
311
+ if task.rundir == RundirE.Unique:
312
+ self.enter_rundir(task.name)
313
+
314
+ if name is None:
315
+ name = task.name
316
+
317
+ callable = None
318
+ if task.run is not None:
319
+ shell = task.shell if task.shell is not None else "shell"
320
+ if shell in self._shell_m.keys():
321
+ self._log.debug("Use shell implementation")
322
+ callable = self._shell_m[shell]
323
+ else:
324
+ raise Exception("Shell %s not found" % shell)
397
325
  else:
398
- raise Exception("Failed to find ctor for task %s" % task_t)
399
- self._pkg_s.pop()
400
- self._logger.debug("<-- mkTaskNode: %s" % task_t)
401
- return ret
326
+ callable = NullCallable
327
+
328
+ node = TaskNodeLeaf(
329
+ name=name,
330
+ srcdir=srcdir,
331
+ params=task.paramT(),
332
+ passthrough=task.passthrough,
333
+ consumes=task.consumes,
334
+ task=callable(task.run))
335
+ self._task_node_m[name] = node
336
+ node.rundir = self.get_rundir()
337
+
338
+ # Now, link up the needs
339
+ self._log.debug("--> processing needs")
340
+ for n in task.needs:
341
+ self._log.debug("-- need %s" % n.name)
342
+ nn = self._getTaskNode(n.name)
343
+ node.needs.append((nn, False))
344
+ self._log.debug("<-- processing needs")
345
+
346
+ if task.rundir == RundirE.Unique:
347
+ self.leave_rundir()
348
+
349
+ self._log.debug("<-- _mkTaskLeafNode %s" % task.name)
350
+ return node
351
+
352
+ def _mkTaskCompoundNode(self, task : Task, name=None) -> TaskNode:
353
+ self._log.debug("--> _mkTaskCompoundNode %s" % task.name)
354
+ srcdir = os.path.dirname(task.srcinfo.file)
355
+
356
+ if name is None:
357
+ name = task.name
358
+
359
+ if task.rundir == RundirE.Unique:
360
+ self.enter_rundir(task.name)
361
+
362
+ # Node represents the terminal node of the sub-DAG
363
+ node = TaskNodeCompound(
364
+ name=name,
365
+ srcdir=srcdir,
366
+ params=task.paramT()
367
+ )
368
+ self._task_node_m[name] = node
369
+
370
+ node.rundir = self.get_rundir()
371
+
372
+ # Put the input node inside the compound task's rundir
373
+ self.enter_rundir(task.name + ".in")
374
+ node.input.rundir = self.get_rundir()
375
+ self.leave_rundir()
376
+
377
+ self._log.debug("--> processing needs")
378
+ for need in task.needs:
379
+ self._log.debug("-- need: %s" % need.name)
380
+ nn = self._getTaskNode(need.name)
381
+ node.input.needs.append((nn, False))
382
+ self._log.debug("<-- processing needs")
383
+
384
+ # TODO: handle strategy
385
+
386
+ # Need a local map of name -> task
387
+ # For now, build out local tasks and link up the needs
388
+ tasks = []
389
+ for t in task.subtasks:
390
+ nn = self._mkTaskNode(t, True)
391
+ tasks.append((t, self._getTaskNode(t.name)))
392
+
393
+ # Fill in 'needs'
394
+ for t, tn in tasks:
395
+
396
+ referenced = None
397
+ for tt in task.subtasks:
398
+ if tt in t.needs:
399
+ referenced = tt
400
+ break
401
+
402
+ refs_internal = None
403
+ for nn,_ in tn.needs:
404
+ for _,tnn in tasks:
405
+ if nn == tnn:
406
+ refs_internal = tnn
407
+ break
408
+ if refs_internal is not None:
409
+ break
410
+
411
+ if not refs_internal:
412
+ # Any node that doesn't depend on an internal
413
+ # task is a top-level task
414
+ self._log.debug("Node %s doesn't reference any internal node" % t.name)
415
+ tn.needs.append((node.input, False))
416
+ else:
417
+ self._log.debug("Node references internal node %s" % refs_internal.name)
418
+
419
+ if referenced is not None:
420
+ # Add this task as a dependency of the output
421
+ # node (the root one)
422
+ self._log.debug("Add node %s as a top-level dependency" % tn.name)
423
+ node.needs.append((tn, False))
424
+ else:
425
+ self._log.debug("Node %s has internal needs" % tn.name)
426
+
427
+ if task.rundir == RundirE.Unique:
428
+ self.leave_rundir()
429
+
430
+ return node
431
+
402
432
 
403
433
  def getTaskCtor(self, spec : Union[str,'TaskSpec'], pkg : PackageDef = None) -> 'TaskNodeCtor':
404
434
  from .task_def import TaskSpec
405
435
  if type(spec) == str:
406
436
  spec = TaskSpec(spec)
407
437
 
408
- self._logger.debug("--> getTaskCtor %s" % spec.name)
438
+ self._log.debug("--> getTaskCtor %s" % spec.name)
409
439
  spec_e = spec.name.split(".")
410
440
  task_name = spec_e[-1]
411
441
 
412
- if len(spec_e) == 1:
413
- # Just have a task name. Use the current package
414
- if len(self._pkg_s) == 0:
415
- raise Exception("No package context for task %s" % spec.name)
416
- pkg = self._pkg_s[-1]
417
- else:
418
- pkg_name = ".".join(spec_e[0:-1])
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])
419
449
 
420
- try:
421
- pkg = self.getPackage(PackageSpec(pkg_name))
422
- except Exception as e:
423
- self._logger.critical("Failed to find package %s while looking for task %s" % (pkg_name, spec.name))
424
- raise e
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
425
455
 
426
456
  ctor = pkg.getTaskCtor(task_name)
427
457
 
428
- self._logger.debug("--> getTaskCtor %s" % spec.name)
458
+ self._log.debug("<-- getTaskCtor %s" % spec.name)
429
459
  return ctor
430
460
 
431
- def error(self, msg):
432
- pass
461
+ def error(self, msg, loc=None):
462
+ if loc is not None:
463
+ marker = TaskMarker(msg=msg, severity=SeverityE.Error, loc=loc)
464
+ else:
465
+ marker = TaskMarker(msg=msg, severity=SeverityE.Error)
466
+ self.marker(marker)
467
+
468
+ def marker(self, marker):
469
+ self.marker_l(marker)
470
+
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
433
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 = []
434
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