Add UnfilledModalError, records.verify_modal(), and .get_*() methods.

The .get_*() methods are used to verify that we aren't reading from a
pattern with un-filled modals.

The GeometryMixin class was also added here and provides some additional
convenience methods: get_xy() to get an (x,y) tuple and
get_layer_tuple() to get a (layer, datatype) tuple.
lethe/LATEST
Jan Petykiewicz 4 years ago
parent e4a62a0f32
commit 705926d443

@ -29,6 +29,7 @@ import pathlib
from .main import OasisLayout, Cell, XName
from .basic import NString, AString, Validation, OffsetTable, OffsetEntry, \
EOFError, SignedError, InvalidDataError, InvalidRecordError, \
UnfilledModalError, \
ReuseRepetition, GridRepetition, ArbitraryRepetition

@ -59,6 +59,12 @@ class InvalidRecordError(FatamorganaError):
"""
pass
class UnfilledModalError(FatamorganaError):
"""
Attempted to call .get_var(), but var() was None!
"""
pass
class PathExtensionScheme(Enum):
"""
@ -2228,4 +2234,3 @@ def read_magic_bytes(stream: io.BufferedIOBase):
if magic != MAGIC_BYTES:
raise InvalidDataError('Could not read magic bytes, '
'found {!r} : {}'.format(magic, magic.decode()))

