dv-flow-mgr 0.0.1.14013608039a1__py3-none-any.whl → 0.0.1.14097297609a1__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 (31) hide show
  1. dv_flow/mgr/__init__.py +2 -2
  2. dv_flow/mgr/__main__.py +26 -1
  3. dv_flow/mgr/cmds/cmd_graph.py +82 -0
  4. dv_flow/mgr/cmds/cmd_show.py +107 -0
  5. dv_flow/mgr/fileset.py +1 -0
  6. dv_flow/mgr/package.py +3 -3
  7. dv_flow/mgr/package_def.py +82 -12
  8. dv_flow/mgr/std/message.py +1 -1
  9. dv_flow/mgr/task_data.py +24 -20
  10. dv_flow/mgr/task_def.py +0 -1
  11. dv_flow/mgr/task_graph_builder.py +121 -12
  12. dv_flow/mgr/task_graph_dot_writer.py +78 -0
  13. dv_flow/mgr/task_node.py +3 -326
  14. dv_flow/mgr/task_node_compound.py +13 -1
  15. dv_flow/mgr/task_node_ctor.py +118 -0
  16. dv_flow/mgr/task_node_ctor_compound.py +117 -0
  17. dv_flow/mgr/task_node_ctor_compound_proxy.py +65 -0
  18. dv_flow/mgr/task_node_ctor_def_base.py +47 -0
  19. dv_flow/mgr/task_node_ctor_proxy.py +56 -0
  20. dv_flow/mgr/task_node_ctor_task.py +64 -0
  21. dv_flow/mgr/task_node_ctor_wrapper.py +96 -0
  22. dv_flow/mgr/task_node_leaf.py +170 -0
  23. dv_flow/mgr/task_runner.py +11 -6
  24. {dv_flow_mgr-0.0.1.14013608039a1.dist-info → dv_flow_mgr-0.0.1.14097297609a1.dist-info}/METADATA +1 -1
  25. {dv_flow_mgr-0.0.1.14013608039a1.dist-info → dv_flow_mgr-0.0.1.14097297609a1.dist-info}/RECORD +29 -20
  26. {dv_flow_mgr-0.0.1.14013608039a1.dist-info → dv_flow_mgr-0.0.1.14097297609a1.dist-info}/WHEEL +1 -1
  27. dv_flow/mgr/task.py +0 -181
  28. dv_flow/mgr/task_ctor.py +0 -64
  29. {dv_flow_mgr-0.0.1.14013608039a1.dist-info → dv_flow_mgr-0.0.1.14097297609a1.dist-info}/entry_points.txt +0 -0
  30. {dv_flow_mgr-0.0.1.14013608039a1.dist-info → dv_flow_mgr-0.0.1.14097297609a1.dist-info}/licenses/LICENSE +0 -0
  31. {dv_flow_mgr-0.0.1.14013608039a1.dist-info → dv_flow_mgr-0.0.1.14097297609a1.dist-info}/top_level.txt +0 -0
dv_flow/mgr/__init__.py CHANGED
@@ -21,10 +21,10 @@
21
21
  #****************************************************************************
22
22
  from .package_def import *
23
23
  from .pkg_rgy import PkgRgy
24
- from .task import *
25
24
  from .task_data import *
26
25
  from .task_graph_builder import TaskGraphBuilder
27
- from .task_node import task
26
+ from .task_runner import TaskRunner
27
+ from .task_node_ctor_wrapper import task
28
28
  from .task_runner import TaskSetRunner
29
29
  from .task_listener_log import TaskListenerLog
30
30
 
dv_flow/mgr/__main__.py CHANGED
@@ -21,10 +21,14 @@
21
21
  #****************************************************************************
22
22
  import argparse
23
23
  import logging
24
+ from .cmds.cmd_graph import CmdGraph
24
25
  from .cmds.cmd_run import CmdRun
26
+ from .cmds.cmd_show import CmdShow
25
27
 
26
28
  def get_parser():
27
- parser = argparse.ArgumentParser(description='dv_flow_mgr')
29
+ parser = argparse.ArgumentParser(
30
+ description='dv_flow_mgr',
31
+ prog='dfm')
28
32
  # parser.add_argument("-d", "--debug",
