Generalize underscore into SINGLE_USE_PREFIX

This commit is contained in:
jan 2023-10-15 23:01:47 -07:00
parent 668d4b5d8b
commit 1bdb998085
3 changed files with 39 additions and 32 deletions

View File

@ -11,7 +11,7 @@ from numpy import pi
from numpy.typing import ArrayLike
from ..pattern import Pattern
from ..library import ILibrary
from ..library import ILibrary, SINGLE_USE_PREFIX
from ..error import PortError, BuildError
from ..ports import PortList, Port
from ..abstract import Abstract
@ -422,7 +422,7 @@ class Pather(Builder):
set_rotation: float | None = None,
tool_port_names: tuple[str, str] = ('A', 'B'),
force_container: bool = False,
base_name: str = '_mpath',
base_name: str = SINGLE_USE_PREFIX + 'mpath',
**kwargs,
) -> Self:
"""

View File

@ -15,7 +15,7 @@ from ..utils import SupportsBool, rotation_matrix_2d, layer_t
from ..ports import Port
from ..pattern import Pattern
from ..abstract import Abstract
from ..library import ILibrary, Library
from ..library import ILibrary, Library, SINGLE_USE_PREFIX
from ..error import BuildError
@ -288,13 +288,13 @@ class BasicTool(Tool, metaclass=ABCMeta):
)
gen_straight, sport_in, sport_out = self.straight
tree, pat = Library.mktree('_path')
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
pat.add_port_pair(names=port_names)
if data.in_transition:
ipat, iport_theirs, _iport_ours = data.in_transition
pat.plug(ipat, {port_names[1]: iport_theirs})
if not numpy.isclose(data.straight_length, 0):
straight = tree <= {'_straight': gen_straight(data.straight_length)}
straight = tree <= {SINGLE_USE_PREFIX + 'straight': gen_straight(data.straight_length)}
pat.plug(straight, {port_names[1]: sport_in})
if data.ccw is not None:
bend, bport_in, bport_out = self.bend
@ -391,7 +391,7 @@ class BasicTool(Tool, metaclass=ABCMeta):
**kwargs,
) -> ILibrary:
tree, pat = Library.mktree('_path')
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
pat.add_port_pair(names=(port_names[0], port_names[1]))
gen_straight, sport_in, _sport_out = self.straight
@ -408,7 +408,7 @@ class BasicTool(Tool, metaclass=ABCMeta):
if append:
pat.plug(straight_pat, {port_names[1]: sport_in}, append=True)
else:
straight = tree <= {'_straight': straight_pat}
straight = tree <= {SINGLE_USE_PREFIX + 'straight': straight_pat}
pat.plug(straight, {port_names[1]: sport_in}, append=True)
if ccw is not None:
bend, bport_in, bport_out = self.bend
@ -463,7 +463,7 @@ class PathTool(Tool, metaclass=ABCMeta):
out_ptype=out_ptype,
)
tree, pat = Library.mktree('_path')
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
pat.path(layer=self.layer, width=self.width, vertices=[(0, 0), (length, 0)])
if ccw is None:
@ -543,7 +543,7 @@ class PathTool(Tool, metaclass=ABCMeta):
# If the path ends in a bend, we need to add the final vertex
path_vertices.append(batch[-1].end_port.offset)
tree, pat = Library.mktree('_path')
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
pat.path(layer=self.layer, width=self.width, vertices=path_vertices)
pat.ports = {
port_names[0]: batch[0].start_port.copy().rotate(pi),

View File

@ -35,15 +35,24 @@ logger = logging.getLogger(__name__)
visitor_function_t = Callable[..., 'Pattern']
SINGLE_USE_PREFIX = '_'
"""
Names starting with this prefix are assumed to refer to single-use patterns,
which may be renamed automatically by `ILibrary.add()` (via
`rename_theirs=_rename_patterns()` )
"""
# TODO what are the consequences of making '_' special? maybe we can make this decision everywhere?
def _rename_patterns(lib: 'ILibraryView', name: str) -> str:
"""
The default `rename_theirs` function for `ILibrary.add`.
Treats names starting with an underscore as "one-offs" for which name conflicts
should be automatically resolved. Conflicts are resolved by calling
`lib.get_name(name.split('$')[0])`.
Names without a leading underscore are directly returned.
Treats names starting with `SINGLE_USE_PREFIX` (default: one underscore) as
"one-offs" for which name conflicts should be automatically resolved.
Conflicts are resolved by calling `lib.get_name(SINGLE_USE_PREFIX + stem)`
where `stem = name.removeprefix(SINGLE_USE_PREFIX).split('$')[0]`.
Names lacking the prefix are directly returned (not renamed).
Args:
lib: The library into which `name` is to be added (but is presumed to conflict)
@ -52,11 +61,11 @@ def _rename_patterns(lib: 'ILibraryView', name: str) -> str:
Returns:
The new name, not guaranteed to be conflict-free!
"""
if not name.startswith('_'): # TODO what are the consequences of making '_' special? maybe we can make this decision everywhere?
if not name.startswith(SINGLE_USE_PREFIX):
return name
stem = name.split('$')[0]
return lib.get_name(stem)
stem = name.removeprefix(SINGLE_USE_PREFIX).split('$')[0]
return lib.get_name(SINGLE_USE_PREFIX + stem)
class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
@ -284,7 +293,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
def get_name(
self,
name: str = '__',
name: str = SINGLE_USE_PREFIX * 2,
sanitize: bool = True,
max_length: int = 32,
quiet: bool | None = None,
@ -295,17 +304,17 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
This function may be overridden in a subclass or monkey-patched to fit the caller's requirements.
Args:
name: Preferred name for the pattern. Default '__'.
name: Preferred name for the pattern. Default is `SINGLE_USE_PREFIX * 2`.
sanitize: Allows only alphanumeric charaters and _?$. Replaces invalid characters with underscores.
max_length: Names longer than this will be truncated.
quiet: If `True`, suppress log messages. Default `None` suppresses messages only if
the name starts with an underscore.
the name starts with `SINGLE_USE_PREFIX`.
Returns:
Name, unique within this library.
"""
if quiet is None:
quiet = name.startswith('_')
quiet = name.startswith(SINGLE_USE_PREFIX)
if sanitize:
# Remove invalid characters
@ -316,7 +325,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
ii = 0
suffixed_name = sanitized_name
while suffixed_name in self or suffixed_name == '':
suffix = base64.b64encode(struct.pack('>Q', ii), b'$?').decode('ASCII')
suffix = base64.b64encode(struct.pack('>Q', ii), altchars=b'$?').decode('ASCII')
suffixed_name = sanitized_name + '$' + suffix[:-1].lstrip('A')
ii += 1
@ -602,16 +611,14 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
If `mutate_other=False` (default), all changes are made to a deepcopy of `other`.
By default, `rename_theirs` makes no changes to the name (causing a `LibraryError`) unless the
name starts with an underscore. Underscored names are truncated to before their first '$'
and then passed to `self.get_name()` to create a new unique name.
name starts with `SINGLE_USE_PREFIX`. Prefixed names are truncated to before their first
non-prefix '$' and then passed to `self.get_name()` to create a new unique name.
Args:
other: The library to insert keys from.
rename_theirs: Called as rename_theirs(self, name) for each duplicate name
encountered in `other`. Should return the new name for the pattern in
`other`.
Default is effectively
`self.get_name(name.split('$')[0]) if name.startswith('_') else name`
`other`. See above for default behavior.
mutate_other: If `True`, modify the original library and its contained patterns
(e.g. when renaming patterns and updating refs). Otherwise, operate on a deepcopy
(default).
@ -703,7 +710,8 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
exclude_types: Shape types passed in this argument are always left untouched, for
speed or convenience. Default: `(shapes.Polygon,)`
label2name: Given a label tuple as returned by `shape.normalized_form(...)`, pick
a name for the generated pattern. Default `self.get_name('_shape')`.
a name for the generated pattern.
Default `self.get_name(SINGLE_USE_PREIX + 'shape')`.
threshold: Only replace shapes with refs if there will be at least this many
instances.
@ -720,8 +728,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
if label2name is None:
def label2name(label):
return self.get_name('_shape')
#label2name = lambda label: self.get_name('_shape')
return self.get_name(SINGLE_USE_PREFIX + 'shape')
shape_counts: MutableMapping[tuple, int] = defaultdict(int)
shape_funcs = {}
@ -801,7 +808,8 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
Args:
name_func: Function f(this_pattern, shape) which generates a name for the
wrapping pattern. Default is `self.get_name('_rep')`.
wrapping pattern.
Default is `self.get_name(SINGLE_USE_PREFIX + 'rep')`.
Returns:
self
@ -810,8 +818,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
if name_func is None:
def name_func(_pat, _shape):
return self.get_name('_rep')
#name_func = lambda _pat, _shape: self.get_name('_rep')
return self.get_name(SINGLE_USE_PREFIX = 'rep')
for pat in tuple(self.values()):
for layer in pat.shapes: