diff --git a/masque/file/gdsii.py b/masque/file/gdsii.py index c323ecf..10f5a9a 100644 --- a/masque/file/gdsii.py +++ b/masque/file/gdsii.py @@ -21,6 +21,7 @@ Notes: """ from typing import IO, cast, Any from collections.abc import Iterable, Mapping, Callable +from types import MappingProxyType import io import mmap import logging @@ -52,6 +53,8 @@ path_cap_map = { 4: Path.Cap.SquareCustom, } +RO_EMPTY_DICT: Mapping[int, bytes] = MappingProxyType({}) + def rint_cast(val: ArrayLike) -> NDArray[numpy.int32]: return numpy.rint(val).astype(numpy.int32) @@ -399,11 +402,15 @@ def _mrefs_to_grefs(refs: dict[str | None, list[Ref]]) -> list[klamath.library.R return grefs -def _properties_to_annotations(properties: dict[int, bytes]) -> annotations_t: +def _properties_to_annotations(properties: Mapping[int, bytes]) -> annotations_t: + if not properties: + return None return {str(k): [v.decode()] for k, v in properties.items()} -def _annotations_to_properties(annotations: annotations_t, max_len: int = 126) -> dict[int, bytes]: +def _annotations_to_properties(annotations: annotations_t, max_len: int = 126) -> Mapping[int, bytes]: + if annotations is None: + return RO_EMPTY_DICT cum_len = 0 props = {} for key, vals in annotations.items(): diff --git a/masque/file/oasis.py b/masque/file/oasis.py index e64bb4c..642ad44 100644 --- a/masque/file/oasis.py +++ b/masque/file/oasis.py @@ -671,6 +671,8 @@ def repetition_masq2fata( def annotations_to_properties(annotations: annotations_t) -> list[fatrec.Property]: #TODO determine is_standard based on key? + if annotations is None: + return [] properties = [] for key, values in annotations.items(): vals = [AString(v) if isinstance(v, str) else v diff --git a/masque/pattern.py b/masque/pattern.py index 5bf030a..f77c64f 100644 --- a/masque/pattern.py +++ b/masque/pattern.py @@ -332,7 +332,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): )) self.ports = dict(sorted(self.ports.items())) - self.annotations = dict(sorted(self.annotations.items())) + self.annotations = dict(sorted(self.annotations.items())) if self.annotations is not None else None return self @@ -354,10 +354,13 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): for layer, lseq in other_pattern.labels.items(): self.labels[layer].extend(lseq) - annotation_conflicts = set(self.annotations.keys()) & set(other_pattern.annotations.keys()) - if annotation_conflicts: - raise PatternError(f'Annotation keys overlap: {annotation_conflicts}') - self.annotations.update(other_pattern.annotations) + if other_pattern.annotations is not None: + if self.annotations is None: + self.annotations = {} + annotation_conflicts = set(self.annotations.keys()) & set(other_pattern.annotations.keys()) + if annotation_conflicts: + raise PatternError(f'Annotation keys overlap: {annotation_conflicts}') + self.annotations.update(other_pattern.annotations) port_conflicts = set(self.ports.keys()) & set(other_pattern.ports.keys()) if port_conflicts: @@ -415,7 +418,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): elif default_keep: pat.refs = copy.copy(self.refs) - if annotations is not None: + if annotations is not None and self.annotations is not None: pat.annotations = {k: v for k, v in self.annotations.items() if annotations(k, v)} elif default_keep: pat.annotations = copy.copy(self.annotations) diff --git a/masque/utils/types.py b/masque/utils/types.py index 4060ac4..c06b7b4 100644 --- a/masque/utils/types.py +++ b/masque/utils/types.py @@ -5,7 +5,7 @@ from typing import Protocol layer_t = int | tuple[int, int] | str -annotations_t = dict[str, list[int | float | str]] +annotations_t = dict[str, list[int | float | str]] | None class SupportsBool(Protocol):