siliconcompiler 0.33.2__py3-none-any.whl → 0.34.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.
Files changed (49) hide show
  1. siliconcompiler/__init__.py +2 -0
  2. siliconcompiler/_metadata.py +1 -1
  3. siliconcompiler/apps/sc_issue.py +5 -3
  4. siliconcompiler/apps/sc_remote.py +0 -17
  5. siliconcompiler/checklist.py +1 -1
  6. siliconcompiler/core.py +34 -47
  7. siliconcompiler/dependencyschema.py +392 -0
  8. siliconcompiler/design.py +664 -0
  9. siliconcompiler/flowgraph.py +32 -1
  10. siliconcompiler/package/__init__.py +383 -223
  11. siliconcompiler/package/git.py +75 -77
  12. siliconcompiler/package/github.py +70 -97
  13. siliconcompiler/package/https.py +77 -93
  14. siliconcompiler/packageschema.py +260 -0
  15. siliconcompiler/pdk.py +2 -2
  16. siliconcompiler/remote/client.py +15 -3
  17. siliconcompiler/report/dashboard/cli/board.py +1 -1
  18. siliconcompiler/scheduler/__init__.py +3 -1382
  19. siliconcompiler/scheduler/docker.py +268 -0
  20. siliconcompiler/scheduler/run_node.py +10 -16
  21. siliconcompiler/scheduler/scheduler.py +308 -0
  22. siliconcompiler/scheduler/schedulernode.py +934 -0
  23. siliconcompiler/scheduler/slurm.py +147 -163
  24. siliconcompiler/scheduler/taskscheduler.py +39 -52
  25. siliconcompiler/schema/__init__.py +3 -3
  26. siliconcompiler/schema/baseschema.py +234 -10
  27. siliconcompiler/schema/editableschema.py +4 -0
  28. siliconcompiler/schema/journal.py +210 -0
  29. siliconcompiler/schema/namedschema.py +31 -2
  30. siliconcompiler/schema/parameter.py +14 -1
  31. siliconcompiler/schema/parametervalue.py +1 -34
  32. siliconcompiler/schema/schema_cfg.py +210 -349
  33. siliconcompiler/tool.py +61 -20
  34. siliconcompiler/tools/builtin/concatenate.py +2 -2
  35. siliconcompiler/tools/builtin/verify.py +1 -2
  36. siliconcompiler/tools/openroad/scripts/common/procs.tcl +27 -25
  37. siliconcompiler/tools/vpr/route.py +69 -0
  38. siliconcompiler/toolscripts/_tools.json +4 -4
  39. siliconcompiler/utils/__init__.py +2 -23
  40. siliconcompiler/utils/flowgraph.py +5 -5
  41. siliconcompiler/utils/logging.py +2 -1
  42. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/METADATA +4 -3
  43. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/RECORD +47 -42
  44. siliconcompiler/scheduler/docker_runner.py +0 -254
  45. siliconcompiler/schema/journalingschema.py +0 -242
  46. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/WHEEL +0 -0
  47. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/entry_points.txt +0 -0
  48. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/licenses/LICENSE +0 -0
  49. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/top_level.txt +0 -0
@@ -22,6 +22,7 @@ except ModuleNotFoundError:
22
22
  import os.path
23
23
 
24
24
  from .parameter import Parameter
25
+ from .journal import Journal
25
26
 
26
27
 
27
28
  class BaseSchema:
@@ -34,6 +35,8 @@ class BaseSchema:
34
35
  # Data storage for the schema
35
36
  self.__manifest = {}
36
37
  self.__default = None
38
+ self.__journal = Journal()
39
+ self.__parent = self
37
40
 
38
41
  def _from_dict(self, manifest, keypath, version=None):
39
42
  '''
@@ -48,6 +51,9 @@ class BaseSchema:
48
51
  handled = set()
49
52
  missing = set()
50
53
 
54
+ if "__journal__" in manifest:
55
+ self.__journal.from_dict(manifest["__journal__"])
56
+
51
57
  if self.__default:
52
58
  data = manifest.get("default", None)
53
59
  if data:
@@ -74,16 +80,17 @@ class BaseSchema:
74
80
  '''
75
81
  Create a new schema based on the provided source files.
76
82
 
77
- The two arguments to this class are mutually exclusive.
83
+ The two arguments to this method are mutually exclusive.
78
84
 
79
85
  Args:
80
86
  filepath (path): Initial manifest.
81
87
  cfg (dict): Initial configuration dictionary.
