[Checkins] SVN: Sandbox/shane/republish/zope.pipeline/ TransactionController now includes annotation. Added TransactionScrubber, which does nothing but abort the transaction.

Shane Hathaway shane at hathawaymix.org
Fri Mar 20 17:22:21 EDT 2009


Log message for revision 98293:
  TransactionController now includes annotation.  Added TransactionScrubber, which does nothing but abort the transaction.  
  Also added a few design notes.
  

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

-=-
Added: Sandbox/shane/republish/zope.pipeline/design.txt
===================================================================
--- Sandbox/shane/republish/zope.pipeline/design.txt	                        (rev 0)
+++ Sandbox/shane/republish/zope.pipeline/design.txt	2009-03-20 21:22:20 UTC (rev 98293)
@@ -0,0 +1,32 @@
+
+
+The zope.pipeline package uses WSGI endware as the overarching design
+pattern. The use of a single design pattern is intended to make most
+publisher functionality discoverable and comprehensible. Endware
+components are configured through ZCML and the endware choices vary
+according to the interfaces provided by the request object.
+
+By contrast, the IPublication interface in zope.publisher makes some
+functionality discoverable but obscures other functionality. For
+example, the IPublication interface has the notion of object traversal,
+so it is easy to see where traversal happens, but the interface does
+not have a notion of transactions, so it is difficult to see where all
+transaction boundaries are (including special cases) without searching
+a lot of source code. In zope.pipeline, transaction boundaries are
+neatly encapsulated in endware components.
+
+In the current zope.publisher, we vary the publication type and the
+request type according to a set of rules. In zope.pipeline, there is a
+similar capability, but it is implemented in endware components to make
+the publication variance easier to understand and customize. One
+endware application creates a request object (and may annotate it with
+a skin), while another application chooses and caches a pipeline based
+on the request interfaces. Even these endware components are easily
+replaced.
+
+
+
+
+TODO: add this text to the zope.pipeline proposal.  Then rewrite the
+proposal without any reference to the "old way" and call it a first
+draft of the docs.

Modified: Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/txnctl.txt
===================================================================
--- Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/txnctl.txt	2009-03-20 21:01:58 UTC (rev 98292)
+++ Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/tests/txnctl.txt	2009-03-20 21:22:20 UTC (rev 98293)
@@ -2,9 +2,30 @@
 TransactionController Tests
 ---------------------------
 
-The TransactionController commits or aborts the current transaction after
-the pipeline processing.
+The TransactionController commits or aborts the current transaction
+after the pipeline processing. It also annotates the transaction with
+info about what the user did in that transaction.
 
+Set up enough of a request to make the annotation work.
+
+    >>> from zope.interface import classImplements
+    >>> from zope.location.interfaces import IRoot
+    >>> from zope.location.traversing import RootPhysicallyLocatable
+    >>> from zope.component import provideAdapter
+    >>> provideAdapter(RootPhysicallyLocatable)
+
+    >>> class TestRoot(object):
+    ...     def mymethod(self):
+    ...         pass
+    >>> classImplements(TestRoot, IRoot)
+    >>> class TestRequest(object):
+    ...     pass
+    >>> request = TestRequest()
+    >>> class TestPrincipal(object):
+    ...     pass
+    >>> request.principal = TestPrincipal()
+    >>> request.principal.id = 'testuser'
+
 Create a data manager object and an application that registers that data
 manager with the transaction in progress.
 
@@ -14,12 +35,16 @@
     ...         self.phase = ''
     ...         self.committed = []
     ...         self.aborted = []
+    ...         self.committed_user = None
+    ...         self.committed_extension = None
     ...     def tpc_begin(self, txn):
     ...         self.phase = 'begin'
     ...     def tpc_vote(self, txn):
     ...         self.phase = 'vote'
     ...     def tpc_finish(self, txn):
     ...         self.phase = 'finish'
+    ...         self.committed_user = txn.user
+    ...         self.committed_extension = txn._extension
     ...     def tpc_abort(self, txn):
     ...         self.phase = 'abort'
     ...     def commit(self, obj, txn):
@@ -35,7 +60,10 @@
 
 Run the app.
 
-    >>> environ = {}
+    >>> environ = {
+    ...     'zope.pipeline.request': request,
+    ...     'zope.pipeline.traversed': [('xyz', TestRoot().mymethod)],
+    ...     }
     >>> app(environ, None)
     ['done']
     >>> dm.phase
@@ -45,6 +73,14 @@
     >>> dm.aborted
     []
 
+Examine the annotations.
+
+    >>> dm.committed_user
+    '/ testuser'
+    >>> from pprint import pprint
+    >>> pprint(dm.committed_extension)
+    {'location': u'/', 'request_type': 'zope.publisher.interfaces.base.IRequest'}
+
 Make the app raise an exception.
 
     >>> dm = TestDataManager()
@@ -82,51 +118,3 @@
     >>> dm.aborted
     [<TestDataManager object at ...>]
 
