Author: Brian Kearns <[email protected]>
Branch: stdlib-2.7.6
Changeset: r69621:a7c8beafaa1b
Date: 2014-03-02 13:06 -0500
http://bitbucket.org/pypy/pypy/changeset/a7c8beafaa1b/
Log: fix zlib decompress flush
diff --git a/pypy/module/zlib/interp_zlib.py b/pypy/module/zlib/interp_zlib.py
--- a/pypy/module/zlib/interp_zlib.py
+++ b/pypy/module/zlib/interp_zlib.py
@@ -157,7 +157,6 @@
rzlib.deflateEnd(self.stream)
self.stream = rzlib.null_stream
-
@unwrap_spec(data='bufferstr')
def compress(self, data):
"""
@@ -181,7 +180,6 @@
raise zlib_error(self.space, e.msg)
return self.space.wrap(result)
-
@unwrap_spec(mode=int)
def flush(self, mode=rzlib.Z_FINISH):
"""
@@ -270,6 +268,15 @@
rzlib.inflateEnd(self.stream)
self.stream = rzlib.null_stream
+ def _save_unconsumed_input(self, data, finished, unused_len):
+ unused_start = len(data) - unused_len
+ assert unused_start >= 0
+ tail = data[unused_start:]
+ if finished:
+ self.unconsumed_tail = ''
+ self.unused_data += tail
+ else:
+ self.unconsumed_tail = tail
@unwrap_spec(data='bufferstr', max_length=int)
def decompress(self, data, max_length=0):
@@ -290,41 +297,39 @@
try:
self.lock()
try:
- result = rzlib.decompress(self.stream, data,
- max_length = max_length)
+ result = rzlib.decompress(self.stream, data,
max_length=max_length)
finally:
self.unlock()
except rzlib.RZlibError, e:
raise zlib_error(self.space, e.msg)
string, finished, unused_len = result
- unused_start = len(data) - unused_len
- assert unused_start >= 0
- tail = data[unused_start:]
- if finished:
- self.unconsumed_tail = ''
- self.unused_data += tail
- else:
- self.unconsumed_tail = tail
+ self._save_unconsumed_input(data, finished, unused_len)
return self.space.wrap(string)
-
- @unwrap_spec(length=int)
- def flush(self, length=sys.maxint):
+ @unwrap_spec(mode="c_int")
+ def flush(self, mode=rzlib.Z_FINISH):
"""
flush( [length] ) -- This is kept for backward compatibility,
because each call to decompress() immediately returns as much
data as possible.
"""
- if length <= 0:
- raise OperationError(self.space.w_ValueError, self.space.wrap(
- "length must be greater than zero"))
- # We could call rzlib.decompress(self.stream, '', rzlib.Z_FINISH)
- # which would complain if the input stream so far is not complete;
- # however CPython's zlib module does not behave like that.
- # I could not figure out a case in which flush() in CPython
- # doesn't simply return an empty string without complaining.
- return self.space.wrap("")
+ if mode == rzlib.Z_NO_FLUSH:
+ return space.wrap("")
+
+ data = self.unconsumed_tail
+ try:
+ self.lock()
+ try:
+ result = rzlib.decompress(self.stream, data, mode)
+ finally:
+ self.unlock()
+ except rzlib.RZlibError, e:
+ raise zlib_error(self.space, e.msg)
+
+ string, finished, unused_len = result
+ self._save_unconsumed_input(data, finished, unused_len)
+ return self.space.wrap(string)
@unwrap_spec(wbits=int)
diff --git a/pypy/module/zlib/test/test_zlib.py
b/pypy/module/zlib/test/test_zlib.py
--- a/pypy/module/zlib/test/test_zlib.py
+++ b/pypy/module/zlib/test/test_zlib.py
@@ -1,4 +1,3 @@
-
"""
Tests for the zlib module.
"""
@@ -12,7 +11,7 @@
from pypy.module.zlib import interp_zlib
except ImportError:
import py; py.test.skip("no zlib C library on this machine")
-
+
def test_unsigned_to_signed_32bit():
assert interp_zlib.unsigned_to_signed_32bit(123) == 123
assert interp_zlib.unsigned_to_signed_32bit(2**31) == -2**31
@@ -52,7 +51,6 @@
assert self.zlib.crc32('\0') == -771559539
assert self.zlib.crc32('hello, world.') == -936931198
-
def test_crc32_start_value(self):
"""
When called with a string and an integer, zlib.crc32 should compute the
@@ -94,7 +92,6 @@
assert self.zlib.adler32('hello, world.') == 571147447
assert self.zlib.adler32('x' * 23) == -2122904887
-
def test_adler32_start_value(self):
"""
When called with a string and an integer, zlib.adler32 should compute
@@ -114,7 +111,6 @@
assert self.zlib.adler32('foo', -1) == 45547858
assert self.zlib.adler32('foo', 99999999999999999999999) == -114818734
-
def test_invalidLevel(self):
"""
zlib.compressobj should raise ValueError when an out of bounds level is
@@ -123,7 +119,6 @@
raises(ValueError, self.zlib.compressobj, -2)
raises(ValueError, self.zlib.compressobj, 10)
-
def test_compression(self):
"""
zlib.compressobj should return an object which can be used to compress
@@ -134,7 +129,6 @@
bytes += compressor.flush()
assert bytes == self.compressed
-
def test_decompression(self):
"""
zlib.decompressobj should return an object which can be used to
@@ -145,7 +139,6 @@
bytes += decompressor.flush()
assert bytes == self.expanded
-
def test_compress(self):
"""
Test the zlib.compress() function.
@@ -153,7 +146,6 @@
bytes = self.zlib.compress(self.expanded)
assert bytes == self.compressed
-
def test_decompress(self):
"""
Test the zlib.decompress() function.
@@ -161,7 +153,6 @@
bytes = self.zlib.decompress(self.compressed)
assert bytes == self.expanded
-
def test_decompress_invalid_input(self):
"""
Try to feed garbage to zlib.decompress().
@@ -169,7 +160,6 @@
raises(self.zlib.error, self.zlib.decompress, self.compressed[:-2])
raises(self.zlib.error, self.zlib.decompress, 'foobar')
-
def test_unused_data(self):
"""
Try to feed too much data to zlib.decompress().
@@ -179,6 +169,8 @@
s = d.decompress(self.compressed + 'extrastuff')
assert s == self.expanded
assert d.unused_data == 'extrastuff'
+ assert d.flush() == ''
+ assert d.unused_data == 'extrastuff'
# try again with several decompression steps
d = self.zlib.decompressobj()
s1 = d.decompress(self.compressed[:10])
@@ -192,7 +184,6 @@
assert d.unused_data == ('spam' * 100) + ('egg' * 50)
assert s4 == ''
-
def test_max_length(self):
"""
Test the max_length argument of the decompress() method
@@ -206,7 +197,6 @@
data = d.unconsumed_tail
assert not data
-
def test_buffer(self):
"""
We should be able to pass buffer objects instead of strings.
@@ -229,3 +219,17 @@
bytes = self.zlib.decompress(buffer(self.compressed))
assert bytes == self.expanded
+
+ def test_flush_with_freed_input(self):
+ # Issue #16411: decompressor accesses input to last decompress() call
+ # in flush(), even if this object has been freed in the meanwhile.
+ input1 = 'abcdefghijklmnopqrstuvwxyz'
+ input2 = 'QWERTYUIOPASDFGHJKLZXCVBNM'
+ data = self.zlib.compress(input1)
+ dco = self.zlib.decompressobj()
+ dco.decompress(data, 1)
+ del data
+ data = self.zlib.compress(input2)
+ assert dco.flush() == input1[1:]
+ assert dco.unused_data == ''
+ assert dco.unconsumed_tail == ''
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit