Commit bd733599 authored by Martin Aspeli's avatar Martin Aspeli

Merge c110187 from 2.12 branch, adding IPubBeforeStreaming event

parent 4ad8ac12
...@@ -5,12 +5,92 @@ This file contains change information for the current Zope release. ...@@ -5,12 +5,92 @@ This file contains change information for the current Zope release.
Change information for previous versions of Zope can be found in the Change information for previous versions of Zope can be found in the
file HISTORY.txt. file HISTORY.txt.
Zope 2.12.2 (unreleased) Zope 2.12.4 (Unreleased)
------------------------ ------------------------
Features Added Features Added
++++++++++++++ ++++++++++++++
- Updated packages:
- Acquisition = 2.13.1
- 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
++++++++++
- Zope 3-style resource directories would throw an Unauthorized error when
trying to use restrictedTraverse() to reach a resource in a sub-directory
of the resource directory.
- Restore ability to traverse to 'macros' on template-based browser views.
- Protect ZCTextIndex's clear method against storing Acquisition wrappers.
- LP #195761: fixed ZMI XML export / import and restored it to the UI.
- MailHost should fall back to HELO when EHLO fails.
Zope 2.12.3 (2010/01/12)
------------------------
Bugs Fixed
++++++++++
- LP #491224: proper escaping of rendered error message
- LP #246983: Enabled unicode conflict resolution on variables inside "string:"
expressions in TALES.
- Fixed possible TypeError while sending multipart emails.
- Also look for ZEXP imports within the clienthome directory. This
provides a place to put imports that won't be clobbered by buildout
in a buildout-based Zope instance.
- Fixed a SyntaxError in utilities/load_site.py script.
Features Added
++++++++++++++
- Made OFS.Image.File and OFS.Image.Image send IObjectModifiedEvent when
created through their factories and modified through the ZMI forms
(manage_edit() and manage_upload()).
- Moved zope.formlib / zope.app.form integration into a separate package
called five.formlib.
Zope 2.12.2 (2009-12-22)
------------------------
Features Added
++++++++++++++
- Updated packages:
- ZODB3 = 3.9.4
- docutils = 0.6
- pytz = 2009r
- zope.dottedname = 3.4.6
- zope.i18n = 3.7.2
- zope.interface = 3.5.3
- zope.minmax = 1.1.1
- zope.security = 3.7.2
- zope.session = 3.9.2
- zope.tal = 3.5.2
- Enhanced the internals of the DateRangeIndex based on an idea from
experimental.daterangeindexoptimisations, thanks to Matt Hamilton.
- Updated the default value for ``management_page_charset`` from iso-8859-1
to the nowadays more standard utf-8.
- Added IPubBeforeAbort event to mirror IPubBeforeCommit in failure scenarios. - Added IPubBeforeAbort event to mirror IPubBeforeCommit in failure scenarios.
This event is fired just before IPubFailure, but, crucially, while the This event is fired just before IPubFailure, but, crucially, while the
transaction is still open. transaction is still open.
...@@ -24,9 +104,26 @@ Features Added ...@@ -24,9 +104,26 @@ Features Added
Bugs Fixed Bugs Fixed
++++++++++ ++++++++++
- LP #143444: add labels to checkboxes / radio buttons on import /
export form.
- LP #496941: Remove all mention of ``standard_html_header`` and
``standard_html_footer`` from default DTML content.
- Fixed a regression in Products.PageTemplates that meant filesystem templates
using Products.Five.browser.pagetemplatefile would treat TALES path
expressions (but not python expressions) as protected code and so attempt
to apply security. See original issue here:
http://codespeak.net/pipermail/z3-five/2007q2/002185.html
- LP #491249: fix tabindex on ZRDB connection test form.
- LP #490514: preserve tainting when calling into DTML from ZPT.
- Avoid possible errors on test tear-down in Products.Five.fiveconfigure's - Avoid possible errors on test tear-down in Products.Five.fiveconfigure's
cleanUp() function if Products.meta_types has not been set cleanUp() function if Products.meta_types has not been set
Zope 2.12.1 (2009/11/02) Zope 2.12.1 (2009/11/02)
------------------------ ------------------------
......
...@@ -18,10 +18,12 @@ __version__ = '$Revision: 1.81 $'[11:-2] ...@@ -18,10 +18,12 @@ __version__ = '$Revision: 1.81 $'[11:-2]
import types, os, sys, re import types, os, sys, re
import zlib, struct import zlib, struct
from string import translate, maketrans from string import translate, maketrans
from zope.event import notify
from BaseResponse import BaseResponse from BaseResponse import BaseResponse
from zExceptions import Unauthorized, Redirect from zExceptions import Unauthorized, Redirect
from zExceptions.ExceptionFormatter import format_exception from zExceptions.ExceptionFormatter import format_exception
from ZPublisher import BadRequest, InternalError, NotFound from ZPublisher import BadRequest, InternalError, NotFound
from ZPublisher.pubevents import PubBeforeStreaming
from cgi import escape from cgi import escape
from urllib import quote from urllib import quote
...@@ -921,6 +923,9 @@ class HTTPResponse(BaseResponse): ...@@ -921,6 +923,9 @@ class HTTPResponse(BaseResponse):
""" """
if not self._wrote: if not self._wrote:
notify(PubBeforeStreaming(self))
self.outputBody() self.outputBody()
self._wrote = 1 self._wrote = 1
self.stdout.flush() self.stdout.flush()
......
...@@ -50,3 +50,11 @@ class IPubBeforeAbort(IPubEvent): ...@@ -50,3 +50,11 @@ class IPubBeforeAbort(IPubEvent):
""" """
exc_info = Attribute('''The exception info as returned by 'sys.exc_info()'.''') exc_info = Attribute('''The exception info as returned by 'sys.exc_info()'.''')
retry = Attribute('Whether the request will be retried') 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")
...@@ -10,7 +10,8 @@ for detailed time related analysis, inline request monitoring. ...@@ -10,7 +10,8 @@ for detailed time related analysis, inline request monitoring.
from zope.interface import implements from zope.interface import implements
from interfaces import IPubStart, IPubSuccess, IPubFailure, \ from interfaces import IPubStart, IPubSuccess, IPubFailure, \
IPubAfterTraversal, IPubBeforeCommit, IPubBeforeAbort IPubAfterTraversal, IPubBeforeCommit, IPubBeforeAbort, \
IPubBeforeStreaming
class _Base(object): class _Base(object):
"""PubEvent base class.""" """PubEvent base class."""
...@@ -49,3 +50,11 @@ class PubBeforeAbort(_Base): ...@@ -49,3 +50,11 @@ class PubBeforeAbort(_Base):
def __init__(self, request, exc_info, retry): def __init__(self, request, exc_info, retry):
self.request, self.exc_info, self.retry = 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
from StringIO import StringIO
from sys import modules, exc_info from sys import modules, exc_info
from unittest import TestCase, TestSuite, makeSuite, main from unittest import TestCase, TestSuite, makeSuite, main
...@@ -7,11 +8,14 @@ from zope.event import subscribers ...@@ -7,11 +8,14 @@ from zope.event import subscribers
from ZPublisher.Publish import publish, Retry from ZPublisher.Publish import publish, Retry
from ZPublisher.BaseRequest import BaseRequest from ZPublisher.BaseRequest import BaseRequest
from ZPublisher.HTTPResponse import HTTPResponse
from ZPublisher.pubevents import PubStart, PubSuccess, PubFailure, \ from ZPublisher.pubevents import PubStart, PubSuccess, PubFailure, \
PubAfterTraversal, PubBeforeCommit, PubBeforeAbort PubAfterTraversal, PubBeforeCommit, PubBeforeAbort, \
PubBeforeStreaming
from ZPublisher.interfaces import \ from ZPublisher.interfaces import \
IPubStart, IPubEnd, IPubSuccess, IPubFailure, \ IPubStart, IPubEnd, IPubSuccess, IPubFailure, \
IPubAfterTraversal, IPubBeforeCommit IPubAfterTraversal, IPubBeforeCommit, \
IPubBeforeStreaming
PUBMODULE = 'TEST_testpubevents' PUBMODULE = 'TEST_testpubevents'
...@@ -41,7 +45,10 @@ class TestInterface(TestCase): ...@@ -41,7 +45,10 @@ class TestInterface(TestCase):
def testBeforeCommit(self): def testBeforeCommit(self):
e = PubBeforeCommit(_Request()) e = PubBeforeCommit(_Request())
verifyObject(IPubBeforeCommit, e) verifyObject(IPubBeforeCommit, e)
def testBeforeStreaming(self):
e = PubBeforeStreaming(_Response())
verifyObject(IPubBeforeStreaming, e)
class TestPubEvents(TestCase): class TestPubEvents(TestCase):
def setUp(self): def setUp(self):
...@@ -127,6 +134,21 @@ class TestPubEvents(TestCase): ...@@ -127,6 +134,21 @@ class TestPubEvents(TestCase):
self.assert_(isinstance(events[5], PubBeforeCommit)) self.assert_(isinstance(events[5], PubBeforeCommit))
self.assert_(isinstance(events[6], PubSuccess)) 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 # Auxiliaries
def _succeed(): def _succeed():
''' ''' ''' '''
......
...@@ -20,8 +20,10 @@ and logging duties. ...@@ -20,8 +20,10 @@ and logging duties.
import time, re, sys, tempfile import time, re, sys, tempfile
from cStringIO import StringIO from cStringIO import StringIO
import thread import thread
from zope.event import notify
from ZPublisher.HTTPResponse import HTTPResponse from ZPublisher.HTTPResponse import HTTPResponse
from ZPublisher.Iterators import IStreamIterator from ZPublisher.Iterators import IStreamIterator
from ZPublisher.pubevents import PubBeforeStreaming
from medusa.http_date import build_http_date from medusa.http_date import build_http_date
from PubCore.ZEvent import Wakeup from PubCore.ZEvent import Wakeup
from medusa.producers import hooked_producer from medusa.producers import hooked_producer
...@@ -165,6 +167,9 @@ class ZServerHTTPResponse(HTTPResponse): ...@@ -165,6 +167,9 @@ class ZServerHTTPResponse(HTTPResponse):
stdout=self.stdout stdout=self.stdout
if not self._wrote: if not self._wrote:
notify(PubBeforeStreaming(self))
l=self.headers.get('content-length', None) l=self.headers.get('content-length', None)
if l is not None: if l is not None:
try: try:
......
...@@ -19,17 +19,21 @@ from ZServer.FTPResponse import FTPResponse ...@@ -19,17 +19,21 @@ from ZServer.FTPResponse import FTPResponse
from ZServer.PCGIServer import PCGIResponse from ZServer.PCGIServer import PCGIResponse
from ZServer.FCGIServer import FCGIResponse from ZServer.FCGIServer import FCGIResponse
from ZPublisher.Iterators import IStreamIterator from ZPublisher.Iterators import IStreamIterator
from ZPublisher.pubevents import PubBeforeStreaming
from zope.interface import implements from zope.interface import implements
import unittest import unittest
from cStringIO import StringIO from cStringIO import StringIO
from zope.event import subscribers
class ZServerResponseTestCase(unittest.TestCase): class ZServerResponseTestCase(unittest.TestCase):
"""Test ZServer response objects.""" """Test ZServer response objects."""
def test_http_response_write_unicode(self): def test_http_response_write_unicode(self):
response = ZServerHTTPResponse() response = ZServerHTTPResponse()
self.assertRaises(TypeError, response.write, u'bad') self.assertRaises(TypeError, response.write, u'bad')
def test_ftp_response_write_unicode(self): def test_ftp_response_write_unicode(self):
response = FTPResponse() response = FTPResponse()
self.assertRaises(TypeError, response.write, u'bad') self.assertRaises(TypeError, response.write, u'bad')
...@@ -57,7 +61,7 @@ class ZServerResponseTestCase(unittest.TestCase): ...@@ -57,7 +61,7 @@ class ZServerResponseTestCase(unittest.TestCase):
one = ZServerHTTPResponse(stdout=DummyChannel()) one = ZServerHTTPResponse(stdout=DummyChannel())
self.assertRaises(AssertionError, self.assertRaises(AssertionError,
one.setBody, test_streamiterator()) one.setBody, test_streamiterator())
class DummyChannel: class DummyChannel:
def __init__(self): def __init__(self):
self.out = StringIO() self.out = StringIO()
...@@ -267,12 +271,39 @@ class ZServerHTTPResponseTestCase(unittest.TestCase): ...@@ -267,12 +271,39 @@ class ZServerHTTPResponseTestCase(unittest.TestCase):
'', '',
'')) ''))
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(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTests(( suite.addTests((
unittest.makeSuite(ZServerResponseTestCase), unittest.makeSuite(ZServerResponseTestCase),
unittest.makeSuite(ZServerHTTPResponseTestCase) unittest.makeSuite(ZServerHTTPResponseTestCase),
unittest.makeSuite(ZServerHTTPResponseEventsTestCase)
)) ))
return suite return suite
......
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