use Self type

master
Jan Petykiewicz 1 year ago committed by jan
parent 1463535676
commit 4482ede3a7

@ -1,4 +1,4 @@
from typing import TypeVar from typing import Self
import copy import copy
import logging import logging
@ -17,9 +17,6 @@ from .utils import rotation_matrix_2d, normalize_mirror
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
AA = TypeVar('AA', bound='Abstract')
class Abstract(PortList): class Abstract(PortList):
__slots__ = ('name', '_ports') __slots__ = ('name', '_ports')
@ -71,7 +68,7 @@ class Abstract(PortList):
s += ']>' s += ']>'
return s return s
def translate_ports(self: AA, offset: ArrayLike) -> AA: def translate_ports(self, offset: ArrayLike) -> Self:
""" """
Translates all ports by the given offset. Translates all ports by the given offset.
@ -85,7 +82,7 @@ class Abstract(PortList):
port.translate(offset) port.translate(offset)
return self return self
def scale_by(self: AA, c: float) -> AA: def scale_by(self, c: float) -> Self:
""" """
Scale this Abstract by the given value Scale this Abstract by the given value
(all port offsets are scaled) (all port offsets are scaled)
@ -100,7 +97,7 @@ class Abstract(PortList):
port.offset *= c port.offset *= c
return self return self
def rotate_around(self: AA, pivot: ArrayLike, rotation: float) -> AA: def rotate_around(self, pivot: ArrayLike, rotation: float) -> Self:
""" """
Rotate the Abstract around the a location. Rotate the Abstract around the a location.
@ -118,7 +115,7 @@ class Abstract(PortList):
self.translate_ports(+pivot) self.translate_ports(+pivot)
return self return self
def rotate_port_offsets(self: AA, rotation: float) -> AA: def rotate_port_offsets(self, rotation: float) -> Self:
""" """
Rotate the offsets of all ports around (0, 0) Rotate the offsets of all ports around (0, 0)
@ -132,7 +129,7 @@ class Abstract(PortList):
port.offset = rotation_matrix_2d(rotation) @ port.offset port.offset = rotation_matrix_2d(rotation) @ port.offset
return self return self
def rotate_ports(self: AA, rotation: float) -> AA: def rotate_ports(self, rotation: float) -> Self:
""" """
Rotate each port around its offset (i.e. in place) Rotate each port around its offset (i.e. in place)
@ -146,7 +143,7 @@ class Abstract(PortList):
port.rotate(rotation) port.rotate(rotation)
return self return self
def mirror_port_offsets(self: AA, across_axis: int) -> AA: def mirror_port_offsets(self, across_axis: int) -> Self:
""" """
Mirror the offsets of all shapes, labels, and refs across an axis Mirror the offsets of all shapes, labels, and refs across an axis
@ -161,7 +158,7 @@ class Abstract(PortList):
port.offset[across_axis - 1] *= -1 port.offset[across_axis - 1] *= -1
return self return self
def mirror_ports(self: AA, across_axis: int) -> AA: def mirror_ports(self, across_axis: int) -> Self:
""" """
Mirror each port's rotation across an axis, relative to its Mirror each port's rotation across an axis, relative to its
offset offset
@ -177,7 +174,7 @@ class Abstract(PortList):
port.mirror(across_axis) port.mirror(across_axis)
return self return self
def mirror(self: AA, across_axis: int) -> AA: def mirror(self, across_axis: int) -> Self:
""" """
Mirror the Pattern across an axis Mirror the Pattern across an axis
@ -192,7 +189,7 @@ class Abstract(PortList):
self.mirror_port_offsets(across_axis) self.mirror_port_offsets(across_axis)
return self return self
def apply_ref_transform(self: AA, ref: Ref) -> AA: def apply_ref_transform(self, ref: Ref) -> Self:
""" """
Apply the transform from a `Ref` to the ports of this `Abstract`. Apply the transform from a `Ref` to the ports of this `Abstract`.
This changes the port locations to where they would be in the Ref's parent pattern. This changes the port locations to where they would be in the Ref's parent pattern.
@ -211,7 +208,7 @@ class Abstract(PortList):
self.translate_ports(ref.offset) self.translate_ports(ref.offset)
return self return self
def undo_ref_transform(self: AA, ref: Ref) -> AA: def undo_ref_transform(self, ref: Ref) -> Self:
""" """
Apply the inverse transform from a `Ref` to the ports of this `Abstract`. Apply the inverse transform from a `Ref` to the ports of this `Abstract`.
This changes the port locations to where they would be in the Ref's target (from the parent). This changes the port locations to where they would be in the Ref's target (from the parent).

@ -1,4 +1,4 @@
from typing import TypeVar, Sequence, MutableMapping, Mapping from typing import Self, Sequence, MutableMapping, Mapping
import copy import copy
import logging import logging
@ -20,10 +20,6 @@ from .utils import ell
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
BB = TypeVar('BB', bound='Builder')
PP = TypeVar('PP', bound='Pather')
class Builder(PortList): class Builder(PortList):
""" """
TODO DOCUMENT Builder TODO DOCUMENT Builder
@ -239,7 +235,7 @@ class Builder(PortList):
return new return new
def plug( def plug(
self: BB, self,
other: Abstract | str | NamedPattern, other: Abstract | str | NamedPattern,
map_in: dict[str, str], map_in: dict[str, str],
map_out: dict[str, str | None] | None = None, map_out: dict[str, str | None] | None = None,
@ -247,7 +243,7 @@ class Builder(PortList):
mirrored: tuple[bool, bool] = (False, False), mirrored: tuple[bool, bool] = (False, False),
inherit_name: bool = True, inherit_name: bool = True,
set_rotation: bool | None = None, set_rotation: bool | None = None,
) -> BB: ) -> Self:
""" """
Instantiate a device `library[name]` into the current device, connecting Instantiate a device `library[name]` into the current device, connecting
the ports specified by `map_in` and renaming the unconnected the ports specified by `map_in` and renaming the unconnected
@ -340,7 +336,7 @@ class Builder(PortList):
return self return self
def place( def place(
self: BB, self,
other: Abstract | str | NamedPattern, other: Abstract | str | NamedPattern,
*, *,
offset: ArrayLike = (0, 0), offset: ArrayLike = (0, 0),
@ -349,7 +345,7 @@ class Builder(PortList):
mirrored: tuple[bool, bool] = (False, False), mirrored: tuple[bool, bool] = (False, False),
port_map: dict[str, str | None] | None = None, port_map: dict[str, str | None] | None = None,
skip_port_check: bool = False, skip_port_check: bool = False,
) -> BB: ) -> Self:
""" """
Instantiate the device `other` into the current device, adding its Instantiate the device `other` into the current device, adding its
ports to those of the current device (but not connecting any ports). ports to those of the current device (but not connecting any ports).
@ -422,7 +418,7 @@ class Builder(PortList):
self.pattern.refs.append(sp) self.pattern.refs.append(sp)
return self return self
def translate(self: BB, offset: ArrayLike) -> BB: def translate(self, offset: ArrayLike) -> Self:
""" """
Translate the pattern and all ports. Translate the pattern and all ports.
@ -437,7 +433,7 @@ class Builder(PortList):
port.translate(offset) port.translate(offset)
return self return self
def rotate_around(self: BB, pivot: ArrayLike, angle: float) -> BB: def rotate_around(self, pivot: ArrayLike, angle: float) -> Self:
""" """
Rotate the pattern and all ports. Rotate the pattern and all ports.
@ -453,7 +449,7 @@ class Builder(PortList):
port.rotate_around(pivot, angle) port.rotate_around(pivot, angle)
return self return self
def mirror(self: BB, axis: int) -> BB: def mirror(self, axis: int) -> Self:
""" """
Mirror the pattern and all ports across the specified axis. Mirror the pattern and all ports across the specified axis.
@ -468,7 +464,7 @@ class Builder(PortList):
p.mirror(axis) p.mirror(axis)
return self return self
def set_dead(self: BB) -> BB: def set_dead(self) -> Self:
""" """
Disallows further changes through `plug()` or `place()`. Disallows further changes through `plug()` or `place()`.
This is meant for debugging: This is meant for debugging:
@ -673,10 +669,10 @@ class Pather(Builder):
return s return s
def retool( def retool(
self: PP, self,
tool: Tool, tool: Tool,
keys: str | Sequence[str | None] | None = None, keys: str | Sequence[str | None] | None = None,
) -> PP: ) -> Self:
if keys is None or isinstance(keys, str): if keys is None or isinstance(keys, str):
self.tools[keys] = tool self.tools[keys] = tool
else: else:
@ -685,7 +681,7 @@ class Pather(Builder):
return self return self
def path( def path(
self: PP, self,
portspec: str, portspec: str,
ccw: SupportsBool | None, ccw: SupportsBool | None,
length: float, length: float,
@ -693,7 +689,7 @@ class Pather(Builder):
tool_port_names: Sequence[str] = ('A', 'B'), tool_port_names: Sequence[str] = ('A', 'B'),
base_name: str = '_path', base_name: str = '_path',
**kwargs, **kwargs,
) -> PP: ) -> Self:
if self._dead: if self._dead:
logger.error('Skipping path() since device is dead') logger.error('Skipping path() since device is dead')
return self return self
@ -706,7 +702,7 @@ class Pather(Builder):
return self.plug(Abstract(name, pat.ports), {portspec: tool_port_names[0]}) return self.plug(Abstract(name, pat.ports), {portspec: tool_port_names[0]})
def path_to( def path_to(
self: PP, self,
portspec: str, portspec: str,
ccw: SupportsBool | None, ccw: SupportsBool | None,
position: float, position: float,
@ -714,7 +710,7 @@ class Pather(Builder):
tool_port_names: Sequence[str] = ('A', 'B'), tool_port_names: Sequence[str] = ('A', 'B'),
base_name: str = '_pathto', base_name: str = '_pathto',
**kwargs, **kwargs,
) -> PP: ) -> Self:
if self._dead: if self._dead:
logger.error('Skipping path_to() since device is dead') logger.error('Skipping path_to() since device is dead')
return self return self
@ -740,7 +736,7 @@ class Pather(Builder):
return self.path(portspec, ccw, length, tool_port_names=tool_port_names, base_name=base_name, **kwargs) return self.path(portspec, ccw, length, tool_port_names=tool_port_names, base_name=base_name, **kwargs)
def mpath( def mpath(
self: PP, self,
portspec: str | Sequence[str], portspec: str | Sequence[str],
ccw: SupportsBool | None, ccw: SupportsBool | None,
*, *,
@ -750,7 +746,7 @@ class Pather(Builder):
force_container: bool = False, force_container: bool = False,
base_name: str = '_mpath', base_name: str = '_mpath',
**kwargs, **kwargs,
) -> PP: ) -> Self:
if self._dead: if self._dead:
logger.error('Skipping mpath() since device is dead') logger.error('Skipping mpath() since device is dead')
return self return self
@ -790,7 +786,7 @@ class Pather(Builder):
# TODO def path_join() and def bus_join()? # TODO def path_join() and def bus_join()?
def flatten(self: PP) -> PP: def flatten(self) -> Self:
""" """
Flatten the contained pattern, using the contained library to resolve references. Flatten the contained pattern, using the contained library to resolve references.