29
33
  # help="Enable debug",
30
34
  # action="store_true")
@@ -36,6 +40,16 @@ def get_parser():
36
40
  # action="store_true")
37
41
  subparsers = parser.add_subparsers(required=True)
38
42
 
43
+ graph_parser = subparsers.add_parser('graph',
44
+ help='Generates the graph of a task')
45
+ graph_parser.add_argument("task", nargs="?", help="task to graph")
46
+ graph_parser.add_argument("-f", "--format", help="Specifies the output format",
47
+ default="dot")
48
+ graph_parser.add_argument("-o", "--output",
49
+ help="Specifies the output file",
50
+ default="-")
51
+ graph_parser.set_defaults(func=CmdGraph())
52
+
39
53
  run_parser = subparsers.add_parser('run', help='run a flow')
40
54
  run_parser.add_argument("tasks", nargs='*', help="tasks to run")
41
55
  run_parser.add_argument("-j",
@@ -43,6 +57,17 @@ def get_parser():
43
57
  type=int, default=-1)
44
58
  run_parser.set_defaults(func=CmdRun())
45
59
 
60
+ show_parser = subparsers.add_parser('show',
61
+ help='Display information about a task or tasks')
62
+ show_parser.add_argument("task", nargs='?', help="task to show")
63
+ show_parser.add_argument("-a", "--all",
64
+ action="store_true",
65
+ help="Shows all tasks required for the subject to run")
66
+ show_parser.add_argument("-v", "--verbose",
67
+ action="store_true",
68
+ help="Shows additional information about tasks")
69
+ show_parser.set_defaults(func=CmdShow())
70
+
46
71
  return parser
47
72
 
48
73
  def main():
