dv-flow-mgr 0.0.2.14180132375a1__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.
- dv_flow/mgr/__init__.py +2 -1
- dv_flow/mgr/cmds/cmd_graph.py +2 -3
- dv_flow/mgr/cmds/cmd_run.py +7 -9
- dv_flow/mgr/cmds/cmd_show.py +1 -2
- dv_flow/mgr/cond_def.py +16 -0
- dv_flow/mgr/config.py +7 -0
- dv_flow/mgr/config_def.py +33 -0
- dv_flow/mgr/exec_callable.py +88 -0
- dv_flow/mgr/{pkg_rgy.py → ext_rgy.py} +44 -35
- dv_flow/mgr/extend_def.py +21 -0
- dv_flow/mgr/fragment_def.py +4 -3
- dv_flow/mgr/need_def.py +6 -0
- dv_flow/mgr/null_callable.py +10 -0
- dv_flow/mgr/package.py +30 -6
- dv_flow/mgr/package_def.py +40 -441
- dv_flow/mgr/package_loader.py +701 -0
- dv_flow/mgr/param_def.py +2 -1
- dv_flow/mgr/parser.out +567 -0
- dv_flow/mgr/pytask_callable.py +25 -0
- dv_flow/mgr/root_package.py +9 -0
- dv_flow/mgr/shell_callable.py +14 -0
- dv_flow/mgr/srcinfo.py +15 -0
- dv_flow/mgr/std/flow.dv +25 -4
- dv_flow/mgr/task.py +68 -0
- dv_flow/mgr/task_def.py +36 -24
- dv_flow/mgr/task_graph_builder.py +503 -240
- dv_flow/mgr/task_listener_log.py +4 -0
- dv_flow/mgr/task_node_ctor.py +11 -3
- dv_flow/mgr/task_node_ctor_compound.py +30 -34
- dv_flow/mgr/task_node_leaf.py +25 -3
- dv_flow/mgr/task_params_ctor.py +0 -1
- dv_flow/mgr/task_run_ctxt.py +4 -0
- dv_flow/mgr/task_runner.py +2 -0
- dv_flow/mgr/util/cmds/cmd_schema.py +0 -2
- dv_flow/mgr/util/util.py +4 -3
- dv_flow/mgr/yaml_srcinfo_loader.py +55 -0
- {dv_flow_mgr-0.0.2.14180132375a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/METADATA +1 -1
- dv_flow_mgr-1.0.0.14370600369a1.dist-info/RECORD +74 -0
- dv_flow_mgr-0.0.2.14180132375a1.dist-info/RECORD +0 -59
- {dv_flow_mgr-0.0.2.14180132375a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/WHEEL +0 -0
- {dv_flow_mgr-0.0.2.14180132375a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/entry_points.txt +0 -0
- {dv_flow_mgr-0.0.2.14180132375a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/licenses/LICENSE +0 -0
- {dv_flow_mgr-0.0.2.14180132375a1.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 .
|
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
|
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 :
|
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
|
-
|
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
|
-
|
73
|
+
_task_rundir_s : List[List[str]] = dc.field(default_factory=list)
|
61
74
|
_uses_count : int = 0
|
62
75
|
|
63
|
-
|
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.
|
71
|
-
|
72
|
-
self.
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
97
|
-
|
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.
|
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.
|
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.
|
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].
|
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.
|
160
|
+
self._log.debug("--> addTask: %s" % name)
|
175
161
|
|
176
162
|
if len(self._compound_task_ctxt_s) == 0:
|
177
|
-
self.
|
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.
|
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,8 +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.
|
195
|
-
task = self.
|
180
|
+
if task is None and name in self._task_node_m.keys():
|
181
|
+
task = self._task_node_m[name]
|
182
|
+
|
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
|
190
|
+
# Go search type definitions
|
191
|
+
pass
|
192
|
+
|
193
|
+
# Check the current package
|
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]
|
196
196
|
|
197
197
|
return task
|
198
198
|
|
@@ -202,220 +202,483 @@ class TaskGraphBuilder(object):
|
|
202
202
|
self._rundir_s.pop()
|
203
203
|
|
204
204
|
def mkTaskGraph(self, task : str, rundir=None) -> TaskNode:
|
205
|
-
self.
|
206
|
-
self.
|
205
|
+
return self.mkTaskNode(task, rundir=rundir)
|
206
|
+
# self._task_node_m.clear()
|
207
207
|
|
208
|
-
if rundir is not None:
|
209
|
-
|
208
|
+
# if rundir is not None:
|
209
|
+
# self._rundir_s.append(rundir)
|
210
210
|
|
211
|
-
ret = self._mkTaskGraph(task)
|
211
|
+
# ret = self._mkTaskGraph(task)
|
212
212
|
|
213
|
-
if rundir is not None:
|
214
|
-
|
213
|
+
# if rundir is not None:
|
214
|
+
# self._rundir_s.pop()
|
215
215
|
|
216
|
-
return ret
|
216
|
+
# return ret
|
217
217
|
|
218
218
|
def _mkTaskGraph(self, task : str) -> TaskNode:
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
pkg_name = ".".join(elems[0:-1])
|
223
|
-
task_name = elems[-1]
|
224
|
-
|
225
|
-
if pkg_name == "":
|
226
|
-
if len(self._pkg_spec_s) == 0:
|
227
|
-
raise Exception("No package context for %s" % task)
|
228
|
-
pkg_spec = self._pkg_spec_s[-1]
|
229
|
-
pkg_name = pkg_spec.name
|
219
|
+
if task in self.root_pkg.task_m.keys():
|
220
|
+
task_t = self.root_pkg.task_m[task]
|
230
221
|
else:
|
231
|
-
|
222
|
+
pass
|
232
223
|
|
233
|
-
|
234
|
-
|
235
|
-
pkg = self.getPackage(pkg_spec)
|
236
|
-
|
237
|
-
self._pkg_s.append(pkg)
|
224
|
+
if task_t is None:
|
225
|
+
raise Exception("Failed to find task %s" % task)
|
238
226
|
|
239
|
-
|
227
|
+
ctor = self._getTaskCtor(task_t)
|
240
228
|
|
241
|
-
|
229
|
+
params = ctor.mkTaskParams()
|
242
230
|
|
243
231
|
needs = []
|
244
232
|
|
245
|
-
for
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
self._rundir_s = [need_fullname]
|
253
|
-
need_t = self._mkTaskGraph(need_fullname)
|
254
|
-
self._rundir_s = rundir_s
|
255
|
-
self._task_m[need_fullname] = need_t
|
256
|
-
needs.append(self._task_m[need_fullname])
|
257
|
-
|
258
|
-
# The returned task should have all param references resolved
|
259
|
-
params = ctor_t.mkTaskParams()
|
260
|
-
|
261
|
-
if params is None:
|
262
|
-
raise Exception("ctor %s returned None for params" % str(ctor_t))
|
263
|
-
|
264
|
-
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(
|
265
240
|
builder=self,
|
266
241
|
params=params,
|
267
242
|
name=task,
|
268
243
|
needs=needs)
|
244
|
+
task.rundir = self.get_rundir(task.name)
|
269
245
|
# task.rundir = rundir
|
270
246
|
|
271
|
-
self.
|
272
|
-
|
273
|
-
self._pkg_s.pop()
|
274
|
-
self._pkg_spec_s.pop()
|
247
|
+
self._task_node_m[task.name] = task
|
275
248
|
|
276
249
|
return task
|
250
|
+
|
251
|
+
def mkTaskNode(self, task_t, name=None, srcdir=None, needs=None, **kwargs):
|
252
|
+
self._log.debug("--> mkTaskNode: %s" % task_t)
|
277
253
|
|
278
|
-
|
279
|
-
|
280
|
-
# Need is a local task. Prefix to avoid ambiguity
|
281
|
-
return self._pkg_s[-1].name + "." + need_def
|
254
|
+
if task_t in self._task_m.keys():
|
255
|
+
task = self._task_m[task_t]
|
282
256
|
else:
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
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)
|
292
268
|
else:
|
293
|
-
|
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]
|
294
278
|
else:
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
pkg_name = spec.name
|
306
|
-
if pkg_def is not None:
|
307
|
-
# Look for an import alias
|
308
|
-
self._logger.debug("Search package %s for import alias %s" % (
|
309
|
-
pkg_def.name, pkg_spec.name))
|
310
|
-
for imp in pkg_def.imports:
|
311
|
-
if type(imp) != str:
|
312
|
-
self._logger.debug("imp: %s" % str(imp))
|
313
|
-
if imp.alias is not None and imp.alias == spec.name:
|
314
|
-
# Found the alias name. Just need to get an instance of this package
|
315
|
-
self._logger.debug("Found alias %s -> %s" % (imp.alias, imp.name))
|
316
|
-
pkg_name = imp.name
|
317
|
-
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([])
|
318
289
|
|
319
|
-
#
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
if spec in self._pkg_m.keys():
|
324
|
-
self._logger.debug("Found cached package instance")
|
325
|
-
pkg = self._pkg_m[spec]
|
326
|
-
elif self.pkg_rgy.hasPackage(spec.name):
|
327
|
-
self._logger.debug("Registry has a definition")
|
328
|
-
p_def = self.pkg_rgy.getPackage(spec.name)
|
329
|
-
|
330
|
-
self._pkg_spec_s.append(p_def)
|
331
|
-
pkg = p_def.mkPackage(self)
|
332
|
-
self._pkg_spec_s.pop()
|
333
|
-
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)
|
334
293
|
else:
|
335
|
-
self.
|
336
|
-
raise Exception("Failed to find definition of package %s" % spec.name)
|
294
|
+
ret = self._mkTaskLeafNode(task)
|
337
295
|
|
338
|
-
|
296
|
+
if not hierarchical:
|
297
|
+
self._task_rundir_s.pop()
|
339
298
|
|
340
|
-
return
|
299
|
+
return ret
|
341
300
|
|
342
|
-
def
|
343
|
-
self.
|
344
|
-
|
345
|
-
if task_t in self._override_m.keys():
|
346
|
-
self._logger.debug("Overriding task %s with %s" % (task_t, self._override_m[task_t]))
|
347
|
-
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]
|
348
304
|
else:
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
if
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
self._logger.debug("Searching for qualifed-name task %s" % need_fullname)
|
370
|
-
if not need_fullname in self._task_m.keys():
|
371
|
-
rundir_s = self._rundir_s
|
372
|
-
self._rundir_s = [need_fullname]
|
373
|
-
need_t = self._mkTaskGraph(need_fullname)
|
374
|
-
self._rundir_s = rundir_s
|
375
|
-
self._task_m[need_fullname] = need_t
|
376
|
-
needs.append(self._task_m[need_fullname])
|
377
|
-
|
378
|
-
self._logger.debug("ctor: %s" % ctor.name)
|
379
|
-
params = ctor.mkTaskParams(kwargs)
|
380
|
-
ret = ctor.mkTaskNode(
|
381
|
-
self,
|
382
|
-
params=params,
|
383
|
-
name=name,
|
384
|
-
srcdir=srcdir,
|
385
|
-
needs=needs)
|
386
|
-
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)
|
387
325
|
else:
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
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
|
+
|
392
432
|
|
393
433
|
def getTaskCtor(self, spec : Union[str,'TaskSpec'], pkg : PackageDef = None) -> 'TaskNodeCtor':
|
394
434
|
from .task_def import TaskSpec
|
395
435
|
if type(spec) == str:
|
396
436
|
spec = TaskSpec(spec)
|
397
437
|
|
398
|
-
self.
|
438
|
+
self._log.debug("--> getTaskCtor %s" % spec.name)
|
399
439
|
spec_e = spec.name.split(".")
|
400
440
|
task_name = spec_e[-1]
|
401
441
|
|
402
|
-
if len(spec_e) == 1:
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
else:
|
408
|
-
|
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])
|
409
449
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
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
|
415
455
|
|
416
456
|
ctor = pkg.getTaskCtor(task_name)
|
417
457
|
|
418
|
-
self.
|
458
|
+
self._log.debug("<-- getTaskCtor %s" % spec.name)
|
419
459
|
return ctor
|
420
460
|
|
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()
|
421
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
|