[PortLoadView / LayerMappedView] rename/add new views
This commit is contained in:
parent
412133533a
commit
cbf75319ad
9 changed files with 287 additions and 87 deletions
|
|
@ -11,7 +11,10 @@ from ..file.gdsii import lazy_write as gdsii_lazy_write
|
|||
from ..error import LibraryError
|
||||
from ..pattern import Pattern
|
||||
from ..ports import Port
|
||||
from ..library import IBorrowing, IMaterializable, LazyLibrary, Library, LibraryView, OverlayLibrary, PortsLibraryView
|
||||
from ..library import (
|
||||
IBorrowing, IMaterializable, LayerMappedView, LazyLibrary, Library, LibraryView,
|
||||
OverlayLibrary, PortLoadView,
|
||||
)
|
||||
|
||||
|
||||
def _make_lazy_port_library() -> Library:
|
||||
|
|
@ -201,13 +204,13 @@ def test_gdsii_lazy_overlay_subtree_preserves_source_laziness(tmp_path: Path) ->
|
|||
assert set(roundtrip) == {'leaf', 'child', 'top'}
|
||||
|
||||
|
||||
def test_gdsii_lazy_ports_view_keeps_raw_source_unmodified(tmp_path: Path) -> None:
|
||||
def test_gdsii_lazy_port_load_view_keeps_raw_source_unmodified(tmp_path: Path) -> None:
|
||||
gds_file = tmp_path / 'lazy_ports.gds'
|
||||
src = _make_lazy_port_library()
|
||||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-ports')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = PortsLibraryView(raw, layers=[(10, 0)], max_depth=2)
|
||||
processed = PortLoadView(raw, layers=[(10, 0)], max_depth=2)
|
||||
assert not hasattr(processed, 'library_info')
|
||||
|
||||
top = processed['top']
|
||||
|
|
@ -219,14 +222,14 @@ def test_gdsii_lazy_ports_view_keeps_raw_source_unmodified(tmp_path: Path) -> No
|
|||
assert not raw_top.ports
|
||||
|
||||
|
||||
def test_gdsii_lazy_ports_view_detaches_previously_cached_source(tmp_path: Path) -> None:
|
||||
def test_gdsii_lazy_port_load_view_detaches_previously_cached_source(tmp_path: Path) -> None:
|
||||
gds_file = tmp_path / 'lazy_cached_ports.gds'
|
||||
src = _make_lazy_port_library()
|
||||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-cached-ports')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
raw_top = raw['top']
|
||||
processed = PortsLibraryView(raw, ports={
|
||||
processed = PortLoadView(raw, ports={
|
||||
'top': {
|
||||
'P': Port((1, 2), rotation=0, ptype='wire'),
|
||||
},
|
||||
|
|
@ -247,7 +250,7 @@ def test_gdsii_lazy_port_overrides_without_data_stay_lazy(tmp_path: Path) -> Non
|
|||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-overrides')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = PortsLibraryView(raw, ports={
|
||||
processed = PortLoadView(raw, ports={
|
||||
'top': {
|
||||
'P': Port((1, 2), rotation=0, ptype='wire'),
|
||||
},
|
||||
|
|
@ -270,7 +273,7 @@ def test_gdsii_lazy_port_overrides_apply_after_extraction(tmp_path: Path) -> Non
|
|||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-override-extracted')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = PortsLibraryView(
|
||||
processed = PortLoadView(
|
||||
raw,
|
||||
layers=[(10, 0)],
|
||||
max_depth=2,
|
||||
|
|
@ -299,7 +302,7 @@ def test_gdsii_lazy_port_overrides_replace_extracted_ports(tmp_path: Path) -> No
|
|||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-replace-ports')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = PortsLibraryView(
|
||||
processed = PortLoadView(
|
||||
raw,
|
||||
layers=[(10, 0)],
|
||||
max_depth=2,
|
||||
|
|
@ -323,7 +326,7 @@ def test_gdsii_lazy_overlay_add_source_stays_lazy_for_processed_view(tmp_path: P
|
|||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-overlay')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = PortsLibraryView(raw, layers=[(10, 0)], max_depth=2)
|
||||
processed = PortLoadView(raw, layers=[(10, 0)], max_depth=2)
|
||||
|
||||
overlay = OverlayLibrary()
|
||||
overlay.add_source(processed)
|
||||
|
|
@ -341,7 +344,7 @@ def test_gdsii_lazy_overlay_add_source_sees_port_overrides(tmp_path: Path) -> No
|
|||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-overlay-override')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = PortsLibraryView(raw, ports={
|
||||
processed = PortLoadView(raw, ports={
|
||||
'top': {
|
||||
'P': Port((1, 2), rotation=0, ptype='wire'),
|
||||
},
|
||||
|
|
@ -393,7 +396,7 @@ def test_gdsii_lazy_processed_write_roundtrips_without_explicit_units(tmp_path:
|
|||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-roundtrip')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = PortsLibraryView(raw, layers=[(10, 0)], max_depth=2)
|
||||
processed = PortLoadView(raw, layers=[(10, 0)], max_depth=2)
|
||||
|
||||
out_file = tmp_path / 'lazy_roundtrip_out.gds'
|
||||
gdsii_lazy.writefile(processed, out_file)
|
||||
|
|
@ -401,6 +404,23 @@ def test_gdsii_lazy_processed_write_roundtrips_without_explicit_units(tmp_path:
|
|||
assert out_file.read_bytes() == gds_file.read_bytes()
|
||||
|
||||
|
||||
def test_gdsii_lazy_layer_mapped_write_materializes_without_source_cache(tmp_path: Path) -> None:
|
||||
gds_file = tmp_path / 'lazy_layer_source.gds'
|
||||
src = _make_lazy_port_library()
|
||||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-layers')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
mapped = LayerMappedView(raw, lambda layer: (20, 0) if layer == (10, 0) else layer)
|
||||
out_file = tmp_path / 'lazy_layer_mapped.gds'
|
||||
gdsii_lazy.writefile(mapped, out_file)
|
||||
|
||||
assert not raw._cache
|
||||
roundtrip, info = gdsii.readfile(out_file)
|
||||
assert info['name'] == 'classic-layers'
|
||||
assert set(roundtrip['leaf'].labels) == {(20, 0)}
|
||||
assert (10, 0) not in roundtrip['leaf'].labels
|
||||
|
||||
|
||||
def test_gdsii_removed_closure_based_lazy_loader() -> None:
|
||||
assert not hasattr(gdsii, 'load_library')
|
||||
assert not hasattr(gdsii, 'load_libraryfile')
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import pytest
|
|||
pytest.importorskip('pyarrow')
|
||||
|
||||
from .. import PatternError
|
||||
from ..library import IBorrowing, IMaterializable, Library, OverlayLibrary, PortsLibraryView
|
||||
from ..library import IBorrowing, IMaterializable, LayerMappedView, Library, OverlayLibrary, PortLoadView
|
||||
from ..pattern import Pattern
|
||||
from ..repetition import Grid
|
||||
from ..file import gdsii
|
||||
|
|
@ -240,7 +240,7 @@ def test_gdsii_raw_copy_resolves_generic_borrowing_views(tmp_path: Path, monkeyp
|
|||
gdsii.writefile(_make_small_library(), gds_file, meters_per_unit=1e-9, library_name='provenance')
|
||||
|
||||
raw, _ = gdsii_lazy_arrow.readfile(gds_file)
|
||||
ports = PortsLibraryView(raw)
|
||||
ports = PortLoadView(raw)
|
||||
subtree = ports.subtree('top')
|
||||
overlay = OverlayLibrary()
|
||||
overlay.add_source(subtree)
|
||||
|
|
@ -270,6 +270,55 @@ def test_gdsii_raw_copy_resolves_generic_borrowing_views(tmp_path: Path, monkeyp
|
|||
assert gdsii_lazy_write._resolve_raw_struct(remapped, 'mid') is None
|
||||
|
||||
|
||||
def test_gdsii_layer_mapped_view_controls_raw_copy_through(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
gds_file = tmp_path / 'layer_mapped_source.gds'
|
||||
gdsii.writefile(_make_small_library(), gds_file, meters_per_unit=1e-9, library_name='layer-mapped')
|
||||
|
||||
raw, _ = gdsii_lazy_arrow.readfile(gds_file)
|
||||
copied: list[str] = []
|
||||
raw_reader = raw.raw_struct_bytes
|
||||
|
||||
def record_raw_read(name: str) -> bytes:
|
||||
copied.append(name)
|
||||
return raw_reader(name)
|
||||
|
||||
def map_layer(layer): # noqa: ANN001,ANN202
|
||||
return (20, 0) if layer == (1, 0) else layer
|
||||
|
||||
monkeypatch.setattr(raw, 'raw_struct_bytes', record_raw_read)
|
||||
|
||||
mapped = LayerMappedView(raw, map_layer)
|
||||
mapped_file = tmp_path / 'layer_mapped_all.gds'
|
||||
gdsii_lazy_arrow.writefile(mapped, mapped_file)
|
||||
|
||||
assert copied == []
|
||||
assert not raw._cache
|
||||
roundtrip, info = gdsii.readfile(mapped_file)
|
||||
assert info['name'] == 'layer-mapped'
|
||||
assert set(roundtrip['leaf'].shapes) == {(20, 0)}
|
||||
|
||||
passthrough = LayerMappedView(raw, map_layer, copy_through=True)
|
||||
copied_file = tmp_path / 'layer_mapped_copied.gds'
|
||||
gdsii_lazy_arrow.writefile(passthrough, copied_file)
|
||||
|
||||
assert copied == ['leaf', 'mid', 'top']
|
||||
assert copied_file.read_bytes() == gds_file.read_bytes()
|
||||
assert not raw._cache
|
||||
|
||||
copied.clear()
|
||||
assert set(passthrough['leaf'].shapes) == {(20, 0)}
|
||||
promoted_file = tmp_path / 'layer_mapped_promoted.gds'
|
||||
gdsii_lazy_arrow.writefile(passthrough, promoted_file)
|
||||
|
||||
assert copied == ['mid', 'top']
|
||||
assert not raw._cache
|
||||
roundtrip, _ = gdsii.readfile(promoted_file)
|
||||
assert set(roundtrip['leaf'].shapes) == {(20, 0)}
|
||||
|
||||
|
||||
def test_gdsii_lazy_arrow_processed_cell_edit_disables_raw_copy(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
|
|
@ -279,7 +328,7 @@ def test_gdsii_lazy_arrow_processed_cell_edit_disables_raw_copy(
|
|||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='processed-edit')
|
||||
|
||||
raw, _ = gdsii_lazy_arrow.readfile(gds_file)
|
||||
processed = PortsLibraryView(raw)
|
||||
processed = PortLoadView(raw)
|
||||
processed['top'].polygon((7, 0), vertices=[[0, 0], [4, 0], [0, 4]])
|
||||
|
||||
copied: list[str] = []
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import pytest
|
|||
from collections.abc import Mapping, MutableMapping
|
||||
from typing import cast, TYPE_CHECKING
|
||||
from numpy.testing import assert_allclose
|
||||
from ..library import IBorrowing, INameView, IMaterializable, Library, LibraryView, LazyLibrary, OverlayLibrary, PortsLibraryView
|
||||
from ..library import (
|
||||
IBorrowing, INameView, IMaterializable, LayerMappedView, Library, LibraryView,
|
||||
LazyLibrary, OverlayLibrary, PortLoadView,
|
||||
)
|
||||
from ..pattern import Pattern
|
||||
from ..error import LibraryError, PatternError
|
||||
from ..ports import Port
|
||||
|
|
@ -540,11 +543,11 @@ def test_overlay_add_source_callback_sees_earlier_name_reservations() -> None:
|
|||
assert set(rename_map.values()) <= set(overlay)
|
||||
|
||||
|
||||
def test_ports_view_detaches_already_materialized_overlay_pattern() -> None:
|
||||
def test_port_load_view_detaches_already_materialized_overlay_pattern() -> None:
|
||||
overlay = OverlayLibrary()
|
||||
overlay.add_source(Library({"top": Pattern()}))
|
||||
raw = overlay["top"]
|
||||
processed = PortsLibraryView(
|
||||
processed = PortLoadView(
|
||||
overlay,
|
||||
ports={"top": {"P": Port((1, 2), 0)}},
|
||||
)
|
||||
|
|
@ -557,6 +560,77 @@ def test_ports_view_detaches_already_materialized_overlay_pattern() -> None:
|
|||
assert not hasattr(processed, "close")
|
||||
|
||||
|
||||
def test_layer_mapped_view_detaches_and_maps_shapes_and_labels() -> None:
|
||||
import masque
|
||||
|
||||
child = Pattern()
|
||||
source_pattern = Pattern(ports={'P': Port((1, 2), 0, ptype='wire')})
|
||||
source_pattern.polygon('A', vertices=[[0, 0], [1, 0], [0, 1]])
|
||||
source_pattern.polygon('B', vertices=[[2, 0], [3, 0], [2, 1]])
|
||||
source_pattern.label('TEXT', string='port', offset=(1, 2))
|
||||
source_pattern.ref('child')
|
||||
source_pattern.annotations['note'] = ['unchanged']
|
||||
source = Library({'child': child, 'top': source_pattern})
|
||||
mapped = LayerMappedView(
|
||||
source,
|
||||
lambda layer: {'A': 'DRAW', 'B': 'DRAW', 'TEXT': 'LABEL'}.get(layer, layer),
|
||||
)
|
||||
|
||||
mapped_top = mapped['top']
|
||||
|
||||
assert mapped_top is not source_pattern
|
||||
assert set(mapped_top.shapes) == {'DRAW'}
|
||||
assert len(mapped_top.shapes['DRAW']) == 2
|
||||
assert set(mapped_top.labels) == {'LABEL'}
|
||||
assert set(mapped_top.ports) == {'P'}
|
||||
assert set(mapped_top.refs) == {'child'}
|
||||
assert mapped_top.annotations == {'note': ['unchanged']}
|
||||
assert set(source_pattern.shapes) == {'A', 'B'}
|
||||
assert set(source_pattern.labels) == {'TEXT'}
|
||||
assert mapped.child_graph() == source.child_graph()
|
||||
assert mapped.source_order() == source.source_order()
|
||||
assert masque.LayerMappedView is LayerMappedView
|
||||
assert masque.PortLoadView is PortLoadView
|
||||
assert not hasattr(masque, 'PortsLibraryView')
|
||||
assert not hasattr(masque.library, 'PortsLibraryView')
|
||||
|
||||
|
||||
def test_layer_mapped_view_materialization_and_copy_through() -> None:
|
||||
pattern = Pattern()
|
||||
pattern.polygon('A', vertices=[[0, 0], [1, 0], [0, 1]])
|
||||
source = Library({'top': pattern})
|
||||
mapped = LayerMappedView(source, lambda _layer: 'B')
|
||||
passthrough = LayerMappedView(source, lambda _layer: 'B', copy_through=True)
|
||||
|
||||
assert mapped.source_cell('top') is None
|
||||
assert passthrough.source_cell('top') == (source, 'top')
|
||||
transient = passthrough.materialize('top', persist=False)
|
||||
assert set(transient.shapes) == {'B'}
|
||||
assert passthrough.source_cell('top') == (source, 'top')
|
||||
|
||||
persistent = passthrough['top']
|
||||
assert passthrough['top'] is persistent
|
||||
assert passthrough.source_cell('top') is None
|
||||
assert set(persistent.shapes) == {'B'}
|
||||
|
||||
|
||||
def test_layer_mapped_view_composes_with_ports_and_overlay() -> None:
|
||||
pattern = Pattern()
|
||||
pattern.polygon('A', vertices=[[0, 0], [1, 0], [0, 1]])
|
||||
source = Library({'top': pattern})
|
||||
ports = PortLoadView(source, ports={'top': {'P': Port((1, 2), 0)}})
|
||||
mapped = LayerMappedView(ports, lambda _layer: 'B')
|
||||
overlay = OverlayLibrary()
|
||||
overlay.add_source(mapped)
|
||||
|
||||
top = overlay['top']
|
||||
|
||||
assert set(top.shapes) == {'B'}
|
||||
assert set(top.ports) == {'P'}
|
||||
assert mapped.borrowed_sources() == (ports,)
|
||||
assert not pattern.ports
|
||||
|
||||
|
||||
def test_library_subtree() -> None:
|
||||
lib = Library()
|
||||
lib["a"] = Pattern()
|
||||
|
|
@ -655,7 +729,7 @@ def test_library_materialization_and_borrowing_capabilities() -> None:
|
|||
|
||||
overlay = OverlayLibrary()
|
||||
overlay.add_source(lazy)
|
||||
ports = PortsLibraryView(overlay)
|
||||
ports = PortLoadView(overlay)
|
||||
subtree = ports.subtree("top")
|
||||
|
||||
assert isinstance(overlay, IMaterializable)
|
||||
|
|
@ -676,7 +750,7 @@ def test_library_materialization_and_borrowing_capabilities() -> None:
|
|||
def test_borrowed_source_cell_tracks_persistent_materialization() -> None:
|
||||
source = Library({"top": Pattern()})
|
||||
source.library_info = {"name": "not-forwarded"} # type: ignore[attr-defined]
|
||||
processed = PortsLibraryView(source)
|
||||
processed = PortLoadView(source)
|
||||
|
||||
assert processed.source_cell("top") == (source, "top")
|
||||
assert not hasattr(processed, "library_info")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue