You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
GzipFile.readinto reads full file before copying into the provided buffer #128646
gzip.GzipFile uses the BufferedIOBase implementation of .readinto(), which simply calls .read and copies the result into a buffer. This negates the purpose of using .readinto() at all.
This may be considered more a missed optimization than a bug, but it is being reported in downstream tools and I've traced it back to CPython.
gzip.GzipFile uses the BufferedIOBase implementation of .readinto(), which simply calls .read and copies the result into a buffer. This negates the purpose of using .readinto() at all.
To expand on the above in hopes of saving some effort for a reviewer, here is my trace of calls:
gzip.GzipFile inherits from _compression.BaseStream, which inherits from io.BufferedIOBase.
These are the _pyio.py and bufferedio.c implementations of io.BufferedIOBase.readinto()/readinto1():
raiseOSError(errno.EBADF, "read() on write-only GzipFile object")
returnself._buffer.read(size)
defread1(self, size=-1):
"""Implements BufferedIOBase.read1()
Reads up to a buffer's worth of data if size is negative."""
self._check_not_closed()
ifself.mode!=READ:
importerrno
raiseOSError(errno.EBADF, "read1() on write-only GzipFile object")
ifsize<0:
size=io.DEFAULT_BUFFER_SIZE
returnself._buffer.read1(size)
Therefore, we are forcing self._buffer to grow its internal buffer to contain the full contents that we intend to pass into a buffer. Instead, we should directly call self._buffer.readinto()/readinto1(), which is implemented here:
Activity
pythongh-128646: Implement GzipFile.readinto() functions
pythongh-128646: Implement GzipFile.readinto() functions
effigies commentedon Jan 23, 2025
To expand on the above in hopes of saving some effort for a reviewer, here is my trace of calls:
gzip.GzipFileinherits from_compression.BaseStream, which inherits fromio.BufferedIOBase.These are the
_pyio.pyandbufferedio.cimplementations ofio.BufferedIOBase.readinto()/readinto1():cpython/Lib/_pyio.py
Lines 694 to 732 in ec91e1c
cpython/Modules/_io/bufferedio.c
Lines 48 to 109 in ec91e1c
These call
self.read()/read1(), whichgzip.GzipFileproxies to an internalself._buffer, which is anio.BufferedReader():cpython/Lib/gzip.py
Lines 206 to 210 in ec797d1
cpython/Lib/gzip.py
Lines 321 to 339 in ec797d1
Therefore, we are forcing
self._bufferto grow its internal buffer to contain the full contents that we intend to pass into a buffer. Instead, we should directly callself._buffer.readinto()/readinto1(), which is implemented here:cpython/Lib/_pyio.py
Lines 1144 to 1190 in ec91e1c
cpython/Modules/_io/bufferedio.c
Lines 1076 to 1184 in ec91e1c
This implementation avoids growing the internal buffer beyond an individual chunk.
effigies commentedon Feb 3, 2025
Following the advice in https://devguide.python.org/getting-started/pull-request-lifecycle/, I'm pinging this issue after ~1mo to see if any reviewers have time to look at this.
I'm not sure if this is enough of a bug to get backported, so I'd like to make sure that it gets resolved in 3.14.
gh-128646: Implement GzipFile.readinto[1]() methods (GH-128647)
effigies commentedon Mar 10, 2025
Closed by #128647.