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.
This commit is contained in:
		
							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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user