siliconcompiler 0.34.1__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 (129) hide show
  1. siliconcompiler/__init__.py +23 -4
  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 +7 -6
  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/__init__.py +17 -0
  19. siliconcompiler/constraints/asic_component.py +378 -0
  20. siliconcompiler/constraints/asic_floorplan.py +449 -0
  21. siliconcompiler/constraints/asic_pins.py +489 -0
  22. siliconcompiler/constraints/asic_timing.py +517 -0
  23. siliconcompiler/core.py +10 -35
  24. siliconcompiler/data/templates/tcl/manifest.tcl.j2 +8 -0
  25. siliconcompiler/dependencyschema.py +96 -202
  26. siliconcompiler/design.py +327 -241
  27. siliconcompiler/filesetschema.py +250 -0
  28. siliconcompiler/flowgraph.py +298 -106
  29. siliconcompiler/fpga.py +124 -1
  30. siliconcompiler/library.py +331 -0
  31. siliconcompiler/metric.py +327 -92
  32. siliconcompiler/metrics/__init__.py +7 -0
  33. siliconcompiler/metrics/asic.py +245 -0
  34. siliconcompiler/metrics/fpga.py +220 -0
  35. siliconcompiler/package/__init__.py +391 -67
  36. siliconcompiler/package/git.py +92 -16
  37. siliconcompiler/package/github.py +114 -22
  38. siliconcompiler/package/https.py +79 -16
  39. siliconcompiler/packageschema.py +341 -16
  40. siliconcompiler/pathschema.py +255 -0
  41. siliconcompiler/pdk.py +566 -1
  42. siliconcompiler/project.py +1460 -0
  43. siliconcompiler/record.py +38 -1
  44. siliconcompiler/remote/__init__.py +5 -2
  45. siliconcompiler/remote/client.py +11 -6
  46. siliconcompiler/remote/schema.py +5 -23
  47. siliconcompiler/remote/server.py +41 -54
  48. siliconcompiler/report/__init__.py +3 -3
  49. siliconcompiler/report/dashboard/__init__.py +48 -14
  50. siliconcompiler/report/dashboard/cli/__init__.py +99 -21
  51. siliconcompiler/report/dashboard/cli/board.py +364 -179
  52. siliconcompiler/report/dashboard/web/__init__.py +90 -12
  53. siliconcompiler/report/dashboard/web/components/__init__.py +219 -240
  54. siliconcompiler/report/dashboard/web/components/flowgraph.py +49 -26
  55. siliconcompiler/report/dashboard/web/components/graph.py +139 -100
  56. siliconcompiler/report/dashboard/web/layouts/__init__.py +29 -1
  57. siliconcompiler/report/dashboard/web/layouts/_common.py +38 -2
  58. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph.py +39 -26
  59. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph_node_tab.py +50 -50
  60. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph_sac_tabs.py +49 -46
  61. siliconcompiler/report/dashboard/web/state.py +141 -14
  62. siliconcompiler/report/dashboard/web/utils/__init__.py +79 -16
  63. siliconcompiler/report/dashboard/web/utils/file_utils.py +74 -11
  64. siliconcompiler/report/dashboard/web/viewer.py +25 -1
  65. siliconcompiler/report/report.py +5 -2
  66. siliconcompiler/report/summary_image.py +29 -11
  67. siliconcompiler/scheduler/__init__.py +9 -1
  68. siliconcompiler/scheduler/docker.py +81 -4
  69. siliconcompiler/scheduler/run_node.py +37 -20
  70. siliconcompiler/scheduler/scheduler.py +211 -36
  71. siliconcompiler/scheduler/schedulernode.py +394 -60
  72. siliconcompiler/scheduler/send_messages.py +77 -29
  73. siliconcompiler/scheduler/slurm.py +76 -12
  74. siliconcompiler/scheduler/taskscheduler.py +142 -21
  75. siliconcompiler/schema/__init__.py +0 -4
  76. siliconcompiler/schema/baseschema.py +338 -59
  77. siliconcompiler/schema/editableschema.py +14 -6
  78. siliconcompiler/schema/journal.py +28 -17
  79. siliconcompiler/schema/namedschema.py +22 -14
  80. siliconcompiler/schema/parameter.py +89 -28
  81. siliconcompiler/schema/parametertype.py +2 -0
  82. siliconcompiler/schema/parametervalue.py +258 -15
  83. siliconcompiler/schema/safeschema.py +25 -2
  84. siliconcompiler/schema/schema_cfg.py +23 -19
  85. siliconcompiler/schema/utils.py +2 -2
  86. siliconcompiler/schema_obj.py +24 -5
  87. siliconcompiler/tool.py +1131 -265
  88. siliconcompiler/tools/bambu/__init__.py +41 -0
  89. siliconcompiler/tools/builtin/concatenate.py +2 -2
  90. siliconcompiler/tools/builtin/minimum.py +2 -1
  91. siliconcompiler/tools/builtin/mux.py +2 -1
  92. siliconcompiler/tools/builtin/nop.py +2 -1
  93. siliconcompiler/tools/builtin/verify.py +2 -1
  94. siliconcompiler/tools/klayout/__init__.py +95 -0
  95. siliconcompiler/tools/openroad/__init__.py +289 -0
  96. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +3 -0
  97. siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +7 -2
  98. siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +8 -4
  99. siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +9 -5
  100. siliconcompiler/tools/openroad/scripts/common/write_images.tcl +5 -1
  101. siliconcompiler/tools/slang/__init__.py +1 -1
  102. siliconcompiler/tools/slang/elaborate.py +2 -1
  103. siliconcompiler/tools/vivado/scripts/sc_run.tcl +1 -1
  104. siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +8 -1
  105. siliconcompiler/tools/vivado/syn_fpga.py +6 -0
  106. siliconcompiler/tools/vivado/vivado.py +35 -2
  107. siliconcompiler/tools/vpr/__init__.py +150 -0
  108. siliconcompiler/tools/yosys/__init__.py +369 -1
  109. siliconcompiler/tools/yosys/scripts/procs.tcl +0 -1
  110. siliconcompiler/toolscripts/_tools.json +5 -10
  111. siliconcompiler/utils/__init__.py +66 -0
  112. siliconcompiler/utils/flowgraph.py +2 -2
  113. siliconcompiler/utils/issue.py +2 -1
  114. siliconcompiler/utils/logging.py +14 -0
  115. siliconcompiler/utils/multiprocessing.py +256 -0
  116. siliconcompiler/utils/showtools.py +10 -0
  117. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/METADATA +6 -6
  118. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/RECORD +122 -115
  119. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/entry_points.txt +3 -0
  120. siliconcompiler/schema/cmdlineschema.py +0 -250
  121. siliconcompiler/schema/packageschema.py +0 -101
  122. siliconcompiler/toolscripts/rhel8/install-slang.sh +0 -40
  123. siliconcompiler/toolscripts/rhel9/install-slang.sh +0 -40
  124. siliconcompiler/toolscripts/ubuntu20/install-slang.sh +0 -47
  125. siliconcompiler/toolscripts/ubuntu22/install-slang.sh +0 -37
  126. siliconcompiler/toolscripts/ubuntu24/install-slang.sh +0 -37
  127. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/WHEEL +0 -0
  128. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/licenses/LICENSE +0 -0
  129. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/top_level.txt +0 -0
@@ -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, Dict, List
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
 
@@ -43,7 +45,7 @@ class Journal:
43
45
  child.__parent = self.__parent
44
46
  return child
45
47
 
46
- def from_dict(self, manifest):
48
+ def from_dict(self, manifest: Dict):
47
49
  '''
48
50
  Import a journal from a manifest dictionary
49
51
 
@@ -53,33 +55,33 @@ class Journal:
53
55
 
54
56
  self.__journal = manifest
55
57
 
56
- def get(self):
58
+ def get(self) -> List[Dict]:
57
59
  """
58
60
  Returns a copy of the current journal
59
61
  """
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
  '''
@@ -116,6 +124,9 @@ class Journal:
116
124
  if record_type not in self.__parent.__record_types:
117
125
  return
118
126
 
127
+ if isinstance(value, set):
128
+ value = list(value)
129
+
119
130
  self.__parent.__journal.append({
120
131
  "type": record_type,
121
132
  "key": tuple([*self.__keyprefix, *key]),
@@ -125,7 +136,7 @@ class Journal:
125
136
  "index": index
126
137
  })
127
138
 
128
- def start(self):
139
+ def start(self) -> None:
129
140
  '''
130
141
  Start journaling the schema transactions
131
142
  '''
@@ -135,7 +146,7 @@ class Journal:
135
146
  self.add_type("remove")
136
147
  self.add_type("unset")
137
148
 
138
- def stop(self):
149
+ def stop(self) -> None:
139
150
  '''
140
151
  Stop journaling the schema transactions
141
152
  '''
@@ -143,7 +154,7 @@ class Journal:
143
154
  self.__parent.__record_types.clear()
144
155
 
145
156
  @staticmethod
146
- def replay_file(schema, filepath):
157
+ def replay_file(schema, filepath: str) -> None:
147
158
  '''
148
159
  Replay a journal into a schema from a manifest
149
160
 
@@ -160,7 +171,7 @@ class Journal:
160
171
  journal.from_dict(data["__journal__"])
161
172
  journal.replay(schema)
162
173
 
163
- def replay(self, schema):
174
+ def replay(self, schema) -> None:
164
175
  '''
165
176
  Replay journal into a schema
166
177
 
@@ -196,7 +207,7 @@ class Journal:
196
207
  raise ValueError(f'Unknown record type {record_type}')
197
208
 
198
209
  @staticmethod
199
- def access(schema):
210
+ def access(schema) -> "Journal":
200
211
  '''
201
212
  Access a journal from a schema
202
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
 
@@ -15,12 +17,13 @@ class NamedSchema(BaseSchema):
15
17
  name (str): name of the schema
16
18
  '''
17
19
 
18
- def __init__(self, name=None):
20
+ def __init__(self, name: str = None):
19
21
  super().__init__()
20
22
 
21
- self.__name = name
23
+ self.set_name(name)
22
24
 
23
- def name(self):
25
+ @property
26
+ def name(self) -> str:
24
27
  '''
25
28
  Returns the name of the schema
26
29
  '''
@@ -29,7 +32,7 @@ class NamedSchema(BaseSchema):
29
32
  except AttributeError:
30
33
  return None
31
34
 
32
- def set_name(self, name):
35
+ def set_name(self, name: str) -> None:
33
36
  """
34
37
  Set the name of this object
35
38
 
@@ -40,24 +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.")
48
+ if name is not None and "." in name:
49
+ raise ValueError("Named schema object cannot contains: .")
45
50
  self.__name = name
46
51
 
47
- def _reset(self) -> None:
52
+ def type(self) -> str:
48
53
  """
49
- Resets the state of the object
54
+ Returns the type of this object
50
55
  """
51
- pass
56
+ raise NotImplementedError("Must be implemented by the child classes.")
52
57
 
53
- def type(self) -> str:
58
+ @classmethod
59
+ def _getdict_type(cls) -> str:
54
60
  """
55
- Returns the type of this object
61
+ Returns the meta data for getdict
56
62
  """
63
+
57
64
  raise NotImplementedError("Must be implemented by the child classes.")
58
65
 
59
66
  @classmethod
60
- def from_manifest(cls, name, filepath=None, cfg=None):
67
+ def from_manifest(cls, name: str, filepath: str = None, cfg: Dict = None):
61
68
  '''
62
69
  Create a new schema based on the provided source files.
63
70
 
@@ -72,20 +79,21 @@ class NamedSchema(BaseSchema):
72
79
  if not filepath and cfg is None:
73
80
  raise RuntimeError("filepath or dictionary is required")
74
81
 
75
- schema = cls(name)
82
+ schema = cls()
83
+ schema.set_name(name)
76
84
  if filepath:
77
85
  schema.read_manifest(filepath)
78
86
  if cfg:
79
87
  schema._from_dict(cfg, [])
80
88
  return schema
81
89
 
82
- def _from_dict(self, manifest, keypath, version=None):
90
+ def _from_dict(self, manifest: Dict, keypath: Tuple[str], version: str = None):
83
91
  if keypath:
84
92
  self.__name = keypath[-1]
85
93
 
86
94
  return super()._from_dict(manifest, keypath, version=version)
87
95
 
88
- def copy(self, key=None):
96
+ def copy(self, key: Tuple[str] = None) -> "NamedSchema":
89
97
  copy = super().copy(key=key)
90
98
 
91
99
  if key and key[-1] != "default":
@@ -11,7 +11,8 @@ import shlex
11
11
 
12
12
  from enum import Enum
13
13
 
14
- from .parametervalue import NodeValue, DirectoryNodeValue, FileNodeValue, NodeListValue
14
+ from .parametervalue import NodeValue, DirectoryNodeValue, FileNodeValue, NodeListValue, \
15
+ NodeSetValue
15
16
  from .parametertype import NodeType, NodeEnumType
16
17
 
17
18
 
@@ -59,6 +60,7 @@ class Parameter:
59
60
  example (list of str): example field
60
61
  help (str): help field
61
62
  pernode (:class:`.PerNode`): pernode field
63
+ kwargs: forwarded to default value constructor
62
64
  '''
63
65
 
64
66
  GLOBAL_KEY = 'global'
@@ -77,7 +79,8 @@ class Parameter:
77
79
  switch=None,
78
80
  example=None,
79
81
  help=None,
80
- pernode=PerNode.NEVER):
82
+ pernode=PerNode.NEVER,
83
+ **kwargs):
81
84
 
82
85
  self.__type = NodeType.parse(type)
83
86
  self.__scope = Scope(scope)
@@ -108,7 +111,7 @@ class Parameter:
108
111
 
109
112
  self.__pernode = PerNode(pernode)
110
113
 
111
- self.__setdefvalue(defvalue)
114
+ self.__setdefvalue(defvalue, **kwargs)
112
115
 
113
116
  self.__node = {}
114
117
 
@@ -123,26 +126,33 @@ class Parameter:
123
126
  self.__hashalgo = str(hashalgo)
124
127
  self.__copy = bool(copy)
125
128
 
126
- def __setdefvalue(self, defvalue):
129
+ def __setdefvalue(self, defvalue, **kwargs):
127
130
  if NodeType.contains(self.__type, 'file'):
128
- base = FileNodeValue(defvalue)
129
131
  if isinstance(self.__type, list):
130
- self.__defvalue = NodeListValue(base)
132
+ self.__defvalue = NodeListValue(FileNodeValue(defvalue, **kwargs))
133
+ elif isinstance(self.__type, set):
134
+ self.__defvalue = NodeSetValue(FileNodeValue(defvalue, **kwargs))
131
135
  else:
132
- self.__defvalue = base
136
+ self.__defvalue = FileNodeValue(defvalue, **kwargs)
133
137
  elif NodeType.contains(self.__type, 'dir'):
134
- base = DirectoryNodeValue(defvalue)
135
138
  if isinstance(self.__type, list):
136
- self.__defvalue = NodeListValue(base)
139
+ self.__defvalue = NodeListValue(DirectoryNodeValue(defvalue, **kwargs))
140
+ elif isinstance(self.__type, set):
141
+ self.__defvalue = NodeSetValue(DirectoryNodeValue(defvalue, **kwargs))
137
142
  else:
138
- self.__defvalue = base
143
+ self.__defvalue = DirectoryNodeValue(defvalue, **kwargs)
139
144
  else:
145
+ kwargs = {}
140
146
  if isinstance(self.__type, list):
141
- self.__defvalue = NodeListValue(NodeValue(self.__type[0]))
147
+ self.__defvalue = NodeListValue(NodeValue(self.__type[0], **kwargs))
148
+ if defvalue:
149
+ self.__defvalue.set(defvalue)
150
+ elif isinstance(self.__type, set):
151
+ self.__defvalue = NodeSetValue(NodeValue(list(self.__type)[0], **kwargs))
142
152
  if defvalue:
143
153
  self.__defvalue.set(defvalue)
144
154
  else:
145
- self.__defvalue = NodeValue(self.__type, value=defvalue)
155
+ self.__defvalue = NodeValue(self.__type, value=defvalue, **kwargs)
146
156
 
147
157
  def __str__(self):
148
158
  return str(self.getvalues())
@@ -344,7 +354,7 @@ class Parameter:
344
354
 
345
355
  if field in self.__defvalue.fields:
346
356
  if not self.is_list() and field == 'value':
347
- raise ValueError("add can only be used on lists")
357
+ raise ValueError("add can only be used on lists or sets")
348
358
 
349
359
  if isinstance(index, int):
350
360
  index = str(index)
@@ -409,6 +419,12 @@ class Parameter:
409
419
 
410
420
  return True
411
421
 
422
+ def reset(self):
423
+ """
424
+ Resets a parameter back to its default state
425
+ """
426
+ self.__node = {}
427
+
412
428
  def getdict(self, include_default=True, values_only=False):
413
429
  """
