From a710494dd80019d6f438164ec98e44fb334b6402 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Thu, 23 Feb 2023 13:37:34 -0800 Subject: [PATCH] use Self type --- masque/abstract.py | 25 ++++++++--------- masque/builder/builder.py | 40 +++++++++++++-------------- masque/builder/flatbuilder.py | 37 ++++++++++++------------- masque/label.py | 11 +++----- masque/library.py | 47 +++++++++++++++----------------- masque/pattern.py | 51 +++++++++++++++++------------------ masque/ports.py | 31 +++++++++------------ masque/ref.py | 9 +++---- masque/shapes/shape.py | 7 ++--- masque/traits/annotatable.py | 6 +---- masque/traits/copyable.py | 9 +++---- masque/traits/layerable.py | 10 +++---- masque/traits/mirrorable.py | 10 +++---- masque/traits/positionable.py | 14 ++++------ masque/traits/repeatable.py | 10 +++---- masque/traits/rotatable.py | 18 +++++-------- masque/traits/scalable.py | 12 +++------ 17 files changed, 142 insertions(+), 205 deletions(-) diff --git a/masque/abstract.py b/masque/abstract.py index d43a3df..3301c66 100644 --- a/masque/abstract.py +++ b/masque/abstract.py @@ -1,4 +1,4 @@ -from typing import TypeVar +from typing import Self import copy import logging @@ -17,9 +17,6 @@ from .utils import rotation_matrix_2d, normalize_mirror logger = logging.getLogger(__name__) -AA = TypeVar('AA', bound='Abstract') - - class Abstract(PortList): __slots__ = ('name', '_ports') @@ -71,7 +68,7 @@ class Abstract(PortList): 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. @@ -85,7 +82,7 @@ class Abstract(PortList): port.translate(offset) return self - def scale_by(self: AA, c: float) -> AA: + def scale_by(self, c: float) -> Self: """ Scale this Abstract by the given value (all port offsets are scaled) @@ -100,7 +97,7 @@ class Abstract(PortList): port.offset *= c 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. @@ -118,7 +115,7 @@ class Abstract(PortList): self.translate_ports(+pivot) 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) @@ -132,7 +129,7 @@ class Abstract(PortList): port.offset = rotation_matrix_2d(rotation) @ port.offset 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) @@ -146,7 +143,7 @@ class Abstract(PortList): port.rotate(rotation) 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 @@ -161,7 +158,7 @@ class Abstract(PortList): port.offset[across_axis - 1] *= -1 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 offset @@ -177,7 +174,7 @@ class Abstract(PortList): port.mirror(across_axis) return self - def mirror(self: AA, across_axis: int) -> AA: + def mirror(self, across_axis: int) -> Self: """ Mirror the Pattern across an axis @@ -192,7 +189,7 @@ class Abstract(PortList): self.mirror_port_offsets(across_axis) 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`. 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) 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`. This changes the port locations to where they would be in the Ref's target (from the parent). diff --git a/masque/builder/builder.py b/masque/builder/builder.py index 9d7b978..3d05e76 100644 --- a/masque/builder/builder.py +++ b/masque/builder/builder.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Sequence, MutableMapping, Mapping +from typing import Self, Sequence, MutableMapping, Mapping import copy import logging @@ -20,10 +20,6 @@ from .utils import ell logger = logging.getLogger(__name__) -BB = TypeVar('BB', bound='Builder') -PP = TypeVar('PP', bound='Pather') - - class Builder(PortList): """ TODO DOCUMENT Builder @@ -239,7 +235,7 @@ class Builder(PortList): return new def plug( - self: BB, + self, other: Abstract | str | NamedPattern, map_in: dict[str, str], map_out: dict[str, str | None] | None = None, @@ -247,7 +243,7 @@ class Builder(PortList): mirrored: tuple[bool, bool] = (False, False), inherit_name: bool = True, set_rotation: bool | None = None, - ) -> BB: + ) -> Self: """ Instantiate a device `library[name]` into the current device, connecting the ports specified by `map_in` and renaming the unconnected @@ -340,7 +336,7 @@ class Builder(PortList): return self def place( - self: BB, + self, other: Abstract | str | NamedPattern, *, offset: ArrayLike = (0, 0), @@ -349,7 +345,7 @@ class Builder(PortList): mirrored: tuple[bool, bool] = (False, False), port_map: dict[str, str | None] | None = None, skip_port_check: bool = False, - ) -> BB: + ) -> Self: """ Instantiate the device `other` into the current device, adding its ports to those of the current device (but not connecting any ports). @@ -422,7 +418,7 @@ class Builder(PortList): self.pattern.refs.append(sp) return self - def translate(self: BB, offset: ArrayLike) -> BB: + def translate(self, offset: ArrayLike) -> Self: """ Translate the pattern and all ports. @@ -437,7 +433,7 @@ class Builder(PortList): port.translate(offset) 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. @@ -453,7 +449,7 @@ class Builder(PortList): port.rotate_around(pivot, angle) 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. @@ -468,7 +464,7 @@ class Builder(PortList): p.mirror(axis) return self - def set_dead(self: BB) -> BB: + def set_dead(self) -> Self: """ Disallows further changes through `plug()` or `place()`. This is meant for debugging: @@ -673,10 +669,10 @@ class Pather(Builder): return s def retool( - self: PP, + self, tool: Tool, keys: str | Sequence[str | None] | None = None, - ) -> PP: + ) -> Self: if keys is None or isinstance(keys, str): self.tools[keys] = tool else: @@ -685,7 +681,7 @@ class Pather(Builder): return self def path( - self: PP, + self, portspec: str, ccw: SupportsBool | None, length: float, @@ -693,7 +689,7 @@ class Pather(Builder): tool_port_names: Sequence[str] = ('A', 'B'), base_name: str = '_path', **kwargs, - ) -> PP: + ) -> Self: if self._dead: logger.error('Skipping path() since device is dead') return self @@ -706,7 +702,7 @@ class Pather(Builder): return self.plug(Abstract(name, pat.ports), {portspec: tool_port_names[0]}) def path_to( - self: PP, + self, portspec: str, ccw: SupportsBool | None, position: float, @@ -714,7 +710,7 @@ class Pather(Builder): tool_port_names: Sequence[str] = ('A', 'B'), base_name: str = '_pathto', **kwargs, - ) -> PP: + ) -> Self: if self._dead: logger.error('Skipping path_to() since device is dead') 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) def mpath( - self: PP, + self, portspec: str | Sequence[str], ccw: SupportsBool | None, *, @@ -750,7 +746,7 @@ class Pather(Builder): force_container: bool = False, base_name: str = '_mpath', **kwargs, - ) -> PP: + ) -> Self: if self._dead: logger.error('Skipping mpath() since device is dead') return self @@ -790,7 +786,7 @@ class Pather(Builder): # 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. diff --git a/masque/builder/flatbuilder.py b/masque/builder/flatbuilder.py index 6263689..6b9d93f 100644 --- a/masque/builder/flatbuilder.py +++ b/masque/builder/flatbuilder.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Sequence, MutableMapping, Mapping +from typing import Self, Sequence, MutableMapping, Mapping import copy import logging @@ -17,9 +17,6 @@ from .utils import ell logger = logging.getLogger(__name__) -BB = TypeVar('BB', bound='FlatBuilder') - - class FlatBuilder(PortList): """ TODO DOCUMENT FlatBuilder @@ -173,7 +170,7 @@ class FlatBuilder(PortList): return new def plug( - self: BB, + self, other: Pattern, map_in: dict[str, str], map_out: dict[str, str | None] | None = None, @@ -181,7 +178,7 @@ class FlatBuilder(PortList): mirrored: tuple[bool, bool] = (False, False), inherit_name: bool = True, set_rotation: bool | None = None, - ) -> BB: + ) -> Self: """ Instantiate another pattern into the current device, connecting the ports specified by `map_in` and renaming the unconnected @@ -269,7 +266,7 @@ class FlatBuilder(PortList): return self def place( - self: BB, + self, other: Pattern, *, offset: ArrayLike = (0, 0), @@ -278,7 +275,7 @@ class FlatBuilder(PortList): mirrored: tuple[bool, bool] = (False, False), port_map: dict[str, str | None] | None = None, skip_port_check: bool = False, - ) -> BB: + ) -> Self: """ Instantiate the device `other` into the current device, adding its ports to those of the current device (but not connecting any ports). @@ -348,7 +345,7 @@ class FlatBuilder(PortList): self.pattern.append(other_copy) return self - def translate(self: BB, offset: ArrayLike) -> BB: + def translate(self, offset: ArrayLike) -> Self: """ Translate the pattern and all ports. @@ -363,7 +360,7 @@ class FlatBuilder(PortList): port.translate(offset) 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. @@ -379,7 +376,7 @@ class FlatBuilder(PortList): port.rotate_around(pivot, angle) 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. @@ -394,7 +391,7 @@ class FlatBuilder(PortList): p.mirror(axis) return self - def set_dead(self: BB) -> BB: + def set_dead(self) -> Self: """ Disallows further changes through `plug()` or `place()`. This is meant for debugging: @@ -417,10 +414,10 @@ class FlatBuilder(PortList): return s def retool( - self: BB, + self, tool: Tool, keys: str | Sequence[str | None] | None = None, - ) -> BB: + ) -> Self: if keys is None or isinstance(keys, str): self.tools[keys] = tool else: @@ -429,7 +426,7 @@ class FlatBuilder(PortList): return self def path( - self: BB, + self, portspec: str, ccw: SupportsBool | None, length: float, @@ -437,7 +434,7 @@ class FlatBuilder(PortList): tool_port_names: Sequence[str] = ('A', 'B'), base_name: str = '_path', **kwargs, - ) -> BB: + ) -> Self: if self._dead: logger.error('Skipping path() since device is dead') return self @@ -448,7 +445,7 @@ class FlatBuilder(PortList): return self.plug(pat, {portspec: tool_port_names[0]}) def path_to( - self: BB, + self, portspec: str, ccw: SupportsBool | None, position: float, @@ -456,7 +453,7 @@ class FlatBuilder(PortList): tool_port_names: Sequence[str] = ('A', 'B'), base_name: str = '_pathto', **kwargs, - ) -> BB: + ) -> Self: if self._dead: logger.error('Skipping path_to() since device is dead') 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) def mpath( - self: BB, + self, portspec: str | Sequence[str], ccw: SupportsBool | None, *, @@ -492,7 +489,7 @@ class FlatBuilder(PortList): force_container: bool = False, base_name: str = '_mpath', **kwargs, - ) -> BB: + ) -> Self: if self._dead: logger.error('Skipping mpath() since device is dead') return self diff --git a/masque/label.py b/masque/label.py index fe34f9b..40b6aba 100644 --- a/masque/label.py +++ b/masque/label.py @@ -1,4 +1,4 @@ -from typing import TypeVar +from typing import Self import copy import numpy @@ -10,9 +10,6 @@ from .traits import PositionableImpl, LayerableImpl, Copyable, Pivotable, Repeat from .traits import AnnotatableImpl -L = TypeVar('L', bound='Label') - - class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl, Pivotable, Copyable, metaclass=AutoSlots): """ @@ -53,7 +50,7 @@ class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl, self.repetition = repetition self.annotations = annotations if annotations is not None else {} - def __copy__(self: L) -> L: + def __copy__(self) -> Self: return type(self)( string=self.string, offset=self.offset.copy(), @@ -61,13 +58,13 @@ class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl, 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 new = copy.copy(self) new._offset = self._offset.copy() 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. diff --git a/masque/library.py b/masque/library.py index 1165140..781983a 100644 --- a/masque/library.py +++ b/masque/library.py @@ -5,7 +5,7 @@ Library classes for managing unique name->pattern mappings and # TODO documentn all library classes # 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 import logging import base64 @@ -33,9 +33,6 @@ logger = logging.getLogger(__name__) 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: @@ -163,10 +160,10 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta): return new def polygonize( - self: L, + self, num_vertices: int | None = None, max_arclen: float | None = None, - ) -> L: + ) -> Self: """ Calls `.polygonize(...)` on each pattern in this library. Arguments are passed on to `shape.to_polygons(...)`. @@ -186,10 +183,10 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta): return self def manhattanize( - self: L, + self, grid_x: ArrayLike, grid_y: ArrayLike, - ) -> L: + ) -> Self: """ 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 def dfs( - self: L, + self, pattern: 'Pattern', visit_before: 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,), transform: ArrayLike | bool | None = False, memo: dict | None = None, - ) -> L: + ) -> Self: """ Convenience function. 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 def rename( - self: ML, + self, old_name: str, new_name: str, move_references: bool = False, - ) -> ML: + ) -> Self: """ Rename a pattern. @@ -476,7 +473,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta) self.move_references(old_name, new_name) 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`. @@ -601,12 +598,12 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta) return rename_map.get(name, name) def dedup( - self: ML, + self, norm_value: int = int(1e6), exclude_types: tuple[Type] = (Polygon,), label2name: Callable[[tuple], str] | None = None, threshold: int = 2, - ) -> ML: + ) -> Self: """ Iterates through all `Pattern`s. Within each `Pattern`, it iterates 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 def wrap_repeated_shapes( - self: ML, + self, name_func: Callable[['Pattern', Shape | Label], str] | None = None, - ) -> ML: + ) -> Self: """ Wraps all shapes and labels with a non-`None` `repetition` attribute into a `Ref`/`Pattern` combination, and applies the `repetition` @@ -755,9 +752,9 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta) return self def subtree( - self: ML, + self, tops: str | Sequence[str], - ) -> ML: + ) -> Self: """ Return a new `Library`, containing only the specified patterns and the patterns they reference (recursively). @@ -797,10 +794,10 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta) return trimmed def delete( - self: ML, + self, key: str, delete_refs: bool = True, - ) -> ML: + ) -> Self: # TODO doc delete() del self[key] if delete_refs: @@ -961,11 +958,11 @@ class LazyLibrary(MutableLibrary): return '' def rename( - self: LL, + self, old_name: str, new_name: str, move_references: bool = False, - ) -> LL: + ) -> Self: """ Rename a pattern. @@ -989,7 +986,7 @@ class LazyLibrary(MutableLibrary): 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`. @@ -1007,7 +1004,7 @@ class LazyLibrary(MutableLibrary): ref.target = new_target return self - def precache(self: LL) -> LL: + def precache(self) -> Self: """ Force all patterns into the cache diff --git a/masque/pattern.py b/masque/pattern.py index a126bad..e69a66a 100644 --- a/masque/pattern.py +++ b/masque/pattern.py @@ -2,7 +2,7 @@ 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 from itertools import chain @@ -20,9 +20,6 @@ from .traits import AnnotatableImpl, Scalable, Mirrorable, Rotatable, Positionab from .ports import Port, PortList -P = TypeVar('P', bound='Pattern') - - class Pattern(PortList, AnnotatableImpl, Mirrorable): """ 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 - 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, labels, and supbatterns. @@ -212,10 +209,10 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): return pat def polygonize( - self: P, + self, num_points: int | 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. Arguments are passed directly to `shape.to_polygons(...)`. @@ -237,10 +234,10 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): return self def manhattanize( - self: P, + self, grid_x: ArrayLike, grid_y: ArrayLike, - ) -> P: + ) -> Self: """ Calls `.polygonize()` on the pattern, then calls `.manhattanize()` on all the resulting shapes, replacing them with the returned Manhattan polygons. @@ -345,7 +342,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): assert bounds is not None 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. @@ -359,7 +356,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): cast(Positionable, entry).translate(offset) 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. @@ -373,7 +370,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): cast(Scalable, entry).scale_by(c) return self - def scale_by(self: P, c: float) -> P: + def scale_by(self, c: float) -> Self: """ Scale this Pattern by the given value (all shapes and refs and their offsets are scaled, @@ -404,7 +401,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): port.offset *= c 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. @@ -422,7 +419,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): self.translate_elements(+pivot) 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) @@ -437,7 +434,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): cast(Positionable, entry).offset = numpy.dot(rotation_matrix_2d(rotation), old_offset) 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) @@ -451,7 +448,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): cast(Rotatable, entry).rotate(rotation) 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 @@ -466,7 +463,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): cast(Positionable, entry).offset[across_axis - 1] *= -1 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 to its offset @@ -482,7 +479,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): cast(Mirrorable, entry).mirror(across_axis) return self - def mirror(self: P, across_axis: int) -> P: + def mirror(self, across_axis: int) -> Self: """ Mirror the Pattern across an axis @@ -497,7 +494,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): self.mirror_element_centers(across_axis) return self - def copy(self: P) -> P: + def copy(self) -> Self: """ Return a copy of the Pattern, deep-copying shapes and copying refs entries, but not deep-copying any referenced patterns. @@ -509,7 +506,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): """ return copy.copy(self) - def deepcopy(self: P) -> P: + def deepcopy(self) -> Self: """ Convenience method for `copy.deepcopy(pattern)` @@ -528,7 +525,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): and len(self.shapes) == 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 to this pattern. @@ -543,7 +540,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): self.refs.append(Ref(*args, **kwargs)) 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 rectangle and adds it to this pattern. @@ -559,8 +556,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): return self def flatten( - self: P, - library: Mapping[str, P], + self, + library: Mapping[str, 'Pattern'], flatten_ports: bool = False, # TODO document ) -> 'Pattern': """ @@ -573,7 +570,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): Returns: self """ - flattened: dict[str | None, P | None] = {} + flattened: dict[str | None, 'Pattern' | None] = {} # TODO both Library and Pattern have flatten()... pattern is in-place? def flatten_single(name: str | None) -> None: @@ -609,8 +606,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable): return self def visualize( - self: P, - library: Mapping[str, P] | None = None, + self, + library: Mapping[str, 'Pattern'] | None = None, offset: ArrayLike = (0., 0.), line_color: str = 'k', fill_color: str = 'none', diff --git a/masque/ports.py b/masque/ports.py index 85ca750..36c47c4 100644 --- a/masque/ports.py +++ b/masque/ports.py @@ -1,4 +1,4 @@ -from typing import Iterable, KeysView, ValuesView, overload, TypeVar +from typing import Iterable, KeysView, ValuesView, overload, Self import warnings import traceback import logging @@ -17,11 +17,6 @@ from .error import PortError 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): """ 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): 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` """ self.ptype = ptype return self - def mirror(self: P, axis: int) -> P: + def mirror(self, axis: int) -> Self: self.offset[1 - axis] *= -1 if self.rotation is not None: self.rotation *= -1 self.rotation += axis * pi return self - def rotate(self: P, rotation: float) -> P: + def rotate(self, rotation: float) -> Self: if self.rotation is not None: self.rotation += rotation return self - def set_rotation(self: P, rotation: float | None) -> P: + def set_rotation(self, rotation: float | None) -> Self: self.rotation = rotation return self @@ -148,10 +143,10 @@ class PortList(metaclass=ABCMeta): # and because you can just grab .ports and use that instead def rename_ports( - self: PL, + self, mapping: dict[str, str | None], overwrite: bool = False, - ) -> PL: + ) -> Self: """ Renames ports as specified by `mapping`. Ports can be explicitly deleted by mapping them to `None`. @@ -179,12 +174,12 @@ class PortList(metaclass=ABCMeta): return self def add_port_pair( - self: PL, + self, offset: ArrayLike = (0, 0), rotation: float = 0.0, names: tuple[str, str] = ('A', 'B'), ptype: str = 'unk', - ) -> PL: + ) -> Self: """ Add a pair of ports with opposing directions at the specified location. @@ -207,11 +202,11 @@ class PortList(metaclass=ABCMeta): return self def check_ports( - self: PL, + self, other_names: Iterable[str], map_in: dict[str, str] | None = None, map_out: dict[str, str | None] | None = None, - ) -> PL: + ) -> Self: """ Given the provided port mappings, check that: - All of the ports specified in the mappings exist @@ -276,8 +271,8 @@ class PortList(metaclass=ABCMeta): return self def find_transform( - self: PL, - other: PL2, + self, + other: 'PortList', map_in: dict[str, str], *, mirrored: tuple[bool, bool] = (False, False), diff --git a/masque/ref.py b/masque/ref.py index ddd2245..17fddb1 100644 --- a/masque/ref.py +++ b/masque/ref.py @@ -4,7 +4,7 @@ """ #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 numpy @@ -24,9 +24,6 @@ if TYPE_CHECKING: from . import Pattern, NamedPattern -R = TypeVar('R', bound='Ref') - - class Ref( PositionableImpl, RotatableImpl, ScalableImpl, Mirrorable, PivotableImpl, Copyable, RepeatableImpl, AnnotatableImpl, @@ -164,13 +161,13 @@ class Ref( return pattern - def rotate(self: R, rotation: float) -> R: + def rotate(self, rotation: float) -> Self: self.rotation += rotation if self.repetition is not None: self.repetition.rotate(rotation) return self - def mirror(self: R, axis: int) -> R: + def mirror(self, axis: int) -> Self: self.mirrored[axis] = not self.mirrored[axis] self.rotation *= -1 if self.repetition is not None: diff --git a/masque/shapes/shape.py b/masque/shapes/shape.py index 201fc2b..362be57 100644 --- a/masque/shapes/shape.py +++ b/masque/shapes/shape.py @@ -1,4 +1,4 @@ -from typing import Callable, TypeVar, TYPE_CHECKING +from typing import Callable, Self, TYPE_CHECKING from abc import ABCMeta, abstractmethod import numpy @@ -26,9 +26,6 @@ normalized_shape_tuple = tuple[ DEFAULT_POLY_NUM_VERTICES = 24 -T = TypeVar('T', bound='Shape') - - class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Scalable, PivotableImpl, RepeatableImpl, AnnotatableImpl, metaclass=ABCMeta): """ @@ -68,7 +65,7 @@ class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Sc pass @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 information separated out from the remaining values. diff --git a/masque/traits/annotatable.py b/masque/traits/annotatable.py index b10b042..e36ceee 100644 --- a/masque/traits/annotatable.py +++ b/masque/traits/annotatable.py @@ -1,4 +1,4 @@ -from typing import TypeVar +from typing import Self #from types import MappingProxyType 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 -T = TypeVar('T', bound='Annotatable') -I = TypeVar('I', bound='AnnotatableImpl') - - class Annotatable(metaclass=ABCMeta): """ Abstract class for all annotatable entities diff --git a/masque/traits/copyable.py b/masque/traits/copyable.py index aa84356..76cd26d 100644 --- a/masque/traits/copyable.py +++ b/masque/traits/copyable.py @@ -1,11 +1,8 @@ -from typing import TypeVar +from typing import Self from abc import ABCMeta import copy -T = TypeVar('T', bound='Copyable') - - class Copyable(metaclass=ABCMeta): """ Abstract class which adds .copy() and .deepcopy() @@ -15,7 +12,7 @@ class Copyable(metaclass=ABCMeta): ''' ---- Non-abstract methods ''' - def copy(self: T) -> T: + def copy(self) -> Self: """ Return a shallow copy of the object. @@ -24,7 +21,7 @@ class Copyable(metaclass=ABCMeta): """ return copy.copy(self) - def deepcopy(self: T) -> T: + def deepcopy(self) -> Self: """ Return a deep copy of the object. diff --git a/masque/traits/layerable.py b/masque/traits/layerable.py index eae2de4..29d9653 100644 --- a/masque/traits/layerable.py +++ b/masque/traits/layerable.py @@ -1,4 +1,4 @@ -from typing import TypeVar +from typing import Self from abc import ABCMeta, abstractmethod 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 -T = TypeVar('T', bound='Layerable') -I = TypeVar('I', bound='LayerableImpl') - - class Layerable(metaclass=ABCMeta): """ Abstract class for all layerable entities @@ -36,7 +32,7 @@ class Layerable(metaclass=ABCMeta): ---- Methods ''' @abstractmethod - def set_layer(self: T, layer: layer_t) -> T: + def set_layer(self, layer: layer_t) -> Self: """ Set the layer @@ -72,6 +68,6 @@ class LayerableImpl(Layerable, metaclass=ABCMeta): ''' ---- Non-abstract methods ''' - def set_layer(self: I, layer: layer_t) -> I: + def set_layer(self, layer: layer_t) -> Self: self.layer = layer return self diff --git a/masque/traits/mirrorable.py b/masque/traits/mirrorable.py index bc82b83..5bbbad4 100644 --- a/masque/traits/mirrorable.py +++ b/masque/traits/mirrorable.py @@ -1,11 +1,7 @@ -from typing import TypeVar +from typing import Self from abc import ABCMeta, abstractmethod -T = TypeVar('T', bound='Mirrorable') -#I = TypeVar('I', bound='MirrorableImpl') - - class Mirrorable(metaclass=ABCMeta): """ Abstract class for all mirrorable entities @@ -16,7 +12,7 @@ class Mirrorable(metaclass=ABCMeta): ---- Abstract methods ''' @abstractmethod - def mirror(self: T, axis: int) -> T: + def mirror(self, axis: int) -> Self: """ Mirror the entity across an axis. @@ -28,7 +24,7 @@ class Mirrorable(metaclass=ABCMeta): """ 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 diff --git a/masque/traits/positionable.py b/masque/traits/positionable.py index 1f4ba6e..ba25cc0 100644 --- a/masque/traits/positionable.py +++ b/masque/traits/positionable.py @@ -1,6 +1,6 @@ # 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 import numpy @@ -12,10 +12,6 @@ from ..error import MasqueError _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): """ Abstract class for all positionable entities @@ -39,7 +35,7 @@ class Positionable(metaclass=ABCMeta): pass @abstractmethod - def set_offset(self: T, offset: ArrayLike) -> T: + def set_offset(self, offset: ArrayLike) -> Self: """ Set the offset @@ -52,7 +48,7 @@ class Positionable(metaclass=ABCMeta): pass @abstractmethod - def translate(self: T, offset: ArrayLike) -> T: + def translate(self, offset: ArrayLike) -> Self: """ Translate the entity by the given offset @@ -116,10 +112,10 @@ class PositionableImpl(Positionable, metaclass=ABCMeta): ''' ---- Methods ''' - def set_offset(self: I, offset: ArrayLike) -> I: + def set_offset(self, offset: ArrayLike) -> Self: self.offset = offset 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?? return self diff --git a/masque/traits/repeatable.py b/masque/traits/repeatable.py index f9f6438..7be1559 100644 --- a/masque/traits/repeatable.py +++ b/masque/traits/repeatable.py @@ -1,4 +1,4 @@ -from typing import TypeVar, TYPE_CHECKING +from typing import Self, TYPE_CHECKING from abc import ABCMeta, abstractmethod from ..error import MasqueError @@ -11,10 +11,6 @@ if TYPE_CHECKING: from ..repetition import Repetition -T = TypeVar('T', bound='Repeatable') -I = TypeVar('I', bound='RepeatableImpl') - - class Repeatable(metaclass=ABCMeta): """ Abstract class for all repeatable entities @@ -41,7 +37,7 @@ class Repeatable(metaclass=ABCMeta): ---- Methods ''' @abstractmethod - def set_repetition(self: T, repetition: 'Repetition' | None) -> T: + def set_repetition(self, repetition: 'Repetition' | None) -> Self: """ Set the repetition @@ -80,6 +76,6 @@ class RepeatableImpl(Repeatable, metaclass=ABCMeta): ''' ---- Non-abstract methods ''' - def set_repetition(self: I, repetition: 'Repetition' | None) -> I: + def set_repetition(self, repetition: 'Repetition' | None) -> Self: self.repetition = repetition return self diff --git a/masque/traits/rotatable.py b/masque/traits/rotatable.py index b9bfab2..0e06527 100644 --- a/masque/traits/rotatable.py +++ b/masque/traits/rotatable.py @@ -1,4 +1,4 @@ -from typing import TypeVar, cast, Any +from typing import Self, cast, Any from abc import ABCMeta, abstractmethod 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 -T = TypeVar('T', bound='Rotatable') -I = TypeVar('I', bound='RotatableImpl') -P = TypeVar('P', bound='Pivotable') -J = TypeVar('J', bound='PivotableImpl') - - class Rotatable(metaclass=ABCMeta): """ Abstract class for all rotatable entities @@ -29,7 +23,7 @@ class Rotatable(metaclass=ABCMeta): ---- Abstract methods ''' @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. @@ -68,11 +62,11 @@ class RotatableImpl(Rotatable, metaclass=ABCMeta): ''' ---- Methods ''' - def rotate(self: I, rotation: float) -> I: + def rotate(self, rotation: float) -> Self: self.rotation += rotation return self - def set_rotation(self: I, rotation: float) -> I: + def set_rotation(self, rotation: float) -> Self: """ Set the rotation to a value @@ -94,7 +88,7 @@ class Pivotable(metaclass=ABCMeta): __slots__ = () @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. @@ -117,7 +111,7 @@ class PivotableImpl(Pivotable, metaclass=ABCMeta): offset: Any # TODO see if we can get around defining `offset` in PivotableImpl """ `[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) cast(Positionable, self).translate(-pivot) cast(Rotatable, self).rotate(rotation) diff --git a/masque/traits/scalable.py b/masque/traits/scalable.py index 2216945..2869aef 100644 --- a/masque/traits/scalable.py +++ b/masque/traits/scalable.py @@ -1,4 +1,4 @@ -from typing import TypeVar +from typing import Self from abc import ABCMeta, abstractmethod 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 -T = TypeVar('T', bound='Scalable') -I = TypeVar('I', bound='ScalableImpl') - - class Scalable(metaclass=ABCMeta): """ Abstract class for all scalable entities @@ -22,7 +18,7 @@ class Scalable(metaclass=ABCMeta): ---- Abstract methods ''' @abstractmethod - def scale_by(self: T, c: float) -> T: + def scale_by(self, c: float) -> Self: """ Scale the entity by a factor @@ -62,11 +58,11 @@ class ScalableImpl(Scalable, metaclass=ABCMeta): ''' ---- Methods ''' - def scale_by(self: I, c: float) -> I: + def scale_by(self, c: float) -> Self: self.scale *= c return self - def set_scale(self: I, scale: float) -> I: + def set_scale(self, scale: float) -> Self: """ Set the sclae to a value