Skip to content

Commit

Permalink
Introduce IUnboundStreamIterator to support publishing iterators of u…
Browse files Browse the repository at this point in the history
…nknown length
  • Loading branch information
Gerhard Weis committed Aug 5, 2015
1 parent 9d17e06 commit 19e5a97
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 14 deletions.
30 changes: 18 additions & 12 deletions src/ZPublisher/Iterators.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
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():
"""
Return a sequence of bytes out of the bytestream, or raise
StopIeration if we've reached the end of the bytestream.
"""


class IStreamIterator(Interface):
"""
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
Expand Down
7 changes: 5 additions & 2 deletions src/ZPublisher/WSGIPublisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
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():
Expand Down Expand Up @@ -137,10 +137,13 @@ def setBody(self, body, title='', is_error=0):
body.seek(0)
self.setHeader('Content-Length', '%d' % length)
self.body = body
elif IStreamIterator.providedBy(body):
elif IUnboundStreamIterator.providedBy(body):
self.body = body
self._streaming = 1
HTTPResponse.setBody(self, '', title, is_error)
elif IStreamIterator.providedBy(body):
self.body = body
HTTPResponse.setBody(self, '', title, is_error)
else:
HTTPResponse.setBody(self, body, title, is_error)

Expand Down
25 changes: 25 additions & 0 deletions src/ZPublisher/tests/test_WSGIPublisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,28 @@ def test_listHeaders_includes_Date_header(self):
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)

def test_setBody_IStreamIterator(self):
from ZPublisher.Iterators import IStreamIterator
from zope.interface import implements
Expand All @@ -151,6 +173,9 @@ def next(self):
return self.data
raise StopIteration

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

response = self._makeOne()
response.setStatus(200)
body = test_streamiterator()
Expand Down

0 comments on commit 19e5a97

Please sign in to comment.