From bb30c0e7598832643aca577dca1958a03fdd1c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <pmaslanka@smok.co> Date: Thu, 14 Oct 2021 22:19:29 +0200 Subject: [PATCH] v2.18.3 --- CHANGELOG.md | 3 +++ docs/coding/transforms.rst | 2 ++ satella/__init__.py | 2 +- satella/coding/transforms/__init__.py | 22 ++++++++++++++++++++-- satella/parsing.py | 26 ++++++++++++++++++-------- tests/test_coding/test_transforms.py | 6 +++++- tests/test_parsing.py | 2 ++ 7 files changed, 51 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3bf669a..ca258b13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,4 @@ # v2.18.3 + +* fixed a bug with `get_remaining_bytes` +* added `unpack_dict` diff --git a/docs/coding/transforms.rst b/docs/coding/transforms.rst index 702c54c1..4f44c5ee 100644 --- a/docs/coding/transforms.rst +++ b/docs/coding/transforms.rst @@ -1,6 +1,8 @@ Rudimentary data transforms and algorithms ========================================== +.. autofunction:: satella.coding.transforms.unpack_dict + .. autofunction:: satella.coding.transforms.is_subset .. autoclass:: satella.coding.transforms.merge_list diff --git a/satella/__init__.py b/satella/__init__.py index 4c424a21..340f88b4 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.18.3a1' +__version__ = '2.18.3' diff --git a/satella/coding/transforms/__init__.py b/satella/coding/transforms/__init__.py index 4104511e..76c35cb3 100644 --- a/satella/coding/transforms/__init__.py +++ b/satella/coding/transforms/__init__.py @@ -16,9 +16,27 @@ from .predicates import is_subset __all__ = ['stringify', 'split_shuffle_and_join', 'one_tuple', 'none_if_false', 'merge_series', 'pad_to_multiple_of_length', 'clip', 'hashables_to_int', 'jsonify', 'intify', 'percentile', 'b64encode', 'linear_interpolate', - 'merge_list', 'is_subset'] + 'merge_list', 'is_subset', 'unpack_dict'] -from satella.coding.typing import T, NoArgCallable, Appendable, Number, Predicate +from satella.coding.typing import T, NoArgCallable, Appendable, Number, Predicate, K, V + + +def unpack_dict(dct: tp.Dict[K, V], *args: K) -> tp.Iterator[V]: + """ + Unpack a dictionary by accessing it's given keys in parallel. + + Example: + + >>> a, b, c = unpack_dict({1:2, 2:3, 4:5}, 1, 2, 4) + >>> assert a == 2 and b == 3 and c == 5 + + :param dct: dictionary to unpack + :param args: keys in this dictionary + :return: an iterator + :raises KeyError: a key was not found + """ + for key in args: + yield dct[key] def none_if_false(y: tp.Any) -> tp.Optional[tp.Any]: diff --git a/satella/parsing.py b/satella/parsing.py index b9cac307..5c93095b 100644 --- a/satella/parsing.py +++ b/satella/parsing.py @@ -12,13 +12,17 @@ class BinaryParser: This supports __len__ to return the amount of bytes in the stream, and __bytes__ to return the bytes. - :param b_stream: an object that allows indiced access, and allows subscripts to - span ranges, which will return items parseable by struct + This is a zero-copy solution, and :meth:`get_parser` will be zero copy + as well. + + :param b_stream: an object that allows subscripts to + span ranges, which will return items parsable by struct :param offset: initial offset into the stream :param length: optional maximum length of byte count :raises NotEnoughBytes: offset larger than stream length - :ivar offset: offset from which bytes will be readed + :ivar pointer: pointer to the next bytes. Can be read and modified at will to + preserve the earlier state of the BinaryParser. """ def __len__(self) -> int: return self.length @@ -53,7 +57,9 @@ class BinaryParser: def assert_has_bytes(self, n: int) -> None: """ - Assert that we have at least n bytes to consume + Assert that we have at least n bytes to consume. + + This does not advance the pointer. :param n: amount of bytes to consume :raises NotEnoughBytes: not enough bytes remain in the stream! @@ -65,7 +71,7 @@ class BinaryParser: """ Return a subclassed binary parser providing a window to another binary parser's data. - This will advance the pointer by length + This will advance the pointer by length bytes :param length: amount of bytes to view :return: a BinaryParser @@ -117,7 +123,8 @@ class BinaryParser: This must be a single-character struct! - This will advance the pointer by size of st. + This will advance the pointer by size of st. Struct objects + will be served from internal instance-specific cache. :param st: a single-character struct.Struct or a single character struct specification :return: a value returned from it @@ -145,7 +152,8 @@ class BinaryParser: """ Try to obtain as many bytes as this struct requires and return them parsed. - This will advance the pointer by size of st. + This will advance the pointer by size of st. Struct objects + will be served from internal instance-specific cache. :param st: a struct.Struct or a multi character struct specification :return: a tuple of un-parsed values @@ -165,4 +173,6 @@ class BinaryParser: This will not advance the pointer """ - return self.b_stream[self.pointer:self.pointer+self.length] + advance = self.pointer - self.init_ofs + remaining = self.length - advance + return self.b_stream[self.pointer:self.pointer+remaining] diff --git a/tests/test_coding/test_transforms.py b/tests/test_coding/test_transforms.py index 202767ab..93d7d2b1 100644 --- a/tests/test_coding/test_transforms.py +++ b/tests/test_coding/test_transforms.py @@ -5,11 +5,15 @@ import base64 from satella.coding.transforms import stringify, split_shuffle_and_join, one_tuple, \ merge_series, pad_to_multiple_of_length, clip, b64encode, linear_interpolate, \ - hashables_to_int, none_if_false, merge_list, is_subset + hashables_to_int, none_if_false, merge_list, is_subset, unpack_dict class TestTransforms(unittest.TestCase): + def test_unpack_dict(self): + a, b, c = unpack_dict({1: 2, 2: 3, 4: 5}, 1, 2, 4) + self.assertTrue(a == 2 and b == 3 and c == 5) + def test_is_subset(self): self.assertTrue(is_subset({}, {})) self.assertTrue(is_subset({}, {1: 2})) diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 202cacf9..b1c2e4ae 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -28,3 +28,5 @@ class TestParsing(unittest.TestCase): bp3 = bp.get_parser(4) self.assertEqual(bytes(bp3), b'\x00\x00\x00\xFF') self.assertRaises(NotEnoughBytes, lambda: bp2.get_parser(4)) + bp3.skip(3) + self.assertEqual(bp3.get_remaining_bytes(), b'\xFF') -- GitLab