[library / gdsii] cleanup using new library rework

This commit is contained in:
Jan Petykiewicz 2026-07-13 21:37:52 -07:00
commit 7b589e4f44
14 changed files with 696 additions and 391 deletions

View file

@ -378,12 +378,20 @@ class ILibraryView(Mapping[str, 'Pattern'], INameView, metaclass=ABCMeta):
dangling_list = sorted(dangling)
return LibraryError(f'Dangling refs found while {context}: ' + pformat(dangling_list))
def _raw_children(self, name: str) -> set[str]:
"""Return direct, non-empty named references for one pattern."""
return {
child
for child, refs in self[name].refs.items()
if child is not None and refs
}
def _raw_child_graph(self) -> tuple[dict[str, set[str]], set[str]]:
existing = set(self.keys())
graph: dict[str, set[str]] = {}
dangling: set[str] = set()
for name, pat in self.items():
children = {child for child, refs in pat.refs.items() if child is not None and refs}
for name in self:
children = self._raw_children(name)
graph[name] = children
dangling |= children - existing
return graph, dangling
@ -626,11 +634,18 @@ class ILibraryView(Mapping[str, 'Pattern'], INameView, metaclass=ABCMeta):
for parent in parent_graph.get(name, set()):
if parent not in self: # parent_graph may be a for a superset of self
continue
for ref in self[parent].refs[name]:
instances[parent].append(ref.as_transforms())
instances[parent].extend(self._raw_ref_transforms(parent, name))
return instances
def _raw_ref_transforms(
self,
parent: str,
target: str,
) -> list[NDArray[numpy.float64]]:
"""Return local transforms from one parent to one target."""
return [ref.as_transforms() for ref in self[parent].refs.get(target, ())]
def find_refs_global(
self,
name: str,

View file

@ -40,3 +40,12 @@ class IBorrowing(ABC):
@abstractmethod
def borrowed_sources(self) -> tuple[ILibraryView, ...]:
"""Return the source views directly borrowed by this library."""
def source_cell(self, name: str) -> tuple[ILibraryView, str] | None: # noqa: ARG002
"""
Return a direct source cell with unchanged layout data, if available.
The source may use a different name, which is returned alongside it.
Port metadata may differ because ports are not layout-file content.
"""
return None

View file

@ -2,7 +2,7 @@
from __future__ import annotations
from pprint import pformat
from typing import TYPE_CHECKING, Any, Self, cast
from typing import TYPE_CHECKING, Self
from ..error import LibraryError
from .base import ILibrary, ILibraryView
@ -70,9 +70,6 @@ class _SubtreeLibraryView(ILibraryView, IMaterializable, IBorrowing):
name: set(child_graph.get(name, set()))
for name in self._order
}
if hasattr(source, 'library_info'):
self.library_info = cast('dict[str, Any]', source.library_info)
def __getitem__(self, key: str) -> Pattern:
if key not in self._names:
raise KeyError(key)
@ -90,6 +87,11 @@ class _SubtreeLibraryView(ILibraryView, IMaterializable, IBorrowing):
def borrowed_sources(self) -> tuple[ILibraryView, ...]:
return (self._source,)
def source_cell(self, name: str) -> tuple[ILibraryView, str] | None:
if name not in self._names:
return None
return self._source, name
def source_order(self) -> tuple[str, ...]:
return self._order
@ -134,21 +136,6 @@ class _SubtreeLibraryView(ILibraryView, IMaterializable, IBorrowing):
refs = self._source.find_refs_local(name, parent_graph=parent_graph, dangling=dangling)
return {parent: transforms for parent, transforms in refs.items() if parent in self._names}
def raw_struct_bytes(self, name: str) -> bytes:
if name not in self._names:
raise KeyError(name)
reader = getattr(self._source, 'raw_struct_bytes', None)
if not callable(reader):
raise TypeError('raw_struct_bytes')
return cast('bytes', reader(name))
def can_copy_raw_struct(self, name: str) -> bool:
if name not in self._names:
return False
can_copy = getattr(self._source, 'can_copy_raw_struct', None)
return bool(callable(can_copy) and can_copy(name))
class Library(ILibrary):
"""
Default implementation for a writeable library.

View file

@ -3,7 +3,7 @@ from __future__ import annotations
from collections import defaultdict
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Literal, Self, cast
from typing import TYPE_CHECKING, Literal, Self, cast
import copy
from ..error import LibraryError
@ -53,9 +53,9 @@ class PortsLibraryView(ILibraryView, IMaterializable, IBorrowing):
The view borrows its source: callers must keep the source open for the
lifetime of the view and close the source themselves.
Graph queries, source ordering, and copy-through capabilities are delegated
to the wrapped source whenever possible, while `__getitem__` and
`materialize_many()` return port-imported patterns.
Graph queries and source ordering are delegated to the wrapped source,
while `source_cell()` exposes unchanged layout provenance and `__getitem__`
and `materialize_many()` return port-imported patterns.
"""
def __init__(
@ -79,9 +79,6 @@ class PortsLibraryView(ILibraryView, IMaterializable, IBorrowing):
self._replace = replace
self._cache: dict[str, Pattern] = {}
self._lookups_in_progress: list[str] = []
if hasattr(source, 'library_info'):
self.library_info = cast('dict[str, Any]', source.library_info)
def __getitem__(self, key: str) -> Pattern:
return self.materialize(key, persist=True)
@ -139,6 +136,11 @@ class PortsLibraryView(ILibraryView, IMaterializable, IBorrowing):
def borrowed_sources(self) -> tuple[ILibraryView, ...]:
return (self._source,)
def source_cell(self, name: str) -> tuple[ILibraryView, str] | None:
if name not in self._source or name in self._cache:
return None
return self._source, name
def child_graph(
self,
dangling: dangling_mode_t = 'error',
@ -158,21 +160,6 @@ class PortsLibraryView(ILibraryView, IMaterializable, IBorrowing):
return cast('dict[str, list[NDArray[numpy.float64]]]', finder(name, parent_graph=parent_graph, dangling=dangling))
return super().find_refs_local(name, parent_graph=parent_graph, dangling=dangling)
def raw_struct_bytes(self, name: str) -> bytes:
reader = getattr(self._source, 'raw_struct_bytes', None)
if not callable(reader):
raise TypeError('raw_struct_bytes')
return cast('bytes', reader(name))
def can_copy_raw_struct(self, name: str) -> bool:
if name in self._cache:
return False
can_copy = getattr(self._source, 'can_copy_raw_struct', None)
if not callable(can_copy):
return False
return bool(can_copy(name))
class OverlayLibrary(ILibrary, IMaterializable, IBorrowing):
"""
Mutable overlay over one or more source libraries.
@ -434,23 +421,12 @@ class OverlayLibrary(ILibrary, IMaterializable, IBorrowing):
def borrowed_sources(self) -> tuple[ILibraryView, ...]:
return tuple(layer.library for layer in self._layers)
def can_copy_raw_struct(self, name: str) -> bool:
entry = self._entries.get(name)
if not isinstance(entry, _SourceEntry) or name != entry.source_name:
return False
layer = self._layers[entry.layer_index]
can_copy = getattr(layer.library, 'can_copy_raw_struct', None)
if not callable(can_copy) or not can_copy(entry.source_name):
return False
children = layer.child_graph.get(entry.source_name, set())
return all(self._effective_target(layer, child) == child for child in children)
def raw_struct_bytes(self, name: str) -> bytes:
def source_cell(self, name: str) -> tuple[ILibraryView, str] | None:
entry = self._entries.get(name)
if not isinstance(entry, _SourceEntry):
raise TypeError('raw_struct_bytes')
return None
layer = self._layers[entry.layer_index]
reader = getattr(layer.library, 'raw_struct_bytes', None)
if not callable(reader):
raise TypeError('raw_struct_bytes')
return cast('bytes', reader(entry.source_name))
children = layer.child_graph.get(entry.source_name, set())
if any(self._effective_target(layer, child) != child for child in children):
return None
return layer.library, entry.source_name