siliconcompiler 0.34.2__py3-none-any.whl → 0.34.3__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 (121) hide show
  1. siliconcompiler/__init__.py +12 -5
  2. siliconcompiler/__main__.py +1 -7
  3. siliconcompiler/_metadata.py +1 -1
  4. siliconcompiler/apps/_common.py +104 -23
  5. siliconcompiler/apps/sc.py +4 -8
  6. siliconcompiler/apps/sc_dashboard.py +6 -4
  7. siliconcompiler/apps/sc_install.py +10 -6
  8. siliconcompiler/apps/sc_issue.py +7 -5
  9. siliconcompiler/apps/sc_remote.py +1 -1
  10. siliconcompiler/apps/sc_server.py +9 -14
  11. siliconcompiler/apps/sc_show.py +6 -5
  12. siliconcompiler/apps/smake.py +130 -94
  13. siliconcompiler/apps/utils/replay.py +4 -7
  14. siliconcompiler/apps/utils/summarize.py +3 -5
  15. siliconcompiler/asic.py +420 -0
  16. siliconcompiler/checklist.py +25 -2
  17. siliconcompiler/cmdlineschema.py +534 -0
  18. siliconcompiler/constraints/asic_component.py +2 -2
  19. siliconcompiler/constraints/asic_pins.py +2 -2
  20. siliconcompiler/constraints/asic_timing.py +3 -3
  21. siliconcompiler/core.py +7 -32
  22. siliconcompiler/data/templates/tcl/manifest.tcl.j2 +8 -0
  23. siliconcompiler/dependencyschema.py +89 -31
  24. siliconcompiler/design.py +176 -207
  25. siliconcompiler/filesetschema.py +250 -0
  26. siliconcompiler/flowgraph.py +274 -95
  27. siliconcompiler/fpga.py +124 -1
  28. siliconcompiler/library.py +218 -20
  29. siliconcompiler/metric.py +233 -20
  30. siliconcompiler/package/__init__.py +271 -50
  31. siliconcompiler/package/git.py +92 -16
  32. siliconcompiler/package/github.py +108 -12
  33. siliconcompiler/package/https.py +79 -16
  34. siliconcompiler/packageschema.py +88 -7
  35. siliconcompiler/pathschema.py +31 -2
  36. siliconcompiler/pdk.py +566 -1
  37. siliconcompiler/project.py +1095 -94
  38. siliconcompiler/record.py +38 -1
  39. siliconcompiler/remote/__init__.py +5 -2
  40. siliconcompiler/remote/client.py +11 -6
  41. siliconcompiler/remote/schema.py +5 -23
  42. siliconcompiler/remote/server.py +41 -54
  43. siliconcompiler/report/__init__.py +3 -3
  44. siliconcompiler/report/dashboard/__init__.py +48 -14
  45. siliconcompiler/report/dashboard/cli/__init__.py +99 -21
  46. siliconcompiler/report/dashboard/cli/board.py +364 -179
  47. siliconcompiler/report/dashboard/web/__init__.py +90 -12
  48. siliconcompiler/report/dashboard/web/components/__init__.py +219 -240
  49. siliconcompiler/report/dashboard/web/components/flowgraph.py +49 -26
  50. siliconcompiler/report/dashboard/web/components/graph.py +139 -100
  51. siliconcompiler/report/dashboard/web/layouts/__init__.py +29 -1
  52. siliconcompiler/report/dashboard/web/layouts/_common.py +38 -2
  53. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph.py +39 -26
  54. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph_node_tab.py +50 -50
  55. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph_sac_tabs.py +49 -46
  56. siliconcompiler/report/dashboard/web/state.py +141 -14
  57. siliconcompiler/report/dashboard/web/utils/__init__.py +79 -16
  58. siliconcompiler/report/dashboard/web/utils/file_utils.py +74 -11
  59. siliconcompiler/report/dashboard/web/viewer.py +25 -1
  60. siliconcompiler/report/report.py +5 -2
  61. siliconcompiler/report/summary_image.py +29 -11
  62. siliconcompiler/scheduler/__init__.py +9 -1
  63. siliconcompiler/scheduler/docker.py +79 -1
  64. siliconcompiler/scheduler/run_node.py +35 -19
  65. siliconcompiler/scheduler/scheduler.py +208 -24
  66. siliconcompiler/scheduler/schedulernode.py +372 -46
  67. siliconcompiler/scheduler/send_messages.py +77 -29
  68. siliconcompiler/scheduler/slurm.py +76 -12
  69. siliconcompiler/scheduler/taskscheduler.py +140 -20
  70. siliconcompiler/schema/__init__.py +0 -2
  71. siliconcompiler/schema/baseschema.py +194 -38
  72. siliconcompiler/schema/journal.py +7 -4
  73. siliconcompiler/schema/namedschema.py +16 -10
  74. siliconcompiler/schema/parameter.py +55 -9
  75. siliconcompiler/schema/parametervalue.py +60 -0
  76. siliconcompiler/schema/safeschema.py +25 -2
  77. siliconcompiler/schema/schema_cfg.py +5 -5
  78. siliconcompiler/schema/utils.py +2 -2
  79. siliconcompiler/schema_obj.py +20 -3
  80. siliconcompiler/tool.py +979 -302
  81. siliconcompiler/tools/bambu/__init__.py +41 -0
  82. siliconcompiler/tools/builtin/concatenate.py +2 -2
  83. siliconcompiler/tools/builtin/minimum.py +2 -1
  84. siliconcompiler/tools/builtin/mux.py +2 -1
  85. siliconcompiler/tools/builtin/nop.py +2 -1
  86. siliconcompiler/tools/builtin/verify.py +2 -1
  87. siliconcompiler/tools/klayout/__init__.py +95 -0
  88. siliconcompiler/tools/openroad/__init__.py +289 -0
  89. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +3 -0
  90. siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +7 -2
  91. siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +8 -4
  92. siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +9 -5
  93. siliconcompiler/tools/openroad/scripts/common/write_images.tcl +5 -1
  94. siliconcompiler/tools/slang/__init__.py +1 -1
  95. siliconcompiler/tools/slang/elaborate.py +2 -1
  96. siliconcompiler/tools/vivado/scripts/sc_run.tcl +1 -1
  97. siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +8 -1
  98. siliconcompiler/tools/vivado/syn_fpga.py +6 -0
  99. siliconcompiler/tools/vivado/vivado.py +35 -2
  100. siliconcompiler/tools/vpr/__init__.py +150 -0
  101. siliconcompiler/tools/yosys/__init__.py +369 -1
  102. siliconcompiler/tools/yosys/scripts/procs.tcl +0 -1
  103. siliconcompiler/toolscripts/_tools.json +5 -10
  104. siliconcompiler/utils/__init__.py +66 -0
  105. siliconcompiler/utils/flowgraph.py +2 -2
  106. siliconcompiler/utils/issue.py +2 -1
  107. siliconcompiler/utils/logging.py +14 -0
  108. siliconcompiler/utils/multiprocessing.py +256 -0
  109. siliconcompiler/utils/showtools.py +10 -0
  110. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/METADATA +5 -5
  111. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/RECORD +115 -118
  112. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/entry_points.txt +3 -0
  113. siliconcompiler/schema/cmdlineschema.py +0 -250
  114. siliconcompiler/toolscripts/rhel8/install-slang.sh +0 -40
  115. siliconcompiler/toolscripts/rhel9/install-slang.sh +0 -40
  116. siliconcompiler/toolscripts/ubuntu20/install-slang.sh +0 -47
  117. siliconcompiler/toolscripts/ubuntu22/install-slang.sh +0 -37
  118. siliconcompiler/toolscripts/ubuntu24/install-slang.sh +0 -37
  119. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/WHEEL +0 -0
  120. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/licenses/LICENSE +0 -0
  121. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,8 @@
6
6
 
7
7
  import contextlib
8
8
  import copy
9
+ import importlib
10
+ import logging
9
11
 
10
12
  try:
11
13
  import gzip
@@ -22,6 +24,9 @@ except ModuleNotFoundError:
22
24
 
23
25
  import os.path
24
26
 
27
+ from functools import cache
28
+ from typing import Dict, Type, Tuple, Union, Set, Callable, List
29
+
25
30
  from .parameter import Parameter, NodeValue
26
31
  from .journal import Journal
27
32
 
@@ -49,7 +54,88 @@ class BaseSchema:
49
54
  return tuple()
50
55
  return tuple([*self.__parent._keypath, self.__key])
51
56
 
52
- def _from_dict(self, manifest, keypath, version=None):
57
+ @staticmethod
58
+ @cache
59
+ def __get_child_classes() -> Dict[str, Type["BaseSchema"]]:
60
+ """
61
+ Returns all known subclasses of BaseSchema
62
+ """
63
+ def recurse(cls):
64
+ subclss = set()
65
+ subclss.add(cls)
66
+ for subcls in cls.__subclasses__():
67
+ subclss.update(recurse(subcls))
68
+ return subclss
69
+
70
+ # Resolve true base
71
+ cls_mapping = {}
72
+ for cls in recurse(BaseSchema):
73
+ try:
74
+ cls_mapping.setdefault(cls._getdict_type(), set()).add(cls)
75
+ except NotImplementedError:
76
+ pass
77
+
78
+ # Build lookup table
79
+ cls_map = {}
80
+ for cls_type, clss in cls_mapping.items():
81
+ for cls in clss:
82
+ cls_map[f"{cls.__module__}/{cls.__name__}"] = cls
83
+
84
+ if len(clss) > 1:
85
+ found = False
86
+ for cls in clss:
87
+ if cls.__name__ == cls_type:
88
+ cls_map[cls_type] = cls
89
+ found = True
90
+ break
91
+ if not found:
92
+ raise RuntimeError(f"fatal error at: {cls_type}")
93
+ else:
94
+ cls_map[cls_type] = list(clss)[0]
95
+ return cls_map
96
+
97
+ @staticmethod
98
+ @cache
99
+ def __load_schema_class(cls_name: str) -> Type["BaseSchema"]:
100
+ """
101
+ Load a schema class from a string
102
+ """
103
+ try:
104
+ module_name, cls_name = cls_name.split("/")
105
+ except (ValueError, AttributeError):
106
+ return None
107
+
108
+ try:
109
+ module = importlib.import_module(module_name)
110
+ except (ImportError, ModuleNotFoundError, SyntaxError):
111
+ return None
112
+
113
+ cls = getattr(module, cls_name, None)
114
+ if not cls:
115
+ return None
116
+ if not issubclass(cls, BaseSchema):
117
+ raise TypeError(f"{cls_name} must be a BaseSchema type")
118
+ return cls
119
+
120
+ @staticmethod
121
+ def __process_meta_section(meta: Dict[str, str]) -> Type["BaseSchema"]:
122
+ """
123
+ Handle __meta__ section of the schema by loading the appropriate class
124
+ """
125
+ cls_map = BaseSchema.__get_child_classes()
126
+
127
+ # Lookup object, use class first, then type
128
+ cls_name = meta.get("class", None)
129
+ cls = None
130
+ if cls_name:
131
+ cls = cls_map.get(cls_name, None)
132
+ if not cls:
133
+ cls = BaseSchema.__load_schema_class(cls_name)
134
+ if not cls:
135
+ cls = cls_map.get(meta.get("sctype", None), None)
136
+ return cls
137
+
138
+ def _from_dict(self, manifest: Dict, keypath: Tuple[str], version: str = None):
53
139
  '''
54
140
  Decodes a dictionary into a schema object
55
141
 
@@ -64,6 +150,10 @@ class BaseSchema:
64
150
 
65
151
  if "__journal__" in manifest:
66
152
  self.__journal.from_dict(manifest["__journal__"])
153
+ del manifest["__journal__"]
154
+
155
+ if "__meta__" in manifest:
156
+ del manifest["__meta__"]
67
157
 
68
158
  if self.__default:
69
159
  data = manifest.get("default", None)
@@ -74,9 +164,25 @@ class BaseSchema:
74
164
 
75
165
  for key, data in manifest.items():
76
166
  obj = self.__manifest.get(key, None)
167
+ if not obj and isinstance(data, dict) and "__meta__" in data:
168
+ # Lookup object, use class first, then type
169
+ cls = BaseSchema.__process_meta_section(data["__meta__"])
170
+ if cls is BaseSchema and self.__default:
171
+ # Use default when BaseSchema is the class
172
+ obj = self.__default.copy(key=keypath + [key])
173
+ self.__manifest[key] = obj
174
+ elif cls:
175
+ # Create object and connect to schema
176
+ obj = cls()
177
+ obj.__parent = self
178
+ obj.__key = key
179
+ self.__manifest[key] = obj
180
+
181
+ # Use default if it is available
77
182
  if not obj and self.__default:
78
- obj = self.__default.copy()
183
+ obj = self.__default.copy(key=keypath + [key])
79
184
  self.__manifest[key] = obj
185
+
80
186
  if obj:
81
187
  obj._from_dict(data, keypath + [key], version=version)
82
188
  handled.add(key)
@@ -87,7 +193,7 @@ class BaseSchema:
87
193
 
88
194
  # Manifest methods
89
195
  @classmethod
90
- def from_manifest(cls, filepath=None, cfg=None):
196
+ def from_manifest(cls, filepath: str = None, cfg: Dict = None) -> "BaseSchema":
91
197
  '''