@@ -0,0 +1,82 @@
1
+ #****************************************************************************
2
+ #* cmd_graph.py
3
+ #*
4
+ #* Copyright 2023-2025 Matthew Ballance and Contributors
5
+ #*
6
+ #* Licensed under the Apache License, Version 2.0 (the "License"); you may
7
+ #* not use this file except in compliance with the License.
8
+ #* You may obtain a copy of the License at:
9
+ #*
10
+ #* http://www.apache.org/licenses/LICENSE-2.0
11
+ #*
12
+ #* Unless required by applicable law or agreed to in writing, software
13
+ #* distributed under the License is distributed on an "AS IS" BASIS,
14
+ #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ #* See the License for the specific language governing permissions and
16
+ #* limitations under the License.
17
+ #*
18
+ #* Created on:
19
+ #* Author:
20
+ #*
21
+ #****************************************************************************
22
+ import asyncio
23
+ import os
24
+ import logging
25
+ from typing import ClassVar
26
+ from ..util import loadProjPkgDef
27
+ from ..task_graph_builder import TaskGraphBuilder
28
+ from ..task_runner import TaskSetRunner
29
+ from ..task_listener_log import TaskListenerLog
30
+ from ..pkg_rgy import PkgRgy
31
+ from ..task_graph_dot_writer import TaskGraphDotWriter
32
+
33
+
34
+ class CmdGraph(object):
35
+ _log : ClassVar = logging.getLogger("CmdGraph")
36
+
37
+ def __call__(self, args):
38
+
39
+ # First, find the project we're working with
40
+ pkg = loadProjPkgDef(os.getcwd())
41
+
42
+ if pkg is None:
43
+ raise Exception("Failed to find a 'flow.dv' file that defines a package in %s or its parent directories" % os.getcwd())
44
+
45
+ self._log.debug("Root flow file defines package: %s" % pkg.name)
46
+
47
+ if args.task is None:
48
+ # Print out available tasks
49
+ tasks = []
50
+ for task in pkg.tasks:
51
+ tasks.append(task)
52
+ for frag in pkg._fragment_l:
53
+ for task in frag.tasks:
54
+ tasks.append(task)
55
+ tasks.sort(key=lambda x: x.name)
56
+
57
+ max_name_len = 0
58
+ for t in tasks:
59
+ if len(t.name) > max_name_len:
60
+ max_name_len = len(t.name)
61
+
62
+ print("No task specified. Available Tasks:")
63
+ for t in tasks:
64
+ desc = t.desc
65
+ if desc is None or t.desc == "":
66
+ "<no descripion>"
67
+ print("%s - %s" % (t.name.ljust(max_name_len), desc))
68
+ else:
69
+ rundir = os.path.join(pkg._basedir, "rundir")
70
+
71
+ builder = TaskGraphBuilder(root_pkg=pkg, rundir=rundir)
72
+
73
+ t = builder.mkTaskGraph(pkg.name + "." + args.task)
74
+
75
+ TaskGraphDotWriter().write(
76
+ t,
77
+ "-"
78
+ )
79
+
80
+ return 0
81
+
82
+
@@ -0,0 +1,107 @@
1
+ #****************************************************************************
2
+ #* cmd_show.py
3
+ #*
4
+ #* Copyright 2023-2025 Matthew Ballance and Contributors
5
+ #*
6
+ #* Licensed under the Apache License, Version 2.0 (the "License"); you may
7
+ #* not use this file except in compliance with the License.
8
+ #* You may obtain a copy of the License at:
9
+ #*
10
+ #* http://www.apache.org/licenses/LICENSE-2.0
11
+ #*
12
+ #* Unless required by applicable law or agreed to in writing, software
13
+ #* distributed under the License is distributed on an "AS IS" BASIS,
14
+ #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ #* See the License for the specific language governing permissions and
16
+ #* limitations under the License.
17
+ #*
18
+ #* Created on:
19
+ #* Author:
20
+ #*
21
+ #****************************************************************************
22
+ import asyncio
23
+ import os
24
+ import logging
25
+ import toposort
26
+ from typing import ClassVar
27
+ from ..util import loadProjPkgDef
28
+ from ..task_graph_builder import TaskGraphBuilder
29
+ from ..task_runner import TaskSetRunner
30
+ from ..task_listener_log import TaskListenerLog
31
+ from ..pkg_rgy import PkgRgy
32
+ from ..task_graph_dot_writer import TaskGraphDotWriter
33
+
34
+
35
+ class CmdShow(object):
36
+ _log : ClassVar = logging.getLogger("CmdGraph")
37
+
38
+ def __call__(self, args):
39
+
40
+ # First, find the project we're working with
41
+ pkg = loadProjPkgDef(os.getcwd())
42
+
43
+ if pkg is None:
44
+ raise Exception("Failed to find a 'flow.dv' file that defines a package in %s or its parent directories" % os.getcwd())
45
+
46
+ self._log.debug("Root flow file defines package: %s" % pkg.name)
47
+
48
+ if args.task is None:
49
+ # Print out available tasks
50
+ tasks = []
51
+ for task in pkg.tasks:
52
+ tasks.append(task)
53
+ for frag in pkg._fragment_l:
54
+ for task in frag.tasks:
55
+ tasks.append(task)
56
+ tasks.sort(key=lambda x: x.name)
57
+
58
+ max_name_len = 0
59
+ for t in tasks:
60
+ if len(t.name) > max_name_len:
61
+ max_name_len = len(t.name)
62
+
63
+ print("No task specified. Available Tasks:")
64
+ for t in tasks:
65
+ desc = t.desc
66
+ if desc is None or t.desc == "":
67
+ "<no descripion>"
68
+ print("%s - %s" % (t.name.ljust(max_name_len), desc))
69
+ else:
70
+ rundir = os.path.join(pkg._basedir, "rundir")
71
+
72
+ builder = TaskGraphBuilder(root_pkg=pkg, rundir=rundir)
73
+
74
+
75
+ if args.all:
76
+ t = builder.mkTaskGraph(pkg.name + "." + args.task)
77
+ dep_m = TaskSetRunner(rundir=None).buildDepMap(t)
78
+
79
+ order = list(toposort.toposort(dep_m))
80
+
81
+ print("Task: %s" % t.name)
82
+
83
+ last_s = set()
84
+ for i,s in enumerate(order):
85
+ print("-- TaskSet %d --" % (i+1))
86
+ for t in s:
87
+ print(" - %s" % t.name)
88
+ if args.verbose and len(t.params.model_fields.items()):
89
+ print(" params:")
90
+ for name,field in t.params.model_fields.items():
91
+ print(" - %s: %s" % (name, getattr(t.params, name)))
92
+ if len(t.needs):
93
+ print(" needs:")
94
+ for n in t.needs:
95
+ print(" - %s%s" % (
96
+ n[0].name,
97
+ ("*" if n[0] in last_s else "")))
98
+ last_s = s
99
+
100
+ pass
101
+ else:
102
+ # Show show info about the current task
103
+ pass
104
+
105
+ return 0
106
+
107
+
dv_flow/mgr/fileset.py CHANGED
@@ -29,6 +29,7 @@ class FileSet(BaseModel):
29
29
  basedir : str
