implement auto-renaming during merge, and change _merge() to support it

This commit is contained in:
Jan Petykiewicz 2023-01-30 13:01:11 -08:00
parent 6f6143da1a
commit 3be4da3e7c

View File

@ -429,7 +429,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
pass pass
@abstractmethod @abstractmethod
def _merge(self, other: Mapping[str, 'Pattern'], key: str) -> None: def _merge(self, key_self: str, other: Mapping[str, 'Pattern'], key_other: str) -> None:
pass pass
def rename(self: ML, old_name: str, new_name: str) -> ML: def rename(self: ML, old_name: str, new_name: str) -> ML:
@ -487,32 +487,39 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
def add( def add(
self: ML, self: ML,
other: Mapping[str, 'Pattern'], other: Mapping[str, 'Pattern'],
use_ours: Callable[[str], bool] = lambda name: False, rename_theirs: Callable[['Library', str], str] = _rename_patterns,
use_theirs: Callable[[str], bool] = lambda name: False,
) -> ML: ) -> ML:
""" """
Add keys from another library into this one. Add keys from another library into this one.
Args: Args:
other: The library to insert keys from other: The library to insert keys from
use_ours: Decision function for name conflicts, called with pattern name. rename_theirs: Called as rename_theirs(self, name) for each duplicate name
Should return `True` if the value from `self` should be used. encountered in `other`. Should return the new name for the pattern in
use_theirs: Decision function for name conflicts. Same format as `use_ours`. `other`.
Should return `True` if the value from `other` should be used. Default is effectively
`use_ours` takes priority over `use_theirs`. `name.split('$')[0] if name.startswith('_') else name`
Returns: Returns:
self self
""" """
duplicates = set(self.keys()) & set(other.keys()) duplicates = set(self.keys()) & set(other.keys())
keep_ours = set(name for name in duplicates if use_ours(name)) rename_map = {name: rename_theirs(self, name) for name in duplicates}
keep_theirs = set(name for name in duplicates - keep_ours if use_theirs(name)) renamed = set(rename_map.keys())
conflicts = duplicates - keep_ours - keep_theirs
if len(renamed) != len(rename_map):
raise LibraryError('Multiple `other` patterns have the same name after renaming!')
internal_conflicts = (set(other.keys()) - duplicates) & renamed
if internal_conflicts:
raise LibraryError('Renamed patterns conflict with un-renamed names in `other`' + pformat(internal_conflicts))
conflicts = set(self.keys()) & renamed
if conflicts: if conflicts:
raise LibraryError('Unresolved duplicate keys encountered in library merge: ' + pformat(conflicts)) raise LibraryError('Unresolved duplicate keys encountered in library merge: ' + pformat(conflicts))
for key in set(other.keys()) - keep_ours: for key in other.keys():
self._merge(other, key) new_key = rename_map.get(key, key)
self._merge(new_key, other, key)
return self return self
@ -692,7 +699,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
new = type(self)() new = type(self)()
for key in keep: for key in keep:
new._merge(self, key) new._merge(key, self, key)
return new return new
@ -758,8 +765,8 @@ class WrapLibrary(MutableLibrary):
def __delitem__(self, key: str) -> None: def __delitem__(self, key: str) -> None:
del self.mapping[key] del self.mapping[key]
def _merge(self, other: Mapping[str, 'Pattern'], key: str) -> None: def _merge(self, key_self: str, other: Mapping[str, 'Pattern'], key_other: str) -> None:
self[key] = other[key] self[key_self] = other[key_other]
def __repr__(self) -> str: def __repr__(self) -> str:
return f'<WrapLibrary ({type(self.mapping)}) with keys\n' + pformat(list(self.keys())) + '>' return f'<WrapLibrary ({type(self.mapping)}) with keys\n' + pformat(list(self.keys())) + '>'
@ -830,16 +837,13 @@ class LazyLibrary(MutableLibrary):
def __len__(self) -> int: def __len__(self) -> int:
return len(self.dict) return len(self.dict)
def set_const(self, key: str, value: 'Pattern') -> None: def _merge(self, key_self: str, other: Mapping[str, 'Pattern'], key_other: str) -> None:
self[key] = lambda: value
def _merge(self, other: Mapping[str, 'Pattern'], key: str) -> None:
if isinstance(other, LazyLibrary): if isinstance(other, LazyLibrary):
self.dict[key] = other.dict[key] self.dict[key_self] = other.dict[key_other]
if key in other.cache: if key_other in other.cache:
self.cache[key] = other.cache[key] self.cache[key_self] = other.cache[key_other]
else: else:
self.set_const(key, other[key]) self[key_self] = other[key_other]
def __repr__(self) -> str: def __repr__(self) -> str:
return '<LazyLibrary with keys\n' + pformat(list(self.keys())) + '>' return '<LazyLibrary with keys\n' + pformat(list(self.keys())) + '>'
@ -902,3 +906,13 @@ class AbstractView(Mapping[str, Abstract]):
def __len__(self) -> int: def __len__(self) -> int:
return self.library.__len__() return self.library.__len__()
def _rename_patterns(lib: Library, name: str) -> str:
# TODO document rename function
if not name.startswith('_'):
return name
stem = name.split('$')[0]
return lib.get_name(stem)