92
198
  Create a new schema based on the provided source files.
93
199
 
@@ -101,15 +207,24 @@ class BaseSchema:
101
207
  if not filepath and cfg is None:
102
208
  raise RuntimeError("filepath or dictionary is required")
103
209
 
104
- schema = cls()
105
210
  if filepath:
106
- schema.read_manifest(filepath)
107
- if cfg:
108
- schema._from_dict(cfg, [])
211
+ cfg = BaseSchema._read_manifest(filepath)
212
+
213
+ new_cls = None
214
+ if "__meta__" in cfg:
215
+ # Determine correct class
216
+ new_cls = BaseSchema.__process_meta_section(cfg["__meta__"])
217
+ if new_cls:
218
+ schema = new_cls()
219
+ else:
220
+ schema = cls()
221
+
222
+ schema._from_dict(cfg, [])
223
+
109
224
  return schema
110
225
 
111
226
  @staticmethod
112
- def __open_file(filepath, is_read=True):
227
+ def __open_file(filepath: str, is_read: bool = True):
113
228
  _, ext = os.path.splitext(filepath)
114
229
  if ext.lower() == ".gz":
115
230
  if not _has_gzip:
@@ -117,10 +232,27 @@ class BaseSchema:
117
232
  return gzip.open(filepath, mode="rt" if is_read else "wt", encoding="utf-8")
