dv-flow-mgr 0.0.1.12761553329a1__py3-none-any.whl → 0.0.1.12849118090a1__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/fileset.py CHANGED
@@ -24,8 +24,9 @@ from pydantic import BaseModel
24
24
  from typing import Any, Dict, List, Tuple
25
25
 
26
26
  class FileSet(BaseModel):
27
- src : str
28
27
  type : str
29
28
  basedir : str
29
+ name : str = ""
30
+ src : str = None
30
31
  files : List[str] = dc.Field(default_factory=list)
31
32
  params : Dict[str,str] = dc.Field(default_factory=dict)
dv_flow_mgr/package.py CHANGED
@@ -54,26 +54,6 @@ class Package(object):
54
54
  def getTaskCtor(self, name : str) -> TaskCtor:
55
55
  return self.tasks[name]
56
56
 
57
- def mkTaskParams(self, name : str) -> TaskParams:
58
- if name not in self.tasks:
59
- raise Exception("Task " + name + " not found")
60
- return self.tasks[name].mkTaskParams()
61
-
62
- def setTaskParams(self, name : str, params : TaskParams, pvals : Dict[str,Any]):
63
- if name not in self.tasks:
64
- raise Exception("Task " + name + " not found")
65
- self.tasks[name].setTaskParams(params, pvals)
66
-
67
- def mkTask(self,
68
- name : str,
69
- task_id : int,
70
- session : 'Session',
71
- params : TaskParams,
72
- depends : List['Task']) -> 'Task':
73
- # TODO: combine parameters to create the full taskname
74
- task = self.tasks[name].mkTask(name, task_id, session, params, depends)
75
- return task
76
-
77
57
  def __hash__(self):
78
58
  return hash(self.fullname())
79
59
 
@@ -19,16 +19,20 @@
19
19
  #* Author:
20
20
  #*
21
21
  #****************************************************************************
22
- import pydantic.dataclasses as dc
23
22
  import json
23
+ import importlib
24
+ import sys
25
+ import pydantic
26
+ import pydantic.dataclasses as dc
24
27
  from pydantic import BaseModel
25
- from typing import Any, Dict, List
28
+ from typing import Any, Dict, List, Callable, Tuple
26
29
  from .flow import Flow
27
30
  from .fragment_def import FragmentDef
28
31
  from .package import Package
29
32
  from .package_import_spec import PackageImportSpec, PackageSpec
30
- from .task import TaskParamCtor
33
+ from .task import TaskCtor, TaskParams
31
34
  from .task_def import TaskDef, TaskSpec
35
+ from .tasklib.builtin_pkg import TaskNull
32
36
 
33
37
 
34
38
  class PackageDef(BaseModel):
@@ -53,46 +57,154 @@ class PackageDef(BaseModel):
53
57
  def mkPackage(self, session, params : Dict[str,Any] = None) -> 'Package':
54
58
  ret = Package(self.name)
55
59
 
60
+ session.push_package(ret)
61
+
62
+ tasks_m : Dict[str,str,TaskCtor]= {}
63
+
56
64
  for task in self.tasks:
57
- if task.type is not None:
58
- # Find package (not package_def) that implements this task
59
- # Insert an indirect reference to that tasks's constructor
60
-
61
- # Only call getTaskCtor if the task is in a different package
62
- task_t = task.type if isinstance(task.type, TaskSpec) else TaskSpec(task.type)
63
- ctor_t = session.getTaskCtor(task_t, self)
64
-
65
- ctor_t = TaskParamCtor(
66
- base=ctor_t,
67
- params=task.params,
68
- basedir=self.basedir,
69
- depend_refs=task.depends)
70
- else:
71
- # We use the Null task from the std package
72
- raise Exception("")
73
- ret.tasks[task.name] = ctor_t
65
+ if task.name in tasks_m.keys():
66
+ raise Exception("Duplicate task %s" % task.name)
67
+ tasks_m[task.name] = (task, self.basedir, ) # We'll add a TaskCtor later
74
68
 
75
69
  for frag in self.fragment_l:
76
70
  for task in frag.tasks:
77
- if task.type is not None:
78
- # Find package (not package_def) that implements this task
79
- # Insert an indirect reference to that tasks's constructor
80
-
81
- # Only call getTaskCtor if the task is in a different package
82
- task_t = task.type if isinstance(task.type, TaskSpec) else TaskSpec(task.type)
83
- ctor_t = session.getTaskCtor(task_t, self)
84
-
85
- ctor_t = TaskParamCtor(
86
- base=ctor_t,
87
- params=task.params,
88
- basedir=frag.basedir,
89
- depend_refs=task.depends)
90
- else:
91
- # We use the Null task from the std package
92
- raise Exception("")
93
- if task.name in ret.tasks:
94
- raise Exception("Task %s already defined" % task.name)
95
- ret.tasks[task.name] = ctor_t
71
+ if task.name in tasks_m.keys():
72
+ raise Exception("Duplicate task %s" % task.name)
73
+ tasks_m[task.name] = (task, frag.basedir, ) # We'll add a TaskCtor later
74
+
75
+ # Now we have a unified map of the tasks declared in this package
76
+ for name in list(tasks_m.keys()):
77
+ task_i = tasks_m[name]
78
+ if len(task_i) < 3:
79
+ # Need to create the task ctor
80
+ ctor_t = self.mkTaskCtor(session, task_i[0], task_i[1], tasks_m)
81
+ tasks_m[name] = (task_i[0], task_i[1], ctor_t)
82
+ ret.tasks[name] = tasks_m[name][2]
83
+
84
+ session.pop_package(ret)
96
85
 
97
86
  return ret
87
+
88
+ def mkTaskCtor(self, session, task, srcdir, tasks_m) -> TaskCtor:
89
+ ctor_t : TaskCtor = None
90
+
91
+ if task.uses is not None:
92
+ # Find package (not package_def) that implements this task
93
+ # Insert an indirect reference to that tasks's constructor
94
+ last_dot = task.uses.rfind('.')
95
+
96
+ if last_dot != -1:
97
+ pkg_name = task.uses[:last_dot]
98
+ task_name = task.uses[last_dot+1:]
99
+ else:
100
+ pkg_name = None
101
+ task_name = task.uses
102
+
103
+ if pkg_name is not None:
104
+ pkg = session.getPackage(PackageSpec(pkg_name))
105
+ if pkg is None:
106
+ raise Exception("Failed to find package %s" % pkg_name)
107
+ ctor_t = pkg.getTaskCtor(task_name)
108
+ ctor_t = ctor_t.copy()
109
+ ctor_t.srcdir = srcdir
110
+ else:
111
+ if task_name not in tasks_m.keys():
112
+ raise Exception("Failed to find task %s" % task_name)
113
+ if len(tasks_m[task_name]) == 3:
114
+ ctor_t = tasks_m[task_name][2].copy()
115
+ ctor_t.srcdir = srcdir
116
+ else:
117
+ task_i = tasks_m[task_name]
118
+ ctor_t = self.mkTaskCtor(
119
+ session,
120
+ task=task_i[0],
121
+ srcdir=srcdir,
122
+ tasks_m=tasks_m)
123
+ tasks_m[task_name] = ctor_t
124
+
125
+ if ctor_t is None:
126
+ # Provide a default implementation
127
+ ctor_t = TaskCtor(
128
+ task_ctor=TaskNull,
129
+ param_ctor=TaskParams,
130
+ srcdir=srcdir)
131
+
132
+ if task.pyclass is not None:
133
+ # Built-in impl
134
+ # Now, lookup the class
135
+ last_dot = task.pyclass.rfind('.')
136
+ clsname = task.pyclass[last_dot+1:]
137
+ modname = task.pyclass[:last_dot]
138
+
139
+ try:
140
+ if modname not in sys.modules:
141
+ if self.basedir not in sys.path:
142
+ sys.path.append(self.basedir)
143
+ mod = importlib.import_module(modname)
144
+ else:
145
+ mod = sys.modules[modname]
146
+ except ModuleNotFoundError as e:
147
+ raise Exception("Failed to import module %s" % modname)
148
+
149
+ if not hasattr(mod, clsname):
150
+ raise Exception("Class %s not found in module %s" % (clsname, modname))
151
+ ctor_t.task_ctor = getattr(mod, clsname)
152
+
153
+ if task.uses is None:
154
+ ctor_t.param_ctor = TaskParams
155
+
156
+ decl_params = False
157
+ for value in task.params.values():
158
+ if "type" in value:
159
+ decl_params = True
160
+ break
161
+
162
+ if decl_params:
163
+ # We need to combine base parameters with new parameters
164
+ field_m = {}
165
+ # First, add parameters from the base class
166
+ for fname,info in ctor_t.param_ctor.model_fields.items():
167
+ print("Field: %s (%s)" % (fname, info.default))
168
+ field_m[fname] = (info.annotation, info.default)
169
+ ptype_m = {
170
+ "str" : str,
171
+ "int" : int,
172
+ "float" : float,
173
+ "bool" : bool
174
+ }
175
+ for p in task.params.keys():
176
+ param = task.params[p]
177
+ if type(param) == dict and "type" in param.keys():
178
+ ptype_s = param["type"]
179
+ if ptype_s not in ptype_m.keys():
180
+ raise Exception("Unknown type %s" % ptype_s)
181
+ ptype = ptype_m[ptype_s]
182
+
183
+ if p in field_m.keys():
184
+ raise Exception("Duplicate field %s" % p)
185
+ if "value" in param.keys():
186
+ field_m[p] = (ptype, param["value"])
187
+ else:
188
+ field_m[p] = (ptype, )
189
+ else:
190
+ if p not in field_m.keys():
191
+ raise Exception("Field %s not found" % p)
192
+ if type(param) != dict:
193
+ value = param
194
+ elif "value" in param.keys():
195
+ value = param["value"]
196
+ else:
197
+ raise Exception("No value specified for param %s: %s" % (
198
+ p, str(param)))
199
+ field_m[p] = (field_m[p][0], value)
200
+ print("field_m: %s" % str(field_m))
201
+ ctor_t.param_ctor = pydantic.create_model(
202
+ "Task%sParams" % task.name, **field_m)
203
+ else:
204
+ if len(task.params) > 0:
205
+ ctor_t.params = task.params
206
+ if len(task.depends) > 0:
207
+ ctor_t.depends.extends(task.depends)
208
+
209
+ return ctor_t
98
210
 
dv_flow_mgr/session.py CHANGED
@@ -27,7 +27,7 @@ from typing import Any, Callable, Dict, List
27
27
  from .fragment_def import FragmentDef
28
28
  from .package import Package
29
29
  from .package_def import PackageDef, PackageSpec
30
- from .task import Task,TaskSpec
30
+ from .task import Task, TaskSpec, TaskCtor
31
31
 
32
32
  @dc.dataclass
33
33
  class Session(object):
@@ -45,29 +45,36 @@ class Session(object):
45
45
  _pkg_m : Dict[PackageSpec,Package] = dc.field(default_factory=dict)
46
46
  _pkg_spec_s : List[PackageDef] = dc.field(default_factory=list)
47
47
  _pkg_def_m : Dict[str,PackageDef] = dc.field(default_factory=dict)
48
+ _pkg_file_m : Dict[str,str] = dc.field(default_factory=dict)
49
+ _pkg_path : List[str] = dc.field(default_factory=list)
48
50
  _task_list : List[Task] = dc.field(default_factory=list)
49
51
  _task_m : Dict[TaskSpec,Task] = dc.field(default_factory=dict)
50
52
  _task_id : int = 0
51
53
 
52
54
  def __post_init__(self):
53
- from .tasklib.std.pkg_std import PackageStd
54
- from .tasklib.hdl.sim.vcs_pkg import VcsPackage
55
- from .tasklib.hdl.sim.vlt_pkg import VltPackage
56
- from .tasklib.hdl.sim.mti_pkg import MtiPackage
57
- self._pkg_m[PackageSpec("std")] = PackageStd("std")
58
- self._pkg_m[PackageSpec("hdl.sim.mti")] = MtiPackage("hdl.sim.mti")
59
- self._pkg_m[PackageSpec("hdl.sim.vcs")] = VcsPackage("hdl.sim.vcs")
60
- self._pkg_m[PackageSpec("hdl.sim.vlt")] = VltPackage("hdl.sim.vlt")
61
-
62
- def load(self, root : str):
63
- if not os.path.isdir(root):
64
- raise Exception("Root directory %s does not exist" % root)
65
-
66
- if not os.path.isfile(os.path.join(root, "flow.yaml")):
55
+ # Add a reference to the built-in 'std' package
56
+ this_dir = os.path.dirname(os.path.abspath(__file__))
57
+ self._pkg_file_m["std"] = os.path.join(this_dir, "tasklib/std/flow.dv")
58
+
59
+ # from .tasklib.std.pkg_std import PackageStd
60
+ # from .tasklib.hdl.sim.vcs_pkg import VcsPackage
61
+ # from .tasklib.hdl.sim.vlt_pkg import VltPackage
62
+ # from .tasklib.hdl.sim.mti_pkg import MtiPackage
63
+ # self._pkg_m[PackageSpec("std")] = PackageStd("std")
64
+ # self._pkg_m[PackageSpec("hdl.sim.mti")] = MtiPackage("hdl.sim.mti")
65
+ # self._pkg_m[PackageSpec("hdl.sim.vcs")] = VcsPackage("hdl.sim.vcs")
66
+ # self._pkg_m[PackageSpec("hdl.sim.vlt")] = VltPackage("hdl.sim.vlt")
67
+ pass
68
+
69
+ def load(self):
70
+ if not os.path.isdir(self.srcdir):
71
+ raise Exception("Root directory %s does not exist" % self.srcdir)
72
+
73
+ if not os.path.isfile(os.path.join(self.srcdir, "flow.dv")):
67
74
  raise Exception("No root flow file")
68
75
 
69
- self._root_dir = os.path.dirname(root)
70
- self.package = self._load_package(os.path.join(root, "flow.yaml"), [])
76
+ self._root_dir = os.path.dirname(self.srcdir)
77
+ self.package = self._load_package(os.path.join(self.srcdir, "flow.dv"), [])
71
78
 
72
79
  return self.package
73
80
 
@@ -77,7 +84,7 @@ class Session(object):
77
84
 
78
85
  return self._mkTaskGraph(task, self.rundir)
79
86
 
80
- def _mkTaskGraph(self, task : str, parent_rundir : str, params : dict = None) -> Task:
87
+ def _mkTaskGraph(self, task : str, parent_rundir : str) -> Task:
81
88
 
82
89
  elems = task.split(".")
83
90
 
@@ -99,23 +106,19 @@ class Session(object):
99
106
 
100
107
  self._pkg_s.append(pkg)
101
108
 
102
- #task_def = pkg.getTask(task_name)
109
+ ctor_t : TaskCtor = pkg.getTaskCtor(task_name)
103
110
 
104
111
  depends = []
105
112
 
106
- params = pkg.mkTaskParams(task_name)
107
-
108
- task_id = self.mkTaskId(None)
109
- # task_name = "%s.%s" % (pkg.name, task_def.name)
110
-
111
113
  # The returned task should have all param references resolved
112
- task = pkg.mkTask(
113
- task_name,
114
- task_id,
115
- self,
116
- params,
117
- depends)
118
- task.rundir = rundir
114
+ print("task_ctor=%s" % str(ctor_t.task_ctor), flush=True)
115
+ task = ctor_t.task_ctor(
116
+ name=task_name,
117
+ session=self,
118
+ params=ctor_t.mkParams(),
119
+ depends=depends,
120
+ rundir=rundir,
121
+ srcdir=ctor_t.srcdir)
119
122
 
120
123
  for i,d in enumerate(task.depend_refs):
121
124
  if d in self._task_m.keys():
@@ -130,6 +133,15 @@ class Session(object):
130
133
  self._pkg_spec_s.pop()
131
134
 
132
135
  return task
136
+
137
+ def push_package(self, pkg : Package):
138
+ self._pkg_s.append(pkg)
139
+
140
+ def pop_package(self, pkg : Package):
141
+ self._pkg_s.pop()
142
+
143
+ def package(self):
144
+ return self._pkg_s[-1]
133
145
 
134
146
  def mkTaskId(self, task : 'Task') -> int:
135
147
  self._task_id += 1
@@ -158,13 +170,13 @@ class Session(object):
158
170
 
159
171
  if not len(self._pkg_spec_s):
160
172
  self._pkg_spec_s.append(PackageSpec(pkg.name))
161
- self._pkg_def_m[PackageSpec(pkg.name)] = pkg
162
173
  else:
163
- if self._pkg_spec_s[0].name != pkg.name:
164
- raise Exception("Package name mismatch: %s != %s" % (self._pkg_m[0].name, pkg.name))
174
+ if self._pkg_spec_s[-1].name != pkg.name:
175
+ raise Exception("Package name mismatch: %s != %s" % (self._pkg_spec_s[-1].name, pkg.name))
165
176
  else:
166
177
  # TODO: merge content
167
178
  self._pkg_spec_s.append(PackageSpec(pkg.name))
179
+ self._pkg_def_m[PackageSpec(pkg.name)] = pkg
168
180
 
169
181
  print("pkg: %s" % str(pkg))
170
182
 
@@ -219,9 +231,12 @@ class Session(object):
219
231
 
220
232
 
221
233
  def getPackage(self, spec : PackageSpec) -> Package:
234
+ # Obtain the active package definition
222
235
  pkg_spec = self._pkg_spec_s[-1]
223
236
  pkg_def = self._pkg_def_m[pkg_spec]
224
237
 
238
+ print("spec: %s ; _pkg_def_m: %s" % (str(spec), str(self._pkg_def_m.keys())))
239
+
225
240
  # Need a stack to track which package we are currently in
226
241
  # Need a map to get a concrete package from a name with parameterization
227
242
 
@@ -261,30 +276,49 @@ class Session(object):
261
276
  pkg = base.mkPackage(self, spec.params)
262
277
  self._pkg_m[spec] = pkg
263
278
  break
279
+
280
+ if pkg is None:
281
+ # Look in the set of registered packages
282
+ if spec.name in self._pkg_file_m.keys():
283
+ # Load the package
284
+ self._pkg_spec_s.append(spec)
285
+ pkg_def = self._load_package(
286
+ self._pkg_file_m[spec.name],
287
+ [])
288
+
289
+ self._pkg_spec_s.pop()
290
+
291
+ # The definition is now in the map, so recurse to create it
292
+ pkg = self.getPackage(spec)
293
+ else:
294
+ # Go search the package path
295
+ pass
296
+
297
+
264
298
 
265
299
  if pkg is None:
266
300
  raise Exception("Failed to find package %s from package %s" % (
267
301
  spec.name, pkg_def.name))
268
302
 
269
- # base_spec = PackageSpec(spec.name)
270
- # if not base_spec in self._pkg_def_m.keys():
271
- # # Template is not present. Go find it...
272
- #
273
- # # If not found...
274
- # raise Exception("Package %s not found" % spec.name)
275
-
276
303
  return pkg
277
304
 
278
305
  def getTaskCtor(self, spec : TaskSpec, pkg : PackageDef) -> 'TaskCtor':
279
306
  spec_e = spec.name.split(".")
280
307
  task_name = spec_e[-1]
281
- pkg_name = ".".join(spec_e[0:-1])
282
308
 
