[Zope-Checkins] SVN: Zope/branches/2.12/ Hoping that silence (or apathy?) is consent here. :) Adding an event to indicate when streaming is starting in case of chunked/streamed responses using response.write()
Martin Aspeli
optilude at gmx.net
Fri Mar 26 09:14:47 EDT 2010
Log message for revision 110187:
Hoping that silence (or apathy?) is consent here. :) Adding an event to indicate when streaming is starting in case of chunked/streamed responses using response.write()
Changed:
U Zope/branches/2.12/doc/CHANGES.rst
U Zope/branches/2.12/src/ZPublisher/HTTPResponse.py
U Zope/branches/2.12/src/ZPublisher/interfaces.py
U Zope/branches/2.12/src/ZPublisher/pubevents.py
U Zope/branches/2.12/src/ZPublisher/tests/testpubevents.py
U Zope/branches/2.12/src/ZServer/HTTPResponse.py
U Zope/branches/2.12/src/ZServer/tests/test_responses.py
-=-
Modified: Zope/branches/2.12/doc/CHANGES.rst
===================================================================
--- Zope/branches/2.12/doc/CHANGES.rst 2010-03-26 12:48:34 UTC (rev 110186)
+++ Zope/branches/2.12/doc/CHANGES.rst 2010-03-26 13:14:46 UTC (rev 110187)
@@ -17,6 +17,11 @@
- ExtensionClass = 2.13.0
- Persistence = 2.13.0
+- There is now an event ZPublisher.interfaces.IPubBeforeStreaming which will
+ be fired just before the first chunk of data is written to the response
+ stream when using the write() method on the response. This is the last
+ possible point at which response headers may be set in this case.
+
Bugs Fixed
++++++++++
Modified: Zope/branches/2.12/src/ZPublisher/HTTPResponse.py
===================================================================
--- Zope/branches/2.12/src/ZPublisher/HTTPResponse.py 2010-03-26 12:48:34 UTC (rev 110186)
+++ Zope/branches/2.12/src/ZPublisher/HTTPResponse.py 2010-03-26 13:14:46 UTC (rev 110187)
@@ -18,10 +18,12 @@
import types, os, sys, re
import zlib, struct
from string import translate, maketrans
+from zope.event import notify
from BaseResponse import BaseResponse
from zExceptions import Unauthorized, Redirect
from zExceptions.ExceptionFormatter import format_exception
from ZPublisher import BadRequest, InternalError, NotFound
+from ZPublisher.pubevents import PubBeforeStreaming
from cgi import escape
from urllib import quote
@@ -921,6 +923,9 @@
"""
if not self._wrote:
+
+ notify(PubBeforeStreaming(self))
+
self.outputBody()
self._wrote = 1
self.stdout.flush()
Modified: Zope/branches/2.12/src/ZPublisher/interfaces.py
===================================================================
--- Zope/branches/2.12/src/ZPublisher/interfaces.py 2010-03-26 12:48:34 UTC (rev 110186)
+++ Zope/branches/2.12/src/ZPublisher/interfaces.py 2010-03-26 13:14:46 UTC (rev 110187)
@@ -50,3 +50,11 @@
"""
exc_info = Attribute('''The exception info as returned by 'sys.exc_info()'.''')
retry = Attribute('Whether the request will be retried')
+
+class IPubBeforeStreaming(Interface):
+ """Event fired just before a streaming response is initiated, i.e. when
+ something calls response.write() for the first time. Note that this is
+ carries a reference to the *response*, not the request.
+ """
+
+ response = Attribute(u"The current HTTP response")
Modified: Zope/branches/2.12/src/ZPublisher/pubevents.py
===================================================================
--- Zope/branches/2.12/src/ZPublisher/pubevents.py 2010-03-26 12:48:34 UTC (rev 110186)
+++ Zope/branches/2.12/src/ZPublisher/pubevents.py 2010-03-26 13:14:46 UTC (rev 110187)
@@ -10,7 +10,8 @@
from zope.interface import implements
from interfaces import IPubStart, IPubSuccess, IPubFailure, \
- IPubAfterTraversal, IPubBeforeCommit, IPubBeforeAbort
+ IPubAfterTraversal, IPubBeforeCommit, IPubBeforeAbort, \
+ IPubBeforeStreaming
class _Base(object):
"""PubEvent base class."""
@@ -49,3 +50,11 @@
def __init__(self, request, exc_info, retry):
self.request, self.exc_info, self.retry = request, exc_info, retry
+
+class PubBeforeStreaming(object):
+ """Notified immediately before streaming via response.write() commences
+ """
+ implements(IPubBeforeStreaming)
+
+ def __init__(self, response):
+ self.response = response
Modified: Zope/branches/2.12/src/ZPublisher/tests/testpubevents.py
===================================================================
--- Zope/branches/2.12/src/ZPublisher/tests/testpubevents.py 2010-03-26 12:48:34 UTC (rev 110186)
+++ Zope/branches/2.12/src/ZPublisher/tests/testpubevents.py 2010-03-26 13:14:46 UTC (rev 110187)
@@ -1,3 +1,4 @@
+from StringIO import StringIO
from sys import modules, exc_info
from unittest import TestCase, TestSuite, makeSuite, main
@@ -7,11 +8,14 @@
from ZPublisher.Publish import publish, Retry
from ZPublisher.BaseRequest import BaseRequest
+from ZPublisher.HTTPResponse import HTTPResponse
from ZPublisher.pubevents import PubStart, PubSuccess, PubFailure, \
- PubAfterTraversal, PubBeforeCommit, PubBeforeAbort
+ PubAfterTraversal, PubBeforeCommit, PubBeforeAbort, \
+ PubBeforeStreaming
from ZPublisher.interfaces import \
IPubStart, IPubEnd, IPubSuccess, IPubFailure, \
- IPubAfterTraversal, IPubBeforeCommit
+ IPubAfterTraversal, IPubBeforeCommit, \
+ IPubBeforeStreaming
PUBMODULE = 'TEST_testpubevents'
@@ -41,7 +45,10 @@
def testBeforeCommit(self):
e = PubBeforeCommit(_Request())
verifyObject(IPubBeforeCommit, e)
-
+
+ def testBeforeStreaming(self):
+ e = PubBeforeStreaming(_Response())
+ verifyObject(IPubBeforeStreaming, e)
class TestPubEvents(TestCase):
def setUp(self):
@@ -127,6 +134,21 @@
self.assert_(isinstance(events[5], PubBeforeCommit))
self.assert_(isinstance(events[6], PubSuccess))
+ def testStreaming(self):
+
+ out = StringIO()
+ response = HTTPResponse(stdout=out)
+ response.write('datachunk1')
+ response.write('datachunk2')
+
+ events = self.reporter.events
+ self.assertEqual(len(events), 1)
+ self.assert_(isinstance(events[0], PubBeforeStreaming))
+ self.assertEqual(events[0].response, response)
+
+ self.failUnless('datachunk1datachunk2' in out.getvalue())
+
+
# Auxiliaries
def _succeed():
''' '''
Modified: Zope/branches/2.12/src/ZServer/HTTPResponse.py
===================================================================
--- Zope/branches/2.12/src/ZServer/HTTPResponse.py 2010-03-26 12:48:34 UTC (rev 110186)
+++ Zope/branches/2.12/src/ZServer/HTTPResponse.py 2010-03-26 13:14:46 UTC (rev 110187)
@@ -20,8 +20,10 @@
import time, re, sys, tempfile
from cStringIO import StringIO
import thread
+from zope.event import notify
from ZPublisher.HTTPResponse import HTTPResponse
from ZPublisher.Iterators import IStreamIterator
+from ZPublisher.pubevents import PubBeforeStreaming
from medusa.http_date import build_http_date
from PubCore.ZEvent import Wakeup
from medusa.producers import hooked_producer
@@ -165,6 +167,9 @@
stdout=self.stdout
if not self._wrote:
+
+ notify(PubBeforeStreaming(self))
+
l=self.headers.get('content-length', None)
if l is not None:
try:
Modified: Zope/branches/2.12/src/ZServer/tests/test_responses.py
===================================================================
--- Zope/branches/2.12/src/ZServer/tests/test_responses.py 2010-03-26 12:48:34 UTC (rev 110186)
+++ Zope/branches/2.12/src/ZServer/tests/test_responses.py 2010-03-26 13:14:46 UTC (rev 110187)
@@ -19,17 +19,21 @@
from ZServer.PCGIServer import PCGIResponse
from ZServer.FCGIServer import FCGIResponse
from ZPublisher.Iterators import IStreamIterator
+from ZPublisher.pubevents import PubBeforeStreaming
from zope.interface import implements
import unittest
from cStringIO import StringIO
+from zope.event import subscribers
+
+
class ZServerResponseTestCase(unittest.TestCase):
"""Test ZServer response objects."""
def test_http_response_write_unicode(self):
response = ZServerHTTPResponse()
self.assertRaises(TypeError, response.write, u'bad')
-
+
def test_ftp_response_write_unicode(self):
response = FTPResponse()
self.assertRaises(TypeError, response.write, u'bad')
@@ -57,7 +61,7 @@
one = ZServerHTTPResponse(stdout=DummyChannel())
self.assertRaises(AssertionError,
one.setBody, test_streamiterator())
-
+
class DummyChannel:
def __init__(self):
self.out = StringIO()
@@ -267,12 +271,39 @@
'',
''))
+class _Reporter(object):
+ def __init__(self): self.events = []
+ def __call__(self, event): self.events.append(event)
+class ZServerHTTPResponseEventsTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self._saved_subscribers = subscribers[:]
+ self.reporter = r = _Reporter()
+ subscribers[:] = [r]
+
+ def tearDown(self):
+ subscribers[:] = self._saved_subscribers
+
+ def testStreaming(self):
+ out = StringIO()
+ response = ZServerHTTPResponse(stdout=out)
+ response.write('datachunk1')
+ response.write('datachunk2')
+
+ events = self.reporter.events
+ self.assertEqual(len(events), 1)
+ self.assert_(isinstance(events[0], PubBeforeStreaming))
+ self.assertEqual(events[0].response, response)
+
+ self.failUnless('datachunk1datachunk2' in out.getvalue())
+
def test_suite():
suite = unittest.TestSuite()
suite.addTests((
unittest.makeSuite(ZServerResponseTestCase),
- unittest.makeSuite(ZServerHTTPResponseTestCase)
+ unittest.makeSuite(ZServerHTTPResponseTestCase),
+ unittest.makeSuite(ZServerHTTPResponseEventsTestCase)
))
return suite
More information about the Zope-Checkins
mailing list