Commit 558b5452 authored by Tres Seaver's avatar Tres Seaver

Merge branch 'gweis-2.13' into 2.13

parents b3f2cf76 c0aae342
......@@ -8,9 +8,14 @@ http://docs.zope.org/zope2/
2.13.23 (unreleased)
--------------------
- Issue #27: Fix publishing of ``ZPublisher.Iterators.IStreamIterator`` under
WSGI. This interface does not have ``seek`` or ``tell``. Introduce
``ZPublisher.Iterators.IUnboundStreamIterator`` to support publishing
iterators of unknown length under WSGI.
- Document running Zope as a WSGI application. See
https://github.com/zopefoundation/Zope/issues/30
- LP #1465432: Ensure that WSGIPublisher starts / ends interaction at
request boundaries (analogous to ZPublisher). Backport from master.
......
from zope.interface import Interface
from zope.interface import implements
class IStreamIterator(Interface):
class IUnboundStreamIterator(Interface):
"""
An iterator with unknown length that can be published.
"""
An iterator that can be published.
IStreamIterators must not read from the object database.
After the application finishes interpreting a request and
returns an iterator to be processed asynchronously, it closes
the ZODB connection. If the iterator then tries to load some
ZODB object, ZODB would do one of two things. If the connection
is still closed, ZODB would raise an error. If the connection
happens to be re-opened by another thread, ZODB might allow it,
but it has a chance of going insane if it happens to be loading
or storing something in the other thread at the same time. """
def next():
"""
......@@ -21,6 +12,21 @@ class IStreamIterator(Interface):
StopIeration if we've reached the end of the bytestream.
"""
class IStreamIterator(IUnboundStreamIterator):
"""
An iterator with known length that can be published.
IStreamIterators must not read from the object database.
After the application finishes interpreting a request and
returns an iterator to be processed asynchronously, it closes
the ZODB connection. If the iterator then tries to load some
ZODB object, ZODB would do one of two things. If the connection
is still closed, ZODB would raise an error. If the connection
happens to be re-opened by another thread, ZODB might allow it,
but it has a chance of going insane if it happens to be loading
or storing something in the other thread at the same time. """
def __len__():
"""
Return an integer representing the length of the object
......
......@@ -32,7 +32,7 @@ from ZPublisher.Publish import dont_publish_class
from ZPublisher.Publish import get_module_info
from ZPublisher.Publish import missing_name
from ZPublisher.pubevents import PubStart, PubBeforeCommit, PubAfterTraversal
from ZPublisher.Iterators import IStreamIterator
from ZPublisher.Iterators import IUnboundStreamIterator, IStreamIterator
_NOW = None # overwrite for testing
def _now():
......@@ -131,12 +131,19 @@ class WSGIResponse(HTTPResponse):
self.stdout.write(data)
def setBody(self, body, title='', is_error=0):
if isinstance(body, file) or IStreamIterator.providedBy(body):
if isinstance(body, file):
body.seek(0, 2)
length = body.tell()
body.seek(0)
self.setHeader('Content-Length', '%d' % length)
self.body = body
elif IStreamIterator.providedBy(body):
self.body = body
HTTPResponse.setBody(self, '', title, is_error)
elif IUnboundStreamIterator.providedBy(body):
self.body = body
self._streaming = 1
HTTPResponse.setBody(self, '', title, is_error)
else:
HTTPResponse.setBody(self, body, title, is_error)
......
......@@ -136,6 +136,57 @@ class WSGIResponseTests(unittest.TestCase):
time.gmtime(time.mktime(WHEN)))
self.assertTrue(('Date', whenstr) in headers)
def test_setBody_IUnboundStreamIterator(self):
from ZPublisher.Iterators import IUnboundStreamIterator
from zope.interface import implements
class test_streamiterator:
implements(IUnboundStreamIterator)
data = "hello"
done = 0
def next(self):
if not self.done:
self.done = 1
return self.data
raise StopIteration
response = self._makeOne()
response.setStatus(200)
body = test_streamiterator()
response.setBody(body)
response.finalize()
self.assertTrue(body is response.body)
self.assertEqual(response._streaming, 1)
def test_setBody_IStreamIterator(self):
from ZPublisher.Iterators import IStreamIterator
from zope.interface import implements
class test_streamiterator:
implements(IStreamIterator)
data = "hello"
done = 0
def next(self):
if not self.done:
self.done = 1
return self.data
raise StopIteration
def __len__(self):
return len(self.data)
response = self._makeOne()
response.setStatus(200)
body = test_streamiterator()
response.setBody(body)
response.finalize()
self.assertTrue(body is response.body)
self.assertEqual(response._streaming, 0)
self.assertEqual(response.getHeader('Content-Length'),
'%d' % len(test_streamiterator.data))
#def test___str__already_wrote_not_chunking(self):
# response = self._makeOne()
# response._wrote = True
......
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