283
- try:
284
- pkg = self.getPackage(PackageSpec(pkg_name))
285
- except Exception as e:
286
- print("Failed to find package %s while looking for task %s" % (pkg_name, spec.name))
287
- raise e
309
+ if len(spec_e) == 1:
310
+ # Just have a task name. Use the current package
311
+ if len(self._pkg_s) == 0:
312
+ raise Exception("No package context for task %s" % spec.name)
313
+ pkg = self._pkg_s[-1]
314
+ else:
315
+ pkg_name = ".".join(spec_e[0:-1])
316
+
317
+ try:
318
+ pkg = self.getPackage(PackageSpec(pkg_name))
319
+ except Exception as e:
320
+ print("Failed to find package %s while looking for task %s" % (pkg_name, spec.name))
321
+ raise e
288
322
 
289
323
  return pkg.getTaskCtor(task_name)
290
324
 
dv_flow_mgr/task.py CHANGED
@@ -24,7 +24,7 @@ import json
24
24
  import asyncio
25
25
  import dataclasses as dc
26
26
  from pydantic import BaseModel
27
- from typing import Any, Dict, List, Tuple
27
+ from typing import Any, Callable, Dict, List, Tuple
28
28
  from .task_data import TaskData
29
29
  from .task_memento import TaskMemento
30
30
 
@@ -37,78 +37,32 @@ class TaskParams(BaseModel):
37
37
 
38
38
  @dc.dataclass
39
39
  class TaskCtor(object):
40
- def mkTaskParams(self) -> TaskParams:
41
- raise NotImplementedError()
42
-
43
- def setTaskParams(self, params : TaskParams, pvals : Dict[str,Any]):
44
- for p in pvals.keys():
45
- if not hasattr(params, p):
46
- raise Exception("Unsupported parameter: " + p)
47
- else:
48
- setattr(params, p, pvals[p])
49
-
50
- def mkTask(self, name : str, task_id : int, session : 'Session', params : TaskParams, depends : List['Task']) -> 'Task':
51
- raise NotImplementedError()
52
-
53
- @dc.dataclass
54
- class TaskCtorT(TaskCtor):
55
- TaskParamsT : TaskParams
56
- TaskT : 'Task'
57
-
58
- def mkTaskParams(self) -> TaskParams:
59
- return self.TaskParamsT()
60
-
61
- def mkTask(self, name : str, task_id : int, session : 'Session', params : TaskParams, depends : List['Task']) -> 'Task':
62
- task = self.TaskT(
63
- name=name,
64
- task_id=task_id,
65
- session=session,
66
- params=params,
67
- depends=depends)
68
- task.depends.extend(depends)
69
- return task
70
-
71
- @dc.dataclass
72
- class TaskParamCtor(object):
73
- base : TaskCtor
74
- params : Dict[str,Any]
75
- basedir : str
76
- depend_refs : List['TaskSpec']
77
-
78
- def mkTaskParams(self) -> TaskParams:
79
- params = self.base.mkTaskParams()
80
- self.base.setTaskParams(params, self.params)
81
- return params
82
-
83
- def setTaskParams(self, params : Dict, pvals : Dict[str,Any]):
84
- pass
85
-
86
- def mkTask(self, name : str, task_id : int, session : 'Session', params : Dict, depends : List['Task']) -> 'Task':
87
- task = self.base.mkTask(
88
- name=name,
89
- task_id=task_id,
90
- session=session,
91
- params=params,
92
- depends=depends)
93
- task.basedir = self.basedir
94
- task.depend_refs.extend(self.depend_refs)
95
- return task
96
-
97
- @dc.dataclass
98
- class PackageTaskCtor(TaskCtor):
99
- name : str
100
- pkg : 'Package'
101
-
102
- def mkTaskParams(self, params : Dict) -> Dict:
103
- return self.pkg.mkTaskParams(self.name, params)
104
- def mkTask(self, name : str, task_id : int, session : 'Session', params : Dict, depends : List['Task']) -> 'Task':
105
- return self.pkg.mkTask(self.name, task_id, session, params, depends)
40
+ task_ctor : Callable
41
+ param_ctor : Callable
42
+ params : Dict[str,Any] = None
43
+ srcdir : str = None
44
+ depends : List[TaskSpec] = dc.field(default_factory=list)
45
+
46
+ def copy(self):
47
+ return TaskCtor(
48
+ task_ctor=self.task_ctor,
49
+ param_ctor=self.param_ctor,
50
+ params=self.params,
51
+ srcdir=self.srcdir,
52
+ depends=self.depends.copy())
53
+
54
+ def mkParams(self):
55
+ print("mkParams: %s" % str(self.params))
56
+ ret = self.param_ctor()
57
+ if self.params is not None:
58
+ for k,v in self.params.items():
59
+ setattr(ret, k, v)
60
+ return ret
106
61
 
107
62
  @dc.dataclass
108
63
  class Task(object):
109
64
  """Executable view of a task"""
110
65
  name : str
111
- task_id : int
112
66
  session : 'Session'
113
67
  params : TaskParams
114
68
  basedir : str
@@ -155,14 +109,6 @@ class Task(object):
155
109
  # Merge filesets. A fileset with the same
156
110
  print("deps_o: %s" % str(deps_o))
157
111
 
158
- # First, merge the dep maps of all the inputs
159
- deps_m = self.depends[0].output.deps.copy()
160
- for deps in map(lambda d: d.deps, self.depends[1:]):
161
- for k,v in deps.items():
162
- if k in deps_m:
163
- deps_m[k].add(v)
164
- else:
165
- deps_m[k] = set(v)
166
112
 
167
113
  print("deps_m: %s" % str(deps_m))
168
114
 
@@ -182,8 +128,7 @@ class Task(object):
182
128
  # Mark the source of this data as being this task
183
129
  input.src = self.name
184
130
 
185
- if not os.path.isdir(self.rundir):
186
- os.makedirs(self.rundir)
131
+ self.init_rundir()
187
132
 
188
133
  result = await self.run(input)
189
134
 
@@ -202,9 +147,7 @@ class Task(object):
202
147
  result = self.getOutput()
203
148
 
204
149
  # Write-back the memento, if specified
205
- if self.memento is not None:
206
- with open(os.path.join(self.rundir, "memento.json"), "w") as fp:
207
- fp.write(self.memento.model_dump_json(indent=2))
150
+ self.save_memento()
208
151
 
209
152
  self.running = False
210
153
 
@@ -214,6 +157,15 @@ class Task(object):
214
157
  async def run(self, input : TaskData) -> TaskData:
215
158
  raise NotImplementedError("TaskImpl.run() not implemented")
216
159
 