118
233
  return open(filepath, mode="r" if is_read else "w", encoding="utf-8")
119
234
 
120
- def __format_key(self, *key):
235
+ def __format_key(self, *key: str):
121
236
  return f"[{','.join([*self._keypath, *key])}]"
122
237
 
123
- def read_manifest(self, filepath):
238
+ @staticmethod
239
+ def _read_manifest(filepath: str):
240
+ """
241
+ Reads a manifest from disk and returns dictionary.
242
+
243
+ Args:
244
+ filename (path): Path to a manifest file to be loaded.
245
+ """
246
+
247
+ fin = BaseSchema.__open_file(filepath)
248
+ try:
249
+ manifest = json.loads(fin.read())
250
+ finally:
251
+ fin.close()
252
+
253
+ return manifest
254
+
255
+ def read_manifest(self, filepath: str):
124
256
  """
125
257
  Reads a manifest from disk and replaces the current data with the data in the file.
126
258
 
@@ -132,13 +264,9 @@ class BaseSchema:
132
264
  Loads the file mychip.json into the current Schema object.
133
265
  """
134
266
 
135
- fin = BaseSchema.__open_file(filepath)
136
- manifest = json.loads(fin.read())
137
- fin.close()
138
-
139
- self._from_dict(manifest, [])
267
+ self._from_dict(BaseSchema._read_manifest(filepath), [])
140
268
 
