dv-flow-mgr 0.0.1.12664275031a1__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 (35) hide show
  1. dv_flow_mgr/__init__.py +6 -0
  2. dv_flow_mgr/__main__.py +21 -0
  3. dv_flow_mgr/cmds/cmd_run.py +28 -0
  4. dv_flow_mgr/fileset.py +31 -0
  5. dv_flow_mgr/flow.py +59 -0
  6. dv_flow_mgr/fragment_def.py +46 -0
  7. dv_flow_mgr/package.py +79 -0
  8. dv_flow_mgr/package_def.py +97 -0
  9. dv_flow_mgr/parameters.py +27 -0
  10. dv_flow_mgr/session.py +247 -0
  11. dv_flow_mgr/share/flow.json +172 -0
  12. dv_flow_mgr/task.py +225 -0
  13. dv_flow_mgr/task_data.py +77 -0
  14. dv_flow_mgr/task_def.py +45 -0
  15. dv_flow_mgr/task_memento.py +34 -0
  16. dv_flow_mgr/tasklib/hdl/sim/mti_pkg.py +11 -0
  17. dv_flow_mgr/tasklib/hdl/sim/mti_task_sim_image.py +69 -0
  18. dv_flow_mgr/tasklib/hdl/sim/mti_task_sim_run.py +47 -0
  19. dv_flow_mgr/tasklib/hdl/sim/pkg_hdl_sim.py +8 -0
  20. dv_flow_mgr/tasklib/hdl/sim/task_sim_image.py +16 -0
  21. dv_flow_mgr/tasklib/hdl/sim/vl_task_sim_image.py +72 -0
  22. dv_flow_mgr/tasklib/hdl/sim/vlt_pkg.py +14 -0
  23. dv_flow_mgr/tasklib/hdl/sim/vlt_task_sim_image.py +50 -0
  24. dv_flow_mgr/tasklib/hdl/sim/vlt_task_sim_run.py +45 -0
  25. dv_flow_mgr/tasklib/std/fileset.py +5 -0
  26. dv_flow_mgr/tasklib/std/pkg_std.py +15 -0
  27. dv_flow_mgr/tasklib/std/std.dfs +7 -0
  28. dv_flow_mgr/tasklib/std/task_fileset.py +89 -0
  29. dv_flow_mgr/tasklib/std/task_null.py +26 -0
  30. dv_flow_mgr-0.0.1.12664275031a1.dist-info/LICENSE +201 -0
  31. dv_flow_mgr-0.0.1.12664275031a1.dist-info/METADATA +211 -0
  32. dv_flow_mgr-0.0.1.12664275031a1.dist-info/RECORD +35 -0
  33. dv_flow_mgr-0.0.1.12664275031a1.dist-info/WHEEL +5 -0
  34. dv_flow_mgr-0.0.1.12664275031a1.dist-info/entry_points.txt +2 -0
  35. dv_flow_mgr-0.0.1.12664275031a1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,6 @@