160
+ def init_rundir(self):
161
+ if not os.path.isdir(self.rundir):
162
+ os.makedirs(self.rundir)
163
+
164
+ def save_memento(self):
165
+ if self.memento is not None:
166
+ with open(os.path.join(self.rundir, "memento.json"), "w") as fp:
167
+ fp.write(self.memento.model_dump_json(indent=2))
168
+
217
169
  def setOutput(self, output : TaskData):
218
170
  self.output_set = True
219
171
  output.src = self.name
dv_flow_mgr/task_data.py CHANGED
@@ -24,6 +24,7 @@ import pydantic.dataclasses as dc
24
24
  from pydantic import BaseModel
25
25
  from typing import Any, Dict, Set, List, Tuple
26
26
  from .fileset import FileSet
27
+ from toposort import toposort
27
28
 
28
29
  class TaskDataParamOpE(enum.Enum):
29
30
  Set = enum.auto()
@@ -32,18 +33,25 @@ class TaskDataParamOpE(enum.Enum):
32
33
  PathAppend = enum.auto()
33
34
  PathPrepend = enum.auto()
34
35
 
36
+ class TaskDataParamKindE(enum.Enum):
37
+ String = enum.auto()
38
+ FilePath = enum.auto()
39
+ SearchPath = enum.auto()
40
+ List = enum.auto()
41
+
35
42
  class TaskDataParamOp(BaseModel):
36
43
  op : TaskDataParamOpE
37
44
  value : Any
38
45
 
39
46
  class TaskDataParam(BaseModel):
40
- value : Any
47
+ kind : TaskDataParamKindE
41
48
  ops : List[TaskDataParamOp] = dc.Field(default_factory=list)
42
49
 
43
50
  class TaskData(BaseModel):
44
51
  src : str = None
45
- params : Dict[str,Any] = dc.Field(default_factory=dict)
52
+ params : Dict[str,TaskDataParam] = dc.Field(default_factory=dict)
46
53
  deps : Dict[str,Set[str]] = dc.Field(default_factory=dict)
54
+ filesets : List[FileSet] = dc.Field(default_factory=list)
47
55
  changed : bool = False
48
56
 
49
57
  def hasParam(self, name: str) -> bool:
@@ -56,16 +64,27 @@ class TaskData(BaseModel):
56
64
  self.params[name] = value
57
65
 
58
66
  def addFileSet(self, fs : FileSet):
59
- fs.src = self.src
60
- if "filesets" not in self.params:
61
- self.params["filesets"] = []
62
- self.params["filesets"].append(fs)
67
+ self.filesets.append(fs)
63
68
 
64
- def getFileSets(self, type=None) -> List[FileSet]:
69
+ def getFileSets(self, type=None, order=True) -> List[FileSet]:
65
70
  ret = []
66
71
 
67
- if "filesets" in self.params:
68
- for fs in self.params["filesets"]:
72
+ if order:
73
+ # The deps map specifies task dependencies
74
+
75
+ candidate_fs = []
76
+ for fs in self.filesets:
77
+ if type is None or fs.type in type:
78
+ candidate_fs.append(fs)
79
+
80
+ order = toposort(self.deps)
81
+
82
+ for order_s in order:
83
+ for fs in candidate_fs:
84
+ if fs.src in order_s:
85
+ ret.append(fs)
86
+ else:
87
+ for fs in self.filesets:
69
88
  if type is None or fs.type in type:
70
89
  ret.append(fs)
71
90
 
@@ -80,15 +99,171 @@ class TaskData(BaseModel):
80
99
  ret.changed = self.changed
81
100
  return ret
82
101
 
