siliconcompiler 0.34.0__py3-none-any.whl → 0.34.2__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.
- siliconcompiler/__init__.py +14 -2
- siliconcompiler/_metadata.py +1 -1
- siliconcompiler/apps/_common.py +1 -1
- siliconcompiler/apps/sc.py +1 -1
- siliconcompiler/apps/sc_issue.py +1 -1
- siliconcompiler/apps/sc_remote.py +3 -3
- siliconcompiler/apps/sc_show.py +3 -3
- siliconcompiler/apps/utils/replay.py +4 -4
- siliconcompiler/checklist.py +203 -2
- siliconcompiler/constraints/__init__.py +17 -0
- siliconcompiler/constraints/asic_component.py +378 -0
- siliconcompiler/constraints/asic_floorplan.py +449 -0
- siliconcompiler/constraints/asic_pins.py +489 -0
- siliconcompiler/constraints/asic_timing.py +517 -0
- siliconcompiler/core.py +31 -249
- siliconcompiler/data/templates/email/general.j2 +3 -3
- siliconcompiler/data/templates/email/summary.j2 +1 -1
- siliconcompiler/data/templates/issue/README.txt +1 -1
- siliconcompiler/data/templates/report/sc_report.j2 +7 -7
- siliconcompiler/dependencyschema.py +10 -174
- siliconcompiler/design.py +325 -114
- siliconcompiler/flowgraph.py +63 -15
- siliconcompiler/library.py +133 -0
- siliconcompiler/metric.py +94 -72
- siliconcompiler/metrics/__init__.py +7 -0
- siliconcompiler/metrics/asic.py +245 -0
- siliconcompiler/metrics/fpga.py +220 -0
- siliconcompiler/optimizer/vizier.py +2 -2
- siliconcompiler/package/__init__.py +138 -35
- siliconcompiler/package/github.py +6 -10
- siliconcompiler/packageschema.py +256 -12
- siliconcompiler/pathschema.py +226 -0
- siliconcompiler/pdk.py +5 -5
- siliconcompiler/project.py +459 -0
- siliconcompiler/remote/client.py +18 -12
- siliconcompiler/remote/server.py +2 -2
- siliconcompiler/report/dashboard/cli/__init__.py +6 -6
- siliconcompiler/report/dashboard/cli/board.py +3 -3
- siliconcompiler/report/dashboard/web/components/__init__.py +5 -5
- siliconcompiler/report/dashboard/web/components/flowgraph.py +4 -4
- siliconcompiler/report/dashboard/web/components/graph.py +2 -2
- siliconcompiler/report/dashboard/web/state.py +1 -1
- siliconcompiler/report/dashboard/web/utils/__init__.py +5 -5
- siliconcompiler/report/html_report.py +1 -1
- siliconcompiler/report/report.py +4 -4
- siliconcompiler/report/summary_table.py +2 -2
- siliconcompiler/report/utils.py +5 -5
- siliconcompiler/scheduler/docker.py +4 -10
- siliconcompiler/scheduler/run_node.py +4 -8
- siliconcompiler/scheduler/scheduler.py +18 -24
- siliconcompiler/scheduler/schedulernode.py +161 -143
- siliconcompiler/scheduler/send_messages.py +3 -3
- siliconcompiler/scheduler/slurm.py +5 -3
- siliconcompiler/scheduler/taskscheduler.py +10 -8
- siliconcompiler/schema/__init__.py +0 -2
- siliconcompiler/schema/baseschema.py +148 -26
- siliconcompiler/schema/editableschema.py +14 -6
- siliconcompiler/schema/journal.py +23 -15
- siliconcompiler/schema/namedschema.py +30 -4
- siliconcompiler/schema/parameter.py +34 -19
- siliconcompiler/schema/parametertype.py +2 -0
- siliconcompiler/schema/parametervalue.py +198 -15
- siliconcompiler/schema/schema_cfg.py +18 -14
- siliconcompiler/schema_obj.py +5 -3
- siliconcompiler/tool.py +591 -179
- siliconcompiler/tools/__init__.py +2 -0
- siliconcompiler/tools/builtin/_common.py +5 -5
- siliconcompiler/tools/builtin/concatenate.py +5 -5
- siliconcompiler/tools/builtin/minimum.py +4 -4
- siliconcompiler/tools/builtin/mux.py +4 -4
- siliconcompiler/tools/builtin/nop.py +4 -4
- siliconcompiler/tools/builtin/verify.py +7 -7
- siliconcompiler/tools/execute/exec_input.py +1 -1
- siliconcompiler/tools/genfasm/genfasm.py +1 -6
- siliconcompiler/tools/openroad/_apr.py +5 -1
- siliconcompiler/tools/openroad/antenna_repair.py +1 -1
- siliconcompiler/tools/openroad/macro_placement.py +1 -1
- siliconcompiler/tools/openroad/power_grid.py +1 -1
- siliconcompiler/tools/openroad/scripts/common/procs.tcl +5 -0
- siliconcompiler/tools/opensta/timing.py +26 -3
- siliconcompiler/tools/slang/__init__.py +2 -2
- siliconcompiler/tools/surfer/__init__.py +0 -0
- siliconcompiler/tools/surfer/show.py +53 -0
- siliconcompiler/tools/surfer/surfer.py +30 -0
- siliconcompiler/tools/vpr/route.py +27 -14
- siliconcompiler/tools/vpr/vpr.py +23 -6
- siliconcompiler/tools/yosys/__init__.py +1 -1
- siliconcompiler/tools/yosys/scripts/procs.tcl +143 -0
- siliconcompiler/tools/yosys/{sc_synth_asic.tcl → scripts/sc_synth_asic.tcl} +4 -0
- siliconcompiler/tools/yosys/{sc_synth_fpga.tcl → scripts/sc_synth_fpga.tcl} +24 -77
- siliconcompiler/tools/yosys/syn_fpga.py +14 -0
- siliconcompiler/toolscripts/_tools.json +9 -13
- siliconcompiler/toolscripts/rhel9/install-vpr.sh +0 -2
- siliconcompiler/toolscripts/ubuntu22/install-surfer.sh +33 -0
- siliconcompiler/toolscripts/ubuntu24/install-surfer.sh +33 -0
- siliconcompiler/utils/__init__.py +2 -1
- siliconcompiler/utils/flowgraph.py +24 -23
- siliconcompiler/utils/issue.py +23 -29
- siliconcompiler/utils/logging.py +35 -6
- siliconcompiler/utils/showtools.py +6 -1
- {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/METADATA +15 -25
- {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/RECORD +109 -97
- siliconcompiler/schema/packageschema.py +0 -101
- siliconcompiler/tools/yosys/procs.tcl +0 -71
- siliconcompiler/toolscripts/rhel9/install-yosys-parmys.sh +0 -68
- siliconcompiler/toolscripts/ubuntu22/install-yosys-parmys.sh +0 -68
- siliconcompiler/toolscripts/ubuntu24/install-yosys-parmys.sh +0 -68
- /siliconcompiler/tools/yosys/{sc_lec.tcl → scripts/sc_lec.tcl} +0 -0
- /siliconcompiler/tools/yosys/{sc_screenshot.tcl → scripts/sc_screenshot.tcl} +0 -0
- /siliconcompiler/tools/yosys/{syn_strategies.tcl → scripts/syn_strategies.tcl} +0 -0
- {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/WHEEL +0 -0
- {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/entry_points.txt +0 -0
- {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/licenses/LICENSE +0 -0
- {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/top_level.txt +0 -0
|
@@ -11,6 +11,7 @@ from siliconcompiler import SiliconCompilerError
|
|
|
11
11
|
from siliconcompiler import utils
|
|
12
12
|
from siliconcompiler.flowgraph import RuntimeFlowgraph
|
|
13
13
|
|
|
14
|
+
from siliconcompiler.package import Resolver
|
|
14
15
|
from siliconcompiler.schema import Journal
|
|
15
16
|
|
|
16
17
|
from siliconcompiler.utils.logging import SCBlankLoggerFormatter
|
|
@@ -33,6 +34,7 @@ class TaskScheduler:
|
|
|
33
34
|
def __init__(self, chip, tasks):
|
|
34
35
|
self.__chip = chip
|
|
35
36
|
self.__logger = self.__chip.logger
|
|
37
|
+
self.__logger_console_handler = self.__chip._logger_console
|
|
36
38
|
self.__schema = self.__chip.schema
|
|
37
39
|
self.__flow = self.__schema.get("flowgraph", self.__chip.get('option', 'flow'),
|
|
38
40
|
field="schema")
|
|
@@ -73,13 +75,14 @@ class TaskScheduler:
|
|
|
73
75
|
if self.__record.get('status', step=step, index=index) != NodeStatus.PENDING:
|
|
74
76
|
continue
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
with tasks[(step, index)].runtime():
|
|
79
|
+
threads = tasks[(step, index)].threads
|
|
77
80
|
if not threads:
|
|
78
81
|
threads = self.__max_threads
|
|
79
82
|
threads = max(1, min(threads, self.__max_threads))
|
|
80
83
|
|
|
81
84
|
task = {
|
|
82
|
-
"name": f"{step}{index}",
|
|
85
|
+
"name": f"{step}/{index}",
|
|
83
86
|
"inputs": runtime.get_node_inputs(step, index, record=self.__record),
|
|
84
87
|
"proc": None,
|
|
85
88
|
"parent_pipe": None,
|
|
@@ -93,7 +96,6 @@ class TaskScheduler:
|
|
|
93
96
|
|
|
94
97
|
task["parent_pipe"], pipe = multiprocessing.Pipe()
|
|
95
98
|
task["node"].set_queue(pipe, self.__log_queue)
|
|
96
|
-
task["node"].init_state() # reinit access to remove holdover access
|
|
97
99
|
|
|
98
100
|
task["proc"] = multiprocessing.Process(target=task["node"].run)
|
|
99
101
|
init_funcs.add(task["node"].init)
|
|
@@ -108,9 +110,9 @@ class TaskScheduler:
|
|
|
108
110
|
multiprocessing.freeze_support()
|
|
109
111
|
|
|
110
112
|
# Handle logs across threads
|
|
111
|
-
log_listener = QueueListener(self.__log_queue, self.
|
|
112
|
-
console_format = self.
|
|
113
|
-
self.
|
|
113
|
+
log_listener = QueueListener(self.__log_queue, self.__logger_console_handler)
|
|
114
|
+
console_format = self.__logger_console_handler.formatter
|
|
115
|
+
self.__logger_console_handler.setFormatter(SCBlankLoggerFormatter())
|
|
114
116
|
log_listener.start()
|
|
115
117
|
|
|
116
118
|
# Update dashboard before run begins
|
|
@@ -130,7 +132,7 @@ class TaskScheduler:
|
|
|
130
132
|
|
|
131
133
|
# Cleanup logger
|
|
132
134
|
log_listener.stop()
|
|
133
|
-
self.
|
|
135
|
+
self.__logger_console_handler.setFormatter(console_format)
|
|
134
136
|
|
|
135
137
|
def __run_loop(self):
|
|
136
138
|
self.__startTimes = {None: time.time()}
|
|
@@ -199,7 +201,7 @@ class TaskScheduler:
|
|
|
199
201
|
packages = info["parent_pipe"].recv()
|
|
200
202
|
if isinstance(packages, dict):
|
|
201
203
|
for package, path in packages.items():
|
|
202
|
-
self.__chip
|
|
204
|
+
Resolver.set_cache(self.__chip, package, path)
|
|
203
205
|
except: # noqa E722
|
|
204
206
|
pass
|
|
205
207
|
|
|
@@ -5,7 +5,6 @@ from .editableschema import EditableSchema
|
|
|
5
5
|
from .baseschema import BaseSchema
|
|
6
6
|
from .cmdlineschema import CommandLineSchema
|
|
7
7
|
from .namedschema import NamedSchema
|
|
8
|
-
from .packageschema import PackageSchema
|
|
9
8
|
|
|
10
9
|
from .schema_cfg import SCHEMA_VERSION
|
|
11
10
|
|
|
@@ -16,7 +15,6 @@ __all__ = [
|
|
|
16
15
|
"EditableSchema",
|
|
17
16
|
"CommandLineSchema",
|
|
18
17
|
"NamedSchema",
|
|
19
|
-
"PackageSchema",
|
|
20
18
|
"Parameter",
|
|
21
19
|
"Scope",
|
|
22
20
|
"PerNode",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# SC dependencies outside of its directory, since it may be used by tool drivers
|
|
5
5
|
# that have isolated Python environments.
|
|
6
6
|
|
|
7
|
+
import contextlib
|
|
7
8
|
import copy
|
|
8
9
|
|
|
9
10
|
try:
|
|
@@ -21,7 +22,7 @@ except ModuleNotFoundError:
|
|
|
21
22
|
|
|
22
23
|
import os.path
|
|
23
24
|
|
|
24
|
-
from .parameter import Parameter
|
|
25
|
+
from .parameter import Parameter, NodeValue
|
|
25
26
|
from .journal import Journal
|
|
26
27
|
|
|
27
28
|
|
|
@@ -32,11 +33,21 @@ class BaseSchema:
|
|
|
32
33
|
'''
|
|
33
34
|
|
|
34
35
|
def __init__(self):
|
|
35
|
-
# Data storage for the schema
|
|
36
36
|
self.__manifest = {}
|
|
37
37
|
self.__default = None
|
|
38
38
|
self.__journal = Journal()
|
|
39
39
|
self.__parent = self
|
|
40
|
+
self.__active = None
|
|
41
|
+
self.__key = None
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def _keypath(self):
|
|
45
|
+
'''
|
|
46
|
+
Returns the key to the current section of the schema
|
|
47
|
+
'''
|
|
48
|
+
if self.__parent is self:
|
|
49
|
+
return tuple()
|
|
50
|
+
return tuple([*self.__parent._keypath, self.__key])
|
|
40
51
|
|
|
41
52
|
def _from_dict(self, manifest, keypath, version=None):
|
|
42
53
|
'''
|
|
@@ -106,6 +117,9 @@ class BaseSchema:
|
|
|
106
117
|
return gzip.open(filepath, mode="rt" if is_read else "wt", encoding="utf-8")
|
|
107
118
|
return open(filepath, mode="r" if is_read else "w", encoding="utf-8")
|
|
108
119
|
|
|
120
|
+
def __format_key(self, *key):
|
|
121
|
+
return f"[{','.join([*self._keypath, *key])}]"
|
|
122
|
+
|
|
109
123
|
def read_manifest(self, filepath):
|
|
110
124
|
"""
|
|
111
125
|
Reads a manifest from disk and replaces the current data with the data in the file.
|
|
@@ -217,21 +231,23 @@ class BaseSchema:
|
|
|
217
231
|
|
|
218
232
|
try:
|
|
219
233
|
require_leaf = True
|
|
234
|
+
insert_defaults = False
|
|
220
235
|
if field == 'schema':
|
|
221
236
|
require_leaf = False
|
|
237
|
+
insert_defaults = True
|
|
222
238
|
param = self.__search(
|
|
223
239
|
*keypath,
|
|
224
|
-
insert_defaults=
|
|
240
|
+
insert_defaults=insert_defaults,
|
|
225
241
|
use_default=True,
|
|
226
242
|
require_leaf=require_leaf)
|
|
227
243
|
if field == 'schema':
|
|
228
244
|
if isinstance(param, Parameter):
|
|
229
|
-
raise ValueError(f"
|
|
245
|
+
raise ValueError(f"{self.__format_key(*keypath)} is a complete keypath")
|
|
230
246
|
self.__journal.record("get", keypath, field=field, step=step, index=index)
|
|
231
247
|
param.__journal = self.__journal.get_child(*keypath)
|
|
232
248
|
return param
|
|
233
249
|
except KeyError:
|
|
234
|
-
raise KeyError(f"
|
|
250
|
+
raise KeyError(f"{self.__format_key(*keypath)} is not a valid keypath")
|
|
235
251
|
if field is None:
|
|
236
252
|
return param
|
|
237
253
|
|
|
@@ -240,7 +256,7 @@ class BaseSchema:
|
|
|
240
256
|
self.__journal.record("get", keypath, field=field, step=step, index=index)
|
|
241
257
|
return get_ret
|
|
242
258
|
except Exception as e:
|
|
243
|
-
new_msg = f"error while accessing
|
|
259
|
+
new_msg = f"error while accessing {self.__format_key(*keypath)}: {e.args[0]}"
|
|
244
260
|
e.args = (new_msg, *e.args[1:])
|
|
245
261
|
raise e
|
|
246
262
|
|
|
@@ -274,7 +290,7 @@ class BaseSchema:
|
|
|
274
290
|
try:
|
|
275
291
|
param = self.__search(*keypath, insert_defaults=True)
|
|
276
292
|
except KeyError:
|
|
277
|
-
raise KeyError(f"
|
|
293
|
+
raise KeyError(f"{self.__format_key(*keypath)} is not a valid keypath")
|
|
278
294
|
|
|
279
295
|
try:
|
|
280
296
|
set_ret = param.set(value, field=field, clobber=clobber,
|
|
@@ -282,9 +298,10 @@ class BaseSchema:
|
|
|
282
298
|
if set_ret:
|
|
283
299
|
self.__journal.record("set", keypath, value=value, field=field,
|
|
284
300
|
step=step, index=index)
|
|
301
|
+
self.__process_active(param, set_ret)
|
|
285
302
|
return set_ret
|
|
286
303
|
except Exception as e:
|
|
287
|
-
new_msg = f"error while setting
|
|
304
|
+
new_msg = f"error while setting {self.__format_key(*keypath)}: {e.args[0]}"
|
|
288
305
|
e.args = (new_msg, *e.args[1:])
|
|
289
306
|
raise e
|
|
290
307
|
|
|
@@ -317,16 +334,17 @@ class BaseSchema:
|
|
|
317
334
|
try:
|
|
318
335
|
param = self.__search(*keypath, insert_defaults=True)
|
|
319
336
|
except KeyError:
|
|
320
|
-
raise KeyError(f"
|
|
337
|
+
raise KeyError(f"{self.__format_key(*keypath)} is not a valid keypath")
|
|
321
338
|
|
|
322
339
|
try:
|
|
323
340
|
add_ret = param.add(value, field=field, step=step, index=index)
|
|
324
341
|
if add_ret:
|
|
325
342
|
self.__journal.record("add", keypath, value=value, field=field,
|
|
326
343
|
step=step, index=index)
|
|
344
|
+
self.__process_active(param, add_ret)
|
|
327
345
|
return add_ret
|
|
328
346
|
except Exception as e:
|
|
329
|
-
new_msg = f"error while adding to
|
|
347
|
+
new_msg = f"error while adding to {self.__format_key(*keypath)}: {e.args[0]}"
|
|
330
348
|
e.args = (new_msg, *e.args[1:])
|
|
331
349
|
raise e
|
|
332
350
|
|
|
@@ -359,13 +377,13 @@ class BaseSchema:
|
|
|
359
377
|
try:
|
|
360
378
|
param = self.__search(*keypath, use_default=True)
|
|
361
379
|
except KeyError:
|
|
362
|
-
raise KeyError(f"
|
|
380
|
+
raise KeyError(f"{self.__format_key(*keypath)} is not a valid keypath")
|
|
363
381
|
|
|
364
382
|
try:
|
|
365
383
|
param.unset(step=step, index=index)
|
|
366
384
|
self.__journal.record("unset", keypath, step=step, index=index)
|
|
367
385
|
except Exception as e:
|
|
368
|
-
new_msg = f"error while unsetting
|
|
386
|
+
new_msg = f"error while unsetting {self.__format_key(*keypath)}: {e.args[0]}"
|
|
369
387
|
e.args = (new_msg, *e.args[1:])
|
|
370
388
|
raise e
|
|
371
389
|
|
|
@@ -385,7 +403,7 @@ class BaseSchema:
|
|
|
385
403
|
try:
|
|
386
404
|
key_param = self.__search(*search_path, require_leaf=False)
|
|
387
405
|
except KeyError:
|
|
388
|
-
raise KeyError(f"
|
|
406
|
+
raise KeyError(f"{self.__format_key(*keypath)} is not a valid keypath")
|
|
389
407
|
|
|
390
408
|
if removal_key not in key_param.__manifest:
|
|
391
409
|
return
|
|
@@ -455,13 +473,13 @@ class BaseSchema:
|
|
|
455
473
|
try:
|
|
456
474
|
key_param = self.__search(*keypath, require_leaf=False)
|
|
457
475
|
except KeyError:
|
|
458
|
-
raise KeyError(f"
|
|
476
|
+
raise KeyError(f"{self.__format_key(*keypath)} is not a valid keypath")
|
|
459
477
|
if isinstance(key_param, Parameter):
|
|
460
478
|
return tuple()
|
|
461
479
|
else:
|
|
462
480
|
key_param = self
|
|
463
481
|
|
|
464
|
-
return tuple(key_param.__manifest.keys())
|
|
482
|
+
return tuple(sorted(key_param.__manifest.keys()))
|
|
465
483
|
|
|
466
484
|
def allkeys(self, *keypath, include_default=True):
|
|
467
485
|
'''
|
|
@@ -556,8 +574,33 @@ class BaseSchema:
|
|
|
556
574
|
else:
|
|
557
575
|
schema_copy.__parent = schema_copy
|
|
558
576
|
|
|
577
|
+
if key:
|
|
578
|
+
schema_copy.__key = key[-1]
|
|
579
|
+
|
|
559
580
|
return schema_copy
|
|
560
581
|
|
|
582
|
+
def _find_files_search_paths(self, keypath, step, index):
|
|
583
|
+
"""
|
|
584
|
+
Returns a list of paths to search during find files.
|
|
585
|
+
|
|
586
|
+
Args:
|
|
587
|
+
keypath (str): final component of keypath
|
|
588
|
+
step (str): Step name.
|
|
589
|
+
index (str): Index name.
|
|
590
|
+
"""
|
|
591
|
+
return []
|
|
592
|
+
|
|
593
|
+
def _find_files_dataroot_resolvers(self):
|
|
594
|
+
"""
|
|
595
|
+
Returns a dictionary of path resolevrs data directory handling for find_files
|
|
596
|
+
|
|
597
|
+
Returns:
|
|
598
|
+
dictionary of str to resolver mapping
|
|
599
|
+
"""
|
|
600
|
+
if self.__parent is self:
|
|
601
|
+
return {}
|
|
602
|
+
return self.__parent._find_files_dataroot_resolvers()
|
|
603
|
+
|
|
561
604
|
def find_files(self, *keypath, missing_ok=False, step=None, index=None,
|
|
562
605
|
packages=None, collection_dir=None, cwd=None):
|
|
563
606
|
"""
|
|
@@ -593,10 +636,13 @@ class BaseSchema:
|
|
|
593
636
|
the schema.
|
|
594
637
|
"""
|
|
595
638
|
|
|
596
|
-
|
|
639
|
+
base_schema = self.get(*keypath[0:-1], field="schema")
|
|
640
|
+
|
|
641
|
+
param = base_schema.get(keypath[-1], field=None)
|
|
597
642
|
paramtype = param.get(field='type')
|
|
598
643
|
if 'file' not in paramtype and 'dir' not in paramtype:
|
|
599
|
-
raise TypeError(
|
|
644
|
+
raise TypeError(
|
|
645
|
+
f'Cannot find files on {self.__format_key(*keypath)}, must be a path type')
|
|
600
646
|
|
|
601
647
|
paths = param.get(field=None, step=step, index=index)
|
|
602
648
|
|
|
@@ -616,23 +662,26 @@ class BaseSchema:
|
|
|
616
662
|
cwd = os.getcwd()
|
|
617
663
|
|
|
618
664
|
if packages is None:
|
|
619
|
-
packages =
|
|
665
|
+
packages = base_schema._find_files_dataroot_resolvers()
|
|
620
666
|
|
|
621
667
|
resolved_paths = []
|
|
668
|
+
root_search_paths = base_schema._find_files_search_paths(keypath[-1], step, index)
|
|
622
669
|
for path in paths:
|
|
623
|
-
search_paths =
|
|
670
|
+
search_paths = root_search_paths.copy()
|
|
624
671
|
|
|
625
672
|
package = path.get(field="package")
|
|
626
673
|
if package:
|
|
627
674
|
if package not in packages:
|
|
628
|
-
raise ValueError(f"Resolver for {package} not provided"
|
|
675
|
+
raise ValueError(f"Resolver for {package} not provided: "
|
|
676
|
+
f"{self.__format_key(*keypath)}")
|
|
629
677
|
package_path = packages[package]
|
|
630
678
|
if isinstance(package_path, str):
|
|
631
679
|
search_paths.append(os.path.abspath(package_path))
|
|
632
680
|
elif callable(package_path):
|
|
633
681
|
search_paths.append(package_path())
|
|
634
682
|
else:
|
|
635
|
-
raise TypeError(f"Resolver for {package} is not a recognized type"
|
|
683
|
+
raise TypeError(f"Resolver for {package} is not a recognized type: "
|
|
684
|
+
f"{self.__format_key(*keypath)}")
|
|
636
685
|
else:
|
|
637
686
|
if cwd:
|
|
638
687
|
search_paths.append(os.path.abspath(cwd))
|
|
@@ -645,10 +694,11 @@ class BaseSchema:
|
|
|
645
694
|
if not missing_ok:
|
|
646
695
|
if package:
|
|
647
696
|
raise FileNotFoundError(
|
|
648
|
-
f'Could not find "{path.get()}" in {package}
|
|
697
|
+
f'Could not find "{path.get()}" in {package} '
|
|
698
|
+
f'{self.__format_key(*keypath)}')
|
|
649
699
|
else:
|
|
650
700
|
raise FileNotFoundError(
|
|
651
|
-
f'Could not find "{path.get()}"
|
|
701
|
+
f'Could not find "{path.get()}" {self.__format_key(*keypath)}')
|
|
652
702
|
resolved_paths.append(resolved_path)
|
|
653
703
|
|
|
654
704
|
if not is_list:
|
|
@@ -716,10 +766,10 @@ class BaseSchema:
|
|
|
716
766
|
if index is None:
|
|
717
767
|
node_indicator = f" ({step})"
|
|
718
768
|
else:
|
|
719
|
-
node_indicator = f" ({step}{index})"
|
|
769
|
+
node_indicator = f" ({step}/{index})"
|
|
720
770
|
|
|
721
|
-
logger.error(f"Parameter
|
|
722
|
-
f"{check_file} is invalid")
|
|
771
|
+
logger.error(f"Parameter {self.__format_key(*keypath)}{node_indicator} "
|
|
772
|
+
f"path {check_file} is invalid")
|
|
723
773
|
|
|
724
774
|
return not error
|
|
725
775
|
|
|
@@ -736,3 +786,75 @@ class BaseSchema:
|
|
|
736
786
|
if self.__parent is self:
|
|
737
787
|
return self
|
|
738
788
|
return self.__parent._parent(root=root)
|
|
789
|
+
|
|
790
|
+
@contextlib.contextmanager
|
|
791
|
+
def _active(self, **kwargs):
|
|
792
|
+
'''
|
|
793
|
+
Use this context to temporarily set additional fields in :meth:`.set` and :meth:`.add`.
|
|
794
|
+
Additional fields can be specified which can be accessed by :meth:`._get_active`.
|
|
795
|
+
|
|
796
|
+
Args:
|
|
797
|
+
kwargs (dict of str): keyword arguments that are used for setting values
|
|
798
|
+
|
|
799
|
+
Example:
|
|
800
|
+
>>> with schema._active(package="lambdalib"):
|
|
801
|
+
... schema.set("file", "top.v")
|
|
802
|
+
Sets the file to top.v and associates lambdalib as the package.
|
|
803
|
+
'''
|
|
804
|
+
if self.__active:
|
|
805
|
+
orig_active = self.__active.copy()
|
|
806
|
+
else:
|
|
807
|
+
orig_active = None
|
|
808
|
+
|
|
809
|
+
if self.__active is None:
|
|
810
|
+
self.__active = {}
|
|
811
|
+
|
|
812
|
+
self.__active.update(kwargs)
|
|
813
|
+
try:
|
|
814
|
+
yield
|
|
815
|
+
finally:
|
|
816
|
+
self.__active = orig_active
|
|
817
|
+
|
|
818
|
+
def _get_active(self, field, defvalue=None):
|
|
819
|
+
'''
|
|
820
|
+
Get the value of a specific field.
|
|
821
|
+
|
|
822
|
+
Args:
|
|
823
|
+
field (str): if None, return the current active dictionary,
|
|
824
|
+
otherwise the value, if the field is not present, defvalue is returned.
|
|
825
|
+
defvalue (any): value to return if the field is not present.
|
|
826
|
+
'''
|
|
827
|
+
if self.__active is None:
|
|
828
|
+
return defvalue
|
|
829
|
+
|
|
830
|
+
if field is None:
|
|
831
|
+
return self.__active.copy()
|
|
832
|
+
|
|
833
|
+
return self.__active.get(field, defvalue)
|
|
834
|
+
|
|
835
|
+
def __process_active(self, param, nodevalues):
|
|
836
|
+
if not self.__active:
|
|
837
|
+
return
|
|
838
|
+
|
|
839
|
+
if not isinstance(nodevalues, (list, set, tuple)):
|
|
840
|
+
# Make everything a list
|
|
841
|
+
nodevalues = [nodevalues]
|
|
842
|
+
|
|
843
|
+
if not all([isinstance(v, NodeValue) for v in nodevalues]):
|
|
844
|
+
nodevalues = []
|
|
845
|
+
nodevalues_fields = []
|
|
846
|
+
else:
|
|
847
|
+
nodevalues_fields = nodevalues[0].fields
|
|
848
|
+
|
|
849
|
+
key_fields = ("copy", "lock")
|
|
850
|
+
|
|
851
|
+
for field, value in self.__active.items():
|
|
852
|
+
if field in key_fields:
|
|
853
|
+
param.set(value, field=field)
|
|
854
|
+
continue
|
|
855
|
+
|
|
856
|
+
if field not in nodevalues_fields:
|
|
857
|
+
continue
|
|
858
|
+
|
|
859
|
+
for param in nodevalues:
|
|
860
|
+
param.set(value, field=field)
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
from .parameter import Parameter
|
|
8
8
|
from .baseschema import BaseSchema
|
|
9
9
|
|
|
10
|
+
from typing import Union, Tuple
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
class EditableSchema:
|
|
12
14
|
'''
|
|
@@ -17,11 +19,15 @@ class EditableSchema:
|
|
|
17
19
|
schema (:class:`BaseSchema`): schema to modify
|
|
18
20
|
'''
|
|
19
21
|
|
|
20
|
-
def __init__(self, schema):
|
|
22
|
+
def __init__(self, schema: BaseSchema):
|
|
21
23
|
# Grab manifest from base class
|
|
22
24
|
self.__schema = schema
|
|
23
25
|
|
|
24
|
-
def __insert(self,
|
|
26
|
+
def __insert(self,
|
|
27
|
+
keypath: Tuple[str],
|
|
28
|
+
value: Union[BaseSchema, Parameter],
|
|
29
|
+
fullkey: Tuple[str],
|
|
30
|
+
clobber: bool) -> None:
|
|
25
31
|
key = keypath[0]
|
|
26
32
|
keypath = keypath[1:]
|
|
27
33
|
|
|
@@ -31,6 +37,7 @@ class EditableSchema:
|
|
|
31
37
|
|
|
32
38
|
if isinstance(value, BaseSchema):
|
|
33
39
|
value._BaseSchema__parent = self.__schema
|
|
40
|
+
value._BaseSchema__key = key
|
|
34
41
|
|
|
35
42
|
if key == "default":
|
|
36
43
|
self.__schema._BaseSchema__default = value
|
|
@@ -47,9 +54,10 @@ class EditableSchema:
|
|
|
47
54
|
self.__schema._BaseSchema__default = new_schema
|
|
48
55
|
else:
|
|
49
56
|
new_schema = self.__schema._BaseSchema__manifest.setdefault(key, new_schema)
|
|
57
|
+
new_schema._BaseSchema__key = key
|
|
50
58
|
EditableSchema(new_schema).__insert(keypath, value, fullkey, clobber)
|
|
51
59
|
|
|
52
|
-
def __remove(self, keypath, fullkey):
|
|
60
|
+
def __remove(self, keypath: Tuple[str], fullkey: Tuple[str]) -> None:
|
|
53
61
|
key = keypath[0]
|
|
54
62
|
keypath = keypath[1:]
|
|
55
63
|
|
|
@@ -69,7 +77,7 @@ class EditableSchema:
|
|
|
69
77
|
else:
|
|
70
78
|
EditableSchema(next_param).__remove(keypath, fullkey)
|
|
71
79
|
|
|
72
|
-
def insert(self, *args, clobber=False):
|
|
80
|
+
def insert(self, *args, clobber: bool = False) -> None:
|
|
73
81
|
'''
|
|
74
82
|
Inserts a :class:`Parameter` or a :class:`BaseSchema` to the schema,
|
|
75
83
|
based on the keypath and value provided in the ``*args``.
|
|
@@ -98,7 +106,7 @@ class EditableSchema:
|
|
|
98
106
|
|
|
99
107
|
self.__insert(keypath, value, keypath, clobber=clobber)
|
|
100
108
|
|
|
101
|
-
def remove(self, *keypath):
|
|
109
|
+
def remove(self, *keypath: str) -> None:
|
|
102
110
|
'''
|
|
103
111
|
Removes a keypath from the schema.
|
|
104
112
|
|
|
@@ -118,7 +126,7 @@ class EditableSchema:
|
|
|
118
126
|
|
|
119
127
|
self.__remove(keypath, keypath)
|
|
120
128
|
|
|
121
|
-
def search(self, *keypath):
|
|
129
|
+
def search(self, *keypath: str) -> Union[BaseSchema, Parameter]:
|
|
122
130
|
'''
|
|
123
131
|
Finds an item in the schema. This will raise a KeyError if
|
|
124
132
|
the path is not found.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import json
|
|
3
3
|
|
|
4
|
+
from typing import Tuple, Set
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
class Journal:
|
|
6
8
|
"""
|
|
@@ -12,7 +14,7 @@ class Journal:
|
|
|
12
14
|
keyprefix (list of str): keypath to prefix on to recorded path
|
|
13
15
|
"""
|
|
14
16
|
|
|
15
|
-
def __init__(self, keyprefix=None):
|
|
17
|
+
def __init__(self, keyprefix: Tuple[str] = None):
|
|
16
18
|
if not keyprefix:
|
|
17
19
|
self.__keyprefix = tuple()
|
|
18
20
|
else:
|
|
@@ -24,14 +26,14 @@ class Journal:
|
|
|
24
26
|
self.stop()
|
|
25
27
|
|
|
26
28
|
@property
|
|
27
|
-
def keypath(self):
|
|
29
|
+
def keypath(self) -> Tuple[str]:
|
|
28
30
|
'''
|
|
29
31
|
Returns the reference key path for this journal
|
|
30
32
|
'''
|
|
31
33
|
|
|
32
34
|
return self.__keyprefix
|
|
33
35
|
|
|
34
|
-
def get_child(self, *keypath):
|
|
36
|
+
def get_child(self, *keypath: Tuple[str]):
|
|
35
37
|
'''
|
|
36
38
|
Get a child journal based on a new keypath
|
|
37
39
|
|
|
@@ -60,26 +62,26 @@ class Journal:
|
|
|
60
62
|
|
|
61
63
|
return copy.deepcopy(self.__parent.__journal)
|
|
62
64
|
|
|
63
|
-
def has_journaling(self):
|
|
65
|
+
def has_journaling(self) -> bool:
|
|
64
66
|
"""
|
|
65
67
|
Returns true if the schema is currently setup and is the root of the journal and has data
|
|
66
68
|
"""
|
|
67
69
|
return self is self.__parent and bool(self.__journal)
|
|
68
70
|
|
|
69
|
-
def is_journaling(self):
|
|
71
|
+
def is_journaling(self) -> bool:
|
|
70
72
|
"""
|
|
71
73
|
Returns true if the schema is currently setup for journaling
|
|
72
74
|
"""
|
|
73
75
|
return self.__parent.__journal is not None
|
|
74
76
|
|
|
75
|
-
def get_types(self):
|
|
77
|
+
def get_types(self) -> Set[str]:
|
|
76
78
|
"""
|
|
77
79
|
Returns the current schema accesses that are being recorded
|
|
78
80
|
"""
|
|
79
81
|
|
|
80
82
|
return self.__parent.__record_types.copy()
|
|
81
83
|
|
|
82
|
-
def add_type(self, value):
|
|
84
|
+
def add_type(self, value: str) -> None:
|
|
83
85
|
"""
|
|
84
86
|
Adds a new access type to the journal record.
|
|
85
87
|
|
|
@@ -90,9 +92,9 @@ class Journal:
|
|
|
90
92
|
if value not in ("set", "add", "remove", "unset", "get"):
|
|
91
93
|
raise ValueError(f"{value} is not a valid type")
|
|
92
94
|
|
|
93
|
-
|
|
95
|
+
self.__parent.__record_types.add(value)
|
|
94
96
|
|
|
95
|
-
def remove_type(self, value):
|
|
97
|
+
def remove_type(self, value: str) -> None:
|
|
96
98
|
"""
|
|
97
99
|
Removes a new access type to the journal record.
|
|
98
100
|
|
|
@@ -105,7 +107,13 @@ class Journal:
|
|
|
105
107
|
except KeyError:
|
|
106
108
|
pass
|
|
107
109
|
|
|
108
|
-
def record(self,
|
|
110
|
+
def record(self,
|
|
111
|
+
record_type: str,
|
|
112
|
+
key: Tuple[str],
|
|
113
|
+
value=None,
|
|
114
|
+
field: str = None,
|
|
115
|
+
step: str = None,
|
|
116
|
+
index: str = None) -> None:
|
|
109
117
|
'''
|
|
110
118
|
Record the schema transaction
|
|
111
119
|
'''
|
|
@@ -125,7 +133,7 @@ class Journal:
|
|
|
125
133
|
"index": index
|
|
126
134
|
})
|
|
127
135
|
|
|
128
|
-
def start(self):
|
|
136
|
+
def start(self) -> None:
|
|
129
137
|
'''
|
|
130
138
|
Start journaling the schema transactions
|
|
131
139
|
'''
|
|
@@ -135,7 +143,7 @@ class Journal:
|
|
|
135
143
|
self.add_type("remove")
|
|
136
144
|
self.add_type("unset")
|
|
137
145
|
|
|
138
|
-
def stop(self):
|
|
146
|
+
def stop(self) -> None:
|
|
139
147
|
'''
|
|
140
148
|
Stop journaling the schema transactions
|
|
141
149
|
'''
|
|
@@ -143,7 +151,7 @@ class Journal:
|
|
|
143
151
|
self.__parent.__record_types.clear()
|
|
144
152
|
|
|
145
153
|
@staticmethod
|
|
146
|
-
def replay_file(schema, filepath):
|
|
154
|
+
def replay_file(schema, filepath: str) -> None:
|
|
147
155
|
'''
|
|
148
156
|
Replay a journal into a schema from a manifest
|
|
149
157
|
|
|
@@ -160,7 +168,7 @@ class Journal:
|
|
|
160
168
|
journal.from_dict(data["__journal__"])
|
|
161
169
|
journal.replay(schema)
|
|
162
170
|
|
|
163
|
-
def replay(self, schema):
|
|
171
|
+
def replay(self, schema) -> None:
|
|
164
172
|
'''
|
|
165
173
|
Replay journal into a schema
|
|
166
174
|
|
|
@@ -196,7 +204,7 @@ class Journal:
|
|
|
196
204
|
raise ValueError(f'Unknown record type {record_type}')
|
|
197
205
|
|
|
198
206
|
@staticmethod
|
|
199
|
-
def access(schema):
|
|
207
|
+
def access(schema) -> None:
|
|
200
208
|
'''
|
|
201
209
|
Access a journal from a schema
|
|
202
210
|
|