diff --git a/README.md b/README.md index d4df9bb..b7aa3d8 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ **Homepage:** https://mpxd.net/code/jan/fatamorgana * [PyPI](https://pypi.org/project/fatamorgana) +* [Github mirror](https://github.com/anewusername/fatamorgana) **Capabilities:** * This package is a work-in-progress and is largely untested -- it works for @@ -21,13 +22,18 @@ ## Installation **Dependencies:** -* python >=3.11 +* python >=3.10 * (optional) numpy Install with pip from PyPi (preferred): ```bash -pip install fatamorgana +pip3 install fatamorgana +``` + +Install directly from git repository: +```bash +pip3 install git+https://mpxd.net/code/jan/fatamorgana.git@release ``` ## Documentation @@ -38,6 +44,7 @@ To read the inline help, import fatamorgana help(fatamorgana.OasisLayout) ``` +The documentation is currently very sparse and I expect to improve it whenever possible! ## Examples diff --git a/fatamorgana/__init__.py b/fatamorgana/__init__.py index 27c9c74..d3bce93 100644 --- a/fatamorgana/__init__.py +++ b/fatamorgana/__init__.py @@ -15,7 +15,7 @@ numpy to speed up reading/writing. Dependencies: - - Python 3.11 or later + - Python 3.8 or later - numpy (optional, faster but no additional functionality) To get started, try: @@ -24,28 +24,17 @@ help(fatamorgana.OasisLayout) ``` """ -from .main import ( - OasisLayout as OasisLayout, - Cell as Cell, - XName as XName, - ) +import pathlib + +from .main import OasisLayout, Cell, XName from .basic import ( - NString as NString, - AString as AString, - Validation as Validation, - OffsetTable as OffsetTable, - OffsetEntry as OffsetEntry, - EOFError as EOFError, - SignedError as SignedError, - InvalidDataError as InvalidDataError, - InvalidRecordError as InvalidRecordError, - UnfilledModalError as UnfilledModalError, - ReuseRepetition as ReuseRepetition, - GridRepetition as GridRepetition, - ArbitraryRepetition as ArbitraryRepetition, + NString, AString, Validation, OffsetTable, OffsetEntry, + EOFError, SignedError, InvalidDataError, InvalidRecordError, + UnfilledModalError, + ReuseRepetition, GridRepetition, ArbitraryRepetition ) __author__ = 'Jan Petykiewicz' -__version__ = '0.13' +__version__ = '0.12' version = __version__ diff --git a/fatamorgana/basic.py b/fatamorgana/basic.py index 70da126..ea96528 100644 --- a/fatamorgana/basic.py +++ b/fatamorgana/basic.py @@ -2,8 +2,7 @@ This module contains all datatypes and parsing/writing functions for all abstractions below the 'record' or 'block' level. """ -from typing import Any, IO, Union, TYPE_CHECKING -from collections.abc import Sequence +from typing import Type, Union, Any, Sequence, IO from fractions import Fraction from enum import Enum import math @@ -21,7 +20,7 @@ except ImportError: ''' Type definitions ''' -real_t = int | float | Fraction +real_t = Union[int, float, Fraction] repetition_t = Union['ReuseRepetition', 'GridRepetition', 'ArbitraryRepetition'] property_value_t = Union[int, bytes, 'AString', 'NString', 'PropStringReference', float, Fraction] bytes_t = bytes @@ -185,7 +184,7 @@ if _USE_NUMPY: byte_arr = _read(stream, 1) return numpy.unpackbits(numpy.frombuffer(byte_arr, dtype=numpy.uint8)) - def _np_write_bool_byte(stream: IO[bytes], bits: tuple[bool | int, ...]) -> int: + def _np_write_bool_byte(stream: IO[bytes], bits: tuple[Union[bool, int], ...]) -> int: """ Pack 8 booleans into a byte, and write it to the stream. @@ -250,7 +249,6 @@ def write_uint(stream: IO[bytes], n: int) -> int: Raises: SignedError: if `n` is negative. """ - n = int(n) if n < 0: raise SignedError(f'uint must be positive: {n}') @@ -296,7 +294,6 @@ def encode_sint(sint: int) -> int: Returns: Unsigned integer encoding for the input. """ - sint = int(sint) return (abs(sint) << 1) | (sint < 0) @@ -346,7 +343,7 @@ def read_bstring(stream: IO[bytes]) -> bytes: return _read(stream, length) -def write_bstring(stream: IO[bytes], bstring: bytes) -> int: +def write_bstring(stream: IO[bytes], bstring: bytes): """ Write a binary string to the stream. See `read_bstring()` for format details. @@ -566,7 +563,7 @@ class NString: """ _string: str - def __init__(self, string_or_bytes: bytes | str) -> None: + def __init__(self, string_or_bytes: Union[bytes, str]) -> None: """ Args: string_or_bytes: Content of the `NString`. @@ -680,7 +677,7 @@ class AString: """ _string: str - def __init__(self, string_or_bytes: bytes | str) -> None: + def __init__(self, string_or_bytes: Union[bytes, str]) -> None: """ Args: string_or_bytes: Content of the AString. @@ -952,7 +949,7 @@ class OctangularDelta: sign = self.octangle & 0x02 > 0 xy[axis] = self.proj_mag * (1 - 2 * sign) return xy - else: # noqa: RET505 + else: yn = (self.octangle & 0x02) > 0 xyn = (self.octangle & 0x01) > 0 ys = 1 - 2 * yn @@ -1099,9 +1096,10 @@ class Delta: """ if self.x == 0 or self.y == 0 or abs(self.x) == abs(self.y): return write_uint(stream, OctangularDelta(self.x, self.y).as_uint() << 1) - size = write_uint(stream, (encode_sint(self.x) << 1) | 0x01) - size += write_uint(stream, encode_sint(self.y)) - return size + else: + size = write_uint(stream, (encode_sint(self.x) << 1) | 0x01) + size += write_uint(stream, encode_sint(self.y)) + return size def __eq__(self, other: Any) -> bool: return hasattr(other, 'as_list') and self.as_list() == other.as_list() @@ -1126,11 +1124,12 @@ def read_repetition(stream: IO[bytes]) -> repetition_t: rtype = read_uint(stream) if rtype == 0: return ReuseRepetition.read(stream, rtype) - if rtype in (1, 2, 3, 8, 9): + elif rtype in (1, 2, 3, 8, 9): return GridRepetition.read(stream, rtype) - if rtype in (4, 5, 6, 7, 10, 11): + elif rtype in (4, 5, 6, 7, 10, 11): return ArbitraryRepetition.read(stream, rtype) - raise InvalidDataError(f'Unexpected repetition type: {rtype}') + else: + raise InvalidDataError(f'Unexpected repetition type: {rtype}') def write_repetition(stream: IO[bytes], repetition: repetition_t) -> int: @@ -1196,8 +1195,7 @@ class GridRepetition: a_vector: Sequence[int], a_count: int, b_vector: Sequence[int] | None = None, - b_count: int | None = None, - ) -> None: + b_count: int | None = None): """ Args: a_vector: First lattice vector, of the form `[x, y]`. @@ -1213,10 +1211,6 @@ class GridRepetition: InvalidDataError: if `b_count` and `b_vector` inputs conflict with each other or if `a_count < 1`. """ - a_count = int(a_count) - if b_count is not None: - b_count = int(b_count) - if b_vector is None or b_count is None: if b_vector is not None or b_count is not None: raise InvalidDataError('Repetition has only one of' @@ -1227,7 +1221,7 @@ class GridRepetition: if b_count < 2: b_count = None b_vector = None - warnings.warn('Removed b_count and b_vector since b_count == 1', stacklevel=2) + warnings.warn('Removed b_count and b_vector since b_count == 1') if a_count < 2: raise InvalidDataError(f'Repetition has too-small a_count: {a_count}') @@ -1316,7 +1310,7 @@ class GridRepetition: size = write_uint(stream, 9) size += write_uint(stream, self.a_count - 2) size += Delta(*self.a_vector).write(stream) - else: # noqa: PLR5501 + else: if self.a_vector[1] == 0 and self.b_vector[0] == 0: size = write_uint(stream, 1) size += write_uint(stream, self.a_count - 2) @@ -1348,7 +1342,7 @@ class GridRepetition: return True if self.b_vector is None or other.b_vector is None: return False - if any(self.b_vector[ii] != other.b_vector[ii] for ii in range(2)): # noqa: SIM103 + if any(self.b_vector[ii] != other.b_vector[ii] for ii in range(2)): return False return True @@ -1492,13 +1486,13 @@ class ArbitraryRepetition: size = write_uint(stream, 10) size += write_uint(stream, len(self.x_displacements) - 1) size += sum(Delta(x, y).write(stream) - for x, y in zip(self.x_displacements, self.y_displacements, strict=True)) + for x, y in zip(self.x_displacements, self.y_displacements)) else: size = write_uint(stream, 11) size += write_uint(stream, len(self.x_displacements) - 1) size += write_uint(stream, gcd) size += sum(Delta(x // gcd, y // gcd).write(stream) - for x, y in zip(self.x_displacements, self.y_displacements, strict=True)) + for x, y in zip(self.x_displacements, self.y_displacements)) return size def __eq__(self, other: Any) -> bool: @@ -1586,7 +1580,7 @@ def read_point_list( assert (dx == 0) or (dy == 0) close_points = [[-dx, -dy]] elif list_type == 3: - assert 0 in (dx, dy) or dx in (dy, -dy) + assert (dx == 0) or (dy == 0) or (dx == dy) or (dx == -dy) close_points = [[-dx, -dy]] else: close_points = [[-dx, -dy]] @@ -1601,7 +1595,7 @@ def read_point_list( def write_point_list( stream: IO[bytes], - points: 'list[Sequence[int]] | NDArray', + points: list[Sequence[int]], fast: bool = False, implicit_closed: bool = True ) -> int: @@ -1624,8 +1618,6 @@ def write_point_list( Number of bytes written. """ # If we're in a hurry, just write the points as arbitrary Deltas - if _USE_NUMPY: - points = numpy.asarray(points, dtype=int) if fast: size = write_uint(stream, 4) size += write_uint(stream, len(points)) @@ -1644,10 +1636,11 @@ def write_point_list( h_first = False v_first = False break - elif point[1] != previous[1] or point[0] == previous[0]: - h_first = False - v_first = False - break + else: + if point[1] != previous[1] or point[0] == previous[0]: + h_first = False + v_first = False + break previous = point # If one of h_first or v_first, write a bunch of 1-deltas @@ -1656,7 +1649,7 @@ def write_point_list( size += write_uint(stream, len(points)) size += sum(write_sint(stream, x + y) for x, y in points) return size - if v_first: + elif v_first: size = write_uint(stream, 1) size += write_uint(stream, len(points)) size += sum(write_sint(stream, x + y) for x, y in points) @@ -1729,20 +1722,19 @@ class PropStringReference: ref: int """ID of the target""" - reference_type: type + reference_type: Type """Type of the target: `bytes`, `NString`, or `AString`""" - def __init__(self, ref: int, ref_type: type) -> None: + def __init__(self, ref: int, ref_type: Type) -> None: """ - Args: - ref: ID number of the target. - ref_type: Type of the target. One of bytes, NString, AString. + :param ref: ID number of the target. + :param ref_type: Type of the target. One of bytes, NString, AString. """ self.ref = ref self.ref_type = ref_type def __eq__(self, other: Any) -> bool: - return isinstance(other, type(self)) and self.ref == other.ref and self.reference_type is other.reference_type + return isinstance(other, type(self)) and self.ref == other.ref and self.reference_type == other.reference_type def __repr__(self) -> str: return f'[{self.ref_type} : {self.ref}]' @@ -1775,33 +1767,34 @@ def read_property_value(stream: IO[bytes]) -> property_value_t: Raises: InvalidDataError: if an invalid type is read. """ - ref_type: type + ref_type: Type prop_type = read_uint(stream) if 0 <= prop_type <= 7: return read_real(stream, prop_type) - if prop_type == 8: + elif prop_type == 8: return read_uint(stream) - if prop_type == 9: + elif prop_type == 9: return read_sint(stream) - if prop_type == 10: + elif prop_type == 10: return AString.read(stream) - if prop_type == 11: + elif prop_type == 11: return read_bstring(stream) - if prop_type == 12: + elif prop_type == 12: return NString.read(stream) - if prop_type == 13: + elif prop_type == 13: ref_type = AString ref = read_uint(stream) return PropStringReference(ref, ref_type) - if prop_type == 14: + elif prop_type == 14: ref_type = bytes ref = read_uint(stream) return PropStringReference(ref, ref_type) - if prop_type == 15: + elif prop_type == 15: ref_type = NString ref = read_uint(stream) return PropStringReference(ref, ref_type) - raise InvalidDataError(f'Invalid property type: {prop_type}') + else: + raise InvalidDataError(f'Invalid property type: {prop_type}') def write_property_value( @@ -1837,7 +1830,7 @@ def write_property_value( else: size = write_uint(stream, 8) size += write_uint(stream, value) - elif isinstance(value, Fraction | float | int): + elif isinstance(value, (Fraction, float, int)): size = write_real(stream, value, force_float32) elif isinstance(value, AString): size = write_uint(stream, 10) @@ -1849,11 +1842,11 @@ def write_property_value( size = write_uint(stream, 12) size += value.write(stream) elif isinstance(value, PropStringReference): - if value.ref_type is AString: + if value.ref_type == AString: size = write_uint(stream, 13) - elif value.ref_type is bytes: + elif value.ref_type == bytes: size = write_uint(stream, 14) - if value.ref_type is AString: + if value.ref_type == AString: size = write_uint(stream, 15) size += write_uint(stream, value.ref) else: @@ -1888,16 +1881,17 @@ def read_interval(stream: IO[bytes]) -> tuple[int | None, int | None]: interval_type = read_uint(stream) if interval_type == 0: return None, None - if interval_type == 1: + elif interval_type == 1: return None, read_uint(stream) - if interval_type == 2: + elif interval_type == 2: return read_uint(stream), None - if interval_type == 3: + elif interval_type == 3: v = read_uint(stream) return v, v - if interval_type == 4: + elif interval_type == 4: return read_uint(stream), read_uint(stream) - raise InvalidDataError(f'Unrecognized interval type: {interval_type}') + else: + raise InvalidDataError(f'Unrecognized interval type: {interval_type}') def write_interval( @@ -1920,15 +1914,18 @@ def write_interval( if min_bound is None: if max_bound is None: return write_uint(stream, 0) - return write_uint(stream, 1) + write_uint(stream, max_bound) - if max_bound is None: - return write_uint(stream, 2) + write_uint(stream, min_bound) - if min_bound == max_bound: - return write_uint(stream, 3) + write_uint(stream, min_bound) - size = write_uint(stream, 4) - size += write_uint(stream, min_bound) - size += write_uint(stream, max_bound) - return size + else: + return write_uint(stream, 1) + write_uint(stream, max_bound) + else: + if max_bound is None: + return write_uint(stream, 2) + write_uint(stream, min_bound) + elif min_bound == max_bound: + return write_uint(stream, 3) + write_uint(stream, min_bound) + else: + size = write_uint(stream, 4) + size += write_uint(stream, min_bound) + size += write_uint(stream, max_bound) + return size class OffsetEntry: @@ -2191,7 +2188,9 @@ class Validation: checksum_type = read_uint(stream) if checksum_type == 0: checksum = None - elif checksum_type in (1, 2): + elif checksum_type == 1: + checksum = read_u32(stream) + elif checksum_type == 2: checksum = read_u32(stream) else: raise InvalidDataError('Invalid validation type!') @@ -2213,13 +2212,14 @@ class Validation: """ if self.checksum_type == 0: return write_uint(stream, 0) - if self.checksum is None: + elif self.checksum is None: raise InvalidDataError(f'Checksum is empty but type is {self.checksum_type}') - if self.checksum_type == 1: + elif self.checksum_type == 1: return write_uint(stream, 1) + write_u32(stream, self.checksum) - if self.checksum_type == 2: + elif self.checksum_type == 2: return write_uint(stream, 2) + write_u32(stream, self.checksum) - raise InvalidDataError(f'Unrecognized checksum type: {self.checksum_type}') + else: + raise InvalidDataError(f'Unrecognized checksum type: {self.checksum_type}') def __repr__(self) -> str: return f'Validation(type: {self.checksum_type} sum: {self.checksum})' @@ -2238,7 +2238,7 @@ def write_magic_bytes(stream: IO[bytes]) -> int: return stream.write(MAGIC_BYTES) -def read_magic_bytes(stream: IO[bytes]) -> None: +def read_magic_bytes(stream: IO[bytes]): """ Read the magic byte sequence from a stream. Raise an `InvalidDataError` if it was not found. diff --git a/fatamorgana/main.py b/fatamorgana/main.py index d0f5c29..1bb7258 100644 --- a/fatamorgana/main.py +++ b/fatamorgana/main.py @@ -3,7 +3,7 @@ This module contains data structures and functions for reading from and writing to whole OASIS layout files, and provides a few additional abstractions for the data contained inside them. """ -from typing import IO +from typing import Type, IO import io import logging @@ -163,10 +163,11 @@ class OasisLayout: """ try: record_id = read_uint(stream) - except EOFError: + except EOFError as e: if file_state.within_cblock: return True - raise + else: + raise e logger.info(f'read_record of type {record_id} at position 0x{stream.tell():x}') @@ -192,7 +193,8 @@ class OasisLayout: if record_id == 1: if file_state.started: raise InvalidRecordError('Duplicate Start record') - file_state.started = True + else: + file_state.started = True if record_id == 2 and file_state.within_cblock: raise InvalidRecordError('End within CBlock') @@ -202,7 +204,7 @@ class OasisLayout: file_state.within_cell = False elif record_id in range(15, 28) or record_id in (32, 33): if not file_state.within_cell: - raise InvalidRecordError('Geometry outside Cell') + raise Exception('Geometry outside Cell') elif record_id in (13, 14): file_state.within_cell = True else: @@ -420,7 +422,7 @@ class Cell: placements: list[records.Placement] | None = None, geometry: list[records.geometry_t] | None = None, ) -> None: - self.name = name if isinstance(name, NString | int) else NString(name) + self.name = name if isinstance(name, (NString, int)) else NString(name) self.properties = [] if properties is None else properties self.placements = [] if placements is None else placements self.geometry = [] if geometry is None else geometry @@ -526,7 +528,7 @@ class XName: # Mapping from record id to record class. -_GEOMETRY: dict[int, type[records.geometry_t]] = { +_GEOMETRY: dict[int, Type[records.geometry_t]] = { 19: records.Text, 20: records.Rectangle, 21: records.Polygon, diff --git a/fatamorgana/records.py b/fatamorgana/records.py index a137b42..3650262 100644 --- a/fatamorgana/records.py +++ b/fatamorgana/records.py @@ -10,8 +10,7 @@ Higher-level code (e.g. monitoring for combinations of records with parse, or code for dealing with nested records in a CBlock) should live in main.py instead. """ -from typing import Any, TypeVar, IO, Union, Protocol -from collections.abc import Sequence +from typing import Union, Sequence, Any, TypeVar, IO from abc import ABCMeta, abstractmethod import copy import math @@ -47,7 +46,8 @@ point_list_t = Sequence[Sequence[int]] class Modals: """ - Modal variables, used to store data about previously-written or -read records. + Modal variables, used to store data about previously-written or + -read records. """ repetition: repetition_t | None = None placement_x: int = 0 @@ -227,15 +227,6 @@ class Record(metaclass=ABCMeta): return f'{self.__class__}: ' + pprint.pformat(self.__dict__) -class HasRepetition(Protocol): - repetition: repetition_t | None - - -class HasXY(Protocol): - x: int | None - y: int | None - - class GeometryMixin(metaclass=ABCMeta): """ Mixin defining common functions for geometry records @@ -283,9 +274,10 @@ def read_refname( """ if not is_present: return None - if is_reference: + elif is_reference: return read_uint(stream) - return NString.read(stream) + else: + return NString.read(stream) def read_refstring( @@ -307,9 +299,10 @@ def read_refstring( """ if not is_present: return None - if is_reference: + elif is_reference: return read_uint(stream) - return AString.read(stream) + else: + return AString.read(stream) class Pad(Record): @@ -474,10 +467,10 @@ class End(Record): self.validation = validation self.offset_table = offset_table - def merge_with_modals(self, modals: Modals) -> None: + def merge_with_modals(self, modals: Modals): pass - def deduplicate_with_modals(self, modals: Modals) -> None: + def deduplicate_with_modals(self, modals: Modals): pass @staticmethod @@ -712,10 +705,10 @@ class PropName(Record): self.nstring = NString(nstring) self.reference_number = reference_number - def merge_with_modals(self, modals: Modals) -> None: + def merge_with_modals(self, modals: Modals): modals.reset() - def deduplicate_with_modals(self, modals: Modals) -> None: + def deduplicate_with_modals(self, modals: Modals): modals.reset() @staticmethod @@ -940,7 +933,7 @@ class Property(Record): is_standard: `True` if this is a standard property. `None` to use modal. Default `None`. """ - if isinstance(name, NString | int) or name is None: + if isinstance(name, (NString, int)) or name is None: self.name = name else: self.name = NString(name) @@ -975,59 +968,61 @@ class Property(Record): record = Property() else: byte = read_byte(stream) # UUUUVCNS - uu = 0x0f & (byte >> 4) - vv = 0x01 & (byte >> 3) - cc = 0x01 & (byte >> 2) - nn = 0x01 & (byte >> 1) - ss = 0x01 & (byte >> 0) + u = 0x0f & (byte >> 4) + v = 0x01 & (byte >> 3) + c = 0x01 & (byte >> 2) + n = 0x01 & (byte >> 1) + s = 0x01 & (byte >> 0) - name = read_refname(stream, cc, nn) - if vv == 0: - if uu < 0x0f: - value_count = uu + name = read_refname(stream, c, n) + if v == 0: + if u < 0x0f: + value_count = u else: value_count = read_uint(stream) values: list[property_value_t] | None = [read_property_value(stream) for _ in range(value_count)] else: values = None -# if uu != 0: +# if u != 0: # logger.warning('Malformed property record header; requested modal' # ' values but had nonzero count. Ignoring count.') - record = Property(name, values, bool(ss)) + record = Property(name, values, bool(s)) logger.debug(f'Record ending at 0x{stream.tell():x}:\n {record}') return record def write(self, stream: IO[bytes]) -> int: if self.is_standard is None and self.values is None and self.name is None: return write_uint(stream, 29) - - if self.is_standard is None: - raise InvalidDataError('Property has value or name, but no is_standard flag!') - - if self.values is not None: - value_count = len(self.values) - vv = 0 - uu = 0x0f if value_count >= 0x0f else value_count else: - vv = 1 - uu = 0 - - cc = self.name is not None - nn = cc and isinstance(self.name, int) - ss = self.is_standard - - size = write_uint(stream, 28) - size += write_byte(stream, (uu << 4) | (vv << 3) | (cc << 2) | (nn << 1) | ss) - if cc: - if nn: - size += write_uint(stream, self.name) # type: ignore + if self.is_standard is None: + raise InvalidDataError('Property has value or name, but no is_standard flag!') + if self.values is not None: + value_count = len(self.values) + v = 0 + if value_count >= 0x0f: + u = 0x0f + else: + u = value_count else: - size += self.name.write(stream) # type: ignore - if not vv: - if uu == 0x0f: - size += write_uint(stream, len(self.values)) # type: ignore - size += sum(write_property_value(stream, pp) for pp in self.values) # type: ignore + v = 1 + u = 0 + + c = self.name is not None + n = c and isinstance(self.name, int) + s = self.is_standard + + size = write_uint(stream, 28) + size += write_byte(stream, (u << 4) | (v << 3) | (c << 2) | (n << 1) | s) + if c: + if n: + size += write_uint(stream, self.name) # type: ignore + else: + size += self.name.write(stream) # type: ignore + if not v: + if u == 0x0f: + size += write_uint(stream, len(self.values)) # type: ignore + size += sum(write_property_value(stream, p) for p in self.values) # type: ignore return size @@ -1207,21 +1202,21 @@ class XGeometry(Record, GeometryMixin): if record_id != 33: raise InvalidDataError(f'Invalid record id for XGeometry: {record_id}') - z0, z1, z2, xx, yy, rr, dd, ll = read_bool_byte(stream) + z0, z1, z2, x, y, r, d, l = read_bool_byte(stream) if z0 or z1 or z2: raise InvalidDataError('Malformed XGeometry header') attribute = read_uint(stream) optional: dict[str, Any] = {} - if ll: + if l: optional['layer'] = read_uint(stream) - if dd: + if d: optional['datatype'] = read_uint(stream) bstring = read_bstring(stream) - if xx: + if x: optional['x'] = read_sint(stream) - if yy: + if y: optional['y'] = read_sint(stream) - if rr: + if r: optional['repetition'] = read_repetition(stream) record = XGeometry(attribute, bstring, **optional) @@ -1229,25 +1224,25 @@ class XGeometry(Record, GeometryMixin): return record def write(self, stream: IO[bytes]) -> int: - xx = self.x is not None - yy = self.y is not None - rr = self.repetition is not None - dd = self.datatype is not None - ll = self.layer is not None + x = self.x is not None + y = self.y is not None + r = self.repetition is not None + d = self.datatype is not None + l = self.layer is not None size = write_uint(stream, 33) - size += write_bool_byte(stream, (0, 0, 0, xx, yy, rr, dd, ll)) + size += write_bool_byte(stream, (0, 0, 0, x, y, r, d, l)) size += write_uint(stream, self.attribute) - if ll: + if l: size += write_uint(stream, self.layer) # type: ignore - if dd: + if d: size += write_uint(stream, self.datatype) # type: ignore size += write_bstring(stream, self.bstring) - if xx: + if x: size += write_sint(stream, self.x) # type: ignore - if yy: + if y: size += write_sint(stream, self.y) # type: ignore - if rr: + if r: size += self.repetition.write(stream) # type: ignore return size @@ -1264,7 +1259,7 @@ class Cell(Record): Args: name: `NString`, or an int specifying a `CellName` reference number. """ - self.name = name if isinstance(name, int | NString) else NString(name) + self.name = name if isinstance(name, (int, NString)) else NString(name) def merge_with_modals(self, modals: Modals) -> None: modals.reset() @@ -1347,7 +1342,7 @@ class Placement(Record): self.flip = flip self.magnification = magnification self.angle = angle - if isinstance(name, int | NString) or name is None: + if isinstance(name, (int, NString)) or name is None: self.name = name else: self.name = NString(name) @@ -1378,25 +1373,25 @@ class Placement(Record): raise InvalidDataError(f'Invalid record id for Placement: {record_id}') #CNXYRAAF (17) or CNXYRMAF (18) - cc, nn, xx, yy, rr, ma0, ma1, flip = read_bool_byte(stream) + c, n, x, y, r, ma0, ma1, flip = read_bool_byte(stream) optional: dict[str, Any] = {} - name = read_refname(stream, cc, nn) + name = read_refname(stream, c, n) if record_id == 17: - aa = int((ma0 << 1) | ma1) + aa = (ma0 << 1) | ma1 optional['angle'] = aa * 90 elif record_id == 18: - mm = ma0 - aa1 = ma1 - if mm: + m = ma0 + a = ma1 + if m: optional['magnification'] = read_real(stream) - if aa1: + if a: optional['angle'] = read_real(stream) - if xx: + if x: optional['x'] = read_sint(stream) - if yy: + if y: optional['y'] = read_sint(stream) - if rr: + if r: optional['repetition'] = read_repetition(stream) record = Placement(flip, name, **optional) @@ -1404,43 +1399,43 @@ class Placement(Record): return record def write(self, stream: IO[bytes]) -> int: - cc = self.name is not None - nn = cc and isinstance(self.name, int) - xx = self.x is not None - yy = self.y is not None - rr = self.repetition is not None - ff = self.flip + c = self.name is not None + n = c and isinstance(self.name, int) + x = self.x is not None + y = self.y is not None + r = self.repetition is not None + f = self.flip if (self.magnification == 1 and self.angle is not None and abs(self.angle % 90.0) < 1e-14): aa = int((self.angle / 90) % 4.0) - bools = (cc, nn, xx, yy, rr, aa & 0b10, aa & 0b01, ff) - mm = False - aq = False + bools = (c, n, x, y, r, aa & 0b10, aa & 0b01, f) + m = False + a = False record_id = 17 else: - mm = self.magnification is not None - aq = self.angle is not None - bools = (cc, nn, xx, yy, rr, mm, aq, ff) + m = self.magnification is not None + a = self.angle is not None + bools = (c, n, x, y, r, m, a, f) record_id = 18 size = write_uint(stream, record_id) size += write_bool_byte(stream, bools) - if cc: - if nn: + if c: + if n: size += write_uint(stream, self.name) # type: ignore else: size += self.name.write(stream) # type: ignore - if mm: + if m: size += write_real(stream, self.magnification) # type: ignore - if aq: + if a: size += write_real(stream, self.angle) # type: ignore - if xx: + if x: size += write_sint(stream, self.x) # type: ignore - if yy: + if y: size += write_sint(stream, self.y) # type: ignore - if rr: + if r: size += self.repetition.write(stream) # type: ignore return size @@ -1483,7 +1478,7 @@ class Text(Record, GeometryMixin): self.x = x self.y = y self.repetition = repetition - if isinstance(string, int | AString) or string is None: + if isinstance(string, (AString, int)) or string is None: self.string = string else: self.string = AString(string) @@ -1511,21 +1506,21 @@ class Text(Record, GeometryMixin): if record_id != 19: raise InvalidDataError(f'Invalid record id for Text: {record_id}') - z0, cc, nn, xx, yy, rr, dd, ll = read_bool_byte(stream) + z0, c, n, x, y, r, d, l = read_bool_byte(stream) if z0: raise InvalidDataError('Malformed Text header') optional: dict[str, Any] = {} - string = read_refstring(stream, cc, nn) - if ll: + string = read_refstring(stream, c, n) + if l: optional['layer'] = read_uint(stream) - if dd: + if d: optional['datatype'] = read_uint(stream) - if xx: + if x: optional['x'] = read_sint(stream) - if yy: + if y: optional['y'] = read_sint(stream) - if rr: + if r: optional['repetition'] = read_repetition(stream) record = Text(string, **optional) @@ -1533,30 +1528,30 @@ class Text(Record, GeometryMixin): return record def write(self, stream: IO[bytes]) -> int: - cc = self.string is not None - nn = cc and isinstance(self.string, int) - xx = self.x is not None - yy = self.y is not None - rr = self.repetition is not None - dd = self.datatype is not None - ll = self.layer is not None + c = self.string is not None + n = c and isinstance(self.string, int) + x = self.x is not None + y = self.y is not None + r = self.repetition is not None + d = self.datatype is not None + l = self.layer is not None size = write_uint(stream, 19) - size += write_bool_byte(stream, (0, cc, nn, xx, yy, rr, dd, ll)) - if cc: - if nn: + size += write_bool_byte(stream, (0, c, n, x, y, r, d, l)) + if c: + if n: size += write_uint(stream, self.string) # type: ignore else: size += self.string.write(stream) # type: ignore - if ll: + if l: size += write_uint(stream, self.layer) # type: ignore - if dd: + if d: size += write_uint(stream, self.datatype) # type: ignore - if xx: + if x: size += write_sint(stream, self.x) # type: ignore - if yy: + if y: size += write_sint(stream, self.y) # type: ignore - if rr: + if r: size += self.repetition.write(stream) # type: ignore return size @@ -1651,51 +1646,51 @@ class Rectangle(Record, GeometryMixin): if record_id != 20: raise InvalidDataError(f'Invalid record id for Rectangle: {record_id}') - is_square, ww, hh, xx, yy, rr, dd, ll = read_bool_byte(stream) + is_square, w, h, x, y, r, d, l = read_bool_byte(stream) optional: dict[str, Any] = {} - if ll: + if l: optional['layer'] = read_uint(stream) - if dd: + if d: optional['datatype'] = read_uint(stream) - if ww: + if w: optional['width'] = read_uint(stream) - if hh: + if h: optional['height'] = read_uint(stream) - if xx: + if x: optional['x'] = read_sint(stream) - if yy: + if y: optional['y'] = read_sint(stream) - if rr: + if r: optional['repetition'] = read_repetition(stream) record = Rectangle(is_square, **optional) logger.debug(f'Record ending at 0x{stream.tell():x}:\n {record}') return record def write(self, stream: IO[bytes]) -> int: - ss = self.is_square - ww = self.width is not None - hh = self.height is not None - xx = self.x is not None - yy = self.y is not None - rr = self.repetition is not None - dd = self.datatype is not None - ll = self.layer is not None + s = self.is_square + w = self.width is not None + h = self.height is not None + x = self.x is not None + y = self.y is not None + r = self.repetition is not None + d = self.datatype is not None + l = self.layer is not None size = write_uint(stream, 20) - size += write_bool_byte(stream, (ss, ww, hh, xx, yy, rr, dd, ll)) - if ll: + size += write_bool_byte(stream, (s, w, h, x, y, r, d, l)) + if l: size += write_uint(stream, self.layer) # type: ignore - if dd: + if d: size += write_uint(stream, self.datatype) # type: ignore - if ww: + if w: size += write_uint(stream, self.width) # type: ignore - if hh: + if h: size += write_uint(stream, self.height) # type: ignore - if xx: + if x: size += write_sint(stream, self.x) # type: ignore - if yy: + if y: size += write_sint(stream, self.y) # type: ignore - if rr: + if r: size += self.repetition.write(stream) # type: ignore return size @@ -1717,8 +1712,8 @@ class Polygon(Record, GeometryMixin): repetition: repetition_t | None point_list: point_list_t | None """ - List of offsets between consecutive vertices, starting from the initial - vertex (x, y): `[[dx0, dy0], [dx1, dy1], ...]`. + List of offsets from the initial vertex (x, y) to the remaining + vertices, `[[dx0, dy0], [dx1, dy1], ...]`. The list is an implicitly closed path, vertices are [int, int]. The initial vertex is located at (x, y) and is not represented in `point_list`. `None` means reuse modal. @@ -1744,8 +1739,9 @@ class Polygon(Record, GeometryMixin): self.point_list = point_list self.properties = [] if properties is None else properties - if point_list is not None and len(point_list) < 2: - warn('Polygon with < 3 points', stacklevel=2) + if point_list is not None: + if len(point_list) < 3: + warn('Polygon with < 3 points') def get_point_list(self) -> point_list_t: return verify_modal(self.point_list) @@ -1769,49 +1765,49 @@ class Polygon(Record, GeometryMixin): if record_id != 21: raise InvalidDataError(f'Invalid record id for Polygon: {record_id}') - z0, z1, pp, xx, yy, rr, dd, ll = read_bool_byte(stream) + z0, z1, p, x, y, r, d, l = read_bool_byte(stream) if z0 or z1: raise InvalidDataError('Invalid polygon header') optional: dict[str, Any] = {} - if ll: + if l: optional['layer'] = read_uint(stream) - if dd: + if d: optional['datatype'] = read_uint(stream) - if pp: + if p: optional['point_list'] = read_point_list(stream, implicit_closed=True) - if xx: + if x: optional['x'] = read_sint(stream) - if yy: + if y: optional['y'] = read_sint(stream) - if rr: + if r: optional['repetition'] = read_repetition(stream) record = Polygon(**optional) logger.debug('Record ending at 0x{stream.tell():x}:\n {record}') return record def write(self, stream: IO[bytes], fast: bool = False) -> int: - pp = self.point_list is not None - xx = self.x is not None - yy = self.y is not None - rr = self.repetition is not None - dd = self.datatype is not None - ll = self.layer is not None + p = self.point_list is not None + x = self.x is not None + y = self.y is not None + r = self.repetition is not None + d = self.datatype is not None + l = self.layer is not None size = write_uint(stream, 21) - size += write_bool_byte(stream, (0, 0, pp, xx, yy, rr, dd, ll)) - if ll: + size += write_bool_byte(stream, (0, 0, p, x, y, r, d, l)) + if l: size += write_uint(stream, self.layer) # type: ignore - if dd: + if d: size += write_uint(stream, self.datatype) # type: ignore - if pp: + if p: size += write_point_list(stream, self.point_list, # type: ignore implicit_closed=True, fast=fast) - if xx: + if x: size += write_sint(stream, self.x) # type: ignore - if yy: + if y: size += write_sint(stream, self.y) # type: ignore - if rr: + if r: size += self.repetition.write(stream) # type: ignore return size @@ -1827,8 +1823,8 @@ class Path(Record, GeometryMixin): repetition: repetition_t | None = None point_list: point_list_t | None = None """ - List of offsets between consecutive vertices, starting from the initial - vertex (x, y): `[[dx0, dy0], [dx1, dy1], ...]`. + List of offsets from the initial vertex (x, y) to the remaining vertices, + `[[dx0, dy0], [dx1, dy1], ...]`. The initial vertex is located at (x, y) and is not represented in `point_list`. Offsets are [int, int]; `None` means reuse modal. """ @@ -1912,15 +1908,15 @@ class Path(Record, GeometryMixin): if record_id != 22: raise InvalidDataError(f'Invalid record id for Path: {record_id}') - ee, ww, pp, xx, yy, rr, dd, ll = read_bool_byte(stream) + e, w, p, x, y, r, d, l = read_bool_byte(stream) optional: dict[str, Any] = {} - if ll: + if l: optional['layer'] = read_uint(stream) - if dd: + if d: optional['datatype'] = read_uint(stream) - if ww: + if w: optional['half_width'] = read_uint(stream) - if ee: + if e: scheme = read_uint(stream) scheme_end = scheme & 0b11 scheme_start = (scheme >> 2) & 0b11 @@ -1928,47 +1924,48 @@ class Path(Record, GeometryMixin): def get_pathext(ext_scheme: int) -> pathextension_t | None: if ext_scheme == 0: return None - if ext_scheme == 1: + elif ext_scheme == 1: return PathExtensionScheme.Flush, None - if ext_scheme == 2: + elif ext_scheme == 2: return PathExtensionScheme.HalfWidth, None - if ext_scheme == 3: + elif ext_scheme == 3: return PathExtensionScheme.Arbitrary, read_sint(stream) - raise InvalidDataError(f'Invalid ext_scheme: {ext_scheme}') + else: + raise InvalidDataError(f'Invalid ext_scheme: {ext_scheme}') optional['extension_start'] = get_pathext(scheme_start) optional['extension_end'] = get_pathext(scheme_end) - if pp: + if p: optional['point_list'] = read_point_list(stream, implicit_closed=False) - if xx: + if x: optional['x'] = read_sint(stream) - if yy: + if y: optional['y'] = read_sint(stream) - if rr: + if r: optional['repetition'] = read_repetition(stream) record = Path(**optional) logger.debug(f'Record ending at 0x{stream.tell():x}:\n {record}') return record def write(self, stream: IO[bytes], fast: bool = False) -> int: - ee = self.extension_start is not None or self.extension_end is not None - ww = self.half_width is not None - pp = self.point_list is not None - xx = self.x is not None - yy = self.y is not None - rr = self.repetition is not None - dd = self.datatype is not None - ll = self.layer is not None + e = self.extension_start is not None or self.extension_end is not None + w = self.half_width is not None + p = self.point_list is not None + x = self.x is not None + y = self.y is not None + r = self.repetition is not None + d = self.datatype is not None + l = self.layer is not None - size = write_uint(stream, 22) - size += write_bool_byte(stream, (ee, ww, pp, xx, yy, rr, dd, ll)) - if ll: + size = write_uint(stream, 21) + size += write_bool_byte(stream, (e, w, p, x, y, r, d, l)) + if l: size += write_uint(stream, self.layer) # type: ignore - if dd: + if d: size += write_uint(stream, self.datatype) # type: ignore - if ww: + if w: size += write_uint(stream, self.half_width) # type: ignore - if ee: + if e: scheme = 0 if self.extension_start is not None: scheme += self.extension_start[0].value << 2 @@ -1979,14 +1976,14 @@ class Path(Record, GeometryMixin): size += write_sint(stream, self.extension_start[1]) # type: ignore if scheme & 0b0011 == 0b0011: size += write_sint(stream, self.extension_end[1]) # type: ignore - if pp: + if p: size += write_point_list(stream, self.point_list, # type: ignore implicit_closed=False, fast=fast) - if xx: + if x: size += write_sint(stream, self.x) # type: ignore - if yy: + if y: size += write_sint(stream, self.y) # type: ignore - if rr: + if r: size += self.repetition.write(stream) # type: ignore return size @@ -2072,8 +2069,9 @@ class Trapezoid(Record, GeometryMixin): if self.is_vertical: if height is not None and delta_b - delta_a > height: raise InvalidDataError(f'Trapezoid: h < delta_b - delta_a ({height} < {delta_b} - {delta_a})') - elif width is not None and delta_b - delta_a > width: - raise InvalidDataError(f'Trapezoid: w < delta_b - delta_a ({width} < {delta_b} - {delta_a})') + else: + if width is not None and delta_b - delta_a > width: + raise InvalidDataError(f'Trapezoid: w < delta_b - delta_a ({width} < {delta_b} - {delta_a})') def get_is_vertical(self) -> bool: return verify_modal(self.is_vertical) @@ -2111,39 +2109,39 @@ class Trapezoid(Record, GeometryMixin): if record_id not in (23, 24, 25): raise InvalidDataError(f'Invalid record id for Trapezoid: {record_id}') - is_vertical, ww, hh, xx, yy, rr, dd, ll = read_bool_byte(stream) + is_vertical, w, h, x, y, r, d, l = read_bool_byte(stream) optional: dict[str, Any] = {} - if ll: + if l: optional['layer'] = read_uint(stream) - if dd: + if d: optional['datatype'] = read_uint(stream) - if ww: + if w: optional['width'] = read_uint(stream) - if hh: + if h: optional['height'] = read_uint(stream) if record_id != 25: optional['delta_a'] = read_sint(stream) if record_id != 24: optional['delta_b'] = read_sint(stream) - if xx: + if x: optional['x'] = read_sint(stream) - if yy: + if y: optional['y'] = read_sint(stream) - if rr: + if r: optional['repetition'] = read_repetition(stream) record = Trapezoid(bool(is_vertical), **optional) logger.debug(f'Record ending at 0x{stream.tell():x}:\n {record}') return record def write(self, stream: IO[bytes]) -> int: - vv = self.is_vertical - ww = self.width is not None - hh = self.height is not None - xx = self.x is not None - yy = self.y is not None - rr = self.repetition is not None - dd = self.datatype is not None - ll = self.layer is not None + v = self.is_vertical + w = self.width is not None + h = self.height is not None + x = self.x is not None + y = self.y is not None + r = self.repetition is not None + d = self.datatype is not None + l = self.layer is not None if self.delta_b == 0: record_id = 24 @@ -2152,24 +2150,24 @@ class Trapezoid(Record, GeometryMixin): else: record_id = 23 size = write_uint(stream, record_id) - size += write_bool_byte(stream, (vv, ww, hh, xx, yy, rr, dd, ll)) - if ll: + size += write_bool_byte(stream, (v, w, h, x, y, r, d, l)) + if l: size += write_uint(stream, self.layer) # type: ignore - if dd: + if d: size += write_uint(stream, self.datatype) # type: ignore - if ww: + if w: size += write_uint(stream, self.width) # type: ignore - if hh: + if h: size += write_uint(stream, self.height) # type: ignore if record_id != 25: size += write_sint(stream, self.delta_a) # type: ignore if record_id != 24: size += write_sint(stream, self.delta_b) # type: ignore - if xx: + if x: size += write_sint(stream, self.x) # type: ignore - if yy: + if y: size += write_sint(stream, self.y) # type: ignore - if rr: + if r: size += self.repetition.write(stream) # type: ignore return size @@ -2334,55 +2332,55 @@ class CTrapezoid(Record, GeometryMixin): if record_id != 26: raise InvalidDataError(f'Invalid record id for CTrapezoid: {record_id}') - tt, ww, hh, xx, yy, rr, dd, ll = read_bool_byte(stream) + t, w, h, x, y, r, d, l = read_bool_byte(stream) optional: dict[str, Any] = {} - if ll: + if l: optional['layer'] = read_uint(stream) - if dd: + if d: optional['datatype'] = read_uint(stream) - if tt: + if t: optional['ctrapezoid_type'] = read_uint(stream) - if ww: + if w: optional['width'] = read_uint(stream) - if hh: + if h: optional['height'] = read_uint(stream) - if xx: + if x: optional['x'] = read_sint(stream) - if yy: + if y: optional['y'] = read_sint(stream) - if rr: + if r: optional['repetition'] = read_repetition(stream) record = CTrapezoid(**optional) logger.debug(f'Record ending at 0x{stream.tell():x}:\n {record}') return record def write(self, stream: IO[bytes]) -> int: - tt = self.ctrapezoid_type is not None - ww = self.width is not None - hh = self.height is not None - xx = self.x is not None - yy = self.y is not None - rr = self.repetition is not None - dd = self.datatype is not None - ll = self.layer is not None + t = self.ctrapezoid_type is not None + w = self.width is not None + h = self.height is not None + x = self.x is not None + y = self.y is not None + r = self.repetition is not None + d = self.datatype is not None + l = self.layer is not None size = write_uint(stream, 26) - size += write_bool_byte(stream, (tt, ww, hh, xx, yy, rr, dd, ll)) - if ll: + size += write_bool_byte(stream, (t, w, h, x, y, r, d, l)) + if l: size += write_uint(stream, self.layer) # type: ignore - if dd: + if d: size += write_uint(stream, self.datatype) # type: ignore - if tt: + if t: size += write_uint(stream, self.ctrapezoid_type) # type: ignore - if ww: + if w: size += write_uint(stream, self.width) # type: ignore - if hh: + if h: size += write_uint(stream, self.height) # type: ignore - if xx: + if x: size += write_sint(stream, self.x) # type: ignore - if yy: + if y: size += write_sint(stream, self.y) # type: ignore - if rr: + if r: size += self.repetition.write(stream) # type: ignore return size @@ -2397,7 +2395,7 @@ class CTrapezoid(Record, GeometryMixin): raise InvalidDataError(f'CTrapezoid has spurious height entry: {height}') if width is not None and height is not None: - if ctrapezoid_type in range(0, 4) and width < height: # noqa: PIE808 + if ctrapezoid_type in range(0, 4) and width < height: raise InvalidDataError(f'CTrapezoid has width < height ({width} < {height})') if ctrapezoid_type in range(4, 8) and width < 2 * height: raise InvalidDataError(f'CTrapezoid has width < 2*height ({width} < 2 * {height})') @@ -2406,7 +2404,7 @@ class CTrapezoid(Record, GeometryMixin): if ctrapezoid_type in range(12, 16) and 2 * width > height: raise InvalidDataError(f'CTrapezoid has 2*width > height ({width} > 2 * {height})') - if ctrapezoid_type is not None and ctrapezoid_type not in range(0, 26): # noqa: PIE808 + if ctrapezoid_type is not None and ctrapezoid_type not in range(0, 26): raise InvalidDataError(f'CTrapezoid has invalid type: {ctrapezoid_type}') @@ -2475,53 +2473,53 @@ class Circle(Record, GeometryMixin): if record_id != 27: raise InvalidDataError(f'Invalid record id for Circle: {record_id}') - z0, z1, has_radius, xx, yy, rr, dd, ll = read_bool_byte(stream) + z0, z1, has_radius, x, y, r, d, l = read_bool_byte(stream) if z0 or z1: raise InvalidDataError('Malformed circle header') optional: dict[str, Any] = {} - if ll: + if l: optional['layer'] = read_uint(stream) - if dd: + if d: optional['datatype'] = read_uint(stream) if has_radius: optional['radius'] = read_uint(stream) - if xx: + if x: optional['x'] = read_sint(stream) - if yy: + if y: optional['y'] = read_sint(stream) - if rr: + if r: optional['repetition'] = read_repetition(stream) record = Circle(**optional) logger.debug(f'Record ending at 0x{stream.tell():x}:\n {record}') return record def write(self, stream: IO[bytes]) -> int: - ss = self.radius is not None - xx = self.x is not None - yy = self.y is not None - rr = self.repetition is not None - dd = self.datatype is not None - ll = self.layer is not None + s = self.radius is not None + x = self.x is not None + y = self.y is not None + r = self.repetition is not None + d = self.datatype is not None + l = self.layer is not None size = write_uint(stream, 27) - size += write_bool_byte(stream, (0, 0, ss, xx, yy, rr, dd, ll)) - if ll: + size += write_bool_byte(stream, (0, 0, s, x, y, r, d, l)) + if l: size += write_uint(stream, self.layer) # type: ignore - if dd: + if d: size += write_uint(stream, self.datatype) # type: ignore - if ss: + if s: size += write_uint(stream, self.radius) # type: ignore - if xx: + if x: size += write_sint(stream, self.x) # type: ignore - if yy: + if y: size += write_sint(stream, self.y) # type: ignore - if rr: + if r: size += self.repetition.write(stream) # type: ignore return size -def adjust_repetition(record: HasRepetition, modals: Modals) -> None: +def adjust_repetition(record, modals: Modals) -> None: """ Merge the record's repetition entry with the one in the modals @@ -2537,12 +2535,13 @@ def adjust_repetition(record: HasRepetition, modals: Modals) -> None: if isinstance(record.repetition, ReuseRepetition): if modals.repetition is None: raise InvalidDataError('Unfillable repetition') - record.repetition = copy.copy(modals.repetition) + else: + record.repetition = copy.copy(modals.repetition) else: modals.repetition = copy.copy(record.repetition) -def adjust_field(record: Record, r_field: str, modals: Modals, m_field: str) -> None: +def adjust_field(record, r_field: str, modals: Modals, m_field: str) -> None: """ Merge `record.r_field` with `modals.m_field` @@ -2566,7 +2565,7 @@ def adjust_field(record: Record, r_field: str, modals: Modals, m_field: str) -> raise InvalidDataError(f'Unfillable field: {m_field}') -def adjust_coordinates(record: HasXY, modals: Modals, mx_field: str, my_field: str) -> None: +def adjust_coordinates(record, modals: Modals, mx_field: str, my_field: str) -> None: """ Merge `record.x` and `record.y` with `modals.mx_field` and `modals.my_field`, taking into account the value of `modals.xy_relative`. @@ -2600,7 +2599,7 @@ def adjust_coordinates(record: HasXY, modals: Modals, mx_field: str, my_field: s # TODO: Clarify the docs on the dedup_* functions -def dedup_repetition(record: HasRepetition, modals: Modals) -> None: +def dedup_repetition(record, modals: Modals) -> None: """ Deduplicate the record's repetition entry with the one in the modals. Update the one in the modals if they are different. @@ -2627,7 +2626,7 @@ def dedup_repetition(record: HasRepetition, modals: Modals) -> None: modals.repetition = record.repetition -def dedup_field(record: Record, r_field: str, modals: Modals, m_field: str) -> None: +def dedup_field(record, r_field: str, modals: Modals, m_field: str) -> None: """ Deduplicate `record.r_field` using `modals.m_field` Update the `modals.m_field` if they are different. @@ -2641,26 +2640,26 @@ def dedup_field(record: Record, r_field: str, modals: Modals, m_field: str) -> N Args: InvalidDataError: if both fields are `None` """ - rr = getattr(record, r_field) - mm = getattr(modals, m_field) - if rr is not None: + r = getattr(record, r_field) + m = getattr(modals, m_field) + if r is not None: if m_field in ('polygon_point_list', 'path_point_list'): if _USE_NUMPY: - equal = numpy.array_equal(mm, rr) + equal = numpy.array_equal(m, r) else: - equal = (mm is not None) and all(tuple(mmm) == tuple(rrr) for mmm, rrr in zip(mm, rr, strict=True)) + equal = (m is not None) and all(tuple(mm) == tuple(rr) for mm, rr in zip(m, r)) else: - equal = (mm is not None) and mm == rr + equal = (m is not None) and m == r if equal: setattr(record, r_field, None) else: - setattr(modals, m_field, rr) - elif mm is None: + setattr(modals, m_field, r) + elif m is None: raise InvalidDataError('Unfillable field') -def dedup_coordinates(record: HasXY, modals: Modals, mx_field: str, my_field: str) -> None: +def dedup_coordinates(record, modals: Modals, mx_field: str, my_field: str) -> None: """ Deduplicate `record.x` and `record.y` using `modals.mx_field` and `modals.my_field`, taking into account the value of `modals.xy_relative`. @@ -2683,18 +2682,20 @@ def dedup_coordinates(record: HasXY, modals: Modals, mx_field: str, my_field: st if modals.xy_relative: record.x -= mx setattr(modals, mx_field, record.x) - elif record.x == mx: - record.x = None else: - setattr(modals, mx_field, record.x) + if record.x == mx: + record.x = None + else: + setattr(modals, mx_field, record.x) if record.y is not None: my = getattr(modals, my_field) if modals.xy_relative: record.y -= my setattr(modals, my_field, record.y) - elif record.y == my: - record.y = None else: - setattr(modals, my_field, record.y) + if record.y == my: + record.y = None + else: + setattr(modals, my_field, record.y) diff --git a/fatamorgana/test/build_testfiles.py b/fatamorgana/test/build_testfiles.py index 50511ba..5bb7525 100644 --- a/fatamorgana/test/build_testfiles.py +++ b/fatamorgana/test/build_testfiles.py @@ -1,10 +1,8 @@ -""" +''' Build files equivalent to the test cases used by KLayout. -""" +''' -from typing import IO -from collections.abc import Callable -from pathlib import Path +from typing import Callable, IO from . import ( @@ -17,8 +15,8 @@ from . import ( def build_file(num: str, func: Callable[[IO[bytes]], IO[bytes]]) -> None: - with Path('t' + num + '.oas').open('wb') as ff: - func(ff) + with open('t' + num + '.oas', 'wb') as f: + func(f) def write_all_files() -> None: diff --git a/fatamorgana/test/test_files_cblocks.py b/fatamorgana/test/test_files_cblocks.py index 18c76fa..3f02e93 100644 --- a/fatamorgana/test/test_files_cblocks.py +++ b/fatamorgana/test/test_files_cblocks.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr" +# type: ignore from typing import IO from io import BytesIO @@ -27,8 +27,8 @@ def base_tests(layout: OasisLayout) -> None: def write_file_1(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) diff --git a/fatamorgana/test/test_files_cells.py b/fatamorgana/test/test_files_cells.py index ba286a7..59bc6d4 100644 --- a/fatamorgana/test/test_files_cells.py +++ b/fatamorgana/test/test_files_cells.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr" +# type: ignore from typing import IO from io import BytesIO @@ -24,9 +24,9 @@ def base_tests(layout: OasisLayout) -> None: def write_file_1(buf: IO[bytes]) -> IO[bytes]: - """ + ''' Single cell with explicit name 'XYZ' - """ + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) @@ -49,9 +49,9 @@ def test_file_1() -> None: def write_file_2(buf: IO[bytes]) -> IO[bytes]: - """ + ''' Two cellnames ('XYZ', 'ABC') and two cells with name references. - """ + ''' buf.write(HEADER) write_uint(buf, 3) # CELLNAME record (implicit id 0) @@ -86,9 +86,9 @@ def test_file_2() -> None: def write_file_3(buf: IO[bytes]) -> IO[bytes]: - """ + ''' Invalid file, contains a mix of explicit and implicit cellnames - """ + ''' buf.write(HEADER) write_uint(buf, 4) # CELLNAME record (explicit id) @@ -113,13 +113,13 @@ def test_file_3() -> None: buf.seek(0) with pytest.raises(InvalidRecordError): - _layout = OasisLayout.read(buf) + layout = OasisLayout.read(buf) def write_file_4(buf: IO[bytes]) -> IO[bytes]: - """ + ''' Two cells referencing two names with explicit ids (unsorted) - """ + ''' buf.write(HEADER) write_uint(buf, 4) # CELLNAME record (explicit id) @@ -156,9 +156,9 @@ def test_file_4() -> None: def write_file_5(buf: IO[bytes]) -> IO[bytes]: - """ + ''' Reference to non-existent cell name. - """ + ''' buf.write(HEADER) write_uint(buf, 4) # CELLNAME record (explicit id) @@ -197,9 +197,9 @@ def test_file_5() -> None: def write_file_6(buf: IO[bytes]) -> IO[bytes]: - """ + ''' Cellname with invalid n-string. - """ + ''' buf.write(HEADER) write_uint(buf, 4) # CELLNAME record (explicit id) @@ -226,7 +226,7 @@ def test_file_6() -> None: buf.seek(0) with pytest.raises(InvalidDataError): - _layout = OasisLayout.read(buf) + layout = OasisLayout.read(buf) #base_tests(layout) #assert len(layout.cellnames) == 2 @@ -238,9 +238,9 @@ def test_file_6() -> None: def write_file_7(buf: IO[bytes]) -> IO[bytes]: - """ + ''' Unused cellname. - """ + ''' buf.write(HEADER) write_uint(buf, 4) # CELLNAME record (explicit id) diff --git a/fatamorgana/test/test_files_circles.py b/fatamorgana/test/test_files_circles.py index 39e54a0..f29b18f 100644 --- a/fatamorgana/test/test_files_circles.py +++ b/fatamorgana/test/test_files_circles.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr" +# type: ignore from typing import IO from io import BytesIO @@ -25,8 +25,8 @@ def base_tests(layout: OasisLayout) -> None: def write_file_1(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) diff --git a/fatamorgana/test/test_files_ctrapezoids.py b/fatamorgana/test/test_files_ctrapezoids.py index 37fc80f..095909f 100644 --- a/fatamorgana/test/test_files_ctrapezoids.py +++ b/fatamorgana/test/test_files_ctrapezoids.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr" +# type: ignore from typing import IO from io import BytesIO @@ -21,8 +21,8 @@ def base_tests(layout: OasisLayout) -> None: def write_file_1(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) @@ -61,7 +61,7 @@ def write_file_1(buf: IO[bytes]) -> IO[bytes]: + [0b11, 0b10] ) - for t, (x, x_en) in enumerate(zip(wh, wh_en, strict=True)): + for t, (x, x_en) in enumerate(zip(wh, wh_en)): write_uint(buf, 26) # CTRAPEZOID record write_byte(buf, 0b1000_1011 | (x_en << 5)) # TWHX_YRDL write_uint(buf, 1) # layer @@ -135,12 +135,13 @@ def test_file_1() -> None: assert gg.width == [250, None][is_ctrapz], msg elif ct_type in range(22, 24) or ct_type == 25: assert gg.height == [100, None][is_ctrapz], msg - elif ct_type < 8 or 16 <= ct_type < 25 or ct_type >= 26: - assert gg.width == 250, msg - assert gg.height == 100, msg else: - assert gg.width == 100, msg - assert gg.height == 250, msg + if ct_type < 8 or 16 <= ct_type < 25 or 26 <= ct_type: + assert gg.width == 250, msg + assert gg.height == 100, msg + else: + assert gg.width == 100, msg + assert gg.height == 250, msg elif ii < 3 and ii % 2: assert gg.ctrapezoid_type == 24, msg elif ii == 55: @@ -153,8 +154,8 @@ def test_file_1() -> None: def write_file_2(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) diff --git a/fatamorgana/test/test_files_empty.py b/fatamorgana/test/test_files_empty.py index a0fdbee..8554c28 100644 --- a/fatamorgana/test/test_files_empty.py +++ b/fatamorgana/test/test_files_empty.py @@ -1,3 +1,4 @@ +# type: ignore from typing import IO from io import BytesIO import struct @@ -22,11 +23,11 @@ def base_tests(layout: OasisLayout) -> None: def write_file_1(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File contains one PAD record. 1000 units/micron Offset table inside START. - """ + ''' buf.write(MAGIC_BYTES) write_uint(buf, 1) # START record @@ -55,11 +56,11 @@ def test_file_1() -> None: def write_file_2(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File contains no records. 1/2 unit/micron Offset table inside START. - """ + ''' buf.write(MAGIC_BYTES) write_uint(buf, 1) # START record @@ -86,11 +87,11 @@ def test_file_2() -> None: def write_file_3(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File contains no records. 10/4 unit/micron Offset table inside START. - """ + ''' buf.write(MAGIC_BYTES) write_uint(buf, 1) # START record @@ -118,11 +119,11 @@ def test_file_3() -> None: def write_file_4(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File contains no records. 12.5 unit/micron (float32) Offset table inside START. - """ + ''' buf.write(MAGIC_BYTES) write_uint(buf, 1) # START record @@ -149,11 +150,11 @@ def test_file_4() -> None: def write_file_5(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File contains no records. 12.5 unit/micron (float64) Offset table inside START. - """ + ''' buf.write(MAGIC_BYTES) write_uint(buf, 1) # START record diff --git a/fatamorgana/test/test_files_layernames.py b/fatamorgana/test/test_files_layernames.py index 72e9af6..d430aaf 100644 --- a/fatamorgana/test/test_files_layernames.py +++ b/fatamorgana/test/test_files_layernames.py @@ -1,5 +1,5 @@ -from typing import IO -from collections.abc import Sequence +# type: ignore +from typing import Sequence, IO from io import BytesIO @@ -27,7 +27,7 @@ def base_tests(layout: OasisLayout) -> None: assert not layout.cellnames assert len(layout.cells) == 1 - assert layout.cells[0].name.string == 'A' # type: ignore + assert layout.cells[0].name.string == 'A' assert not layout.cells[0].properties @@ -207,8 +207,8 @@ def elem_test_text(geometry: Sequence) -> None: def write_file_1(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_names_geom(buf) @@ -237,8 +237,8 @@ def test_file_1() -> None: def write_file_2(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_names_text(buf) @@ -267,8 +267,8 @@ def test_file_2() -> None: def write_file_3(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_names_text(buf, prefix=b'T') write_names_geom(buf, short=True) @@ -283,8 +283,8 @@ def write_file_3(buf: IO[bytes]) -> IO[bytes]: def write_file_4(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) diff --git a/fatamorgana/test/test_files_modals.py b/fatamorgana/test/test_files_modals.py index fddc576..9eac770 100644 --- a/fatamorgana/test/test_files_modals.py +++ b/fatamorgana/test/test_files_modals.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr" +# type: ignore from typing import IO from io import BytesIO @@ -21,8 +21,8 @@ def base_tests(layout: OasisLayout) -> None: def write_file_1(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) diff --git a/fatamorgana/test/test_files_paths.py b/fatamorgana/test/test_files_paths.py index 1dc1def..3243a79 100644 --- a/fatamorgana/test/test_files_paths.py +++ b/fatamorgana/test/test_files_paths.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr" +# type: ignore from typing import IO from io import BytesIO @@ -27,8 +27,8 @@ def base_tests(layout: OasisLayout) -> None: def write_file_1(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) @@ -182,7 +182,7 @@ def test_file_1() -> None: else: assert gg.half_width == 12, msg - assert len(gg.point_list) == 3, msg # type: ignore + assert len(gg.point_list) == 3, msg assert_equal(gg.point_list, [[150, 0], [0, 50], [-50, 0]], err_msg=msg) if ii >= 4: diff --git a/fatamorgana/test/test_files_placements.py b/fatamorgana/test/test_files_placements.py index ce4c8aa..290ffee 100644 --- a/fatamorgana/test/test_files_placements.py +++ b/fatamorgana/test/test_files_placements.py @@ -1,5 +1,5 @@ -# mypy: disable-error-code="union-attr" -from typing import IO, cast +# type: ignore +from typing import Tuple, IO, cast, List from io import BytesIO from numpy.testing import assert_equal @@ -22,7 +22,7 @@ def base_tests(layout: OasisLayout) -> None: assert not layout.layers -def write_rectangle(buf: IO[bytes], pos: tuple[int, int] = (300, -400)) -> None: +def write_rectangle(buf: IO[bytes], pos: Tuple[int, int] = (300, -400)) -> None: write_uint(buf, 20) # RECTANGLE record write_byte(buf, 0b0111_1011) # SWHX_YRDL write_uint(buf, 1) # layer @@ -34,8 +34,8 @@ def write_rectangle(buf: IO[bytes], pos: tuple[int, int] = (300, -400)) -> None: def write_file_1(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) @@ -174,7 +174,7 @@ def test_file_1() -> None: assert not layout.cells[1].properties assert not layout.cells[1].geometry - geometry = cast(list[Rectangle], layout.cells[0].geometry) + geometry = cast(List[Rectangle], layout.cells[0].geometry) assert len(geometry) == 1 assert geometry[0].layer == 1 assert geometry[0].datatype == 2 @@ -202,7 +202,7 @@ def test_file_1() -> None: if ii < 3: assert pp.y == 400 * (ii + 1), msg - elif ii >= 7: + elif 7 <= ii: assert pp.y == 0, msg if ii < 4 or ii == 5: @@ -214,7 +214,7 @@ def test_file_1() -> None: assert pp.angle == 0, msg elif ii in (5, 6): assert pp.angle == 90, msg - elif ii >= 7: + elif 7 <= ii: assert pp.angle == 270, msg if ii < 7: @@ -250,8 +250,8 @@ def test_file_1() -> None: def write_file_common(buf: IO[bytes], variant: int) -> IO[bytes]: - """ - """ + ''' + ''' assert variant in (2, 3, 5, 7), 'Error in test definition!' buf.write(HEADER) @@ -516,8 +516,8 @@ def common_tests(layout: OasisLayout, variant: int) -> None: def write_file_4(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 3) # CELLNAME record (implicit id 0) @@ -595,8 +595,8 @@ def write_file_4(buf: IO[bytes]) -> IO[bytes]: def write_file_6(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) @@ -748,8 +748,8 @@ def test_file_6() -> None: def write_file_8(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) @@ -843,7 +843,7 @@ def test_file_8() -> None: assert not layout.cells[2].properties assert not layout.cells[2].placements - geometry = cast(list[Rectangle], layout.cells[2].geometry) + geometry = cast(List[Rectangle], layout.cells[2].geometry) assert len(geometry) == 1 assert geometry[0].layer == 1 assert geometry[0].datatype == 2 diff --git a/fatamorgana/test/test_files_polygons.py b/fatamorgana/test/test_files_polygons.py index 7ac0102..f89a019 100644 --- a/fatamorgana/test/test_files_polygons.py +++ b/fatamorgana/test/test_files_polygons.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr, arg-type" +# type: ignore from typing import IO from io import BytesIO @@ -107,8 +107,8 @@ def common_tests(layout: OasisLayout) -> None: def write_file_common(buf: IO[bytes], variant: int) -> IO[bytes]: - """ - """ + ''' + ''' assert variant in (1, 3), 'Error in test!!' buf.write(HEADER) @@ -376,8 +376,8 @@ def test_file_1() -> None: def write_file_2(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) @@ -445,7 +445,7 @@ def test_file_3() -> None: for ii, gg in enumerate(geometry): msg = f'Fail on polygon {ii}' assert len(gg.properties) == 1, msg - assert gg.properties[0].name == 0, msg # type: ignore + assert gg.properties[0].name == 0, msg assert len(gg.properties[0].values) == 1, msg - assert gg.properties[0].values[0] * 5 == 1, msg # type: ignore + assert gg.properties[0].values[0] * 5 == 1, msg diff --git a/fatamorgana/test/test_files_properties.py b/fatamorgana/test/test_files_properties.py index 98fdede..99bdc37 100644 --- a/fatamorgana/test/test_files_properties.py +++ b/fatamorgana/test/test_files_properties.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr, index, arg-type" +# type: ignore from typing import IO from io import BytesIO @@ -24,11 +24,11 @@ def base_tests(layout: OasisLayout) -> None: def write_file_common(buf: IO[bytes], variant: int) -> IO[bytes]: - """ - """ + ''' + ''' include_repetitions = variant in (2, 5) - def var_byte(buf: IO[bytes], byte: int) -> None: + def var_byte(buf, byte): if include_repetitions: byte |= 0b0100 write_byte(buf, byte) @@ -355,8 +355,8 @@ def test_file_5() -> None: def write_file_3(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 10) # PROPSTRING (explicit id) @@ -580,8 +580,8 @@ def test_file_3() -> None: def write_file_4_6(buf: IO[bytes], variant: int) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 10) # PROPSTRING (explicit id) @@ -786,8 +786,8 @@ def write_file_4_6(buf: IO[bytes], variant: int) -> IO[bytes]: def test_file_4() -> None: - """ - """ + ''' + ''' buf = write_file_4_6(BytesIO(), 4) buf.seek(0) @@ -823,7 +823,7 @@ def test_file_4() -> None: assert pp.x == [-300, 0, 0, 300, 700, 700, 700, 2000, 4000, 6000, 8000, 10000, 12000][ii], msg assert pp.y == [400, 200, 400, 400, 400, 1400, 2400, 0, 0, 0, 0, 0, 0][ii], msg - if ii == 4 or ii >= 6: + if ii == 4 or 6 <= ii: assert pp.flip, msg else: assert not pp.flip, msg @@ -855,8 +855,8 @@ def test_file_4() -> None: def test_file_6() -> None: - """ - """ + ''' + ''' buf = write_file_4_6(BytesIO(), 6) buf.seek(0) @@ -892,7 +892,7 @@ def test_file_6() -> None: assert pp.x == [-300, 0, 0, 300, 700, 700, 700, 2000, 4000, 6000, 8000, 10000, 12000][ii], msg assert pp.y == [400, 400, 400, 400, 400, 1400, 2400, 0, 0, 0, 0, 0, 0][ii], msg - if ii == 4 or ii >= 6: + if ii == 4 or 6 <= ii: assert pp.flip, msg else: assert not pp.flip, msg @@ -928,8 +928,8 @@ def test_file_6() -> None: def write_file_7_8_9(buf: IO[bytes], variant: int) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 28) # PROPERTY record @@ -1059,20 +1059,20 @@ def test_file_7() -> None: def test_file_8() -> None: - """ - """ + ''' + ''' buf = write_file_7_8_9(BytesIO(), 8) buf.seek(0) with pytest.raises(InvalidDataError): - _layout = OasisLayout.read(buf) + layout = OasisLayout.read(buf) def test_file_9() -> None: - """ - """ + ''' + ''' buf = write_file_7_8_9(BytesIO(), 9) buf.seek(0) with pytest.raises(InvalidDataError): - _layout = OasisLayout.read(buf) + layout = OasisLayout.read(buf) diff --git a/fatamorgana/test/test_files_rectangles.py b/fatamorgana/test/test_files_rectangles.py index 204b9b3..a7ffdc7 100644 --- a/fatamorgana/test/test_files_rectangles.py +++ b/fatamorgana/test/test_files_rectangles.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr" +# type: ignore from typing import IO from io import BytesIO @@ -75,8 +75,8 @@ def base_tests(layout: OasisLayout) -> None: def write_file_common(buf: IO[bytes], variant: int) -> IO[bytes]: - """ - """ + ''' + ''' assert variant in (1, 2), 'Error in test!!' buf.write(HEADER) @@ -267,7 +267,7 @@ def test_file_2() -> None: prop = gg.properties[0] assert prop.name == 0, msg - assert len(prop.values) == 1, msg # type: ignore - assert prop.values[0].numerator == 1, msg # type: ignore - assert prop.values[0].denominator == 5, msg # type: ignore + assert len(prop.values) == 1, msg + assert prop.values[0].numerator == 1, msg + assert prop.values[0].denominator == 5, msg diff --git a/fatamorgana/test/test_files_texts.py b/fatamorgana/test/test_files_texts.py index 8980868..0d10744 100644 --- a/fatamorgana/test/test_files_texts.py +++ b/fatamorgana/test/test_files_texts.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr, index" +# type: ignore from typing import IO from io import BytesIO @@ -30,8 +30,8 @@ def common_tests(layout: OasisLayout) -> None: geometry = layout.cells[0].geometry - assert geometry[0].layer == 1 - assert geometry[0].datatype == 2 + geometry[0].layer == 1 + geometry[0].datatype == 2 for ii, gg in enumerate(geometry[1:]): assert gg.layer == 2, f'textstring #{ii + 1}' assert gg.datatype == 1, f'textstring #{ii + 1}' @@ -100,9 +100,9 @@ def common_tests(layout: OasisLayout) -> None: def write_file_common(buf: IO[bytes], variant: int) -> IO[bytes]: - """ + ''' Single cell with explicit name 'XYZ' - """ + ''' assert variant in (1, 2, 5, 12), 'Error in test!!' buf.write(HEADER) @@ -461,10 +461,10 @@ def test_file_12() -> None: def write_file_3(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File with one textstring with explicit id, and one with an implicit id. Should fail. - """ + ''' buf.write(HEADER) write_uint(buf, 6) # TEXTSTRING record (explicit id) @@ -494,15 +494,15 @@ def test_file_3() -> None: buf.seek(0) with pytest.raises(InvalidRecordError): - _layout = OasisLayout.read(buf) + layout = OasisLayout.read(buf) def write_file_4(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File with a TEXT record that references a non-existent TEXTSTRING TODO add an optional check for valid references - """ + ''' buf.write(HEADER) write_uint(buf, 5) # TEXTSTRING record (implicit id 0) @@ -538,9 +538,9 @@ def test_file_4() -> None: def write_file_6(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File with TEXT record that uses an un-filled modal for the repetition - """ + ''' buf.write(HEADER) write_uint(buf, 5) # TEXTSTRING record (implicit id 0) @@ -567,13 +567,13 @@ def test_file_6() -> None: buf.seek(0) with pytest.raises(InvalidDataError): - _layout = OasisLayout.read(buf) + layout = OasisLayout.read(buf) def write_file_7(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File with TEXT record that uses an un-filled modal for the layer - """ + ''' buf.write(HEADER) write_uint(buf, 5) # TEXTSTRING record (implicit id 0) @@ -598,13 +598,13 @@ def test_file_7() -> None: buf.seek(0) with pytest.raises(InvalidDataError): - _layout = OasisLayout.read(buf) + layout = OasisLayout.read(buf) def write_file_8(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File with TEXT record that uses an un-filled modal for the datatype - """ + ''' buf.write(HEADER) write_uint(buf, 5) # TEXTSTRING record (implicit id 0) @@ -629,13 +629,13 @@ def test_file_8() -> None: buf.seek(0) with pytest.raises(InvalidDataError): - _layout = OasisLayout.read(buf) + layout = OasisLayout.read(buf) def write_file_9(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File with TEXT record that uses a default modal for the x coordinate - """ + ''' buf.write(HEADER) write_uint(buf, 5) # TEXTSTRING record (implicit id 0) @@ -669,9 +669,9 @@ def test_file_9() -> None: def write_file_10(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File with TEXT record that uses a default modal for the y coordinate - """ + ''' buf.write(HEADER) write_uint(buf, 5) # TEXTSTRING record (implicit id 0) @@ -705,9 +705,9 @@ def test_file_10() -> None: def write_file_11(buf: IO[bytes]) -> IO[bytes]: - """ + ''' File with TEXT record that uses an un-filled modal for the text string - """ + ''' buf.write(HEADER) write_uint(buf, 5) # TEXTSTRING record (implicit id 0) @@ -732,4 +732,4 @@ def test_file_11() -> None: buf.seek(0) with pytest.raises(InvalidDataError): - _layout = OasisLayout.read(buf) + layout = OasisLayout.read(buf) diff --git a/fatamorgana/test/test_files_trapezoids.py b/fatamorgana/test/test_files_trapezoids.py index 262f449..cea3123 100644 --- a/fatamorgana/test/test_files_trapezoids.py +++ b/fatamorgana/test/test_files_trapezoids.py @@ -1,4 +1,4 @@ -# mypy: disable-error-code="union-attr" +# type: ignore from typing import IO from io import BytesIO @@ -25,8 +25,8 @@ def base_tests(layout: OasisLayout) -> None: def write_file_1(buf: IO[bytes]) -> IO[bytes]: - """ - """ + ''' + ''' buf.write(HEADER) write_uint(buf, 14) # CELL record (explicit) @@ -209,7 +209,7 @@ def test_file_1() -> None: if ii in (0, 4): assert gg.delta_a == -20, msg - elif ii >= 8: + elif 8 <= ii: assert gg.delta_a == 0, msg else: assert gg.delta_a == 20, msg diff --git a/pyproject.toml b/pyproject.toml index 9c149ed..3e6d0d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,16 +34,17 @@ keywords = [ ] classifiers = [ "Programming Language :: Python :: 3", - "Development Status :: 4 - Beta", + "Development Status :: 3 - Alpha", + "Environment :: Other Environment", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Intended Audience :: Manufacturing", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU Affero General Public License v3", + "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", - "Topic :: File Formats", ] -requires-python = ">=3.11" +requires-python = ">=3.10" dynamic = ["version"] dependencies = [ ] @@ -52,38 +53,4 @@ dependencies = [ path = "fatamorgana/__init__.py" [project.optional-dependencies] -numpy = ["numpy>=1.26"] - - -[tool.ruff] -exclude = [ - ".git", - "dist", - ] -line-length = 145 -indent-width = 4 -lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -lint.select = [ - "NPY", "E", "F", "W", "B", "ANN", "UP", "SLOT", "SIM", "LOG", - "C4", "ISC", "PIE", "PT", "RET", "TCH", "PTH", "INT", - "ARG", "PL", "R", "TRY", - "G010", "G101", "G201", "G202", - "Q002", "Q003", "Q004", - ] -lint.ignore = [ - #"ANN001", # No annotation - "ANN002", # *args - "ANN003", # **kwargs - "ANN401", # Any - "ANN101", # self: Self - "SIM108", # single-line if / else assignment - "RET504", # x=y+z; return x - "PIE790", # unnecessary pass - "ISC003", # non-implicit string concatenation - "C408", # dict(x=y) instead of {'x': y} - "PLR09", # Too many xxx - "PLR2004", # magic number - "PLC0414", # import x as x - "TRY003", # Long exception message - ] - +numpy = ["numpy~=1.21"]