83
- def merge(self, other):
84
- for k,v in other.params.items():
85
- if k not in self.params:
86
- if hasattr(v, "copy"):
87
- self.params[k] = v.copy()
102
+ def setParamVal(self, name: str, kind : TaskDataParamKindE, value: Any):
103
+ if name not in self.params:
104
+ self.params[name] = TaskDataParam(kind=kind)
105
+ self.params[name].ops.append(TaskDataParamOp(op=TaskDataParamOpE.Set, value=value))
106
+
107
+ def getParamVal(self, name: str) -> Any:
108
+ if name not in self.params.keys():
109
+ raise Exception("No such parameter: %s" % name)
110
+ param = self.params[name]
111
+ value = param.ops[0].value
112
+
113
+ if len(param.ops) > 1:
114
+ for op in param.ops[1:]:
115
+ if op.op == TaskDataParamOpE.Append:
116
+ if isinstance(value, list):
117
+ value.extend(op.value)
118
+ else:
119
+ value += op.value
120
+ elif op.op == TaskDataParamOpE.Prepend:
121
+ if isinstance(value, list):
122
+ for nv in op.value:
123
+ value.insert(0, nv)
124
+ else:
125
+ value = op.value + value
126
+ elif op.op == TaskDataParamOpE.PathAppend:
127
+ if isinstance(value, list):
128
+ value = ":".join(value)
129
+ value = value + ":" + op.value
130
+ elif op.op == TaskDataParamOpE.PathPrepend:
131
+ if isinstance(value, list):
132
+ value = ":".join(value)
133
+ value = op.value + ":" + value
134
+
135
+ return value
136
+
137
+ @staticmethod
138
+ def merge(incoming : List['TaskData'], local : 'TaskData' = None) -> 'TaskData':
139
+ """Merges incoming data with local settings and produces an output"""
140
+
141
+ # Deal with the dependency trees first
142
+ output = TaskData()
143
+
144
+ # First, merge the dep maps of all the inputs
145
+ output.deps = incoming[0].deps.copy()
146
+ for deps in map(lambda i: i.deps, incoming[1:]):
147
+ for k,v in deps.items():
148
+ if k not in output.deps:
149
+ output.deps[k] = []
150
+ for vi in v:
151
+ if vi not in output.deps[k]:
152
+ output.deps[k].append(v)
153
+
154
+ # Process filesets
155
+ for inp in incoming:
156
+ for fs in inp.filesets:
157
+ exists = False
158
+ for fs_o in output.filesets:
159
+ if fs_o.name == fs.name and fs_o.src == fs.src:
160
+ exists = True
161
+ break
162
+ if not exists:
163
+ output.addFileSet(fs.model_copy())
164
+
165
+ # Now, deal with parameters
166
+ # Find collisions first
167
+ colliding_keys = set()
168
+ passthrough_keys = set()
169
+
170
+ for i in incoming:
171
+ for k in i.params.keys():
172
+ if k in passthrough_keys:
173
+ colliding_keys.add(k)
174
+ else:
175
+ passthrough_keys.add(k)
176
+
177
+ # Now, removes those that are locally set
178
+ local_set_params = set()
179
+ if local is not None:
180
+ for k,v in local.params.items():
181
+ if len(v.ops) == 1 and v.ops[0].op == TaskDataParamOpE.Set:
182
+ local_set_params.add(k)
183
+ # If are setting locally, it's not passthrough
184
+ passthrough_keys.remove(k)
185
+ if k in colliding_keys:
186
+ colliding_keys.remove(k)
187
+
188
+ # Construct the passthrough set by removing
189
+ # colliding entries and those that we will set locally
190
+ for k in colliding_keys:
191
+ if k in passthrough_keys:
192
+ passthrough_keys.remove(k)
193
+
194
+ # For the remaining keys, check for conflicts by
195
+ # confirming that the last 'set' in each incoming parameter
196
+ # are equal
197
+ for k in colliding_keys:
198
+ value = None
199
+ for i,inp in enumerate(incoming):
200
+ value_i = None
201
+ param = inp.params[k]
202
+ if len(param.ops) == 1:
203
+ value_i = param.ops[0].value
204
+ else:
205
+ # Iterate in reverse over the operations
206
+ for op in param.ops[::-1]:
207
+ if op.op == TaskDataParamOpE.Set:
208
+ value_i = op.value
209
+ break
210
+ if not i:
211
+ value = value_i
212
+ else:
213
+ if value != value_i:
214
+ raise Exception("Parameter %s has conflicting values (%s %s)" % (
215
+ k,
216
+ str(value),
217
+ value(value_i)))
218
+
219
+
220
+ # Now, we need to construct the result
221
+ # - copy over passthrough parameters
222
+ # - add locally-set parameters
223
+ # - for others
224
+ # - Apply full list for first input
225
+ # - Apply all beyond the last 'set' operation for others
226
+ for k in passthrough_keys:
227
+ # Find an input that has the parameter
228
+ for inp in incoming:
229
+ if k in inp.params:
230
+ break
231
+ # Find the value of the param
232
+ param = inp.params[k]
233
+
234
+ if len(param.ops) == 1:
235
+ output.params[k] = TaskDataParam(kind=param.kind)
236
+ output.params[k].ops.append(param.ops[0])
237
+ else:
238
+ for op in param.ops[::-1]:
239
+ if op.op == TaskDataParamOpE.Set:
240
+ output.params[k] = TaskDataParam(kind=param.kind)
241
+ output.params[k].ops.append(op)
242
+ break
243
+ for k in local_set_params:
244
+ output.params[k] = local.params[k].model_copy()
245
+
246
+ for k in colliding_keys:
247
+ value = None
248
+ for i,inp in enumerate(incoming):
249
+ # Find the last location that performs a 'set'
250
+ last_set_i = -1
251
+ param = inp.params[k]
252
+ if len(param.ops) == 1:
253
+ last_set_i = 0
254
+ else:
255
+ # Iterate in reverse over the operations
256
+ for j,op in enumerate(param.ops[::-1]):
257
+ if op.op == TaskDataParamOpE.Set:
258
+ last_set_i = j
259
+ break
260
+
261
+ if not i:
262
+ # Copy the full list, including the last 'set'
263
+ output.params[k].ops = param.param[last_set_i:].copy()
88
264
  else:
89
- self.params[k] = v
90
- elif hasattr(self.params[k], "merge"):
91
- self.params[k].merge(v)
92
- elif self.params[k] != v:
93
- raise Exception("Parameter %s has conflicting values" % k)
265
+ # append any additional directives
266
+ if last_set_i+1 < len(param.ops):
267
+ output.params[k].extend(param.ops[last_set_i+1:])
94
268
 
269
+ return output
dv_flow_mgr/task_def.py CHANGED
@@ -28,11 +28,13 @@ from .task import Task
28
28
  class TaskSpec(object):
29
29
  name : str
30
30
 
31
-
32
31
  class TaskDef(BaseModel):
33
32
  """Holds definition information (ie the YAML view) for a task"""
34
33
  name : str
35
- type : Union[str,TaskSpec] = dc.Field(default_factory=list)
34
+ # type : Union[str,TaskSpec] = dc.Field(default_factory=list)
35
+ uses : str = dc.Field(default=None)
36
+ pyclass : str = dc.Field(default=None)
37
+ doc : str = dc.Field(default=None)
36
38
  depends : List[Union[str,TaskSpec]] = dc.Field(default_factory=list, alias="needs")
37
39
  params: Dict[str,Any] = dc.Field(default_factory=dict, alias="with")
38
40
 
@@ -0,0 +1,62 @@
1
+ # import os
2
+ # import sys
3
+ # import glob
4
+ # import fnmatch
5
+ # import importlib
6
+ # import pydantic.dataclasses as dc
7
+ # from ..package import TaskCtor
8
+ from ..task import Task
9
+ from ..task_data import TaskData
10
+ # from ..task_memento import TaskMemento
11
+ # from typing import List, Tuple
12
+ # import dataclasses as dc
13
+ # from ..package_def import Package
14
+
15
+ # class TaskPyClass(Task):
16
+ # pyclass : str = None
17
+
18
+ # async def run(self, input : TaskData) -> TaskData:
19
+
20
+ # if self.srcdir not in sys.path:
21
+ # sys.path.insert(0, self.srcdir)
22
+
23
+ # print("sys.path: %s" % str(sys.path), flush=True)
24
+ # idx = self.params.pyclass.rfind('.')
25
+ # modname = self.params.pyclass[:idx]
26
+ # clsname = self.params.pyclass[idx+1:]
27
+
28
+ # if os.path.isfile(os.path.join(self.basedir, "my_module.py")):
29
+ # print("my_module.py exists", flush=True)
30
+ # else:
31
+ # print("my_module.py does not exist", flush=True)
32
+
33
+ # if modname in sys.modules.keys():
34
+ # module = sys.modules[modname]
35
+ # else:
36
+ # try:
37
+ # print("modname=%s" % modname, flush=True)
38
+ # module = importlib.import_module(modname)
39
+ # except ModuleNotFoundError as e:
40
+ # print("Module not found: %s syspath=%s" % (str(e), str(sys.path)), flush=True)
41
+ # raise e
42
+
43
+ # cls = getattr(module, clsname)
44
+
45
+ # obj = cls(self.name, self.task_id, self.session, self.basedir, srcdir=self.srcdir)
46
+
47
+ # return await obj.run(input)
48
+
49
+
50
+ # class TaskPyClassMemento(TaskMemento):
51
+ # pass
52
+
53
+ class TaskNull(Task):
54
+ async def run(self, input : TaskData) -> TaskData:
55
+ return input
56
+
57
+ # @dc.dataclass
58
+ # class PackageBuiltin(Package):
59
+
60
+ # def __post_init__(self):
61
+ # print("PackageBuiltin::__post_init__", flush=True)
62
+ # self.tasks["PyClass"] = TaskPyClass()
@@ -1,5 +1,68 @@
1
1
 
2
- from dv_flow_mgr import Task
2
+ import os
3
+ import fnmatch
4
+ import glob
5
+ import pydantic.dataclasses as dc
6
+ from typing import List, Tuple
7
+ from dv_flow_mgr import Task, TaskData, TaskMemento
8
+ from dv_flow_mgr import FileSet as _FileSet
9
+
10
+ class TaskFileSetMemento(TaskMemento):
11
+ files : List[Tuple[str,float]] = dc.Field(default_factory=list)
3
12
 
4
13
  class FileSet(Task):
14
+
15
+ async def run(self, input : TaskData) -> TaskData:
16
+ print("TaskFileSet run: %s: basedir=%s, base=%s type=%s include=%s" % (
17
+ self.name,
18
+ self.srcdir,
19
+ self.params.base, self.params.type, str(self.params.include)
20
+ ))
21
+
22
+
23
+ ex_memento = self.getMemento(TaskFileSetMemento)
24
+ memento = TaskFileSetMemento()
25
+
26
+ if self.params is not None:
27
+ glob_root = os.path.join(self.srcdir, self.params.base)
28
+
29
+ print("glob_root: %s" % glob_root)
30
+
31
+ fs = _FileSet(
32
+ src=self.name,
33
+ type=self.params.type,
34
+ basedir=glob_root)
35
+ print("glob_root: %s" % glob_root)
36
+
37
+ if not isinstance(self.params.include, list):
38
+ self.params.include = [self.params.include]
39
+
40
+ included_files = []
41
+ for pattern in self.params.include:
42
+ print("pattern: %s" % pattern)
43
+ included_files.extend(glob.glob(os.path.join(glob_root, pattern), recursive=False))
44
+
45
+ for file in included_files:
46
+ if not any(glob.fnmatch.fnmatch(file, os.path.join(glob_root, pattern)) for pattern in self.params.exclude):
47
+ memento.files.append((file, os.path.getmtime(os.path.join(glob_root, file))))
48
+ fs.files.append(file[len(glob_root):])
49
+
50
+ # Check to see if the filelist or fileset have changed
51
+ # Only bother doing this if the upstream task data has not changed
52
+ if ex_memento is not None and not input.changed:
53
+ ex_memento.files.sort(key=lambda x: x[0])
54
+ memento.files.sort(key=lambda x: x[0])
55
+ print("ex_memento.files: %s" % str(ex_memento.files))
56
+ print("memento.files: %s" % str(memento.files))
57
+ input.changed = ex_memento != memento
58
+ else:
59
+ input.changed = True
60
+
61
+ self.setMemento(memento)
62
+
63
+ if fs is not None:
64
+ input.addFileSet(fs)
65
+
66
+ return input
67
+
5
68
  pass
@@ -0,0 +1,26 @@
1
+
2
+ package:
3
+ name: std
4
+
5
+ tasks:
6
+ - name: Message
7
+ pyclass: message.Message
8
+ with:
9
+ msg:
10
+ type: str
11
+ value: ""
12
+ - name: FileSet
13
+ pyclass: fileset.FileSet
14
+ with:
15
+ base:
16
+ type: str
17
+ value: ""
18
+ type:
19
+ type: str
20
+ value: ""
21
+ include:
22
+ type: str
23
+ value: ""
24
+ exclude:
25
+ type: str
26
+ value: ""
@@ -0,0 +1,7 @@
1
+
2
+ from dv_flow_mgr import Task, TaskData
3
+
4
+ class Message(Task):
5
+ async def run(self, input : TaskData) -> TaskData:
6
+ print("%s: %s" % (self.name, self.params.msg))
7
+ return input
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dv-flow-mgr
3
- Version: 0.0.1.12761553329a1
3
+ Version: 0.0.1.12849118090a1
4
4
  Summary: DV Flow Manager is a build system for silicon design
5
5
  Author-email: Matthew Ballance <matt.ballance@gmail.com>
6
6
  License: Apache License
@@ -1,19 +1,20 @@
1
1
  dv_flow_mgr/__init__.py,sha256=GLfYxZONxJNNUplfmlsXrcycJ2VhTc8gvXN6qdlH7kg,97
2
2
  dv_flow_mgr/__main__.py,sha256=4xXA4XCqLELlIak6bs-H6rRHp2URucipHPvCiVdR01c,513
3
- dv_flow_mgr/fileset.py,sha256=89knoTNsDF85QJwrctLiM6ryqHS1oxW25Gd5jgpX-IQ,1123
3
+ dv_flow_mgr/fileset.py,sha256=FNvC5sU2ArxJ0OO3v8dXTv8zX-bZ5t0a0ljne0fQQ1o,1150
4
4
  dv_flow_mgr/flow.py,sha256=UdgJOIqBS2wTRpO-sNWCCqO9oQFxDfGPGVD0r42aTrA,1562
5
5
  dv_flow_mgr/fragment_def.py,sha256=p5i6ONtBWlDHTBFsduu3Z36_76Bn8PCIylp_xoZ7jfQ,1552
6
- dv_flow_mgr/package.py,sha256=jFhXgKDEYSRfs_VRUjKY4C7KScQfmrNLEXQgtWKaBfI,2767
7
- dv_flow_mgr/package_def.py,sha256=Js8bZj8bSmJQ6Wk9xAhIBJScNGJOHNbXNhw_zkDVU5w,3834
6
+ dv_flow_mgr/package.py,sha256=k6gaDun9mJeGwGsFP5YOYOaFLmKb3KyPZy5wGRgJd_E,1965
7
+ dv_flow_mgr/package_def.py,sha256=YZkXk6alkP6hjGT61EAglAHPEE-Rax8VCybIYbQ9NJs,8040
8
8
  dv_flow_mgr/package_import_spec.py,sha256=bStPa727wAKMcixydVY1Ht6ylzXsSMy2K31HWPXhc9k,921
9
9
  dv_flow_mgr/parameters.py,sha256=kUjRss5VtMMz5eL3-Z_M6BS-wFs7MhQu3ANXO54UPo0,896