141
- def write_manifest(self, filepath):
269
+ def write_manifest(self, filepath: str):
142
270
  '''
143
271
  Writes the manifest to a file.
144
272
 
@@ -162,11 +290,11 @@ class BaseSchema:
162
290
 
163
291
  # Accessor methods
164
292
  def __search(self,
165
- *keypath,
166
- insert_defaults=False,
167
- use_default=False,
168
- require_leaf=True,
169
- complete_path=None):
293
+ *keypath: str,
294
+ insert_defaults: bool = False,
295
+ use_default: bool = False,
296
+ require_leaf: bool = True,
297
+ complete_path: bool = None) -> Union["BaseSchema", Parameter]:
170
298
  if len(keypath) == 0:
171
299
  if require_leaf:
172
300
  raise KeyError
@@ -204,7 +332,8 @@ class BaseSchema:
204
332
  complete_path=complete_path)
205
333
  return key_param
206
334
 
207
- def get(self, *keypath, field='value', step=None, index=None):
335
+ def get(self, *keypath: str, field: str = 'value',
336
+ step: str = None, index: Union[int, str] = None):
208
337
  """
209
338
  Returns a parameter field from the schema.
210
339
 
@@ -260,7 +389,8 @@ class BaseSchema:
260
389
  e.args = (new_msg, *e.args[1:])
261
390
  raise e
262
391
 
263
- def set(self, *args, field='value', clobber=True, step=None, index=None):
392
+ def set(self, *args: str, field: str = 'value', clobber: bool = True,
393
+ step: str = None, index: Union[int, str] = None):
264
394
  '''
265
395
  Sets a schema parameter field.
266
396
 
@@ -305,7 +435,8 @@ class BaseSchema:
305
435
  e.args = (new_msg, *e.args[1:])
306
436
  raise e
307
437
 
308
- def add(self, *args, field='value', step=None, index=None):
438
+ def add(self, *args: str, field: str = 'value',
439
+ step: str = None, index: Union[int, str] = None):
309
440
  '''
310
441
  Adds item(s) to a schema parameter list.
311
442
 
@@ -348,7 +479,7 @@ class BaseSchema:
348
479
  e.args = (new_msg, *e.args[1:])
349
480
  raise e
350
481
 
351
- def unset(self, *keypath, step=None, index=None):
482
+ def unset(self, *keypath: str, step: str = None, index: Union[int, str] = None):
352
483
  '''
353
484
  Unsets a schema parameter.
354
485
 
@@ -387,7 +518,7 @@ class BaseSchema:
387
518
  e.args = (new_msg, *e.args[1:])
388
519
  raise e
389
520
 
390
- def remove(self, *keypath):
521
+ def remove(self, *keypath: str):
391
522
  '''
392
523
  Remove a schema parameter and its subparameters.
393
524
 
@@ -417,7 +548,8 @@ class BaseSchema:
417
548
  del key_param.__manifest[removal_key]
418
549
  self.__journal.record("remove", keypath)
419
550
 
420
- def valid(self, *keypath, default_valid=False, check_complete=False):
551
+ def valid(self, *keypath: str, default_valid: bool = False,
552
+ check_complete: bool = False) -> bool:
421
553
  """
422
554
  Checks validity of a keypath.
423
555
 
@@ -451,7 +583,7 @@ class BaseSchema:
451
583
  return isinstance(param, Parameter)
452
584
  return True
453
585
 
454
- def getkeys(self, *keypath):
586
+ def getkeys(self, *keypath: str) -> Tuple[str]:
455
587
  """
456
588
  Returns a tuple of schema dictionary keys.
457
589
 
@@ -481,7 +613,7 @@ class BaseSchema:
481
613
 
482
614
  return tuple(sorted(key_param.__manifest.keys()))
483
615
 
484
- def allkeys(self, *keypath, include_default=True):
616
+ def allkeys(self, *keypath: str, include_default: bool = True) -> Set[Tuple[str]]:
485
617
  '''
