Compare commits

...

5 Commits

Author SHA1 Message Date
jan
45f675ca22 use klamath_rs_ext if available 2025-10-26 18:07:07 -07:00
jan
74b9672b4a bump version to v1.5 2025-10-12 23:37:53 -07:00
jan
0ac4c3b275 fix path extensions not getting written correctly 2025-10-12 23:37:53 -07:00
ae9c2d7b5f add type annotation 2025-04-21 19:39:23 -07:00
428e396b9b Loosen type requirements / guarantees for properties
We will still return a dict, but only require a mapping when writing
2025-04-21 19:39:23 -07:00
4 changed files with 24 additions and 12 deletions

View File

@ -36,5 +36,5 @@ from . import (
) )
__author__ = 'Jan Petykiewicz' __author__ = 'Jan Petykiewicz'
__version__ = '1.4' __version__ = '1.5'

View File

@ -17,6 +17,11 @@ logger = logging.getLogger(__name__)
class KlamathError(Exception): class KlamathError(Exception):
pass pass
try:
from klamath_rs_ext import pack_int2, pack_int4
_USE_RS_EXT = True
except ImportError:
_USE_RS_EXT = False
# #
# Parse functions # Parse functions
@ -93,14 +98,14 @@ def pack_bitarray(data: int) -> bytes:
return struct.pack('>H', data) return struct.pack('>H', data)
def pack_int2(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes: def _pack_int2(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes:
arr = numpy.asarray(data) arr = numpy.asarray(data)
if (arr > 32767).any() or (arr < -32768).any(): if (arr > 32767).any() or (arr < -32768).any():
raise KlamathError(f'int2 data out of range: {arr}') raise KlamathError(f'int2 data out of range: {arr}')
return arr.astype('>i2').tobytes() return arr.astype('>i2').tobytes()
def pack_int4(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes: def _pack_int4(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes:
arr = numpy.asarray(data) arr = numpy.asarray(data)
if (arr > 2147483647).any() or (arr < -2147483648).any(): if (arr > 2147483647).any() or (arr < -2147483648).any():
raise KlamathError(f'int4 data out of range: {arr}') raise KlamathError(f'int4 data out of range: {arr}')
@ -189,3 +194,9 @@ def read(stream: IO[bytes], size: int) -> bytes:
if len(data) != size: if len(data) != size:
raise EOFError raise EOFError
return data return data
if not _USE_RS_EXT:
pack_int2 = _pack_int2
pack_int4 = _pack_int4

View File

@ -3,6 +3,7 @@ Functionality for reading/writing elements (geometry, text labels,
structure references) and associated properties. structure references) and associated properties.
""" """
from typing import IO, TypeVar from typing import IO, TypeVar
from collections.abc import Mapping
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from dataclasses import dataclass from dataclasses import dataclass
@ -56,7 +57,7 @@ def read_properties(stream: IO[bytes]) -> dict[int, bytes]:
return properties return properties
def write_properties(stream: IO[bytes], properties: dict[int, bytes]) -> int: def write_properties(stream: IO[bytes], properties: Mapping[int, bytes]) -> int:
""" """
Write element properties. Write element properties.
@ -147,7 +148,7 @@ class Reference(Element):
colrow: tuple[int, int] | NDArray[numpy.int16] | None colrow: tuple[int, int] | NDArray[numpy.int16] | None
""" Number of columns and rows (AREF) or None (SREF) """ """ Number of columns and rows (AREF) or None (SREF) """
properties: dict[int, bytes] properties: Mapping[int, bytes]
""" Properties associated with this reference. """ """ Properties associated with this reference. """
@classmethod @classmethod
@ -229,7 +230,7 @@ class Boundary(Element):
xy: NDArray[numpy.int32] xy: NDArray[numpy.int32]
""" Ordered vertices of the shape. First and last points should be identical. """ """ Ordered vertices of the shape. First and last points should be identical. """
properties: dict[int, bytes] properties: Mapping[int, bytes]
""" Properties for the element. """ """ Properties for the element. """
@classmethod @classmethod
@ -275,7 +276,7 @@ class Path(Element):
xy: NDArray[numpy.int32] xy: NDArray[numpy.int32]
""" Path centerline coordinates """ """ Path centerline coordinates """
properties: dict[int, bytes] properties: Mapping[int, bytes]
""" Properties for the element. """ """ Properties for the element. """
@classmethod @classmethod
@ -315,7 +316,7 @@ class Path(Element):
if self.width != 0: if self.width != 0:
b += WIDTH.write(stream, self.width) b += WIDTH.write(stream, self.width)
if self.path_type < 4: if self.path_type == 4:
bgn_ext, end_ext = self.extension bgn_ext, end_ext = self.extension
if bgn_ext != 0: if bgn_ext != 0:
b += BGNEXTN.write(stream, bgn_ext) b += BGNEXTN.write(stream, bgn_ext)
@ -340,7 +341,7 @@ class Box(Element):
xy: NDArray[numpy.int32] xy: NDArray[numpy.int32]
""" Box coordinates (5 pairs) """ """ Box coordinates (5 pairs) """
properties: dict[int, bytes] properties: Mapping[int, bytes]
""" Properties for the element. """ """ Properties for the element. """
@classmethod @classmethod
@ -374,7 +375,7 @@ class Node(Element):
xy: NDArray[numpy.int32] xy: NDArray[numpy.int32]
""" 1-50 pairs of coordinates. """ """ 1-50 pairs of coordinates. """
properties: dict[int, bytes] properties: Mapping[int, bytes]
""" Properties for the element. """ """ Properties for the element. """
@classmethod @classmethod
@ -434,7 +435,7 @@ class Text(Element):
string: bytes string: bytes
""" Text content """ """ Text content """
properties: dict[int, bytes] properties: Mapping[int, bytes]
""" Properties for the element. """ """ Properties for the element. """
@classmethod @classmethod

View File

@ -120,7 +120,7 @@ def test_pack_ascii() -> None:
assert pack_ascii(b'321') == b'321\0' assert pack_ascii(b'321') == b'321\0'
def test_invalid_date(): def test_invalid_date() -> None:
default = [datetime(1900, 1, 1, 0, 0, 0)] default = [datetime(1900, 1, 1, 0, 0, 0)]
assert parse_datetime(pack_int2((0, 0, 0, 0, 0, 0))) == default assert parse_datetime(pack_int2((0, 0, 0, 0, 0, 0))) == default
assert parse_datetime(pack_int2((0, 1, 32, 0, 0, 0))) == default assert parse_datetime(pack_int2((0, 1, 32, 0, 0, 0))) == default