diff --git a/README.md b/README.md index fc5f845..b7aa3d8 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ **fatamorgana** is a Python package for reading and writing OASIS format layout files. **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 @@ -20,7 +22,7 @@ ## Installation **Dependencies:** -* python 3.5 or newer +* python >=3.10 * (optional) numpy diff --git a/fatamorgana/basic.py b/fatamorgana/basic.py index 476e5f6..ea96528 100644 --- a/fatamorgana/basic.py +++ b/fatamorgana/basic.py @@ -2,7 +2,7 @@ This module contains all datatypes and parsing/writing functions for all abstractions below the 'record' or 'block' level. """ -from typing import List, Tuple, Type, Union, Optional, Any, Sequence, IO +from typing import Type, Union, Any, Sequence, IO from fractions import Fraction from enum import Enum import math @@ -132,7 +132,7 @@ def write_byte(stream: IO[bytes], n: int) -> int: return stream.write(bytes((n,))) -def _py_read_bool_byte(stream: IO[bytes]) -> List[bool]: +def _py_read_bool_byte(stream: IO[bytes]) -> list[bool]: """ Read a single byte from the stream, and interpret its bits as a list of 8 booleans. @@ -147,7 +147,7 @@ def _py_read_bool_byte(stream: IO[bytes]) -> List[bool]: bits = [bool((byte >> i) & 0x01) for i in reversed(range(8))] return bits -def _py_write_bool_byte(stream: IO[bytes], bits: Tuple[Union[bool, int], ...]) -> int: +def _py_write_bool_byte(stream: IO[bytes], bits: tuple[bool | int, ...]) -> int: """ Pack 8 booleans into a byte, and write it to the stream. @@ -184,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[Union[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. @@ -457,7 +457,7 @@ def write_float64(stream: IO[bytes], f: float) -> int: return stream.write(b) -def read_real(stream: IO[bytes], real_type: Optional[int] = None) -> real_t: +def read_real(stream: IO[bytes], real_type: int | None = None) -> real_t: """ Read a real number from the stream. @@ -783,13 +783,12 @@ def write_astring(stream: IO[bytes], string: str) -> int: class ManhattanDelta: """ Class representing an axis-aligned ("Manhattan") vector. - - Attributes: - vertical (bool): `True` if aligned along y-axis - value (int): signed length of the vector """ vertical: bool + """`True` if aligned along y-axis""" + value: int + """signed length of the vector""" def __init__(self, x: int, y: int) -> None: """ @@ -810,7 +809,7 @@ class ManhattanDelta: self.vertical = True self.value = y - def as_list(self) -> List[int]: + def as_list(self) -> list[int]: """ Return a list representation of this vector. @@ -892,25 +891,26 @@ class ManhattanDelta: class OctangularDelta: """ Class representing an axis-aligned or 45-degree ("Octangular") vector. - - Attributes: - proj_mag (int): projection of the vector onto the x or y axis (non-zero) - octangle (int): bitfield: - bit 2: 1 if non-axis-aligned (non-Manhattan) - if Manhattan: - bit 1: 1 if direction is negative - bit 0: 1 if direction is y - if non-Manhattan: - bit 1: 1 if in lower half-plane - bit 0: 1 if x==-y - - Resulting directions: - 0: +x, 1: +y, 2: -x, 3: -y, - 4: +x+y, 5: -x+y, - 6: +x-y, 7: -x-y """ proj_mag: int + """projection of the vector onto the x or y axis (non-zero)""" + octangle: int + """ + bitfield: + bit 2: 1 if non-axis-aligned (non-Manhattan) + if Manhattan: + bit 1: 1 if direction is negative + bit 0: 1 if direction is y + if non-Manhattan: + bit 1: 1 if in lower half-plane + bit 0: 1 if x==-y + + Resulting directions: + 0: +x, 1: +y, 2: -x, 3: -y, + 4: +x+y, 5: -x+y, + 6: +x-y, 7: -x-y + """ def __init__(self, x: int, y: int) -> None: """ @@ -936,7 +936,7 @@ class OctangularDelta: else: raise InvalidDataError(f'Non-octangular delta! ({x}, {y})') - def as_list(self) -> List[int]: + def as_list(self) -> list[int]: """ Return a list representation of this vector. @@ -1028,13 +1028,12 @@ class OctangularDelta: class Delta: """ Class representing an arbitrary vector - - Attributes - x (int): x-displacement - y (int): y-displacement """ x: int + """x-displacement""" + y: int + """y-displacement""" def __init__(self, x: int, y: int) -> None: """ @@ -1047,7 +1046,7 @@ class Delta: self.x = x self.y = y - def as_list(self) -> List[int]: + def as_list(self) -> list[int]: """ Return a list representation of this vector. @@ -1168,32 +1167,35 @@ class ReuseRepetition: class GridRepetition: """ - Class representing a repetition entry denoting a 1D or 2D array - of regularly-spaced elements. The spacings are stored as one or - two lattice vectors, and the extent of the grid is stored as the - number of elements along each lattice vector. - - Attributes: - a_vector (Tuple[int, int]): `(xa, ya)` vector specifying a center-to-center - displacement between adjacent elements in the grid. - b_vector (Optional[Tuple[int, int]]): `(xb, yb)`, a second displacement, - present if a 2D grid is being specified. - a_count (int): number of elements (>=1) along the grid axis specified by - `a_vector`. - b_count (Optional[int]): Number of elements (>=1) along the grid axis - specified by `b_vector`, if `b_vector` is not `None`. + A repetition entry denoting a 1D or 2D array of regularly-spaced elements. The + spacings are stored as one or two lattice vectors, and the extent of the grid + is stored as the number of elements along each lattice vector. """ - a_vector: List[int] - b_vector: Optional[List[int]] = None + + a_vector: list[int] + """`(xa, ya)` vector specifying a center-to-center + displacement between adjacent elements in the grid. + """ + + b_vector: list[int] | None = None + """`(xb, yb)`, a second displacement, + present if a 2D grid is being specified. + """ + a_count: int - b_count: Optional[int] = None + """number of elements (>=1) along the grid axis specified by `a_vector`.""" + + b_count: int | None = None + """Number of elements (>=1) along the grid axis + specified by `b_vector`, if `b_vector` is not `None`. + """ def __init__( self, a_vector: Sequence[int], a_count: int, - b_vector: Optional[Sequence[int]] = None, - b_count: Optional[int] = None): + b_vector: Sequence[int] | None = None, + b_count: int | None = None): """ Args: a_vector: First lattice vector, of the form `[x, y]`. @@ -1245,8 +1247,8 @@ class GridRepetition: Raises: InvalidDataError: if `repetition_type` is invalid. """ - nb: Optional[int] - b_vector: Optional[List[int]] + nb: int | None + b_vector: list[int] | None if repetition_type == 1: na = read_uint(stream) + 2 nb = read_uint(stream) + 2 @@ -1352,14 +1354,13 @@ class ArbitraryRepetition: """ Class representing a repetition entry denoting a 1D or 2D array of arbitrarily-spaced elements. - - Attributes: - x_displacements (List[int]): x-displacements between consecutive elements - y_displacements (List[int]): y-displacements between consecutive elements """ - x_displacements: List[int] - y_displacements: List[int] + x_displacements: list[int] + """x-displacements between consecutive elements""" + + y_displacements: list[int] + """y-displacements between consecutive elements""" def __init__( self, @@ -1443,7 +1444,7 @@ class ArbitraryRepetition: Returns: Number of bytes written. """ - def get_gcd(vals: List[int]) -> int: + def get_gcd(vals: list[int]) -> int: """ Get the greatest common denominator of a list of ints. """ @@ -1506,7 +1507,7 @@ class ArbitraryRepetition: def read_point_list( stream: IO[bytes], implicit_closed: bool, - ) -> List[List[int]]: + ) -> Sequence[Sequence[int]]: """ Read a point list from a stream. @@ -1594,7 +1595,7 @@ def read_point_list( def write_point_list( stream: IO[bytes], - points: List[Sequence[int]], + points: list[Sequence[int]], fast: bool = False, implicit_closed: bool = True ) -> int: @@ -1655,7 +1656,7 @@ def write_point_list( return size # Try writing a bunch of Manhattan or Octangular deltas - deltas: Union[List[ManhattanDelta], List[OctangularDelta], List[Delta]] + deltas: list[ManhattanDelta] | list[OctangularDelta] | list[Delta] list_type = None try: deltas = [ManhattanDelta(x, y) for x, y in points] @@ -1717,13 +1718,12 @@ def write_point_list( class PropStringReference: """ Reference to a property string. - - Attributes: - ref (int): ID of the target - ref_type (Type): Type of the target: `bytes`, `NString`, or `AString` """ ref: int + """ID of the target""" + reference_type: Type + """Type of the target: `bytes`, `NString`, or `AString`""" def __init__(self, ref: int, ref_type: Type) -> None: """ @@ -1854,7 +1854,7 @@ def write_property_value( return size -def read_interval(stream: IO[bytes]) -> Tuple[Optional[int], Optional[int]]: +def read_interval(stream: IO[bytes]) -> tuple[int | None, int | None]: """ Read an interval from a stream. These are used for storing layer info. @@ -1896,8 +1896,8 @@ def read_interval(stream: IO[bytes]) -> Tuple[Optional[int], Optional[int]]: def write_interval( stream: IO[bytes], - min_bound: Optional[int] = None, - max_bound: Optional[int] = None, + min_bound: int | None = None, + max_bound: int | None = None, ) -> int: """ Write an interval to a stream. @@ -1931,23 +1931,24 @@ def write_interval( class OffsetEntry: """ Entry for the file's offset table. - - Attributes: - strict (bool): If `False`, the records pointed to by this - offset entry may also appear elsewhere in the file. If `True`, all - records of the type pointed to by this offset entry must be present - in a contiuous block at the specified offset [pad records also allowed]. - Additionally: - - All references to strict-mode records must be - explicit (using reference_number). - - The offset may point to an encapsulating CBlock record, if the first - record in that CBlock is of the target record type. A strict modei - table cannot begin in the middle of a CBlock. - offset (int): offset from the start of the file; may be 0 - for records that are not present. """ + strict: bool = False + """ + If `False`, the records pointed to by this offset entry may also appear + elsewhere in the file. If `True`, all records of the type pointed to by + this offset entry must be present in a contiuous block at the specified + offset [pad records also allowed]. + Additionally: + - All references to strict-mode records must be explicit (using + `reference_number`). + - The offset may point to an encapsulating CBlock record, if the first + record in that CBlock is of the target record type. A strict mode + table cannot begin in the middle of a CBlock. + """ + offset: int = 0 + """offset from the start of the file; 0 for records that are not present.""" def __init__(self, strict: bool = False, offset: int = 0) -> None: """ @@ -2006,14 +2007,6 @@ class OffsetTable: XName which are stored in the above order in the file's offset table. - - Attributes: - cellnames (OffsetEntry): Offset for CellNames - textstrings (OffsetEntry): Offset for TextStrings - propnames (OffsetEntry): Offset for PropNames - propstrings (OffsetEntry): Offset for PropStrings - layernames (OffsetEntry): Offset for LayerNames - xnames (OffsetEntry): Offset for XNames """ cellnames: OffsetEntry textstrings: OffsetEntry @@ -2024,12 +2017,12 @@ class OffsetTable: def __init__( self, - cellnames: Optional[OffsetEntry] = None, - textstrings: Optional[OffsetEntry] = None, - propnames: Optional[OffsetEntry] = None, - propstrings: Optional[OffsetEntry] = None, - layernames: Optional[OffsetEntry] = None, - xnames: Optional[OffsetEntry] = None, + cellnames: OffsetEntry | None = None, + textstrings: OffsetEntry | None = None, + propnames: OffsetEntry | None = None, + propstrings: OffsetEntry | None = None, + layernames: OffsetEntry | None = None, + xnames: OffsetEntry | None = None, ) -> None: """ All parameters default to a non-strict entry with offset `0`. @@ -2149,14 +2142,18 @@ class Validation: The checksum is calculated using the entire file, excluding the final 4 bytes (the value of the checksum itself). - Attributes: - checksum_type (int): `0` for no checksum, `1` for crc32, `2` for checksum32 - checksum (Optional[int]): value of the checksum """ checksum_type: int - checksum: Optional[int] = None + """ + `0` for no checksum, + `1` for crc32, + `2` for checksum32, + """ - def __init__(self, checksum_type: int, checksum: Optional[int] = None) -> None: + checksum: int | None = None + """value of the checksum""" + + def __init__(self, checksum_type: int, checksum: int | None = None) -> None: """ Args: checksum_type: 0,1,2 (No checksum, crc32, checksum32) diff --git a/fatamorgana/main.py b/fatamorgana/main.py index 8748d7f..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 List, Dict, Union, Optional, Type, IO +from typing import Type, IO import io import logging @@ -27,20 +27,20 @@ class FileModals: """ File-scoped modal variables """ - cellname_implicit: Optional[bool] = None - propname_implicit: Optional[bool] = None - xname_implicit: Optional[bool] = None - textstring_implicit: Optional[bool] = None - propstring_implicit: Optional[bool] = None + cellname_implicit: bool | None = None + propname_implicit: bool | None = None + xname_implicit: bool | None = None + textstring_implicit: bool | None = None + propstring_implicit: bool | None = None - property_target: List[records.Property] + property_target: list[records.Property] within_cell: bool = False within_cblock: bool = False end_has_offset_table: bool = False started: bool = False - def __init__(self, property_target: List[records.Property]) -> None: + def __init__(self, property_target: list[records.Property]) -> None: self.property_target = property_target @@ -53,46 +53,48 @@ class OasisLayout: record objects. Cells are stored using `Cell` objects (different from `records.Cell` record objects). - - Attributes: - (File properties) - version (AString): Version string ('1.0') - unit (real_t): grid steps per micron - validation (Validation): checksum data - - (Names) - cellnames (Dict[int, CellName]): Cell names - propnames (Dict[int, NString]): Property names - xnames (Dict[int, XName]): Custom names - - (Strings) - textstrings (Dict[int, AString]): Text strings - propstrings (Dict[int, AString]): Property strings - - (Data) - layers (List[records.LayerName]): Layer definitions - properties (List[records.Property]): Property values - cells (List[Cell]): Layout cells """ + # File properties version: AString + """File format version string ('1.0')""" + unit: real_t + """grid steps per micron""" + validation: Validation + """checksum data""" - properties: List[records.Property] - cells: List['Cell'] + # Data + properties: list[records.Property] + """Property values""" - cellnames: Dict[int, 'CellName'] - propnames: Dict[int, NString] - xnames: Dict[int, 'XName'] + cells: list['Cell'] + """Layout cells""" - textstrings: Dict[int, AString] - propstrings: Dict[int, AString] - layers: List[records.LayerName] + layers: list[records.LayerName] + """Layer definitions""" + + # Names + cellnames: dict[int, 'CellName'] + """Cell names""" + + propnames: dict[int, NString] + """Property names""" + + xnames: dict[int, 'XName'] + """Custom names""" + + # String storage + textstrings: dict[int, AString] + """Text strings""" + + propstrings: dict[int, AString] + """Property strings""" def __init__( self, unit: real_t, - validation: Optional[Validation] = None, + validation: Validation | None = None, ) -> None: """ Args: @@ -404,26 +406,21 @@ class OasisLayout: class Cell: """ Representation of an OASIS cell. - - Attributes: - name (Union[NString, int]): name or "CellName reference" number - - properties (List[records.Property]): Properties of this cell - placements (List[records.Placement]): Placement record objects - geometry: (List[records.geometry_t]): Geometry record objectes """ - name: Union[NString, int] - properties: List[records.Property] - placements: List[records.Placement] - geometry: List[records.geometry_t] + name: NString | int + """name or "CellName reference" number""" + + properties: list[records.Property] + placements: list[records.Placement] + geometry: list[records.geometry_t] def __init__( self, - name: Union[NString, str, int], + name: NString | str | int, *, - properties: Optional[List[records.Property]] = None, - placements: Optional[List[records.Placement]] = None, - geometry: Optional[List[records.geometry_t]] = None, + properties: list[records.Property] | None = None, + 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.properties = [] if properties is None else properties @@ -464,12 +461,12 @@ class CellName: with the reference data stripped out. """ nstring: NString - properties: List[records.Property] + properties: list[records.Property] def __init__( self, - nstring: Union[NString, str], - properties: Optional[List[records.Property]] = None, + nstring: NString | str, + properties: list[records.Property] | None = None, ) -> None: """ Args: @@ -531,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 46beb48..3650262 100644 --- a/fatamorgana/records.py +++ b/fatamorgana/records.py @@ -10,7 +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 List, Dict, Tuple, Union, Optional, Sequence, Any, TypeVar, IO +from typing import Union, Sequence, Any, TypeVar, IO from abc import ABCMeta, abstractmethod import copy import math @@ -40,7 +40,7 @@ logger = logging.getLogger(__name__) ''' geometry_t = Union['Text', 'Rectangle', 'Polygon', 'Path', 'Trapezoid', 'CTrapezoid', 'Circle', 'XElement', 'XGeometry'] -pathextension_t = Tuple['PathExtensionScheme', Optional[int]] +pathextension_t = tuple['PathExtensionScheme', int | None] point_list_t = Sequence[Sequence[int]] @@ -49,32 +49,32 @@ class Modals: Modal variables, used to store data about previously-written or -read records. """ - repetition: Optional[repetition_t] = None + repetition: repetition_t | None = None placement_x: int = 0 placement_y: int = 0 - placement_cell: Optional[NString] = None - layer: Optional[int] = None - datatype: Optional[int] = None - text_layer: Optional[int] = None - text_datatype: Optional[int] = None + placement_cell: NString | None = None + layer: int | None = None + datatype: int | None = None + text_layer: int | None = None + text_datatype: int | None = None text_x: int = 0 text_y: int = 0 - text_string: Union[AString, int, None] = None + text_string: AString | int | None = None geometry_x: int = 0 geometry_y: int = 0 xy_relative: bool = False - geometry_w: Optional[int] = None - geometry_h: Optional[int] = None - polygon_point_list: Optional[point_list_t] = None - path_half_width: Optional[int] = None - path_point_list: Optional[point_list_t] = None - path_extension_start: Optional[pathextension_t] = None - path_extension_end: Optional[pathextension_t] = None - ctrapezoid_type: Optional[int] = None - circle_radius: Optional[int] = None - property_value_list: Optional[Sequence[property_value_t]] = None - property_name: Union[int, NString, None] = None - property_is_standard: Optional[bool] = None + geometry_w: int | None = None + geometry_h: int | None = None + polygon_point_list: point_list_t | None = None + path_half_width: int | None = None + path_point_list: point_list_t | None = None + path_extension_start: pathextension_t | None = None + path_extension_end: pathextension_t | None = None + ctrapezoid_type: int | None = None + circle_radius: int | None = None + property_value_list: Sequence[property_value_t] | None = None + property_name: int | NString | None = None + property_is_standard: bool | None = None def __init__(self) -> None: self.reset() @@ -116,17 +116,17 @@ class Modals: T = TypeVar('T') -def verify_modal(var: Optional[T]) -> T: +def verify_modal(var: T | None) -> T: if var is None: raise UnfilledModalError return var -''' - - Records - -''' +# +# +# Records +# +# class Record(metaclass=ABCMeta): """ @@ -231,10 +231,10 @@ class GeometryMixin(metaclass=ABCMeta): """ Mixin defining common functions for geometry records """ - x: Optional[int] - y: Optional[int] - layer: Optional[int] - datatype: Optional[int] + x: int | None + y: int | None + layer: int | None + datatype: int | None def get_x(self) -> int: return verify_modal(self.x) @@ -242,7 +242,7 @@ class GeometryMixin(metaclass=ABCMeta): def get_y(self) -> int: return verify_modal(self.y) - def get_xy(self) -> Tuple[int, int]: + def get_xy(self) -> tuple[int, int]: return (self.get_x(), self.get_y()) def get_layer(self) -> int: @@ -251,15 +251,15 @@ class GeometryMixin(metaclass=ABCMeta): def get_datatype(self) -> int: return verify_modal(self.datatype) - def get_layer_tuple(self) -> Tuple[int, int]: + def get_layer_tuple(self) -> tuple[int, int]: return (self.get_layer(), self.get_datatype()) def read_refname( stream: IO[bytes], - is_present: Union[bool, int], - is_reference: Union[bool, int] - ) -> Union[None, int, NString]: + is_present: bool | int, + is_reference: bool | int, + ) -> int | NString | None: """ Helper function for reading a possibly-absent, possibly-referenced NString. @@ -282,9 +282,9 @@ def read_refname( def read_refstring( stream: IO[bytes], - is_present: Union[bool, int], - is_reference: Union[bool, int], - ) -> Union[None, int, AString]: + is_present: bool | int, + is_reference: bool | int, + ) -> int | AString | None: """ Helper function for reading a possibly-absent, possibly-referenced `AString`. @@ -369,22 +369,21 @@ class XYMode(Record): class Start(Record): """ Start Record (ID 1) - - Attributes: - version (AString): - unit (real_t): positive real number, grid steps per micron - offset_table (Optional[OffsetTable]): If `None` then table must be - placed in the `End` record) """ version: AString + """File format version string""" + unit: real_t - offset_table: Optional[OffsetTable] + """positive real number, grid steps per micron""" + + offset_table: OffsetTable | None + """If `None` then table must be placed in the `End` record""" def __init__( self, unit: real_t, - version: Union[AString, str] = "1.0", - offset_table: Optional[OffsetTable] = None, + version: AString | str = "1.0", + offset_table: OffsetTable | None = None, ) -> None: """ Args @@ -423,7 +422,7 @@ class Start(Record): version = AString.read(stream) unit = read_real(stream) has_offset_table = read_uint(stream) == 0 - offset_table: Optional[OffsetTable] + offset_table: OffsetTable | None if has_offset_table: offset_table = OffsetTable.read(stream) else: @@ -447,19 +446,17 @@ class End(Record): End record (ID 2) The end record is always padded to a total length of 256 bytes. - - Attributes: - offset_table (Optional[OffsetTable]): `None` if offset table was - written into the `Start` record instead - validation (Validation): object containing checksum """ - offset_table: Optional[OffsetTable] + offset_table: OffsetTable | None + """`None` if offset table was written into the `Start` record instead""" + validation: Validation + """object containing checksum""" def __init__( self, validation: Validation, - offset_table: Optional[OffsetTable] = None, + offset_table: OffsetTable | None = None, ) -> None: """ Args: @@ -485,7 +482,7 @@ class End(Record): if record_id != 2: raise InvalidDataError(f'Invalid record id for End {record_id}') if has_offset_table: - offset_table: Optional[OffsetTable] = OffsetTable.read(stream) + offset_table: OffsetTable | None = OffsetTable.read(stream) else: offset_table = None _padding_string = read_bstring(stream) # noqa @@ -514,15 +511,15 @@ class End(Record): class CBlock(Record): """ CBlock (Compressed Block) record (ID 34) - - Attributes: - compression_type (int): `0` for zlib - decompressed_byte_count (int): size after decompressing - compressed_bytes (bytes): compressed data """ compression_type: int + """ `0` for zlib""" + decompressed_byte_count: int + """size after decompressing""" + compressed_bytes: bytes + """compressed data""" def __init__( self, @@ -571,7 +568,7 @@ class CBlock(Record): def from_decompressed( decompressed_bytes: bytes, compression_type: int = 0, - compression_args: Optional[Dict[str, Any]] = None, + compression_args: dict[str, Any] | None = None, ) -> 'CBlock': """ Create a CBlock record from uncompressed data. @@ -600,7 +597,7 @@ class CBlock(Record): return CBlock(compression_type, count, compressed_bytes) - def decompress(self, decompression_args: Optional[Dict[str, Any]] = None) -> bytes: + def decompress(self, decompression_args: dict[str, Any] | None = None) -> bytes: """ Decompress the contents of this CBlock. @@ -630,18 +627,17 @@ class CBlock(Record): class CellName(Record): """ CellName record (ID 3, 4) - - Attributes: - nstring (NString): name - reference_number (Optional[int]): `None` results in implicit assignment """ nstring: NString - reference_number: Optional[int] + """name string""" + + reference_number: int | None + """`None` results in implicit assignment""" def __init__( self, - nstring: Union[NString, str], - reference_number: Optional[int] = None, + nstring: str | NString, + reference_number: int | None = None, ) -> None: """ Args: @@ -667,7 +663,7 @@ class CellName(Record): raise InvalidDataError(f'Invalid record id for CellName {record_id}') nstring = NString.read(stream) if record_id == 4: - reference_number: Optional[int] = read_uint(stream) + reference_number: int | None = read_uint(stream) else: reference_number = None record = CellName(nstring, reference_number) @@ -685,18 +681,17 @@ class CellName(Record): class PropName(Record): """ PropName record (ID 7, 8) - - Attributes: - nstring (NString): name - reference_number (Optional[int]): `None` results in implicit assignment """ nstring: NString - reference_number: Optional[int] = None + """name string""" + + reference_number: int | None = None + """`None` results in implicit assignment""" def __init__( self, - nstring: Union[NString, str], - reference_number: Optional[int] = None, + nstring: str | NString, + reference_number: int | None = None, ) -> None: """ Args: @@ -722,7 +717,7 @@ class PropName(Record): raise InvalidDataError(f'Invalid record id for PropName {record_id}') nstring = NString.read(stream) if record_id == 8: - reference_number: Optional[int] = read_uint(stream) + reference_number: int | None = read_uint(stream) else: reference_number = None record = PropName(nstring, reference_number) @@ -741,18 +736,17 @@ class PropName(Record): class TextString(Record): """ TextString record (ID 5, 6) - - Attributes: - astring (AString): string data - reference_number (Optional[int]): `None` results in implicit assignment """ astring: AString - reference_number: Optional[int] = None + """string contents""" + + reference_number: int | None = None + """`None` results in implicit assignment""" def __init__( self, - string: Union[AString, str], - reference_number: Optional[int] = None, + string: AString | str, + reference_number: int | None = None, ) -> None: """ Args: @@ -778,7 +772,7 @@ class TextString(Record): raise InvalidDataError(f'Invalid record id for TextString: {record_id}') astring = AString.read(stream) if record_id == 6: - reference_number: Optional[int] = read_uint(stream) + reference_number: int | None = read_uint(stream) else: reference_number = None record = TextString(astring, reference_number) @@ -797,18 +791,17 @@ class TextString(Record): class PropString(Record): """ PropString record (ID 9, 10) - - Attributes: - astring (AString): string data - reference_number (Optional[int]): `None` results in implicit assignment """ astring: AString - reference_number: Optional[int] + """string contents""" + + reference_number: int | None + """`None` results in implicit assignment""" def __init__( self, - string: Union[AString, str], - reference_number: Optional[int] = None, + string: AString | str, + reference_number: int | None = None, ) -> None: """ Args: @@ -834,7 +827,7 @@ class PropString(Record): raise InvalidDataError(f'Invalid record id for PropString: {record_id}') astring = AString.read(stream) if record_id == 10: - reference_number: Optional[int] = read_uint(stream) + reference_number: int | None = read_uint(stream) else: reference_number = None record = PropString(astring, reference_number) @@ -853,32 +846,31 @@ class PropString(Record): class LayerName(Record): """ LayerName record (ID 11, 12) - - Attributes: - nstring (NString): name - layer_interval (Tuple[Optional[int], Optional[int]]): bounds on the interval - type_interval (Tuple[Optional[int], Optional[int]]): bounds on the interval - is_textlayer (bool): Is this a text layer? """ nstring: NString - layer_interval: Tuple[Optional[int], Optional[int]] - type_interval: Tuple[Optional[int], Optional[int]] + """name string""" + + layer_interval: tuple[int | None, int | None] + """bounds on the interval""" + + type_interval: tuple[int | None, int | None] + """bounds on the interval""" + is_textlayer: bool + """Is this a text layer?""" def __init__( self, - nstring: Union[NString, str], - layer_interval: Tuple[Optional[int], Optional[int]], - type_interval: Tuple[Optional[int], Optional[int]], + nstring: str | NString, + layer_interval: tuple[int | None, int | None], + type_interval: tuple[int | None, int | None], is_textlayer: bool, ) -> None: """ Args: nstring: The layer name. - layer_interval: Tuple (int or None, int or None) giving bounds - (or lack of thereof) on the layer number. - type_interval: Tuple (int or None, int or None) giving bounds - (or lack of thereof) on the type number. + layer_interval: Tuple giving bounds (or lack of thereof) on the layer number. + type_interval: Tuple giving bounds (or lack of thereof) on the type number. is_textlayer: `True` if the layer is a text layer. """ if isinstance(nstring, NString): @@ -919,22 +911,18 @@ class LayerName(Record): class Property(Record): """ LayerName record (ID 28, 29) - - Attributes: - name (Union[NString, int, None]): `int` is an explicit reference, - `None` is a flag to use Modal) - values (Optional[List[property_value_t]]): List of property values. - is_standard (bool): Whether this is a standard property. """ - name: Optional[Union[NString, int]] - values: Optional[List[property_value_t]] - is_standard: Optional[bool] + name: NString | int | None + """`int` is an explicit reference, `None` is a flag to use Modal""" + values: list[property_value_t] | None + is_standard: bool | None + """Whether this is a standard property.""" def __init__( self, - name: Union[NString, str, int, None] = None, - values: Optional[List[property_value_t]] = None, - is_standard: Optional[bool] = None, + name: NString | str | int | None = None, + values: list[property_value_t] | None= None, + is_standard: bool | None = None, ) -> None: """ Args: @@ -952,10 +940,10 @@ class Property(Record): self.values = values self.is_standard = is_standard - def get_name(self) -> Union[NString, int]: + def get_name(self) -> NString | int: return verify_modal(self.name) # type: ignore - def get_values(self) -> List[property_value_t]: + def get_values(self) -> list[property_value_t]: return verify_modal(self.values) def get_is_standard(self) -> bool: @@ -992,8 +980,8 @@ class Property(Record): value_count = u else: value_count = read_uint(stream) - values: Optional[List[property_value_t]] = [read_property_value(stream) - for _ in range(value_count)] + values: list[property_value_t] | None = [read_property_value(stream) + for _ in range(value_count)] else: values = None # if u != 0: @@ -1041,21 +1029,21 @@ class Property(Record): class XName(Record): """ XName record (ID 30, 31) - - Attributes: - attribute (int): Attribute number - bstring (bytes): XName data - reference_number (Optional[int]): None means to use implicit numbering """ attribute: int + """Attribute number""" + bstring: bytes - reference_number: Optional[int] + """XName data""" + + reference_number: int | None + """None means to use implicit numbering""" def __init__( self, attribute: int, bstring: bytes, - reference_number: Optional[int] = None, + reference_number: int | None = None, ) -> None: """ Args: @@ -1081,7 +1069,7 @@ class XName(Record): attribute = read_uint(stream) bstring = read_bstring(stream) if record_id == 31: - reference_number: Optional[int] = read_uint(stream) + reference_number: int | None = read_uint(stream) else: reference_number = None record = XName(attribute, bstring, reference_number) @@ -1101,20 +1089,20 @@ class XName(Record): class XElement(Record): """ XElement record (ID 32) - - Attributes: - attribute (int): Attribute number. - bstring (bytes): XElement data. """ attribute: int + """Attribute number""" + bstring: bytes - properties: List['Property'] + """XElement data""" + + properties: list['Property'] def __init__( self, attribute: int, bstring: bytes, - properties: Optional[List['Property']] = None, + properties: list['Property'] | None = None, ) -> None: """ Args: @@ -1152,36 +1140,30 @@ class XElement(Record): class XGeometry(Record, GeometryMixin): """ XGeometry record (ID 33) - - Attributes: - attribute (int): Attribute number. - bstring (bytes): XGeometry data. - layer (Optional[int]): None means reuse modal - datatype (Optional[int]): None means reuse modal - x (Optional[int]): None means reuse modal - y (Optional[int]): None means reuse modal - repetition (Optional[repetition_t]): Repetition, if any - properties (List[Property]): List of property records associate with this record. """ attribute: int + """Attribute number""" + bstring: bytes - layer: Optional[int] = None - datatype: Optional[int] = None - x: Optional[int] = None - y: Optional[int] = None - repetition: Optional[repetition_t] = None - properties: List['Property'] + """XGeometry data""" + + layer: int | None = None + datatype: int | None = None + x: int | None = None + y: int | None = None + repetition: repetition_t | None = None + properties: list['Property'] def __init__( self, attribute: int, bstring: bytes, - layer: Optional[int] = None, - datatype: Optional[int] = None, - x: Optional[int] = None, - y: Optional[int] = None, - repetition: Optional[repetition_t] = None, - properties: Optional[List['Property']] = None, + layer: int | None = None, + datatype: int | None = None, + x: int | None = None, + y: int | None = None, + repetition: repetition_t | None = None, + properties: list['Property'] | None = None, ) -> None: """ Args: @@ -1224,7 +1206,7 @@ class XGeometry(Record, GeometryMixin): if z0 or z1 or z2: raise InvalidDataError('Malformed XGeometry header') attribute = read_uint(stream) - optional: Dict[str, Any] = {} + optional: dict[str, Any] = {} if l: optional['layer'] = read_uint(stream) if d: @@ -1268,13 +1250,11 @@ class XGeometry(Record, GeometryMixin): class Cell(Record): """ Cell record (ID 13, 14) - - Attributes: - name (Union[int, NString]): int specifies "CellName reference" number """ - name: Union[int, NString] + name: int | NString + """int specifies "CellName reference" number""" - def __init__(self, name: Union[int, str, NString]) -> None: + def __init__(self, name: int | str | NString) -> None: """ Args: name: `NString`, or an int specifying a `CellName` reference number. @@ -1289,7 +1269,7 @@ class Cell(Record): @staticmethod def read(stream: IO[bytes], record_id: int) -> 'Cell': - name: Union[int, NString] + name: int | NString if record_id == 13: name = read_uint(stream) elif record_id == 14: @@ -1314,37 +1294,34 @@ class Cell(Record): class Placement(Record): """ Placement record (ID 17, 18) - - Attributes: - name (Union[NString, int, None]): name, "CellName reference" - number, or reuse modal - magnification (real_t): Magnification factor - angle (real_t): Rotation, degrees counterclockwise - x (Optional[int]): x-offset, None means reuse modal - y (Optional[int]): y-offset, None means reuse modal - repetition (repetition_t or None): Repetition, if any - flip (bool): Whether to perform reflection about the x-axis. - properties (List[Property]): List of property records associate with this record. """ - name: Union[NString, int, None] = None - magnification: Optional[real_t] = None - angle: Optional[real_t] = None - x: Optional[int] = None - y: Optional[int] = None - repetition: Optional[repetition_t] = None + name: int | NString | None = None + """name, "CellName reference" number, or reuse modal""" + + magnification: real_t | None = None + """magnification factor""" + + angle: real_t | None = None + """Rotation, degrees counterclockwise""" + + x: int | None = None + y: int | None = None + repetition: repetition_t | None = None flip: bool - properties: List['Property'] + """Whether to perform reflection about the x-axis""" + + properties: list['Property'] def __init__( self, flip: bool, - name: Union[NString, str, int, None] = None, - magnification: Optional[real_t] = None, - angle: Optional[real_t] = None, - x: Optional[int] = None, - y: Optional[int] = None, - repetition: Optional[repetition_t] = None, - properties: Optional[List['Property']] = None, + name: NString | str | int | None = None, + magnification: real_t | None = None, + angle: real_t | None = None, + x: int | None = None, + y: int | None = None, + repetition: repetition_t | None = None, + properties: list['Property'] | None = None, ) -> None: """ Args: @@ -1371,7 +1348,7 @@ class Placement(Record): self.name = NString(name) self.properties = [] if properties is None else properties - def get_name(self) -> Union[NString, int]: + def get_name(self) -> NString | int: return verify_modal(self.name) # type: ignore def get_x(self) -> int: @@ -1398,7 +1375,7 @@ class Placement(Record): #CNXYRAAF (17) or CNXYRMAF (18) c, n, x, y, r, ma0, ma1, flip = read_bool_byte(stream) - optional: Dict[str, Any] = {} + optional: dict[str, Any] = {} name = read_refname(stream, c, n) if record_id == 17: aa = (ma0 << 1) | ma1 @@ -1466,33 +1443,24 @@ class Placement(Record): class Text(Record, GeometryMixin): """ Text record (ID 19) - - Attributes: - string (Union[AString, int, None]): None means reuse modal - layer (Optiona[int]): None means reuse modal - datatype (Optional[int]): None means reuse modal - x (Optional[int]): x-offset, None means reuse modal - y (Optional[int]): y-offset, None means reuse modal - repetition (Optional[repetition_t]): Repetition, if any - properties (List[Property]): List of property records associate with this record. """ - string: Optional[Union[AString, int]] = None - layer: Optional[int] = None - datatype: Optional[int] = None - x: Optional[int] = None - y: Optional[int] = None - repetition: Optional[repetition_t] = None - properties: List['Property'] + string: AString | int | None = None + layer: int | None = None + datatype: int | None = None + x: int | None = None + y: int | None = None + repetition: repetition_t | None = None + properties: list['Property'] def __init__( self, - string: Union[AString, str, int, None] = None, - layer: Optional[int] = None, - datatype: Optional[int] = None, - x: Optional[int] = None, - y: Optional[int] = None, - repetition: Optional[repetition_t] = None, - properties: Optional[List['Property']] = None, + string: AString | str | int | None = None, + layer: int | None = None, + datatype: int | None = None, + x: int | None = None, + y: int | None = None, + repetition: repetition_t | None = None, + properties: list['Property'] | None = None, ) -> None: """ Args: @@ -1516,7 +1484,7 @@ class Text(Record, GeometryMixin): self.string = AString(string) self.properties = [] if properties is None else properties - def get_string(self) -> Union[AString, int]: + def get_string(self) -> AString | int: return verify_modal(self.string) # type: ignore def merge_with_modals(self, modals: Modals) -> None: @@ -1542,7 +1510,7 @@ class Text(Record, GeometryMixin): if z0: raise InvalidDataError('Malformed Text header') - optional: Dict[str, Any] = {} + optional: dict[str, Any] = {} string = read_refstring(stream, c, n) if l: optional['layer'] = read_uint(stream) @@ -1593,43 +1561,43 @@ class Rectangle(Record, GeometryMixin): Rectangle record (ID 20) (x, y) denotes the lower-left (min-x, min-y) corner of the rectangle. - - Attributes: - is_square (bool): `True` if this is a square. - If `True`, `height` must be `None`. - width (Optional[int]): X-width. `None` means reuse modal. - height (Optional[int]): Y-height. Must be `None` if `is_square` is `True`. - If `is_square` is `False`, `None` means reuse modal. - layer (Optional[int]): None means reuse modal - datatype (Optional[int]): None means reuse modal - x (Optional[int]): x-offset of the rectangle's lower-left (min-x) point. - None means reuse modal. - y (Optional[int]): y-offset of the rectangle's lower-left (min-y) point. - None means reuse modal - repetition (Optional[repetition_t]): Repetition, if any. - properties (List[Property]): List of property records associate with this record. """ - layer: Optional[int] - datatype: Optional[int] - width: Optional[int] - height: Optional[int] - x: Optional[int] - y: Optional[int] - repetition: Optional[repetition_t] + layer: int | None + datatype: int | None + width: int | None + """X-width. `None` means reuse modal""" + + height: int | None + """Y-height. Must be `None` if `is_square` is `True`. + If `is_square` is `False`, `None` means reuse modal + """ + + x: int | None + """x-offset of the rectangle's lower-left (min-x) point. + None means reuse modal. + """ + y: int | None + """y-offset of the rectangle's lower-left (min-y) point. + None means reuse modal + """ + + repetition: repetition_t | None is_square: bool - properties: List['Property'] + """If `True`, `height` must be `None`""" + + properties: list['Property'] def __init__( self, is_square: bool = False, - layer: Optional[int] = None, - datatype: Optional[int] = None, - width: Optional[int] = None, - height: Optional[int] = None, - x: Optional[int] = None, - y: Optional[int] = None, - repetition: Optional[repetition_t] = None, - properties: Optional[List['Property']] = None, + layer: int | None = None, + datatype: int | None = None, + width: int | None = None, + height: int | None = None, + x: int | None = None, + y: int | None = None, + repetition: repetition_t | None = None, + properties: list['Property'] | None = None, ) -> None: self.is_square = is_square self.layer = layer @@ -1679,7 +1647,7 @@ class Rectangle(Record, GeometryMixin): raise InvalidDataError(f'Invalid record id for Rectangle: {record_id}') is_square, w, h, x, y, r, d, l = read_bool_byte(stream) - optional: Dict[str, Any] = {} + optional: dict[str, Any] = {} if l: optional['layer'] = read_uint(stream) if d: @@ -1730,42 +1698,38 @@ class Rectangle(Record, GeometryMixin): class Polygon(Record, GeometryMixin): """ Polygon record (ID 21) - - Attributes: - point_list (Optional[point_list_t]): 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. - layer (Optional[int]): Layer number. None means reuse modal - datatype (Optional[int]): Datatype number. None means reuse modal - x (Optional[int]): x-offset of the polygon's first point. - None means reuse modal - y (Optional[int]): y-offset of the polygon's first point. - None means reuse modal - repetition (Optional[repetition_t]): Repetition, if any. - Default no repetition. - properties (List[Property]): List of property records associate with this record. """ - layer: Optional[int] - datatype: Optional[int] - x: Optional[int] - y: Optional[int] - repetition: Optional[repetition_t] - point_list: Optional[point_list_t] - properties: List['Property'] + layer: int | None + datatype: int | None + x: int | None + """x-offset of the polygon's first point. + None means reuse modal + """ + y: int | None + """y-offset of the polygon's first point. + None means reuse modal + """ + repetition: repetition_t | None + point_list: point_list_t | None + """ + 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. + """ + + properties: list['Property'] def __init__( self, - point_list: Optional[point_list_t] = None, - layer: Optional[int] = None, - datatype: Optional[int] = None, - x: Optional[int] = None, - y: Optional[int] = None, - repetition: Optional[repetition_t] = None, - properties: Optional[List['Property']] = None, + point_list: point_list_t | None = None, + layer: int | None = None, + datatype: int | None = None, + x: int | None = None, + y: int | None = None, + repetition: repetition_t | None = None, + properties: list['Property'] | None = None, ) -> None: self.layer = layer self.datatype = datatype @@ -1805,7 +1769,7 @@ class Polygon(Record, GeometryMixin): if z0 or z1: raise InvalidDataError('Invalid polygon header') - optional: Dict[str, Any] = {} + optional: dict[str, Any] = {} if l: optional['layer'] = read_uint(stream) if d: @@ -1851,51 +1815,50 @@ class Polygon(Record, GeometryMixin): class Path(Record, GeometryMixin): """ Polygon record (ID 22) - - Attributes: - point_list (Optional[point_list_t]): 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. - half_width (Optional[int]): None means reuse modal - extension_start (Optional[Tuple]): None means reuse modal. - Tuple is of the form (`PathExtensionScheme`, Optional[int]) - Second value is None unless using `PathExtensionScheme.Arbitrary` - Value determines extension past start point. - extension_end (Optional[Tuple]): Same form as `extension_end`. - Value determines extension past end point. - layer (Optional[int]): None means reuse modal - datatype (Optional[int]): None means reuse modal - x (Optional[int]): x-offset, None means reuse modal - y (Optional[int]): y-offset, None means reuse modal - repetition (Optional[repetition_t]): Repetition, if any - properties (List[Property]): List of property records associate with this record. """ - layer: Optional[int] = None - datatype: Optional[int] = None - x: Optional[int] = None - y: Optional[int] = None - repetition: Optional[repetition_t] = None - point_list: Optional[point_list_t] = None - half_width: Optional[int] = None - extension_start: Optional[pathextension_t] = None - extension_end: Optional[pathextension_t] = None - properties: List['Property'] + layer: int | None = None + datatype: int | None = None + x: int | None = None + y: int | None = None + repetition: repetition_t | None = None + point_list: point_list_t | None = None + """ + 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. + """ + + half_width: int | None = None + """None means reuse modal""" + + extension_start: pathextension_t | None = None + """ + `None` means reuse modal. + Tuple is of the form (`PathExtensionScheme`, int | None) + Second value is None unless using `PathExtensionScheme.Arbitrary` + Value determines extension past start point. + """ + + extension_end: pathextension_t | None = None + """ + Same form as `extension_end`. Value determines extension past end point. + """ + + properties: list['Property'] def __init__( self, - point_list: Optional[point_list_t] = None, - half_width: Optional[int] = None, - extension_start: Optional[pathextension_t] = None, - extension_end: Optional[pathextension_t] = None, - layer: Optional[int] = None, - datatype: Optional[int] = None, - x: Optional[int] = None, - y: Optional[int] = None, - repetition: Optional[repetition_t] = None, - properties: Optional[List['Property']] = None, + point_list: point_list_t | None = None, + half_width: int | None = None, + extension_start: pathextension_t | None = None, + extension_end: pathextension_t | None = None, + layer: int | None = None, + datatype: int | None = None, + x: int | None = None, + y: int | None = None, + repetition: repetition_t | None = None, + properties: list['Property'] | None = None, ) -> None: self.layer = layer self.datatype = datatype @@ -1946,7 +1909,7 @@ class Path(Record, GeometryMixin): raise InvalidDataError(f'Invalid record id for Path: {record_id}') e, w, p, x, y, r, d, l = read_bool_byte(stream) - optional: Dict[str, Any] = {} + optional: dict[str, Any] = {} if l: optional['layer'] = read_uint(stream) if d: @@ -1958,7 +1921,7 @@ class Path(Record, GeometryMixin): scheme_end = scheme & 0b11 scheme_start = (scheme >> 2) & 0b11 - def get_pathext(ext_scheme: int) -> Optional[pathextension_t]: + def get_pathext(ext_scheme: int) -> pathextension_t | None: if ext_scheme == 0: return None elif ext_scheme == 1: @@ -2031,55 +1994,61 @@ class Trapezoid(Record, GeometryMixin): Trapezoid with at least two sides parallel to the x- or y-axis. (x, y) denotes the lower-left (min-x, min-y) corner of the trapezoid's bounding box. - - Attributes: - delta_a (Optional[int]): If horizontal, signed x-distance from top left - vertex to bottom left vertex. If vertical, signed y-distance from - bottom left vertex to bottom right vertex. - None means reuse modal. - delta_b (Optional[int]): If horizontal, signed x-distance from bottom right - vertex to top right vertex. If vertical, signed y-distance from top - right vertex to top left vertex. - None means reuse modal. - is_vertical (bool): `True` if the left and right sides are aligned to - the y-axis. If the trapezoid is a rectangle, either `True` or `False` - can be used. - width (Optional[int]): Bounding box x-width, None means reuse modal. - height (Optional[int]): Bounding box y-height, None means reuse modal. - layer (Optional[int]): None means reuse modal - datatype (Optional[int]): None means reuse modal - x (Optional[int]): x-offset to lower-left corner of the trapezoid's bounding box. - None means reuse modal - y (Optional[int]): y-offset to lower-left corner of the trapezoid's bounding box. - None means reuse modal - repetition (Optional[repetition_t]): Repetition, if any - properties (List[Property]): List of property records associate with this record. """ - layer: Optional[int] = None - datatype: Optional[int] = None - width: Optional[int] = None - height: Optional[int] = None - x: Optional[int] = None - y: Optional[int] = None - repetition: Optional[repetition_t] = None + layer: int | None = None + datatype: int | None = None + width: int | None = None + """Bounding box x-width, None means reuse modal.""" + + height: int | None = None + """Bounding box y-height, None means reuse modal.""" + + x: int | None = None + """x-offset to lower-left corner of the trapezoid's bounding box. + None means reuse modal + """ + + y: int | None = None + """y-offset to lower-left corner of the trapezoid's bounding box. + None means reuse modal + """ + + repetition: repetition_t | None = None delta_a: int = 0 + """ + If horizontal, signed x-distance from top left vertex to bottom left vertex. + If vertical, signed y-distance from bottom left vertex to bottom right vertex. + None means reuse modal. + """ + delta_b: int = 0 + """ + If horizontal, signed x-distance from bottom right vertex to top right vertex. + If vertical, signed y-distance from top right vertex to top left vertex. + None means reuse modal. + """ + is_vertical: bool - properties: List['Property'] + """ + `True` if the left and right sides are aligned to the y-axis. + If the trapezoid is a rectangle, either `True` or `False` can be used. + """ + + properties: list['Property'] def __init__( self, is_vertical: bool, delta_a: int = 0, delta_b: int = 0, - layer: Optional[int] = None, - datatype: Optional[int] = None, - width: Optional[int] = None, - height: Optional[int] = None, - x: Optional[int] = None, - y: Optional[int] = None, - repetition: Optional[repetition_t] = None, - properties: Optional[List['Property']] = None, + layer: int | None = None, + datatype: int | None = None, + width: int | None = None, + height: int | None = None, + x: int | None = None, + y: int | None = None, + repetition: repetition_t | None = None, + properties: list['Property'] | None = None, ) -> None: """ Raises: @@ -2141,7 +2110,7 @@ class Trapezoid(Record, GeometryMixin): raise InvalidDataError(f'Invalid record id for Trapezoid: {record_id}') is_vertical, w, h, x, y, r, d, l = read_bool_byte(stream) - optional: Dict[str, Any] = {} + optional: dict[str, Any] = {} if l: optional['layer'] = read_uint(stream) if d: @@ -2241,44 +2210,45 @@ class CTrapezoid(Record, GeometryMixin): w = h w = 2h w = h set h = None set w = None set h = None - - Attributes: - ctrapezoid_type (Optional[int]): See above for details. - None means reuse modal. - width (Optional[int]): Bounding box x-width. - None means unnecessary, or reuse modal if necessary. - height (Optional[int]): Bounding box y-height. - None means unnecessary, or reuse modal if necessary. - layer (Optional[int]): None means reuse modal - datatype (Optional[int]): None means reuse modal - x (Optional[int]): x-offset of lower-left (min-x) point of bounding box. - None means reuse modal - y (Optional[int]): y-offset of lower-left (min-y) point of bounding box. - None means reuse modal - repetition (Optional[repetition_t]): Repetition, if any - properties (List[Property]): List of property records associate with this record. """ - ctrapezoid_type: Optional[int] = None - layer: Optional[int] = None - datatype: Optional[int] = None - width: Optional[int] = None - height: Optional[int] = None - x: Optional[int] = None - y: Optional[int] = None - repetition: Optional[repetition_t] = None - properties: List['Property'] + ctrapezoid_type: int | None = None + """See class docstring for details. None means reuse modal.""" + + layer: int | None = None + datatype: int | None = None + width: int | None = None + """width: Bounding box x-width + None means unnecessary, or reuse modal if necessary. + """ + + height: int | None = None + """Bounding box y-height. + None means unnecessary, or reuse modal if necessary. + """ + + x: int | None = None + """x-offset of lower-left (min-x) point of bounding box. + None means reuse modal + """ + y: int | None = None + """y-offset of lower-left (min-y) point of bounding box. + None means reuse modal + """ + + repetition: repetition_t | None = None + properties: list['Property'] def __init__( self, - ctrapezoid_type: Optional[int] = None, - layer: Optional[int] = None, - datatype: Optional[int] = None, - width: Optional[int] = None, - height: Optional[int] = None, - x: Optional[int] = None, - y: Optional[int] = None, - repetition: Optional[repetition_t] = None, - properties: Optional[List['Property']] = None, + ctrapezoid_type: int | None = None, + layer: int | None = None, + datatype: int | None = None, + width: int | None = None, + height: int | None = None, + x: int | None = None, + y: int | None = None, + repetition: repetition_t | None = None, + properties: list['Property'] | None = None, ) -> None: """ Raises: @@ -2363,7 +2333,7 @@ class CTrapezoid(Record, GeometryMixin): raise InvalidDataError(f'Invalid record id for CTrapezoid: {record_id}') t, w, h, x, y, r, d, l = read_bool_byte(stream) - optional: Dict[str, Any] = {} + optional: dict[str, Any] = {} if l: optional['layer'] = read_uint(stream) if d: @@ -2441,33 +2411,24 @@ class CTrapezoid(Record, GeometryMixin): class Circle(Record, GeometryMixin): """ Circle record (ID 27) - - Attributes: - radius (Optional[int]): None means reuse modal - layer (Optional[int]): None means reuse modal - datatype (Optional[int]): None means reuse modal - x (Optional[int]): x-offset, None means reuse modal - y (Optional[int]): y-offset, None means reuse modal - repetition (Optional[repetition_t]): Repetition, if any - properties (List[Property]): List of property records associate with this record. """ - layer: Optional[int] - datatype: Optional[int] - x: Optional[int] - y: Optional[int] - repetition: Optional[repetition_t] - radius: Optional[int] - properties: List['Property'] + layer: int | None + datatype: int | None + x: int | None + y: int | None + repetition: repetition_t | None + radius: int | None + properties: list['Property'] def __init__( self, - radius: Optional[int] = None, - layer: Optional[int] = None, - datatype: Optional[int] = None, - x: Optional[int] = None, - y: Optional[int] = None, - repetition: Optional[repetition_t] = None, - properties: Optional[List['Property']] = None, + radius: int | None = None, + layer: int | None = None, + datatype: int | None = None, + x: int | None = None, + y: int | None = None, + repetition: repetition_t | None = None, + properties: list['Property'] | None = None, ) -> None: """ Args: @@ -2516,7 +2477,7 @@ class Circle(Record, GeometryMixin): if z0 or z1: raise InvalidDataError('Malformed circle header') - optional: Dict[str, Any] = {} + optional: dict[str, Any] = {} if l: optional['layer'] = read_uint(stream) if d: diff --git a/pyproject.toml b/pyproject.toml index 146471e..3e6d0d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ classifiers = [ "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", ] -requires-python = ">=3.8" +requires-python = ">=3.10" dynamic = ["version"] dependencies = [ ]