Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix: wsgi.file_wrapper should pass-through seek/tell attrs #363

Merged
merged 3 commits into from Jan 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGES.txt
Expand Up @@ -11,6 +11,13 @@ Python Version Support
Bugfix
~~~~~~

- ``wsgi.file_wrapper`` now sets the ``seekable``, ``seek``, and ``tell`` attributes from
the underlying file if the underlying file is seekable. This allows WSGI
middleware to implement things like range requests for example

See https://github.com/Pylons/waitress/issues/359 and
https://github.com/Pylons/waitress/pull/363

- In Python 3 ``OSError`` is no longer subscriptable, this caused failures on
Windows attempting to loop to find an socket that would work for use in the
trigger.
Expand Down
10 changes: 10 additions & 0 deletions src/waitress/buffers.py
Expand Up @@ -145,6 +145,16 @@ def __init__(self, file, block_size=32768):
self.file = file
self.block_size = block_size # for __iter__

# This is for the benefit of anyone that is attempting to wrap this
# wsgi.file_wrapper in a WSGI middleware and wants to seek, this is
# useful for instance for support Range requests
if _is_seekable(self.file):
if hasattr(self.file, "seekable"):
self.seekable = self.file.seekable

self.seek = self.file.seek
self.tell = self.file.tell

def prepare(self, size=None):
if _is_seekable(self.file):
start_pos = self.file.tell()
Expand Down
4 changes: 4 additions & 0 deletions tests/test_buffers.py
Expand Up @@ -185,6 +185,8 @@ def tearDown(self):
def test_prepare_not_seekable(self):
f = KindaFilelike(b"abc")
inst = self._makeOne(f)
self.assertFalse(hasattr(inst, "seek"))
self.assertFalse(hasattr(inst, "tell"))
result = inst.prepare()
self.assertEqual(result, False)
self.assertEqual(inst.remain, 0)
Expand All @@ -200,6 +202,8 @@ def test_prepare_not_seekable_closeable(self):
def test_prepare_seekable_closeable(self):
f = Filelike(b"abc", close=1, tellresults=[0, 10])
inst = self._makeOne(f)
self.assertEqual(inst.seek, f.seek)
self.assertEqual(inst.tell, f.tell)
result = inst.prepare()
self.assertEqual(result, 10)
self.assertEqual(inst.remain, 10)
Expand Down