[OverlayLibrary] enable renaming all cells during add_source with rename_when='always'

This commit is contained in:
Jan Petykiewicz 2026-06-19 21:34:16 -07:00
commit 08bbb10827
2 changed files with 58 additions and 4 deletions

View file

@ -17,7 +17,7 @@ Both the classic and Arrow-backed lazy GDS readers rely on these helpers.
from __future__ import annotations
from dataclasses import dataclass
from typing import IO, Any, cast
from typing import IO, Any, Literal, cast
from collections import defaultdict
from collections.abc import Callable, Iterator, Mapping, Sequence
import copy
@ -303,7 +303,23 @@ class OverlayLibrary(ILibrary):
source: Mapping[str, Pattern] | ILibraryView,
*,
rename_theirs: Callable[[ILibraryView, str], str] | None = None,
rename_when: Literal['conflict', 'always'] = 'conflict',
) -> dict[str, str]:
"""
Add a source-backed library layer.
Args:
rename_theirs: Function used to choose visible names for imported
source cells.
rename_when: If `'conflict'`, only conflicting names are renamed.
If `'always'`, every imported source name is passed through
`rename_theirs`.
"""
if rename_when not in ('conflict', 'always'):
raise ValueError(f'Unknown source rename mode: {rename_when!r}')
if rename_when == 'always' and rename_theirs is None:
raise TypeError('rename_theirs is required when rename_when="always"')
view = _coerce_library_view(source)
source_order = list(view.source_order())
child_graph = view.child_graph(dangling='include')
@ -314,12 +330,20 @@ class OverlayLibrary(ILibrary):
for name in source_order:
visible = name
if visible in self._entries or visible in visible_to_source:
renamed = False
if rename_when == 'always':
visible = cast('Callable[[ILibraryView, str], str]', rename_theirs)(self, name)
renamed = True
elif visible in self._entries or visible in visible_to_source:
if rename_theirs is None:
raise LibraryError(f'Conflicting name while adding source: {name!r}')
visible = rename_theirs(self, name)
renamed = True
if visible in self._entries or visible in visible_to_source:
if not renamed:
raise LibraryError(f'Conflicting name while adding source: {name!r}')
raise LibraryError(f'Unresolved duplicate key encountered while adding source: {name!r} -> {visible!r}')
if visible != name:
rename_map[name] = visible
source_to_visible[name] = visible
visible_to_source[visible] = name

View file

@ -1,6 +1,7 @@
from pathlib import Path
import numpy
import pytest
from numpy.testing import assert_allclose
from ..file import gdsii, gdsii_lazy
@ -82,6 +83,35 @@ def test_gdsii_lazy_overlay_add_source_stays_lazy_for_processed_view(tmp_path: P
assert set(abstract.ports) == {'A'}
def test_gdsii_lazy_overlay_add_source_can_rename_every_source_cell() -> None:
src = _make_lazy_port_library()
overlay = gdsii_lazy.OverlayLibrary()
rename_map = overlay.add_source(
src,
rename_theirs=lambda _lib, name: f'mapped_{name}',
rename_when='always',
)
assert rename_map == {
'leaf': 'mapped_leaf',
'child': 'mapped_child',
'top': 'mapped_top',
}
assert tuple(overlay.keys()) == ('mapped_leaf', 'mapped_child', 'mapped_top')
assert 'mapped_leaf' in overlay['mapped_child'].refs
def test_gdsii_lazy_overlay_add_source_rename_when_validation() -> None:
src = _make_lazy_port_library()
with pytest.raises(TypeError, match='rename_theirs'):
gdsii_lazy.OverlayLibrary().add_source(src, rename_when='always')
with pytest.raises(ValueError, match='rename mode'):
gdsii_lazy.OverlayLibrary().add_source(src, rename_when='sometimes') # type: ignore[arg-type]
def test_gdsii_lazy_processed_write_roundtrips_without_explicit_units(tmp_path: Path) -> None:
gds_file = tmp_path / 'lazy_roundtrip.gds'
src = _make_lazy_port_library()