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:
|
||||