30
30
  name : str = ""
31
31
  src : str = None
32
+ seq : int = -1
32
33
  files : List[str] = dc.Field(default_factory=list)
33
34
  incdirs: List[str] = dc.Field(default_factory=list)
34
35
  params : Dict[str,str] = dc.Field(default_factory=dict)
dv_flow/mgr/package.py CHANGED
@@ -22,7 +22,7 @@
22
22
  import dataclasses as dc
23
23
  import logging
24
24
  from typing import Any, ClassVar, Dict
25
- from .task_ctor import TaskCtor
25
+ from .task_node_ctor import TaskNodeCtor
26
26
 
27
27
  @dc.dataclass
28
28
  class Package(object):
@@ -30,11 +30,11 @@ class Package(object):
30
30
  params : Dict[str,Any] = dc.field(default_factory=dict)
31
31
  # Package holds constructors for tasks
32
32
  # - Dict holds the default parameters for the task
33
- tasks : Dict[str,TaskCtor] = dc.field(default_factory=dict)
33
+ tasks : Dict[str,TaskNodeCtor] = dc.field(default_factory=dict)
34
34
  types : Dict[str,Any] = dc.field(default_factory=dict)
35
35
  _log : ClassVar = logging.getLogger("Package")
36
36
 
37
- def getTaskCtor(self, name : str) -> TaskCtor:
37
+ def getTaskCtor(self, name : str) -> TaskNodeCtor:
38
38
  self._log.debug("-- %s::getTaskCtor: %s" % (self.name, name))
39
39
  if name not in self.tasks.keys():
40
40
  raise Exception("Task %s not present in package %s" % (name, self.name))
@@ -21,7 +21,6 @@
21
21
  #****************************************************************************
22
22
  import io
23
23
  import os
24
- import json
25
24
  import yaml
26
25
  import importlib
27
26
  import logging
@@ -34,10 +33,13 @@ from .fragment_def import FragmentDef
34
33
  from .package import Package
35
34
  from .package_import_spec import PackageImportSpec, PackageSpec
36
35
  from .param_def import ParamDef
37
- from .task_node import TaskNodeCtor, TaskNodeCtorProxy, TaskNodeCtorTask
38
- from .task_ctor import TaskCtor
39
- from .task_def import TaskDef, TaskSpec
40
- from .std.task_null import TaskNull, TaskNullParams
36
+ from .task_def import TaskDef
37
+ from .task_node_ctor import TaskNodeCtor
38
+ from .task_node_ctor_proxy import TaskNodeCtorProxy
39
+ from .task_node_ctor_task import TaskNodeCtorTask
40
+ from .task_node_ctor_compound import TaskNodeCtorCompound
41
+ from .task_node_ctor_compound_proxy import TaskNodeCtorCompoundProxy
42
+ from .std.task_null import TaskNull
41
43
  from .type_def import TypeDef
42
44
 
43
45
 
@@ -104,18 +106,18 @@ class PackageDef(BaseModel):
104
106
 
105
107
  session.push_package(ret, add=True)
106
108
 
107
- tasks_m : Dict[str,str,TaskCtor]= {}
109
+ tasks_m : Dict[str,str,TaskNodeCtor]= {}
108
110
 
109
111
  for task in self.tasks:
110
112
  if task.name in tasks_m.keys():
111
113
  raise Exception("Duplicate task %s" % task.name)
112
- tasks_m[task.name] = (task, self._basedir, ) # We'll add a TaskCtor later
114
+ tasks_m[task.name] = (task, self._basedir, ) # We'll add a TaskNodeCtor later
113
115
 
114
116
  for frag in self._fragment_l:
115
117
  for task in frag.tasks:
116
118
  if task.name in tasks_m.keys():
117
119
  raise Exception("Duplicate task %s" % task.name)
118
- tasks_m[task.name] = (task, frag._basedir, ) # We'll add a TaskCtor later
120
+ tasks_m[task.name] = (task, frag._basedir, ) # We'll add a TaskNodeCtor later
119
121
 
120
122
  # Now we have a unified map of the tasks declared in this package
121
123
  for name in list(tasks_m.keys()):
@@ -161,10 +163,23 @@ class PackageDef(BaseModel):
161
163
  ctor_t = tasks_m[task_name][2]
162
164
  return ctor_t
163
165
 
164
- def mkTaskCtor(self, session, task, srcdir, tasks_m) -> TaskCtor:
166
+ def mkTaskCtor(self, session, task, srcdir, tasks_m) -> TaskNodeCtor:
165
167
  self._log.debug("--> %s::mkTaskCtor %s (srcdir: %s)" % (self.name, task.name, srcdir))
166
- base_ctor_t : TaskCtor = None
167
- ctor_t : TaskCtor = None
168
+
169
+ if len(task.tasks) > 0:
170
+ # Compound task
171
+ ctor = self._mkCompoundTaskCtor(session, task, srcdir, tasks_m)
172
+ else:
173
+ # Leaf task
174
+ ctor = self._mkLeafTaskCtor(session, task, srcdir, tasks_m)
175
+
176
+ return ctor
177
+
178
+
179
+ def _mkLeafTaskCtor(self, session, task, srcdir, tasks_m) -> TaskNodeCtor:
180
+ self._log.debug("--> _mkLeafTaskCtor")
181
+ base_ctor_t : TaskNodeCtor = None
182
+ ctor_t : TaskNodeCtor = None
168
183
  base_params : BaseModel = None
169
184
  callable = None
170
185
  passthrough = task.passthrough
@@ -245,7 +260,62 @@ class PackageDef(BaseModel):
245
260
 
246
261
  self._log.debug("<-- %s::mkTaskCtor %s" % (self.name, task.name))
247
262
  return ctor_t
248
-
263
+
264
+ def _mkCompoundTaskCtor(self, session, task, srcdir, tasks_m) -> TaskNodeCtor:
265
+ self._log.debug("--> _mkCompoundTaskCtor")
266
+ base_ctor_t : TaskNodeCtor = None
267
+ ctor_t : TaskNodeCtor = None
268
+ base_params : BaseModel = None
269
+ callable = None
270
+ passthrough = task.passthrough
271
+ consumes = [] if task.consumes is None else task.consumes.copy()
272
+ needs = [] if task.needs is None else task.needs.copy()
273
+ fullname = self.name + "." + task.name
274
+
275
+
276
+ if task.uses is not None:
277
+ self._log.debug("Uses: %s" % task.uses)
278
+ base_ctor_t = self.getTaskCtor(session, task.uses, tasks_m)
279
+ base_params = base_ctor_t.mkTaskParams()
280
+
281
+ # Once we have passthrough, we can't turn it off
282
+ passthrough |= base_ctor_t.passthrough
283
+ consumes.extend(base_ctor_t.consumes)
284
+
285
+ if base_ctor_t is None:
286
+ self._log.error("Failed to load task ctor %s" % task.uses)
287
+
288
+ # Determine if we need to use a new
289
+ paramT = self._getParamT(session, task, base_params)
290
+
291
+ if base_ctor_t is not None:
292
+ ctor_t = TaskNodeCtorCompoundProxy(
293
+ name=fullname,
294
+ srcdir=srcdir,
295
+ paramT=paramT,
296
+ passthrough=passthrough,
297
+ consumes=consumes,
298
+ needs=needs,
299
+ task_def=task,
300
+ uses=base_ctor_t)
301
+ else:
302
+ self._log.debug("No 'uses' specified")
303
+ ctor_t = TaskNodeCtorCompound(
304
+ name=fullname,
305
+ srcdir=srcdir,
306
+ paramT=paramT,
307
+ passthrough=passthrough,
308
+ consumes=consumes,
309
+ needs=needs,
310
+ task_def=task)
311
+
312
+ for t in task.tasks:
313
+ ctor_t.tasks.append(self.mkTaskCtor(session, t, srcdir, tasks_m))
314
+
315
+
316
+ self._log.debug("<-- %s::mkTaskCtor %s (%d)" % (self.name, task.name, len(ctor_t.tasks)))
317
+ return ctor_t
318
+
249
319
  def _getParamT(self, session, task, base_t : BaseModel):