82
88
  '''
83
89
 
84
- schema = cls()
85
90
  if not filepath and cfg is None:
86
91
  raise RuntimeError("filepath or dictionary is required")
92
+
93
+ schema = cls()
87
94
  if filepath:
88
95
  schema.read_manifest(filepath)
89
96
  if cfg:
@@ -220,6 +227,8 @@ class BaseSchema:
220
227
  if field == 'schema':
221
228
  if isinstance(param, Parameter):
222
229
  raise ValueError(f"[{','.join(keypath)}] is a complete keypath")
230
+ self.__journal.record("get", keypath, field=field, step=step, index=index)
231
+ param.__journal = self.__journal.get_child(*keypath)
223
232
  return param
224
233
  except KeyError:
225
234
  raise KeyError(f"[{','.join(keypath)}] is not a valid keypath")
@@ -227,7 +236,9 @@ class BaseSchema:
227
236
  return param
228
237
 
229
238
  try:
230
- return param.get(field, step=step, index=index)
239
+ get_ret = param.get(field, step=step, index=index)
240
+ self.__journal.record("get", keypath, field=field, step=step, index=index)
241
+ return get_ret
231
242
  except Exception as e:
232
243
  new_msg = f"error while accessing [{','.join(keypath)}]: {e.args[0]}"
233
244
  e.args = (new_msg, *e.args[1:])
@@ -266,7 +277,12 @@ class BaseSchema:
266
277
  raise KeyError(f"[{','.join(keypath)}] is not a valid keypath")
267
278
 
268
279
  try:
269
- return param.set(value, field=field, clobber=clobber, step=step, index=index)
280
+ set_ret = param.set(value, field=field, clobber=clobber,
281
+ step=step, index=index)
282
+ if set_ret:
283
+ self.__journal.record("set", keypath, value=value, field=field,
284
+ step=step, index=index)
285
+ return set_ret
270
286
  except Exception as e:
271
287
  new_msg = f"error while setting [{','.join(keypath)}]: {e.args[0]}"
272
288
  e.args = (new_msg, *e.args[1:])
@@ -304,7 +320,11 @@ class BaseSchema:
304
320
  raise KeyError(f"[{','.join(keypath)}] is not a valid keypath")
305
321
 
306
322
  try:
307
- return param.add(value, field=field, step=step, index=index)
323
+ add_ret = param.add(value, field=field, step=step, index=index)
324
+ if add_ret:
325
+ self.__journal.record("add", keypath, value=value, field=field,
326
+ step=step, index=index)
327
+ return add_ret
308
328
  except Exception as e:
309
329
  new_msg = f"error while adding to [{','.join(keypath)}]: {e.args[0]}"
310
330
  e.args = (new_msg, *e.args[1:])
@@ -343,6 +363,7 @@ class BaseSchema:
343
363
 
344
364
  try:
345
365
  param.unset(step=step, index=index)
366
+ self.__journal.record("unset", keypath, step=step, index=index)
346
367
  except Exception as e:
347
368
  new_msg = f"error while unsetting [{','.join(keypath)}]: {e.args[0]}"
348
369
  e.args = (new_msg, *e.args[1:])
@@ -376,6 +397,7 @@ class BaseSchema:
376
397
  return
377
398
 
378
399
  del key_param.__manifest[removal_key]
400
+ self.__journal.record("remove", keypath)
379
401
 
380
402
  def valid(self, *keypath, default_valid=False, check_complete=False):
381
403
  """
@@ -470,7 +492,7 @@ class BaseSchema:
470
492
  add(keys, key, item)
471
493
  return set(keys)
472
494
 
473
- def getdict(self, *keypath, include_default=True):
495
+ def getdict(self, *keypath, include_default=True, values_only=False):
474
496
  """
475
497
  Returns a schema dictionary.
476
498
 
@@ -480,6 +502,7 @@ class BaseSchema:
480
502
  Args:
481
503
  keypath (list of str): Variable length ordered schema key list
482
504
  include_default (boolean): If true will include default key paths
505
+ values_only (boolean): If true will only return values
483
506
 
484
507
  Returns:
485
508
  A schema dictionary
@@ -493,13 +516,25 @@ class BaseSchema:
493
516
  key_param = self.__manifest.get(keypath[0], None)
494
517
  if not key_param:
495
518
  return {}
496
- return key_param.getdict(*keypath[1:], include_default=include_default)
519
+ return key_param.getdict(*keypath[1:],
520
+ include_default=include_default,
521
+ values_only=values_only)
497
522
 
498
523
  manifest = {}
499
524
  if include_default and self.__default:
500
- manifest["default"] = self.__default.getdict(include_default=include_default)
525
+ manifest_dict = self.__default.getdict(include_default=include_default,
526
+ values_only=values_only)
527
+ if manifest_dict or not values_only:
528
+ manifest["default"] = manifest_dict
501
529
  for key, item in self.__manifest.items():
502
- manifest[key] = item.getdict(include_default=include_default)
530
+ manifest_dict = item.getdict(include_default=include_default,
531
+ values_only=values_only)
532
+ if manifest_dict or not values_only:
533
+ manifest[key] = manifest_dict
534
+
535
+ if not values_only and self.__journal.has_journaling():
536
+ manifest["__journal__"] = self.__journal.get()
537
+
503
538
  return manifest
504
539
 
505
540
  # Utility functions
@@ -511,4 +546,193 @@ class BaseSchema:
511
546
  key (list of str): keypath to this schema
512
547
  """
513
548
 
514
- return copy.deepcopy(self)
549
+ parent = self.__parent
550
+ self.__parent = None
551
+ schema_copy = copy.deepcopy(self)
552
+ self.__parent = parent
553
+
554
+ if self is not self.__parent:
555
+ schema_copy.__parent = self.__parent
556
+ else:
557
+ schema_copy.__parent = schema_copy
558
+
559
+ return schema_copy
560
+
561
+ def find_files(self, *keypath, missing_ok=False, step=None, index=None,
562
+ packages=None, collection_dir=None, cwd=None):
563
+ """
564
+ Returns absolute paths to files or directories based on the keypath
565
+ provided.
566
+
567
+ The keypath provided must point to a schema parameter of type file, dir,
568
+ or lists of either. Otherwise, it will trigger an error.
569
+
570
+ Args:
571
+ keypath (list of str): Variable length schema key list.
572
+ missing_ok (bool): If True, silently return None when files aren't
573
+ found. If False, print an error and set the error flag.
574
+ step (str): Step name to access for parameters that may be specified
575
+ on a per-node basis.
576
+ index (str): Index name to access for parameters that may be specified
577
+ on a per-node basis.
578
+ packages (dict of resolvers): dirctionary of path resolvers for package
579
+ paths, these can either be a path or a callable function
580
+ collection_dir (path): optional path to a collections directory
581
+ cwd (path): optional path to current working directory, this will default
582
+ to os.getcwd() if not provided.
583
+
584
+ Returns:
585
+ If keys points to a scalar entry, returns an absolute path to that
586
+ file/directory, or None if not found. It keys points to a list
587
+ entry, returns a list of either the absolute paths or None for each
588
+ entry, depending on whether it is found.
589
+
590
+ Examples:
591
+ >>> chip.find_files('input', 'verilog')
592
+ Returns a list of absolute paths to source files, as specified in
593
+ the schema.
594
+ """
595
+
596
+ param = self.get(*keypath, field=None)
597
+ paramtype = param.get(field='type')
598
+ 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')
600
+
601
+ paths = param.get(field=None, step=step, index=index)
602
+
603
+ is_list = True
604
+ if not isinstance(paths, list):
605
+ is_list = False
606
+ if paths.get():
607
+ paths = [paths]
608
+ else:
609
+ paths = []
610
+
611
+ # Ignore collection directory if it does not exist
612
+ if collection_dir and not os.path.exists(collection_dir):
613
+ collection_dir = None
614
+
615
+ if cwd is None:
616
+ cwd = os.getcwd()
617
+
618
+ if packages is None:
619
+ packages = {}
620
+
621
+ resolved_paths = []
622
+ for path in paths:
623
+ search_paths = []
624
+
625
+ package = path.get(field="package")
626
+ if package:
627
+ if package not in packages:
628
+ raise ValueError(f"Resolver for {package} not provided")
629
+ package_path = packages[package]
630
+ if isinstance(package_path, str):
631
+ search_paths.append(os.path.abspath(package_path))
632
+ elif callable(package_path):
633
+ search_paths.append(package_path())
634
+ else:
635
+ raise TypeError(f"Resolver for {package} is not a recognized type")
636
+ else:
637
+ if cwd:
638
+ search_paths.append(os.path.abspath(cwd))
639
+
640
+ try:
641
+ resolved_path = path.resolve_path(search=search_paths,
642
+ collection_dir=collection_dir)
643
+ except FileNotFoundError:
644
+ resolved_path = None
645
+ if not missing_ok:
646
+ if package:
647
+ raise FileNotFoundError(
648
+ f'Could not find "{path.get()}" in {package} [{",".join(keypath)}]')
649
+ else:
650
+ raise FileNotFoundError(
651
+ f'Could not find "{path.get()}" [{",".join(keypath)}]')
652
+ resolved_paths.append(resolved_path)
653
+
654
+ if not is_list:
655
+ if not resolved_paths:
656
+ return None
657
+ return resolved_paths[0]
658
+ return resolved_paths
659
+
660
+ def check_filepaths(self, ignore_keys=None, logger=None,
661
+ packages=None, collection_dir=None, cwd=None):
662
+ '''
663
+ Verifies that paths to all files in manifest are valid.
664
+
665
+ Args:
666
+ ignore_keys (list of keypaths): list of keypaths to ignore while checking
667
+ logger (:class:`logging.Logger`): optional logger to use to report errors
668
+ packages (dict of resolvers): dirctionary of path resolvers for package
669
+ paths, these can either be a path or a callable function
670
+ collection_dir (path): optional path to a collections directory
671
+ cwd (path): optional path to current working directory, this will default
672
+ to os.getcwd() if not provided.
673
+
674
+ Returns:
675
+ True if all file paths are valid, otherwise False.
676
+ '''
677
+
678
+ if ignore_keys is None:
679
+ ignore_keys = set()
680
+ else:
681
+ ignore_keys = set([
682
+ tuple(keypath) for keypath in ignore_keys
683
+ ])
684
+
685
+ error = False
686
+
687
+ for keypath in self.allkeys():
688
+ if keypath in ignore_keys:
689
+ continue
690
+
691
+ param = self.get(*keypath, field=None)
692
+ paramtype = param.get(field='type')
693
+
694
+ if 'file' not in paramtype and 'dir' not in paramtype:
695
+ continue
696
+
697
+ for check_files, step, index in param.getvalues():
698
+ if not check_files:
699
+ # nothing set so continue
700
+ continue
701
+
702
+ found_files = BaseSchema.find_files(
703
+ self, *keypath, missing_ok=True, step=step, index=index,
704
+ packages=packages, collection_dir=collection_dir, cwd=cwd)
705
+
706
+ if not param.is_list():
707
+ check_files = [check_files]
708
+ found_files = [found_files]
709
+
710
+ for check_file, found_file in zip(check_files, found_files):
711
+ if not found_file:
712
+ error = True
713
+ if logger:
714
+ node_indicator = ""
715
+ if step is not None:
716
+ if index is None:
717
+ node_indicator = f" ({step})"
718
+ else:
719
+ node_indicator = f" ({step}{index})"
720
+
721
+ logger.error(f"Parameter [{','.join(keypath)}]{node_indicator} path "
722
+ f"{check_file} is invalid")
723
+
724
+ return not error
725
+
726
+ def _parent(self, root=False):
727
+ '''
728
+ Returns the parent of this schema section, if root is true the root parent
729
+ will be returned.
730
+
731
+ Args:
732
+ root (bool): if true, returns the root of the schemas.
733
+ '''
734
+ if not root:
735
+ return self.__parent
736
+ if self.__parent is self:
737
+ return self
738
+ return self.__parent._parent(root=root)
@@ -29,6 +29,9 @@ class EditableSchema:
29
29
  if key in self.__schema._BaseSchema__manifest and not clobber:
30
30
  raise KeyError(f"[{','.join(fullkey)}] is already defined")
31
31
 
32
+ if isinstance(value, BaseSchema):
33
+ value._BaseSchema__parent = self.__schema
34
+
32
35
  if key == "default":
33
36
  self.__schema._BaseSchema__default = value
34
37
  else:
@@ -36,6 +39,7 @@ class EditableSchema:
36
39
  return
37
40
 
38
41
  new_schema = BaseSchema()
42
+ new_schema._BaseSchema__parent = self.__schema
39
43
  if key == "default":
40
44
  if self.__schema._BaseSchema__default:
41
45
  new_schema = self.__schema._BaseSchema__default
@@ -0,0 +1,210 @@
1
+ import copy
2
+ import json
3
+
4
+
5
+ class Journal:
6
+ """
7
+ This class provides the ability to record the schema transactions:
8
+ :meth:`BaseSchema.set`, :meth:`BaseSchema.add`, :meth:`BaseSchema.remove`,
9
+ :meth:`BaseSchema.unset`, and :meth:`BaseSchema.get`.
10
+
11
+ Args:
12
+ keyprefix (list of str): keypath to prefix on to recorded path
13
+ """
14
+
15
+ def __init__(self, keyprefix=None):
16
+ if not keyprefix:
17
+ self.__keyprefix = tuple()
18
+ else:
19
+ self.__keyprefix = tuple(keyprefix)
20
+
21
+ self.__parent = self
22
+
23
+ self.__record_types = set()
24
+ self.stop()
25
+
26
+ @property
27
+ def keypath(self):
28
+ '''
29
+ Returns the reference key path for this journal
30
+ '''
31
+
32
+ return self.__keyprefix
33
+
34
+ def get_child(self, *keypath):
35
+ '''
36
+ Get a child journal based on a new keypath
37
+
38
+ Args:
39
+ keypath (list of str): keypath to prefix on to recorded path
40
+ '''
41
+
42
+ child = Journal(keyprefix=[*self.__keyprefix, *keypath])
43
+ child.__parent = self.__parent
44
+ return child
45
+
46
+ def from_dict(self, manifest):
47
+ '''
48
+ Import a journal from a manifest dictionary
49
+
50
+ Args:
51
+ manifest (dict): Manifest to decode.
52
+ '''
53
+
54
+ self.__journal = manifest
55
+
56
+ def get(self):
57
+ """
58
+ Returns a copy of the current journal
59
+ """
60
+
61
+ return copy.deepcopy(self.__parent.__journal)
62
+
63
+ def has_journaling(self):
64
+ """
65
+ Returns true if the schema is currently setup and is the root of the journal and has data
66
+ """
67
+ return self is self.__parent and bool(self.__journal)
68
+
69
+ def is_journaling(self):
70
+ """
71
+ Returns true if the schema is currently setup for journaling
72
+ """
73
+ return self.__parent.__journal is not None
74
+
75
+ def get_types(self):
76
+ """
77
+ Returns the current schema accesses that are being recorded
78
+ """
79
+
80
+ return self.__parent.__record_types.copy()
81
+
82
+ def add_type(self, value):
83
+ """
84
+ Adds a new access type to the journal record.
85
+
86
+ Args:
87
+ value (str): access type
88
+ """
89
+
90
+ if value not in ("set", "add", "remove", "unset", "get"):
91
+ raise ValueError(f"{value} is not a valid type")
92
+
93
+ return self.__parent.__record_types.add(value)
94
+
95
+ def remove_type(self, value):
96
+ """
97
+ Removes a new access type to the journal record.
98
+
99
+ Args:
100
+ value (str): access type
101
+ """
102
+
103
+ try:
104
+ self.__parent.__record_types.remove(value)
105
+ except KeyError:
106
+ pass
107
+
108
+ def record(self, record_type, key, value=None, field=None, step=None, index=None):
109
+ '''
110
+ Record the schema transaction
111
+ '''
112
+
113
+ if self.__parent.__journal is None:
114
+ return
115
+
116
+ if record_type not in self.__parent.__record_types:
117
+ return
118
+
119
+ self.__parent.__journal.append({
120
+ "type": record_type,
121
+ "key": tuple([*self.__keyprefix, *key]),
122
+ "value": value,
123
+ "field": field,
124
+ "step": step,
125
+ "index": index
126
+ })
127
+
128
+ def start(self):
129
+ '''
130
+ Start journaling the schema transactions
131
+ '''
132
+ self.__parent.__journal = []
133
+ self.add_type("set")
134
+ self.add_type("add")
135
+ self.add_type("remove")
136
+ self.add_type("unset")
137
+
138
+ def stop(self):
139
+ '''
140
+ Stop journaling the schema transactions
141
+ '''
142
+ self.__parent.__journal = None
143
+ self.__parent.__record_types.clear()
144
+
145
+ @staticmethod
146
+ def replay_file(schema, filepath):
147
+ '''
148
+ Replay a journal into a schema from a manifest
149
+
150
+ Args:
151
+ schema (:class:`BaseSchema`): schema to replay transactions to
152
+ filepath (path): path to manifest
153
+ '''
154
+ with open(filepath, "r") as fid:
155
+ data = json.load(fid)
156
+ if "__journal__" not in data:
157
+ return
158
+
159
+ journal = Journal()
160
+ journal.from_dict(data["__journal__"])
161
+ journal.replay(schema)
162
+
163
+ def replay(self, schema):
164
+ '''
165
+ Replay journal into a schema
166
+
167
+ Args:
168
+ schema (:class:`BaseSchema`): schema to replay transactions to
169
+ '''
170
+
171
+ from .baseschema import BaseSchema
172
+ if not isinstance(schema, BaseSchema):
173
+ raise TypeError(f"schema must be a BaseSchema, not {type(schema)}")
174
+
175
+ if not self.__parent.__journal:
176
+ return
177
+
178
+ for action in self.__parent.__journal:
179
+ record_type = action['type']
180
+ keypath = action['key']
181
+ value = action['value']
182
+ field = action['field']
183
+ step = action['step']
184
+ index = action['index']
185
+ if record_type == 'set':
186
+ schema.set(*keypath, value, field=field, step=step, index=index)
187
+ elif record_type == 'add':
188
+ schema.add(*keypath, value, field=field, step=step, index=index)
189
+ elif record_type == 'unset':
190
+ schema.unset(*keypath, step=step, index=index)
191
+ elif record_type == 'remove':
192
+ schema.remove(*keypath)
193
+ elif record_type == 'get':
194
+ continue
195
+ else:
196
+ raise ValueError(f'Unknown record type {record_type}')
197
+
198
+ @staticmethod
199
+ def access(schema):
200
+ '''
201
+ Access a journal from a schema
202
+
203
+ Args:
204
+ schema (:class:`BaseSchema`): schema to replay transactions to access journal
205
+ '''
206
+ from .baseschema import BaseSchema
207
+ if isinstance(schema, BaseSchema):
208
+ return schema._BaseSchema__journal
209
+
210
+ raise TypeError(f"schema must be a BaseSchema, not {type(schema)}")
@@ -15,7 +15,7 @@ class NamedSchema(BaseSchema):
15
15
  name (str): name of the schema
16
16
  '''
17
17
 
18
- def __init__(self, name=None):
18
+ def __init__(self, name):
19
19
  super().__init__()
20
20
 
21
21
  self.__name = name
@@ -26,6 +26,35 @@ class NamedSchema(BaseSchema):
26
26
  '''
27
27
  return self.__name
28
28
 
29
+ def _reset(self) -> None:
30
+ """
31
+ Resets the state of the object
32
+ """
33
+ pass
34
+
35
+ @classmethod
36
+ def from_manifest(cls, name, filepath=None, cfg=None):
37
+ '''
38
+ Create a new schema based on the provided source files.
39
+
40
+ The two arguments to this method are mutually exclusive.
41
+
42
+ Args:
43
+ name (str): name of the schema
44
+ filepath (path): Initial manifest.
45
+ cfg (dict): Initial configuration dictionary.
46
+ '''
47
+
48
+ if not filepath and cfg is None:
49
+ raise RuntimeError("filepath or dictionary is required")
50
+
51
+ schema = cls(name)
52
+ if filepath:
53
+ schema.read_manifest(filepath)
54
+ if cfg:
55
+ schema._from_dict(cfg, [])
56
+ return schema
57
+
29
58
  def _from_dict(self, manifest, keypath, version=None):
30
59
  if keypath:
31
60
  self.__name = keypath[-1]
@@ -35,7 +64,7 @@ class NamedSchema(BaseSchema):
35
64
  def copy(self, key=None):
36
65
  copy = super().copy(key=key)
37
66
 
38
- if not self.__name and key and key[-1] != "default":
67
+ if key and key[-1] != "default":
39
68
  copy.__name = key[-1]
40
69
 
41
70
  return copy
@@ -409,12 +409,13 @@ class Parameter:
409
409
 
410
410
  return True
411
411
 
412
- def getdict(self, include_default=True):
412
+ def getdict(self, include_default=True, values_only=False):
413
413
  """
414
414
  Returns a schema dictionary.
415
415
 
416
416
  Args:
417
417
  include_default (boolean): If true will include default values
418
+ values_only (boolean): If true will only return values
418
419
 
419
420
  Returns:
420
421
  A schema dictionary
@@ -424,6 +425,18 @@ class Parameter:
424
425
  Returns the complete dictionary for the parameter
425
426
  """
426
427
 
428
+ if values_only:
429
+ dictvals = {}
430
+ is_list = self.is_list()
431
+ for value, step, index in self.getvalues(return_defvalue=include_default):
432
+ if is_list:
433
+ if value:
434
+ dictvals.setdefault(step, {})[index] = value
435
+ else:
436
+ if value is not None:
437
+ dictvals.setdefault(step, {})[index] = value
438
+ return dictvals
439
+
427
440
  dictvals = {
428
441
  "type": NodeType.encode(self.__type),
429
442
  "require": self.__require,