[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