-
-TransactionAnnotator Tests
---------------------------
-
-The TransactionAnnotator application adds information to the
-zope.pipeline.request after the rest of the pipeline has finished.
-
-Set up enough of a request to make the TransactionAnnotator work.
-
-    >>> from zope.interface import classImplements
-    >>> from zope.location.interfaces import IRoot
-    >>> from zope.location.traversing import RootPhysicallyLocatable
-    >>> from zope.component import provideAdapter
-    >>> provideAdapter(RootPhysicallyLocatable)
-
-    >>> class TestRoot(object):
-    ...     def mymethod(self):
-    ...         pass
-    >>> classImplements(TestRoot, IRoot)
-    >>> class TestRequest(object):
-    ...     pass
-    >>> request = TestRequest()
-    >>> class TestPrincipal(object):
-    ...     pass
-    >>> request.principal = TestPrincipal()
-    >>> request.principal.id = 'testuser'
-
-Create and run an app with a TransactionAnnotator.
-
-    >>> def my_app(environ, start_response):
-    ...     return ['done annotating']
-    >>> from zope.pipeline.apps.txnctl import TransactionAnnotator
-    >>> app = TransactionAnnotator(my_app)
-    >>> environ = {
-    ...     'zope.pipeline.request': request,
-    ...     'zope.pipeline.traversed': [('xyz', TestRoot().mymethod)],
-    ...     }
-    >>> app(environ, None)
-    ['done annotating']
-
-Examine the annotations.
-
-    >>> transaction.get().user
-    '/ testuser'
-    >>> from pprint import pprint
-    >>> pprint(transaction.get()._extension)
-    {'location': u'/', 'request_type': 'zope.publisher.interfaces.base.IRequest'}
-    >>> transaction.abort()

Modified: Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/txnctl.py
===================================================================
--- Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/txnctl.py	2009-03-20 21:01:58 UTC (rev 98292)
+++ Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/apps/txnctl.py	2009-03-20 21:22:20 UTC (rev 98293)
@@ -26,8 +26,10 @@
 from zope.pipeline.envkeys import TRAVERSED_KEY
 
 
-class TransactionController(object):
-    """WSGI application that begins and commits/aborts transactions.
+class TransactionScrubber(object):
+    """WSGI application that scrubs transaction state.
+
+    Aborts the transaction on the way in and on the way out.
     """
     implements(IWSGIApplication)
 
@@ -35,25 +37,18 @@
         self.next_app = next_app
 
     def __call__(self, environ, start_response):
-        transaction.begin()
+        transaction.abort()
         try:
             res = self.next_app(environ, start_response)
-        except:
+        finally:
             transaction.abort()
-            raise
-        txn = transaction.get()
-        if txn.isDoomed():
-            txn.abort()
-        else:
-            txn.commit()
-        return res
 
 
-class TransactionAnnotator(object):
-    """WSGI application that annotates transactions.
+class TransactionController(object):
+    """WSGI application that begins and commits/aborts transactions.
 
-    Requires 'zope.pipeline.request' and 'zope.pipeline.traversed'
-    in the environment.
+    Also annotates the transaction.  Requires 'zope.pipeline.request'
+    and 'zope.pipeline.traversed' in the environment.
     """
     implements(IWSGIApplication)
 
@@ -61,12 +56,20 @@
         self.next_app = next_app
 
     def __call__(self, environ, start_response):
-        res = self.next_app(environ, start_response)
+        transaction.begin()
+        try:
+            res = self.next_app(environ, start_response)
+        except:
+            transaction.abort()
+            raise
         txn = transaction.get()
-        if not txn.isDoomed():
+        if txn.isDoomed():
+            txn.abort()
+        else:
             request = environ[REQUEST_KEY]
             name, ob = environ[TRAVERSED_KEY][-1]
             self.annotate(txn, request, ob)
+            txn.commit()
         return res
 
     def annotate(self, txn, request, ob):

Modified: Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/configure.zcml
===================================================================
--- Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/configure.zcml	2009-03-20 21:01:58 UTC (rev 98292)
+++ Sandbox/shane/republish/zope.pipeline/src/zope/pipeline/configure.zcml	2009-03-20 21:22:20 UTC (rev 98293)
@@ -16,27 +16,27 @@
 <wsgi:pipeline for="zope.publisher.interfaces.IRequest" names="
     log
     open_root
-    control_transaction
+    scrub_transaction
     set_site
     event
     handle_error
-    process_input
+    control_transaction
+    process_inputs
     authenticate
     traverse
-    annotate_transaction
     fix_relative_links
     call
     " />
 
 <!-- A wsgi:application directive registers an application for use
-     as an application in a pipeline.  If the application is the last
-     element of the pipeline, the factory will be passed no positional
-     parameters.  Otherwise, the application will be passed the next
-     application in the pipeline as the only positional parameter.
+     in a pipeline. If the application is the last element of the
+     pipeline, the factory will be passed no positional parameters.
+     Otherwise, the application will be passed the next application in
+     the pipeline as the only positional parameter.
 
      The implementation of this directive registers a new
      WSGIApplicationFactory as an adapter from a marker request to
-     IWSGIApplicationFactory.  If neither the directive nor the factory
+     IWSGIApplicationFactory. If neither the directive nor the factory
      says what kind of request is required, assume IRequest. -->
 
 <wsgi:application
@@ -64,8 +64,8 @@
     factory=".apps.openroot.RootOpener" />
 
 <wsgi:application
-    name="control_transaction"
-    factory=".apps.txnctl.TransactionController" />
+    name="scrub_transaction"
+    factory=".apps.txnctl.TransactionScrubber" />
 
 <wsgi:application
     name="event"
@@ -75,6 +75,10 @@
     name="handle_error"
     factory="..." /-->
 
+<wsgi:application
+    name="control_transaction"
+    factory=".apps.txnctl.TransactionController" />
+
 <!-- no input processing for non-browser requests -->
 <wsgi:application
     name="process_input"
@@ -102,10 +106,6 @@
     factory=".apps.traversal.BrowserTraverser"
     for="zope.publisher.interfaces.browser.IBrowserRequest" />
 
-<wsgi:application
-    name="annotate_transaction"
-    factory=".apps.txnctl.TransactionAnnotator" />
-
 <!-- Just use mapply for non-HTTP requests -->
 <wsgi:application
     name="call"



More information about the Checkins mailing list