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

Wsgi file wrapper #719

Merged
merged 7 commits into from
Oct 27, 2019
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst
- Fix ``OFS.Image.File.__str__`` for ``Pdata`` contents
(`#711 <https://github.com/zopefoundation/Zope/issues/711>`_)

- Add ``wsgi.file_wrapper`` implementation
https://www.python.org/dev/peps/pep-0333/#optional-platform-specific-file-handling
(`#719 <https://github.com/zopefoundation/Zope/pull/719>`_)

- Set ``REMOTE_USER`` in wsgi environ using Zope user authentication
(`#713 <https://github.com/zopefoundation/Zope/pull/713>`_)


- Improve documentation for Zope's error logging services.

Backwards incompatible changes
Expand Down
5 changes: 4 additions & 1 deletion src/ZPublisher/WSGIPublisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,10 @@ def publish_module(environ, start_response,

if isinstance(response.body, _FILE_TYPES) or \
IUnboundStreamIterator.providedBy(response.body):
result = response.body
if 'wsgi.file_wrapper' in environ:
result = environ['wsgi.file_wrapper'](response.body)
else:
result = response.body
else:
# If somebody used response.write, that data will be in the
# response.stdout BytesIO, so we put that before the body.
Expand Down
67 changes: 67 additions & 0 deletions src/ZPublisher/tests/test_WSGIPublisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,73 @@ def __next__(self):
app_iter = self._callFUT(environ, start_response, _publish)
self.assertTrue(app_iter is body)

def test_stream_file_wrapper(self):
from ZPublisher.Iterators import IStreamIterator
from zope.interface import implementer
from ZPublisher.HTTPResponse import WSGIResponse

@implementer(IStreamIterator)
class TestStreamIterator(object):
data = "hello" * 20

def __len__(self):
return len(self.data)

class Wrapper(object):
def __init__(self, file):
self.file = file

_response = WSGIResponse()
_response.setHeader('Content-Type', 'text/plain')
body = _response.body = TestStreamIterator()
environ = self._makeEnviron(**{'wsgi.file_wrapper': Wrapper})
start_response = DummyCallable()
_publish = DummyCallable()
_publish._result = _response
app_iter = self._callFUT(environ, start_response, _publish)
self.assertTrue(app_iter.file is body)
self.assertTrue(isinstance(app_iter, Wrapper))
self.assertEqual(
int(_response.headers['content-length']), len(body))
self.assertTrue(
_response.headers['content-type'].startswith('text/plain'))
self.assertEqual(_response.status, 200)

def test_unboundstream_file_wrapper(self):
from ZPublisher.Iterators import IUnboundStreamIterator
from zope.interface import implementer
from ZPublisher.HTTPResponse import WSGIResponse

@implementer(IUnboundStreamIterator)
class TestUnboundStreamIterator(object):
data = "hello"

def __len__(self):
return len(self.data)

class Wrapper(object):
def __init__(self, file):
self.file = file

_response = WSGIResponse()
_response.setStatus(200)
# UnboundStream needs Content-Length header
_response.setHeader('Content-Length', '5')
_response.setHeader('Content-Type', 'text/plain')
body = _response.body = TestUnboundStreamIterator()
environ = self._makeEnviron(**{'wsgi.file_wrapper': Wrapper})
start_response = DummyCallable()
_publish = DummyCallable()
_publish._result = _response
app_iter = self._callFUT(environ, start_response, _publish)
self.assertTrue(app_iter.file is body)
self.assertTrue(isinstance(app_iter, Wrapper))
self.assertEqual(
int(_response.headers['content-length']), len(body))
self.assertTrue(
_response.headers['content-type'].startswith('text/plain'))
self.assertEqual(_response.status, 200)

def test_request_closed(self):
environ = self._makeEnviron()
start_response = DummyCallable()
Expand Down