Commit c3ed2e7f authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #9962: GzipFile now has the peek() method.

parent 4c2e4fa2
...@@ -25,10 +25,10 @@ The module defines the following items: ...@@ -25,10 +25,10 @@ The module defines the following items:
.. class:: GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None) .. class:: GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None)
Constructor for the :class:`GzipFile` class, which simulates most of the methods Constructor for the :class:`GzipFile` class, which simulates most of the
of a :term:`file object`, with the exception of the :meth:`readinto` and methods of a :term:`file object`, with the exception of the :meth:`truncate`
:meth:`truncate` methods. At least one of *fileobj* and *filename* must be method. At least one of *fileobj* and *filename* must be given a non-trivial
given a non-trivial value. value.
The new class instance is based on *fileobj*, which can be a regular file, a The new class instance is based on *fileobj*, which can be a regular file, a
:class:`StringIO` object, or any other object which simulates a file. It :class:`StringIO` object, or any other object which simulates a file. It
...@@ -66,8 +66,9 @@ The module defines the following items: ...@@ -66,8 +66,9 @@ The module defines the following items:
writing as *fileobj*, and retrieve the resulting memory buffer using the writing as *fileobj*, and retrieve the resulting memory buffer using the
:class:`io.BytesIO` object's :meth:`~io.BytesIO.getvalue` method. :class:`io.BytesIO` object's :meth:`~io.BytesIO.getvalue` method.
:class:`GzipFile` supports the whole :class:`io.BufferedIOBase` interface, :class:`GzipFile` supports the :class:`io.BufferedIOBase` interface,
including iteration and the :keyword:`with` statement. including iteration and the :keyword:`with` statement. Only the
:meth:`truncate` method isn't implemented.
.. versionchanged:: 3.1 .. versionchanged:: 3.1
Support for the :keyword:`with` statement was added. Support for the :keyword:`with` statement was added.
...@@ -78,6 +79,9 @@ The module defines the following items: ...@@ -78,6 +79,9 @@ The module defines the following items:
.. versionchanged:: 3.2 .. versionchanged:: 3.2
Support for unseekable files was added. Support for unseekable files was added.
.. versionchanged:: 3.2
The :meth:`peek` method was implemented.
.. function:: open(filename, mode='rb', compresslevel=9) .. function:: open(filename, mode='rb', compresslevel=9)
......
...@@ -204,7 +204,10 @@ class GzipFile(io.BufferedIOBase): ...@@ -204,7 +204,10 @@ class GzipFile(io.BufferedIOBase):
return self.name return self.name
def __repr__(self): def __repr__(self):
s = repr(self.fileobj) fileobj = self.fileobj
if isinstance(fileobj, _PaddedFile):
fileobj = fileobj.file
s = repr(fileobj)
return '<gzip ' + s[1:-1] + ' ' + hex(id(self)) + '>' return '<gzip ' + s[1:-1] + ' ' + hex(id(self)) + '>'
def _init_write(self, filename): def _init_write(self, filename):
...@@ -336,6 +339,26 @@ class GzipFile(io.BufferedIOBase): ...@@ -336,6 +339,26 @@ class GzipFile(io.BufferedIOBase):
self.offset += size self.offset += size
return chunk return chunk
def peek(self, n):
if self.mode != READ:
import errno
raise IOError(errno.EBADF, "read() on write-only GzipFile object")
# Do not return ridiculously small buffers
if n < 100:
n = 100
if self.extrasize == 0:
if self.fileobj is None:
return b''
try:
self._read(max(self.max_read_chunk, n))
except EOFError:
pass
offset = self.offset - self.extrastart
remaining = self.extrasize
assert remaining == len(self.extrabuf) - offset
return self.extrabuf[offset:offset + n]
def _unread(self, buf): def _unread(self, buf):
self.extrasize = len(buf) + self.extrasize self.extrasize = len(buf) + self.extrasize
self.offset -= len(buf) self.offset -= len(buf)
......
...@@ -286,6 +286,28 @@ class TestGzip(unittest.TestCase): ...@@ -286,6 +286,28 @@ class TestGzip(unittest.TestCase):
with gzip.GzipFile(fileobj=buf, mode="rb") as f: with gzip.GzipFile(fileobj=buf, mode="rb") as f:
self.assertEqual(f.read(), uncompressed) self.assertEqual(f.read(), uncompressed)
def test_peek(self):
uncompressed = data1 * 200
with gzip.GzipFile(self.filename, "wb") as f:
f.write(uncompressed)
def sizes():
while True:
for n in range(5, 50, 10):
yield n
with gzip.GzipFile(self.filename, "rb") as f:
f.max_read_chunk = 33
nread = 0
for n in sizes():
s = f.peek(n)
if s == b'':
break
self.assertEqual(f.read(len(s)), s)
nread += len(s)
self.assertEqual(f.read(100), b'')
self.assertEqual(nread, len(uncompressed))
# Testing compress/decompress shortcut functions # Testing compress/decompress shortcut functions
def test_compress(self): def test_compress(self):
......
...@@ -76,6 +76,8 @@ Core and Builtins ...@@ -76,6 +76,8 @@ Core and Builtins
Library Library
------- -------
- Issue #9962: GzipFile now has the peek() method.
- Issue #9090: When a socket with a timeout fails with EWOULDBLOCK or EAGAIN, - Issue #9090: When a socket with a timeout fails with EWOULDBLOCK or EAGAIN,
retry the select() loop instead of bailing out. This is because select() retry the select() loop instead of bailing out. This is because select()
can incorrectly report a socket as ready for reading (for example, if it can incorrectly report a socket as ready for reading (for example, if it
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment