[Zope3-checkins] SVN: Zope3/branches/srichter-twisted-integration2/
Merged rev 29982, the fix for bug 390.
Stephan Richter
srichter at cosmos.phy.tufts.edu
Wed Sep 7 20:34:12 EDT 2005
Log message for revision 38377:
Merged rev 29982, the fix for bug 390.
Changed:
U Zope3/branches/srichter-twisted-integration2/doc/CHANGES.txt
_U Zope3/branches/srichter-twisted-integration2/src/
U Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/mkcol.py
U Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/propfind.py
U Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/proppatch.py
U Zope3/branches/srichter-twisted-integration2/src/zope/app/fssync/browser/__init__.py
U Zope3/branches/srichter-twisted-integration2/src/zope/app/http/put.py
U Zope3/branches/srichter-twisted-integration2/src/zope/publisher/base.py
U Zope3/branches/srichter-twisted-integration2/src/zope/publisher/http.py
U Zope3/branches/srichter-twisted-integration2/src/zope/publisher/interfaces/__init__.py
U Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/basetestiapplicationrequest.py
U Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/test_baserequest.py
U Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/test_http.py
-=-
Modified: Zope3/branches/srichter-twisted-integration2/doc/CHANGES.txt
===================================================================
--- Zope3/branches/srichter-twisted-integration2/doc/CHANGES.txt 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/doc/CHANGES.txt 2005-09-08 00:34:11 UTC (rev 38377)
@@ -6,10 +6,15 @@
For information on future releases, see ROADMAP.txt.
- Some future release (Zope 3.2.0)
+ Some future release Zope 3.2.0
New features
+ - Fixed issue 390. Deprecated ``IBaseRequest.body`` and
+ ``IBaseRequest.bodyFile``. The latter was simply renamed to
+ ``IBaseRequest.bodyStream``. No code assumes anymore that the input
+ streams are seekable.
+
- Formalized the Publisher Response API.
+ Until now the publisher made assumptions about the form of ouput of
@@ -72,6 +77,8 @@
Much thanks to everyone who contributed to this release:
+ Stephan Richter, Michael Kerrin, Jim Fulton
+
Note: If you are not listed and contributed, please add yourself. This
note will be deleted before the release.
Property changes on: Zope3/branches/srichter-twisted-integration2/src
___________________________________________________________________
Name: svn:externals
- BTrees svn://svn.zope.org/repos/main/ZODB/tags/3.4.0a2/src/BTrees
persistent svn://svn.zope.org/repos/main/ZODB/tags/3.4.0a2/src/persistent
ThreadedAsync svn://svn.zope.org/repos/main/ZODB/tags/3.4.0a2/src/ThreadedAsync
transaction svn://svn.zope.org/repos/main/ZODB/tags/3.4.0a2/src/transaction
ZEO svn://svn.zope.org/repos/main/ZODB/tags/3.4.0a2/src/ZEO
ZODB svn://svn.zope.org/repos/main/ZODB/tags/3.4.0a2/src/ZODB
twisted svn://svn.twistedmatrix.com/svn/Twisted/trunk/twisted
+ ZConfig svn://svn.zope.org/repos/main/ZConfig/tags/ZConfig-2.3.1
zdaemon svn://svn.zope.org/repos/main/zdaemon/tags/zdaemon-1.1
BTrees svn://svn.zope.org/repos/main/ZODB/tags/3.6.0a3/src/BTrees
persistent svn://svn.zope.org/repos/main/ZODB/tags/3.6.0a3/src/persistent
ThreadedAsync svn://svn.zope.org/repos/main/ZODB/tags/3.6.0a3/src/ThreadedAsync
transaction svn://svn.zope.org/repos/main/ZODB/tags/3.6.0a3/src/transaction
ZEO svn://svn.zope.org/repos/main/ZODB/tags/3.6.0a3/src/ZEO
ZODB svn://svn.zope.org/repos/main/ZODB/tags/3.6.0a3/src/ZODB
twisted svn://svn.twistedmatrix.com/svn/Twisted/trunk/twisted
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/mkcol.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/mkcol.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/mkcol.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -29,9 +29,7 @@
def MKCOL(self):
request = self.request
- data = request.bodyFile
- data.seek(0)
- data = data.read()
+ data = request.bodyStream.read()
if len(data):
# We don't (yet) support a request body on MKCOL.
request.response.setStatus(415)
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/propfind.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/propfind.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/propfind.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -16,6 +16,7 @@
__docformat__ = 'restructuredtext'
from xml.dom import minidom
+from xml.parsers import expat
from zope.schema import getFieldNamesInOrder, getFields
from zope.app import zapi
from zope.app.container.interfaces import IReadContainer
@@ -52,13 +53,16 @@
_avail_props[ns] = list(oprops.keys())
self.avail_props = _avail_props
+ # The xmldoc attribute will be set later, if needed.
+ self.xmldoc = None
+
def getDepth(self):
return self._depth
def setDepth(self, depth):
self._depth = depth.lower()
- def PROPFIND(self):
+ def PROPFIND(self, xmldoc=None):
if self.content_type not in ['text/xml', 'application/xml']:
self.request.response.setStatus(400)
return ''
@@ -70,11 +74,14 @@
if IReadContainer.providedBy(self.context):
resource_url += '/'
- self.request.bodyFile.seek(0, 2)
- size = self.request.bodyFile.tell()
- self.request.bodyFile.seek(0)
+ if xmldoc is None:
+ try:
+ xmldoc = minidom.parse(self.request.bodyStream)
+ except expat.ExpatError:
+ pass
- xmldoc = size and minidom.parse(self.request.bodyFile) or None
+ self.xmldoc = xmldoc
+
resp = minidom.Document()
ms = resp.createElement('multistatus')
ms.setAttribute('xmlns', self.default_ns)
@@ -84,7 +91,8 @@
ms.lastChild.lastChild.appendChild(resp.createTextNode(resource_url))
if xmldoc is not None:
- propname = xmldoc.getElementsByTagNameNS(self.default_ns, 'propname')
+ propname = xmldoc.getElementsByTagNameNS(
+ self.default_ns, 'propname')
if propname:
self._handlePropname(resp)
else:
@@ -111,7 +119,7 @@
if pfind is None:
continue
pfind.setDepth(subdepth)
- value = pfind.PROPFIND()
+ value = pfind.PROPFIND(self.xmldoc)
parsed = minidom.parseString(value)
responses = parsed.getElementsByTagNameNS(
self.default_ns, 'response')
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/proppatch.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/proppatch.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/app/dav/proppatch.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -63,8 +63,7 @@
if IReadContainer.providedBy(self.context):
resource_url += '/'
- self.request.bodyFile.seek(0)
- xmldoc = minidom.parse(self.request.bodyFile)
+ xmldoc = minidom.parse(self.request.bodyStream)
resp = minidom.Document()
ms = resp.createElement('multistatus')
ms.setAttribute('xmlns', self.default_ns)
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/app/fssync/browser/__init__.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/app/fssync/browser/__init__.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/app/fssync/browser/__init__.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -127,9 +127,8 @@
shutil.rmtree(self.tempdir)
def unsnarf_body(self):
- fp = self.request.bodyFile
- fp.seek(0)
- uns = Unsnarfer(fp)
+ stream = self.request.bodyStream
+ uns = Unsnarfer(stream)
uns.unsnarf(self.tempdir)
def call_committer(self):
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/app/http/put.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/app/http/put.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/app/http/put.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -53,7 +53,7 @@
request.response.setStatus(501)
return ''
- body = request.bodyFile
+ body = request.bodyStream
name = self.context.name
container = self.context.container
@@ -102,7 +102,7 @@
request.response.setStatus(501)
return ''
- body = self.request.bodyFile
+ body = self.request.bodyStream
file = self.context
adapter = IWriteFile(file)
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/publisher/base.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/publisher/base.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/publisher/base.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -21,7 +21,8 @@
import traceback
from cStringIO import StringIO
-from zope.deprecation import deprecation
+from zope.deprecation import deprecated
+
from zope.interface import implements, providedBy
from zope.interface.common.mapping import IReadMapping, IEnumerableMapping
@@ -80,7 +81,7 @@
# BBB: Backward-compatibility for old body API
def setBody(self, body):
return self.setResult(body)
- setBody = deprecation.deprecated(
+ setBody = deprecated(
setBody,
'setBody() has been deprecated in favor of setResult(). '
'This will go away in Zope 3.4.')
@@ -326,26 +327,40 @@
'See IPublicationRequest'
self._traversal_stack[:] = list(stack)
+ def _getBodyStream(self):
+ 'See zope.publisher.interfaces.IApplicationRequest'
+ return self._body_instream
+
+ bodyStream = property(_getBodyStream)
+
+ ########################################################################
+ # BBB: Deprecated; will go away in Zope 3.4
+
def _getBody(self):
body = getattr(self, '_body', None)
if body is None:
s = self._body_instream
if s is None:
return None # TODO: what should be returned here?
- p = s.tell()
- s.seek(0)
body = s.read()
- s.seek(p)
self._body = body
return body
body = property(_getBody)
+ body = deprecated(body,
+ 'The ``body`` attribute has been deprecated. Please '
+ 'use the ``bodyStream`` attribute directly. This '
+ 'attribute will go away in Zope 3.4.')
- def _getBodyFile(self):
- 'See IApplicationRequest'
- return self._body_instream
+ bodyFile = bodyStream
+ bodyFile = deprecated(bodyFile,
+ 'The ``bodyFile`` attribute has been replaced by '
+ '``bodyStream``, which is a more accurate name. '
+ 'Streams are not necessarily files, i.e. they are '
+ 'not seekable. This attribute will go away in Zope '
+ '3.4.')
- bodyFile = property(_getBodyFile)
+ ########################################################################
def __len__(self):
'See Interface.Common.Mapping.IEnumerableMapping'
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/publisher/http.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/publisher/http.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/publisher/http.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -16,6 +16,7 @@
$Id$
"""
import re, time, random
+import cStringIO
from urllib import quote, unquote, splitport
from types import StringTypes, ClassType
from cgi import escape
@@ -169,6 +170,37 @@
return default
raise
+class HTTPInputStream(object):
+ """Special stream that supports caching the read data.
+
+ This is important, so that we can retry requests.
+ """
+
+ def __init__(self, stream):
+ self.stream = stream
+ self.cacheStream = cStringIO.StringIO()
+
+ def getCacheStream(self):
+ self.read()
+ self.cacheStream.seek(0)
+ return self.cacheStream
+
+ def read(self, size=-1):
+ data = self.stream.read(size)
+ self.cacheStream.write(data)
+ return data
+
+ def readline(self):
+ data = self.stream.readline()
+ self.cacheStream.write(data)
+ return data
+
+ def readlines(self, hint=None):
+ data = self.stream.readlines(hint)
+ self.cacheStream.write(''.join(data))
+ return data
+
+
DEFAULT_PORTS = {'http': '80', 'https': '443'}
STAGGER_RETRIES = True
@@ -249,7 +281,8 @@
2)
environ, response = response, outstream
- super(HTTPRequest, self).__init__(body_instream, environ, response)
+ super(HTTPRequest, self).__init__(
+ HTTPInputStream(body_instream), environ, response)
self._orig_env = environ
environ = sane_environment(environ)
@@ -380,10 +413,11 @@
'See IPublisherRequest'
count = getattr(self, '_retry_count', 0)
self._retry_count = count + 1
- self._body_instream.seek(0)
+
new_response = self.response.retry()
request = self.__class__(
- body_instream=self._body_instream,
+ # Use the cache stream as the new input stream.
+ body_instream=self._body_instream.getCacheStream(),
environ=self._orig_env,
response=new_response,
)
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/publisher/interfaces/__init__.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/publisher/interfaces/__init__.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/publisher/interfaces/__init__.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -410,10 +410,21 @@
This is a read-only attribute.
""")
- body = Attribute("""The body of the request as a string""")
+ bodyStream = Attribute(
+ """The stream that provides the data of the request.
- bodyFile = Attribute("""The body of the request as a file""")
+ The data returned by the stream will not include any possible header
+ information, which should have been stripped by the server (or
+ previous layer) before.
+ Also, the body stream might already be read and not return any
+ data. This is commonly done when retrieving the data for the ``body``
+ attribute.
+
+ If you access this stream directly to retrieve data, it will not be
+ possible by other parts of the framework to access the data of the
+ request via the ``body`` attribute.""")
+
debug = Attribute("""Debug flags (see IDebugFlags).""")
def __getitem__(key):
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/basetestiapplicationrequest.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/basetestiapplicationrequest.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/basetestiapplicationrequest.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -29,7 +29,7 @@
def testHaveCustomTestsForIApplicationRequest(self):
# Make sure that tests are defined for things we can't test here
- self.test_IApplicationRequest_body
+ self.test_IApplicationRequest_bodyStream
def testEnvironment(self):
request = self._Test__new(foo='Foo', bar='Bar')
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/test_baserequest.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/test_baserequest.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/test_baserequest.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -40,15 +40,12 @@
def _Test__expectedViewType(self):
return None # we don't expect
- def test_IApplicationRequest_body(self):
+ def test_IApplicationRequest_bodyStream(self):
from zope.publisher.base import BaseRequest
request = BaseRequest(StringIO('spam'), {})
- self.assertEqual(request.body, 'spam')
+ self.assertEqual(request.bodyStream.read(), 'spam')
- request = BaseRequest(StringIO('spam'), {})
- self.assertEqual(request.bodyFile.read(), 'spam')
-
def test_IPublicationRequest_getPositionalArguments(self):
self.assertEqual(self._Test__new().getPositionalArguments(), ())
Modified: Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/test_http.py
===================================================================
--- Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/test_http.py 2005-09-07 23:48:38 UTC (rev 38376)
+++ Zope3/branches/srichter-twisted-integration2/src/zope/publisher/tests/test_http.py 2005-09-08 00:34:11 UTC (rev 38377)
@@ -20,7 +20,8 @@
from zope.interface import implements
from zope.publisher.interfaces.logginginfo import ILoggingInfo
-from zope.publisher.http import HTTPRequest, HTTPResponse, StrResult
+from zope.publisher.http import HTTPRequest, HTTPResponse
+from zope.publisher.http import HTTPInputStream, StrResult
from zope.publisher.publish import publish
from zope.publisher.base import DefaultPublication
from zope.publisher.interfaces.http import IHTTPRequest, IHTTPResponse
@@ -48,6 +49,48 @@
return self._id
+data = '''\
+line 1
+line 2
+line 3'''
+
+
+class HTTPInputStreamTests(unittest.TestCase):
+
+ def setUp(self):
+ self.stream = HTTPInputStream(StringIO(data))
+
+ def testRead(self):
+ output = ''
+ self.assertEqual(output, self.stream.cacheStream.getvalue())
+ output += self.stream.read(5)
+ self.assertEqual(output, self.stream.cacheStream.getvalue())
+ output += self.stream.read()
+ self.assertEqual(output, self.stream.cacheStream.getvalue())
+ self.assertEqual(data, self.stream.cacheStream.getvalue())
+
+ def testReadLine(self):
+ output = self.stream.readline()
+ self.assertEqual(output, self.stream.cacheStream.getvalue())
+ output += self.stream.readline()
+ self.assertEqual(output, self.stream.cacheStream.getvalue())
+ output += self.stream.readline()
+ self.assertEqual(output, self.stream.cacheStream.getvalue())
+ output += self.stream.readline()
+ self.assertEqual(output, self.stream.cacheStream.getvalue())
+ self.assertEqual(data, self.stream.cacheStream.getvalue())
+
+ def testReadLines(self):
+ output = ''.join(self.stream.readlines(4))
+ self.assertEqual(output, self.stream.cacheStream.getvalue())
+ output += ''.join(self.stream.readlines())
+ self.assertEqual(output, self.stream.cacheStream.getvalue())
+ self.assertEqual(data, self.stream.cacheStream.getvalue())
+
+ def testGetChacheStream(self):
+ self.stream.read(5)
+ self.assertEqual(data, self.stream.getCacheStream().getvalue())
+
class HTTPTests(unittest.TestCase):
_testEnv = {
@@ -558,6 +601,7 @@
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(ConcreteHTTPTests))
suite.addTest(unittest.makeSuite(TestHTTPResponse))
+ suite.addTest(unittest.makeSuite(HTTPInputStreamTests))
return suite
More information about the Zope3-Checkins
mailing list