[Checkins] SVN: Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/ added tests of retry and requestsetup

Shane Hathaway shane at hathawaymix.org
Tue Feb 17 23:48:43 EST 2009


Log message for revision 96662:
  added tests of retry and requestsetup
  

Changed:
  U   Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/requestsetup.py
  U   Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/retry.py
  A   Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/requestsetup.txt
  A   Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/retry.txt
  A   Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/tests.py
  U   Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/configure.zcml

-=-
Modified: Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/requestsetup.py
===================================================================
--- Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/requestsetup.py	2009-02-18 03:02:05 UTC (rev 96661)
+++ Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/requestsetup.py	2009-02-18 04:48:42 UTC (rev 96662)
@@ -18,7 +18,9 @@
 from zope.configuration.exceptions import ConfigurationError
 from zope.httpform import FormParser
 from zope.i18n.interfaces import IUserPreferredCharsets
+from zope.i18n.interfaces import IUserPreferredLanguages
 from zope.interface import implements
+from zope.publisher.interfaces.browser import IBrowserRequest
 from zope.publisher.interfaces import IWSGIApplication
 from zope.testing import cleanup
 
@@ -80,8 +82,8 @@
             setDefaultSkin(request)
 
 
-class ProcessForm(object):
-    """WSGI middleware that processes HTML form data.
+class ParseForm(object):
+    """WSGI middleware that parses HTML form data.
 
     This step is separate from request creation so that the
     error handling step can catch form data errors.
@@ -100,9 +102,13 @@
         charsets = []
         def to_unicode(text):
             if not charsets:
-                envadapter = IUserPreferredCharsets(request)
-                charsets.extend(
-                    envadapter.getPreferredCharsets() or ['utf-8'])
+                envadapter = IUserPreferredCharsets(request, None)
+                if envadapter:
+                    pref = envadapter.getPreferredCharsets()
+                    if pref:
+                        charsets.extend(pref)
+                if not charsets:
+                    charsets.append('utf-8')
             for charset in charsets:
                 try:
                     return unicode(text, charset)
@@ -164,9 +170,9 @@
         priorities = [item['priority'] for item in l]
         if len(set(priorities)) != len(l):
             raise ConfigurationError('All registered publishers for a given '
-                                     'method+mimetype must have distinct '
-                                     'priorities. Please check your ZCML '
-                                     'configuration')
+                                     'scheme+method+mimetype must have '
+                                     'distinct priorities. Please check your '
+                                     'configuration.')
 
     def get_factories_for(self, scheme, method, mimetype):
         if ';' in mimetype:

Modified: Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/retry.py
===================================================================
--- Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/retry.py	2009-02-18 03:02:05 UTC (rev 96661)
+++ Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/retry.py	2009-02-18 04:48:42 UTC (rev 96662)
@@ -20,10 +20,10 @@
 from zope.pipeline.autotemp import AutoTemporaryFile
 
 
-class Retry(object):
+class RetryApp(object):
     """Retries requests when a Retry or ConflictError propagates.
 
-    This middleware app should enclose the app that creates zope.request.
+    This app should enclose the app that creates zope.request.
     It sets an environment variable named 'zope.can_retry'.  Error handlers
     should propagate Retry or ConflictError when 'zope.can_retry' is
     true.
@@ -37,7 +37,9 @@
     def __call__(self, environ, start_response):
         wsgi_input = environ.get('wsgi.input')
         if wsgi_input is not None:
-            if not hasattr(wsgi_input, 'seek'):
+            try:
+                wsgi_input.seek(0)
+            except (AttributeError, IOError):
                 # make the input stream rewindable
                 f = AutoTemporaryFile()
                 f.copyfrom(wsgi_input)
@@ -58,8 +60,6 @@
             try:
                 res = self.next_app(environ, retryable_start_response)
             except (Retry, ConflictError):
-                if 'zope.request' in environ:
-                    del environ['zope.request']
                 if wsgi_input is not None:
                     wsgi_input.seek(0)
                 attempt += 1

Added: Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/requestsetup.txt
===================================================================
--- Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/requestsetup.txt	                        (rev 0)
+++ Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/requestsetup.txt	2009-02-18 04:48:42 UTC (rev 96662)
@@ -0,0 +1,90 @@
+
+Request Creation
+----------------
+
+The ``CreateRequest`` application puts a Zope-style request object in the
+WSGI environment.  Zope-style request objects are mainly used for
+object traversal.
+
+Create an app that prints the contents of the created request.
+
+    >>> attempts = []
+    >>> def my_app(environ, start_response):
+    ...     status = '200 OK'
+    ...     response_headers = [('Content-type','text/plain')]
+    ...     start_response(status, response_headers)
+    ...     request = environ['zope.request']
+    ...     return [repr(request)]
+
+Now put CreateRequest in front of the test app and try it out.
+
+    >>> from zope.pipeline.apps.requestsetup import CreateRequest
+    >>> app = CreateRequest(my_app)
+    >>> env = {'CONTENT_TYPE': 'text/html; charset=UTF-8'}
+    >>> got_headers = []
+    >>> def start_response(status, headers, exc_info=None):
+    ...     got_headers[:] = list(headers)
+    >>> app(env, start_response)
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: No registered request factory found for (http/GET/text/html; charset=UTF-8)
+
+That happened because no request factories have been registered.  Add one
+and try again.
+
+    >>> class TestRequest(object):
+    ...     def __init__(self, environ):
+    ...         self.environ = environ
+    ...     def close(self):
+    ...         pass
+    >>> from zope.pipeline.apps.requestsetup import factoryRegistry
+    >>> factoryRegistry.register('http', 'GET', '*', 'a', 10, TestRequest)
+    >>> app(env, start_response)
+    ['<TestRequest object at ...>']
+
+Register another type of request and give it a higher priority.
+
+    >>> class HighTestRequest(TestRequest):
+    ...     pass
+    >>> factoryRegistry.register('http', 'GET', '*', 'b', 20, HighTestRequest)
+    >>> app(env, start_response)
+    ['<HighTestRequest object at ...>']
+
+Overwrite the registration by reusing a name.
+
+    >>> class HigherTestRequest(TestRequest):
+    ...     pass
+    >>> factoryRegistry.register('http', 'GET', '*', 'b', 20, HigherTestRequest)
+    >>> app(env, start_response)
+    ['<HigherTestRequest object at ...>']
+
+Try to register another by a different name but with the same priority.
+
+    >>> class XTestRequest(HighTestRequest):
+    ...     pass
+    >>> factoryRegistry.register('http', 'GET', '*', 'c', 20, XTestRequest)
+    Traceback (most recent call last):
+    ...
+    ConfigurationError: All registered publishers for a given scheme+method+mimetype must have distinct priorities. Please check your configuration.
+
+
+Form Parsing
+------------
+
+The ``ParseForm`` application uses ``zope.httpform`` to parse the form
+data in a request.
+
+    >>> from zope.pipeline.apps.requestsetup import ParseForm
+    >>> class FormTestRequest(object):
+    ...     def __init__(self, environ):
+    ...         self.environ = environ
+    ...     def close(self):
+    ...         pass
+    ...     def __repr__(self):
+    ...         return 'FormTestRequest(%s)' % repr(self.form)
+    >>> env = {'QUERY_STRING': 'a:int:list=3&a:int:list=4'}
+    >>> factoryRegistry.__init__()
+    >>> factoryRegistry.register('http', 'GET', '*', 't', 0, FormTestRequest)
+    >>> app = CreateRequest(ParseForm(my_app))
+    >>> app(env, start_response)
+    ["FormTestRequest({u'a': [3, 4]})"]

Added: Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/retry.txt
===================================================================
--- Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/retry.txt	                        (rev 0)
+++ Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/retry.txt	2009-02-18 04:48:42 UTC (rev 96662)
@@ -0,0 +1,106 @@
+
+Retry Application
+-----------------
+
+The retry application catches certain kinds of exceptions that occur
+later in the pipeline and retries them automatically.  This is useful
+for any transactional system that sometimes generates conflict errors
+that are resolved easily.
+
+To test the retry app, first we'll make a simple app that generates a
+Retry exception the first time, but not the second time.
+
+    >>> from zope.publisher.interfaces.exceptions import Retry
+    >>> attempts = []
+    >>> def my_app(environ, start_response):
+    ...     attempts.append(1)
+    ...     status = '200 OK'
+    ...     response_headers = [('Content-type','text/plain')]
+    ...     response_headers += [('X-Attempts', str(len(attempts)))]
+    ...     start_response(status, response_headers)
+    ...     if len(attempts) == 1:
+    ...         if environ.get('zope.can_retry'):
+    ...             raise Retry()
+    ...         else:
+    ...             raise ValueError()
+    ...     return ['Hello world!\n']
+
+Now put RetryApp in front of the test app and try it out.
+
+    >>> from zope.pipeline.apps.retry import RetryApp
+    >>> app = RetryApp(my_app)
+    >>> env = {}
+    >>> got_headers = []
+    >>> def start_response(status, headers, exc_info=None):
+    ...     got_headers[:] = list(headers)
+    >>> app(env, start_response)
+    ['Hello world!\n']
+
+The test app was called twice.
+
+    >>> attempts
+    [1, 1]
+    >>> got_headers
+    [('Content-type', 'text/plain'), ('X-Attempts', '2')]
+
+Do it again, but this time only allow 2 attempts.
+
+    >>> del attempts[:]
+    >>> del got_headers[:]
+    >>> app = RetryApp(my_app, max_attempts=2)
+    >>> app(env, start_response)
+    ['Hello world!\n']
+
+The test app was called twice.
+
+    >>> attempts
+    [1, 1]
+    >>> got_headers
+    [('Content-type', 'text/plain'), ('X-Attempts', '2')]
+
+Once more, but this time only allow 1 attempt.
+
+    >>> del attempts[:]
+    >>> del got_headers[:]
+    >>> app = RetryApp(my_app, max_attempts=1)
+    >>> app(env, start_response)
+    Traceback (most recent call last):
+    ...
+    ValueError
+
+
+Repeating the input stream
+--------------------------
+
+Create a test app that echoes a stream back.
+
+    >>> del attempts[:]
+    >>> del got_headers[:]
+    >>> def echo_app(environ, start_response):
+    ...     data = environ['wsgi.input'].read()
+    ...     attempts.append(1)
+    ...     status = '200 Ok'
+    ...     response_headers = [('Content-Type', 'text/plain'),
+    ...                         ('Content-Length', str(len(data)))]
+    ...     start_response(status, response_headers)
+    ...     if len(attempts) == 1:
+    ...         if environ.get('zope.can_retry'):
+    ...             raise Retry()
+    ...         else:
+    ...             raise ValueError()
+    ...     return [data]
+    >>> class Stream:
+    ...     def __init__(self, data):
+    ...         self.pos = 0
+    ...         self.data = data
+    ...     def read(self, bytes=-1):
+    ...         if bytes < 0:
+    ...             bytes = len(self.data) - self.pos
+    ...         res = self.data[self.pos : self.pos + bytes]
+    ...         self.pos += bytes
+    ...         return res
+    >>> env = {'wsgi.input': Stream('0123456789' * 4)}
+    >>> app = RetryApp(echo_app)
+    >>> app(env, start_response)
+    ['0123456789012345678901234567890123456789']
+

Added: Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/tests.py
===================================================================
--- Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/tests.py	                        (rev 0)
+++ Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/tests.py	2009-02-18 04:48:42 UTC (rev 96662)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests of this package"""
+
+import unittest
+
+from zope.testing import doctest
+
+flags = doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS
+
+from zope.testing.cleanup import cleanUp
+
+def setUp(test=None):
+    cleanUp()
+
+def tearDown(test=None):
+    cleanUp()
+
+def test_suite():
+    return unittest.TestSuite([
+        doctest.DocFileSuite('retry.txt', optionflags=flags),
+        doctest.DocFileSuite('requestsetup.txt', optionflags=flags,
+            setUp=setUp, tearDown=tearDown),
+    ])
+
+if __name__ == '__main__':
+    unittest.main()

Modified: Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/configure.zcml
===================================================================
--- Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/configure.zcml	2009-02-18 03:02:05 UTC (rev 96661)
+++ Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/configure.zcml	2009-02-18 04:48:42 UTC (rev 96662)
@@ -19,7 +19,7 @@
     control_transaction
     event
     handle_error
-    process_form
+    parse_form
     authenticate
     traverse
     annotate_transaction
@@ -40,7 +40,7 @@
 
 <wsgi:application
     name="retry"
-    factory=".apps.retry.Retry"
+    factory=".apps.retry.RetryApp"
     for=".interfaces.INoRequest" />
 
 <wsgi:application
@@ -76,14 +76,14 @@
 
 <!-- no form processing for non-browser requests -->
 <wsgi:application
-    name="process_form"
+    name="parse_form"
     factory=".apps.passthrough"
     for="zope.publisher.interfaces.IRequest" />
 
 <!-- process forms for browser requests -->
 <wsgi:application
-    name="process_form"
-    factory=".apps.requestsetup.ProcessForm"
+    name="parse_form"
+    factory=".apps.requestsetup.ParseForm"
     for="zope.publisher.interfaces.browser.IBrowserRequest" />
 
 <!--wsgi:application



More information about the Checkins mailing list