Compare commits
	
		
			5 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cd90571c2f | |||
| 18319e0a0b | |||
| aa5967b58c | |||
| 406550cfd6 | |||
| 3016aec3df | 
@ -22,7 +22,7 @@
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
**Dependencies:**
 | 
			
		||||
* python >=3.11
 | 
			
		||||
* python >=3.10
 | 
			
		||||
* (optional) numpy
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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__
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
@ -344,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.
 | 
			
		||||
@ -564,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`.
 | 
			
		||||
@ -678,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.
 | 
			
		||||
@ -950,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
 | 
			
		||||
@ -1097,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()
 | 
			
		||||
@ -1124,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:
 | 
			
		||||
@ -1194,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]`.
 | 
			
		||||
@ -1221,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}')
 | 
			
		||||
@ -1310,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)
 | 
			
		||||
@ -1342,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
 | 
			
		||||
 | 
			
		||||
@ -1486,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:
 | 
			
		||||
@ -1580,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]]
 | 
			
		||||
@ -1636,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
 | 
			
		||||
@ -1648,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)
 | 
			
		||||
@ -1721,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}]'
 | 
			
		||||
@ -1767,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(
 | 
			
		||||
@ -1829,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)
 | 
			
		||||
@ -1841,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:
 | 
			
		||||
@ -1880,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(
 | 
			
		||||
@ -1912,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:
 | 
			
		||||
@ -2183,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!')
 | 
			
		||||
@ -2205,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})'
 | 
			
		||||
@ -2230,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.
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -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:
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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:
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ classifiers = [
 | 
			
		||||
    "Topic :: Scientific/Engineering",
 | 
			
		||||
    "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
 | 
			
		||||
    ]
 | 
			
		||||
requires-python = ">=3.11"
 | 
			
		||||
requires-python = ">=3.10"
 | 
			
		||||
dynamic = ["version"]
 | 
			
		||||
dependencies = [
 | 
			
		||||
    ]
 | 
			
		||||
@ -53,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"]
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user