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.
- 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 -444
- 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 +497 -247
- 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 +21 -33
- 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.14182043984a1.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.14182043984a1.dist-info/RECORD +0 -59
- {dv_flow_mgr-0.0.2.14182043984a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/WHEEL +0 -0
- {dv_flow_mgr-0.0.2.14182043984a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/entry_points.txt +0 -0
- {dv_flow_mgr-0.0.2.14182043984a1.dist-info → dv_flow_mgr-1.0.0.14370600369a1.dist-info}/licenses/LICENSE +0 -0
- {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 .
|
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,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.
|
195
|
-
task = self.
|
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.
|
213
|
-
self.
|
205
|
+
return self.mkTaskNode(task, rundir=rundir)
|
206
|
+
# self._task_node_m.clear()
|
214
207
|
|
215
|
-
if rundir is not None:
|
216
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
222
|
+
pass
|
239
223
|
|
240
|
-
|
241
|
-
|
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
|
-
|
227
|
+
ctor = self._getTaskCtor(task_t)
|
247
228
|
|
248
|
-
|
229
|
+
params = ctor.mkTaskParams()
|
249
230
|
|
250
231
|
needs = []
|
251
232
|
|
252
|
-
for
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
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.
|
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
|
286
|
-
|
251
|
+
def mkTaskNode(self, task_t, name=None, srcdir=None, needs=None, **kwargs):
|
252
|
+
self._log.debug("--> mkTaskNode: %s" % task_t)
|
287
253
|
|
288
|
-
|
289
|
-
|
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
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
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
|
-
|
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
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
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
|
-
#
|
330
|
-
|
331
|
-
|
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.
|
346
|
-
raise Exception("Failed to find definition of package %s" % spec.name)
|
294
|
+
ret = self._mkTaskLeafNode(task)
|
347
295
|
|
348
|
-
|
296
|
+
if not hierarchical:
|
297
|
+
self._task_rundir_s.pop()
|
349
298
|
|
350
|
-
return
|
299
|
+
return ret
|
351
300
|
|
352
|
-
def
|
353
|
-
self.
|
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
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
if
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
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
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
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.
|
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
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
else:
|
418
|
-
|
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
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
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.
|
458
|
+
self._log.debug("<-- getTaskCtor %s" % spec.name)
|
429
459
|
return ctor
|
430
460
|
|
431
|
-
def error(self, msg):
|
432
|
-
|
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
|