Skip to content

Commit a82be71

Browse files
committed
Fix pack_map_pairs recovery after packing errors
1 parent 51428c4 commit a82be71

3 files changed

Lines changed: 47 additions & 8 deletions

File tree

‎msgpack/_packer.pyx‎

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -332,13 +332,17 @@ cdef class Packer:
332332
(`len(pairs)` and `for k, v in pairs:` should be supported.)
333333
"""
334334
self._check_exports()
335-
size = len(pairs)
336-
if size > ITEM_LIMIT:
337-
raise ValueError("map too large")
338-
msgpack_pack_map(&self.pk, size)
339-
for k, v in pairs:
340-
self._pack(k)
341-
self._pack(v)
335+
try:
336+
size = len(pairs)
337+
if size > ITEM_LIMIT:
338+
raise ValueError("map too large")
339+
msgpack_pack_map(&self.pk, size)
340+
for k, v in pairs:
341+
self._pack(k)
342+
self._pack(v)
343+
except:
344+
self.pk.length = 0
345+
raise
342346
if self.autoreset:
343347
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
344348
self.pk.length = 0

‎msgpack/fallback.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,11 @@ def pack(self, obj):
830830
return ret
831831

832832
def pack_map_pairs(self, pairs):
833-
self._pack_map_pairs(len(pairs), pairs)
833+
try:
834+
self._pack_map_pairs(len(pairs), pairs)
835+
except:
836+
self._buffer = BytesIO()
837+
raise
834838
if self._autoreset:
835839
ret = self._buffer.getvalue()
836840
self._buffer = BytesIO()

‎test/test_pack.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,37 @@ def test_pairlist():
190190
assert pairlist == unpacked
191191

192192

193+
@pytest.mark.parametrize("autoreset", [True, False])
194+
@pytest.mark.parametrize("method", ["pack", "pack_map_pairs"])
195+
def test_packer_resets_after_default_error(autoreset, method):
196+
class Invoice:
197+
def __init__(self, ready):
198+
self.ready = ready
199+
200+
def default(invoice):
201+
if not invoice.ready:
202+
raise ValueError("invoice not ready")
203+
return {"amount": 15}
204+
205+
packer = Packer(default=default, autoreset=autoreset)
206+
packer.pack({"previous": 1})
207+
pack = getattr(packer, method)
208+
failed = [("id", 1), ("invoice", Invoice(False))]
209+
with pytest.raises(ValueError, match="invoice not ready"):
210+
pack(dict(failed) if method == "pack" else failed)
211+
assert packer.bytes() == b""
212+
213+
valid = [("invoice", Invoice(True))]
214+
packed = pack(dict(valid) if method == "pack" else valid)
215+
if autoreset:
216+
assert unpackb(packed) == {"invoice": {"amount": 15}}
217+
else:
218+
packer.pack({"next": 2})
219+
unpacker = Unpacker()
220+
unpacker.feed(packer.bytes())
221+
assert list(unpacker) == [{"invoice": {"amount": 15}}, {"next": 2}]
222+
223+
193224
def test_get_buffer():
194225
packer = Packer(autoreset=0, use_bin_type=True)
195226
packer.pack([1, 2])

0 commit comments

Comments
 (0)