diff --git a/masque/file/utils.py b/masque/file/utils.py index ca6cb6d..87c3d67 100644 --- a/masque/file/utils.py +++ b/masque/file/utils.py @@ -22,6 +22,7 @@ logger = logging.getLogger(__name__) def preflight( lib: Library, sort: bool = True, + sort_elements: bool = False, allow_dangling_refs: bool | None = None, allow_named_layers: bool = True, prune_empty_patterns: bool = False, @@ -32,8 +33,9 @@ def preflight( to writing to a file (or immediately after reading). Args: - sort: Whether to sort the patterns based on their names, and sort the pattern contents. + sort: Whether to sort the patterns based on their names, and optionaly sort the pattern contents. Default True. Useful for reproducible builds. + sort_elements: Whether to sort the pattern contents. Requires sort=True to run. allow_dangling_refs: If `None` (default), warns about any refs to patterns that are not in the provided library. If `True`, no check is performed; if `False`, a `LibraryError` is raised instead. @@ -48,7 +50,7 @@ def preflight( """ if sort: lib = Library(dict(sorted( - (nn, pp.sort()) for nn, pp in lib.items() + (nn, pp.sort(sort_elements=sort_elements)) for nn, pp in lib.items() ))) if not allow_dangling_refs: diff --git a/masque/pattern.py b/masque/pattern.py index 0acdf3f..9419c28 100644 --- a/masque/pattern.py +++ b/masque/pattern.py @@ -300,23 +300,33 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): return True - def sort(self) -> Self: + def sort(self, sort_elements: bool = True) -> Self: """ - Sort the element dicts (shapes, labels, refs) and their contained lists. + Sort the element dicts (shapes, labels, refs) and (optionally) their contained lists. This is primarily useful for making builds more reproducible. + Args: + sort_elements: Whether to sort all the shapes/labels/refs within each layer/target. + Returns: self """ + if sort_elements: + def maybe_sort(xx): + return sorted(xx) + else: + def maybe_sort(xx): + return xx + self.refs = defaultdict(list, sorted( - (tgt, sorted(rrs)) for tgt, rrs in self.refs.items() + (tgt, maybe_sort(rrs)) for tgt, rrs in self.refs.items() )) self.labels = defaultdict(list, sorted( - ((layer, sorted(lls)) for layer, lls in self.labels.items()), + ((layer, maybe_sort(lls)) for layer, lls in self.labels.items()), key=lambda tt: layer2key(tt[0]), )) self.shapes = defaultdict(list, sorted( - ((layer, sorted(sss)) for layer, sss in self.shapes.items()), + ((layer, maybe_sort(sss)) for layer, sss in self.shapes.items()), key=lambda tt: layer2key(tt[0]), ))