u-msgpack-python 2.8.0__tar.gz → 2.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2023 vsergeev / Ivan (Vanya) A. Sergeev
1
+ Copyright (c) 2013-2026 vsergeev / Ivan (Vanya) A. Sergeev
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: u-msgpack-python
3
- Version: 2.8.0
3
+ Version: 2.9.0
4
4
  Summary: A portable, lightweight MessagePack serializer and deserializer written in pure Python.
5
5
  Home-page: https://github.com/vsergeev/u-msgpack-python
6
6
  Author: vsergeev
@@ -15,6 +15,17 @@ Classifier: Programming Language :: Python :: 2
15
15
  Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Programming Language :: Python :: Implementation :: CPython
17
17
  Classifier: Programming Language :: Python :: Implementation :: PyPy
18
+ Description-Content-Type: text/x-rst
18
19
  License-File: LICENSE
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: keywords
27
+ Dynamic: license
28
+ Dynamic: license-file
29
+ Dynamic: summary
19
30
 
20
- u-msgpack-python is a lightweight `MessagePack <http://msgpack.org/>`_ serializer and deserializer module written in pure Python, compatible with both Python 2 and Python 3, as well as CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest `MessagePack specification <https://github.com/msgpack/msgpack/blob/master/spec.md>`_. In particular, it supports the new binary, UTF-8 string, and application-defined ext types. See https://github.com/vsergeev/u-msgpack-python for more information.
31
+ u-msgpack-python is a lightweight `MessagePack <http://msgpack.org/>`_ serializer and deserializer module written in pure Python, compatible with both Python 2 and Python 3, as well as CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest `MessagePack specification <https://github.com/msgpack/msgpack/blob/master/spec.md>`_. In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. See https://github.com/vsergeev/u-msgpack-python for more information.
@@ -1,6 +1,6 @@
1
- # u-msgpack-python [![Tests Status](https://github.com/vsergeev/u-msgpack-python/actions/workflows/tests.yml/badge.svg)](https://github.com/vsergeev/u-msgpack-python/actions/workflows/tests.yml) [![Docs Status](https://readthedocs.org/projects/u-msgpack-python/badge/)](https://u-msgpack-python.readthedocs.io/en/latest/) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE)
1
+ # u-msgpack-python [![Tests Status](https://github.com/vsergeev/u-msgpack-python/actions/workflows/tests.yml/badge.svg)](https://github.com/vsergeev/u-msgpack-python/actions/workflows/tests.yml) [![Docs Status](https://app.readthedocs.org/projects/u-msgpack-python/badge/)](https://u-msgpack-python.readthedocs.io/en/latest/) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE)
2
2
 
3
- u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types.
3
+ u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with Python 2 and 3, as well as CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types.
4
4
 
5
5
  u-msgpack-python is currently distributed as a package on [PyPI](https://pypi.python.org/pypi/u-msgpack-python) and as a single file module.
6
6
 
@@ -11,11 +11,6 @@ With pip:
11
11
  $ pip install u-msgpack-python
12
12
  ```
13
13
 
14
- With easy_install:
15
- ``` text
16
- $ easy_install u-msgpack-python
17
- ```
18
-
19
14
  or simply drop `umsgpack.py` into your project!
20
15
  ``` text
21
16
  $ wget https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack/__init__.py -O umsgpack.py
@@ -1,6 +1,3 @@
1
- [metadata]
2
- description_file = README.md
3
-
4
1
  [bdist_wheel]
5
2
  universal = True
6
3
 
@@ -5,14 +5,15 @@ except ImportError:
5
5
 
6
6
  setup(
7
7
  name='u-msgpack-python',
8
- version='2.8.0',
8
+ version='2.9.0',
9
9
  description='A portable, lightweight MessagePack serializer and deserializer written in pure Python.',
10
10
  author='vsergeev',
11
11
  author_email='v@sergeev.io',
12
12
  url='https://github.com/vsergeev/u-msgpack-python',
13
13
  packages=['umsgpack'],
14
14
  package_data={'umsgpack': ['*.pyi', 'py.typed']},
15
- long_description="""u-msgpack-python is a lightweight `MessagePack <http://msgpack.org/>`_ serializer and deserializer module written in pure Python, compatible with both Python 2 and Python 3, as well as CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest `MessagePack specification <https://github.com/msgpack/msgpack/blob/master/spec.md>`_. In particular, it supports the new binary, UTF-8 string, and application-defined ext types. See https://github.com/vsergeev/u-msgpack-python for more information.""",
15
+ long_description="""u-msgpack-python is a lightweight `MessagePack <http://msgpack.org/>`_ serializer and deserializer module written in pure Python, compatible with both Python 2 and Python 3, as well as CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest `MessagePack specification <https://github.com/msgpack/msgpack/blob/master/spec.md>`_. In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. See https://github.com/vsergeev/u-msgpack-python for more information.""",
16
+ long_description_content_type='text/x-rst',
16
17
  classifiers=[
17
18
  "Development Status :: 5 - Production/Stable",
18
19
  "License :: OSI Approved :: MIT License",
@@ -381,6 +381,7 @@ exported_vars_test_vector = [
381
381
  "ReservedCodeException",
382
382
  "UnhashableKeyException",
383
383
  "DuplicateKeyException",
384
+ "MaximumDepthException",
384
385
  "KeyNotPrimitiveException",
385
386
  "KeyDuplicateException",
386
387
  "ext_serializable",
@@ -438,9 +439,9 @@ class TestUmsgpack(unittest.TestCase):
438
439
  # one we end up with depends on the architecture (32-bit/64-bit)
439
440
  if sys.version_info[0] == 2:
440
441
  # Allow both {int,long} -> unpackb -> {int,long}
441
- if isinstance(obj, int) or isinstance(obj, long):
442
+ if isinstance(obj, int) or isinstance(obj, long): # noqa: F821
442
443
  self.assertTrue(isinstance(unpacked, int) or
443
- isinstance(unpacked, long))
444
+ isinstance(unpacked, long)) # noqa: F821
444
445
  else:
445
446
  self.assertTrue(isinstance(unpacked, type(obj)))
446
447
  # In Python3, we only have the int integer type
@@ -490,7 +491,7 @@ class TestUmsgpack(unittest.TestCase):
490
491
  # so convert any string obj to bytes before comparison
491
492
  if sys.version_info[0] == 3 and isinstance(obj, str):
492
493
  _obj = obj.encode('utf-8')
493
- elif sys.version_info[0] == 2 and isinstance(obj, unicode):
494
+ elif sys.version_info[0] == 2 and isinstance(obj, unicode): # noqa: F821
494
495
  _obj = bytes(obj)
495
496
  else:
496
497
  _obj = obj
@@ -712,11 +713,6 @@ class TestUmsgpack(unittest.TestCase):
712
713
  with self.assertRaises(NotImplementedError):
713
714
  umsgpack.unpackb(b"\xd4\x21\x00")
714
715
 
715
- # Unregister Ext serializable classes to prevent interference with
716
- # subsequent tests
717
- umsgpack._ext_classes_to_code = {}
718
- umsgpack._ext_code_to_classes = {}
719
-
720
716
  def test_ext_serializable_subclass(self):
721
717
  @umsgpack.ext_serializable(0x10)
722
718
  class Rectangle:
@@ -746,17 +742,12 @@ class TestUmsgpack(unittest.TestCase):
746
742
  unpacked = umsgpack.unpackb(packed)
747
743
  self.assertEqual(unpacked, Rectangle(5, 5))
748
744
 
749
- # Unregister Ext serializable classes to prevent interference with
750
- # subsequent tests
751
- umsgpack._ext_classes_to_code = {}
752
- umsgpack._ext_code_to_classes = {}
753
-
754
745
  def test_streaming_writer(self):
755
746
  # Try first composite test vector
756
747
  (_, obj, data) = composite_test_vectors[0]
757
748
  writer = io.BytesIO()
758
749
  umsgpack.pack(obj, writer)
759
- self.assertTrue(writer.getvalue(), data)
750
+ self.assertEqual(writer.getvalue(), data)
760
751
 
761
752
  def test_streaming_reader(self):
762
753
  # Try first composite test vector
@@ -764,12 +755,34 @@ class TestUmsgpack(unittest.TestCase):
764
755
  reader = io.BytesIO(data)
765
756
  self.assertEqual(umsgpack.unpack(reader), obj)
766
757
 
758
+ def test_max_depth_default_wraps_recursion(self):
759
+ # Default (no max_depth): deeply nested input must raise a
760
+ # MaximumDepthException (an UnpackException), not a bare RecursionError.
761
+ payload = b"\x91" * 5000 + b"\xc0"
762
+ self.assertRaises(umsgpack.MaximumDepthException, umsgpack.unpackb, payload)
763
+ self.assertRaises(umsgpack.UnpackException, umsgpack.unpackb, payload)
764
+ # Moderate nesting still works.
765
+ self.assertEqual(umsgpack.unpackb(b"\x91" * 100 + b"\xc0"),
766
+ umsgpack.unpackb(b"\x91" * 100 + b"\xc0"))
767
+
768
+ def test_max_depth_cap_arrays_and_maps(self):
769
+ self.assertTrue(
770
+ umsgpack.unpackb(b"\x91" * 32 + b"\xc0", max_depth=32) is not None)
771
+ self.assertRaises(umsgpack.MaximumDepthException,
772
+ umsgpack.unpackb, b"\x91" * 33 + b"\xc0", max_depth=32)
773
+ self.assertRaises(umsgpack.MaximumDepthException,
774
+ umsgpack.unpackb, b"\x81\xa1k" * 17 + b"\xc0", max_depth=16)
775
+ # Siblings are not counted, only nesting depth.
776
+ self.assertEqual(
777
+ len(umsgpack.unpackb(b"\xdc\x00\xc8" + b"\xc0" * 200, max_depth=1)), 200)
778
+
767
779
  def test_namespacing(self):
768
780
  # Get a list of global variables from umsgpack module
769
781
  exported_vars = list([x for x in dir(umsgpack) if not x.startswith("_")])
770
782
  # Ignore imports
771
- exported_vars = list([x for x in exported_vars if x != "struct" and x != "collections" and x != "datetime" and x !=
772
- "sys" and x != "io" and x != "xrange" and x != "Hashable"])
783
+ ignore_vars = ["struct", "collections", "datetime", "sys", "io",
784
+ "xrange", "Hashable", "functools"]
785
+ exported_vars = [x for x in exported_vars if x not in ignore_vars]
773
786
 
774
787
  self.assertTrue(len(exported_vars) == len(exported_vars_test_vector))
775
788
  for var in exported_vars_test_vector:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: u-msgpack-python
3
- Version: 2.8.0
3
+ Version: 2.9.0
4
4
  Summary: A portable, lightweight MessagePack serializer and deserializer written in pure Python.
5
5
  Home-page: https://github.com/vsergeev/u-msgpack-python
6
6
  Author: vsergeev
@@ -15,6 +15,17 @@ Classifier: Programming Language :: Python :: 2
15
15
  Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Programming Language :: Python :: Implementation :: CPython
17
17
  Classifier: Programming Language :: Python :: Implementation :: PyPy
18
+ Description-Content-Type: text/x-rst
18
19
  License-File: LICENSE
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: keywords
27
+ Dynamic: license
28
+ Dynamic: license-file
29
+ Dynamic: summary
19
30
 
20
- u-msgpack-python is a lightweight `MessagePack <http://msgpack.org/>`_ serializer and deserializer module written in pure Python, compatible with both Python 2 and Python 3, as well as CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest `MessagePack specification <https://github.com/msgpack/msgpack/blob/master/spec.md>`_. In particular, it supports the new binary, UTF-8 string, and application-defined ext types. See https://github.com/vsergeev/u-msgpack-python for more information.
31
+ u-msgpack-python is a lightweight `MessagePack <http://msgpack.org/>`_ serializer and deserializer module written in pure Python, compatible with both Python 2 and Python 3, as well as CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest `MessagePack specification <https://github.com/msgpack/msgpack/blob/master/spec.md>`_. In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. See https://github.com/vsergeev/u-msgpack-python for more information.
@@ -9,4 +9,5 @@ u_msgpack_python.egg-info/SOURCES.txt
9
9
  u_msgpack_python.egg-info/dependency_links.txt
10
10
  u_msgpack_python.egg-info/top_level.txt
11
11
  umsgpack/__init__.py
12
- umsgpack/__init__.pyi
12
+ umsgpack/__init__.pyi
13
+ umsgpack/py.typed
@@ -1,16 +1,17 @@
1
- # u-msgpack-python v2.8.0 - v at sergeev.io
1
+ # u-msgpack-python v2.9.0 - v at sergeev.io
2
2
  # https://github.com/vsergeev/u-msgpack-python
3
3
  #
4
4
  # u-msgpack-python is a lightweight MessagePack serializer and deserializer
5
- # module, compatible with both Python 2 and 3, as well CPython and PyPy
5
+ # module, compatible with both Python 2 and 3, as well as CPython and PyPy
6
6
  # implementations of Python. u-msgpack-python is fully compliant with the
7
- # latest MessagePack specification.com/msgpack/msgpack/blob/master/spec.md). In
8
- # particular, it supports the new binary, UTF-8 string, and application ext
9
- # types.
7
+ # latest MessagePack specification
8
+ # (https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it
9
+ # supports the new binary, UTF-8 string, application-defined ext, and
10
+ # timestamp types.
10
11
  #
11
12
  # MIT License
12
13
  #
13
- # Copyright (c) 2013-2023 vsergeev / Ivan (Vanya) A. Sergeev
14
+ # Copyright (c) 2013-2026 vsergeev / Ivan (Vanya) A. Sergeev
14
15
  #
15
16
  # Permission is hereby granted, free of charge, to any person obtaining a copy
16
17
  # of this software and associated documentation files (the "Software"), to deal
@@ -31,20 +32,22 @@
31
32
  # THE SOFTWARE.
32
33
  #
33
34
  """
34
- u-msgpack-python v2.8.0 - v at sergeev.io
35
+ u-msgpack-python v2.9.0 - v at sergeev.io
35
36
  https://github.com/vsergeev/u-msgpack-python
36
37
 
37
38
  u-msgpack-python is a lightweight MessagePack serializer and deserializer
38
- module, compatible with both Python 2 and 3, as well CPython and PyPy
39
+ module, compatible with both Python 2 and 3, as well as CPython and PyPy
39
40
  implementations of Python. u-msgpack-python is fully compliant with the
40
- latest MessagePack specification.com/msgpack/msgpack/blob/master/spec.md). In
41
- particular, it supports the new binary, UTF-8 string, and application ext
41
+ latest MessagePack specification
42
+ (https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it
43
+ supports the new binary, UTF-8 string, application-defined ext, and timestamp
42
44
  types.
43
45
 
44
46
  License: MIT
45
47
  """
46
48
  import struct
47
49
  import collections
50
+ import functools
48
51
  import datetime
49
52
  import sys
50
53
  import io
@@ -54,10 +57,11 @@ if sys.version_info[0:2] >= (3, 3):
54
57
  else:
55
58
  from collections import Hashable
56
59
 
57
- __version__ = "2.8.0"
60
+
61
+ __version__ = "2.9.0"
58
62
  "Module version string"
59
63
 
60
- version = (2, 8, 0)
64
+ version = (2, 9, 0)
61
65
  "Module version tuple"
62
66
 
63
67
 
@@ -91,10 +95,10 @@ class Ext(object):
91
95
  Example:
92
96
  >>> foo = umsgpack.Ext(5, b"\\x01\\x02\\x03")
93
97
  >>> umsgpack.packb({u"special stuff": foo, u"awesome": True})
94
- '\\x82\\xa7awesome\\xc3\\xadspecial stuff\\xc7\\x03\\x05\\x01\\x02\\x03'
98
+ b'\\x82\\xadspecial stuff\\xc7\\x03\\x05\\x01\\x02\\x03\\xa7awesome\\xc3'
95
99
  >>> bar = umsgpack.unpackb(_)
96
100
  >>> print(bar["special stuff"])
97
- Ext Object (Type: 5, Data: 01 02 03)
101
+ Ext Object (Type: 5, Data: 0x01 0x02 0x03)
98
102
  """
99
103
  # Check type is type int and in range
100
104
  if not isinstance(type, int):
@@ -238,6 +242,10 @@ class DuplicateKeyException(UnpackException):
238
242
  "Duplicate key encountered during map unpacking."
239
243
 
240
244
 
245
+ class MaximumDepthException(UnpackException):
246
+ "Maximum nesting depth exceeded during unpacking."
247
+
248
+
241
249
  # Backwards compatibility
242
250
  KeyNotPrimitiveException = UnhashableKeyException
243
251
  KeyDuplicateException = DuplicateKeyException
@@ -442,7 +450,7 @@ def _pack_map(obj, fp, options):
442
450
  elif obj_len < 2**32:
443
451
  fp.write(b"\xdf" + struct.pack(">I", obj_len))
444
452
  else:
445
- raise UnsupportedTypeException("huge array")
453
+ raise UnsupportedTypeException("huge map")
446
454
 
447
455
  for k, v in obj.items():
448
456
  pack(k, fp, **options)
@@ -480,8 +488,6 @@ def _pack2(obj, fp, **options):
480
488
  >>> f = open('test.bin', 'wb')
481
489
  >>> umsgpack.pack({u"compact": True, u"schema": 0}, f)
482
490
  """
483
- global compatibility
484
-
485
491
  ext_handlers = options.get("ext_handlers")
486
492
 
487
493
  if obj is None:
@@ -566,8 +572,6 @@ def _pack3(obj, fp, **options):
566
572
  >>> f = open('test.bin', 'wb')
567
573
  >>> umsgpack.pack({u"compact": True, u"schema": 0}, f)
568
574
  """
569
- global compatibility
570
-
571
575
  ext_handlers = options.get("ext_handlers")
572
576
 
573
577
  if obj is None:
@@ -777,7 +781,6 @@ def _unpack_string(code, fp, options):
777
781
  raise Exception("logic error, not string: 0x{:02x}".format(ord(code)))
778
782
 
779
783
  # Always return raw bytes in compatibility mode
780
- global compatibility
781
784
  if compatibility:
782
785
  return _read_except(fp, length)
783
786
 
@@ -868,7 +871,7 @@ def _unpack_ext_timestamp(ext_data, options):
868
871
  microseconds=microseconds)
869
872
 
870
873
 
871
- def _unpack_array(code, fp, options):
874
+ def _unpack_array(code, fp, options, depth=None):
872
875
  if (ord(code) & 0xf0) == 0x90:
873
876
  length = (ord(code) & ~0xf0)
874
877
  elif code == b'\xdc':
@@ -879,9 +882,12 @@ def _unpack_array(code, fp, options):
879
882
  raise Exception("logic error, not array: 0x{:02x}".format(ord(code)))
880
883
 
881
884
  if options.get('use_tuple'):
882
- return tuple((_unpack(fp, options) for i in xrange(length)))
885
+ return tuple((_unpack(fp, options) if depth is None
886
+ else _unpack_depth(fp, options, depth)
887
+ for i in xrange(length)))
883
888
 
884
- return [_unpack(fp, options) for i in xrange(length)]
889
+ return [_unpack(fp, options) if depth is None
890
+ else _unpack_depth(fp, options, depth) for i in xrange(length)]
885
891
 
886
892
 
887
893
  def _deep_list_to_tuple(obj):
@@ -890,7 +896,7 @@ def _deep_list_to_tuple(obj):
890
896
  return obj
891
897
 
892
898
 
893
- def _unpack_map(code, fp, options):
899
+ def _unpack_map(code, fp, options, depth=None):
894
900
  if (ord(code) & 0xf0) == 0x80:
895
901
  length = (ord(code) & ~0xf0)
896
902
  elif code == b'\xde':
@@ -903,7 +909,8 @@ def _unpack_map(code, fp, options):
903
909
  d = {} if not options.get('use_ordered_dict') else collections.OrderedDict()
904
910
  for _ in xrange(length):
905
911
  # Unpack key
906
- k = _unpack(fp, options)
912
+ k = _unpack(fp, options) if depth is None \
913
+ else _unpack_depth(fp, options, depth)
907
914
 
908
915
  if isinstance(k, list):
909
916
  # Attempt to convert list into a hashable tuple
@@ -916,7 +923,8 @@ def _unpack_map(code, fp, options):
916
923
  "encountered duplicate key: \"{:s}\" ({:s})".format(str(k), str(type(k))))
917
924
 
918
925
  # Unpack value
919
- v = _unpack(fp, options)
926
+ v = _unpack(fp, options) if depth is None \
927
+ else _unpack_depth(fp, options, depth)
920
928
 
921
929
  try:
922
930
  d[k] = v
@@ -930,6 +938,33 @@ def _unpack(fp, options):
930
938
  code = _read_except(fp, 1)
931
939
  return _unpack_dispatch_table[code](code, fp, options)
932
940
 
941
+
942
+ def _unpack_depth(fp, options, depth):
943
+ code = _read_except(fp, 1)
944
+ return _unpack_depth_dispatch_table[code](code, fp, options, depth)
945
+
946
+
947
+ def _limit_depth(unpack_container):
948
+ """Wrap a depth-aware container unpacker to enforce options['max_depth'].
949
+
950
+ depth is passed as a local argument and incremented on entry.
951
+ """
952
+ @functools.wraps(unpack_container)
953
+ def wrapper(code, fp, options, depth):
954
+ depth += 1
955
+ if depth > options['max_depth']:
956
+ raise MaximumDepthException("maximum nesting depth exceeded")
957
+ return unpack_container(code, fp, options, depth)
958
+ return wrapper
959
+
960
+
961
+ def _ignore_depth(unpacker):
962
+ """Adapt a scalar (non-container) unpacker to the depth dispatch signature."""
963
+ @functools.wraps(unpacker)
964
+ def wrapper(code, fp, options, depth):
965
+ return unpacker(code, fp, options)
966
+ return wrapper
967
+
933
968
  ########################################
934
969
 
935
970
 
@@ -951,6 +986,10 @@ def _unpack2(fp, **options):
951
986
  allow_invalid_utf8 (bool): unpack invalid strings into instances of
952
987
  :class:`InvalidString`, for access to the
953
988
  bytes (default False)
989
+ max_depth (int): maximum nesting depth of arrays/maps to unpack; when
990
+ exceeded, :class:`MaximumDepthException` is raised. If
991
+ None (default), depth is bounded only by the
992
+ interpreter's recursion limit.
954
993
 
955
994
  Returns:
956
995
  Python object
@@ -969,13 +1008,23 @@ def _unpack2(fp, **options):
969
1008
  The serialized map cannot be deserialized into a Python dictionary.
970
1009
  DuplicateKeyException(UnpackException):
971
1010
  Duplicate key encountered during map unpacking.
1011
+ MaximumDepthException(UnpackException):
1012
+ Maximum nesting depth exceeded while unpacking a deeply-nested
1013
+ object, or the configured limit was exceeded (when ``max_depth`` is
1014
+ set).
972
1015
 
973
1016
  Example:
974
1017
  >>> f = open('test.bin', 'rb')
975
- >>> umsgpack.unpackb(f)
1018
+ >>> umsgpack.unpack(f)
976
1019
  {u'compact': True, u'schema': 0}
977
1020
  """
978
- return _unpack(fp, options)
1021
+ try:
1022
+ return _unpack(fp, options) if options.get('max_depth') is None \
1023
+ else _unpack_depth(fp, options, 0)
1024
+ except RuntimeError as e:
1025
+ if "maximum recursion depth" in str(e):
1026
+ raise MaximumDepthException("maximum nesting depth exceeded")
1027
+ raise
979
1028
 
980
1029
 
981
1030
  def _unpack3(fp, **options):
@@ -996,6 +1045,10 @@ def _unpack3(fp, **options):
996
1045
  allow_invalid_utf8 (bool): unpack invalid strings into instances of
997
1046
  :class:`InvalidString`, for access to the
998
1047
  bytes (default False)
1048
+ max_depth (int): maximum nesting depth of arrays/maps to unpack; when
1049
+ exceeded, :class:`MaximumDepthException` is raised. If
1050
+ None (default), depth is bounded only by the
1051
+ interpreter's recursion limit.
999
1052
 
1000
1053
  Returns:
1001
1054
  Python object
@@ -1014,13 +1067,21 @@ def _unpack3(fp, **options):
1014
1067
  The serialized map cannot be deserialized into a Python dictionary.
1015
1068
  DuplicateKeyException(UnpackException):
1016
1069
  Duplicate key encountered during map unpacking.
1070
+ MaximumDepthException(UnpackException):
1071
+ Maximum nesting depth exceeded while unpacking a deeply-nested
1072
+ object, or the configured limit was exceeded (when ``max_depth`` is
1073
+ set).
1017
1074
 
1018
1075
  Example:
1019
1076
  >>> f = open('test.bin', 'rb')
1020
- >>> umsgpack.unpackb(f)
1077
+ >>> umsgpack.unpack(f)
1021
1078
  {'compact': True, 'schema': 0}
1022
1079
  """
1023
- return _unpack(fp, options)
1080
+ try:
1081
+ return _unpack(fp, options) if options.get('max_depth') is None \
1082
+ else _unpack_depth(fp, options, 0)
1083
+ except RecursionError:
1084
+ raise MaximumDepthException("maximum nesting depth exceeded")
1024
1085
 
1025
1086
 
1026
1087
  # For Python 2, expects a str object
@@ -1042,6 +1103,10 @@ def _unpackb2(s, **options):
1042
1103
  allow_invalid_utf8 (bool): unpack invalid strings into instances of
1043
1104
  :class:`InvalidString`, for access to the
1044
1105
  bytes (default False)
1106
+ max_depth (int): maximum nesting depth of arrays/maps to unpack; when
1107
+ exceeded, :class:`MaximumDepthException` is raised. If
1108
+ None (default), depth is bounded only by the
1109
+ interpreter's recursion limit.
1045
1110
 
1046
1111
  Returns:
1047
1112
  Python object
@@ -1062,6 +1127,10 @@ def _unpackb2(s, **options):
1062
1127
  The serialized map cannot be deserialized into a Python dictionary.
1063
1128
  DuplicateKeyException(UnpackException):
1064
1129
  Duplicate key encountered during map unpacking.
1130
+ MaximumDepthException(UnpackException):
1131
+ Maximum nesting depth exceeded while unpacking a deeply-nested
1132
+ object, or the configured limit was exceeded (when ``max_depth`` is
1133
+ set).
1065
1134
 
1066
1135
  Example:
1067
1136
  >>> umsgpack.unpackb(b'\\x82\\xa7compact\\xc3\\xa6schema\\x00')
@@ -1069,7 +1138,13 @@ def _unpackb2(s, **options):
1069
1138
  """
1070
1139
  if not isinstance(s, (str, bytearray)):
1071
1140
  raise TypeError("packed data must be type 'str' or 'bytearray'")
1072
- return _unpack(io.BytesIO(s), options)
1141
+ try:
1142
+ return _unpack(io.BytesIO(s), options) if options.get('max_depth') is None \
1143
+ else _unpack_depth(io.BytesIO(s), options, 0)
1144
+ except RuntimeError as e:
1145
+ if "maximum recursion depth" in str(e):
1146
+ raise MaximumDepthException("maximum nesting depth exceeded")
1147
+ raise
1073
1148
 
1074
1149
 
1075
1150
  # For Python 3, expects a bytes object
@@ -1091,6 +1166,10 @@ def _unpackb3(s, **options):
1091
1166
  allow_invalid_utf8 (bool): unpack invalid strings into instances of
1092
1167
  :class:`InvalidString`, for access to the
1093
1168
  bytes (default False)
1169
+ max_depth (int): maximum nesting depth of arrays/maps to unpack; when
1170
+ exceeded, :class:`MaximumDepthException` is raised. If
1171
+ None (default), depth is bounded only by the
1172
+ interpreter's recursion limit.
1094
1173
 
1095
1174
  Returns:
1096
1175
  Python object
@@ -1111,6 +1190,10 @@ def _unpackb3(s, **options):
1111
1190
  The serialized map cannot be deserialized into a Python dictionary.
1112
1191
  DuplicateKeyException(UnpackException):
1113
1192
  Duplicate key encountered during map unpacking.
1193
+ MaximumDepthException(UnpackException):
1194
+ Maximum nesting depth exceeded while unpacking a deeply-nested
1195
+ object, or the configured limit was exceeded (when ``max_depth`` is
1196
+ set).
1114
1197
 
1115
1198
  Example:
1116
1199
  >>> umsgpack.unpackb(b'\\x82\\xa7compact\\xc3\\xa6schema\\x00')
@@ -1118,7 +1201,11 @@ def _unpackb3(s, **options):
1118
1201
  """
1119
1202
  if not isinstance(s, (bytes, bytearray)):
1120
1203
  raise TypeError("packed data must be type 'bytes' or 'bytearray'")
1121
- return _unpack(io.BytesIO(s), options)
1204
+ try:
1205
+ return _unpack(io.BytesIO(s), options) if options.get('max_depth') is None \
1206
+ else _unpack_depth(io.BytesIO(s), options, 0)
1207
+ except RecursionError:
1208
+ raise MaximumDepthException("maximum nesting depth exceeded")
1122
1209
 
1123
1210
  #############################################################################
1124
1211
  # Module Initialization
@@ -1194,7 +1281,7 @@ def __init():
1194
1281
  # Build a dispatch table for fast lookup of unpacking function
1195
1282
 
1196
1283
  _unpack_dispatch_table = {}
1197
- # Fix uint
1284
+ # Positive fixint
1198
1285
  for code in range(0, 0x7f + 1):
1199
1286
  _unpack_dispatch_table[struct.pack("B", code)] = _unpack_integer
1200
1287
  # Fix map
@@ -1244,5 +1331,17 @@ def __init():
1244
1331
  for code in range(0xe0, 0xff + 1):
1245
1332
  _unpack_dispatch_table[struct.pack("B", code)] = _unpack_integer
1246
1333
 
1334
+ # Parallel dispatch table for the optional max_depth path. The array/map
1335
+ # entries are the same container functions as the default table, wrapped by
1336
+ # _limit_depth; scalar unpackers are adapted to accept (and ignore) the
1337
+ # depth argument so _unpack() can dispatch uniformly with no per-code switch.
1338
+ global _unpack_depth_dispatch_table
1339
+ _unpack_depth_dispatch_table = {}
1340
+ for code_bytes, unpacker in _unpack_dispatch_table.items():
1341
+ if unpacker is _unpack_array or unpacker is _unpack_map:
1342
+ _unpack_depth_dispatch_table[code_bytes] = _limit_depth(unpacker)
1343
+ else:
1344
+ _unpack_depth_dispatch_table[code_bytes] = _ignore_depth(unpacker)
1345
+
1247
1346
 
1248
1347
  __init()
@@ -0,0 +1,126 @@
1
+ from typing import Any, Callable, Protocol, TypeVar
2
+
3
+ _T = TypeVar("_T")
4
+
5
+ __version__: str
6
+
7
+ version: tuple[int, int, int]
8
+
9
+
10
+ def pack(
11
+ obj: Any,
12
+ fp: _SupportsWrite,
13
+ *,
14
+ ext_handlers: dict[type[Any], Callable[[Any], Ext]] | None = None,
15
+ force_float_precision: str = ...,
16
+ ) -> None: ...
17
+
18
+
19
+ def packb(
20
+ obj: Any,
21
+ *,
22
+ ext_handlers: dict[type[Any], Callable[[Any], Ext]] | None = None,
23
+ force_float_precision: str = ...,
24
+ ) -> bytes: ...
25
+
26
+
27
+ def dump(
28
+ obj: Any,
29
+ fp: _SupportsWrite,
30
+ *,
31
+ ext_handlers: dict[type[Any], Callable[[Any], Ext]] | None = None,
32
+ force_float_precision: str = ...,
33
+ ) -> None: ...
34
+
35
+
36
+ def dumps(
37
+ obj: Any,
38
+ *,
39
+ ext_handlers: dict[type[Any], Callable[[Any], Ext]] | None = None,
40
+ force_float_precision: str = ...,
41
+ ) -> bytes: ...
42
+
43
+
44
+ def unpackb(
45
+ s: bytes | bytearray | memoryview,
46
+ *,
47
+ ext_handlers: dict[int, Callable[[Ext], Any]] | None = None,
48
+ use_ordered_dict: bool = False,
49
+ use_tuple: bool = False,
50
+ allow_invalid_utf8: bool = False,
51
+ max_depth: int | None = None,
52
+ ) -> Any: ...
53
+
54
+
55
+ def unpack(
56
+ fp: _SupportsRead,
57
+ *,
58
+ ext_handlers: dict[int, Callable[[Ext], Any]] | None = None,
59
+ use_ordered_dict: bool = False,
60
+ use_tuple: bool = False,
61
+ allow_invalid_utf8: bool = False,
62
+ max_depth: int | None = None,
63
+ ) -> Any: ...
64
+
65
+
66
+ def loads(
67
+ s: bytes | bytearray | memoryview,
68
+ *,
69
+ ext_handlers: dict[int, Callable[[Ext], Any]] | None = None,
70
+ use_ordered_dict: bool = False,
71
+ use_tuple: bool = False,
72
+ allow_invalid_utf8: bool = False,
73
+ max_depth: int | None = None,
74
+ ) -> Any: ...
75
+
76
+
77
+ def load(
78
+ fp: _SupportsRead,
79
+ *,
80
+ ext_handlers: dict[int, Callable[[Ext], Any]] | None = None,
81
+ use_ordered_dict: bool = False,
82
+ use_tuple: bool = False,
83
+ allow_invalid_utf8: bool = False,
84
+ max_depth: int | None = None,
85
+ ) -> Any: ...
86
+
87
+
88
+ class _SupportsRead(Protocol):
89
+ def read(self, __size: int) -> bytes: ...
90
+
91
+
92
+ class _SupportsWrite(Protocol):
93
+ def write(self, __data: bytes) -> Any: ...
94
+
95
+
96
+ class Ext:
97
+ type: int
98
+ data: bytes
99
+ def __init__(self, type: int, data: bytes) -> None: ...
100
+ def __eq__(self, other: object) -> bool: ...
101
+ def __ne__(self, other: object) -> bool: ...
102
+ def __hash__(self) -> int: ...
103
+
104
+
105
+ class InvalidString(bytes): ...
106
+
107
+
108
+ def ext_serializable(ext_type: int) -> Callable[[type[_T]], type[_T]]: ...
109
+
110
+
111
+ class PackException(Exception): ...
112
+ class UnpackException(Exception): ...
113
+ class UnsupportedTypeException(PackException): ...
114
+ class InsufficientDataException(UnpackException): ...
115
+ class InvalidStringException(UnpackException): ...
116
+ class UnsupportedTimestampException(UnpackException): ...
117
+ class ReservedCodeException(UnpackException): ...
118
+ class UnhashableKeyException(UnpackException): ...
119
+ class DuplicateKeyException(UnpackException): ...
120
+ class MaximumDepthException(UnpackException): ...
121
+
122
+
123
+ KeyNotPrimitiveException = UnhashableKeyException
124
+ KeyDuplicateException = DuplicateKeyException
125
+
126
+ compatibility: bool
File without changes
@@ -1,41 +0,0 @@
1
- from typing import Any
2
-
3
- __version__: str
4
-
5
- version: tuple[int, int, int]
6
-
7
- def pack(obj, fp, **options) -> None: ...
8
- def packb(obj, **options) -> bytes: ...
9
- def dump(obj, fp, **options) -> None: ...
10
- def dumps(obj, **options) -> bytes: ...
11
-
12
- def unpackb(s: bytes | bytearray, **options) -> Any: ...
13
- def unpack(fp, **options) -> Any: ...
14
- def loads(s: bytes | bytearray, **options) -> Any: ...
15
- def load(fp, **options) -> Any: ...
16
-
17
- class Ext:
18
- type: int
19
- data: bytes
20
- def __init__(self, type: int, data: bytes) -> None: ...
21
- def __eq__(self, other) -> bool: ...
22
- def __ne__(self, other) -> bool: ...
23
- def __hash__(self) -> int: ...
24
-
25
- class InvalidString(bytes): ...
26
-
27
- def ext_serializable(ext_type: int): ...
28
-
29
- class PackException(Exception): ...
30
- class UnpackException(Exception): ...
31
- class UnsupportedTypeException(PackException): ...
32
- class InsufficientDataException(UnpackException): ...
33
- class InvalidStringException(UnpackException): ...
34
- class UnsupportedTimestampException(UnpackException): ...
35
- class ReservedCodeException(UnpackException): ...
36
- class UnhashableKeyException(UnpackException): ...
37
- class DuplicateKeyException(UnpackException): ...
38
- KeyNotPrimitiveException = UnhashableKeyException
39
- KeyDuplicateException = DuplicateKeyException
40
-
41
- compatibility: bool