@ -1,4 +1,4 @@
from typing import TypeVar, Sequence, MutableMapping, Mapping from typing import Self, Sequence, MutableMapping, Mapping
import copy import copy
import logging import logging
@ -17,9 +17,6 @@ from .utils import ell
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
BB = TypeVar('BB', bound='FlatBuilder')
class FlatBuilder(PortList): class FlatBuilder(PortList):
""" """
TODO DOCUMENT FlatBuilder TODO DOCUMENT FlatBuilder
@ -173,7 +170,7 @@ class FlatBuilder(PortList):
return new return new
def plug( def plug(
self: BB, self,
other: Pattern, other: Pattern,
map_in: dict[str, str], map_in: dict[str, str],
map_out: dict[str, str | None] | None = None, map_out: dict[str, str | None] | None = None,
@ -181,7 +178,7 @@ class FlatBuilder(PortList):
mirrored: tuple[bool, bool] = (False, False), mirrored: tuple[bool, bool] = (False, False),
inherit_name: bool = True, inherit_name: bool = True,
set_rotation: bool | None = None, set_rotation: bool | None = None,
) -> BB: ) -> Self:
""" """
Instantiate another pattern into the current device, connecting Instantiate another pattern into the current device, connecting
the ports specified by `map_in` and renaming the unconnected the ports specified by `map_in` and renaming the unconnected
@ -269,7 +266,7 @@ class FlatBuilder(PortList):
return self return self
def place( def place(
self: BB, self,
other: Pattern, other: Pattern,
*, *,
offset: ArrayLike = (0, 0), offset: ArrayLike = (0, 0),
@ -278,7 +275,7 @@ class FlatBuilder(PortList):
mirrored: tuple[bool, bool] = (False, False), mirrored: tuple[bool, bool] = (False, False),
port_map: dict[str, str | None] | None = None, port_map: dict[str, str | None] | None = None,
skip_port_check: bool = False, skip_port_check: bool = False,
) -> BB: ) -> Self:
""" """
Instantiate the device `other` into the current device, adding its Instantiate the device `other` into the current device, adding its
ports to those of the current device (but not connecting any ports). ports to those of the current device (but not connecting any ports).
@ -348,7 +345,7 @@ class FlatBuilder(PortList):
self.pattern.append(other_copy) self.pattern.append(other_copy)
return self return self
def translate(self: BB, offset: ArrayLike) -> BB: def translate(self, offset: ArrayLike) -> Self:
""" """
Translate the pattern and all ports. Translate the pattern and all ports.
@ -363,7 +360,7 @@ class FlatBuilder(PortList):
port.translate(offset) port.translate(offset)
return self return self
def rotate_around(self: BB, pivot: ArrayLike, angle: float) -> BB: def rotate_around(self, pivot: ArrayLike, angle: float) -> Self:
""" """
Rotate the pattern and all ports. Rotate the pattern and all ports.
@ -379,7 +376,7 @@ class FlatBuilder(PortList):
port.rotate_around(pivot, angle) port.rotate_around(pivot, angle)
return self return self
def mirror(self: BB, axis: int) -> BB: def mirror(self, axis: int) -> Self:
""" """
Mirror the pattern and all ports across the specified axis. Mirror the pattern and all ports across the specified axis.
@ -394,7 +391,7 @@ class FlatBuilder(PortList):
p.mirror(axis) p.mirror(axis)
return self return self
def set_dead(self: BB) -> BB: def set_dead(self) -> Self:
""" """
Disallows further changes through `plug()` or `place()`. Disallows further changes through `plug()` or `place()`.
This is meant for debugging: This is meant for debugging:
@ -417,10 +414,10 @@ class FlatBuilder(PortList):
return s return s
def retool( def retool(
self: BB, self,
tool: Tool, tool: Tool,
keys: str | Sequence[str | None] | None = None, keys: str | Sequence[str | None] | None = None,
) -> BB: ) -> Self:
if keys is None or isinstance(keys, str): if keys is None or isinstance(keys, str):
self.tools[keys] = tool self.tools[keys] = tool
else: else:
@ -429,7 +426,7 @@ class FlatBuilder(PortList):
return self return self
def path( def path(
self: BB, self,
portspec: str, portspec: str,
ccw: SupportsBool | None, ccw: SupportsBool | None,
length: float, length: float,
@ -437,7 +434,7 @@ class FlatBuilder(PortList):
tool_port_names: Sequence[str] = ('A', 'B'), tool_port_names: Sequence[str] = ('A', 'B'),
base_name: str = '_path', base_name: str = '_path',
**kwargs, **kwargs,
) -> BB: ) -> Self:
if self._dead: if self._dead:
logger.error('Skipping path() since device is dead') logger.error('Skipping path() since device is dead')
return self return self
@ -448,7 +445,7 @@ class FlatBuilder(PortList):
return self.plug(pat, {portspec: tool_port_names[0]}) return self.plug(pat, {portspec: tool_port_names[0]})
def path_to( def path_to(
self: BB, self,
portspec: str, portspec: str,
ccw: SupportsBool | None, ccw: SupportsBool | None,
position: float, position: float,
@ -456,7 +453,7 @@ class FlatBuilder(PortList):
tool_port_names: Sequence[str] = ('A', 'B'), tool_port_names: Sequence[str] = ('A', 'B'),
base_name: str = '_pathto', base_name: str = '_pathto',
**kwargs, **kwargs,
) -> BB: ) -> Self:
if self._dead: if self._dead:
logger.error('Skipping path_to() since device is dead') logger.error('Skipping path_to() since device is dead')
return self return self
@ -482,7 +479,7 @@ class FlatBuilder(PortList):
return self.path(portspec, ccw, length, tool_port_names=tool_port_names, base_name=base_name, **kwargs) return self.path(portspec, ccw, length, tool_port_names=tool_port_names, base_name=base_name, **kwargs)
def mpath( def mpath(
self: BB, self,
portspec: str | Sequence[str], portspec: str | Sequence[str],
ccw: SupportsBool | None, ccw: SupportsBool | None,
*, *,
@ -492,7 +489,7 @@ class FlatBuilder(PortList):
force_container: bool = False, force_container: bool = False,
base_name: str = '_mpath', base_name: str = '_mpath',
**kwargs, **kwargs,
) -> BB: ) -> Self:
if self._dead: if self._dead:
logger.error('Skipping mpath() since device is dead') logger.error('Skipping mpath() since device is dead')
return self return self

@ -1,4 +1,4 @@
from typing import TypeVar from typing import Self
import copy import copy
import numpy import numpy
@ -10,9 +10,6 @@ from .traits import PositionableImpl, LayerableImpl, Copyable, Pivotable, Repeat
from .traits import AnnotatableImpl from .traits import AnnotatableImpl
L = TypeVar('L', bound='Label')
class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl, class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl,
Pivotable, Copyable, metaclass=AutoSlots): Pivotable, Copyable, metaclass=AutoSlots):
""" """
@ -53,7 +50,7 @@ class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl,
self.repetition = repetition self.repetition = repetition
self.annotations = annotations if annotations is not None else {} self.annotations = annotations if annotations is not None else {}
def __copy__(self: L) -> L: def __copy__(self) -> Self:
return type(self)( return type(self)(
string=self.string, string=self.string,
offset=self.offset.copy(), offset=self.offset.copy(),
@ -61,13 +58,13 @@ class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl,
repetition=self.repetition, repetition=self.repetition,
) )
def __deepcopy__(self: L, memo: dict | None = None) -> L: def __deepcopy__(self, memo: dict | None = None) -> Self:
memo = {} if memo is None else memo memo = {} if memo is None else memo
new = copy.copy(self) new = copy.copy(self)
new._offset = self._offset.copy() new._offset = self._offset.copy()
return new return new
def rotate_around(self: L, pivot: ArrayLike, rotation: float) -> L: def rotate_around(self, pivot: ArrayLike, rotation: float) -> Self:
""" """
Rotate the label around a point. Rotate the label around a point.

@ -5,7 +5,7 @@ Library classes for managing unique name->pattern mappings and
# TODO documentn all library classes # TODO documentn all library classes
# TODO toplevel documentation of library, classes, and abstracts # TODO toplevel documentation of library, classes, and abstracts
""" """
from typing import Callable, TypeVar, Type, TYPE_CHECKING, cast from typing import Callable, Self, Type, TYPE_CHECKING, cast
from typing import Iterator, Mapping, MutableMapping, Sequence from typing import Iterator, Mapping, MutableMapping, Sequence
import logging import logging
import base64 import base64
@ -33,9 +33,6 @@ logger = logging.getLogger(__name__)
visitor_function_t = Callable[..., 'Pattern'] visitor_function_t = Callable[..., 'Pattern']
L = TypeVar('L', bound='Library')
ML = TypeVar('ML', bound='MutableLibrary')
LL = TypeVar('LL', bound='LazyLibrary')
def _rename_patterns(lib: 'Library', name: str) -> str: def _rename_patterns(lib: 'Library', name: str) -> str:
@ -163,10 +160,10 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
return new return new
def polygonize( def polygonize(
self: L, self,
num_vertices: int | None = None, num_vertices: int | None = None,
max_arclen: float | None = None, max_arclen: float | None = None,
) -> L: ) -> Self:
""" """
Calls `.polygonize(...)` on each pattern in this library. Calls `.polygonize(...)` on each pattern in this library.
Arguments are passed on to `shape.to_polygons(...)`. Arguments are passed on to `shape.to_polygons(...)`.
@ -186,10 +183,10 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
return self return self
def manhattanize( def manhattanize(
self: L, self,
grid_x: ArrayLike, grid_x: ArrayLike,
grid_y: ArrayLike, grid_y: ArrayLike,
) -> L: ) -> Self:
""" """
Calls `.manhattanize(grid_x, grid_y)` on each pattern in this library. Calls `.manhattanize(grid_x, grid_y)` on each pattern in this library.
@ -324,7 +321,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
return toplevel return toplevel
def dfs( def dfs(
self: L, self,
pattern: 'Pattern', pattern: 'Pattern',
visit_before: visitor_function_t | None = None, visit_before: visitor_function_t | None = None,
visit_after: visitor_function_t | None = None, visit_after: visitor_function_t | None = None,
@ -332,7 +329,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
hierarchy: tuple[str | None, ...] = (None,), hierarchy: tuple[str | None, ...] = (None,),
transform: ArrayLike | bool | None = False, transform: ArrayLike | bool | None = False,
memo: dict | None = None, memo: dict | None = None,
) -> L: ) -> Self:
""" """
Convenience function. Convenience function.
Performs a depth-first traversal of a pattern and its referenced patterns. Performs a depth-first traversal of a pattern and its referenced patterns.
@ -454,11 +451,11 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
pass pass
def rename( def rename(
self: ML, self,
old_name: str, old_name: str,
new_name: str, new_name: str,
move_references: bool = False, move_references: bool = False,
) -> ML: ) -> Self:
""" """
Rename a pattern. Rename a pattern.
@ -476,7 +473,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
self.move_references(old_name, new_name) self.move_references(old_name, new_name)
return self return self
def move_references(self: ML, old_target: str, new_target: str) -> ML: def move_references(self, old_target: str, new_target: str) -> Self:
""" """
Change all references pointing at `old_target` into references pointing at `new_target`. Change all references pointing at `old_target` into references pointing at `new_target`.
@ -601,12 +598,12 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
return rename_map.get(name, name) return rename_map.get(name, name)
def dedup( def dedup(
self: ML, self,
norm_value: int = int(1e6), norm_value: int = int(1e6),
exclude_types: tuple[Type] = (Polygon,), exclude_types: tuple[Type] = (Polygon,),
label2name: Callable[[tuple], str] | None = None, label2name: Callable[[tuple], str] | None = None,
threshold: int = 2, threshold: int = 2,
) -> ML: ) -> Self:
""" """
Iterates through all `Pattern`s. Within each `Pattern`, it iterates Iterates through all `Pattern`s. Within each `Pattern`, it iterates
over all shapes, calling `.normalized_form(norm_value)` on them to retrieve a scale-, over all shapes, calling `.normalized_form(norm_value)` on them to retrieve a scale-,
@ -706,9 +703,9 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
return self return self
def wrap_repeated_shapes( def wrap_repeated_shapes(
self: ML, self,
name_func: Callable[['Pattern', Shape | Label], str] | None = None, name_func: Callable[['Pattern', Shape | Label], str] | None = None,
) -> ML: ) -> Self:
""" """
Wraps all shapes and labels with a non-`None` `repetition` attribute Wraps all shapes and labels with a non-`None` `repetition` attribute
into a `Ref`/`Pattern` combination, and applies the `repetition` into a `Ref`/`Pattern` combination, and applies the `repetition`
@ -755,9 +752,9 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
return self return self
def subtree( def subtree(
self: ML, self,
tops: str | Sequence[str], tops: str | Sequence[str],
) -> ML: ) -> Self:
""" """
Return a new `Library`, containing only the specified patterns and the patterns they Return a new `Library`, containing only the specified patterns and the patterns they
reference (recursively). reference (recursively).
@ -797,10 +794,10 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
return trimmed return trimmed
def delete( def delete(
self: ML, self,
key: str, key: str,
delete_refs: bool = True, delete_refs: bool = True,
) -> ML: ) -> Self:
# TODO doc delete() # TODO doc delete()
del self[key] del self[key]
if delete_refs: if delete_refs:
@ -961,11 +958,11 @@ class LazyLibrary(MutableLibrary):
return '<LazyLibrary with keys\n' + pformat(list(self.keys())) + '>' return '<LazyLibrary with keys\n' + pformat(list(self.keys())) + '>'
def rename( def rename(
self: LL, self,
old_name: str, old_name: str,
new_name: str, new_name: str,
move_references: bool = False, move_references: bool = False,
) -> LL: ) -> Self:
""" """
Rename a pattern. Rename a pattern.
@ -989,7 +986,7 @@ class LazyLibrary(MutableLibrary):
return self return self
def move_references(self: LL, old_target: str, new_target: str) -> LL: def move_references(self, old_target: str, new_target: str) -> Self:
""" """
Change all references pointing at `old_target` into references pointing at `new_target`. Change all references pointing at `old_target` into references pointing at `new_target`.
@ -1007,7 +1004,7 @@ class LazyLibrary(MutableLibrary):
ref.target = new_target ref.target = new_target
return self return self
def precache(self: LL) -> LL: def precache(self) -> Self:
""" """
Force all patterns into the cache Force all patterns into the cache

