[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