1
+
2
+ from .package_def import *
3
+ from .session import *
4
+ from .task import *
5
+ from .task_data import *
6
+
@@ -0,0 +1,21 @@
1
+
2
+ import argparse
3
+ from .cmds.cmd_run import CmdRun
4
+
5
+ def get_parser():
6
+ parser = argparse.ArgumentParser(description='dv_flow_mgr')
7
+ subparsers = parser.add_subparsers(required=True)
8
+
9
+ run_parser = subparsers.add_parser('run', help='run a flow')
10
+ run_parser.add_argument("tasks", nargs='+', help="tasks to run")
11
+ run_parser.set_defaults(func=CmdRun())
12
+
13
+ return parser
14
+
15
+ def main():
16
+ parser = get_parser()
17
+ args = parser.parse_args()
18
+ args.func(args)
19
+
20
+ if __name__ == "__main__":
21
+ main()
@@ -0,0 +1,28 @@
1
+ import asyncio
2
+ import os
3
+ from ..session import Session
4
+
5
+ class CmdRun(object):
6
+
7
+ def __call__(self, args):
8
+ srcdir = os.getcwd()
9
+ rundir = os.path.join(srcdir, "rundir")
10
+
11
+ session = Session(srcdir, rundir)
12
+
13
+ package = session.load(srcdir)
14
+
15
+ graphs = []
16
+ for task in args.tasks:
17
+ if task.find(".") == -1:
18
+ task = package.name + "." + task
19
+ subgraph = session.mkTaskGraph(task)
20
+ graphs.append(subgraph)
21
+
22
+ awaitables = [subgraph.do_run() for subgraph in graphs]
23
+ print("%d awaitables" % len(awaitables))
24
+
25
+ out = asyncio.get_event_loop().run_until_complete(asyncio.gather(*awaitables))
26
+
27
+ print("out: %s" % str(out))
28
+
dv_flow_mgr/fileset.py ADDED
@@ -0,0 +1,31 @@
1
+ #****************************************************************************
2
+ #* fileset.py
3
+ #*
4
+ #* Copyright 2023 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 pydantic.dataclasses as dc
23
+ from pydantic import BaseModel
24
+ from typing import Any, Dict, List, Tuple
25
+
26
+ class FileSet(BaseModel):
27
+ src : str
28
+ type : str
29
+ basedir : str
30
+ files : List[str] = dc.Field(default_factory=list)
31
+ params : Dict[str,str] = dc.Field(default_factory=dict)
dv_flow_mgr/flow.py ADDED
@@ -0,0 +1,59 @@
1
+ #****************************************************************************
2
+ #* flow.py
3
+ #*
4
+ #* Copyright 2023 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
+ from pydantic import BaseModel, Field
23
+ from typing import ClassVar
24
+ #from .task import Task
25
+
26
+ class Flow(BaseModel):
27
+ # - Parameters are user-facing
28
+ # - Any implementation data must be stored elsewhere, such that it isn't
29
+ # checked for equality...
30
+ name : str
31
+ description : str = Field(None)
32
+
33
+
34
+ @classmethod
35
+ def mk(cls, *args, **kwargs):
36
+ pass
37
+
38
+ async def my_method(self):
39
+ return Task(a,b,c)(self, input)
40
+
41
+ #@extend(target)
42
+ #class FlowExt(object):
43
+ # pass
44
+
45
+
46
+ class Flow2(Flow):
47
+ description : str = "abc"
48
+
49
+ async def my_method(self):
50
+ super().my_method()
51
+
52
+ f = Flow2(name="foo")
53
+
54
+ #for d in dir(f):
55
+ # if not d.startswith("_"):
56
+ # print("%s: %s" % (d, str(getattr(f, d))))
57
+
58
+
59
+
@@ -0,0 +1,46 @@
1
+ #****************************************************************************
2
+ #* fragment_def.py
3
+ #*
4
+ #* Copyright 2023 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 pydantic.dataclasses as dc
23
+ import json
24
+ from pydantic import BaseModel
25
+ from typing import Any, Dict, List
26
+ from .flow import Flow
27
+ from .package import Package
28
+ from .package_def import PackageImportSpec
29
+ from .task import TaskParamCtor
30
+ from .task_def import TaskDef, TaskSpec
31
+
32
+ class FragmentDef(BaseModel):
33
+ tasks : List[TaskDef] = dc.Field(default_factory=list)
34
+ imports : List[(str|PackageImportSpec)] = dc.Field(default_factory=list, alias="imports")
35
+ fragments: List[str] = dc.Field(default_factory=list)
36
+
37
+ basedir : str = None
38
+
39
+ def getTask(self, name : str) -> 'TaskDef':
40
+ for t in self.tasks:
41
+ if t.name == name:
42
+ return t
43
+
44
+ def apply(self, session, pkg : Package):
45
+ pass
46
+
dv_flow_mgr/package.py ADDED
@@ -0,0 +1,79 @@
1
+ #****************************************************************************
2
+ #* package.py
3
+ #*
4
+ #* Copyright 2023 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 dataclasses as dc
23
+ import json
24
+ from pydantic import BaseModel
25
+ from typing import Any, Callable, Dict, List, Tuple
26
+ from .flow import Flow
27
+ from .task import TaskParams, TaskCtor
28
+ from .task_def import TaskDef
29
+
30
+ class PackageAcc(object):
31
+ pkg_spec : 'PackageSpec'
32
+ session : 'Session'
33
+ pkg : 'Package' = None
34
+
35
+ def getPackage(self) -> 'Package':
36
+ if self.pkg is None:
37
+ self.pkg = self.session.getPackage(self.pkg_spec)
38
+ return self.pkg
39
+
40
+ @dc.dataclass
41
+ class Package(object):
42
+ name : str
43
+ params : Dict[str,Any] = dc.field(default_factory=dict)
44
+ # Package holds constructors for tasks
45
+ # - Dict holds the default parameters for the task
46
+ tasks : Dict[str,TaskCtor] = dc.field(default_factory=dict)
47
+ imports : List['PackageAcc'] = dc.field(default_factory=list)
48
+
49
+ def getPackage(self, name : str) -> 'Package':
50
+ for p in self.imports:
51
+ if p.name == name:
52
+ return p.getPackage()
53
+
54
+ def getTaskCtor(self, name : str) -> TaskCtor:
55
+ return self.tasks[name]
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
+ def __hash__(self):
78
+ return hash(self.fullname())
79
+
@@ -0,0 +1,97 @@
1
+ #****************************************************************************
2
+ #* package_def.py
3
+ #*
4
+ #* Copyright 2023 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 pydantic.dataclasses as dc
23
+ import json
24
+ from pydantic import BaseModel
25
+ from typing import Any, Dict, List
26
+ from .flow import Flow
27
+ from .package import Package
28
+ from .task import TaskParamCtor
29
+ from .task_def import TaskDef, TaskSpec
30
+
31
+ @dc.dataclass
32
+ class PackageSpec(object):
33
+ name : str
34
+ params : Dict[str,Any] = dc.Field(default_factory=dict)
35
+ _fullname : str = None
36
+
37
+ def get_fullname(self) -> str:
38
+ if self._fullname is None:
39
+ if len(self.params) != 0:
40
+ self._fullname = "%s%s}" % (
41
+ self.name,
42
+ json.dumps(self.params, separators=(',', ':')))
43
+ else:
44
+ self._fullname = self.name
45
+ return self._fullname
46
+
47
+ def __hash__(self):
48
+ return hash(self.get_fullname())
49
+
50
+ def __eq__(self, value):
51
+ return isinstance(value, PackageSpec) and value.get_fullname() == self.get_fullname()
52
+
53
+ @dc.dataclass
54
+ class PackageImportSpec(PackageSpec):
55
+ path : str = dc.Field(default=None, alias="from")
56
+ alias : str = dc.Field(default=None, alias="as")
57
+
58
+ class PackageDef(BaseModel):
59
+ name : str
60
+ params : Dict[str,Any] = dc.Field(default_factory=dict)
61
+ type : List[PackageSpec] = dc.Field(default_factory=list)
62
+ tasks : List[TaskDef] = dc.Field(default_factory=list)
63
+ imports : List[PackageImportSpec] = dc.Field(default_factory=list)
64
+ fragments: List[str] = dc.Field(default_factory=list)
65
+
66
+ # import_m : Dict['PackageSpec','Package'] = dc.Field(default_factory=dict)
67
+
68
+ basedir : str = None
69
+
70
+ def getTask(self, name : str) -> 'TaskDef':
71
+ for t in self.tasks:
72
+ if t.name == name:
73
+ return t
74
+
75
+ def mkPackage(self, session, params : Dict[str,Any] = None) -> 'Package':
76
+ ret = Package(self.name)
77
+
78
+ for task in self.tasks:
79
+ if task.type is not None:
80
+ # Find package (not package_def) that implements this task
81
+ # Insert an indirect reference to that tasks's constructor
82
+
83
+ # Only call getTaskCtor if the task is in a different package
84
+ task_t = task.type if isinstance(task.type, TaskSpec) else TaskSpec(task.type)
85
+ ctor_t = session.getTaskCtor(task_t, self)
86
+
87
+ ctor_t = TaskParamCtor(
88
+ base=ctor_t,
89
+ params=task.params,
90
+ basedir=self.basedir,
91
+ depend_refs=task.depends)
92
+ else:
93
+ raise Exception("")
94
+ ret.tasks[task.name] = ctor_t
95
+
96
+ return ret
97
+
@@ -0,0 +1,27 @@
1
+ #****************************************************************************
2
+ #* parameters.py
3
+ #*
4
+ #* Copyright 2023 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
+
23
+ class Parameters(object):
24
+
25
+ def __init__(self):
26
+ pass
27
+
dv_flow_mgr/session.py ADDED
@@ -0,0 +1,247 @@
1
+ #****************************************************************************
2
+ #* session.py
3
+ #*
4
+ #* Copyright 2023 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 yaml
25
+ import dataclasses as dc
26
+ from typing import Any, Callable, Dict, List
27
+ from .package import Package
28
+ from .package_def import PackageDef, PackageSpec
29
+ from .task import Task,TaskSpec
30
+
31
+ @dc.dataclass
32
+ class Session(object):
33
+ """Manages the running of a flow"""
34
+
35
+ srcdir : str
36
+ rundir : str
37
+
38
+ # Search path for .dfs files
39
+ package_path : List[str] = dc.field(default_factory=list)
40
+ package : PackageDef = None
41
+ create_subprocess : Callable = asyncio.create_subprocess_exec
42
+ _root_dir : str = None
43
+ _pkg_s : List[Package] = dc.field(default_factory=list)
44
+ _pkg_m : Dict[PackageSpec,Package] = dc.field(default_factory=dict)
45
+ _pkg_spec_s : List[PackageDef] = dc.field(default_factory=list)
46
+ _pkg_def_m : Dict[str,PackageDef] = dc.field(default_factory=dict)
47
+ _task_list : List[Task] = dc.field(default_factory=list)
48
+ _task_m : Dict[TaskSpec,Task] = dc.field(default_factory=dict)
49
+ _task_id : int = 0
50
+ _rundir_s : List[str] = dc.field(default_factory=list)
51
+
52
+ def __post_init__(self):
53
+ from .tasklib.std.pkg_std import PackageStd
54
+ from .tasklib.hdl.sim.vlt_pkg import VltPackage
55
+ from .tasklib.hdl.sim.mti_pkg import MtiPackage
56
+ self._pkg_m[PackageSpec("std")] = PackageStd("std")
57
+ self._pkg_m[PackageSpec("hdl.sim.mti")] = MtiPackage("hdl.sim.mti")
58
+ self._pkg_m[PackageSpec("hdl.sim.vlt")] = VltPackage("hdl.sim.vlt")
59
+
60
+ def load(self, root : str):
61
+ if not os.path.isdir(root):
62
+ raise Exception("Root directory %s does not exist" % root)
63
+
64
+ if not os.path.isfile(os.path.join(root, "flow.yaml")):
65
+ raise Exception("No root flow file")
66
+
67
+ self._root_dir = os.path.dirname(root)
68
+ self.package = self._load_package(os.path.join(root, "flow.yaml"), [])
69
+
70
+ return self.package
71
+
72
+ def mkTaskGraph(self, task : str) -> Task:
73
+ self._pkg_s.clear()
74
+ self._task_m.clear()
75
+
76
+ self._rundir_s.clear()
77
+ self._rundir_s.append(self.rundir)
78
+
79
+ return self._mkTaskGraph(task)
80
+
81
+ def _mkTaskGraph(self, task : str, params : dict = None) -> Task:
82
+
83
+ elems = task.split(".")
84
+
85
+ pkg_name = ".".join(elems[0:-1])
86
+ task_name = elems[-1]
87
+
88
+ self._rundir_s.append(os.path.join(self._rundir_s[-1], pkg_name, task_name))
89
+
90
+ if pkg_name == "":
91
+ if len(self._pkg_spec_s) == 0:
92
+ raise Exception("No package context for %s" % task)
93
+ pkg_spec = self._pkg_spec_s[-1]
94
+ else:
95
+ pkg_spec = PackageSpec(pkg_name)
96
+
97
+ self._pkg_spec_s.append(pkg_spec)
98
+ pkg = self.getPackage(pkg_spec)
99
+
100
+ self._pkg_s.append(pkg)
101
+
102
+ #task_def = pkg.getTask(task_name)
103
+
104
+ depends = []
105
+
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
+ # 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 = self._rundir_s[-1]
119
+
120
+ for i,d in enumerate(task.depend_refs):
121
+ if d in self._task_m.keys():
122
+ task.depends.append(self._task_m[d])
123
+ else:
124
+ print("mkTaskGraph: %s" % d)
125
+ task.depends.append(self._mkTaskGraph(d))
126
+
127
+ self._task_m[task.name] = task
128
+
129
+ self._pkg_s.pop()
130
+ self._pkg_spec_s.pop()
131
+ self._rundir_s.pop()
132
+
133
+ return task
134
+
135
+ def mkTaskId(self, task : 'Task') -> int:
136
+ self._task_id += 1
137
+ # TODO: save task <-> id map for later?
138
+ return self._task_id
139
+
140
+ async def run(self, task : str) -> 'TaskData':
141
+ impl = self.mkTaskGraph(task)
142
+ return await impl.do_run()
143
+
144
+ def _load_package(self, root : str, file_s : List[str]) -> PackageDef:
145
+ if root in file_s:
146
+ raise Exception("Recursive file processing @ %s: %s" % (root, ",".join(self._file_s)))
147
+ file_s.append(root)
148
+ ret = None
149
+ with open(root, "r") as fp:
150
+ doc = yaml.load(fp, Loader=yaml.FullLoader)
151
+ if "package" not in doc.keys():
152
+ raise Exception("Missing 'package' key in %s" % root)
153
+ pkg = PackageDef(**(doc["package"]))
154
+ pkg.basedir = os.path.dirname(root)
155
+
156
+ # for t in pkg.tasks:
157
+ # t.basedir = os.path.dirname(root)
158
+
159
+ if not len(self._pkg_spec_s):
160
+ self._pkg_spec_s.append(PackageSpec(pkg.name))
161
+ self._pkg_def_m[PackageSpec(pkg.name)] = pkg
162
+ 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))
165
+ else:
166
+ # TODO: merge content
167
+ self._pkg_spec_s.append(PackageSpec(pkg.name))
168
+
169
+ print("pkg: %s" % str(pkg))
170
+
171
+ # TODO: read in fragments
172
+
173
+ self._pkg_spec_s.pop()
174
+ file_s.pop()
175
+
176
+ return pkg
177
+
178
+ def getPackage(self, spec : PackageSpec) -> Package:
179
+ pkg_spec = self._pkg_spec_s[-1]
180
+ pkg_def = self._pkg_def_m[pkg_spec]
181
+
182
+ # Need a stack to track which package we are currently in
183
+ # Need a map to get a concrete package from a name with parameterization
184
+
185
+ # Note: _pkg_m needs to be context specific, such that imports from
186
+ # one package don't end up visible in another
187
+ if spec in self._pkg_m.keys():
188
+ pkg = self._pkg_m[spec]
189
+ elif spec in self._pkg_def_m.keys():
190
+ pkg = self._pkg_def_m[spec].mkPackage(self)
191
+ self._pkg_m[spec] = pkg
192
+ else:
193
+ pkg = None
194
+ print("imports: %s" % str(pkg_def.imports))
195
+ for imp in pkg_def.imports:
196
+ print("imp: %s" % str(imp))
197
+ if imp.alias is not None and imp.alias == spec.name:
198
+ # Found the alias name. Just need to get an instance of this package
199
+ tgt_pkg_spec = PackageSpec(imp.name)
200
+ if tgt_pkg_spec in self._pkg_m.keys():
201
+ pkg = self._pkg_m[tgt_pkg_spec]
202
+ elif tgt_pkg_spec in self._pkg_def_m.keys():
203
+ base = self._pkg_def_m[tgt_pkg_spec]
204
+ pkg = base.mkPackage(self, spec.params)
205
+ self._pkg_m[spec] = pkg
206
+ elif imp.path is not None:
207
+ # See if we can load the package
208
+ print("TODO: load referenced package")
209
+ else:
210
+ raise Exception("Import alias %s not found" % imp.name)
211
+ break
212
+ else:
213
+ # Need to compare the spec with the full import spec
214
+ imp_spec = PackageSpec(imp.name)
215
+ # TODO: set parameters
216
+ if imp_spec == spec:
217
+ base = self._pkg_def_m[PackageSpec(spec.name)]
218
+ pkg = base.mkPackage(self, spec.params)
219
+ self._pkg_m[spec] = pkg
220
+ break
221
+
222
+ if pkg is None:
223
+ raise Exception("Failed to find package %s from package %s" % (
224
+ spec.name, pkg_def.name))
225
+
226
+ # base_spec = PackageSpec(spec.name)
227
+ # if not base_spec in self._pkg_def_m.keys():
228
+ # # Template is not present. Go find it...
229
+ #
230
+ # # If not found...
231
+ # raise Exception("Package %s not found" % spec.name)
232
+
233
+ return pkg
234
+
235
+ def getTaskCtor(self, spec : TaskSpec, pkg : PackageDef) -> 'TaskCtor':
236
+ spec_e = spec.name.split(".")
237
+ task_name = spec_e[-1]
238
+ pkg_name = ".".join(spec_e[0:-1])
239
+
240
+ try:
241
+ pkg = self.getPackage(PackageSpec(pkg_name))
242
+ except Exception as e:
243
+ print("Failed to find package %s while looking for task %s" % (pkg_name, spec.name))
244
+ raise e
245
+
246
+ return pkg.getTaskCtor(task_name)
247
+