siliconcompiler 0.32.3__py3-none-any.whl → 0.33.0__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 +19 -2
- siliconcompiler/_metadata.py +1 -1
- siliconcompiler/apps/sc.py +2 -2
- siliconcompiler/apps/sc_install.py +3 -3
- siliconcompiler/apps/sc_issue.py +1 -1
- siliconcompiler/apps/sc_remote.py +4 -4
- siliconcompiler/apps/sc_show.py +2 -2
- siliconcompiler/apps/utils/replay.py +5 -3
- siliconcompiler/asic.py +120 -0
- siliconcompiler/checklist.py +150 -0
- siliconcompiler/core.py +267 -289
- siliconcompiler/flowgraph.py +803 -515
- siliconcompiler/fpga.py +84 -0
- siliconcompiler/metric.py +420 -0
- siliconcompiler/optimizer/vizier.py +2 -3
- siliconcompiler/package/__init__.py +29 -6
- siliconcompiler/pdk.py +415 -0
- siliconcompiler/record.py +449 -0
- siliconcompiler/remote/client.py +6 -3
- siliconcompiler/remote/schema.py +116 -112
- siliconcompiler/remote/server.py +3 -5
- siliconcompiler/report/dashboard/cli/__init__.py +13 -722
- siliconcompiler/report/dashboard/cli/board.py +895 -0
- siliconcompiler/report/dashboard/web/__init__.py +10 -10
- siliconcompiler/report/dashboard/web/components/__init__.py +5 -4
- siliconcompiler/report/dashboard/web/components/flowgraph.py +3 -3
- siliconcompiler/report/dashboard/web/components/graph.py +6 -3
- siliconcompiler/report/dashboard/web/state.py +1 -1
- siliconcompiler/report/dashboard/web/utils/__init__.py +4 -3
- siliconcompiler/report/html_report.py +2 -3
- siliconcompiler/report/report.py +13 -7
- siliconcompiler/report/summary_image.py +1 -1
- siliconcompiler/report/summary_table.py +3 -3
- siliconcompiler/report/utils.py +11 -10
- siliconcompiler/scheduler/__init__.py +145 -280
- siliconcompiler/scheduler/run_node.py +2 -1
- siliconcompiler/scheduler/send_messages.py +4 -4
- siliconcompiler/scheduler/slurm.py +2 -2
- siliconcompiler/schema/__init__.py +19 -2
- siliconcompiler/schema/baseschema.py +493 -0
- siliconcompiler/schema/cmdlineschema.py +250 -0
- siliconcompiler/{sphinx_ext → schema/docs}/__init__.py +3 -1
- siliconcompiler/{sphinx_ext → schema/docs}/dynamicgen.py +63 -81
- siliconcompiler/{sphinx_ext → schema/docs}/schemagen.py +73 -85
- siliconcompiler/{sphinx_ext → schema/docs}/utils.py +12 -13
- siliconcompiler/schema/editableschema.py +136 -0
- siliconcompiler/schema/journalingschema.py +238 -0
- siliconcompiler/schema/namedschema.py +41 -0
- siliconcompiler/schema/packageschema.py +101 -0
- siliconcompiler/schema/parameter.py +791 -0
- siliconcompiler/schema/parametertype.py +323 -0
- siliconcompiler/schema/parametervalue.py +736 -0
- siliconcompiler/schema/safeschema.py +37 -0
- siliconcompiler/schema/schema_cfg.py +109 -1789
- siliconcompiler/schema/utils.py +5 -68
- siliconcompiler/schema_obj.py +119 -0
- siliconcompiler/tool.py +1308 -0
- siliconcompiler/tools/_common/__init__.py +6 -10
- siliconcompiler/tools/_common/sdc/sc_constraints.sdc +1 -1
- siliconcompiler/tools/bluespec/convert.py +7 -7
- siliconcompiler/tools/builtin/_common.py +1 -1
- siliconcompiler/tools/builtin/concatenate.py +2 -2
- siliconcompiler/tools/builtin/minimum.py +1 -1
- siliconcompiler/tools/builtin/mux.py +2 -1
- siliconcompiler/tools/builtin/nop.py +1 -1
- siliconcompiler/tools/builtin/verify.py +6 -4
- siliconcompiler/tools/chisel/convert.py +4 -4
- siliconcompiler/tools/genfasm/bitstream.py +3 -3
- siliconcompiler/tools/ghdl/convert.py +1 -1
- siliconcompiler/tools/icarus/compile.py +4 -4
- siliconcompiler/tools/icepack/bitstream.py +6 -1
- siliconcompiler/tools/klayout/convert_drc_db.py +5 -0
- siliconcompiler/tools/klayout/klayout_export.py +0 -1
- siliconcompiler/tools/klayout/klayout_utils.py +3 -10
- siliconcompiler/tools/nextpnr/apr.py +6 -1
- siliconcompiler/tools/nextpnr/nextpnr.py +4 -4
- siliconcompiler/tools/openroad/_apr.py +13 -0
- siliconcompiler/tools/openroad/rdlroute.py +3 -3
- siliconcompiler/tools/openroad/scripts/apr/postamble.tcl +1 -1
- siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +5 -5
- siliconcompiler/tools/openroad/scripts/apr/sc_antenna_repair.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_placement.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_endcap_tapcell_insertion.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_fillercell_insertion.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_fillmetal_insertion.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_global_placement.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_macro_placement.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_metrics.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_pin_placement.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_power_grid.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_write_data.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/common/procs.tcl +57 -1
- siliconcompiler/tools/openroad/scripts/common/screenshot.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/common/write_images.tcl +28 -3
- siliconcompiler/tools/openroad/scripts/sc_rcx.tcl +1 -1
- siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/sc_show.tcl +6 -6
- siliconcompiler/tools/slang/__init__.py +10 -10
- siliconcompiler/tools/surelog/parse.py +4 -4
- siliconcompiler/tools/sv2v/convert.py +20 -3
- siliconcompiler/tools/verilator/compile.py +2 -2
- siliconcompiler/tools/verilator/verilator.py +3 -3
- siliconcompiler/tools/vpr/place.py +1 -1
- siliconcompiler/tools/vpr/route.py +4 -4
- siliconcompiler/tools/vpr/screenshot.py +1 -1
- siliconcompiler/tools/vpr/show.py +5 -5
- siliconcompiler/tools/vpr/vpr.py +24 -24
- siliconcompiler/tools/xdm/convert.py +2 -2
- siliconcompiler/tools/xyce/simulate.py +1 -1
- siliconcompiler/tools/yosys/sc_synth_asic.tcl +74 -68
- siliconcompiler/tools/yosys/syn_asic.py +2 -2
- siliconcompiler/toolscripts/_tools.json +7 -7
- siliconcompiler/toolscripts/ubuntu22/install-vpr.sh +0 -2
- siliconcompiler/toolscripts/ubuntu24/install-vpr.sh +0 -2
- siliconcompiler/utils/__init__.py +8 -112
- siliconcompiler/utils/flowgraph.py +339 -0
- siliconcompiler/{issue.py → utils/issue.py} +4 -3
- siliconcompiler/utils/logging.py +1 -2
- {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.0.dist-info}/METADATA +9 -8
- {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.0.dist-info}/RECORD +151 -134
- {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.0.dist-info}/WHEEL +1 -1
- {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.0.dist-info}/entry_points.txt +8 -8
- siliconcompiler/schema/schema_obj.py +0 -1936
- siliconcompiler/toolscripts/ubuntu20/install-vpr.sh +0 -29
- siliconcompiler/toolscripts/ubuntu20/install-yosys-parmys.sh +0 -61
- /siliconcompiler/{templates → data/templates}/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/email/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/email/general.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/email/summary.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/issue/README.txt +0 -0
- /siliconcompiler/{templates → data/templates}/issue/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/issue/run.sh +0 -0
- /siliconcompiler/{templates → data/templates}/replay/replay.py.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/replay/replay.sh.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/replay/requirements.txt +0 -0
- /siliconcompiler/{templates → data/templates}/replay/setup.sh +0 -0
- /siliconcompiler/{templates → data/templates}/report/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/report/bootstrap.min.css +0 -0
- /siliconcompiler/{templates → data/templates}/report/bootstrap.min.js +0 -0
- /siliconcompiler/{templates → data/templates}/report/bootstrap_LICENSE.md +0 -0
- /siliconcompiler/{templates → data/templates}/report/sc_report.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/slurm/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/slurm/run.sh +0 -0
- /siliconcompiler/{templates → data/templates}/tcl/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/tcl/manifest.tcl.j2 +0 -0
- /siliconcompiler/{units.py → utils/units.py} +0 -0
- {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.0.dist-info}/licenses/LICENSE +0 -0
- {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.0.dist-info}/top_level.txt +0 -0
|
@@ -5,8 +5,9 @@ from docutils.statemachine import ViewList
|
|
|
5
5
|
from sphinx.util.docutils import SphinxDirective
|
|
6
6
|
|
|
7
7
|
import siliconcompiler
|
|
8
|
-
from siliconcompiler
|
|
9
|
-
from siliconcompiler.
|
|
8
|
+
from siliconcompiler import Schema
|
|
9
|
+
from siliconcompiler.schema import utils, PerNode
|
|
10
|
+
from siliconcompiler.schema.docs.utils import (
|
|
10
11
|
strong,
|
|
11
12
|
code,
|
|
12
13
|
para,
|
|
@@ -15,8 +16,7 @@ from siliconcompiler.sphinx_ext.utils import (
|
|
|
15
16
|
build_section_with_target,
|
|
16
17
|
build_list
|
|
17
18
|
)
|
|
18
|
-
from siliconcompiler.schema import
|
|
19
|
-
from siliconcompiler.sphinx_ext import sc_root as SC_ROOT
|
|
19
|
+
from siliconcompiler.schema.docs import sc_root as SC_ROOT
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
# Main Sphinx plugin
|
|
@@ -28,72 +28,71 @@ class SchemaGen(SphinxDirective):
|
|
|
28
28
|
self.env.note_dependency(__file__)
|
|
29
29
|
self.env.note_dependency(utils.__file__)
|
|
30
30
|
|
|
31
|
-
schema = Schema()
|
|
32
|
-
|
|
33
|
-
return self.process_schema(
|
|
34
|
-
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
for
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
examples = [code(ex.strip()) for ex in exs]
|
|
64
|
-
p = None
|
|
65
|
-
for ex in examples:
|
|
66
|
-
if not p:
|
|
67
|
-
p = para("")
|
|
68
|
-
else:
|
|
69
|
-
p += para("")
|
|
70
|
-
p += ex
|
|
71
|
-
entries.append([strong(f'Example ({name.upper()})'), p])
|
|
72
|
-
|
|
73
|
-
table = build_table(entries, colwidths=[25, 75])
|
|
74
|
-
body = self.parse_rst(utils.trim(schema['help']))
|
|
75
|
-
|
|
76
|
-
return [table, body]
|
|
77
|
-
else:
|
|
78
|
-
sections = []
|
|
79
|
-
for key in schema.keys():
|
|
80
|
-
if key == 'default':
|
|
81
|
-
for n in self.process_schema(schema['default'], parents=parents):
|
|
82
|
-
sections.append(n)
|
|
31
|
+
self.schema = Schema()
|
|
32
|
+
|
|
33
|
+
return self.process_schema([])
|
|
34
|
+
|
|
35
|
+
def process_parameter(self, parameter):
|
|
36
|
+
entries = [[strong('Description'), para(parameter.get(field='shorthelp'))],
|
|
37
|
+
[strong('Type'), para(parameter.get(field='type'))]]
|
|
38
|
+
|
|
39
|
+
if parameter.get(field='pernode') != PerNode.NEVER:
|
|
40
|
+
entries.append([strong('Per step/index'),
|
|
41
|
+
para(str(parameter.get(field='pernode').value).lower())])
|
|
42
|
+
|
|
43
|
+
entries.append([strong('Scope'), para(str(parameter.get(field='scope').value).lower())])
|
|
44
|
+
|
|
45
|
+
if parameter.get(field='unit'):
|
|
46
|
+
entries.append([strong('Unit'), para(parameter.get(field='unit'))])
|
|
47
|
+
|
|
48
|
+
switch_list = [code(switch) for switch in parameter.get(field='switch')]
|
|
49
|
+
entries.extend([[strong('Default Value'), para(parameter.default.get())],
|
|
50
|
+
[strong('CLI Switch'), build_list(switch_list)]])
|
|
51
|
+
|
|
52
|
+
examples = {}
|
|
53
|
+
for example in parameter.get(field='example'):
|
|
54
|
+
name, ex = example.split(':', 1)
|
|
55
|
+
examples.setdefault(name, []).append(ex)
|
|
56
|
+
|
|
57
|
+
for name, exs in examples.items():
|
|
58
|
+
examples = [code(ex.strip()) for ex in exs]
|
|
59
|
+
p = None
|
|
60
|
+
for ex in examples:
|
|
61
|
+
if not p:
|
|
62
|
+
p = para("")
|
|
83
63
|
else:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
64
|
+
p += para("")
|
|
65
|
+
p += ex
|
|
66
|
+
entries.append([strong(f'Example ({name.upper()})'), p])
|
|
67
|
+
|
|
68
|
+
table = build_table(entries, colwidths=[25, 75])
|
|
69
|
+
body = self.parse_rst(utils.trim(parameter.get(field='help')))
|
|
70
|
+
|
|
71
|
+
return [table, body]
|
|
72
|
+
|
|
73
|
+
def process_schema(self, keypath):
|
|
74
|
+
if self.schema.valid(*keypath, default_valid=True, check_complete=True):
|
|
75
|
+
return self.process_parameter(self.schema.get(*keypath, field=None))
|
|
76
|
+
|
|
77
|
+
sections = []
|
|
78
|
+
if self.schema.valid(*keypath, "default"):
|
|
79
|
+
for n in self.process_schema(keypath + ["default"]):
|
|
80
|
+
sections.append(n)
|
|
81
|
+
for key in self.schema.getkeys(*keypath):
|
|
82
|
+
if not keypath and key in ('history', 'library'):
|
|
83
|
+
continue
|
|
84
|
+
section_key = 'param-' + '-'.join(
|
|
85
|
+
[key for key in (keypath + [key]) if key != "default"])
|
|
86
|
+
section = build_section_with_target(key, section_key, self.state.document)
|
|
87
|
+
for n in self.process_schema(keypath + [key]):
|
|
88
|
+
section += n
|
|
89
|
+
sections.append(section)
|
|
90
|
+
|
|
91
|
+
# Sort all sections alphabetically by title. We may also have nodes
|
|
92
|
+
# in this list that aren't sections if `schema` has a 'default'
|
|
93
|
+
# entry that's a leaf. In this case, we sort this as an empty string
|
|
94
|
+
# in order to put this node at the beginning of the list.
|
|
95
|
+
return sorted(sections, key=lambda s: s[0][0] if isinstance(s, nodes.section) else '')
|
|
97
96
|
|
|
98
97
|
def parse_rst(self, content):
|
|
99
98
|
rst = ViewList()
|
|
@@ -143,17 +142,6 @@ class CategorySummary(SphinxDirective):
|
|
|
143
142
|
|
|
144
143
|
class CategoryGroupTable(SphinxDirective):
|
|
145
144
|
|
|
146
|
-
def count_keys(self, schema, *keypath):
|
|
147
|
-
cfgs = schema.getdict(*keypath)
|
|
148
|
-
count = 0
|
|
149
|
-
for key, cfg in cfgs.items():
|
|
150
|
-
if schema._is_leaf(cfg):
|
|
151
|
-
count += 1
|
|
152
|
-
else:
|
|
153
|
-
count += self.count_keys(schema, *keypath, key)
|
|
154
|
-
|
|
155
|
-
return count
|
|
156
|
-
|
|
157
145
|
def run(self):
|
|
158
146
|
self.env.note_dependency(__file__)
|
|
159
147
|
|
|
@@ -182,22 +170,22 @@ class CategoryGroupTable(SphinxDirective):
|
|
|
182
170
|
"arg": "",
|
|
183
171
|
}
|
|
184
172
|
|
|
185
|
-
schema = Schema()
|
|
173
|
+
self.schema = Schema()
|
|
186
174
|
|
|
187
175
|
# Check if all groups have desc
|
|
188
|
-
for group in schema.getkeys():
|
|
176
|
+
for group in self.schema.getkeys():
|
|
189
177
|
if group not in desc:
|
|
190
178
|
raise ValueError(f"{group} not found in group descriptions")
|
|
191
179
|
|
|
192
180
|
# Check if all groups have schema
|
|
193
181
|
for group in desc.keys():
|
|
194
|
-
if group not in schema.getkeys():
|
|
182
|
+
if group not in self.schema.getkeys():
|
|
195
183
|
raise ValueError(f"{group} not found in schema")
|
|
196
184
|
|
|
197
185
|
table = [[strong('Group'), strong('Parameters'), strong('Description')]]
|
|
198
186
|
|
|
199
187
|
total = 0
|
|
200
|
-
for group in schema.getkeys():
|
|
188
|
+
for group in self.schema.getkeys():
|
|
201
189
|
text = desc[group]
|
|
202
190
|
if len(text) == 0:
|
|
203
191
|
continue
|
|
@@ -205,7 +193,7 @@ class CategoryGroupTable(SphinxDirective):
|
|
|
205
193
|
key = para('')
|
|
206
194
|
key += keypath([group], self.env.docname)
|
|
207
195
|
|
|
208
|
-
count = self.
|
|
196
|
+
count = len(self.schema.allkeys(group, include_default=True))
|
|
209
197
|
total += count
|
|
210
198
|
|
|
211
199
|
table.append([key, para(f'{count}'), para(text)])
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from docutils import nodes
|
|
2
2
|
import sphinx.addnodes
|
|
3
3
|
|
|
4
|
-
from siliconcompiler
|
|
4
|
+
from siliconcompiler import Schema
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
# Docutils helpers
|
|
@@ -127,32 +127,31 @@ def keypath(key_path, refdoc, key_text=None):
|
|
|
127
127
|
'''Helper function for displaying Schema keypaths.'''
|
|
128
128
|
text_parts = []
|
|
129
129
|
key_parts = []
|
|
130
|
-
|
|
130
|
+
schema = Schema()
|
|
131
131
|
for key in key_path:
|
|
132
|
-
if
|
|
133
|
-
|
|
134
|
-
key_parts.append(key)
|
|
135
|
-
try:
|
|
136
|
-
cfg = cfg[key]
|
|
137
|
-
except KeyError:
|
|
138
|
-
raise ValueError(f'Invalid keypath {key_path}')
|
|
139
|
-
else:
|
|
140
|
-
cfg = cfg['default']
|
|
132
|
+
if schema.valid(*key_parts, "default", default_valid=True):
|
|
133
|
+
key_parts.append("default")
|
|
141
134
|
if key.startswith('<') and key.endswith('>'):
|
|
142
135
|
# Placeholder
|
|
143
136
|
text_parts.append(key)
|
|
144
137
|
else:
|
|
145
138
|
# Fully-qualified
|
|
146
139
|
text_parts.append(f"'{key}'")
|
|
140
|
+
else:
|
|
141
|
+
key_parts.append(key)
|
|
142
|
+
text_parts.append(f"'{key}'")
|
|
143
|
+
|
|
144
|
+
if not schema.valid(*key_parts, default_valid=True):
|
|
145
|
+
raise ValueError(f'Invalid keypath {key_path}')
|
|
147
146
|
|
|
148
|
-
if
|
|
147
|
+
if not schema.valid(*key_parts, default_valid=True, check_complete=True):
|
|
149
148
|
# Not leaf
|
|
150
149
|
text_parts.append('...')
|
|
151
150
|
|
|
152
151
|
if key_text:
|
|
153
152
|
text_parts = key_text
|
|
154
153
|
text = f"[{', '.join(text_parts)}]"
|
|
155
|
-
refid = get_ref_id('param-' + '-'.join(key_parts))
|
|
154
|
+
refid = get_ref_id('param-' + '-'.join([key for key in key_parts if key != "default"]))
|
|
156
155
|
|
|
157
156
|
opt = {'refdoc': refdoc,
|
|
158
157
|
'refdomain': 'sc',
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Copyright 2025 Silicon Compiler Authors. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
# NOTE: this file cannot rely on any third-party dependencies, including other
|
|
4
|
+
# SC dependencies outside of its directory, since it may be used by tool drivers
|
|
5
|
+
# that have isolated Python environments.
|
|
6
|
+
|
|
7
|
+
from .parameter import Parameter
|
|
8
|
+
from .baseschema import BaseSchema
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EditableSchema:
|
|
12
|
+
'''
|
|
13
|
+
This class provides access to modify the underlying schema.
|
|
14
|
+
This should only be used when creating new schema entries.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
schema (:class:`BaseSchema`): schema to modify
|
|
18
|
+
'''
|
|
19
|
+
|
|
20
|
+
def __init__(self, schema):
|
|
21
|
+
# Grab manifest from base class
|
|
22
|
+
self.__schema = schema
|
|
23
|
+
|
|
24
|
+
def __insert(self, keypath, value, fullkey, clobber):
|
|
25
|
+
key = keypath[0]
|
|
26
|
+
keypath = keypath[1:]
|
|
27
|
+
|
|
28
|
+
if len(keypath) == 0:
|
|
29
|
+
if key in self.__schema._BaseSchema__manifest and not clobber:
|
|
30
|
+
raise KeyError(f"[{','.join(fullkey)}] is already defined")
|
|
31
|
+
|
|
32
|
+
if key == "default":
|
|
33
|
+
self.__schema._BaseSchema__default = value
|
|
34
|
+
else:
|
|
35
|
+
self.__schema._BaseSchema__manifest[key] = value
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
new_schema = BaseSchema()
|
|
39
|
+
if key == "default":
|
|
40
|
+
if self.__schema._BaseSchema__default:
|
|
41
|
+
new_schema = self.__schema._BaseSchema__default
|
|
42
|
+
else:
|
|
43
|
+
self.__schema._BaseSchema__default = new_schema
|
|
44
|
+
else:
|
|
45
|
+
new_schema = self.__schema._BaseSchema__manifest.setdefault(key, new_schema)
|
|
46
|
+
EditableSchema(new_schema).__insert(keypath, value, fullkey, clobber)
|
|
47
|
+
|
|
48
|
+
def __remove(self, keypath, fullkey):
|
|
49
|
+
key = keypath[0]
|
|
50
|
+
keypath = keypath[1:]
|
|
51
|
+
|
|
52
|
+
if key == "default":
|
|
53
|
+
next_param = self.__schema._BaseSchema__default
|
|
54
|
+
else:
|
|
55
|
+
next_param = self.__schema._BaseSchema__manifest.get(key, None)
|
|
56
|
+
|
|
57
|
+
if not next_param:
|
|
58
|
+
raise KeyError(f"[{','.join(fullkey)}] cannot be found")
|
|
59
|
+
|
|
60
|
+
if len(keypath) == 0:
|
|
61
|
+
if key == "default":
|
|
62
|
+
self.__schema._BaseSchema__default = None
|
|
63
|
+
else:
|
|
64
|
+
del self.__schema._BaseSchema__manifest[key]
|
|
65
|
+
else:
|
|
66
|
+
EditableSchema(next_param).__remove(keypath, fullkey)
|
|
67
|
+
|
|
68
|
+
def insert(self, *args, clobber=False):
|
|
69
|
+
'''
|
|
70
|
+
Inserts a :class:`Parameter` or a :class:`BaseSchema` to the schema,
|
|
71
|
+
based on the keypath and value provided in the ``*args``.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
args (list): Parameter keypath followed by a item to add.
|
|
75
|
+
clobber (boolean): If true, will overwrite existing value,
|
|
76
|
+
otherwise will raise a KeyError if it is already defined.
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
>>> schema.insert('option', 'value', Parameter('str'))
|
|
80
|
+
Adds the keypath [option,value] with a string parameter.
|
|
81
|
+
'''
|
|
82
|
+
|
|
83
|
+
value = args[-1]
|
|
84
|
+
keypath = args[0:-1]
|
|
85
|
+
|
|
86
|
+
if not keypath:
|
|
87
|
+
raise ValueError("A keypath is required")
|
|
88
|
+
|
|
89
|
+
if any([not isinstance(key, str) for key in keypath]):
|
|
90
|
+
raise ValueError("Keypath must only be strings")
|
|
91
|
+
|
|
92
|
+
if not isinstance(value, (Parameter, BaseSchema)):
|
|
93
|
+
raise ValueError(f"Value ({type(value)}) must be schema type: Parameter, BaseSchema")
|
|
94
|
+
|
|
95
|
+
self.__insert(keypath, value, keypath, clobber=clobber)
|
|
96
|
+
|
|
97
|
+
def remove(self, *keypath):
|
|
98
|
+
'''
|
|
99
|
+
Removes a keypath from the schema.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
keypath (list): keypath to be removed.
|
|
103
|
+
|
|
104
|
+
Examples:
|
|
105
|
+
>>> schema.remove('option', 'value')
|
|
106
|
+
Removes the keypath [option,value] from the schema.
|
|
107
|
+
'''
|
|
108
|
+
|
|
109
|
+
if not keypath:
|
|
110
|
+
raise ValueError("A keypath is required")
|
|
111
|
+
|
|
112
|
+
if any([not isinstance(key, str) for key in keypath]):
|
|
113
|
+
raise ValueError("Keypath must only be strings")
|
|
114
|
+
|
|
115
|
+
self.__remove(keypath, keypath)
|
|
116
|
+
|
|
117
|
+
def search(self, *keypath):
|
|
118
|
+
'''
|
|
119
|
+
Finds an item in the schema. This will raise a KeyError if
|
|
120
|
+
the path is not found.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
keypath (list): keypath to be found.
|
|
124
|
+
|
|
125
|
+
Examples:
|
|
126
|
+
>>> schema.search('option', 'value')
|
|
127
|
+
Returns the parameter stored in at [option,value].
|
|
128
|
+
'''
|
|
129
|
+
|
|
130
|
+
if not keypath:
|
|
131
|
+
raise ValueError("A keypath is required")
|
|
132
|
+
|
|
133
|
+
if any([not isinstance(key, str) for key in keypath]):
|
|
134
|
+
raise ValueError("Keypath must only be strings")
|
|
135
|
+
|
|
136
|
+
return self.__schema._BaseSchema__search(*keypath, require_leaf=False)
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import types
|
|
3
|
+
|
|
4
|
+
from .baseschema import BaseSchema
|
|
5
|
+
from .baseschema import json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class JournalingSchema(BaseSchema):
|
|
9
|
+
"""
|
|
10
|
+
This class provides the ability to record the schema transactions:
|
|
11
|
+
:meth:`set`, :meth:`add`, :meth:`remove`, and :meth:`unset`.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
schema (:class:`BaseSchema`): schema to track
|
|
15
|
+
keyprefix (list of str): keypath to prefix on to recorded path
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, schema, keyprefix=None):
|
|
19
|
+
if not isinstance(schema, BaseSchema):
|
|
20
|
+
raise TypeError(f"schema must be of BaseSchema type, not: {type(schema)}")
|
|
21
|
+
if isinstance(schema, JournalingSchema):
|
|
22
|
+
raise TypeError("schema must be of cannot be a JournalingSchema")
|
|
23
|
+
|
|
24
|
+
self.__schema = schema
|
|
25
|
+
|
|
26
|
+
journal_attrs = dir(self)
|
|
27
|
+
|
|
28
|
+
# Transfer access to internal schema
|
|
29
|
+
for param, value in self.__schema.__dict__.items():
|
|
30
|
+
setattr(self, param, value)
|
|
31
|
+
for param in dir(type(self.__schema)):
|
|
32
|
+
if param in journal_attrs:
|
|
33
|
+
continue
|
|
34
|
+
method = getattr(type(self.__schema), param)
|
|
35
|
+
if callable(method):
|
|
36
|
+
setattr(self, param, types.MethodType(method, self))
|
|
37
|
+
|
|
38
|
+
if not keyprefix:
|
|
39
|
+
self.__keyprefix = tuple()
|
|
40
|
+
else:
|
|
41
|
+
self.__keyprefix = tuple(keyprefix)
|
|
42
|
+
|
|
43
|
+
self.__record_types = set()
|
|
44
|
+
self.stop_journal()
|
|
45
|
+
|
|
46
|
+
self.__parent = self
|
|
47
|
+
|
|
48
|
+
def get(self, *keypath, field='value', step=None, index=None):
|
|
49
|
+
get_ret = super().get(*keypath, field=field, step=step, index=index)
|
|
50
|
+
self.__record_journal("get", keypath, field=field, step=step, index=index)
|
|
51
|
+
if field == 'schema':
|
|
52
|
+
child = JournalingSchema(get_ret, keyprefix=[*self.__keyprefix, *keypath])
|
|
53
|
+
child.__parent = self.__parent
|
|
54
|
+
return child
|
|
55
|
+
|
|
56
|
+
return get_ret
|
|
57
|
+
|
|
58
|
+
def set(self, *args, field='value', clobber=True, step=None, index=None):
|
|
59
|
+
ret = super().set(*args, field=field, clobber=clobber, step=step, index=index)
|
|
60
|
+
if ret:
|
|
61
|
+
*keypath, value = args
|
|
62
|
+
self.__record_journal("set", keypath, value=value, field=field, step=step, index=index)
|
|
63
|
+
return ret
|
|
64
|
+
|
|
65
|
+
def add(self, *args, field='value', step=None, index=None):
|
|
66
|
+
ret = super().add(*args, field=field, step=step, index=index)
|
|
67
|
+
if ret:
|
|
68
|
+
*keypath, value = args
|
|
69
|
+
self.__record_journal("add", keypath, value=value, field=field, step=step, index=index)
|
|
70
|
+
return ret
|
|
71
|
+
|
|
72
|
+
def remove(self, *keypath):
|
|
73
|
+
self.__record_journal("remove", keypath)
|
|
74
|
+
return super().remove(*keypath)
|
|
75
|
+
|
|
76
|
+
def unset(self, *keypath, step=None, index=None):
|
|
77
|
+
self.__record_journal("unset", keypath, step=step, index=index)
|
|
78
|
+
return super().unset(*keypath, step=step, index=index)
|
|
79
|
+
|
|
80
|
+
def _from_dict(self, manifest, keypath, version=None):
|
|
81
|
+
if "__journal__" in manifest:
|
|
82
|
+
self.__journal = manifest["__journal__"]
|
|
83
|
+
del manifest["__journal__"]
|
|
84
|
+
|
|
85
|
+
self.__schema._from_dict(manifest, keypath, version=version)
|
|
86
|
+
|
|
87
|
+
def getdict(self, *keypath, include_default=True):
|
|
88
|
+
manifest = super().getdict(*keypath, include_default=include_default)
|
|
89
|
+
|
|
90
|
+
if self.__journal:
|
|
91
|
+
manifest["__journal__"] = self.get_journal()
|
|
92
|
+
|
|
93
|
+
return manifest
|
|
94
|
+
|
|
95
|
+
def get_journal(self):
|
|
96
|
+
"""
|
|
97
|
+
Returns a copy of the current journal
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
return copy.deepcopy(self.__journal)
|
|
101
|
+
|
|
102
|
+
def is_journaling(self):
|
|
103
|
+
"""
|
|
104
|
+
Returns true if the schema is currently setup for journaling
|
|
105
|
+
"""
|
|
106
|
+
return self.__journal is not None
|
|
107
|
+
|
|
108
|
+
def get_journaling_types(self):
|
|
109
|
+
"""
|
|
110
|
+
Returns the current schema accesses that are being recorded
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
return self.__record_types.copy()
|
|
114
|
+
|
|
115
|
+
def add_journaling_type(self, value):
|
|
116
|
+
"""
|
|
117
|
+
Adds a new access type to the journal record.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
value (str): access type
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
if value not in ("set", "add", "remove", "unset", "get"):
|
|
124
|
+
raise ValueError(f"{value} is not a valid type")
|
|
125
|
+
|
|
126
|
+
return self.__record_types.add(value)
|
|
127
|
+
|
|
128
|
+
def remove_journaling_type(self, value):
|
|
129
|
+
"""
|
|
130
|
+
Removes a new access type to the journal record.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
value (str): access type
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
self.__record_types.remove(value)
|
|
138
|
+
except KeyError:
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
def get_base_schema(self):
|
|
142
|
+
"""
|
|
143
|
+
Returns the base schema
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
return self.__schema
|
|
147
|
+
|
|
148
|
+
def __record_journal(self, record_type, key, value=None, field=None, step=None, index=None):
|
|
149
|
+
'''
|
|
150
|
+
Record the schema transaction
|
|
151
|
+
'''
|
|
152
|
+
|
|
153
|
+
if self.__parent.__journal is None:
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
if record_type not in self.__parent.__record_types:
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
self.__parent.__journal.append({
|
|
160
|
+
"type": record_type,
|
|
161
|
+
"key": tuple([*self.__keyprefix, *key]),
|
|
162
|
+
"value": value,
|
|
163
|
+
"field": field,
|
|
164
|
+
"step": step,
|
|
165
|
+
"index": index
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
def start_journal(self):
|
|
169
|
+
'''
|
|
170
|
+
Start journaling the schema transactions
|
|
171
|
+
'''
|
|
172
|
+
self.__journal = []
|
|
173
|
+
self.add_journaling_type("set")
|
|
174
|
+
self.add_journaling_type("add")
|
|
175
|
+
self.add_journaling_type("remove")
|
|
176
|
+
self.add_journaling_type("unset")
|
|
177
|
+
|
|
178
|
+
def stop_journal(self):
|
|
179
|
+
'''
|
|
180
|
+
Stop journaling the schema transactions
|
|
181
|
+
'''
|
|
182
|
+
self.__journal = None
|
|
183
|
+
self.__record_types.clear()
|
|
184
|
+
|
|
185
|
+
def read_journal(self, filename):
|
|
186
|
+
'''
|
|
187
|
+
Reads a manifest and replays the journal
|
|
188
|
+
'''
|
|
189
|
+
|
|
190
|
+
with open(filename) as f:
|
|
191
|
+
data = json.loads(f.read())
|
|
192
|
+
|
|
193
|
+
self.import_journal(cfg=data)
|
|
194
|
+
|
|
195
|
+
def import_journal(self, schema=None, cfg=None):
|
|
196
|
+
'''
|
|
197
|
+
Import the journaled transactions from a different schema.
|
|
198
|
+
Only one argument is supported at a time.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
schema (:class:`JournalingSchema`): schema to replay transactions from
|
|
202
|
+
cfg (dict): dictionary to replay transactions from
|
|
203
|
+
'''
|
|
204
|
+
|
|
205
|
+
if schema and cfg:
|
|
206
|
+
raise ValueError("only one argument is supported")
|
|
207
|
+
|
|
208
|
+
journal = []
|
|
209
|
+
if schema:
|
|
210
|
+
if isinstance(schema, JournalingSchema):
|
|
211
|
+
journal = schema.__journal
|
|
212
|
+
else:
|
|
213
|
+
raise TypeError(f"schema must be a JournalingSchema, not {type(schema)}")
|
|
214
|
+
elif cfg and "__journal__" in cfg:
|
|
215
|
+
journal = cfg["__journal__"]
|
|
216
|
+
|
|
217
|
+
if journal is None:
|
|
218
|
+
return
|
|
219
|
+
|
|
220
|
+
for action in journal:
|
|
221
|
+
record_type = action['type']
|
|
222
|
+
keypath = action['key']
|
|
223
|
+
value = action['value']
|
|
224
|
+
field = action['field']
|
|
225
|
+
step = action['step']
|
|
226
|
+
index = action['index']
|
|
227
|
+
if record_type == 'set':
|
|
228
|
+
self.set(*keypath, value, field=field, step=step, index=index)
|
|
229
|
+
elif record_type == 'add':
|
|
230
|
+
self.add(*keypath, value, field=field, step=step, index=index)
|
|
231
|
+
elif record_type == 'unset':
|
|
232
|
+
self.unset(*keypath, step=step, index=index)
|
|
233
|
+
elif record_type == 'remove':
|
|
234
|
+
self.remove(*keypath)
|
|
235
|
+
elif record_type == 'get':
|
|
236
|
+
continue
|
|
237
|
+
else:
|
|
238
|
+
raise ValueError(f'Unknown record type {record_type}')
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Copyright 2025 Silicon Compiler Authors. All Rights Reserved.
|
|
2
|
+
|
|
3
|
+
# NOTE: this file cannot rely on any third-party dependencies, including other
|
|
4
|
+
# SC dependencies outside of its directory, since it may be used by tool drivers
|
|
5
|
+
# that have isolated Python environments.
|
|
6
|
+
|
|
7
|
+
from .baseschema import BaseSchema
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NamedSchema(BaseSchema):
|
|
11
|
+
'''
|
|
12
|
+
This object provides a named :class:`BaseSchema`.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
name (str): name of the schema
|
|
16
|
+
'''
|
|
17
|
+
|
|
18
|
+
def __init__(self, name=None):
|
|
19
|
+
super().__init__()
|
|
20
|
+
|
|
21
|
+
self.__name = name
|
|
22
|
+
|
|
23
|
+
def name(self):
|
|
24
|
+
'''
|
|
25
|
+
Returns the name of the schema
|
|
26
|
+
'''
|
|
27
|
+
return self.__name
|
|
28
|
+
|
|
29
|
+
def _from_dict(self, manifest, keypath, version=None):
|
|
30
|
+
if keypath:
|
|
31
|
+
self.__name = keypath[-1]
|
|
32
|
+
|
|
33
|
+
return super()._from_dict(manifest, keypath, version=version)
|
|
34
|
+
|
|
35
|
+
def copy(self, key=None):
|
|
36
|
+
copy = super().copy(key=key)
|
|
37
|
+
|
|
38
|
+
if not self.__name and key and key[-1] != "default":
|
|
39
|
+
copy.__name = key[-1]
|
|
40
|
+
|
|
41
|
+
return copy
|