486
618
  Returns all keypaths in the schema as a set of tuples.
487
619
 
@@ -510,7 +642,16 @@ class BaseSchema:
510
642
  add(keys, key, item)
511
643
  return set(keys)
512
644
 
513
- def getdict(self, *keypath, include_default=True, values_only=False):
645
+ @classmethod
646
+ def _getdict_type(cls) -> str:
647
+ """
648
+ Returns the meta data for getdict
649
+ """
650
+
651
+ return "BaseSchema"
652
+
653
+ def getdict(self, *keypath: str, include_default: bool = True,
654
+ values_only: bool = False) -> Dict:
514
655
  """
515
656
  Returns a schema dictionary.
516
657
 
@@ -553,10 +694,19 @@ class BaseSchema:
553
694
  if not values_only and self.__journal.has_journaling():
554
695
  manifest["__journal__"] = self.__journal.get()
555
696
 
697
+ if not values_only and self.__class__ is not BaseSchema:
698
+ manifest["__meta__"] = {
699
+ "class": f"{self.__class__.__module__}/{self.__class__.__name__}"
700
+ }
701
+ try:
702
+ manifest["__meta__"]["sctype"] = self._getdict_type()
703
+ except NotImplementedError:
704
+ pass
705
+
556
706
  return manifest
557
707
 
558
708
  # Utility functions
559
- def copy(self, key=None):
709
+ def copy(self, key: Tuple[str] = None):
560
710
  """
561
711
  Returns a copy of this schema.
562
712
 
@@ -579,7 +729,7 @@ class BaseSchema:
579
729
 
580
730
  return schema_copy
581
731
 
582
- def _find_files_search_paths(self, keypath, step, index):
732
+ def _find_files_search_paths(self, keypath: Tuple[str], step: str, index: str):
583
733
  """
584
734
  Returns a list of paths to search during find files.
585
735
 
@@ -601,8 +751,11 @@ class BaseSchema:
601
751
  return {}
602
752
  return self.__parent._find_files_dataroot_resolvers()
603
753
 
604
- def find_files(self, *keypath, missing_ok=False, step=None, index=None,
605
- packages=None, collection_dir=None, cwd=None):
754
+ def find_files(self, *keypath: str, missing_ok: bool = False,
755
+ step: str = None, index: Union[int, str] = None,
756
+ packages: Dict[str, Union[str, Callable]] = None,
757
+ collection_dir: str = None,
758
+ cwd: str = None) -> Union[str, List[str], Set[str]]:
606
759
  """
607
760
  Returns absolute paths to files or directories based on the keypath
608
761
  provided.
@@ -707,8 +860,11 @@ class BaseSchema:
707
860
  return resolved_paths[0]
708
861
  return resolved_paths
709
862
 
710
- def check_filepaths(self, ignore_keys=None, logger=None,
711
- packages=None, collection_dir=None, cwd=None):
863
+ def check_filepaths(self, ignore_keys: bool = None,
864
+ logger: logging.Logger = None,
865
+ packages: Dict[str, Union[str, Callable]] = None,
866
+ collection_dir: str = None,
867
+ cwd: str = None) -> bool:
712
868
  '''
713
869
  Verifies that paths to all files in manifest are valid.
714
870
 
@@ -773,7 +929,7 @@ class BaseSchema:
773
929
 
774
930
  return not error
775
931
 
776
- def _parent(self, root=False):
932
+ def _parent(self, root: bool = False) -> "BaseSchema":
777
933
  '''
778
934
  Returns the parent of this schema section, if root is true the root parent
779
935
  will be returned.