414
430
  Returns a schema dictionary.
@@ -523,15 +539,14 @@ class Parameter:
523
539
  requires_set = NodeType.contains(self.__type, tuple) or NodeType.contains(self.__type, set)
524
540
 
525
541
  try:
526
- defvalue = manifest["node"]["default"]["default"]["value"]
542
+ defvalue = manifest["node"]["default"]["default"]
527
543
  del manifest["node"]["default"]
528
544
  except KeyError:
529
545
  defvalue = None
530
546
 
531
- if requires_set:
532
- self.__setdefvalue(NodeType.normalize(defvalue, self.__type))
533
- else:
534
- self.__setdefvalue(defvalue)
547
+ self.__setdefvalue(None)
548
+ if defvalue:
549
+ self.__defvalue._from_dict(defvalue, keypath, version)
535
550
 
536
551
  for step, indexdata in manifest["node"].items():
537
552
  self.__node[step] = {}
@@ -559,14 +574,27 @@ class Parameter:
559
574
 
560
575
  if self.__pernode == PerNode.REQUIRED and (step is None or index is None):
561
576
  return None
562
- if not self.__pernode.is_never():
563
- value = self.get(step=step, index=index)
564
- else:
565
- value = self.get()
566
577
 
567
- return NodeType.to_tcl(value, self.__type)
578
+ if isinstance(index, int):
579
+ index = str(index)
580
+
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()
568
596
 
569
- def getvalues(self, return_defvalue=True):
597
+ def getvalues(self, return_defvalue=True, return_values=True):
570
598
  """
571
599
  Returns all values (global and pernode) associated with a particular parameter.
572
600
 
@@ -584,10 +612,16 @@ class Parameter:
584
612
  index_arg = None if index == Parameter.GLOBAL_KEY else index
585
613
  if step_arg is None and index_arg is None:
586
614
  has_global = True
587
- 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))
588
619
 
589
- if (self.__pernode != PerNode.REQUIRED) and not has_global and return_defvalue:
590
- 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))
591
625
 
592
626
  return vals
593
627
 
@@ -607,7 +641,7 @@ class Parameter:
607
641
  Returns true is this parameter is a list type
608
642
  """
609
643
 
610
- return isinstance(self.__type, list)
644
+ return isinstance(self.__type, (list, set))
611
645
 
612
646
  def is_empty(self):
613
647
  '''
@@ -641,6 +675,33 @@ class Parameter:
641
675
  index in self.__node[step] and \
642
676
  self.__node[step][index]
643
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
+
644
705
  @property
645
706
  def default(self):
646
707
  """
@@ -100,6 +100,8 @@ class NodeType:
100
100
  return True
101
101
  if isinstance(value, list):
102
102
  return NodeType.contains(value[0], check)
103
+ if isinstance(value, set):
104
+ return NodeType.contains(list(value)[0], check)
103
105
  if isinstance(value, tuple):
104
106
  return any([NodeType.contains(v, check) for v in value])
105
107
  return value == check