from pathlib import Path import numpy import pytest from numpy.testing import assert_allclose from ..file import gdsii from ..file.gdsii import lazy as gdsii_lazy from ..pattern import Pattern from ..library import Library, OverlayLibrary def _make_lazy_port_library() -> Library: lib = Library() leaf = Pattern() leaf.label(layer=(10, 0), string='A:type1 0', offset=(5, 0)) lib['leaf'] = leaf child = Pattern() child.ref('leaf', offset=(10, 20), rotation=numpy.pi / 2) lib['child'] = child top = Pattern() top.ref('child', offset=(100, 200)) lib['top'] = top return lib def test_gdsii_lazy_source_exposes_order_and_graph_without_materializing(tmp_path: Path) -> None: gds_file = tmp_path / 'lazy_source.gds' src = _make_lazy_port_library() gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-lazy') lib, info = gdsii_lazy.readfile(gds_file) assert info['name'] == 'classic-lazy' assert lib.source_order() == ('leaf', 'child', 'top') assert lib.child_graph(dangling='ignore') == { 'leaf': set(), 'child': {'leaf'}, 'top': {'child'}, } assert not lib._cache child = lib['child'] assert list(child.refs.keys()) == ['leaf'] assert set(lib._cache) == {'child'} def test_gdsii_lazy_ports_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 = raw.with_ports_from_data(layers=[(10, 0)], max_depth=2) top = processed['top'] assert set(top.ports) == {'A'} assert_allclose(top.ports['A'].offset, [110, 225], atol=1e-10) assert not raw._cache raw_top = raw['top'] assert not raw_top.ports def test_gdsii_lazy_overlay_add_source_stays_lazy_for_processed_view(tmp_path: Path) -> None: gds_file = tmp_path / 'lazy_overlay.gds' src = _make_lazy_port_library() gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-overlay') raw, _ = gdsii_lazy.readfile(gds_file) processed = raw.with_ports_from_data(layers=[(10, 0)], max_depth=2) overlay = OverlayLibrary() overlay.add_source(processed) assert not raw._cache assert not processed._cache abstract = overlay.abstract('top') assert set(abstract.ports) == {'A'} def test_gdsii_lazy_overlay_add_source_can_rename_every_source_cell() -> None: src = _make_lazy_port_library() overlay = 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'): OverlayLibrary().add_source(src, rename_when='always') with pytest.raises(ValueError, match='rename mode'): 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() gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-roundtrip') raw, _ = gdsii_lazy.readfile(gds_file) processed = raw.with_ports_from_data(layers=[(10, 0)], max_depth=2) out_file = tmp_path / 'lazy_roundtrip_out.gds' gdsii_lazy.writefile(processed, out_file) assert out_file.read_bytes() == gds_file.read_bytes() def test_gdsii_removed_closure_based_lazy_loader() -> None: assert not hasattr(gdsii, 'load_library') assert not hasattr(gdsii, 'load_libraryfile')