@@ -106,6 +106,7 @@ cdef class Packer:
106106 cdef object _berrors
107107 cdef const char * unicode_errors
108108 cdef size_t exports # number of exported buffers
109+ cdef int _packing # public pack methods currently running
109110 cdef bint strict_types
110111 cdef bint use_float
111112 cdef bint autoreset
@@ -118,6 +119,7 @@ cdef class Packer:
118119 self .pk.buf_size = buf_size
119120 self .pk.length = 0
120121 self .exports = 0
122+ self ._packing = 0
121123
122124 def __dealloc__ (self ):
123125 PyMem_Free(self .pk.buf)
@@ -273,29 +275,40 @@ cdef class Packer:
273275 def pack (self , object obj ):
274276 cdef int ret
275277 self ._check_exports()
278+ # User code runs inside _pack (default, items(), iteration) and can
279+ # export this buffer. Hold the flag until every write from this call
280+ # has finished, including a nested pack.
281+ self ._packing += 1
276282 try :
277- ret = self ._pack(obj, DEFAULT_RECURSE_LIMIT)
278- except :
279- self .pk.length = 0
280- raise
281- if ret: # should not happen.
282- raise RuntimeError (" internal error" )
283- if self .autoreset:
284- buf = PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
285- self .pk.length = 0
286- return buf
283+ try :
284+ ret = self ._pack(obj, DEFAULT_RECURSE_LIMIT)
285+ except :
286+ self .pk.length = 0
287+ raise
288+ if ret: # should not happen.
289+ raise RuntimeError (" internal error" )
290+ if self .autoreset:
291+ buf = PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
292+ self .pk.length = 0
293+ return buf
294+ finally :
295+ self ._packing -= 1
287296
288297 @cython.critical_section
289298 def pack_ext_type (self , typecode , data ):
290299 self ._check_exports()
291- if len (data) > ITEM_LIMIT:
292- raise ValueError (" ext data too large" )
293- msgpack_pack_ext(& self .pk, typecode, len (data))
294- msgpack_pack_raw_body(& self .pk, data, len (data))
295- if self .autoreset:
296- buf = PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
297- self .pk.length = 0
298- return buf
300+ self ._packing += 1
301+ try :
302+ if len (data) > ITEM_LIMIT:
303+ raise ValueError (" ext data too large" )
304+ msgpack_pack_ext(& self .pk, typecode, len (data))
305+ msgpack_pack_raw_body(& self .pk, data, len (data))
306+ if self .autoreset:
307+ buf = PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
308+ self .pk.length = 0
309+ return buf
310+ finally :
311+ self ._packing -= 1
299312
300313 @cython.critical_section
301314 def pack_array_header (self , long long size ):
@@ -332,17 +345,21 @@ cdef class Packer:
332345 (`len(pairs)` and `for k, v in pairs:` should be supported.)
333346 """
334347 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)
342- if self .autoreset:
343- buf = PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
344- self .pk.length = 0
345- return buf
348+ self ._packing += 1
349+ try :
350+ size = len (pairs)
351+ if size > ITEM_LIMIT:
352+ raise ValueError (" map too large" )
353+ msgpack_pack_map(& self .pk, size)
354+ for k, v in pairs:
355+ self ._pack(k)
356+ self ._pack(v)
357+ if self .autoreset:
358+ buf = PyBytes_FromStringAndSize(self .pk.buf, self .pk.length)
359+ self .pk.length = 0
360+ return buf
361+ finally :
362+ self ._packing -= 1
346363
347364 @cython.critical_section
348365 def reset (self ):
@@ -362,11 +379,16 @@ cdef class Packer:
362379 """ Return memoryview of internal buffer.
363380
364381 Note: Packer now supports buffer protocol. You can use memoryview(packer).
382+ Raises BufferError while a pack method is still running.
365383 """
366384 return memoryview(self )
367385
368386 @cython.critical_section
369387 def __getbuffer__ (self , Py_buffer *buffer , int flags ):
388+ if self ._packing:
389+ raise BufferError(
390+ " Packer cannot export its buffer while packing is in progress"
391+ )
370392 PyBuffer_FillInfo(buffer , self , self .pk.buf, self .pk.length, 1 , flags)
371393 self .exports += 1
372394
0 commit comments