[Checkins] SVN: Sandbox/shane/republish/zope/pipeline/ Checkpoint. Trying to provide pipeline extensibility without excess complexity.

Shane Hathaway shane at hathawaymix.org
Wed Feb 11 04:20:26 EST 2009


Log message for revision 96440:
  Checkpoint.  Trying to provide pipeline extensibility without excess complexity.
  

Changed:
  U   Sandbox/shane/republish/zope/pipeline/authenticator.py
  U   Sandbox/shane/republish/zope/pipeline/caller.py
  U   Sandbox/shane/republish/zope/pipeline/configure.zcml
  A   Sandbox/shane/republish/zope/pipeline/main.py
  A   Sandbox/shane/republish/zope/pipeline/traverser.py

-=-
Modified: Sandbox/shane/republish/zope/pipeline/authenticator.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/authenticator.py	2009-02-11 09:09:39 UTC (rev 96439)
+++ Sandbox/shane/republish/zope/pipeline/authenticator.py	2009-02-11 09:20:25 UTC (rev 96440)
@@ -16,9 +16,9 @@
     The WSGI environment must contain 'zope.request'.
     """
     implements(IWSGIApplication)
-    adapts(IWSGIApplication)
+    adapts(IWSGIApplication, IRequest)
 
-    def __init__(self, app):
+    def __init__(self, app, marker_request=None):
         self.app = app
 
     def __call__(self, environ, start_response):
@@ -39,7 +39,10 @@
         finally:
             endInteraction()
 
+    def __repr__(self):
+        return '%s(%s)' % (self.__class__.__name__, repr(self.app))
 
+
 def placeful_auth(request, ob):
     """Traversal hook that tries to authenticate in a context"""
 

Modified: Sandbox/shane/republish/zope/pipeline/caller.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/caller.py	2009-02-11 09:09:39 UTC (rev 96439)
+++ Sandbox/shane/republish/zope/pipeline/caller.py	2009-02-11 09:20:25 UTC (rev 96440)
@@ -2,7 +2,9 @@
 from zope.interface import implements
 from zope.proxy import removeAllProxies
 from zope.publisher.interfaces import IWSGIApplication
+from zope.publisher.interfaces import IRequest
 
+
 class Caller(object):
     """WSGI app that calls the traversed object.
 
