Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions msgpack/_unpacker.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ cdef class Unpacker:
cdef object unicode_errors
cdef Py_ssize_t max_buffer_size
cdef uint64_t stream_offset
# stream_offset at the start of an incomplete object
cdef uint64_t object_offset
cdef bint _unpacking

def __dealloc__(self):
Expand Down Expand Up @@ -486,6 +488,8 @@ cdef class Unpacker:
raise RuntimeError("Unpacker cannot be called recursively while unpacking is in progress")
if self.ctx.execute != NULL and self.ctx.execute != execute:
raise ValueError("Cannot switch unpacking methods while an object is incomplete")
if self.ctx.execute == NULL:
self.object_offset = self.stream_offset

self._unpacking = True
try:
Expand Down Expand Up @@ -585,6 +589,9 @@ cdef class Unpacker:
number of bytes that were read from the input, also the starting
position of the next object.
"""
if self.ctx.execute != NULL:
# Part of the next object has been decoded, but it still starts here.
return self.object_offset
return self.stream_offset

def __iter__(self):
Expand Down
19 changes: 19 additions & 0 deletions test/test_sequnpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,22 @@ def test_unpack_tell():
m2 = next(unpacker)
assert m == m2
assert o == unpacker.tell()


def test_unpack_tell_incomplete_object():
# tell() is where the next object starts, even when only part of it has arrived.
first = packb([1, 2, 3])
second = packb(["x" * 40, "y" * 40])
packed = first + second
for cut in (len(first) + 1, len(first) + 3, len(first) + 45):
unpacker = Unpacker()
unpacker.feed(packed[:cut])
assert list(unpacker) == [[1, 2, 3]]
assert unpacker.tell() == len(first)
unpacker.feed(packed[cut:])
assert list(unpacker) == [["x" * 40, "y" * 40]]
assert unpacker.tell() == len(packed)

unpacker = Unpacker(io.BytesIO(packed[:cut]), read_size=16)
assert list(unpacker) == [[1, 2, 3]]
assert unpacker.tell() == len(first)
Loading