[library] auto-rename single-use names in add_source
This commit is contained in:
parent
4b0a899e17
commit
ac34108253
5 changed files with 48 additions and 4 deletions
|
|
@ -287,7 +287,7 @@ class LibraryBuilder(INameView):
|
||||||
self,
|
self,
|
||||||
source: Mapping[str, Pattern] | ILibraryView,
|
source: Mapping[str, Pattern] | ILibraryView,
|
||||||
*,
|
*,
|
||||||
rename_theirs: Callable[[INameView, str], str] | None = None,
|
rename_theirs: Callable[[INameView, str], str] | None = _rename_patterns,
|
||||||
rename_when: Literal['conflict', 'always'] = 'conflict',
|
rename_when: Literal['conflict', 'always'] = 'conflict',
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -309,6 +309,8 @@ class LibraryBuilder(INameView):
|
||||||
rename_theirs: Function used to choose visible names for imported
|
rename_theirs: Function used to choose visible names for imported
|
||||||
source cells. Its `INameView` argument contains existing and
|
source cells. Its `INameView` argument contains existing and
|
||||||
previously reserved names, but does not support pattern lookup.
|
previously reserved names, but does not support pattern lookup.
|
||||||
|
By default, conflicting single-use names are made unique;
|
||||||
|
pass `None` to reject every conflict.
|
||||||
rename_when: If `'conflict'`, only conflicting names are renamed.
|
rename_when: If `'conflict'`, only conflicting names are renamed.
|
||||||
If `'always'`, every imported source name is passed through
|
If `'always'`, every imported source name is passed through
|
||||||
`rename_theirs`.
|
`rename_theirs`.
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,14 @@ from ..error import LibraryError
|
||||||
from ..pattern import Pattern, map_layers, map_targets
|
from ..pattern import Pattern, map_layers, map_targets
|
||||||
from .base import ILibrary, ILibraryView
|
from .base import ILibrary, ILibraryView
|
||||||
from .capabilities import IBorrowing, IMaterializable
|
from .capabilities import IBorrowing, IMaterializable
|
||||||
from .utils import INameView, dangling_mode_t, _plan_source_names, _source_rename_map, _validate_dangling_mode
|
from .utils import (
|
||||||
|
INameView,
|
||||||
|
dangling_mode_t,
|
||||||
|
_plan_source_names,
|
||||||
|
_rename_patterns,
|
||||||
|
_source_rename_map,
|
||||||
|
_validate_dangling_mode,
|
||||||
|
)
|
||||||
from .mapping import LibraryView
|
from .mapping import LibraryView
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
@ -304,7 +311,7 @@ class OverlayLibrary(ILibrary, IMaterializable, IBorrowing):
|
||||||
self,
|
self,
|
||||||
source: Mapping[str, Pattern] | ILibraryView,
|
source: Mapping[str, Pattern] | ILibraryView,
|
||||||
*,
|
*,
|
||||||
rename_theirs: Callable[[INameView, str], str] | None = None,
|
rename_theirs: Callable[[INameView, str], str] | None = _rename_patterns,
|
||||||
rename_when: Literal['conflict', 'always'] = 'conflict',
|
rename_when: Literal['conflict', 'always'] = 'conflict',
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -317,6 +324,8 @@ class OverlayLibrary(ILibrary, IMaterializable, IBorrowing):
|
||||||
rename_theirs: Function used to choose visible names for imported
|
rename_theirs: Function used to choose visible names for imported
|
||||||
source cells. Its `INameView` argument contains existing and
|
source cells. Its `INameView` argument contains existing and
|
||||||
previously reserved names, but does not support pattern lookup.
|
previously reserved names, but does not support pattern lookup.
|
||||||
|
By default, conflicting single-use names are made unique;
|
||||||
|
pass `None` to reject every conflict.
|
||||||
rename_when: If `'conflict'`, only conflicting names are renamed.
|
rename_when: If `'conflict'`, only conflicting names are renamed.
|
||||||
If `'always'`, every imported source name is passed through
|
If `'always'`, every imported source name is passed through
|
||||||
`rename_theirs`.
|
`rename_theirs`.
|
||||||
|
|
|
||||||
|
|
@ -437,6 +437,19 @@ def test_build_library_add_source_can_rename_every_source_cell() -> None:
|
||||||
assert report.provenance["mapped_child"].requested_name == "child"
|
assert report.provenance["mapped_child"].requested_name == "child"
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_library_add_source_renames_conflicting_single_use_name_by_default() -> None:
|
||||||
|
source = Library({'_myCellName$F': Pattern(), 'source_top': Pattern()})
|
||||||
|
source['source_top'].ref('_myCellName$F')
|
||||||
|
builder = LibraryBuilder()
|
||||||
|
builder['_myCellName$F'] = Pattern()
|
||||||
|
|
||||||
|
rename_map = builder.add_source(source)
|
||||||
|
built, _report = builder.build()
|
||||||
|
|
||||||
|
assert rename_map == {'_myCellName$F': '_myCellName'}
|
||||||
|
assert set(built['source_top'].refs) == {'_myCellName'}
|
||||||
|
|
||||||
|
|
||||||
def test_library_builder_adds_an_ordinary_view_eagerly() -> None:
|
def test_library_builder_adds_an_ordinary_view_eagerly() -> None:
|
||||||
child = Pattern()
|
child = Pattern()
|
||||||
top = Pattern()
|
top = Pattern()
|
||||||
|
|
|
||||||
|
|
@ -382,7 +382,7 @@ def test_gdsii_lazy_overlay_add_source_rename_when_validation() -> None:
|
||||||
src = _make_lazy_port_library()
|
src = _make_lazy_port_library()
|
||||||
|
|
||||||
with pytest.raises(TypeError, match='rename_theirs'):
|
with pytest.raises(TypeError, match='rename_theirs'):
|
||||||
OverlayLibrary().add_source(src, rename_when='always')
|
OverlayLibrary().add_source(src, rename_theirs=None, rename_when='always')
|
||||||
|
|
||||||
with pytest.raises(ValueError, match='rename mode'):
|
with pytest.raises(ValueError, match='rename mode'):
|
||||||
OverlayLibrary().add_source(src, rename_when='sometimes') # type: ignore[arg-type]
|
OverlayLibrary().add_source(src, rename_when='sometimes') # type: ignore[arg-type]
|
||||||
|
|
|
||||||
|
|
@ -621,6 +621,26 @@ def test_overlay_add_source_callback_sees_earlier_name_reservations() -> None:
|
||||||
assert set(rename_map.values()) <= set(overlay)
|
assert set(rename_map.values()) <= set(overlay)
|
||||||
|
|
||||||
|
|
||||||
|
def test_overlay_add_source_renames_conflicting_single_use_name_by_default() -> None:
|
||||||
|
overlay = OverlayLibrary()
|
||||||
|
overlay['_myCellName$F'] = Pattern()
|
||||||
|
source = Library({'_myCellName$F': Pattern(), 'source_top': Pattern()})
|
||||||
|
source['source_top'].ref('_myCellName$F')
|
||||||
|
|
||||||
|
rename_map = overlay.add_source(source)
|
||||||
|
|
||||||
|
assert rename_map == {'_myCellName$F': '_myCellName'}
|
||||||
|
assert set(overlay['source_top'].refs) == {'_myCellName'}
|
||||||
|
|
||||||
|
|
||||||
|
def test_overlay_add_source_can_explicitly_disable_default_renaming() -> None:
|
||||||
|
overlay = OverlayLibrary()
|
||||||
|
overlay['_helper$A'] = Pattern()
|
||||||
|
|
||||||
|
with pytest.raises(LibraryError, match='Conflicting name'):
|
||||||
|
overlay.add_source(Library({'_helper$A': Pattern()}), rename_theirs=None)
|
||||||
|
|
||||||
|
|
||||||
def test_port_load_view_detaches_already_materialized_overlay_pattern() -> None:
|
def test_port_load_view_detaches_already_materialized_overlay_pattern() -> None:
|
||||||
overlay = OverlayLibrary()
|
overlay = OverlayLibrary()
|
||||||
overlay.add_source(Library({"top": Pattern()}))
|
overlay.add_source(Library({"top": Pattern()}))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue