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

Reply via email to