@ -2,7 +2,7 @@
Base object representing a lithography mask. Base object representing a lithography mask.
""" """
from typing import Callable, Sequence, cast, Mapping, TypeVar, Any from typing import Callable, Sequence, cast, Mapping, Self, Any
import copy import copy
from itertools import chain from itertools import chain
@ -20,9 +20,6 @@ from .traits import AnnotatableImpl, Scalable, Mirrorable, Rotatable, Positionab
from .ports import Port, PortList from .ports import Port, PortList
P = TypeVar('P', bound='Pattern')
class Pattern(PortList, AnnotatableImpl, Mirrorable): class Pattern(PortList, AnnotatableImpl, Mirrorable):
""" """
2D layout consisting of some set of shapes, labels, and references to other Pattern objects 2D layout consisting of some set of shapes, labels, and references to other Pattern objects
@ -128,7 +125,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
) )
return new return new
def append(self: P, other_pattern: 'Pattern') -> P: def append(self, other_pattern: 'Pattern') -> Self:
""" """
Appends all shapes, labels and refs from other_pattern to self's shapes, Appends all shapes, labels and refs from other_pattern to self's shapes,
labels, and supbatterns. labels, and supbatterns.
@ -212,10 +209,10 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
return pat return pat
def polygonize( def polygonize(
self: P, self,
num_points: int | None = None, num_points: int | None = None,
max_arclen: float | None = None, max_arclen: float | None = None,
) -> P: ) -> Self:
""" """
Calls `.to_polygons(...)` on all the shapes in this Pattern, replacing them with the returned polygons. Calls `.to_polygons(...)` on all the shapes in this Pattern, replacing them with the returned polygons.
Arguments are passed directly to `shape.to_polygons(...)`. Arguments are passed directly to `shape.to_polygons(...)`.
@ -237,10 +234,10 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
return self return self
def manhattanize( def manhattanize(
self: P, self,
grid_x: ArrayLike, grid_x: ArrayLike,
grid_y: ArrayLike, grid_y: ArrayLike,
) -> P: ) -> Self:
""" """
Calls `.polygonize()` on the pattern, then calls `.manhattanize()` on all the Calls `.polygonize()` on the pattern, then calls `.manhattanize()` on all the
resulting shapes, replacing them with the returned Manhattan polygons. resulting shapes, replacing them with the returned Manhattan polygons.
@ -345,7 +342,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
assert bounds is not None assert bounds is not None
return bounds return bounds
def translate_elements(self: P, offset: ArrayLike) -> P: def translate_elements(self, offset: ArrayLike) -> Self:
""" """
Translates all shapes, label, refs, and ports by the given offset. Translates all shapes, label, refs, and ports by the given offset.
@ -359,7 +356,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
cast(Positionable, entry).translate(offset) cast(Positionable, entry).translate(offset)
return self return self
def scale_elements(self: P, c: float) -> P: def scale_elements(self, c: float) -> Self:
"""" """"
Scales all shapes and refs by the given value. Scales all shapes and refs by the given value.
@ -373,7 +370,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
cast(Scalable, entry).scale_by(c) cast(Scalable, entry).scale_by(c)
return self return self
def scale_by(self: P, c: float) -> P: def scale_by(self, c: float) -> Self:
""" """
Scale this Pattern by the given value Scale this Pattern by the given value
(all shapes and refs and their offsets are scaled, (all shapes and refs and their offsets are scaled,
@ -404,7 +401,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
port.offset *= c port.offset *= c
return self return self
def rotate_around(self: P, pivot: ArrayLike, rotation: float) -> P: def rotate_around(self, pivot: ArrayLike, rotation: float) -> Self:
""" """
Rotate the Pattern around the a location. Rotate the Pattern around the a location.
@ -422,7 +419,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
self.translate_elements(+pivot) self.translate_elements(+pivot)
return self return self
def rotate_element_centers(self: P, rotation: float) -> P: def rotate_element_centers(self, rotation: float) -> Self:
""" """
Rotate the offsets of all shapes, labels, refs, and ports around (0, 0) Rotate the offsets of all shapes, labels, refs, and ports around (0, 0)
@ -437,7 +434,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
cast(Positionable, entry).offset = numpy.dot(rotation_matrix_2d(rotation), old_offset) cast(Positionable, entry).offset = numpy.dot(rotation_matrix_2d(rotation), old_offset)
return self return self
def rotate_elements(self: P, rotation: float) -> P: def rotate_elements(self, rotation: float) -> Self:
""" """
Rotate each shape, ref, and port around its origin (offset) Rotate each shape, ref, and port around its origin (offset)
@ -451,7 +448,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
cast(Rotatable, entry).rotate(rotation) cast(Rotatable, entry).rotate(rotation)
return self return self
def mirror_element_centers(self: P, across_axis: int) -> P: def mirror_element_centers(self, across_axis: int) -> Self:
""" """
Mirror the offsets of all shapes, labels, and refs across an axis Mirror the offsets of all shapes, labels, and refs across an axis
@ -466,7 +463,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
cast(Positionable, entry).offset[across_axis - 1] *= -1 cast(Positionable, entry).offset[across_axis - 1] *= -1
return self return self
def mirror_elements(self: P, across_axis: int) -> P: def mirror_elements(self, across_axis: int) -> Self:
""" """
Mirror each shape, ref, and pattern across an axis, relative Mirror each shape, ref, and pattern across an axis, relative
to its offset to its offset
@ -482,7 +479,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
cast(Mirrorable, entry).mirror(across_axis) cast(Mirrorable, entry).mirror(across_axis)
return self return self
def mirror(self: P, across_axis: int) -> P: def mirror(self, across_axis: int) -> Self:
""" """
Mirror the Pattern across an axis Mirror the Pattern across an axis
@ -497,7 +494,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
self.mirror_element_centers(across_axis) self.mirror_element_centers(across_axis)
return self return self
def copy(self: P) -> P: def copy(self) -> Self:
""" """
Return a copy of the Pattern, deep-copying shapes and copying refs Return a copy of the Pattern, deep-copying shapes and copying refs
entries, but not deep-copying any referenced patterns. entries, but not deep-copying any referenced patterns.
@ -509,7 +506,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
""" """
return copy.copy(self) return copy.copy(self)
def deepcopy(self: P) -> P: def deepcopy(self) -> Self:
""" """
Convenience method for `copy.deepcopy(pattern)` Convenience method for `copy.deepcopy(pattern)`
@ -528,7 +525,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
and len(self.shapes) == 0 and len(self.shapes) == 0
and len(self.labels) == 0) and len(self.labels) == 0)
def ref(self: P, *args: Any, **kwargs: Any) -> P: def ref(self, *args: Any, **kwargs: Any) -> Self:
""" """
Convenience function which constructs a `Ref` object and adds it Convenience function which constructs a `Ref` object and adds it
to this pattern. to this pattern.
@ -543,7 +540,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
self.refs.append(Ref(*args, **kwargs)) self.refs.append(Ref(*args, **kwargs))
return self return self
def rect(self: P, *args: Any, **kwargs: Any) -> P: def rect(self, *args: Any, **kwargs: Any) -> Self:
""" """
Convenience function which calls `Polygon.rect` to construct a Convenience function which calls `Polygon.rect` to construct a
rectangle and adds it to this pattern. rectangle and adds it to this pattern.
@ -559,8 +556,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
return self return self
def flatten( def flatten(
self: P, self,
library: Mapping[str, P], library: Mapping[str, 'Pattern'],
flatten_ports: bool = False, # TODO document flatten_ports: bool = False, # TODO document
) -> 'Pattern': ) -> 'Pattern':
""" """
@ -573,7 +570,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
Returns: Returns:
self self
""" """
flattened: dict[str | None, P | None] = {} flattened: dict[str | None, 'Pattern' | None] = {}
# TODO both Library and Pattern have flatten()... pattern is in-place? # TODO both Library and Pattern have flatten()... pattern is in-place?
def flatten_single(name: str | None) -> None: def flatten_single(name: str | None) -> None:
@ -609,8 +606,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
return self return self
def visualize( def visualize(
self: P, self,
library: Mapping[str, P] | None = None, library: Mapping[str, 'Pattern'] | None = None,
offset: ArrayLike = (0., 0.), offset: ArrayLike = (0., 0.),
line_color: str = 'k', line_color: str = 'k',
fill_color: str = 'none', fill_color: str = 'none',

@ -1,4 +1,4 @@
from typing import Iterable, KeysView, ValuesView, overload, TypeVar from typing import Iterable, KeysView, ValuesView, overload, Self
import warnings import warnings
import traceback import traceback
import logging import logging
@ -17,11 +17,6 @@ from .error import PortError
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
P = TypeVar('P', bound='Port')
PL = TypeVar('PL', bound='PortList')
PL2 = TypeVar('PL2', bound='PortList')
class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable): class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
""" """
A point at which a `Device` can be snapped to another `Device`. A point at which a `Device` can be snapped to another `Device`.
@ -76,24 +71,24 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
def get_bounds(self): def get_bounds(self):
return numpy.vstack((self.offset, self.offset)) return numpy.vstack((self.offset, self.offset))
def set_ptype(self: P, ptype: str) -> P: def set_ptype(self, ptype: str) -> Self:
""" Chainable setter for `ptype` """ """ Chainable setter for `ptype` """
self.ptype = ptype self.ptype = ptype
return self return self
def mirror(self: P, axis: int) -> P: def mirror(self, axis: int) -> Self:
self.offset[1 - axis] *= -1 self.offset[1 - axis] *= -1
if self.rotation is not None: if self.rotation is not None:
self.rotation *= -1 self.rotation *= -1
self.rotation += axis * pi self.rotation += axis * pi
return self return self
def rotate(self: P, rotation: float) -> P: def rotate(self, rotation: float) -> Self:
if self.rotation is not None: if self.rotation is not None:
self.rotation += rotation self.rotation += rotation
return self return self
def set_rotation(self: P, rotation: float | None) -> P: def set_rotation(self, rotation: float | None) -> Self:
self.rotation = rotation self.rotation = rotation
return self return self
@ -148,10 +143,10 @@ class PortList(metaclass=ABCMeta):
# and because you can just grab .ports and use that instead # and because you can just grab .ports and use that instead
def rename_ports( def rename_ports(
self: PL, self,
mapping: dict[str, str | None], mapping: dict[str, str | None],
overwrite: bool = False, overwrite: bool = False,
) -> PL: ) -> Self:
""" """
Renames ports as specified by `mapping`. Renames ports as specified by `mapping`.
Ports can be explicitly deleted by mapping them to `None`. Ports can be explicitly deleted by mapping them to `None`.
@ -179,12 +174,12 @@ class PortList(metaclass=ABCMeta):
return self return self
def add_port_pair( def add_port_pair(
self: PL, self,
offset: ArrayLike = (0, 0), offset: ArrayLike = (0, 0),
rotation: float = 0.0, rotation: float = 0.0,
names: tuple[str, str] = ('A', 'B'), names: tuple[str, str] = ('A', 'B'),
ptype: str = 'unk', ptype: str = 'unk',
) -> PL: ) -> Self:
""" """
Add a pair of ports with opposing directions at the specified location. Add a pair of ports with opposing directions at the specified location.
@ -207,11 +202,11 @@ class PortList(metaclass=ABCMeta):
return self return self
def check_ports( def check_ports(
self: PL, self,
other_names: Iterable[str], other_names: Iterable[str],
map_in: dict[str, str] | None = None, map_in: dict[str, str] | None = None,
map_out: dict[str, str | None] | None = None, map_out: dict[str, str | None] | None = None,
) -> PL: ) -> Self:
""" """
Given the provided port mappings, check that: Given the provided port mappings, check that:
- All of the ports specified in the mappings exist - All of the ports specified in the mappings exist
@ -276,8 +271,8 @@ class PortList(metaclass=ABCMeta):
return self return self
def find_transform( def find_transform(
self: PL, self,
other: PL2, other: 'PortList',
map_in: dict[str, str], map_in: dict[str, str],
*, *,
mirrored: tuple[bool, bool] = (False, False), mirrored: tuple[bool, bool] = (False, False),

@ -4,7 +4,7 @@
""" """
#TODO more top-level documentation #TODO more top-level documentation
from typing import Sequence, Mapping, TYPE_CHECKING, Any, TypeVar, cast from typing import Sequence, Mapping, TYPE_CHECKING, Any, Self, cast
import copy import copy
import numpy import numpy
@ -24,9 +24,6 @@ if TYPE_CHECKING:
from . import Pattern, NamedPattern from . import Pattern, NamedPattern
R = TypeVar('R', bound='Ref')
class Ref( class Ref(
PositionableImpl, RotatableImpl, ScalableImpl, Mirrorable, PositionableImpl, RotatableImpl, ScalableImpl, Mirrorable,
PivotableImpl, Copyable, RepeatableImpl, AnnotatableImpl, PivotableImpl, Copyable, RepeatableImpl, AnnotatableImpl,
@ -164,13 +161,13 @@ class Ref(
return pattern return pattern
def rotate(self: R, rotation: float) -> R: def rotate(self, rotation: float) -> Self:
self.rotation += rotation self.rotation += rotation
if self.repetition is not None: if self.repetition is not None:
self.repetition.rotate(rotation) self.repetition.rotate(rotation)
return self return self
def mirror(self: R, axis: int) -> R: def mirror(self, axis: int) -> Self:
self.mirrored[axis] = not self.mirrored[axis] self.mirrored[axis] = not self.mirrored[axis]
self.rotation *= -1 self.rotation *= -1
if self.repetition is not None: if self.repetition is not None:

@ -1,4 +1,4 @@
from typing import Callable, TypeVar, TYPE_CHECKING from typing import Callable, Self, TYPE_CHECKING
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
import numpy import numpy
@ -26,9 +26,6 @@ normalized_shape_tuple = tuple[
DEFAULT_POLY_NUM_VERTICES = 24 DEFAULT_POLY_NUM_VERTICES = 24
T = TypeVar('T', bound='Shape')
class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Scalable, class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Scalable,
PivotableImpl, RepeatableImpl, AnnotatableImpl, metaclass=ABCMeta): PivotableImpl, RepeatableImpl, AnnotatableImpl, metaclass=ABCMeta):
""" """
@ -68,7 +65,7 @@ class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Sc
pass pass
@abstractmethod @abstractmethod
def normalized_form(self: T, norm_value: int) -> normalized_shape_tuple: def normalized_form(self, norm_value: int) -> normalized_shape_tuple:
""" """
Writes the shape in a standardized notation, with offset, scale, and rotation Writes the shape in a standardized notation, with offset, scale, and rotation
information separated out from the remaining values. information separated out from the remaining values.

@ -1,4 +1,4 @@
from typing import TypeVar from typing import Self
#from types import MappingProxyType #from types import MappingProxyType
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
@ -9,10 +9,6 @@ from ..error import MasqueError
_empty_slots = () # Workaround to get mypy to ignore intentionally empty slots for superclass _empty_slots = () # Workaround to get mypy to ignore intentionally empty slots for superclass
T = TypeVar('T', bound='Annotatable')
I = TypeVar('I', bound='AnnotatableImpl')
class Annotatable(metaclass=ABCMeta): class Annotatable(metaclass=ABCMeta):
""" """
Abstract class for all annotatable entities Abstract class for all annotatable entities

@ -1,11 +1,8 @@
from typing import TypeVar from typing import Self
from abc import ABCMeta from abc import ABCMeta
import copy import copy
T = TypeVar('T', bound='Copyable')
class Copyable(metaclass=ABCMeta): class Copyable(metaclass=ABCMeta):
""" """
Abstract class which adds .copy() and .deepcopy() Abstract class which adds .copy() and .deepcopy()
@ -15,7 +12,7 @@ class Copyable(metaclass=ABCMeta):
''' '''
---- Non-abstract methods ---- Non-abstract methods
''' '''
def copy(self: T) -> T: def copy(self) -> Self:
""" """
Return a shallow copy of the object. Return a shallow copy of the object.
@ -24,7 +21,7 @@ class Copyable(metaclass=ABCMeta):
""" """
return copy.copy(self) return copy.copy(self)
def deepcopy(self: T) -> T: def deepcopy(self) -> Self:
""" """
Return a deep copy of the object. Return a deep copy of the object.

@ -1,4 +1,4 @@
from typing import TypeVar from typing import Self
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from ..utils import layer_t from ..utils import layer_t
@ -7,10 +7,6 @@ from ..utils import layer_t
_empty_slots = () # Workaround to get mypy to ignore intentionally empty slots for superclass _empty_slots = () # Workaround to get mypy to ignore intentionally empty slots for superclass
T = TypeVar('T', bound='Layerable')
I = TypeVar('I', bound='LayerableImpl')
class Layerable(metaclass=ABCMeta): class Layerable(metaclass=ABCMeta):
""" """
Abstract class for all layerable entities Abstract class for all layerable entities
@ -36,7 +32,7 @@ class Layerable(metaclass=ABCMeta):
---- Methods ---- Methods
''' '''
@abstractmethod @abstractmethod
def set_layer(self: T, layer: layer_t) -> T: def set_layer(self, layer: layer_t) -> Self:
""" """
Set the layer Set the layer
@ -72,6 +68,6 @@ class LayerableImpl(Layerable, metaclass=ABCMeta):
''' '''
---- Non-abstract methods ---- Non-abstract methods
''' '''
def set_layer(self: I, layer: layer_t) -> I: def set_layer(self, layer: layer_t) -> Self:
self.layer = layer self.layer = layer
return self return self

@ -1,11 +1,7 @@
from typing import TypeVar from typing import Self
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
T = TypeVar('T', bound='Mirrorable')
#I = TypeVar('I', bound='MirrorableImpl')
class Mirrorable(metaclass=ABCMeta): class Mirrorable(metaclass=ABCMeta):
""" """
Abstract class for all mirrorable entities Abstract class for all mirrorable entities
@ -16,7 +12,7 @@ class Mirrorable(metaclass=ABCMeta):
---- Abstract methods ---- Abstract methods
''' '''
@abstractmethod @abstractmethod
def mirror(self: T, axis: int) -> T: def mirror(self, axis: int) -> Self:
""" """
Mirror the entity across an axis. Mirror the entity across an axis.
@ -28,7 +24,7 @@ class Mirrorable(metaclass=ABCMeta):
""" """
pass pass
def mirror2d(self: T, axes: tuple[bool, bool]) -> T: def mirror2d(self, axes: tuple[bool, bool]) -> Self:
""" """
Optionally mirror the entity across both axes Optionally mirror the entity across both axes

@ -1,6 +1,6 @@
# TODO top-level comment about how traits should set __slots__ = (), and how to use AutoSlots # TODO top-level comment about how traits should set __slots__ = (), and how to use AutoSlots
from typing import TypeVar, Any from typing import Self, Any
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
import numpy import numpy
@ -12,10 +12,6 @@ from ..error import MasqueError
_empty_slots = () # Workaround to get mypy to ignore intentionally empty slots for superclass _empty_slots = () # Workaround to get mypy to ignore intentionally empty slots for superclass
T = TypeVar('T', bound='Positionable')
I = TypeVar('I', bound='PositionableImpl')
class Positionable(metaclass=ABCMeta): class Positionable(metaclass=ABCMeta):
""" """
Abstract class for all positionable entities Abstract class for all positionable entities
@ -39,7 +35,7 @@ class Positionable(metaclass=ABCMeta):
pass pass
@abstractmethod @abstractmethod
def set_offset(self: T, offset: ArrayLike) -> T: def set_offset(self, offset: ArrayLike) -> Self:
""" """
Set the offset Set the offset
@ -52,7 +48,7 @@ class Positionable(metaclass=ABCMeta):
pass pass
@abstractmethod @abstractmethod
def translate(self: T, offset: ArrayLike) -> T: def translate(self, offset: ArrayLike) -> Self:
""" """
Translate the entity by the given offset Translate the entity by the given offset
@ -116,10 +112,10 @@ class PositionableImpl(Positionable, metaclass=ABCMeta):
''' '''
---- Methods ---- Methods
''' '''
def set_offset(self: I, offset: ArrayLike) -> I: def set_offset(self, offset: ArrayLike) -> Self:
self.offset = offset self.offset = offset
return self return self
def translate(self: I, offset: ArrayLike) -> I: def translate(self, offset: ArrayLike) -> Self:
self._offset += offset # type: ignore # NDArray += ArrayLike should be fine?? self._offset += offset # type: ignore # NDArray += ArrayLike should be fine??
return self return self

@ -1,4 +1,4 @@
from typing import TypeVar, TYPE_CHECKING from typing import Self, TYPE_CHECKING
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from ..error import MasqueError from ..error import MasqueError
@ -11,10 +11,6 @@ if TYPE_CHECKING:
from ..repetition import Repetition from ..repetition import Repetition
T = TypeVar('T', bound='Repeatable')
I = TypeVar('I', bound='RepeatableImpl')
class Repeatable(metaclass=ABCMeta): class Repeatable(metaclass=ABCMeta):
""" """
Abstract class for all repeatable entities Abstract class for all repeatable entities
@ -41,7 +37,7 @@ class Repeatable(metaclass=ABCMeta):
---- Methods ---- Methods
''' '''
@abstractmethod @abstractmethod
def set_repetition(self: T, repetition: 'Repetition' | None) -> T: def set_repetition(self, repetition: 'Repetition' | None) -> Self:
""" """
Set the repetition Set the repetition
@ -80,6 +76,6 @@ class RepeatableImpl(Repeatable, metaclass=ABCMeta):
''' '''
---- Non-abstract methods ---- Non-abstract methods
''' '''
def set_repetition(self: I, repetition: 'Repetition' | None) -> I: def set_repetition(self, repetition: 'Repetition' | None) -> Self:
self.repetition = repetition self.repetition = repetition
return self return self

@ -1,4 +1,4 @@
from typing import TypeVar, cast, Any from typing import Self, cast, Any
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
import numpy import numpy
@ -13,12 +13,6 @@ from ..utils import rotation_matrix_2d
_empty_slots = () # Workaround to get mypy to ignore intentionally empty slots for superclass _empty_slots = () # Workaround to get mypy to ignore intentionally empty slots for superclass
T = TypeVar('T', bound='Rotatable')
I = TypeVar('I', bound='RotatableImpl')
P = TypeVar('P', bound='Pivotable')
J = TypeVar('J', bound='PivotableImpl')
class Rotatable(metaclass=ABCMeta): class Rotatable(metaclass=ABCMeta):
""" """
Abstract class for all rotatable entities Abstract class for all rotatable entities
@ -29,7 +23,7 @@ class Rotatable(metaclass=ABCMeta):
---- Abstract methods ---- Abstract methods
''' '''
@abstractmethod @abstractmethod
def rotate(self: T, val: float) -> T: def rotate(self, val: float) -> Self:
""" """
Rotate the shape around its origin (0, 0), ignoring its offset. Rotate the shape around its origin (0, 0), ignoring its offset.
@ -68,11 +62,11 @@ class RotatableImpl(Rotatable, metaclass=ABCMeta):
''' '''
---- Methods ---- Methods
''' '''
def rotate(self: I, rotation: float) -> I: def rotate(self, rotation: float) -> Self:
self.rotation += rotation self.rotation += rotation
return self return self
def set_rotation(self: I, rotation: float) -> I: def set_rotation(self, rotation: float) -> Self:
""" """
Set the rotation to a value Set the rotation to a value
@ -94,7 +88,7 @@ class Pivotable(metaclass=ABCMeta):
__slots__ = () __slots__ = ()
@abstractmethod @abstractmethod
def rotate_around(self: P, pivot: ArrayLike, rotation: float) -> P: def rotate_around(self, pivot: ArrayLike, rotation: float) -> Self:
""" """
Rotate the object around a point. Rotate the object around a point.
@ -117,7 +111,7 @@ class PivotableImpl(Pivotable, metaclass=ABCMeta):
offset: Any # TODO see if we can get around defining `offset` in PivotableImpl offset: Any # TODO see if we can get around defining `offset` in PivotableImpl
""" `[x_offset, y_offset]` """ """ `[x_offset, y_offset]` """
def rotate_around(self: J, pivot: ArrayLike, rotation: float) -> J: def rotate_around(self, pivot: ArrayLike, rotation: float) -> Self:
pivot = numpy.array(pivot, dtype=float) pivot = numpy.array(pivot, dtype=float)
cast(Positionable, self).translate(-pivot) cast(Positionable, self).translate(-pivot)
cast(Rotatable, self).rotate(rotation) cast(Rotatable, self).rotate(rotation)

@ -1,4 +1,4 @@
from typing import TypeVar from typing import Self
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from ..error import MasqueError from ..error import MasqueError
@ -8,10 +8,6 @@ from ..utils import is_scalar
_empty_slots = () # Workaround to get mypy to ignore intentionally empty slots for superclass _empty_slots = () # Workaround to get mypy to ignore intentionally empty slots for superclass
T = TypeVar('T', bound='Scalable')
I = TypeVar('I', bound='ScalableImpl')
class Scalable(metaclass=ABCMeta): class Scalable(metaclass=ABCMeta):
""" """
Abstract class for all scalable entities Abstract class for all scalable entities
@ -22,7 +18,7 @@ class Scalable(metaclass=ABCMeta):
---- Abstract methods ---- Abstract methods
''' '''
@abstractmethod @abstractmethod
def scale_by(self: T, c: float) -> T: def scale_by(self, c: float) -> Self:
""" """
Scale the entity by a factor Scale the entity by a factor
@ -62,11 +58,11 @@ class ScalableImpl(Scalable, metaclass=ABCMeta):
''' '''
---- Methods ---- Methods
''' '''
def scale_by(self: I, c: float) -> I: def scale_by(self, c: float) -> Self:
self.scale *= c self.scale *= c
return self return self
def set_scale(self: I, scale: float) -> I: def set_scale(self, scale: float) -> Self:
""" """
Set the sclae to a value Set the sclae to a value

Loading…
Cancel
Save