mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-01 18:21:30 +08:00
fix: add __iter__ and __next__ to FileWrapper for PEP 3333 compliance (#3550)
* fix: add __iter__ and __next__ to FileWrapper for PEP 3333 compliance The WSGI spec (PEP 3333) requires that wsgi.file_wrapper return an iterable object. Gunicorn's FileWrapper only implemented __getitem__, which technically makes it iterable via old-style iteration but breaks code that explicitly relies on the iterator protocol (e.g., calling iter() or using next()). This adds __iter__ (returning self) and __next__ to make FileWrapper a proper iterator, maintaining backward compatibility with existing __getitem__-based usage. Fixes #3396 * Fix lint: move imports to top of file --------- Co-authored-by: contributor <noreply@users.noreply.github.com> Co-authored-by: Benoit Chesneau <bchesneau@gmail.com>
This commit is contained in:
parent
0ad47db800
commit
f8fca7a72f
@ -38,6 +38,15 @@ class FileWrapper:
|
||||
return data
|
||||
raise IndexError
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
data = self.filelike.read(self.blksize)
|
||||
if data:
|
||||
return data
|
||||
raise StopIteration
|
||||
|
||||
|
||||
class WSGIErrorsWrapper(io.RawIOBase):
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ from unittest import mock
|
||||
|
||||
from gunicorn import util
|
||||
from gunicorn.http.body import Body, LengthReader, EOFReader
|
||||
from gunicorn.http.wsgi import Response
|
||||
from gunicorn.http.wsgi import FileWrapper, Response
|
||||
from gunicorn.http.unreader import Unreader, IterUnreader, SocketUnreader
|
||||
from gunicorn.http.errors import InvalidHeader, InvalidHeaderName, InvalidHTTPVersion
|
||||
from gunicorn.http.message import TOKEN_RE
|
||||
@ -253,3 +253,27 @@ def test_eof_reader_read_invalid_size():
|
||||
def test_invalid_http_version_error():
|
||||
assert str(InvalidHTTPVersion('foo')) == "Invalid HTTP Version: 'foo'"
|
||||
assert str(InvalidHTTPVersion((2, 1))) == 'Invalid HTTP Version: (2, 1)'
|
||||
|
||||
|
||||
def test_file_wrapper_iterable():
|
||||
"""FileWrapper should support the iterator protocol per PEP 3333."""
|
||||
filelike = io.BytesIO(b"hello world")
|
||||
wrapper = FileWrapper(filelike, blksize=5)
|
||||
|
||||
# Should be iterable
|
||||
assert hasattr(wrapper, '__iter__')
|
||||
assert hasattr(wrapper, '__next__')
|
||||
assert iter(wrapper) is wrapper
|
||||
|
||||
# Should yield chunks via next()
|
||||
assert next(wrapper) == b"hello"
|
||||
assert next(wrapper) == b" worl"
|
||||
assert next(wrapper) == b"d"
|
||||
with pytest.raises(StopIteration):
|
||||
next(wrapper)
|
||||
|
||||
# Also works with for loop
|
||||
filelike2 = io.BytesIO(b"abc")
|
||||
wrapper2 = FileWrapper(filelike2, blksize=2)
|
||||
chunks = list(wrapper2)
|
||||
assert chunks == [b"ab", b"c"]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user