[Checkins] SVN: Sandbox/shane/republish/zope/p Configure the pipeline using zope.component :-)
Shane Hathaway
shane at hathawaymix.org
Tue Feb 10 21:02:49 EST 2009
Log message for revision 96434:
Configure the pipeline using zope.component :-)
Changed:
A Sandbox/shane/republish/zope/pipeline/autotemp.py
A Sandbox/shane/republish/zope/pipeline/build.py
U Sandbox/shane/republish/zope/pipeline/caller.py
A Sandbox/shane/republish/zope/pipeline/configure.zcml
D Sandbox/shane/republish/zope/pipeline/dbopener.py
A Sandbox/shane/republish/zope/pipeline/retry.py
D Sandbox/shane/republish/zope/pipeline/standard.py
A Sandbox/shane/republish/zope/pipeline/traversalroot.py
D Sandbox/shane/republish/zope/pipeline/txncontroller.py
A Sandbox/shane/republish/zope/pipeline/txnmiddle.py
U Sandbox/shane/republish/zope/publisher/interfaces/base.py
U Sandbox/shane/republish/zope/publisher/interfaces/http.py
-=-
Added: Sandbox/shane/republish/zope/pipeline/autotemp.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/autotemp.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/autotemp.py 2009-02-11 02:02:48 UTC (rev 96434)
@@ -0,0 +1,39 @@
+
+from cStringIO import StringIO
+
+bufsize = 8192
+
+class AutoTemporaryFile(object):
+ """Initially a StringIO, but becomes a TemporaryFile if it grows big"""
+ def __init__(self, threshold=bufsize):
+ self._threshold = threshold
+ self._f = f = StringIO()
+ # delegate most methods
+ for m in ('read', 'seek', 'tell', 'close'):
+ setattr(self, m, getattr(f, m))
+
+ def write(self, data):
+ if self.tell() + len(data) >= self._threshold:
+ # convert to TemporaryFile
+ f = tempfile.TemporaryFile()
+ f.write(self._f.getvalue())
+ f.seek(self.tell())
+ self._f = f
+ # delegate all important methods
+ for m in ('write', 'read', 'seek', 'tell', 'close'):
+ setattr(self, m, getattr(f, m))
+ self._f.write(data)
+
+ def copyfrom(self, src):
+ while True:
+ data = src.read(bufsize)
+ if not data:
+ break
+ self.write(data)
+
+ def copyto(self, dest):
+ while True:
+ data = self.read(bufsize)
+ if not data:
+ break
+ dest.write(data)
Copied: Sandbox/shane/republish/zope/pipeline/build.py (from rev 96373, Sandbox/shane/republish/zope/pipeline/standard.py)
===================================================================
--- Sandbox/shane/republish/zope/pipeline/build.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/build.py 2009-02-11 02:02:48 UTC (rev 96434)
@@ -0,0 +1,34 @@
+
+"""Creates the WSGI pipeline configured by ZCML and the component architecture.
+
+
+"""
+
+from zope.publisher.interfaces import IWSGIApplication
+
+standard_stages = [] # set by pipeline:stages directive
+
+def make_app(configuration, stages=standard_stages):
+ stages = list(stages) # make a copy
+ name = stages.pop()
+ app = build_stage(None, name, configuration)
+ while stages:
+ name = stages.pop()
+ app = build_stage(app, name, configuration)
+ return app
+
+def build_stage(app, name, configuration):
+ res = None
+ # look for an app that requires the configuration
+ if app is None:
+ res = IWSGIApplication(configuration, name=name, default=None)
+ else:
+ res = IWSGIApplication(app, configuration, name=name, default=None)
+ if res is not None:
+ return res
+ # look for an app that requires no configuration
+ if app is None:
+ res = IWSGIApplication(name=name)
+ else:
+ res = IWSGIApplication(app, name=name)
+ return res
Property changes on: Sandbox/shane/republish/zope/pipeline/build.py
___________________________________________________________________
Added: svn:mergeinfo
+
Modified: Sandbox/shane/republish/zope/pipeline/caller.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/caller.py 2009-02-10 23:52:56 UTC (rev 96433)
+++ Sandbox/shane/republish/zope/pipeline/caller.py 2009-02-11 02:02:48 UTC (rev 96434)
@@ -5,6 +5,7 @@
"""WSGI app that calls the traversed object.
Requires 'zope.request', which implements IRequest, in the environment.
+ The 'traversed' attribute of the request must be set.
"""
def __call__(self, environ, start_response):
request = environ['zope.request']
Added: Sandbox/shane/republish/zope/pipeline/configure.zcml
===================================================================
--- Sandbox/shane/republish/zope/pipeline/configure.zcml (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/configure.zcml 2009-02-11 02:02:48 UTC (rev 96434)
@@ -0,0 +1,44 @@
+
+
+<!--
+ # other possible stages:
+ # RequestProfiler
+ # CodeFreshnessChecker
+ # Multiprocessor
+ # ComponentConfigurator
+ # DetailedTracebackGenerator
+
+ # stages we're using:
+ 'request_logger',
+ 'retry',
+ 'request_creator', # also sets locale (?)
+ 'traversal_root_opener',
+ 'transaction_controller',
+ 'app_error_handler',
+ 'global_authenticator',
+ # Note: the traverser also has to invoke authentication at each step.
+ 'traverser',
+ 'transaction_annotator',
+ 'caller',
+-->
+
+<pipeline:stages names="
+ request_logger
+ retry
+ request_creator
+ traversal_root_opener
+ transaction_controller
+ app_error_handler
+ global_authenticator
+ traverser
+ transaction_annotator
+ caller
+ " />
+
+<zope:adapter
+ factory=".txnmiddle.TransactionController"
+ name="transaction_controller" />
+
+<zope:adapter
+ factory=".txnmiddle.TransactionAnnotator"
+ name="transaction_annotator" />
Added: Sandbox/shane/republish/zope/pipeline/retry.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/retry.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/retry.py 2009-02-11 02:02:48 UTC (rev 96434)
@@ -0,0 +1,62 @@
+
+from zope.publisher.interfaces import IWSGIApplication
+from zope.publisher.interfaces.exceptions import Retry
+from ZODB.POSException import ConflictError
+
+from zope.pipeline.autotemp import AutoTemporaryFile
+
+
+class Retry(object):
+ """Retries requests when a Retry or ConflictError propagates.
+
+ This middleware 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 has
+ a true value.
+ """
+ implements(IWSGIApplication)
+ adapts(IWSGIApplication)
+
+ def __init__(self, app, max_attempts=3):
+ self.app = app
+ self.max_attempts = max_attempts
+
+ def __call__(self, environ, start_response):
+ wsgi_input = environ.get('wsgi.input')
+ if wsgi_input is not None:
+ if not hasattr(wsgi_input, 'seek'):
+ # make the input stream rewindable
+ f = AutoTemporaryFile()
+ f.copyfrom(wsgi_input)
+ environ['wsgi.input'] = wsgi_input = f
+
+ def retryable_start_response(status, response_headers, exc_info=None):
+ start_response_params[:] = [status, response_headers, exc_info]
+ tmp = AutoTemporaryFile()
+ output_file[:] = [tmp]
+ return tmp
+
+ attempt = 1
+ while attempt < self.max_attempts:
+ start_response_params = []
+ output_file = []
+ environ['zope.can_retry'] = True
+ try:
+ res = self.app(environ, retryable_start_response)
+ if start_response_params:
+ dest = start_response(*tuple(start_response_params))
+ src = output_file[0]
+ src.seek(0)
+ src.copyto(dest)
+ src.close()
+ return res
+ except (Retry, ConflictError):
+ if 'zope.request' in environ:
+ del environ['zope.request']
+ if wsgi_input is not None:
+ wsgi_input.seek(0)
+ attempt += 1
+
+ # try once more, this time without retry support
+ environ['zope.can_retry'] = False
+ return self.app(environ, start_response)
Deleted: Sandbox/shane/republish/zope/pipeline/standard.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/standard.py 2009-02-10 23:52:56 UTC (rev 96433)
+++ Sandbox/shane/republish/zope/pipeline/standard.py 2009-02-11 02:02:48 UTC (rev 96434)
@@ -1,28 +0,0 @@
-
-"""Creates a standard Zope WSGI publishing pipeline."""
-
-from zope.pipeline.call import Caller
-
-standard_pipeline = (
- # RequestProfiler,
- # CodeFreshnessChecker,
- # Multiprocessor,
- # ComponentConfigurator,
- # DetailedTracebackGenerator,
- RequestLogger,
- RequestCreator, # also sets locale (?)
- DatabaseOpener,
- TransactionController, # includes retry logic and annotation
- choose_traverser, # complex or simple traversal based on zope.conf
- AppErrorHandler,
- Caller
-)
-
-def make_app(zope_conf, pipeline=standard_pipeline):
- p = list(pipeline)
- factory = p.pop()
- app = factory(zope_conf)
- while p:
- factory, kw = p.pop()
- app = factory(app, zope_conf)
- return app
Copied: Sandbox/shane/republish/zope/pipeline/traversalroot.py (from rev 96372, Sandbox/shane/republish/zope/pipeline/dbopener.py)
===================================================================
--- Sandbox/shane/republish/zope/pipeline/traversalroot.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/traversalroot.py 2009-02-11 02:02:48 UTC (rev 96434)
@@ -0,0 +1,7 @@
+
+
+class TraversalRootOpener(object):
+
+ def __init__(self, app, zope_conf):
+ self.app = app
+
\ No newline at end of file
Copied: Sandbox/shane/republish/zope/pipeline/txnmiddle.py (from rev 96372, Sandbox/shane/republish/zope/pipeline/txncontroller.py)
===================================================================
--- Sandbox/shane/republish/zope/pipeline/txnmiddle.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/txnmiddle.py 2009-02-11 02:02:48 UTC (rev 96434)
@@ -0,0 +1,92 @@
+
+
+import transaction
+from zope.location.interfaces import ILocationInfo
+from zope.interface import providedBy
+from zope.interface import adapts
+from zope.interface import implements
+from zope.publisher.interfaces import IRequest
+from zope.publisher.interfaces import IWSGIApplication
+from zope.security.proxy import removeSecurityProxy
+
+
+class TransactionController(object):
+ """WSGI middleware that manages transactions.
+ """
+ implements(IWSGIApplication)
+ adapts(IWSGIApplication)
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ transaction.begin()
+ try:
+ res = self.app(environ, start_response)
+ except:
+ transaction.abort()
+ raise
+ txn = transaction.get()
+ if txn.isDoomed():
+ txn.abort()
+ else:
+ txn.commit()
+ return res
+
+
+class TransactionAnnotator(object):
+ """WSGI middleware that annotates transactions.
+
+ Requires 'zope.request' in the environment.
+ """
+ implements(IWSGIApplication)
+ adapts(IWSGIApplication)
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ res = self.app(environ, start_response)
+ txn = transaction.get()
+ if not txn.isDoomed():
+ request = environ['zope.request']
+ name, ob = request.traversed[-1]
+ self.annotate(txn, request, ob)
+ return res
+
+ def annotate(self, txn, request, ob):
+ """Set some useful meta-information on the transaction.
+
+ This information is used by the undo framework, for example.
+ """
+ if request.principal is not None:
+ txn.setUser(request.principal.id)
+
+ # Work around methods that are usually used for views
+ bare = removeSecurityProxy(ob)
+ if isinstance(bare, instancemethod):
+ ob = bare.im_self
+
+ # set the location path
+ path = None
+ location = ILocationInfo(ob, None)
+ if location is not None:
+ # Views are made children of their contexts, but that
+ # doesn't necessarily mean that we can fully resolve the
+ # path. E.g. the family tree of a resource cannot be
+ # resolved completely, as the site manager is a dead end.
+ try:
+ path = location.getPath()
+ except (AttributeError, TypeError):
+ pass
+ if path is not None:
+ txn.setExtendedInfo('location', path)
+
+ # set the request type
+ iface = IRequest
+ for iface in providedBy(request):
+ if iface.extends(IRequest):
+ break
+ iface_dotted = iface.__module__ + '.' + iface.getName()
+ txn.setExtendedInfo('request_type', iface_dotted)
+ return txn
Modified: Sandbox/shane/republish/zope/publisher/interfaces/base.py
===================================================================
--- Sandbox/shane/republish/zope/publisher/interfaces/base.py 2009-02-10 23:52:56 UTC (rev 96433)
+++ Sandbox/shane/republish/zope/publisher/interfaces/base.py 2009-02-11 02:02:48 UTC (rev 96434)
@@ -5,7 +5,8 @@
from zope.interface import Interface
from zope.interface.common.mapping import IExtendedReadMapping
-__all__ = ('IRequest', 'IResponse', 'IResult', 'IHeld', 'IDebugFlags')
+__all__ = ('IRequest', 'IResponse', 'IResult', 'IHeld', 'IDebugFlags',
+ 'IWSGIApplication')
class IRequest(IExtendedReadMapping):
@@ -62,13 +63,6 @@
"""IPrincipal object associated with the request.
""")
- def retry():
- """Returns a re-initialized request to be retried.
-
- Returns a request suitable for repeating the publication attempt,
- or raises RetryNotSupported if the response can not be retried.
- """
-
bodyStream = Attribute(
"""The stream that provides the data of the request.
@@ -204,17 +198,10 @@
def reset():
"""Reset the output result.
- Reset the response by nullifying already set variables.
+ Reset the response by clearing the status, headers, and body.
"""
- def retry():
- """Returns a re-initialized response to be retried.
- Returns a response suitable for repeating the publication attempt,
- or raises RetryNotSupported if the response can not be retried.
- """
-
-
class IResult(Interface):
"""An iterable that provides the body data of the response.
@@ -267,3 +254,14 @@
sourceAnnotations = Attribute("""Enable ZPT source annotations""")
showTAL = Attribute("""Leave TAL markup in rendered page templates""")
+
+
+class IWSGIApplication(Interface):
+ """Implements the WSGI application spec. See PEP 333:
+
+ http://www.python.org/dev/peps/pep-0333/
+ """
+
+ def __call__(environ, start_response):
+ """Call the application and return a body iterator."""
+
Modified: Sandbox/shane/republish/zope/publisher/interfaces/http.py
===================================================================
--- Sandbox/shane/republish/zope/publisher/interfaces/http.py 2009-02-10 23:52:56 UTC (rev 96433)
+++ Sandbox/shane/republish/zope/publisher/interfaces/http.py 2009-02-11 02:02:48 UTC (rev 96434)
@@ -51,7 +51,7 @@
method = Attribute("Request method, normalized to upper case")
def setPathSuffix(steps):
- """Deprecated: Add to traversal_stack instead.
+ """Deprecated: Prepend to traversal_stack instead.
"""
locale = Attribute(
More information about the Checkins
mailing list