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.
Files changed (114) hide show
  1. siliconcompiler/__init__.py +14 -2
  2. siliconcompiler/_metadata.py +1 -1
  3. siliconcompiler/apps/_common.py +1 -1
  4. siliconcompiler/apps/sc.py +1 -1
  5. siliconcompiler/apps/sc_issue.py +1 -1
  6. siliconcompiler/apps/sc_remote.py +3 -3
  7. siliconcompiler/apps/sc_show.py +3 -3
  8. siliconcompiler/apps/utils/replay.py +4 -4
  9. siliconcompiler/checklist.py +203 -2
  10. siliconcompiler/constraints/__init__.py +17 -0
  11. siliconcompiler/constraints/asic_component.py +378 -0
  12. siliconcompiler/constraints/asic_floorplan.py +449 -0
  13. siliconcompiler/constraints/asic_pins.py +489 -0
  14. siliconcompiler/constraints/asic_timing.py +517 -0
  15. siliconcompiler/core.py +31 -249
  16. siliconcompiler/data/templates/email/general.j2 +3 -3
  17. siliconcompiler/data/templates/email/summary.j2 +1 -1
  18. siliconcompiler/data/templates/issue/README.txt +1 -1
  19. siliconcompiler/data/templates/report/sc_report.j2 +7 -7
  20. siliconcompiler/dependencyschema.py +10 -174
  21. siliconcompiler/design.py +325 -114
  22. siliconcompiler/flowgraph.py +63 -15
  23. siliconcompiler/library.py +133 -0
  24. siliconcompiler/metric.py +94 -72
  25. siliconcompiler/metrics/__init__.py +7 -0
  26. siliconcompiler/metrics/asic.py +245 -0
  27. siliconcompiler/metrics/fpga.py +220 -0
  28. siliconcompiler/optimizer/vizier.py +2 -2
  29. siliconcompiler/package/__init__.py +138 -35
  30. siliconcompiler/package/github.py +6 -10
  31. siliconcompiler/packageschema.py +256 -12
  32. siliconcompiler/pathschema.py +226 -0
  33. siliconcompiler/pdk.py +5 -5
  34. siliconcompiler/project.py +459 -0
  35. siliconcompiler/remote/client.py +18 -12
  36. siliconcompiler/remote/server.py +2 -2
  37. siliconcompiler/report/dashboard/cli/__init__.py +6 -6
  38. siliconcompiler/report/dashboard/cli/board.py +3 -3
  39. siliconcompiler/report/dashboard/web/components/__init__.py +5 -5
  40. siliconcompiler/report/dashboard/web/components/flowgraph.py +4 -4
  41. siliconcompiler/report/dashboard/web/components/graph.py +2 -2
  42. siliconcompiler/report/dashboard/web/state.py +1 -1
  43. siliconcompiler/report/dashboard/web/utils/__init__.py +5 -5
  44. siliconcompiler/report/html_report.py +1 -1
  45. siliconcompiler/report/report.py +4 -4
  46. siliconcompiler/report/summary_table.py +2 -2
  47. siliconcompiler/report/utils.py +5 -5
  48. siliconcompiler/scheduler/docker.py +4 -10
  49. siliconcompiler/scheduler/run_node.py +4 -8
  50. siliconcompiler/scheduler/scheduler.py +18 -24
  51. siliconcompiler/scheduler/schedulernode.py +161 -143
  52. siliconcompiler/scheduler/send_messages.py +3 -3
  53. siliconcompiler/scheduler/slurm.py +5 -3
  54. siliconcompiler/scheduler/taskscheduler.py +10 -8
  55. siliconcompiler/schema/__init__.py +0 -2
  56. siliconcompiler/schema/baseschema.py +148 -26
  57. siliconcompiler/schema/editableschema.py +14 -6
  58. siliconcompiler/schema/journal.py +23 -15
  59. siliconcompiler/schema/namedschema.py +30 -4
  60. siliconcompiler/schema/parameter.py +34 -19
  61. siliconcompiler/schema/parametertype.py +2 -0
  62. siliconcompiler/schema/parametervalue.py +198 -15
  63. siliconcompiler/schema/schema_cfg.py +18 -14
  64. siliconcompiler/schema_obj.py +5 -3
  65. siliconcompiler/tool.py +591 -179
  66. siliconcompiler/tools/__init__.py +2 -0
  67. siliconcompiler/tools/builtin/_common.py +5 -5
  68. siliconcompiler/tools/builtin/concatenate.py +5 -5
  69. siliconcompiler/tools/builtin/minimum.py +4 -4
  70. siliconcompiler/tools/builtin/mux.py +4 -4
  71. siliconcompiler/tools/builtin/nop.py +4 -4
  72. siliconcompiler/tools/builtin/verify.py +7 -7
  73. siliconcompiler/tools/execute/exec_input.py +1 -1
  74. siliconcompiler/tools/genfasm/genfasm.py +1 -6
  75. siliconcompiler/tools/openroad/_apr.py +5 -1
  76. siliconcompiler/tools/openroad/antenna_repair.py +1 -1
  77. siliconcompiler/tools/openroad/macro_placement.py +1 -1
  78. siliconcompiler/tools/openroad/power_grid.py +1 -1
  79. siliconcompiler/tools/openroad/scripts/common/procs.tcl +5 -0
  80. siliconcompiler/tools/opensta/timing.py +26 -3
  81. siliconcompiler/tools/slang/__init__.py +2 -2
  82. siliconcompiler/tools/surfer/__init__.py +0 -0
  83. siliconcompiler/tools/surfer/show.py +53 -0
  84. siliconcompiler/tools/surfer/surfer.py +30 -0
  85. siliconcompiler/tools/vpr/route.py +27 -14
  86. siliconcompiler/tools/vpr/vpr.py +23 -6
  87. siliconcompiler/tools/yosys/__init__.py +1 -1
  88. siliconcompiler/tools/yosys/scripts/procs.tcl +143 -0
  89. siliconcompiler/tools/yosys/{sc_synth_asic.tcl → scripts/sc_synth_asic.tcl} +4 -0
  90. siliconcompiler/tools/yosys/{sc_synth_fpga.tcl → scripts/sc_synth_fpga.tcl} +24 -77
  91. siliconcompiler/tools/yosys/syn_fpga.py +14 -0
  92. siliconcompiler/toolscripts/_tools.json +9 -13
  93. siliconcompiler/toolscripts/rhel9/install-vpr.sh +0 -2
  94. siliconcompiler/toolscripts/ubuntu22/install-surfer.sh +33 -0
  95. siliconcompiler/toolscripts/ubuntu24/install-surfer.sh +33 -0
  96. siliconcompiler/utils/__init__.py +2 -1
  97. siliconcompiler/utils/flowgraph.py +24 -23
  98. siliconcompiler/utils/issue.py +23 -29
  99. siliconcompiler/utils/logging.py +35 -6
  100. siliconcompiler/utils/showtools.py +6 -1
  101. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/METADATA +15 -25
  102. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/RECORD +109 -97
  103. siliconcompiler/schema/packageschema.py +0 -101
  104. siliconcompiler/tools/yosys/procs.tcl +0 -71
  105. siliconcompiler/toolscripts/rhel9/install-yosys-parmys.sh +0 -68
  106. siliconcompiler/toolscripts/ubuntu22/install-yosys-parmys.sh +0 -68
  107. siliconcompiler/toolscripts/ubuntu24/install-yosys-parmys.sh +0 -68
  108. /siliconcompiler/tools/yosys/{sc_lec.tcl → scripts/sc_lec.tcl} +0 -0
  109. /siliconcompiler/tools/yosys/{sc_screenshot.tcl → scripts/sc_screenshot.tcl} +0 -0
  110. /siliconcompiler/tools/yosys/{syn_strategies.tcl → scripts/syn_strategies.tcl} +0 -0
  111. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/WHEEL +0 -0
  112. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/entry_points.txt +0 -0
  113. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/licenses/LICENSE +0 -0
  114. {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
- threads = tasks[(step, index)].threads
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.__logger._console)
112
- console_format = self.__logger._console.formatter
113
- self.__logger._console.setFormatter(SCBlankLoggerFormatter())
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.__logger._console.setFormatter(console_format)
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.get("package", field="schema")._set_cache(package, path)
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=False,
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"[{','.join(keypath)}] is a complete keypath")
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"[{','.join(keypath)}] is not a valid keypath")
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 [{','.join(keypath)}]: {e.args[0]}"
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"[{','.join(keypath)}] is not a valid keypath")
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 [{','.join(keypath)}]: {e.args[0]}"
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"[{','.join(keypath)}] is not a valid keypath")
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 [{','.join(keypath)}]: {e.args[0]}"
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"[{','.join(keypath)}] is not a valid keypath")
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 [{','.join(keypath)}]: {e.args[0]}"
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"[{','.join(keypath)}] is not a valid keypath")
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"[{','.join(keypath)}] is not a valid keypath")
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
- param = self.get(*keypath, field=None)
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(f'Cannot find files on [{",".join(keypath)}], must be a path type')
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} [{",".join(keypath)}]')
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()}" [{",".join(keypath)}]')
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 [{','.join(keypath)}]{node_indicator} path "
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, keypath, value, fullkey, clobber):
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
- return self.__parent.__record_types.add(value)
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, record_type, key, value=None, field=None, step=None, index=None):
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