@ -11,7 +11,7 @@ Higher-level code (e.g. monitoring for combinations of records with
in main.py instead.
"""
from abc import ABCMeta, abstractmethod
from typing import List, Dict, Tuple, Union, Optional, Sequence, Any
from typing import List, Dict, Tuple, Union, Optional, Sequence, Any, TypeVar
import copy
import math
import zlib
@ -24,7 +24,7 @@ from .basic import AString, NString, repetition_t, property_value_t, real_t, \
read_bstring, read_uint, read_sint, read_real, read_repetition, read_interval, \
write_bstring, write_uint, write_sint, write_real, write_interval, write_point_list, \
write_property_value, read_bool_byte, write_bool_byte, read_byte, write_byte, \
InvalidDataError, PathExtensionScheme, _USE_NUMPY
InvalidDataError, UnfilledModalError, PathExtensionScheme, _USE_NUMPY
if _USE_NUMPY:
import numpy
@ -32,6 +32,7 @@ if _USE_NUMPY:
logger = logging.getLogger(__name__)
'''
Type definitions
'''
@ -112,6 +113,12 @@ class Modals:
self.property_is_standard = None
T = TypeVar('T')
def verify_modal(var: Optional[T]) -> T:
if var is None:
raise UnfilledModalError
return var
'''
Records
@ -217,6 +224,34 @@ class Record(metaclass=ABCMeta):
return '{}: {}'.format(self.__class__, pprint.pformat(self.__dict__))
class GeometryMixin(metaclass=ABCMeta):
"""
Mixin defining common functions for geometry records
"""
x: Optional[int]
y: Optional[int]
layer: Optional[int]
datatype: Optional[int]
def get_x(self) -> int:
return verify_modal(self.x)
def get_y(self) -> int:
return verify_modal(self.y)
def get_xy(self) -> Tuple[int, int]:
return (self.get_x(), self.get_y())
def get_layer(self) -> int:
return verify_modal(self.layer)
def get_datatype(self) -> int:
return verify_modal(self.datatype)
def get_layer_tuple(self) -> Tuple[int, int]:
return (self.get_layer(), self.get_datatype())
def read_refname(stream: io.BufferedIOBase,
is_present: Union[bool, int],
is_reference: Union[bool, int]
@ -910,6 +945,15 @@ class Property(Record):
self.values = values
self.is_standard = is_standard
def get_name(self) -> Union[NString, int]:
return verify_modal(self.name) # type: ignore
def get_values(self) -> List[property_value_t]:
return verify_modal(self.values)
def get_is_standard(self) -> bool:
return verify_modal(self.is_standard)
def merge_with_modals(self, modals: Modals):
adjust_field(self, 'name', modals, 'property_name')
adjust_field(self, 'values', modals, 'property_value_list')
@ -1091,7 +1135,7 @@ class XElement(Record):
return size
class XGeometry(Record):
class XGeometry(Record, GeometryMixin):
"""
XGeometry record (ID 33)
@ -1301,6 +1345,15 @@ class Placement(Record):
else:
self.name = name
def get_name(self) -> Union[NString, int]:
return verify_modal(self.name) # type: ignore
def get_x(self) -> int:
return verify_modal(self.x)
def get_y(self) -> int:
return verify_modal(self.y)
def merge_with_modals(self, modals: Modals):
adjust_coordinates(self, modals, 'placement_x', 'placement_y')
adjust_repetition(self, modals)
@ -1384,7 +1437,7 @@ class Placement(Record):
return size
class Text(Record):
class Text(Record, GeometryMixin):
"""
Text record (ID 19)
@ -1430,6 +1483,9 @@ class Text(Record):
else:
self.string = string
def get_string(self) -> Union[AString, int]:
return verify_modal(self.string) # type: ignore
def merge_with_modals(self, modals: Modals):
adjust_coordinates(self, modals, 'text_x', 'text_y')
adjust_repetition(self, modals)
@ -1500,7 +1556,7 @@ class Text(Record):
return size
class Rectangle(Record):
class Rectangle(Record, GeometryMixin):
"""
Rectangle record (ID 20)
@ -1558,6 +1614,14 @@ class Rectangle(Record):
if is_square and self.height is not None:
raise InvalidDataError('Rectangle is square and also has height')
def get_width(self) -> int:
return verify_modal(self.width)
def get_height(self) -> int:
if self.is_square:
return verify_modal(self.width)
return verify_modal(self.height)
def merge_with_modals(self, modals: Modals):
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
adjust_repetition(self, modals)
@ -1635,7 +1699,7 @@ class Rectangle(Record):
return size
class Polygon(Record):
class Polygon(Record, GeometryMixin):
"""
Polygon record (ID 21)
@ -1685,6 +1749,9 @@ class Polygon(Record):
if len(point_list) < 3:
warn('Polygon with < 3 points')
def get_point_list(self) -> point_list_t:
return verify_modal(self.point_list)
def merge_with_modals(self, modals: Modals):
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
adjust_repetition(self, modals)
@ -1752,7 +1819,7 @@ class Polygon(Record):
return size
class Path(Record):
class Path(Record, GeometryMixin):
"""
Polygon record (ID 22)
@ -1821,6 +1888,18 @@ class Path(Record):
self.extension_start = extension_start
self.extension_end = extension_end
def get_point_list(self) -> point_list_t:
return verify_modal(self.point_list)
def get_half_width(self) -> int:
return verify_modal(self.half_width)
def get_extension_start(self) -> pathextension_t:
return verify_modal(self.extension_start)
def get_extension_end(self) -> pathextension_t:
return verify_modal(self.extension_end)
def merge_with_modals(self, modals: Modals):
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
adjust_repetition(self, modals)
@ -1927,7 +2006,7 @@ class Path(Record):
return size
class Trapezoid(Record):
class Trapezoid(Record, GeometryMixin):
"""
Trapezoid record (ID 23, 24, 25)
@ -2017,6 +2096,21 @@ class Trapezoid(Record):
raise InvalidDataError('Trapezoid: w < delta_b - delta_a'
' ({} < {} - {})'.format(width, delta_b, delta_a))
def get_is_vertical(self) -> bool:
return verify_modal(self.is_vertical)
def get_delta_a(self) -> int:
return verify_modal(self.delta_a)
def get_delta_b(self) -> int:
return verify_modal(self.delta_b)
def get_width(self) -> int:
return verify_modal(self.width)
def get_height(self) -> int:
return verify_modal(self.height)
def merge_with_modals(self, modals: Modals):
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
adjust_repetition(self, modals)
@ -2103,7 +2197,7 @@ class Trapezoid(Record):
# TODO: CTrapezoid type descriptions
class CTrapezoid(Record):
class CTrapezoid(Record, GeometryMixin):
"""
CTrapezoid record (ID 26)
@ -2161,6 +2255,23 @@ class CTrapezoid(Record):
self.check_valid()
def get_ctrapezoid_type(self) -> int:
return verify_modal(self.ctrapezoid_type)
def get_height(self) -> int:
if self.ctrapezoid_type is None:
return verify_modal(self.height)
if self.ctrapezoid_type in (16, 17, 18, 19, 22, 23, 25):
return verify_modal(self.width)
return verify_modal(self.height)
def get_width(self) -> int:
if self.ctrapezoid_type is None:
return verify_modal(self.width)
if self.ctrapezoid_type in (20, 21):
return verify_modal(self.height)
return verify_modal(self.width)
def merge_with_modals(self, modals: Modals):
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
adjust_repetition(self, modals)
@ -2267,7 +2378,6 @@ class CTrapezoid(Record):
size += self.repetition.write(stream) # type: ignore
return size
def check_valid(self):
ctrapezoid_type = self.ctrapezoid_type
width = self.width
@ -2299,7 +2409,7 @@ class CTrapezoid(Record):
'{}'.format(ctrapezoid_type))
class Circle(Record):
class Circle(Record, GeometryMixin):
"""
Circle record (ID 27)
@ -2344,6 +2454,9 @@ class Circle(Record):
self.y = y
self.repetition = repetition
def get_radius(self) -> int:
return verify_modal(self.radius)
def merge_with_modals(self, modals: Modals):
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
adjust_repetition(self, modals)

Loading…
Cancel
Save