@@ -10,7 +12,11 @@
     The 'traversed' attribute of the request must be set.
     """
     implements(IWSGIApplication)
+    adapts(IRequest)
 
+    def __init__(self, marker_request=None):
+        pass
+
     def __call__(self, environ, start_response):
         request = environ['zope.request']
         name, ob = request.traversed[-1]
@@ -21,7 +27,10 @@
         start_response(response.getStatusString(), response.getHeaders())
         return response.consumeBodyIter()
 
+    def __repr__(self):
+        return '%s()' % self.__class__.__name__
 
+
 _marker = object()  # Create a new marker object.
 
 def unwrapMethod(obj):

Modified: Sandbox/shane/republish/zope/pipeline/configure.zcml
===================================================================
--- Sandbox/shane/republish/zope/pipeline/configure.zcml	2009-02-11 09:09:39 UTC (rev 96439)
+++ Sandbox/shane/republish/zope/pipeline/configure.zcml	2009-02-11 09:20:25 UTC (rev 96440)
@@ -1,31 +1,15 @@
+<configuration>
 
+<!-- a wsgi:stage-list directive registers a bound StageList.adapt() method
+     as an unnamed adapter from the request type to IStageList. -->
 
-<!--
-    # other possible stages:
-    # RequestProfiler
-    # CodeFreshnessChecker
-    # Multiprocessor
-    # ComponentConfigurator
-    # DetailedTracebackGenerator
-
-    # stages we're using:
-    'logger',
-    'retry',
-    'request_factory',        # also sets locale (?)
-    'root_opener',
-    'transaction_controller',
-    'error_handler',
-    'global_authenticator',
-    # Note: the traverser also has to invoke authentication at each step.
-    'traverser',
-    'transaction_annotator',
-    'caller',
--->
-
-<pipeline:stages names="
-    logger
+<wsgi:stage-list require=".interfaces.IUndecidedRequest" names="
     retry
     request_factory
+    select_pipeline
+    " />
+
+<wsgi:stage-list require="zope.publisher.interfaces.IRequest" names="
     root_opener
     transaction_controller
     notifier
@@ -36,6 +20,15 @@
     caller
     " />
 
+<!-- a wsgi:pipeline directive registers a bound Pipeline.adapt() method as
+     an adapter from the request type to IWSGIApplication with name
+     "pipeline". -->
+
+<wsgi:pipeline require=".interfaces.IUndecidedRequest" />
+<wsgi:pipeline require="zope.publisher.interfaces.IRequest" />
+<wsgi:pipeline require="zope.publisher.interfaces.http.IHTTPRequest" />
+<wsgi:pipeline require="zope.publisher.interfaces.browser.IBrowserRequest" />
+
 <adapter
     factory="..."
     name="logger" />
@@ -79,3 +72,28 @@
 <adapter
     factory=".caller.Caller"
     name="caller" />
+
+</configuration>
+
+
+
+<!--
+    # other possible stages:
+    # RequestProfiler
+    # CodeFreshnessChecker
+    # Multiprocessor
+    # ComponentConfigurator
+    # DetailedTracebackGenerator
+
+    # stages we're using:
+    'logger',
+    'retry',
+    'request_factory',        # also sets locale (?)
+    'root_opener',
+    'transaction_controller',
+    'error_handler',
+    'global_authenticator',
+    'traverser',
+    'transaction_annotator',
+    'caller',
+-->

Added: Sandbox/shane/republish/zope/pipeline/main.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/main.py	                        (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/main.py	2009-02-11 09:20:25 UTC (rev 96440)
@@ -0,0 +1,52 @@
+
+
+class StageList(object):
+    implements(IStageList)
+
+    def __init__(self, names):
+        self.names = names
+
+    def adapt(self, request):
+        """Called by adapter lookup"""
+        return self
+
+
+class MarkerRequest(object):
+    """A marker object that claims to provide a request type.
+
+    This is used for adapter lookup.
+    """
+
+    __slots__ = ('__provides__',)
+
+    def __init__(self, request_type):
+        request_type.directlyProvidedBy(self)
+
+
+class Pipeline(object):
+    implements(IWSGIApplication)
+
+    def __init__(self, request_type):
+        self.app = None
+        self.request_type = request_type
+
+    def adapt(self, request):
+        """Called by adapter lookup"""
+        app = self.app
+        if app is None:
+            self.app = app = self.make_app()
+        return app
+
+    def make_app(self):
+        marker_req = MarkerRequest(self.request_type)
+        stage_list = IStageList(marker_req)
+        names = list(stage_list.names)  # make a copy
+        # The last name in the list is an application.
+        name = names.pop()
+        app = IWSGIApplication(marker_req, name=name)
+        while names:
+            # The rest of the names are middleware.
+            name = names.pop()
+            app = getMultiAdapter(
+                (app, marker_req), IWSGIApplication, name=name)
+        return app

Added: Sandbox/shane/republish/zope/pipeline/traverser.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/traverser.py	                        (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/traverser.py	2009-02-11 09:20:25 UTC (rev 96440)
@@ -0,0 +1,55 @@
+
+from zope.interface import adapts
+from zope.interface import implements
+from zope.publisher.interfaces import IWSGIApplication
+
+
+class Traverser(object):
+    """Traverses the object graph based on the traversal stack.
+
+    Requires 'zope.request' in the WSGI environment.
+    """
+    implements(IWSGIApplication)
+    adapts(IWSGIApplication)
+
+    def __init__(self, app):
+        self.app = app
+
+    def __call__(self, environ, start_response):
+        request = environ['zope.request']
+        self.traverse(request)
+        return self.app(environ, start_response)
+
+    def traverse(self, request):
+        traversal_stack = request.traversal_stack
+        traversal_hooks = request.traversal_hooks
+        traversed = request.traversed
+
+        root_name, obj = traversed[-1]
+        prev_object = None
+
+        while True:
+            if obj is not prev_object:
+                # Call hooks (but not more than once).
+                for hook in traversal_hooks:
+                    hook(request, obj)
+
+            if not traversal_stack:
+                break
+
+            prev_object = obj
+
+            # Traverse to the next step.
+            name = traversal_stack.pop()
+            obj = self.traverse_name(obj, name)
+            traversed.append((name, obj))
+
+    def traverse_name(self, obj, name):
+        pass
+
+
+class HTTPTraverser(Traverser):
+    implements(IWSGIApplication)
+    adapts(IWSGIApplication)
+    request_type = IHTTPRequest
+



More information about the Checkins mailing list