From: Sergey Matveev Date: Sun, 7 Jan 2024 08:56:45 +0000 (+0300) Subject: Raise copyright years X-Git-Url: http://www.git.cypherpunks.ru/?p=pyderasn.git;a=commitdiff_plain;h=HEAD;hp=b6b3754b3ec4806bb4c43a5530ae12c96e8d2d29 Raise copyright years --- diff --git a/PUBKEY.asc b/PUBKEY.asc index efe5113..0332845 100644 --- a/PUBKEY.asc +++ b/PUBKEY.asc @@ -16,6 +16,8 @@ y8RmHsszF3sJ5wLuGk1vpSh1jgq61RUquQYJa1iE2B8fxpL6Qr+T8IR2Jan4TFIn vzGeBXtCD2yUIeJgSeF/3VoEq8lxJ+rwHwcsIqHF7QdqJCc7S0wviHUEEBEIAB0W IQTPYOiaWSMeduJjZCKuGoEJ5JhX7wUCWcLAIAAKCRCuGoEJ5JhX7+lbAP9+WNA4 Uk0pNH5BAASabuT+zllnHZ5SqZoKWbs7bzWfogD+NWmjTfSJCr7GSZ4Suy3Vw4nn -hUu3L6dceWUU+hAEOBw= -=Qodb +hUu3L6dceWUU+hAEOByIdQQQFgoAHRYhBBKtMmicZg1CaWf9dcuCBWMhB62KBQJi +6jwUAAoJEMuCBWMhB62KYHMBAOQ6VHkVXpBrQAWCNYUEo9LZAvM2CokI6HVpJps1 +7mZNAP0RI3s/4v8N7a4b+ghbaEtxBIWWlXxqlBgDj/Rbnke0Dg== +=0AVp -----END PGP PUBLIC KEY BLOCK----- diff --git a/VERSION b/VERSION index dd98ee6..c3cae12 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0 +9.3 diff --git a/doc/browser.webp b/doc/browser.webp new file mode 100644 index 0000000..6556f0d Binary files /dev/null and b/doc/browser.webp differ diff --git a/doc/build.log.do b/doc/build.log.do index 76ab32d..be6a370 100644 --- a/doc/build.log.do +++ b/doc/build.log.do @@ -1,7 +1,7 @@ rm -fr _build html=_build/html PYTHONPATH=.. ${PYTHON:=python} -msphinx . $html -[ -d download ] && cp -r download $html || echo No download directory, skipping +[ -d download ] && cp -a download $html || echo No download directory, skipping rm -r $html/.doctrees $html/.buildinfo find $html -type d -exec chmod 755 {} + find $html -type f -exec chmod 644 {} + diff --git a/doc/conf.py b/doc/conf.py index 9061ce7..a82a7b5 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -5,11 +5,10 @@ templates_path = ["_templates"] source_suffix = ".rst" master_doc = "index" project = "pyderasn" -copyright = "2017-2021, Sergey Matveev" +copyright = "2017-2023, Sergey Matveev" author = "Sergey Matveev" version = version release = version -language = None exclude_patterns = ["_build"] pygments_style = "sphinx" todo_include_todos = False diff --git a/doc/features.rst b/doc/features.rst index fd7bb7d..5cfabee 100644 --- a/doc/features.rst +++ b/doc/features.rst @@ -65,14 +65,14 @@ Also there is `asn1crypto `__. conveniently replace utilities like either ``dumpasn1`` or ``openssl asn1parse`` - .. figure:: pprinting.png + .. figure:: pprinting.webp :alt: Pretty printing example output An example of pretty printed X.509 certificate with automatically parsed DEFINED BY fields. * :ref:`ASN.1 browser ` - .. figure:: browser.png + .. figure:: browser.webp :alt: ASN.1 browser example An example of browser running. diff --git a/doc/index.rst b/doc/index.rst index 60fadae..b9c56a3 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -38,13 +38,13 @@ There are articles about its history and usage: * `Как я написал ASN.1 библиотеку с slots and blobs `__ (on russian) * `Как я добавил big-data поддержку `__ (on russian) -.. figure:: pprinting.png +.. figure:: pprinting.webp :alt: Pretty printing example output An example of pretty printed X.509 certificate with automatically parsed DEFINED BY fields. -.. figure:: browser.png +.. figure:: browser.webp :alt: ASN.1 browser example An example of browser running. diff --git a/doc/install.rst b/doc/install.rst index db2ccf2..057f47d 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -4,11 +4,11 @@ Install Preferable way is to :ref:`download ` tarball with the signature from `official website `__:: - $ [fetch|wget] http://www.pyderasn.cypherpunks.ru/download/pyderasn-9.0.tar.zst - $ [fetch|wget] http://www.pyderasn.cypherpunks.ru/download/pyderasn-9.0.tar.zst.sig - $ gpg --verify pyderasn-9.0.tar.zst.sig pyderasn-9.0.tar.zst - $ zstd -d < pyderasn-9.0.tar.zst | tar xf - - $ cd pyderasn-9.0 + $ [fetch|wget] http://www.pyderasn.cypherpunks.ru/download/pyderasn-9.3.tar.zst + $ [fetch|wget] http://www.pyderasn.cypherpunks.ru/download/pyderasn-9.3.tar.zst.asc + $ gpg --verify pyderasn-9.3.tar.zst.asc pyderasn-9.3.tar.zst + $ zstd -d < pyderasn-9.3.tar.zst | tar xf - + $ cd pyderasn-9.3 $ python setup.py install # or copy pyderasn.py (possibly termcolor.py) to your PYTHONPATH @@ -16,11 +16,6 @@ signature from `official website `__:: * ``urwid`` is an optional dependency used for :ref:`interactive browser `. * ``dateutil`` is an optional dependency used for ``.totzdatetime()`` method. -You could use pip (**no** OpenPGP authentication is performed!) with PyPI:: - - $ echo pyderasn==9.0 --hash=sha256:TO-BE-FILLED > requirements.txt - $ pip install --requirement requirements.txt - You have to verify downloaded tarballs integrity and authenticity to be sure that you retrieved trusted and untampered software. `GNU Privacy Guard `__ is used for that purpose. @@ -36,6 +31,6 @@ resources. uid PyDERASN releases $ gpg --auto-key-locate dane --locate-keys pyderasn at cypherpunks dot ru - $ gpg --auto-key-locate wkd --locate-keys pyderasn at cypherpunks dot ru + $ gpg --auto-key-locate wkd --locate-keys pyderasn at cypherpunks dot ru .. literalinclude:: ../PUBKEY.asc diff --git a/doc/news.rst b/doc/news.rst index 68d0a6e..92f0646 100644 --- a/doc/news.rst +++ b/doc/news.rst @@ -1,6 +1,22 @@ News ==== +.. _release9.3: + +9.3 +--- +* CommonString's ``.memoryview()`` raises ValueError now for + friendliness with linters + +.. _release9.2: + +9.2 +--- +* ``keep_memoryview`` context option appeared, respected by OctetString + and Any objects during DER decoding. If set, then their internal + values will keep memoryview reference instead of full bytes copy +* Correspondingly OctetString and Any have ``.memoryview()`` method + .. _release9.1: 9.1 diff --git a/doc/pprinting.png b/doc/pprinting.png deleted file mode 100644 index 8f74110..0000000 Binary files a/doc/pprinting.png and /dev/null differ diff --git a/doc/pprinting.webp b/doc/pprinting.webp new file mode 100644 index 0000000..2e94c1a Binary files /dev/null and b/doc/pprinting.webp differ diff --git a/makedist.sh b/makedist similarity index 75% rename from makedist.sh rename to makedist index aaf7fb9..3aafd67 100755 --- a/makedist.sh +++ b/makedist @@ -10,7 +10,7 @@ mkdir $tmp/pyderasn-"$release" echo pyderasn.py echo setup.py find $(perl -lane 'print $F[1]' MANIFEST.in) -} | tar cfI - - | tar xfC - $tmp/pyderasn-"$release" +} | tar cfT - - | tar xfC - $tmp/pyderasn-"$release" PYTHONPATH="$tmp/pyderasn-$release" redo $tmp/pyderasn-"$release"/doc/build.log rm -r $tmp/pyderasn-"$release"/doc/.redo $tmp/pyderasn-"$release"/doc/build.log @@ -18,19 +18,19 @@ rm -r $tmp/pyderasn-"$release"/doc/.redo $tmp/pyderasn-"$release"/doc/build.log tar xvfC doc/download/termcolor-1.1.0.tar.gz $tmp --include "*/termcolor.py" mv -v $tmp/termcolor-*/termcolor.py $tmp/pyderasn-"$release" -pip_hash=$(pip hash dist/pyderasn-"$release".tar.gz | sed -n '$p') - cd $tmp find . -type d -exec chmod 755 {} + find . -type f -exec chmod 644 {} + chmod 755 pyderasn-"$release"/pyderasn.py tar cvf pyderasn-"$release".tar --uid=0 --gid=0 --numeric-owner pyderasn-"$release" zstd -19 -v pyderasn-"$release".tar -gpg --detach-sign --sign --local-user 04A933D1BA20327A pyderasn-"$release".tar.zst - tarball=pyderasn-"$release".tar.zst +gpg --armor --detach-sign --sign --local-user pyderasn@cypherpunks.ru $tarball +meta4-create -fn "$tarball" -mtime "$tarball" -sig "$tarball".asc \ + http://www.pyderasn.cypherpunks.ru/download/"$tarball" \ + http://y.www.pyderasn.cypherpunks.ru/download/"$tarball" < "$tarball" > "$tarball".meta4 + size=$(( $(stat -f %z $tarball) / 1024 )) -hash=$(gpg --print-md SHA256 < $tarball) release_date=$(date "+%Y-%m-%d") cat <\` - $release_date - $size KiB - - \`link \`__ - \`sign \`__ - - \`\`$hash\`\` - -pyderasn==$release $pip_hash + - \`meta4 \`__ + \`tar \`__ + \`sig \`__ EOF -mv $tmp/$tarball $tmp/"$tarball".sig $cur/doc/download +mv $tmp/$tarball $tmp/"$tarball".asc $tmp/"$tarball".meta4 $cur/doc/download cat < -pip'es requirements file: - - pyderasn==$release $pip_hash - Please send questions regarding the use of PyDERASN, bug reports and patches to mailing list: http://lists.cypherpunks.ru/pyderasn_002ddevel.html EOF diff --git a/pip-requirements-tests.txt b/pip-requirements-tests.txt index 48f7556..3498a07 100644 --- a/pip-requirements-tests.txt +++ b/pip-requirements-tests.txt @@ -1,4 +1,4 @@ attrs==19.3.0 coverage==4.5.4 -hypothesis==3.57.0 +hypothesis==6.39.4 python-dateutil==2.8.1 diff --git a/pyderasn.py b/pyderasn.py index 3eb1071..54242a3 100755 --- a/pyderasn.py +++ b/pyderasn.py @@ -4,7 +4,7 @@ # pylint: disable=line-too-long,superfluous-parens,protected-access,too-many-lines # pylint: disable=too-many-return-statements,too-many-branches,too-many-statements # PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures -# Copyright (C) 2017-2021 Sergey Matveev +# Copyright (C) 2017-2024 Sergey Matveev # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as @@ -235,6 +235,7 @@ Currently available context options: * :ref:`bered ` * :ref:`defines_by_path ` * :ref:`evgen_mode_upto ` +* :ref:`keep_memoryview ` .. _pprinting: @@ -706,6 +707,15 @@ creates read-only memoryview on the file contents:: page cache used for mmaps. It can take twice the necessary size in the memory: both in page cache and ZFS ARC. +.. _keep_memoryview_ctx: + +That read-only memoryview could be safe to be used as a value inside +decoded :py:class:`pyderasn.OctetString` and :py:class:`pyderasn.Any` +objects. You can enable that by setting `"keep_memoryview": True` in +:ref:`decode context `. No OCTET STRING and ANY values will be +copied to memory. Of course that works only in DER encoding, where the +value is continuously encoded. + CER encoding ____________ @@ -1186,7 +1196,7 @@ except ImportError: # pragma: no cover tzUTC = "missing" -__version__ = "9.1" +__version__ = "9.3" __all__ = ( "agg_octet_string", @@ -3630,6 +3640,7 @@ class OctetString(Obj): tag_default = tag_encode(4) asn1_type_name = "OCTET STRING" evgen_mode_skip_value = True + memoryview_safe = True def __init__( self, @@ -3726,6 +3737,10 @@ class OctetString(Obj): self._assert_ready() return bytes(self._value) + def memoryview(self): + self._assert_ready() + return memoryview(self._value) + def __eq__(self, their): if their.__class__ == bytes: return self._value == their @@ -3839,12 +3854,15 @@ class OctetString(Obj): decode_path=decode_path, offset=offset, ) + if evgen_mode and self.evgen_mode_skip_value: + value = None + elif self.memoryview_safe and ctx.get("keep_memoryview", False): + value = v + else: + value = v.tobytes() try: obj = self.__class__( - value=( - None if (evgen_mode and self.evgen_mode_skip_value) - else v.tobytes() - ), + value=value, bounds=(self._bound_min, self._bound_max), impl=self.tag, expl=self._expl, @@ -4694,6 +4712,7 @@ class CommonString(OctetString): - utf-16-be """ __slots__ = () + memoryview_safe = False def _value_sanitize(self, value): value_raw = None @@ -4743,6 +4762,9 @@ class CommonString(OctetString): return self._value.decode(self.encoding) return str(self._value) + def memoryview(self): + raise ValueError("CommonString does not support .memoryview()") + def __repr__(self): return pp_console_row(next(self.pps())) @@ -5842,7 +5864,7 @@ class Any(Obj): value = self._value_sanitize(value) self._value = value if self._expl is None: - if value.__class__ == bytes: + if value.__class__ == bytes or value.__class__ == memoryview: tag_class, _, tag_num = tag_decode(tag_strip(value)[0]) else: tag_class, tag_num = value.tag_order @@ -5852,7 +5874,7 @@ class Any(Obj): self.defined = None def _value_sanitize(self, value): - if value.__class__ == bytes: + if value.__class__ == bytes or value.__class__ == memoryview: if len(value) == 0: raise ValueError("%s value can not be empty" % self.__class__.__name__) return value @@ -5903,13 +5925,13 @@ class Any(Obj): self.defined = state.defined def __eq__(self, their): - if their.__class__ == bytes: - if self._value.__class__ == bytes: + if their.__class__ == bytes or their.__class__ == memoryview: + if self._value.__class__ == bytes or their.__class__ == memoryview: return self._value == their return self._value.encode() == their if issubclass(their.__class__, Any): if self.ready and their.ready: - return bytes(self) == bytes(their) + return self.memoryview() == their.memoryview() return self.ready == their.ready return False @@ -5930,8 +5952,17 @@ class Any(Obj): value = self._value if value.__class__ == bytes: return value + if value.__class__ == memoryview: + return bytes(value) return self._value.encode() + def memoryview(self): + self._assert_ready() + value = self._value + if value.__class__ == memoryview: + return memoryview(value) + return memoryview(bytes(self)) + @property def tlen(self): return 0 @@ -5939,20 +5970,20 @@ class Any(Obj): def _encode(self): self._assert_ready() value = self._value - if value.__class__ == bytes: - return value + if value.__class__ == bytes or value.__class__ == memoryview: + return bytes(self) return value.encode() def _encode1st(self, state): self._assert_ready() value = self._value - if value.__class__ == bytes: + if value.__class__ == bytes or value.__class__ == memoryview: return len(value), state return value.encode1st(state) def _encode2nd(self, writer, state_iter): value = self._value - if value.__class__ == bytes: + if value.__class__ == bytes or value.__class__ == memoryview: write_full(writer, value) else: value.encode2nd(writer, state_iter) @@ -5960,7 +5991,7 @@ class Any(Obj): def _encode_cer(self, writer): self._assert_ready() value = self._value - if value.__class__ == bytes: + if value.__class__ == bytes or value.__class__ == memoryview: write_full(writer, value) else: value.encode_cer(writer) @@ -6027,8 +6058,14 @@ class Any(Obj): ) tlvlen = tlen + llen + l v, tail = tlv[:tlvlen], v[l:] + if evgen_mode: + value = None + elif ctx.get("keep_memoryview", False): + value = v + else: + value = v.tobytes() obj = self.__class__( - value=None if evgen_mode else v.tobytes(), + value=value, expl=self._expl, optional=self.optional, _decoded=(offset, 0, tlvlen), @@ -6043,7 +6080,7 @@ class Any(Obj): value = self._value if value is None: pass - elif value.__class__ == bytes: + elif value.__class__ == bytes or value.__class__ == memoryview: value = None else: value = repr(value) @@ -6053,7 +6090,10 @@ class Any(Obj): obj_name=self.__class__.__name__, decode_path=decode_path, value=value, - blob=self._value if self._value.__class__ == bytes else None, + blob=self._value if ( + self._value.__class__ == bytes or + value.__class__ == memoryview + ) else None, optional=self.optional, default=self == self.default, impl=None if self.tag == self.tag_default else tag_decode(self.tag), diff --git a/tests/test_cms.py b/tests/test_cms.py index 1936b6e..fb45580 100644 --- a/tests/test_cms.py +++ b/tests/test_cms.py @@ -1,6 +1,6 @@ # coding: utf-8 # PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures -# Copyright (C) 2017-2021 Sergey Matveev +# Copyright (C) 2017-2024 Sergey Matveev # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as diff --git a/tests/test_crl.py b/tests/test_crl.py index 9dc5c00..9174e2f 100644 --- a/tests/test_crl.py +++ b/tests/test_crl.py @@ -1,6 +1,6 @@ # coding: utf-8 # PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures -# Copyright (C) 2017-2021 Sergey Matveev +# Copyright (C) 2017-2024 Sergey Matveev # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as diff --git a/tests/test_crts.py b/tests/test_crts.py index 79b2f1d..e43b4c2 100644 --- a/tests/test_crts.py +++ b/tests/test_crts.py @@ -1,6 +1,6 @@ # coding: utf-8 # PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures -# Copyright (C) 2017-2021 Sergey Matveev +# Copyright (C) 2017-2024 Sergey Matveev # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as @@ -229,7 +229,7 @@ class TestGoSelfSignedVector(TestCase): "ba3ca12568fdc6c7b4511cd40a7f659980402df2b998bb9a4a8cbeb34c0f0a78c", "f8d91ede14a5ed76bf116fe360aafa8821490435", ))) - crt = Certificate().decod(raw) + crt = Certificate().decod(raw, ctx={"keep_memoryview": True}) tbs = crt["tbsCertificate"] self.assertEqual(tbs["version"], 0) self.assertFalse(tbs["version"].decoded) @@ -301,6 +301,7 @@ class TestGoSelfSignedVector(TestCase): "998bb9a4a8cbeb34c0f0a78cf8d91ede14a5ed76bf116fe360aafa8821490435", ))))) self.assertSequenceEqual(crt.encode(), raw) + crt = Certificate().decod(raw) pprint(crt) repr(crt) pickle_loads(pickle_dumps(crt, pickle_proto)) diff --git a/tests/test_pyderasn.py b/tests/test_pyderasn.py index 4febf74..e8f8bdf 100644 --- a/tests/test_pyderasn.py +++ b/tests/test_pyderasn.py @@ -1,6 +1,6 @@ # coding: utf-8 # PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures -# Copyright (C) 2017-2021 Sergey Matveev +# Copyright (C) 2017-2024 Sergey Matveev # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as @@ -679,7 +679,7 @@ class TestBoolean(CommonMixin, TestCase): repr(obj) list(obj.pps()) - @given(integers(min_value=2)) + @given(integers(min_value=2, max_value=10)) def test_invalid_len(self, l): with self.assertRaises(InvalidLength): Boolean().decode(b"".join(( @@ -1272,6 +1272,8 @@ def bit_string_values_strategy(draw, schema=None, value_required=False, do_expl= if generation_choice == 2 or draw(booleans()): return draw(binary(max_size=len(schema) // 8)) if generation_choice == 3 or draw(booleans()): + if len(schema) == 0: + return () return tuple(draw(lists(sampled_from([name for name, _ in schema])))) return None value = _value(value_required) @@ -2214,8 +2216,18 @@ class TestOctetString(CommonMixin, TestCase): integers(min_value=0), binary(max_size=5), decode_path_strat, + booleans(), ) - def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path): + def test_symmetric( + self, + values, + value, + tag_expl, + offset, + tail_junk, + decode_path, + keep_memoryview, + ): for klass in (OctetString, OctetStringInherited): _, _, _, _, default, optional, _decoded = values obj = klass( @@ -2243,6 +2255,7 @@ class TestOctetString(CommonMixin, TestCase): obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(), obj_expled_encoded, ) + ctx_dummy["keep_memoryview"] = keep_memoryview ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, @@ -2258,6 +2271,10 @@ class TestOctetString(CommonMixin, TestCase): self.assertNotEqual(obj_decoded, obj) self.assertEqual(bytes(obj_decoded), bytes(obj_expled)) self.assertEqual(bytes(obj_decoded), bytes(obj)) + self.assertIsInstance( + obj_decoded._value, + memoryview if keep_memoryview else bytes, + ) self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded) self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl) self.assertEqual(obj_decoded.expl_tlen, len(tag_expl)) @@ -2678,8 +2695,8 @@ def oid_strategy(draw): if first_arc in (0, 1): second_arc = draw(integers(min_value=0, max_value=39)) else: - second_arc = draw(integers(min_value=0)) - other_arcs = draw(lists(integers(min_value=0))) + second_arc = draw(integers(min_value=0, max_value=1 << 63)) + other_arcs = draw(lists(integers(min_value=0, max_value=1 << 63))) return tuple([first_arc, second_arc] + other_arcs) @@ -2910,21 +2927,27 @@ class TestObjectIdentifier(CommonMixin, TestCase): with self.assertRaisesRegex(DecodeError, "unfinished OID"): obj.decode(data) - @given(integers(min_value=0)) + @given(integers(min_value=0, max_value=1 << 63)) def test_invalid_short(self, value): with self.assertRaises(InvalidOID): ObjectIdentifier((value,)) with self.assertRaises(InvalidOID): ObjectIdentifier("%d" % value) - @given(integers(min_value=3), integers(min_value=0)) + @given( + integers(min_value=3, max_value=1 << 63), + integers(min_value=0, max_value=1 << 63), + ) def test_invalid_first_arc(self, first_arc, second_arc): with self.assertRaises(InvalidOID): ObjectIdentifier((first_arc, second_arc)) with self.assertRaises(InvalidOID): ObjectIdentifier("%d.%d" % (first_arc, second_arc)) - @given(integers(min_value=0, max_value=1), integers(min_value=40)) + @given( + integers(min_value=0, max_value=1), + integers(min_value=40, max_value=1 << 63), + ) def test_invalid_second_arc(self, first_arc, second_arc): with self.assertRaises(InvalidOID): ObjectIdentifier((first_arc, second_arc)) @@ -5259,8 +5282,18 @@ class TestAny(CommonMixin, TestCase): integers(min_value=0), binary(max_size=5), decode_path_strat, + booleans(), ) - def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path): + def test_symmetric( + self, + values, + value, + tag_expl, + offset, + tail_junk, + decode_path, + keep_memoryview, + ): for klass in (Any, AnyInherited): _, _, optional, _decoded = values obj = klass(value=value, optional=optional, _decoded=_decoded) @@ -5280,6 +5313,7 @@ class TestAny(CommonMixin, TestCase): list(obj_expled.pps()) pprint(obj_expled, big_blobs=True, with_decode_path=True) obj_expled_encoded = obj_expled.encode() + ctx_dummy["keep_memoryview"] = keep_memoryview ctx_copied = deepcopy(ctx_dummy) obj_decoded, tail = obj_expled.decode( obj_expled_encoded + tail_junk, @@ -5294,6 +5328,10 @@ class TestAny(CommonMixin, TestCase): self.assertEqual(obj_decoded, obj_expled) self.assertEqual(bytes(obj_decoded), bytes(obj_expled)) self.assertEqual(bytes(obj_decoded), bytes(obj)) + self.assertIsInstance( + obj_decoded._value, + memoryview if keep_memoryview else bytes, + ) self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded) self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl) self.assertEqual(obj_decoded.expl_tlen, len(tag_expl)) @@ -6723,7 +6761,7 @@ class SeqOfMixin(object): schema = Boolean() bound_min = d.draw(integers(min_value=1, max_value=1 << 7)) bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7)) - value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1)) + value = [Boolean(False)] * d.draw(integers(min_value=0, max_value=bound_min - 1)) with self.assertRaises(BoundsError) as err: SeqOf(value=value, bounds=(bound_min, bound_max)) repr(err.exception) @@ -7428,7 +7466,11 @@ class TestPP(TestCase): def test_oid_printing(self, d): oids = { str(ObjectIdentifier(k)): v * 2 - for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items() + for k, v in d.draw(dictionaries( + oid_strategy(), + text_letters(), + min_size=1, + )).items() } chosen = d.draw(sampled_from(sorted(oids))) chosen_id = oids[chosen]