10
- dv_flow_mgr/session.py,sha256=sYRyTojsfPFZOGu4mz9yNTX6ESNye0i5szgj5QVE-Pk,10802
11
- dv_flow_mgr/task.py,sha256=ZTuSulc6lBLcTByD6gQjmbaJqqL1c1mlVtP84iLrLbE,7973
12
- dv_flow_mgr/task_data.py,sha256=32rgwPxSthIjNbzbXK2sZoPo6Jedio_L54Qibu65fYk,2962
13
- dv_flow_mgr/task_def.py,sha256=Yjw1Q-bnbi7Ct5X1PriMMNJ6i4TBVFWrO8BTnipz9hg,1532
10
+ dv_flow_mgr/session.py,sha256=oj75khugWLdBe7ouAmQX9sVAMRZi6O4CdLquokIkXDY,12163
11
+ dv_flow_mgr/task.py,sha256=AIz9YI-K3riv1EQ-hArL6eYsfbWjHMoN07Ao4mRdiW8,6146
12
+ dv_flow_mgr/task_data.py,sha256=8AcFCxVmv9agzUDXnwUNJImpfp0N4jB1IHodIECf-0k,9735
13
+ dv_flow_mgr/task_def.py,sha256=w0V5FSHi8D6PFyq_3j_smO3fmJp-cAMpigWT0yU4-GY,1654
14
14
  dv_flow_mgr/task_memento.py,sha256=C7VTQpBhDEoYuDmE6YTM-6TLMLnqHp6Y0Vat1aTgtCs,1096
15
15
  dv_flow_mgr/cmds/cmd_run.py,sha256=ii48_jdxCxh4MOJCeKKX66Cui4IvQQy_RrJEIixsph8,733
16
16
  dv_flow_mgr/share/flow.json,sha256=lNmZex9NXkYbyb2aZseQfUOkV9CMyfH0iLODEI7EPBw,5096
17
+ dv_flow_mgr/tasklib/builtin_pkg.py,sha256=ps5SkO7s29xVzyeEw96qbMAg9Dk6UPErH2Lc87foIcs,1883
17
18
  dv_flow_mgr/tasklib/hdl/sim/mti_pkg.py,sha256=l_jerOEQrVSmxT6qJx0PlsorZsK037Y-arQvWzdm9No,348
18
19
  dv_flow_mgr/tasklib/hdl/sim/mti_task_sim_image.py,sha256=U1XADvEGaH412n_JujS1Maz08GJ07UL6mRT4B4qihZQ,2229
19
20
  dv_flow_mgr/tasklib/hdl/sim/mti_task_sim_run.py,sha256=QkKYOs0OhQ42F5bUbfCsezE4L50TfiIm7lVflVY4On8,1290
@@ -26,14 +27,16 @@ dv_flow_mgr/tasklib/hdl/sim/vl_task_sim_image.py,sha256=QJP1gjclUSYfq-B9NIA46C1E
26
27
  dv_flow_mgr/tasklib/hdl/sim/vlt_pkg.py,sha256=Pu9mWAR93kiTNT8GZJrX-VDjMOfr9P5ddcSPt1BElQQ,413
27
28
  dv_flow_mgr/tasklib/hdl/sim/vlt_task_sim_image.py,sha256=9RDJ3r37C4tA4PIa7tDUYYgRpIh19up-Y9HTOwKxr0I,1564
28
29
  dv_flow_mgr/tasklib/hdl/sim/vlt_task_sim_run.py,sha256=RxPaE1RSRi2QdadrZp2J3lZqTt6EnLjCnk__uPAV6_o,1199
29
- dv_flow_mgr/tasklib/std/fileset.py,sha256=u88qjKQpIip_eETk0k6J1AGxk9blKlqAqYinHGKwco8,61
30
+ dv_flow_mgr/tasklib/std/fileset.py,sha256=m7b85A81pNkkq_HM7XdDO5zdkoGvbbOCaTckEH666jg,2350
31
+ dv_flow_mgr/tasklib/std/flow.dv,sha256=d3jFrZnTqdI2IZB-lSOs5tjA_beLtuLHgjusytf2_nA,391
32
+ dv_flow_mgr/tasklib/std/message.py,sha256=O61dY00bQPCd7Ak5o-KGXh3C7a9wfZcbU_D2fiBudd8,193
30
33
  dv_flow_mgr/tasklib/std/pkg_std.py,sha256=ERDd515qtetQaldk9opUH19MyNwo8nP4-GLz-ke-5gE,370
31
34
  dv_flow_mgr/tasklib/std/std.dfs,sha256=An2NUvvWaR9xI4U-kRQ4NN6U-GhX6GQmd3mNyRkW4NQ,68
32
35
  dv_flow_mgr/tasklib/std/task_fileset.py,sha256=ZBG4e2gsTsRMsr_FhhzIoE3J7YwYQZ9HigpxpGePd74,3145
33
36
  dv_flow_mgr/tasklib/std/task_null.py,sha256=bQH1HN4r-TtdUaJxfnmvoqKZSY4MWO48eK7z_yngdNU,599
34
- dv_flow_mgr-0.0.1.12761553329a1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
35
- dv_flow_mgr-0.0.1.12761553329a1.dist-info/METADATA,sha256=vQplCReVKB6eEKc6Ys_XM6nEB_T1m7Mw35b8ZV_5FzM,13252
36
- dv_flow_mgr-0.0.1.12761553329a1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
37
- dv_flow_mgr-0.0.1.12761553329a1.dist-info/entry_points.txt,sha256=VHxIon5AeHCtK73fYb1veDImAGwv76hrtBiuZePmzhI,51
38
- dv_flow_mgr-0.0.1.12761553329a1.dist-info/top_level.txt,sha256=FGuhGIxN1FgD_YBkPcDq05fTxZcwaX9G_O8FFBvrTk4,12
39
- dv_flow_mgr-0.0.1.12761553329a1.dist-info/RECORD,,
37
+ dv_flow_mgr-0.0.1.12849118090a1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
38
+ dv_flow_mgr-0.0.1.12849118090a1.dist-info/METADATA,sha256=Si9x7uqsXSWlCl6c6eQUJ8CpIi3duJ_4rBM2QkPTJ_Q,13252
39
+ dv_flow_mgr-0.0.1.12849118090a1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
40
+ dv_flow_mgr-0.0.1.12849118090a1.dist-info/entry_points.txt,sha256=VHxIon5AeHCtK73fYb1veDImAGwv76hrtBiuZePmzhI,51
41
+ dv_flow_mgr-0.0.1.12849118090a1.dist-info/top_level.txt,sha256=FGuhGIxN1FgD_YBkPcDq05fTxZcwaX9G_O8FFBvrTk4,12
42
+ dv_flow_mgr-0.0.1.12849118090a1.dist-info/RECORD,,