250
320
  self._log.debug("--> _getParamT %s" % task.fullname)
251
321
  # Get the base parameter type (if available)
@@ -19,7 +19,7 @@
19
19
  #* Author:
20
20
  #*
21
21
  #****************************************************************************
22
- from dv_flow.mgr import Task, TaskDataResult
22
+ from dv_flow.mgr import TaskDataResult
23
23
 
24
24
  async def Message(runner, input) -> TaskDataResult:
25
25
  print("%s: %s" % (input.name, input.params.msg), flush=True)
dv_flow/mgr/task_data.py CHANGED
@@ -48,20 +48,16 @@ class TaskMarker(BaseModel):
48
48
  severity : SeverityE
49
49
  loc : TaskMarkerLoc = dc.Field(default=None)
50
50
 
51
- class TaskParameterSet(BaseModel):
52
- type : str = None
53
- task : str = None # Name of the task that produced this param set
54
- seq : int = -1 # Order in which the param-set must appear
55
-
56
51
  class TaskDataInput(BaseModel):
57
52
  """
58
- Input data to a task
59
- - name - name of the task
60
- - changed - indicates whether any of this task's dependencies have changed
61
- - rundir - directory in which the task is to be run
62
- - params - parameters to the task
63
- - inputs - list of parameter sets 'consumed' by this task
64
- - memento - memento data previously returned by this task. None if no memento is available
53
+ Input data to a task:
54
+
55
+ * name - name of the task
56
+ * changed - indicates whether any of this task's dependencies have changed
57
+ * rundir - directory in which the task is to be run
58
+ * params - parameters to the task
59
+ * inputs - list of `TaskDataItem` that are consumed' by this task
60
+ * memento - memento data previously returned by this task. None if no memento is available
65
61
  """
66
62
  name : str
67
63
  changed : bool
@@ -73,12 +69,13 @@ class TaskDataInput(BaseModel):
73
69
 
74
70
  class TaskDataResult(BaseModel):
75
71
  """
76
- Result data from a task
77
- - changed - indicates whether the task modified its result data
78
- - output - list of output parameter sets
79
- - memento - memento data to be passed to the next invocation of the task
80
- - markers - list of markers produced by the task
81
- - status - status code (0=success, non-zero=failure)
72
+ Result data from a task:
73
+
74
+ * changed - indicates whether the task modified its result data
75
+ * output - list of output parameter sets
76
+ * memento - memento data to be passed to the next invocation of the task
77
+ * markers - list of markers produced by the task
78
+ * status - status code (0=success, non-zero=failure)
82
79
  """
83
80
  changed : bool = dc.Field(default=True)
84
81
  output : List[Any] = dc.Field(default_factory=list)
@@ -113,9 +110,16 @@ class TaskDataParam(BaseModel):
113
110
  ops : List[TaskDataParamOp] = dc.Field(default_factory=list)
114
111
 
115
112
  class TaskDataItem(BaseModel):
113
+ """
114
+ Base class for task data items
115
+
116
+ * type - Name of the data item type
117
+ * src - Name of the task that produced this item
118
+ * seq - Sequence number of the item within the task
119
+ """
116
120
  type : str
117
- src : str
118
- id : str
121
+ src : str = None
122
+ seq : int = -1
119
123
 
120
124
  class TaskData(BaseModel):
121
125
  src : str = None
dv_flow/mgr/task_def.py CHANGED
@@ -24,7 +24,6 @@ import enum
24
24
  from pydantic import BaseModel
25
25
  from typing import Any, Dict, List, Union, Tuple
26
26
  from .param_def import ParamDef
27
- from .task import Task
28
27
  from .task_output import TaskOutput
29
28
 
30
29
  @dc.dataclass