From 476a68916b33a0057f26c3f9377747cb683a5c93 Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sat, 18 Jul 2020 21:07:12 +0200 Subject: [PATCH] Merge implementations of read_exact functions/methods The old functions/methods still exist, so that they continue to raise the same exceptions as before (which are different depending on context), but they now use the same implementation internally. --- rsrcfork/_io_utils.py | 18 ++++++++++++++++++ rsrcfork/api.py | 9 +++++---- rsrcfork/compress/common.py | 10 ++++++---- 3 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 rsrcfork/_io_utils.py diff --git a/rsrcfork/_io_utils.py b/rsrcfork/_io_utils.py new file mode 100644 index 0000000..ec2d87b --- /dev/null +++ b/rsrcfork/_io_utils.py @@ -0,0 +1,18 @@ +"""A collection of utility functions and classes related to IO streams. For internal use only.""" + +import typing + + +def read_exact(stream: typing.BinaryIO, byte_count: int) -> bytes: + """Read byte_count bytes from the stream and raise an exception if too few bytes are read (i. e. if EOF was hit prematurely). + + :param stream: The stream to read from. + :param byte_count: The number of bytes to read. + :return: The read data, which is exactly ``byte_count`` bytes long. + :raise EOFError: If not enough data could be read from the stream. + """ + + data = stream.read(byte_count) + if len(data) != byte_count: + raise EOFError(f"Attempted to read {byte_count} bytes of data, but only got {len(data)} bytes") + return data diff --git a/rsrcfork/api.py b/rsrcfork/api.py index d6dc98a..7461be8 100644 --- a/rsrcfork/api.py +++ b/rsrcfork/api.py @@ -8,6 +8,7 @@ import types import typing import warnings +from . import _io_utils from . import compress # The formats of all following structures is as described in the Inside Macintosh book (see module docstring). @@ -393,10 +394,10 @@ class ResourceFile(typing.Mapping[bytes, typing.Mapping[int, Resource]], typing. def _read_exact(self, byte_count: int) -> bytes: """Read byte_count bytes from the stream and raise an exception if too few bytes are read (i. e. if EOF was hit prematurely).""" - data = self._stream.read(byte_count) - if len(data) != byte_count: - raise InvalidResourceFileError(f"Attempted to read {byte_count} bytes of data, but only got {len(data)} bytes") - return data + try: + return _io_utils.read_exact(self._stream, byte_count) + except EOFError as e: + raise InvalidResourceFileError(str(e)) def _stream_unpack(self, st: struct.Struct) -> tuple: """Unpack data from the stream according to the struct st. The number of bytes to read is determined using st.size, so variable-sized structs cannot be used with this method.""" diff --git a/rsrcfork/compress/common.py b/rsrcfork/compress/common.py index 6d16870..f8141cb 100644 --- a/rsrcfork/compress/common.py +++ b/rsrcfork/compress/common.py @@ -2,6 +2,8 @@ import io import struct import typing +from .. import _io_utils + class DecompressError(Exception): """Raised when resource data decompression fails, because the data is invalid or the compression type is not supported.""" @@ -182,10 +184,10 @@ def make_peekable(stream: typing.BinaryIO) -> "PeekableIO": def read_exact(stream: typing.BinaryIO, byte_count: int) -> bytes: """Read byte_count bytes from the stream and raise an exception if too few bytes are read (i. e. if EOF was hit prematurely).""" - data = stream.read(byte_count) - if len(data) != byte_count: - raise DecompressError(f"Attempted to read {byte_count} bytes of data, but only got {len(data)} bytes") - return data + try: + return _io_utils.read_exact(stream, byte_count) + except EOFError as e: + raise DecompressError(str(e)) def read_variable_length_integer(stream: typing.BinaryIO) -> int: