From 89f327ba37c6a80fddc7577749f439f69cb92dcc Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 23 Feb 2022 11:27:11 -0800 Subject: [PATCH] reformat some multiline arg lists and add missing 'None' return types --- masque/builder/devices.py | 107 ++++++++++++++++-------------- masque/builder/utils.py | 3 +- masque/file/dxf.py | 73 ++++++++++++--------- masque/file/gdsii.py | 109 ++++++++++++++++--------------- masque/file/oasis.py | 105 +++++++++++++++++------------- masque/file/python_gdsii.py | 84 +++++++++++++----------- masque/file/svg.py | 9 +-- masque/file/utils.py | 5 +- masque/label.py | 23 +++---- masque/library/library.py | 16 ++++- masque/pattern.py | 122 ++++++++++++++++++---------------- masque/repetition.py | 14 ++-- masque/shapes/arc.py | 60 ++++++++--------- masque/shapes/circle.py | 38 +++++------ masque/shapes/ellipse.py | 48 +++++++------- masque/shapes/path.py | 75 ++++++++++----------- masque/shapes/polygon.py | 126 +++++++++++++++++++----------------- masque/shapes/shape.py | 27 ++++---- masque/shapes/text.py | 55 ++++++++-------- masque/subpattern.py | 27 ++++---- 20 files changed, 616 insertions(+), 510 deletions(-) diff --git a/masque/builder/devices.py b/masque/builder/devices.py index af33fb4..1dbd313 100644 --- a/masque/builder/devices.py +++ b/masque/builder/devices.py @@ -167,12 +167,13 @@ class Device(Copyable, Mirrorable): _dead: bool """ If True, plug()/place() are skipped (for debugging)""" - def __init__(self, - pattern: Optional[Pattern] = None, - ports: Optional[Dict[str, Port]] = None, - *, - name: Optional[str] = None, - ) -> None: + def __init__( + self, + pattern: Optional[Pattern] = None, + ports: Optional[Dict[str, Port]] = None, + *, + name: Optional[str] = None, + ) -> None: """ If `ports` is `None`, two default ports ('A' and 'B') are created. Both are placed at (0, 0) and have default `ptype`, but 'A' has rotation 0 @@ -218,10 +219,11 @@ class Device(Copyable, Mirrorable): else: return {k: self.ports[k] for k in key} - def rename_ports(self: D, - mapping: Dict[str, Optional[str]], - overwrite: bool = False, - ) -> D: + def rename_ports( + self: D, + mapping: Dict[str, Optional[str]], + overwrite: bool = False, + ) -> D: """ Renames ports as specified by `mapping`. Ports can be explicitly deleted by mapping them to `None`. @@ -248,11 +250,12 @@ class Device(Copyable, Mirrorable): self.ports.update(renamed) # type: ignore return self - def check_ports(self: D, - other_names: Iterable[str], - map_in: Optional[Dict[str, str]] = None, - map_out: Optional[Dict[str, Optional[str]]] = None, - ) -> D: + def check_ports( + self: D, + other_names: Iterable[str], + map_in: Optional[Dict[str, str]] = None, + map_out: Optional[Dict[str, Optional[str]]] = None, + ) -> D: """ Given the provided port mappings, check that: - All of the ports specified in the mappings exist @@ -332,12 +335,13 @@ class Device(Copyable, Mirrorable): new = Device(pat, ports=self.ports) return new - def as_interface(self, - name: str, - in_prefix: str = 'in_', - out_prefix: str = '', - port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None - ) -> 'Device': + def as_interface( + self, + name: str, + in_prefix: str = 'in_', + out_prefix: str = '', + port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None + ) -> 'Device': """ Begin building a new device based on all or some of the ports in the current device. Do not include the current device; instead use it @@ -406,15 +410,16 @@ class Device(Copyable, Mirrorable): new = Device(name=name, ports={**ports_in, **ports_out}) return new - def plug(self: D, - other: O, - map_in: Dict[str, str], - map_out: Optional[Dict[str, Optional[str]]] = None, - *, - mirrored: Tuple[bool, bool] = (False, False), - inherit_name: bool = True, - set_rotation: Optional[bool] = None, - ) -> D: + def plug( + self: D, + other: O, + map_in: Dict[str, str], + map_out: Optional[Dict[str, Optional[str]]] = None, + *, + mirrored: Tuple[bool, bool] = (False, False), + inherit_name: bool = True, + set_rotation: Optional[bool] = None, + ) -> D: """ Instantiate the device `other` into the current device, connecting the ports specified by `map_in` and renaming the unconnected @@ -495,16 +500,17 @@ class Device(Copyable, Mirrorable): mirrored=mirrored, port_map=map_out, skip_port_check=True) return self - def place(self: D, - other: O, - *, - offset: vector2 = (0, 0), - rotation: float = 0, - pivot: vector2 = (0, 0), - mirrored: Tuple[bool, bool] = (False, False), - port_map: Optional[Dict[str, Optional[str]]] = None, - skip_port_check: bool = False, - ) -> D: + def place( + self: D, + other: O, + *, + offset: vector2 = (0, 0), + rotation: float = 0, + pivot: vector2 = (0, 0), + mirrored: Tuple[bool, bool] = (False, False), + port_map: Optional[Dict[str, Optional[str]]] = None, + skip_port_check: bool = False, + ) -> D: """ Instantiate the device `other` into the current device, adding its ports to those of the current device (but not connecting any ports). @@ -572,13 +578,14 @@ class Device(Copyable, Mirrorable): self.pattern.subpatterns.append(sp) return self - def find_transform(self: D, - other: O, - map_in: Dict[str, str], - *, - mirrored: Tuple[bool, bool] = (False, False), - set_rotation: Optional[bool] = None, - ) -> Tuple[numpy.ndarray, float, numpy.ndarray]: + def find_transform( + self: D, + other: O, + map_in: Dict[str, str], + *, + mirrored: Tuple[bool, bool] = (False, False), + set_rotation: Optional[bool] = None, + ) -> Tuple[numpy.ndarray, float, numpy.ndarray]: """ Given a device `other` and a mapping `map_in` specifying port connections, find the transform which will correctly align the specified ports. @@ -745,7 +752,11 @@ class Device(Copyable, Mirrorable): return s -def rotate_offsets_around(offsets: ArrayLike, pivot: ArrayLike, angle: float) -> numpy.ndarray: +def rotate_offsets_around( + offsets: ArrayLike, + pivot: ArrayLike, + angle: float, + ) -> numpy.ndarray: offsets -= pivot offsets[:] = (rotation_matrix_2d(angle) @ offsets.T).T offsets += pivot diff --git a/masque/builder/utils.py b/masque/builder/utils.py index d4a9dbe..872a1c3 100644 --- a/masque/builder/utils.py +++ b/masque/builder/utils.py @@ -9,7 +9,8 @@ from ..utils import rotation_matrix_2d, vector2 from ..error import BuildError -def ell(ports: Dict[str, Port], +def ell( + ports: Dict[str, Port], ccw: Optional[bool], bound_type: str, bound: Union[float, vector2], diff --git a/masque/file/dxf.py b/masque/file/dxf.py index 4a94c05..3f442c3 100644 --- a/masque/file/dxf.py +++ b/masque/file/dxf.py @@ -28,13 +28,14 @@ logger.warning('DXF support is experimental and only slightly tested!') DEFAULT_LAYER = 'DEFAULT' -def write(pattern: Pattern, - stream: io.TextIOBase, - *, - modify_originals: bool = False, - dxf_version='AC1024', - disambiguate_func: Callable[[Iterable[Pattern]], None] = None, - ) -> None: +def write( + pattern: Pattern, + stream: io.TextIOBase, + *, + modify_originals: bool = False, + dxf_version='AC1024', + disambiguate_func: Callable[[Iterable[Pattern]], None] = None, + ) -> None: """ Write a `Pattern` to a DXF file, by first calling `.polygonize()` to change the shapes into polygons, and then writing patterns as DXF `Block`s, polygons as `LWPolyline`s, @@ -99,11 +100,12 @@ def write(pattern: Pattern, lib.write(stream) -def writefile(pattern: Pattern, - filename: Union[str, pathlib.Path], - *args, - **kwargs, - ) -> None: +def writefile( + pattern: Pattern, + filename: Union[str, pathlib.Path], + *args, + **kwargs, + ) -> None: """ Wrapper for `dxf.write()` that takes a filename or path instead of a stream. @@ -125,10 +127,11 @@ def writefile(pattern: Pattern, write(pattern, stream, *args, **kwargs) -def readfile(filename: Union[str, pathlib.Path], - *args, - **kwargs, - ) -> Tuple[Pattern, Dict[str, Any]]: +def readfile( + filename: Union[str, pathlib.Path], + *args, + **kwargs, + ) -> Tuple[Pattern, Dict[str, Any]]: """ Wrapper for `dxf.read()` that takes a filename or path instead of a stream. @@ -150,9 +153,10 @@ def readfile(filename: Union[str, pathlib.Path], return results -def read(stream: io.TextIOBase, - clean_vertices: bool = True, - ) -> Tuple[Pattern, Dict[str, Any]]: +def read( + stream: io.TextIOBase, + clean_vertices: bool = True, + ) -> Tuple[Pattern, Dict[str, Any]]: """ Read a dxf file and translate it into a dict of `Pattern` objects. DXF `Block`s are translated into `Pattern` objects; `LWPolyline`s are translated into polygons, and `Insert`s @@ -273,8 +277,10 @@ def _read_block(block, clean_vertices: bool) -> Pattern: return pat -def _subpatterns_to_refs(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace], - subpatterns: List[SubPattern]) -> None: +def _subpatterns_to_refs( + block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace], + subpatterns: List[SubPattern], + ) -> None: for subpat in subpatterns: if subpat.pattern is None: continue @@ -318,9 +324,11 @@ def _subpatterns_to_refs(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.M block.add_blockref(encoded_name, subpat.offset + dd, dxfattribs=attribs) -def _shapes_to_elements(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace], - shapes: List[Shape], - polygonize_paths: bool = False): +def _shapes_to_elements( + block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace], + shapes: List[Shape], + polygonize_paths: bool = False, + ) -> None: # Add `LWPolyline`s for each shape. # Could set do paths with width setting, but need to consider endcaps. for shape in shapes: @@ -331,8 +339,10 @@ def _shapes_to_elements(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Mo block.add_lwpolyline(xy_closed, dxfattribs=attribs) -def _labels_to_texts(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace], - labels: List[Label]) -> None: +def _labels_to_texts( + block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace], + labels: List[Label], + ) -> None: for label in labels: attribs = {'layer': _mlayer2dxf(label.layer)} xy = label.offset @@ -349,11 +359,12 @@ def _mlayer2dxf(layer: layer_t) -> str: raise PatternError(f'Unknown layer type: {layer} ({type(layer)})') -def disambiguate_pattern_names(patterns: Iterable[Pattern], - max_name_length: int = 32, - suffix_length: int = 6, - dup_warn_filter: Callable[[str], bool] = None, # If returns False, don't warn about this name - ) -> None: +def disambiguate_pattern_names( + patterns: Iterable[Pattern], + max_name_length: int = 32, + suffix_length: int = 6, + dup_warn_filter: Callable[[str], bool] = None, # If returns False, don't warn about this name + ) -> None: used_names = [] for pat in patterns: sanitized_name = re.compile(r'[^A-Za-z0-9_\?\$]').sub('_', pat.name) diff --git a/masque/file/gdsii.py b/masque/file/gdsii.py index e7015be..e4583fe 100644 --- a/masque/file/gdsii.py +++ b/masque/file/gdsii.py @@ -52,15 +52,16 @@ path_cap_map = { } -def write(patterns: Union[Pattern, Sequence[Pattern]], - stream: BinaryIO, - meters_per_unit: float, - logical_units_per_unit: float = 1, - library_name: str = 'masque-klamath', - *, - modify_originals: bool = False, - disambiguate_func: Callable[[Iterable[Pattern]], None] = None, - ) -> None: +def write( + patterns: Union[Pattern, Sequence[Pattern]], + stream: BinaryIO, + meters_per_unit: float, + logical_units_per_unit: float = 1, + library_name: str = 'masque-klamath', + *, + modify_originals: bool = False, + disambiguate_func: Callable[[Iterable[Pattern]], None] = None, + ) -> None: """ Convert a `Pattern` or list of patterns to a GDSII stream, and then mapping data as follows: Pattern -> GDSII structure @@ -136,11 +137,12 @@ def write(patterns: Union[Pattern, Sequence[Pattern]], records.ENDLIB.write(stream, None) -def writefile(patterns: Union[Sequence[Pattern], Pattern], - filename: Union[str, pathlib.Path], - *args, - **kwargs, - ) -> None: +def writefile( + patterns: Union[Sequence[Pattern], Pattern], + filename: Union[str, pathlib.Path], + *args, + **kwargs, + ) -> None: """ Wrapper for `write()` that takes a filename or path instead of a stream. @@ -162,10 +164,11 @@ def writefile(patterns: Union[Sequence[Pattern], Pattern], write(patterns, stream, *args, **kwargs) -def readfile(filename: Union[str, pathlib.Path], - *args, - **kwargs, - ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: +def readfile( + filename: Union[str, pathlib.Path], + *args, + **kwargs, + ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: """ Wrapper for `read()` that takes a filename or path instead of a stream. @@ -187,9 +190,10 @@ def readfile(filename: Union[str, pathlib.Path], return results -def read(stream: BinaryIO, - raw_mode: bool = True, - ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: +def read( + stream: BinaryIO, + raw_mode: bool = True, + ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: """ Read a gdsii file and translate it into a dict of Pattern objects. GDSII structures are translated into Pattern objects; boundaries are translated into polygons, and srefs and arefs @@ -243,10 +247,11 @@ def _read_header(stream: BinaryIO) -> Dict[str, Any]: return library_info -def read_elements(stream: BinaryIO, - name: str, - raw_mode: bool = True, - ) -> Pattern: +def read_elements( + stream: BinaryIO, + name: str, + raw_mode: bool = True, + ) -> Pattern: """ Read elements from a GDS structure and build a Pattern from them. @@ -296,8 +301,7 @@ def _mlayer2gds(mlayer: layer_t) -> Tuple[int, int]: return layer, data_type -def _ref_to_subpat(ref: klamath.library.Reference, - ) -> SubPattern: +def _ref_to_subpat(ref: klamath.library.Reference) -> SubPattern: """ Helper function to create a SubPattern from an SREF or AREF. Sets subpat.pattern to None and sets the instance .identifier to (struct_name,). @@ -351,8 +355,7 @@ def _boundary_to_polygon(boundary: klamath.library.Boundary, raw_mode: bool) -> ) -def _subpatterns_to_refs(subpatterns: List[SubPattern] - ) -> List[klamath.library.Reference]: +def _subpatterns_to_refs(subpatterns: List[SubPattern]) -> List[klamath.library.Reference]: refs = [] for subpat in subpatterns: if subpat.pattern is None: @@ -427,9 +430,10 @@ def _annotations_to_properties(annotations: annotations_t, max_len: int = 126) - return props -def _shapes_to_elements(shapes: List[Shape], - polygonize_paths: bool = False - ) -> List[klamath.elements.Element]: +def _shapes_to_elements( + shapes: List[Shape], + polygonize_paths: bool = False, + ) -> List[klamath.elements.Element]: elements: List[klamath.elements.Element] = [] # Add a Boundary element for each shape, and Path elements if necessary for shape in shapes: @@ -492,11 +496,12 @@ def _labels_to_texts(labels: List[Label]) -> List[klamath.elements.Text]: return texts -def disambiguate_pattern_names(patterns: Sequence[Pattern], - max_name_length: int = 32, - suffix_length: int = 6, - dup_warn_filter: Optional[Callable[[str], bool]] = None, - ): +def disambiguate_pattern_names( + patterns: Sequence[Pattern], + max_name_length: int = 32, + suffix_length: int = 6, + dup_warn_filter: Optional[Callable[[str], bool]] = None, + ) -> None: """ Args: patterns: List of patterns to disambiguate @@ -549,12 +554,13 @@ def disambiguate_pattern_names(patterns: Sequence[Pattern], used_names.append(suffixed_name) -def load_library(stream: BinaryIO, - tag: str, - is_secondary: Optional[Callable[[str], bool]] = None, - *, - full_load: bool = False, - ) -> Tuple[Library, Dict[str, Any]]: +def load_library( + stream: BinaryIO, + tag: str, + is_secondary: Optional[Callable[[str], bool]] = None, + *, + full_load: bool = False, + ) -> Tuple[Library, Dict[str, Any]]: """ Scan a GDSII stream to determine what structures are present, and create a library from them. This enables deferred reading of structures @@ -581,7 +587,7 @@ def load_library(stream: BinaryIO, Additional library info (dict, same format as from `read`). """ if is_secondary is None: - def is_secondary(k: str): + def is_secondary(k: str) -> bool: return False assert(is_secondary is not None) @@ -611,13 +617,14 @@ def load_library(stream: BinaryIO, return lib, library_info -def load_libraryfile(filename: Union[str, pathlib.Path], - tag: str, - is_secondary: Optional[Callable[[str], bool]] = None, - *, - use_mmap: bool = True, - full_load: bool = False, - ) -> Tuple[Library, Dict[str, Any]]: +def load_libraryfile( + filename: Union[str, pathlib.Path], + tag: str, + is_secondary: Optional[Callable[[str], bool]] = None, + *, + use_mmap: bool = True, + full_load: bool = False, + ) -> Tuple[Library, Dict[str, Any]]: """ Wrapper for `load_library()` that takes a filename or path instead of a stream. diff --git a/masque/file/oasis.py b/masque/file/oasis.py index 92abbfd..89b0a11 100644 --- a/masque/file/oasis.py +++ b/masque/file/oasis.py @@ -47,14 +47,15 @@ path_cap_map = { #TODO implement more shape types? -def build(patterns: Union[Pattern, Sequence[Pattern]], - units_per_micron: int, - layer_map: Optional[Dict[str, Union[int, Tuple[int, int]]]] = None, - *, - modify_originals: bool = False, - disambiguate_func: Optional[Callable[[Iterable[Pattern]], None]] = None, - annotations: Optional[annotations_t] = None - ) -> fatamorgana.OasisLayout: +def build( + patterns: Union[Pattern, Sequence[Pattern]], + units_per_micron: int, + layer_map: Optional[Dict[str, Union[int, Tuple[int, int]]]] = None, + *, + modify_originals: bool = False, + disambiguate_func: Optional[Callable[[Iterable[Pattern]], None]] = None, + annotations: Optional[annotations_t] = None, + ) -> fatamorgana.OasisLayout: """ Convert a `Pattern` or list of patterns to an OASIS stream, writing patterns as OASIS cells, subpatterns as Placement records, and other shapes and labels @@ -153,10 +154,12 @@ def build(patterns: Union[Pattern, Sequence[Pattern]], return lib -def write(patterns: Union[Sequence[Pattern], Pattern], - stream: io.BufferedIOBase, - *args, - **kwargs): +def write( + patterns: Union[Sequence[Pattern], Pattern], + stream: io.BufferedIOBase, + *args, + **kwargs, + ) -> None: """ Write a `Pattern` or list of patterns to a OASIS file. See `oasis.build()` for details. @@ -171,11 +174,12 @@ def write(patterns: Union[Sequence[Pattern], Pattern], lib.write(stream) -def writefile(patterns: Union[Sequence[Pattern], Pattern], - filename: Union[str, pathlib.Path], - *args, - **kwargs, - ): +def writefile( + patterns: Union[Sequence[Pattern], Pattern], + filename: Union[str, pathlib.Path], + *args, + **kwargs, + ) -> None: """ Wrapper for `oasis.write()` that takes a filename or path instead of a stream. @@ -198,10 +202,11 @@ def writefile(patterns: Union[Sequence[Pattern], Pattern], return results -def readfile(filename: Union[str, pathlib.Path], - *args, - **kwargs, - ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: +def readfile( + filename: Union[str, pathlib.Path], + *args, + **kwargs, + ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: """ Wrapper for `oasis.read()` that takes a filename or path instead of a stream. @@ -223,9 +228,10 @@ def readfile(filename: Union[str, pathlib.Path], return results -def read(stream: io.BufferedIOBase, - clean_vertices: bool = True, - ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: +def read( + stream: io.BufferedIOBase, + clean_vertices: bool = True, + ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: """ Read a OASIS file and translate it into a dict of Pattern objects. OASIS cells are translated into Pattern objects; Polygons are translated into polygons, and Placements @@ -496,8 +502,9 @@ def _placement_to_subpat(placement: fatrec.Placement, lib: fatamorgana.OasisLayo return subpat -def _subpatterns_to_placements(subpatterns: List[SubPattern] - ) -> List[fatrec.Placement]: +def _subpatterns_to_placements( + subpatterns: List[SubPattern], + ) -> List[fatrec.Placement]: refs = [] for subpat in subpatterns: if subpat.pattern is None: @@ -523,9 +530,10 @@ def _subpatterns_to_placements(subpatterns: List[SubPattern] return refs -def _shapes_to_elements(shapes: List[Shape], - layer2oas: Callable[[layer_t], Tuple[int, int]], - ) -> List[Union[fatrec.Polygon, fatrec.Path, fatrec.Circle]]: +def _shapes_to_elements( + shapes: List[Shape], + layer2oas: Callable[[layer_t], Tuple[int, int]], + ) -> List[Union[fatrec.Polygon, fatrec.Path, fatrec.Circle]]: # Add a Polygon record for each shape, and Path elements if necessary elements: List[Union[fatrec.Polygon, fatrec.Path, fatrec.Circle]] = [] for shape in shapes: @@ -576,9 +584,10 @@ def _shapes_to_elements(shapes: List[Shape], return elements -def _labels_to_texts(labels: List[Label], - layer2oas: Callable[[layer_t], Tuple[int, int]], - ) -> List[fatrec.Text]: +def _labels_to_texts( + labels: List[Label], + layer2oas: Callable[[layer_t], Tuple[int, int]], + ) -> List[fatrec.Text]: texts = [] for label in labels: layer, datatype = layer2oas(label.layer) @@ -595,9 +604,10 @@ def _labels_to_texts(labels: List[Label], return texts -def disambiguate_pattern_names(patterns, - dup_warn_filter: Callable[[str], bool] = None, # If returns False, don't warn about this name - ): +def disambiguate_pattern_names( + patterns, + dup_warn_filter: Callable[[str], bool] = None, # If returns False, don't warn about this name + ) -> None: used_names = [] for pat in patterns: sanitized_name = re.compile(r'[^A-Za-z0-9_\?\$]').sub('_', pat.name) @@ -625,8 +635,9 @@ def disambiguate_pattern_names(patterns, used_names.append(suffixed_name) -def repetition_fata2masq(rep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None] - ) -> Optional[Repetition]: +def repetition_fata2masq( + rep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None], + ) -> Optional[Repetition]: mrep: Optional[Repetition] if isinstance(rep, fatamorgana.GridRepetition): mrep = Grid(a_vector=rep.a_vector, @@ -643,11 +654,12 @@ def repetition_fata2masq(rep: Union[fatamorgana.GridRepetition, fatamorgana.Arbi return mrep -def repetition_masq2fata(rep: Optional[Repetition] - ) -> Tuple[Union[fatamorgana.GridRepetition, - fatamorgana.ArbitraryRepetition, - None], - Tuple[int, int]]: +def repetition_masq2fata( + rep: Optional[Repetition], + ) -> Tuple[Union[fatamorgana.GridRepetition, + fatamorgana.ArbitraryRepetition, + None], + Tuple[int, int]]: frep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None] if isinstance(rep, Grid): frep = fatamorgana.GridRepetition( @@ -678,10 +690,11 @@ def annotations_to_properties(annotations: annotations_t) -> List[fatrec.Propert return properties -def properties_to_annotations(properties: List[fatrec.Property], - propnames: Dict[int, NString], - propstrings: Dict[int, AString], - ) -> annotations_t: +def properties_to_annotations( + properties: List[fatrec.Property], + propnames: Dict[int, NString], + propstrings: Dict[int, AString], + ) -> annotations_t: annotations = {} for proprec in properties: assert(proprec.name is not None) diff --git a/masque/file/python_gdsii.py b/masque/file/python_gdsii.py index f42d626..b160f65 100644 --- a/masque/file/python_gdsii.py +++ b/masque/file/python_gdsii.py @@ -53,14 +53,15 @@ path_cap_map = { } -def build(patterns: Union[Pattern, Sequence[Pattern]], - meters_per_unit: float, - logical_units_per_unit: float = 1, - library_name: str = 'masque-gdsii-write', - *, - modify_originals: bool = False, - disambiguate_func: Callable[[Iterable[Pattern]], None] = None, - ) -> gdsii.library.Library: +def build( + patterns: Union[Pattern, Sequence[Pattern]], + meters_per_unit: float, + logical_units_per_unit: float = 1, + library_name: str = 'masque-gdsii-write', + *, + modify_originals: bool = False, + disambiguate_func: Callable[[Iterable[Pattern]], None] = None, + ) -> gdsii.library.Library: """ Convert a `Pattern` or list of patterns to a GDSII stream, by first calling `.polygonize()` to change the shapes into polygons, and then writing patterns @@ -137,10 +138,12 @@ def build(patterns: Union[Pattern, Sequence[Pattern]], return lib -def write(patterns: Union[Pattern, Sequence[Pattern]], - stream: io.BufferedIOBase, - *args, - **kwargs): +def write( + patterns: Union[Pattern, Sequence[Pattern]], + stream: io.BufferedIOBase, + *args, + **kwargs, + ) -> None: """ Write a `Pattern` or list of patterns to a GDSII file. See `masque.file.gdsii.build()` for details. @@ -155,11 +158,12 @@ def write(patterns: Union[Pattern, Sequence[Pattern]], lib.save(stream) return -def writefile(patterns: Union[Sequence[Pattern], Pattern], - filename: Union[str, pathlib.Path], - *args, - **kwargs, - ): +def writefile( + patterns: Union[Sequence[Pattern], Pattern], + filename: Union[str, pathlib.Path], + *args, + **kwargs, + ) -> None: """ Wrapper for `masque.file.gdsii.write()` that takes a filename or path instead of a stream. @@ -182,10 +186,11 @@ def writefile(patterns: Union[Sequence[Pattern], Pattern], return results -def readfile(filename: Union[str, pathlib.Path], - *args, - **kwargs, - ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: +def readfile( + filename: Union[str, pathlib.Path], + *args, + **kwargs, + ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: """ Wrapper for `masque.file.gdsii.read()` that takes a filename or path instead of a stream. @@ -207,9 +212,10 @@ def readfile(filename: Union[str, pathlib.Path], return results -def read(stream: io.BufferedIOBase, - clean_vertices: bool = True, - ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: +def read( + stream: io.BufferedIOBase, + clean_vertices: bool = True, + ) -> Tuple[Dict[str, Pattern], Dict[str, Any]]: """ Read a gdsii file and translate it into a dict of Pattern objects. GDSII structures are translated into Pattern objects; boundaries are translated into polygons, and srefs and arefs @@ -294,9 +300,10 @@ def _mlayer2gds(mlayer: layer_t) -> Tuple[int, int]: return layer, data_type -def _ref_to_subpat(element: Union[gdsii.elements.SRef, - gdsii.elements.ARef] - ) -> SubPattern: +def _ref_to_subpat( + element: Union[gdsii.elements.SRef, + gdsii.elements.ARef] + ) -> SubPattern: """ Helper function to create a SubPattern from an SREF or AREF. Sets subpat.pattern to None and sets the instance .identifier to (struct_name,). @@ -379,8 +386,9 @@ def _boundary_to_polygon(element: gdsii.elements.Boundary, raw_mode: bool) -> Po return Polygon(**args) -def _subpatterns_to_refs(subpatterns: List[SubPattern] - ) -> List[Union[gdsii.elements.ARef, gdsii.elements.SRef]]: +def _subpatterns_to_refs( + subpatterns: List[SubPattern], + ) -> List[Union[gdsii.elements.ARef, gdsii.elements.SRef]]: refs = [] for subpat in subpatterns: if subpat.pattern is None: @@ -450,9 +458,10 @@ def _annotations_to_properties(annotations: annotations_t, max_len: int = 126) - return props -def _shapes_to_elements(shapes: List[Shape], - polygonize_paths: bool = False - ) -> List[Union[gdsii.elements.Boundary, gdsii.elements.Path]]: +def _shapes_to_elements( + shapes: List[Shape], + polygonize_paths: bool = False, + ) -> List[Union[gdsii.elements.Boundary, gdsii.elements.Path]]: elements: List[Union[gdsii.elements.Boundary, gdsii.elements.Path]] = [] # Add a Boundary element for each shape, and Path elements if necessary for shape in shapes: @@ -496,11 +505,12 @@ def _labels_to_texts(labels: List[Label]) -> List[gdsii.elements.Text]: return texts -def disambiguate_pattern_names(patterns: Sequence[Pattern], - max_name_length: int = 32, - suffix_length: int = 6, - dup_warn_filter: Optional[Callable[[str], bool]] = None, - ): +def disambiguate_pattern_names( + patterns: Sequence[Pattern], + max_name_length: int = 32, + suffix_length: int = 6, + dup_warn_filter: Optional[Callable[[str], bool]] = None, + ) -> None: """ Args: patterns: List of patterns to disambiguate diff --git a/masque/file/svg.py b/masque/file/svg.py index a9f7c47..0b3ef85 100644 --- a/masque/file/svg.py +++ b/masque/file/svg.py @@ -11,10 +11,11 @@ from .utils import mangle_name from .. import Pattern -def writefile(pattern: Pattern, - filename: str, - custom_attributes: bool = False, - ) -> None: +def writefile( + pattern: Pattern, + filename: str, + custom_attributes: bool = False, + ) -> None: """ Write a Pattern to an SVG file, by first calling .polygonize() on it to change the shapes into polygons, and then writing patterns as SVG diff --git a/masque/file/utils.py b/masque/file/utils.py index d183b39..47e8b7d 100644 --- a/masque/file/utils.py +++ b/masque/file/utils.py @@ -95,8 +95,9 @@ def dtype2dose(pattern: Pattern) -> Pattern: return pattern -def dose2dtype(patterns: List[Pattern], - ) -> Tuple[List[Pattern], List[float]]: +def dose2dtype( + patterns: List[Pattern], + ) -> Tuple[List[Pattern], List[float]]: """ For each shape in each pattern, set shape.layer to the tuple (base_layer, datatype), where: diff --git a/masque/label.py b/masque/label.py index 1907095..57fe4ca 100644 --- a/masque/label.py +++ b/masque/label.py @@ -36,19 +36,20 @@ class Label(PositionableImpl, LayerableImpl, LockableImpl, RepeatableImpl, Annot return self._string @string.setter - def string(self, val: str): + def string(self, val: str) -> None: self._string = val - def __init__(self, - string: str, - *, - offset: vector2 = (0.0, 0.0), - layer: layer_t = 0, - repetition: Optional[Repetition] = None, - annotations: Optional[annotations_t] = None, - locked: bool = False, - identifier: Tuple = (), - ) -> None: + def __init__( + self, + string: str, + *, + offset: vector2 = (0.0, 0.0), + layer: layer_t = 0, + repetition: Optional[Repetition] = None, + annotations: Optional[annotations_t] = None, + locked: bool = False, + identifier: Tuple = (), + ) -> None: LockableImpl.unlock(self) self.identifier = identifier self.string = string diff --git a/masque/library/library.py b/masque/library/library.py index 91fd6a8..38e942b 100644 --- a/masque/library/library.py +++ b/masque/library/library.py @@ -143,7 +143,13 @@ class Library: def __repr__(self) -> str: return '' - def set_const(self, key: str, tag: Any, const: 'Pattern', secondary: bool = False) -> None: + def set_const( + self, + key: str, + tag: Any, + const: 'Pattern', + secondary: bool = False, + ) -> None: """ Convenience function to avoid having to manually wrap constant values into callables. @@ -162,7 +168,13 @@ class Library: else: self.primary[key] = pg - def set_value(self, key: str, tag: str, value: Callable[[], 'Pattern'], secondary: bool = False) -> None: + def set_value( + self, + key: str, + tag: str, + value: Callable[[], 'Pattern'], + secondary: bool = False, + ) -> None: """ Convenience function to automatically build a PatternGenerator. diff --git a/masque/pattern.py b/masque/pattern.py index ddd1632..3fef604 100644 --- a/masque/pattern.py +++ b/masque/pattern.py @@ -53,15 +53,16 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): name: str """ A name for this pattern """ - def __init__(self, - name: str = '', - *, - shapes: Sequence[Shape] = (), - labels: Sequence[Label] = (), - subpatterns: Sequence[SubPattern] = (), - annotations: Optional[annotations_t] = None, - locked: bool = False, - ) -> None: + def __init__( + self, + name: str = '', + *, + shapes: Sequence[Shape] = (), + labels: Sequence[Label] = (), + subpatterns: Sequence[SubPattern] = (), + annotations: Optional[annotations_t] = None, + locked: bool = False, + ) -> None: """ Basic init; arguments get assigned to member variables. Non-list inputs for shapes and subpatterns get converted to lists. @@ -141,12 +142,13 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): self.labels += other_pattern.labels return self - def subset(self, - shapes_func: Callable[[Shape], bool] = None, - labels_func: Callable[[Label], bool] = None, - subpatterns_func: Callable[[SubPattern], bool] = None, - recursive: bool = False, - ) -> 'Pattern': + def subset( + self, + shapes_func: Callable[[Shape], bool] = None, + labels_func: Callable[[Label], bool] = None, + subpatterns_func: Callable[[SubPattern], bool] = None, + recursive: bool = False, + ) -> 'Pattern': """ Returns a Pattern containing only the entities (e.g. shapes) for which the given entity_func returns True. @@ -186,10 +188,11 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): assert(pat is not None) return pat - def apply(self, - func: Callable[[Optional['Pattern']], Optional['Pattern']], - memo: Optional[Dict[int, Optional['Pattern']]] = None, - ) -> Optional['Pattern']: + def apply( + self, + func: Callable[[Optional['Pattern']], Optional['Pattern']], + memo: Optional[Dict[int, Optional['Pattern']]] = None, + ) -> Optional['Pattern']: """ Recursively apply func() to this pattern and any pattern it references. func() is expected to take and return a Pattern. @@ -229,7 +232,8 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): pat = memo[pat_id] return pat - def dfs(self: P, + def dfs( + self: P, visit_before: visitor_function_t = None, visit_after: visitor_function_t = None, transform: Union[numpy.ndarray, bool, None] = False, @@ -237,7 +241,7 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): hierarchy: Tuple[P, ...] = (), ) -> P: """ - Experimental convenience function. + Convenience function. Performs a depth-first traversal of this pattern and its subpatterns. At each pattern in the tree, the following sequence is called: ``` @@ -314,10 +318,11 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): pat = visit_after(pat, hierarchy=hierarchy, memo=memo, transform=transform) # type: ignore return pat - def polygonize(self: P, - poly_num_points: Optional[int] = None, - poly_max_arclen: Optional[float] = None, - ) -> P: + def polygonize( + self: P, + poly_num_points: Optional[int] = None, + poly_max_arclen: Optional[float] = None, + ) -> P: """ Calls `.to_polygons(...)` on all the shapes in this Pattern and any referenced patterns, replacing them with the returned polygons. @@ -342,10 +347,11 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): subpat.pattern.polygonize(poly_num_points, poly_max_arclen) return self - def manhattanize(self: P, - grid_x: ArrayLike, - grid_y: ArrayLike, - ) -> P: + def manhattanize( + self: P, + grid_x: ArrayLike, + grid_y: ArrayLike, + ) -> P: """ Calls `.polygonize()` and `.flatten()` on the pattern, then calls `.manhattanize()` on all the resulting shapes, replacing them with the returned Manhattan polygons. @@ -364,11 +370,12 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): (shape.manhattanize(grid_x, grid_y) for shape in old_shapes))) return self - def subpatternize(self: P, - recursive: bool = True, - norm_value: int = int(1e6), - exclude_types: Tuple[Type] = (Polygon,) - ) -> P: + def subpatternize( + self: P, + recursive: bool = True, + norm_value: int = int(1e6), + exclude_types: Tuple[Type] = (Polygon,) + ) -> P: """ Iterates through this `Pattern` and all referenced `Pattern`s. Within each `Pattern`, it iterates over all shapes, calling `.normalized_form(norm_value)` on them to retrieve a scale-, @@ -456,11 +463,12 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): def referenced_patterns_by_id(self, include_none: bool) -> Dict[int, Optional['Pattern']]: pass - def referenced_patterns_by_id(self, - include_none: bool = False, - recursive: bool = True, - ) -> Union[Dict[int, Optional['Pattern']], - Dict[int, 'Pattern']]: + def referenced_patterns_by_id( + self, + include_none: bool = False, + recursive: bool = True, + ) -> Union[Dict[int, Optional['Pattern']], + Dict[int, 'Pattern']]: """ Create a dictionary with `{id(pat): pat}` for all Pattern objects referenced by this @@ -484,7 +492,10 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): ids.update(pat.referenced_patterns_by_id()) return ids - def referenced_patterns_by_name(self, **kwargs: Any) -> List[Tuple[Optional[str], Optional['Pattern']]]: + def referenced_patterns_by_name( + self, + **kwargs: Any, + ) -> List[Tuple[Optional[str], Optional['Pattern']]]: """ Create a list of `(pat.name, pat)` tuples for all Pattern objects referenced by this Pattern (operates recursively on all referenced Patterns as well). @@ -502,10 +513,11 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): pat_list = [(p.name if p is not None else None, p) for p in pats_by_id.values()] return pat_list - def subpatterns_by_id(self, - include_none: bool = False, - recursive: bool = True, - ) -> Dict[int, List[SubPattern]]: + def subpatterns_by_id( + self, + include_none: bool = False, + recursive: bool = True, + ) -> Dict[int, List[SubPattern]]: """ Create a dictionary which maps `{id(referenced_pattern): [subpattern0, ...]}` for all SubPattern objects referenced by this Pattern (by default, operates @@ -593,10 +605,11 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): flatten_single(self, set()) return self - def wrap_repeated_shapes(self: P, - name_func: Callable[['Pattern', Union[Shape, Label]], str] = lambda p, s: '_repetition', - recursive: bool = True, - ) -> P: + def wrap_repeated_shapes( + self: P, + name_func: Callable[['Pattern', Union[Shape, Label]], str] = lambda p, s: '_repetition', + recursive: bool = True, + ) -> P: """ Wraps all shapes and labels with a non-`None` `repetition` attribute into a `SubPattern`/`Pattern` combination, and applies the `repetition` @@ -930,12 +943,13 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): pickle.dump(self, f, protocol=pickle.HIGHEST_PROTOCOL) return self - def visualize(self, - offset: vector2 = (0., 0.), - line_color: str = 'k', - fill_color: str = 'none', - overdraw: bool = False, - ) -> None: + def visualize( + self, + offset: vector2 = (0., 0.), + line_color: str = 'k', + fill_color: str = 'none', + overdraw: bool = False, + ) -> None: """ Draw a picture of the Pattern and wait for the user to inspect it diff --git a/masque/repetition.py b/masque/repetition.py index 4bf289b..032ac89 100644 --- a/masque/repetition.py +++ b/masque/repetition.py @@ -61,12 +61,14 @@ class Grid(LockableImpl, Repetition, metaclass=AutoSlots): _b_count: int """ Number of instances along the direction specified by the `b_vector` """ - def __init__(self, - a_vector: ArrayLike, - a_count: int, - b_vector: Optional[ArrayLike] = None, - b_count: Optional[int] = 1, - locked: bool = False,): + def __init__( + self, + a_vector: ArrayLike, + a_count: int, + b_vector: Optional[ArrayLike] = None, + b_count: Optional[int] = 1, + locked: bool = False, + ) -> None: """ Args: a_vector: First lattice vector, of the form `[x, y]`. diff --git a/masque/shapes/arc.py b/masque/shapes/arc.py index f4542c4..3bea013 100644 --- a/masque/shapes/arc.py +++ b/masque/shapes/arc.py @@ -51,7 +51,7 @@ class Arc(Shape, metaclass=AutoSlots): return self._radii @radii.setter - def radii(self, val: vector2): + def radii(self, val: vector2) -> None: val = numpy.array(val, dtype=float).flatten() if not val.size == 2: raise PatternError('Radii must have length 2') @@ -64,7 +64,7 @@ class Arc(Shape, metaclass=AutoSlots): return self._radii[0] @radius_x.setter - def radius_x(self, val: float): + def radius_x(self, val: float) -> None: if not val >= 0: raise PatternError('Radius must be non-negative') self._radii[0] = val @@ -74,7 +74,7 @@ class Arc(Shape, metaclass=AutoSlots): return self._radii[1] @radius_y.setter - def radius_y(self, val: float): + def radius_y(self, val: float) -> None: if not val >= 0: raise PatternError('Radius must be non-negative') self._radii[1] = val @@ -92,7 +92,7 @@ class Arc(Shape, metaclass=AutoSlots): return self._angles @angles.setter - def angles(self, val: vector2): + def angles(self, val: vector2) -> None: val = numpy.array(val, dtype=float).flatten() if not val.size == 2: raise PatternError('Angles must have length 2') @@ -103,7 +103,7 @@ class Arc(Shape, metaclass=AutoSlots): return self.angles[0] @start_angle.setter - def start_angle(self, val: float): + def start_angle(self, val: float) -> None: self.angles = (val, self.angles[1]) @property @@ -111,7 +111,7 @@ class Arc(Shape, metaclass=AutoSlots): return self.angles[1] @stop_angle.setter - def stop_angle(self, val: float): + def stop_angle(self, val: float) -> None: self.angles = (self.angles[0], val) # Rotation property @@ -126,7 +126,7 @@ class Arc(Shape, metaclass=AutoSlots): return self._rotation @rotation.setter - def rotation(self, val: float): + def rotation(self, val: float) -> None: if not is_scalar(val): raise PatternError('Rotation must be a scalar') self._rotation = val % (2 * pi) @@ -143,30 +143,31 @@ class Arc(Shape, metaclass=AutoSlots): return self._width @width.setter - def width(self, val: float): + def width(self, val: float) -> None: if not is_scalar(val): raise PatternError('Width must be a scalar') if not val > 0: raise PatternError('Width must be positive') self._width = val - def __init__(self, - radii: vector2, - angles: vector2, - width: float, - *, - poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS, - poly_max_arclen: Optional[float] = None, - offset: vector2 = (0.0, 0.0), - rotation: float = 0, - mirrored: Sequence[bool] = (False, False), - layer: layer_t = 0, - dose: float = 1.0, - repetition: Optional[Repetition] = None, - annotations: Optional[annotations_t] = None, - locked: bool = False, - raw: bool = False, - ): + def __init__( + self, + radii: vector2, + angles: vector2, + width: float, + *, + poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS, + poly_max_arclen: Optional[float] = None, + offset: vector2 = (0.0, 0.0), + rotation: float = 0, + mirrored: Sequence[bool] = (False, False), + layer: layer_t = 0, + dose: float = 1.0, + repetition: Optional[Repetition] = None, + annotations: Optional[annotations_t] = None, + locked: bool = False, + raw: bool = False, + ) -> None: LockableImpl.unlock(self) self.identifier = () if raw: @@ -204,10 +205,11 @@ class Arc(Shape, metaclass=AutoSlots): new.set_locked(self.locked) return new - def to_polygons(self, - poly_num_points: Optional[int] = None, - poly_max_arclen: Optional[float] = None, - ) -> List[Polygon]: + def to_polygons( + self, + poly_num_points: Optional[int] = None, + poly_max_arclen: Optional[float] = None, + ) -> List[Polygon]: if poly_num_points is None: poly_num_points = self.poly_num_points if poly_max_arclen is None: diff --git a/masque/shapes/circle.py b/masque/shapes/circle.py index d7aaba4..44c9896 100644 --- a/masque/shapes/circle.py +++ b/masque/shapes/circle.py @@ -35,26 +35,27 @@ class Circle(Shape, metaclass=AutoSlots): return self._radius @radius.setter - def radius(self, val: float): + def radius(self, val: float) -> None: if not is_scalar(val): raise PatternError('Radius must be a scalar') if not val >= 0: raise PatternError('Radius must be non-negative') self._radius = val - def __init__(self, - radius: float, - *, - poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS, - poly_max_arclen: Optional[float] = None, - offset: vector2 = (0.0, 0.0), - layer: layer_t = 0, - dose: float = 1.0, - repetition: Optional[Repetition] = None, - annotations: Optional[annotations_t] = None, - locked: bool = False, - raw: bool = False, - ): + def __init__( + self, + radius: float, + *, + poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS, + poly_max_arclen: Optional[float] = None, + offset: vector2 = (0.0, 0.0), + layer: layer_t = 0, + dose: float = 1.0, + repetition: Optional[Repetition] = None, + annotations: Optional[annotations_t] = None, + locked: bool = False, + raw: bool = False, + ) -> None: LockableImpl.unlock(self) self.identifier = () if raw: @@ -83,10 +84,11 @@ class Circle(Shape, metaclass=AutoSlots): new.set_locked(self.locked) return new - def to_polygons(self, - poly_num_points: Optional[int] = None, - poly_max_arclen: Optional[float] = None, - ) -> List[Polygon]: + def to_polygons( + self, + poly_num_points: Optional[int] = None, + poly_max_arclen: Optional[float] = None, + ) -> List[Polygon]: if poly_num_points is None: poly_num_points = self.poly_num_points if poly_max_arclen is None: diff --git a/masque/shapes/ellipse.py b/masque/shapes/ellipse.py index f9aefbf..51b7eef 100644 --- a/masque/shapes/ellipse.py +++ b/masque/shapes/ellipse.py @@ -41,7 +41,7 @@ class Ellipse(Shape, metaclass=AutoSlots): return self._radii @radii.setter - def radii(self, val: vector2): + def radii(self, val: vector2) -> None: val = numpy.array(val).flatten() if not val.size == 2: raise PatternError('Radii must have length 2') @@ -54,7 +54,7 @@ class Ellipse(Shape, metaclass=AutoSlots): return self.radii[0] @radius_x.setter - def radius_x(self, val: float): + def radius_x(self, val: float) -> None: if not val >= 0: raise PatternError('Radius must be non-negative') self.radii[0] = val @@ -64,7 +64,7 @@ class Ellipse(Shape, metaclass=AutoSlots): return self.radii[1] @radius_y.setter - def radius_y(self, val: float): + def radius_y(self, val: float) -> None: if not val >= 0: raise PatternError('Radius must be non-negative') self.radii[1] = val @@ -82,26 +82,27 @@ class Ellipse(Shape, metaclass=AutoSlots): return self._rotation @rotation.setter - def rotation(self, val: float): + def rotation(self, val: float) -> None: if not is_scalar(val): raise PatternError('Rotation must be a scalar') self._rotation = val % pi - def __init__(self, - radii: vector2, - *, - poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS, - poly_max_arclen: Optional[float] = None, - offset: vector2 = (0.0, 0.0), - rotation: float = 0, - mirrored: Sequence[bool] = (False, False), - layer: layer_t = 0, - dose: float = 1.0, - repetition: Optional[Repetition] = None, - annotations: Optional[annotations_t] = None, - locked: bool = False, - raw: bool = False, - ): + def __init__( + self, + radii: vector2, + *, + poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS, + poly_max_arclen: Optional[float] = None, + offset: vector2 = (0.0, 0.0), + rotation: float = 0, + mirrored: Sequence[bool] = (False, False), + layer: layer_t = 0, + dose: float = 1.0, + repetition: Optional[Repetition] = None, + annotations: Optional[annotations_t] = None, + locked: bool = False, + raw: bool = False, + ) -> None: LockableImpl.unlock(self) self.identifier = () if raw: @@ -134,10 +135,11 @@ class Ellipse(Shape, metaclass=AutoSlots): new.set_locked(self.locked) return new - def to_polygons(self, - poly_num_points: Optional[int] = None, - poly_max_arclen: Optional[float] = None, - ) -> List[Polygon]: + def to_polygons( + self, + poly_num_points: Optional[int] = None, + poly_max_arclen: Optional[float] = None, + ) -> List[Polygon]: if poly_num_points is None: poly_num_points = self.poly_num_points if poly_max_arclen is None: diff --git a/masque/shapes/path.py b/masque/shapes/path.py index 1d64ca2..b73c9cd 100644 --- a/masque/shapes/path.py +++ b/masque/shapes/path.py @@ -46,7 +46,7 @@ class Path(Shape, metaclass=AutoSlots): return self._width @width.setter - def width(self, val: float): + def width(self, val: float) -> None: if not is_scalar(val): raise PatternError('Width must be a scalar') if not val >= 0: @@ -62,7 +62,7 @@ class Path(Shape, metaclass=AutoSlots): return self._cap @cap.setter - def cap(self, val: PathCap): + def cap(self, val: PathCap) -> None: # TODO: Document that setting cap can change cap_extensions self._cap = PathCap(val) if self.cap != PathCap.SquareCustom: @@ -83,7 +83,7 @@ class Path(Shape, metaclass=AutoSlots): return self._cap_extensions @cap_extensions.setter - def cap_extensions(self, vals: Optional[numpy.ndarray]): + def cap_extensions(self, vals: Optional[numpy.ndarray]) -> None: custom_caps = (PathCap.SquareCustom,) if self.cap in custom_caps: if vals is None: @@ -103,7 +103,7 @@ class Path(Shape, metaclass=AutoSlots): return self._vertices @vertices.setter - def vertices(self, val: ArrayLike): + def vertices(self, val: ArrayLike) -> None: val = numpy.array(val, dtype=float) # TODO document that these might not be copied if len(val.shape) < 2 or val.shape[1] != 2: raise PatternError('Vertices must be an Nx2 array') @@ -120,7 +120,7 @@ class Path(Shape, metaclass=AutoSlots): return self.vertices[:, 0] @xs.setter - def xs(self, val: ArrayLike): + def xs(self, val: ArrayLike) -> None: val = numpy.array(val, dtype=float).flatten() if val.size != self.vertices.shape[0]: raise PatternError('Wrong number of vertices') @@ -135,28 +135,29 @@ class Path(Shape, metaclass=AutoSlots): return self.vertices[:, 1] @ys.setter - def ys(self, val: ArrayLike): + def ys(self, val: ArrayLike) -> None: val = numpy.array(val, dtype=float).flatten() if val.size != self.vertices.shape[0]: raise PatternError('Wrong number of vertices') self.vertices[:, 1] = val - def __init__(self, - vertices: ArrayLike, - width: float = 0.0, - *, - cap: PathCap = PathCap.Flush, - cap_extensions: Optional[ArrayLike] = None, - offset: vector2 = (0.0, 0.0), - rotation: float = 0, - mirrored: Sequence[bool] = (False, False), - layer: layer_t = 0, - dose: float = 1.0, - repetition: Optional[Repetition] = None, - annotations: Optional[annotations_t] = None, - locked: bool = False, - raw: bool = False, - ): + def __init__( + self, + vertices: ArrayLike, + width: float = 0.0, + *, + cap: PathCap = PathCap.Flush, + cap_extensions: Optional[ArrayLike] = None, + offset: vector2 = (0.0, 0.0), + rotation: float = 0, + mirrored: Sequence[bool] = (False, False), + layer: layer_t = 0, + dose: float = 1.0, + repetition: Optional[Repetition] = None, + annotations: Optional[annotations_t] = None, + locked: bool = False, + raw: bool = False, + ) -> None: LockableImpl.unlock(self) self._cap_extensions = None # Since .cap setter might access it @@ -197,16 +198,17 @@ class Path(Shape, metaclass=AutoSlots): return new @staticmethod - def travel(travel_pairs: Tuple[Tuple[float, float]], - width: float = 0.0, - cap: PathCap = PathCap.Flush, - cap_extensions: Optional[Tuple[float, float]] = None, - offset: vector2 = (0.0, 0.0), - rotation: float = 0, - mirrored: Sequence[bool] = (False, False), - layer: layer_t = 0, - dose: float = 1.0, - ) -> 'Path': + def travel( + travel_pairs: Tuple[Tuple[float, float]], + width: float = 0.0, + cap: PathCap = PathCap.Flush, + cap_extensions: Optional[Tuple[float, float]] = None, + offset: vector2 = (0.0, 0.0), + rotation: float = 0, + mirrored: Sequence[bool] = (False, False), + layer: layer_t = 0, + dose: float = 1.0, + ) -> 'Path': """ Build a path by specifying the turn angles and travel distances rather than setting the distances directly. @@ -243,10 +245,11 @@ class Path(Shape, metaclass=AutoSlots): offset=offset, rotation=rotation, mirrored=mirrored, layer=layer, dose=dose) - def to_polygons(self, - poly_num_points: int = None, - poly_max_arclen: float = None, - ) -> List['Polygon']: + def to_polygons( + self, + poly_num_points: int = None, + poly_max_arclen: float = None, + ) -> List['Polygon']: extensions = self._calculate_cap_extensions() v = remove_colinear_vertices(self.vertices, closed_path=False) diff --git a/masque/shapes/polygon.py b/masque/shapes/polygon.py index 6bfa4b3..fe24ef4 100644 --- a/masque/shapes/polygon.py +++ b/masque/shapes/polygon.py @@ -34,7 +34,7 @@ class Polygon(Shape, metaclass=AutoSlots): return self._vertices @vertices.setter - def vertices(self, val: ArrayLike): + def vertices(self, val: ArrayLike) -> None: val = numpy.array(val, dtype=float) # TODO document that these might not be copied if len(val.shape) < 2 or val.shape[1] != 2: raise PatternError('Vertices must be an Nx2 array') @@ -51,7 +51,7 @@ class Polygon(Shape, metaclass=AutoSlots): return self.vertices[:, 0] @xs.setter - def xs(self, val: ArrayLike): + def xs(self, val: ArrayLike) -> None: val = numpy.array(val, dtype=float).flatten() if val.size != self.vertices.shape[0]: raise PatternError('Wrong number of vertices') @@ -66,25 +66,26 @@ class Polygon(Shape, metaclass=AutoSlots): return self.vertices[:, 1] @ys.setter - def ys(self, val: ArrayLike): + def ys(self, val: ArrayLike) -> None: val = numpy.array(val, dtype=float).flatten() if val.size != self.vertices.shape[0]: raise PatternError('Wrong number of vertices') self.vertices[:, 1] = val - def __init__(self, - vertices: ArrayLike, - *, - offset: vector2 = (0.0, 0.0), - rotation: float = 0.0, - mirrored: Sequence[bool] = (False, False), - layer: layer_t = 0, - dose: float = 1.0, - repetition: Optional[Repetition] = None, - annotations: Optional[annotations_t] = None, - locked: bool = False, - raw: bool = False, - ): + def __init__( + self, + vertices: ArrayLike, + *, + offset: vector2 = (0.0, 0.0), + rotation: float = 0.0, + mirrored: Sequence[bool] = (False, False), + layer: layer_t = 0, + dose: float = 1.0, + repetition: Optional[Repetition] = None, + annotations: Optional[annotations_t] = None, + locked: bool = False, + raw: bool = False, + ) -> None: LockableImpl.unlock(self) self.identifier = () if raw: @@ -115,14 +116,15 @@ class Polygon(Shape, metaclass=AutoSlots): return new @staticmethod - def square(side_length: float, - *, - rotation: float = 0.0, - offset: vector2 = (0.0, 0.0), - layer: layer_t = 0, - dose: float = 1.0, - repetition: Optional[Repetition] = None, - ) -> 'Polygon': + def square( + side_length: float, + *, + rotation: float = 0.0, + offset: vector2 = (0.0, 0.0), + layer: layer_t = 0, + dose: float = 1.0, + repetition: Optional[Repetition] = None, + ) -> 'Polygon': """ Draw a square given side_length, centered on the origin. @@ -148,15 +150,16 @@ class Polygon(Shape, metaclass=AutoSlots): return poly @staticmethod - def rectangle(lx: float, - ly: float, - *, - rotation: float = 0, - offset: vector2 = (0.0, 0.0), - layer: layer_t = 0, - dose: float = 1.0, - repetition: Optional[Repetition] = None, - ) -> 'Polygon': + def rectangle( + lx: float, + ly: float, + *, + rotation: float = 0, + offset: vector2 = (0.0, 0.0), + layer: layer_t = 0, + dose: float = 1.0, + repetition: Optional[Repetition] = None, + ) -> 'Polygon': """ Draw a rectangle with side lengths lx and ly, centered on the origin. @@ -182,19 +185,20 @@ class Polygon(Shape, metaclass=AutoSlots): return poly @staticmethod - def rect(*, - xmin: Optional[float] = None, - xctr: Optional[float] = None, - xmax: Optional[float] = None, - lx: Optional[float] = None, - ymin: Optional[float] = None, - yctr: Optional[float] = None, - ymax: Optional[float] = None, - ly: Optional[float] = None, - layer: layer_t = 0, - dose: float = 1.0, - repetition: Optional[Repetition] = None, - ) -> 'Polygon': + def rect( + *, + xmin: Optional[float] = None, + xctr: Optional[float] = None, + xmax: Optional[float] = None, + lx: Optional[float] = None, + ymin: Optional[float] = None, + yctr: Optional[float] = None, + ymax: Optional[float] = None, + ly: Optional[float] = None, + layer: layer_t = 0, + dose: float = 1.0, + repetition: Optional[Repetition] = None, + ) -> 'Polygon': """ Draw a rectangle by specifying side/center positions. @@ -282,16 +286,17 @@ class Polygon(Shape, metaclass=AutoSlots): return poly @staticmethod - def octagon(*, - side_length: Optional[float] = None, - inner_radius: Optional[float] = None, - regular: bool = True, - center: vector2 = (0.0, 0.0), - rotation: float = 0.0, - layer: layer_t = 0, - dose: float = 1.0, - repetition: Optional[Repetition] = None, - ) -> 'Polygon': + def octagon( + *, + side_length: Optional[float] = None, + inner_radius: Optional[float] = None, + regular: bool = True, + center: vector2 = (0.0, 0.0), + rotation: float = 0.0, + layer: layer_t = 0, + dose: float = 1.0, + repetition: Optional[Repetition] = None, + ) -> 'Polygon': """ Draw an octagon given one of (side length, inradius, circumradius). @@ -341,10 +346,11 @@ class Polygon(Shape, metaclass=AutoSlots): return poly - def to_polygons(self, - poly_num_points: int = None, # unused - poly_max_arclen: float = None, # unused - ) -> List['Polygon']: + def to_polygons( + self, + poly_num_points: int = None, # unused + poly_max_arclen: float = None, # unused + ) -> List['Polygon']: return [copy.deepcopy(self)] def get_bounds(self) -> numpy.ndarray: diff --git a/masque/shapes/shape.py b/masque/shapes/shape.py index 8dcf465..f903e9e 100644 --- a/masque/shapes/shape.py +++ b/masque/shapes/shape.py @@ -47,10 +47,11 @@ class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable --- Abstract methods ''' @abstractmethod - def to_polygons(self, - num_vertices: Optional[int] = None, - max_arclen: Optional[float] = None, - ) -> List['Polygon']: + def to_polygons( + self, + num_vertices: Optional[int] = None, + max_arclen: Optional[float] = None, + ) -> List['Polygon']: """ Returns a list of polygons which approximate the shape. @@ -93,10 +94,11 @@ class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable ''' ---- Non-abstract methods ''' - def manhattanize_fast(self, - grid_x: ArrayLike, - grid_y: ArrayLike, - ) -> List['Polygon']: + def manhattanize_fast( + self, + grid_x: ArrayLike, + grid_y: ArrayLike, + ) -> List['Polygon']: """ Returns a list of polygons with grid-aligned ("Manhattan") edges approximating the shape. @@ -200,10 +202,11 @@ class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable return manhattan_polygons - def manhattanize(self, - grid_x: ArrayLike, - grid_y: ArrayLike, - ) -> List['Polygon']: + def manhattanize( + self, + grid_x: ArrayLike, + grid_y: ArrayLike, + ) -> List['Polygon']: """ Returns a list of polygons with grid-aligned ("Manhattan") edges approximating the shape. diff --git a/masque/shapes/text.py b/masque/shapes/text.py index 53dd601..9504ce5 100644 --- a/masque/shapes/text.py +++ b/masque/shapes/text.py @@ -35,7 +35,7 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots): return self._string @string.setter - def string(self, val: str): + def string(self, val: str) -> None: self._string = val # Height property @@ -44,7 +44,7 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots): return self._height @height.setter - def height(self, val: float): + def height(self, val: float) -> None: if not is_scalar(val): raise PatternError('Height must be a scalar') self._height = val @@ -55,26 +55,27 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots): return self._mirrored @mirrored.setter - def mirrored(self, val: Sequence[bool]): + def mirrored(self, val: Sequence[bool]) -> None: if is_scalar(val): raise PatternError('Mirrored must be a 2-element list of booleans') self._mirrored = numpy.array(val, dtype=bool, copy=True) - def __init__(self, - string: str, - height: float, - font_path: str, - *, - offset: vector2 = (0.0, 0.0), - rotation: float = 0.0, - mirrored: Tuple[bool, bool] = (False, False), - layer: layer_t = 0, - dose: float = 1.0, - repetition: Optional[Repetition] = None, - annotations: Optional[annotations_t] = None, - locked: bool = False, - raw: bool = False, - ): + def __init__( + self, + string: str, + height: float, + font_path: str, + *, + offset: vector2 = (0.0, 0.0), + rotation: float = 0.0, + mirrored: Tuple[bool, bool] = (False, False), + layer: layer_t = 0, + dose: float = 1.0, + repetition: Optional[Repetition] = None, + annotations: Optional[annotations_t] = None, + locked: bool = False, + raw: bool = False, + ) -> None: LockableImpl.unlock(self) self.identifier = () if raw: @@ -109,10 +110,11 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots): new.set_locked(self.locked) return new - def to_polygons(self, - poly_num_points: Optional[int] = None, # unused - poly_max_arclen: Optional[float] = None, # unused - ) -> List[Polygon]: + def to_polygons( + self, + poly_num_points: Optional[int] = None, # unused + poly_max_arclen: Optional[float] = None, # unused + ) -> List[Polygon]: all_polygons = [] total_advance = 0.0 for char in self.string: @@ -166,10 +168,11 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots): return bounds -def get_char_as_polygons(font_path: str, - char: str, - resolution: float = 48 * 64, - ) -> Tuple[List[List[List[float]]], float]: +def get_char_as_polygons( + font_path: str, + char: str, + resolution: float = 48 * 64, + ) -> Tuple[List[List[List[float]]], float]: from freetype import Face # type: ignore from matplotlib.path import Path # type: ignore diff --git a/masque/subpattern.py b/masque/subpattern.py index 3913b33..9bee4af 100644 --- a/masque/subpattern.py +++ b/masque/subpattern.py @@ -46,19 +46,20 @@ class SubPattern(PositionableImpl, DoseableImpl, RotatableImpl, ScalableImpl, Mi identifier: Tuple[Any, ...] """ Arbitrary identifier, used internally by some `masque` functions. """ - def __init__(self, - pattern: Optional['Pattern'], - *, - offset: vector2 = (0.0, 0.0), - rotation: float = 0.0, - mirrored: Optional[Sequence[bool]] = None, - dose: float = 1.0, - scale: float = 1.0, - repetition: Optional[Repetition] = None, - annotations: Optional[annotations_t] = None, - locked: bool = False, - identifier: Tuple[Any, ...] = (), - ) -> None: + def __init__( + self, + pattern: Optional['Pattern'], + *, + offset: vector2 = (0.0, 0.0), + rotation: float = 0.0, + mirrored: Optional[Sequence[bool]] = None, + dose: float = 1.0, + scale: float = 1.0, + repetition: Optional[Repetition] = None, + annotations: Optional[annotations_t] = None, + locked: bool = False, + identifier: Tuple[Any, ...] = (), + ) -> None: """ Args: pattern: Pattern to reference.