@@ -815,7 +971,7 @@ class BaseSchema:
815
971
  finally:
816
972
  self.__active = orig_active
817
973
 
818
- def _get_active(self, field, defvalue=None):
974
+ def _get_active(self, field: str, defvalue=None):
819
975
  '''
820
976
  Get the value of a specific field.
821
977
 
@@ -1,7 +1,7 @@
1
1
  import copy
2
2
  import json
3
3
 
4
- from typing import Tuple, Set
4
+ from typing import Tuple, Set, Dict, List
5
5
 
6
6
 
7
7
  class Journal:
@@ -45,7 +45,7 @@ class Journal:
45
45
  child.__parent = self.__parent
46
46
  return child
47
47
 
48
- def from_dict(self, manifest):
48
+ def from_dict(self, manifest: Dict):
49
49
  '''
50
50
  Import a journal from a manifest dictionary
51
51
 
@@ -55,7 +55,7 @@ class Journal:
55
55
 
56
56
  self.__journal = manifest
57
57
 
58
- def get(self):
58
+ def get(self) -> List[Dict]:
59
59
  """
60
60
  Returns a copy of the current journal
61
61
  """
@@ -124,6 +124,9 @@ class Journal:
124
124
  if record_type not in self.__parent.__record_types:
125
125
  return
126
126
 
127
+ if isinstance(value, set):
128
+ value = list(value)
129
+
127
130
  self.__parent.__journal.append({
128
131
  "type": record_type,
129
132
  "key": tuple([*self.__keyprefix, *key]),
@@ -204,7 +207,7 @@ class Journal:
204
207
  raise ValueError(f'Unknown record type {record_type}')
205
208
 
206
209
  @staticmethod
207
- def access(schema) -> None:
210
+ def access(schema) -> "Journal":
208
211
  '''
209
212
  Access a journal from a schema
210
213
 
@@ -4,6 +4,8 @@
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
+ from typing import Dict, Tuple
8
+
7
9
  from .baseschema import BaseSchema
8
10
 
9
11
 
@@ -20,6 +22,7 @@ class NamedSchema(BaseSchema):
20
22
 
21
23
  self.set_name(name)
22
24
 
25
+ @property
23
26
  def name(self) -> str:
24
27
  '''
25
28
  Returns the name of the schema
@@ -40,26 +43,28 @@ class NamedSchema(BaseSchema):
40
43
  name (str): name for object
41
44
  """
42
45
 
43
- if self.name() is not None:
46
+ if self.name is not None:
44
47
  raise RuntimeError("Cannot call set_name more than once.")
45
48
  if name is not None and "." in name:
46
49
  raise ValueError("Named schema object cannot contains: .")
47
50
  self.__name = name
48
51
 
49
- def _reset(self) -> None:
52
+ def type(self) -> str:
50
53
  """
51
- Resets the state of the object
54
+ Returns the type of this object
52
55
  """
53
- pass
56
+ raise NotImplementedError("Must be implemented by the child classes.")
54
57
 
55
- def type(self) -> str:
58
+ @classmethod
59
+ def _getdict_type(cls) -> str:
56
60
  """
57
- Returns the type of this object
61
+ Returns the meta data for getdict
58
62
  """
63
+
59
64
  raise NotImplementedError("Must be implemented by the child classes.")
60
65
 
61
66
  @classmethod
62
- def from_manifest(cls, name, filepath=None, cfg=None):
67
+ def from_manifest(cls, name: str, filepath: str = None, cfg: Dict = None):
63
68
  '''
64
69
  Create a new schema based on the provided source files.
65
70
 
@@ -74,20 +79,21 @@ class NamedSchema(BaseSchema):
74
79
  if not filepath and cfg is None:
75
80
  raise RuntimeError("filepath or dictionary is required")
76
81
 
77
- schema = cls(name)
82
+ schema = cls()
83
+ schema.set_name(name)
78
84
  if filepath:
79
85
  schema.read_manifest(filepath)
80
86
  if cfg:
81
87
  schema._from_dict(cfg, [])
82
88
  return schema
83
89
 
84
- def _from_dict(self, manifest, keypath, version=None):
90
+ def _from_dict(self, manifest: Dict, keypath: Tuple[str], version: str = None):
85
91
  if keypath:
86
92
  self.__name = keypath[-1]
87
93
 
88
94
  return super()._from_dict(manifest, keypath, version=version)
89
95
 
90
- def copy(self, key=None):
96
+ def copy(self, key: Tuple[str] = None) -> "NamedSchema":
91
97
  copy = super().copy(key=key)
92
98
 
93
99
  if key and key[-1] != "default":
@@ -574,14 +574,27 @@ class Parameter:
574
574
 
575
575
  if self.__pernode == PerNode.REQUIRED and (step is None or index is None):
576
576
  return None
577
- if not self.__pernode.is_never():
578
- value = self.get(step=step, index=index)
579
- else:
580
- value = self.get()
581
577
 
582
- return NodeType.to_tcl(value, self.__type)
578
+ if isinstance(index, int):
579
+ index = str(index)
583
580
 
584
- def getvalues(self, return_defvalue=True):
581
+ try:
582
+ return self.__node[step][index].gettcl()
583
+ except KeyError:
584
+ if self.__pernode == PerNode.REQUIRED:
585
+ return self.__defvalue.gettcl()
586
+
587
+ try:
588
+ return self.__node[step][Parameter.GLOBAL_KEY].gettcl()
589
+ except KeyError:
590
+ pass
591
+
592
+ try:
593
+ return self.__node[Parameter.GLOBAL_KEY][Parameter.GLOBAL_KEY].gettcl()
594
+ except KeyError:
595
+ return self.__defvalue.gettcl()
596
+
597
+ def getvalues(self, return_defvalue=True, return_values=True):
585
598
  """
586
599
  Returns all values (global and pernode) associated with a particular parameter.
587
600
 
@@ -599,10 +612,16 @@ class Parameter:
599
612
  index_arg = None if index == Parameter.GLOBAL_KEY else index
600
613
  if step_arg is None and index_arg is None:
601
614
  has_global = True
602
- vals.append((self.__node[step][index].get(), step_arg, index_arg))
615
+ if return_values:
616
+ vals.append((self.__node[step][index].get(), step_arg, index_arg))
617
+ else:
618
+ vals.append((self.__node[step][index], step_arg, index_arg))
603
619
 
604
- if (self.__pernode != PerNode.REQUIRED) and not has_global and return_defvalue:
605
- vals.append((self.__defvalue.get(), None, None))
620
+ if self.__pernode != PerNode.REQUIRED and not has_global and return_defvalue:
621
+ if return_values:
622
+ vals.append((self.__defvalue.get(), None, None))
623
+ else:
624
+ vals.append((self.__defvalue, None, None))
606
625
 
607
626
  return vals
608
627
 
@@ -656,6 +675,33 @@ class Parameter:
656
675
  index in self.__node[step] and \
657
676
  self.__node[step][index]
658
677
 
678
+ def has_value(self, step=None, index=None) -> bool:
679
+ '''
680
+ Returns whether the parameter as a value.
681
+
682
+ A value counts as set if a user has set a global value OR a value for
683
+ the provided step/index.
684
+ '''
685
+
686
+ if isinstance(index, int):
687
+ index = str(index)
688
+
689
+ try:
690
+ return self.__node[step][index].has_value
691
+ except KeyError:
692
+ if self.__pernode == PerNode.REQUIRED:
693
+ return self.__defvalue.has_value
694
+
695
+ try:
696
+ return self.__node[step][Parameter.GLOBAL_KEY].has_value
697
+ except KeyError:
698
+ pass
699
+
700
+ try:
701
+ return self.__node[Parameter.GLOBAL_KEY][Parameter.GLOBAL_KEY].has_value
702
+ except KeyError:
703
+ return self.__defvalue.has_value
704
+
659
705
  @property
660
706
  def default(self):
661
707
  """