Update comments
This commit is contained in:
parent
ed10f57a31
commit
94300d926a
@ -1,5 +1,7 @@
|
||||
"""
|
||||
Tools are objects which dynamically generate simple single-use devices (e.g. wires or waveguides)
|
||||
|
||||
# TODO document all tools
|
||||
"""
|
||||
from typing import Sequence, Literal, Callable, Any
|
||||
from abc import ABCMeta, abstractmethod # TODO any way to make Tool ok with implementing only one method?
|
||||
|
@ -36,7 +36,21 @@ visitor_function_t = Callable[..., 'Pattern']
|
||||
|
||||
|
||||
def _rename_patterns(lib: 'ILibraryView', name: str) -> str:
|
||||
# TODO document rename function
|
||||
"""
|
||||
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.
|
||||
|
||||
Args:
|
||||
lib: The library into which `name` is to be added (but is presumed to conflict)
|
||||
name: The original name, to be modified
|
||||
|
||||
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?
|
||||
return name
|
||||
|
||||
@ -45,6 +59,11 @@ def _rename_patterns(lib: 'ILibraryView', name: str) -> str:
|
||||
|
||||
|
||||
class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
"""
|
||||
Interface for a read-only library.
|
||||
|
||||
A library is a mapping from unique names (str) to collections of geometry (`Pattern`).
|
||||
"""
|
||||
# inherited abstract functions
|
||||
#def __getitem__(self, key: str) -> 'Pattern':
|
||||
#def __iter__(self) -> Iterator[str]:
|
||||
@ -53,6 +72,10 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
#__contains__, keys, items, values, get, __eq__, __ne__ supplied by Mapping
|
||||
|
||||
def abstract_view(self) -> 'AbstractView':
|
||||
"""
|
||||
Returns:
|
||||
An AbstractView into this library
|
||||
"""
|
||||
return AbstractView(self)
|
||||
|
||||
def abstract(self, name: str) -> Abstract:
|
||||
@ -205,14 +228,19 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
def flatten(
|
||||
self,
|
||||
tops: str | Sequence[str],
|
||||
flatten_ports: bool = False, # TODO document
|
||||
flatten_ports: bool = False,
|
||||
) -> dict[str, 'Pattern']:
|
||||
"""
|
||||
Removes all refs and adds equivalent shapes.
|
||||
Also flattens all referenced patterns.
|
||||
Returns copies of all `tops` patterns with all refs
|
||||
removed and replaced with equivalent shapes.
|
||||
Also returns flattened copies of all referenced patterns.
|
||||
The originals in the calling `Library` are not modified.
|
||||
For an in-place variant, see `Pattern.flatten`.
|
||||
|
||||
Args:
|
||||
tops: The pattern(s) to flattern.
|
||||
flatten_ports: If `True`, keep ports from any referenced
|
||||
patterns; otherwise discard them.
|
||||
|
||||
Returns:
|
||||
{name: flat_pattern} mapping for all flattened patterns.
|
||||
@ -222,7 +250,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
|
||||
flattened: dict[str, 'Pattern | None'] = {}
|
||||
|
||||
def flatten_single(name) -> None:
|
||||
def flatten_single(name: str) -> None:
|
||||
flattened[name] = None
|
||||
pat = self[name].deepcopy()
|
||||
|
||||
@ -428,11 +456,12 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
pattern = visit_after(pattern, hierarchy=hierarchy, memo=memo, transform=transform)
|
||||
|
||||
if pattern is not original_pattern:
|
||||
name = hierarchy[-1] # TODO what is name=None?
|
||||
name = hierarchy[-1]
|
||||
if not isinstance(self, ILibrary):
|
||||
raise LibraryError('visit_* functions returned a new `Pattern` object'
|
||||
' but the library is immutable')
|
||||
if name is None:
|
||||
# The top pattern is not the original pattern, but we don't know what to call it!
|
||||
raise LibraryError('visit_* functions returned a new `Pattern` object'
|
||||
' but no top-level name was provided in `hierarchy`')
|
||||
|
||||
@ -442,6 +471,11 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
|
||||
|
||||
class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
"""
|
||||
Interface for a writeable library.
|
||||
|
||||
A library is a mapping from unique names (str) to collections of geometry (`Pattern`).
|
||||
"""
|
||||
# inherited abstract functions
|
||||
#def __getitem__(self, key: str) -> 'Pattern':
|
||||
#def __iter__(self) -> Iterator[str]:
|
||||
@ -477,7 +511,8 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
Args:
|
||||
old_name: Current name for the pattern
|
||||
new_name: New name for the pattern
|
||||
#TODO move_Reference
|
||||
move_references: If `True`, any refs in this library pointing to `old_name`
|
||||
will be updated to point to `new_name`.
|
||||
|
||||
Returns:
|
||||
self
|
||||
@ -534,19 +569,31 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
rename_theirs: Callable[['ILibraryView', str], str] = _rename_patterns,
|
||||
) -> dict[str, str]:
|
||||
"""
|
||||
Add keys from another library into this one.
|
||||
Add items from another library into this one.
|
||||
|
||||
# TODO explain reference renaming and return
|
||||
If any name in `other` is already present in `self`, `rename_theirs(self, name)` is called
|
||||
to pick a new name for the newly-added pattern. If the new name still conflicts with a name
|
||||
in `self` a `LibraryError` is raised. All references to the original name (within `other)`
|
||||
are updated to the new name.
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
other: The library to insert keys from
|
||||
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
|
||||
`name.split('$')[0] if name.startswith('_') else name`
|
||||
`self.get_name(name.split('$')[0]) if name.startswith('_') else name`
|
||||
|
||||
Returns:
|
||||
self
|
||||
A mapping of `{old_name: new_name}` for all `old_name`s in `other`. Unchanged
|
||||
names map to themselves.
|
||||
|
||||
Raises:
|
||||
`LibraryError` if a duplicate name is encountered even after applying `rename_theirs()`.
|
||||
"""
|
||||
from .pattern import map_targets
|
||||
duplicates = set(self.keys()) & set(other.keys())
|
||||
@ -785,7 +832,15 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
self,
|
||||
repeat: bool = True,
|
||||
) -> set[str]:
|
||||
# TODO doc prune_empty
|
||||
"""
|
||||
Delete any empty patterns (i.e. where `Pattern.is_empty` returns `True`).
|
||||
|
||||
Args:
|
||||
repeat: Also recursively delete any patterns which only contain(ed) empty patterns.
|
||||
|
||||
Returns:
|
||||
A set containing the names of all deleted patterns
|
||||
"""
|
||||
trimmed = set()
|
||||
while empty := set(name for name, pat in self.items() if pat.is_empty()):
|
||||
for name in empty:
|
||||
@ -807,7 +862,13 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
key: str,
|
||||
delete_refs: bool = True,
|
||||
) -> Self:
|
||||
# TODO doc delete()
|
||||
"""
|
||||
Delete a pattern and (optionally) all refs pointing to that pattern.
|
||||
|
||||
Args:
|
||||
key: Name of the pattern to be deleted.
|
||||
delete_refs: If `True` (default), also delete all refs pointing to the pattern.
|
||||
"""
|
||||
del self[key]
|
||||
if delete_refs:
|
||||
for pat in self.values():
|
||||
@ -817,6 +878,12 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
|
||||
|
||||
class LibraryView(ILibraryView):
|
||||
"""
|
||||
Default implementation for a read-only library.
|
||||
|
||||
A library is a mapping from unique names (str) to collections of geometry (`Pattern`).
|
||||
This library is backed by an arbitrary python object which implements the `Mapping` interface.
|
||||
"""
|
||||
mapping: Mapping[str, 'Pattern']
|
||||
|
||||
def __init__(
|
||||
@ -842,6 +909,12 @@ class LibraryView(ILibraryView):
|
||||
|
||||
|
||||
class Library(ILibrary):
|
||||
"""
|
||||
Default implementation for a writeable library.
|
||||
|
||||
A library is a mapping from unique names (str) to collections of geometry (`Pattern`).
|
||||
This library is backed by an arbitrary python object which implements the `MutableMapping` interface.
|
||||
"""
|
||||
mapping: MutableMapping[str, 'Pattern']
|
||||
|
||||
def __init__(
|
||||
@ -892,6 +965,12 @@ class Library(ILibrary):
|
||||
def mktree(cls, name: str) -> tuple[Self, 'Pattern']:
|
||||
"""
|
||||
Create a new Library and immediately add a pattern
|
||||
|
||||
Args:
|
||||
name: The name for the new pattern (usually the name of the topcell).
|
||||
|
||||
Returns:
|
||||
The newly created `Library` and the newly created `Pattern`
|
||||
"""
|
||||
from .pattern import Pattern
|
||||
tree = cls()
|
||||
@ -1041,6 +1120,11 @@ class LazyLibrary(ILibrary):
|
||||
|
||||
|
||||
class AbstractView(Mapping[str, Abstract]):
|
||||
"""
|
||||
A read-only mapping from names to `Abstract` objects.
|
||||
|
||||
This is usually just used as a shorthand for repeated calls to `library.abstract()`.
|
||||
"""
|
||||
library: ILibraryView
|
||||
|
||||
def __init__(self, library: ILibraryView) -> None:
|
||||
|
@ -318,7 +318,12 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
Returns `None` if the Pattern is empty.
|
||||
|
||||
Args:
|
||||
TODO docs for get_bounds
|
||||
library: If `recurse=True`, any referenced patterns are loaded from this library.
|
||||
recurse: If `False`, do not evaluate the bounds of any refs (i.e. assume they are empty).
|
||||
If `True`, evaluate the bounds of all refs and their conained geometry recursively.
|
||||
Default `True`.
|
||||
cache: Mapping of `{name: bounds}` for patterns for which the bounds have already been calculated.
|
||||
Modified during the run (any referenced pattern's bounds are added).
|
||||
|
||||
Returns:
|
||||
`[[x_min, y_min], [x_max, y_max]]` or `None`
|
||||
@ -401,7 +406,12 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
Convenience wrapper for `get_bounds()` which asserts that the Pattern as non-None bounds.
|
||||
|
||||
Args:
|
||||
TODO docs for get_bounds
|
||||
library: If `recurse=True`, any referenced patterns are loaded from this library.
|
||||
recurse: If `False`, do not evaluate the bounds of any refs (i.e. assume they are empty).
|
||||
If `True`, evaluate the bounds of all refs and their conained geometry recursively.
|
||||
Default `True`.
|
||||
cache: Mapping of `{name: bounds}` for patterns for which the bounds have already been calculated.
|
||||
Modified during the run (any referenced pattern's bounds are added).
|
||||
|
||||
Returns:
|
||||
`[[x_min, y_min], [x_max, y_max]]`
|
||||
@ -590,12 +600,24 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
return not (self.has_refs() or self.has_shapes() or self.has_labels())
|
||||
|
||||
def has_refs(self) -> bool:
|
||||
"""
|
||||
Returns:
|
||||
True if the pattern contains any refs.
|
||||
"""
|
||||
return any(True for _ in chain.from_iterable(self.refs.values()))
|
||||
|
||||
def has_shapes(self) -> bool:
|
||||
"""
|
||||
Returns:
|
||||
True if the pattern contains any shapes.
|
||||
"""
|
||||
return any(True for _ in chain.from_iterable(self.shapes.values()))
|
||||
|
||||
def has_labels(self) -> bool:
|
||||
"""
|
||||
Returns:
|
||||
True if the pattern contains any labels.
|
||||
"""
|
||||
return any(True for _ in chain.from_iterable(self.labels.values()))
|
||||
|
||||
def ref(self, target: str | None, *args: Any, **kwargs: Any) -> Self:
|
||||
@ -708,21 +730,23 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
def flatten(
|
||||
self,
|
||||
library: Mapping[str, 'Pattern'],
|
||||
flatten_ports: bool = False, # TODO document
|
||||
flatten_ports: bool = False,
|
||||
) -> 'Pattern':
|
||||
"""
|
||||
Removes all refs (recursively) and adds equivalent shapes.
|
||||
Alters the current pattern in-place
|
||||
Alters the current pattern in-place.
|
||||
For a version which creates copies, see `Library.flatten`.
|
||||
|
||||
Args:
|
||||
library: Source for referenced patterns.
|
||||
flatten_ports: If `True`, keep ports from any referenced
|
||||
patterns; otherwise discard them.
|
||||
|
||||
Returns:
|
||||
self
|
||||
"""
|
||||
flattened: dict[str | None, 'Pattern | None'] = {}
|
||||
|
||||
# TODO both Library and Pattern have flatten()... pattern is in-place?
|
||||
def flatten_single(name: str | None) -> None:
|
||||
if name is None:
|
||||
pat = self
|
||||
|
@ -2,7 +2,7 @@
|
||||
Ref provides basic support for nesting Pattern objects within each other, by adding
|
||||
offset, rotation, scaling, and other such properties to the reference.
|
||||
"""
|
||||
#TODO more top-level documentation
|
||||
#TODO more top-level documentation for ref
|
||||
|
||||
from typing import Mapping, TYPE_CHECKING, Self
|
||||
import copy
|
||||
|
@ -17,6 +17,9 @@ class Polygon(Shape):
|
||||
A polygon, consisting of a bunch of vertices (Nx2 ndarray) which specify an
|
||||
implicitly-closed boundary, and an offset.
|
||||
|
||||
Note that the setter for `Polygon.vertices` may (but may not) create a copy of the
|
||||
passed vertex coordinates. See `numpy.array()` for details.
|
||||
|
||||
A `normalized_form(...)` is available, but can be quite slow with lots of vertices.
|
||||
"""
|
||||
__slots__ = (
|
||||
@ -38,7 +41,7 @@ class Polygon(Shape):
|
||||
|
||||
@vertices.setter
|
||||
def vertices(self, val: ArrayLike) -> None:
|
||||
val = numpy.array(val, dtype=float) # TODO document that these might not be copied
|
||||
val = numpy.array(val, dtype=float) # note that this hopefully won't create a copy
|
||||
if len(val.shape) < 2 or val.shape[1] != 2:
|
||||
raise PatternError('Vertices must be an Nx2 array')
|
||||
if val.shape[0] < 3:
|
||||
|
@ -60,8 +60,8 @@ def data_to_ports(
|
||||
# TODO missing ok?
|
||||
) -> Pattern:
|
||||
"""
|
||||
# TODO fixup documentation in port_utils
|
||||
# TODO move port_utils to utils.file?
|
||||
# TODO fixup documentation in ports2data
|
||||
# TODO move to utils.file?
|
||||
Examine `pattern` for labels specifying port info, and use that info
|
||||
to fill out its `ports` attribute.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user