[Checkins] SVN: Sandbox/shane/republish/zope/pipeline/ Experiment: the main database will be in the environment as 'zope.database'.
Shane Hathaway
shane at hathawaymix.org
Fri Feb 13 00:21:32 EST 2009
Log message for revision 96485:
Experiment: the main database will be in the environment as 'zope.database'.
Along with the usual sketching and renaming.
Changed:
A Sandbox/shane/republish/zope/pipeline/__init__.py
A Sandbox/shane/republish/zope/pipeline/apps/authenticate.py
D Sandbox/shane/republish/zope/pipeline/apps/authenticator.py
D Sandbox/shane/republish/zope/pipeline/apps/caller.py
A Sandbox/shane/republish/zope/pipeline/apps/event.py
A Sandbox/shane/republish/zope/pipeline/apps/mapply.py
D Sandbox/shane/republish/zope/pipeline/apps/notify.py
A Sandbox/shane/republish/zope/pipeline/apps/openroot.py
U Sandbox/shane/republish/zope/pipeline/apps/requestsetup.py
D Sandbox/shane/republish/zope/pipeline/apps/rootopen.py
U Sandbox/shane/republish/zope/pipeline/apps/switch.py
A Sandbox/shane/republish/zope/pipeline/apps/traversal.py
D Sandbox/shane/republish/zope/pipeline/apps/traverser.py
U Sandbox/shane/republish/zope/pipeline/configure.zcml
U Sandbox/shane/republish/zope/pipeline/entry.py
A Sandbox/shane/republish/zope/pipeline/interfaces.py
U Sandbox/shane/republish/zope/pipeline/zcml.py
-=-
Added: Sandbox/shane/republish/zope/pipeline/__init__.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/__init__.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/__init__.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -0,0 +1,18 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Configurable WSGI pipeline for publishing Zope applications.
+
+The zope.pipeline.entry module provides the functions for setting
+up a WSGI application built from a pipeline.
+"""
Copied: Sandbox/shane/republish/zope/pipeline/apps/authenticate.py (from rev 96459, Sandbox/shane/republish/zope/pipeline/apps/authenticator.py)
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/authenticate.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/apps/authenticate.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -0,0 +1,86 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+from zope.component import getGlobalSiteManager
+from zope.interface import adapts
+from zope.interface import implements
+from zope.publisher.interfaces import IWSGIApplication
+from zope.security.management import newInteraction
+from zope.security.management import endInteraction
+
+from zope.app.security.interfaces import IAuthentication
+from zope.app.security.interfaces import IFallbackUnauthenticatedPrincipal
+
+
+class Authenticator(object):
+ """WSGI app that hooks into Zope-based authentication.
+
+ The WSGI environment must contain 'zope.request'.
+ """
+ implements(IWSGIApplication)
+ adapts(IWSGIApplication, IRequest)
+
+ def __init__(self, app, marker_request=None):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ request = environ['zope.request']
+ auth = getGlobalSiteManager().getUtility(IAuthentication)
+ principal = auth.authenticate(request)
+ if principal is None:
+ request.traversal_hooks.append(placeful_auth)
+ principal = auth.unauthenticatedPrincipal()
+ if principal is None:
+ # Get the fallback unauthenticated principal
+ principal = getUtility(IFallbackUnauthenticatedPrincipal)
+ request.principal = principal
+
+ newInteraction(request)
+ try:
+ return self.app(environ, start_response)
+ 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"""
+
+ if not IUnauthenticatedPrincipal.providedBy(request.principal):
+ # We've already got an authenticated user. There's nothing to do.
+ # Note that beforeTraversal guarentees that user is not None.
+ return
+
+ if not ISite.providedBy(ob):
+ # We won't find an authentication utility here, so give up.
+ return
+
+ sm = removeSecurityProxy(ob).getSiteManager()
+
+ auth = sm.queryUtility(IAuthentication)
+ if auth is None:
+ # No auth utility here
+ return
+
+ # Try to authenticate against the auth utility
+ principal = auth.authenticate(request)
+ if principal is None:
+ principal = auth.unauthenticatedPrincipal()
+ if principal is None:
+ # nothing to do here
+ return
+
+ request.setPrincipal(principal)
Deleted: Sandbox/shane/republish/zope/pipeline/apps/authenticator.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/authenticator.py 2009-02-13 01:04:24 UTC (rev 96484)
+++ Sandbox/shane/republish/zope/pipeline/apps/authenticator.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -1,86 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2009 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-
-from zope.component import getGlobalSiteManager
-from zope.interface import adapts
-from zope.interface import implements
-from zope.publisher.interfaces import IWSGIApplication
-from zope.security.management import newInteraction
-from zope.security.management import endInteraction
-
-from zope.app.security.interfaces import IAuthentication
-from zope.app.security.interfaces import IFallbackUnauthenticatedPrincipal
-
-
-class Authenticator(object):
- """WSGI app that hooks into Zope-based authentication.
-
- The WSGI environment must contain 'zope.request'.
- """
- implements(IWSGIApplication)
- adapts(IWSGIApplication, IRequest)
-
- def __init__(self, app, marker_request=None):
- self.app = app
-
- def __call__(self, environ, start_response):
- request = environ['zope.request']
- auth = getGlobalSiteManager().getUtility(IAuthentication)
- principal = auth.authenticate(request)
- if principal is None:
- request.traversal_hooks.append(placeful_auth)
- principal = auth.unauthenticatedPrincipal()
- if principal is None:
- # Get the fallback unauthenticated principal
- principal = getUtility(IFallbackUnauthenticatedPrincipal)
- request.principal = principal
-
- newInteraction(request)
- try:
- return self.app(environ, start_response)
- 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"""
-
- if not IUnauthenticatedPrincipal.providedBy(request.principal):
- # We've already got an authenticated user. There's nothing to do.
- # Note that beforeTraversal guarentees that user is not None.
- return
-
- if not ISite.providedBy(ob):
- # We won't find an authentication utility here, so give up.
- return
-
- sm = removeSecurityProxy(ob).getSiteManager()
-
- auth = sm.queryUtility(IAuthentication)
- if auth is None:
- # No auth utility here
- return
-
- # Try to authenticate against the auth utility
- principal = auth.authenticate(request)
- if principal is None:
- principal = auth.unauthenticatedPrincipal()
- if principal is None:
- # nothing to do here
- return
-
- request.setPrincipal(principal)
Deleted: Sandbox/shane/republish/zope/pipeline/apps/caller.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/caller.py 2009-02-13 01:04:24 UTC (rev 96484)
+++ Sandbox/shane/republish/zope/pipeline/apps/caller.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -1,131 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2009 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-
-from zope.interface import implements
-from zope.proxy import removeAllProxies
-from zope.publisher.interfaces import IWSGIApplication
-
-
-class Caller(object):
- """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.
- """
- implements(IWSGIApplication)
-
- def __call__(self, environ, start_response):
- request = environ['zope.request']
- name, ob = request.traversed[-1]
- result = mapply(ob, request.positional_arguments, request)
- response = request.response
- if result is not response:
- response.setResult(result)
- 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):
- """obj -> (unwrapped, wrapperCount)
-
- Unwrap 'obj' until we get to a real function, counting the number of
- unwrappings.
-
- Bail if we find a class or something we can't identify as callable.
- """
- wrapperCount = 0
- unwrapped = obj
-
- for i in range(10):
- bases = getattr(unwrapped, '__bases__', None)
- if bases is not None:
- raise TypeError("mapply() can not call class constructors")
-
- im_func = getattr(unwrapped, 'im_func', None)
- if im_func is not None:
- unwrapped = im_func
- wrapperCount += 1
- elif getattr(unwrapped, 'func_code', None) is not None:
- break
- else:
- unwrapped = getattr(unwrapped, '__call__' , None)
- if unwrapped is None:
- raise TypeError("mapply() can not call %s" % repr(obj))
- else:
- raise TypeError("couldn't find callable metadata, mapply() error on %s"
- % repr(obj))
-
- return unwrapped, wrapperCount
-
-
-def mapply(obj, positional=(), request={}):
- __traceback_info__ = obj
-
- # we need deep access for introspection. Waaa.
- unwrapped = removeAllProxies(obj)
-
- unwrapped, wrapperCount = unwrapMethod(unwrapped)
-
- code = unwrapped.func_code
- defaults = unwrapped.func_defaults
- names = code.co_varnames[wrapperCount:code.co_argcount]
-
- nargs = len(names)
- if not positional:
- args = []
- else:
- args = list(positional)
- if len(args) > nargs:
- given = len(args)
- if wrapperCount:
- given += wrapperCount
- raise TypeError('%s() takes at most %d argument%s(%d given)' % (
- getattr(unwrapped, '__name__', repr(obj)),
- code.co_argcount,
- (code.co_argcount > 1 and 's ' or ' '),
- given))
-
- get = request.get
- nrequired = len(names)
- if defaults:
- nrequired -= len(defaults)
-
- for index in range(len(args), nargs):
- name = names[index]
- v = get(name, _marker)
- if v is _marker:
- if name == 'REQUEST':
- v = request
- elif index < nrequired:
- raise TypeError('Missing argument to %s(): %s' % (
- getattr(unwrapped, '__name__', repr(obj)), name))
- else:
- v = defaults[index - nrequired]
- args.append(v)
-
- args = tuple(args)
-
- if __debug__:
- return debug_call(obj, args)
-
- return obj(*args)
-
-def debug_call(obj, args):
- # The presence of this function allows us to set a pdb breakpoint
- return obj(*args)
Copied: Sandbox/shane/republish/zope/pipeline/apps/event.py (from rev 96459, Sandbox/shane/republish/zope/pipeline/apps/notify.py)
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/event.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/apps/event.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+from zope.event import notify
+from zope.interface import adapts
+from zope.interface import implements
+from zope.publisher.interfaces import IWSGIApplication
+from zope.publisher.interfaces.event import BeforeTraverseEvent
+from zope.publisher.interfaces.event import EndRequestEvent
+
+
+class EventNotifier(object):
+ """Fires request-related events.
+
+ Fires BeforeTraverseEvent and EndRequestEvent at the appropriate
+ times.
+ """
+ implements(IWSGIApplication)
+ adapts(IWSGIApplication)
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ request = environ['zope.request']
+ request.traversal_hooks.append(fireBeforeTraverse)
+ try:
+ return self.app(environ, start_response)
+ finally:
+ if request.traversed:
+ name, ob = request.traversed[-1]
+ else:
+ ob = None
+ notify(EndRequestEvent(ob, request))
+
+def fireBeforeTraverse(request, ob):
+ notify(BeforeTraverseEvent(ob, request))
Copied: Sandbox/shane/republish/zope/pipeline/apps/mapply.py (from rev 96459, Sandbox/shane/republish/zope/pipeline/apps/caller.py)
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/mapply.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/apps/mapply.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -0,0 +1,131 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+from zope.interface import implements
+from zope.proxy import removeAllProxies
+from zope.publisher.interfaces import IWSGIApplication
+
+
+class Caller(object):
+ """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.
+ """
+ implements(IWSGIApplication)
+
+ def __call__(self, environ, start_response):
+ request = environ['zope.request']
+ name, ob = request.traversed[-1]
+ result = mapply(ob, request.positional_arguments, request)
+ response = request.response
+ if result is not response:
+ response.setResult(result)
+ 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):
+ """obj -> (unwrapped, wrapperCount)
+
+ Unwrap 'obj' until we get to a real function, counting the number of
+ unwrappings.
+
+ Bail if we find a class or something we can't identify as callable.
+ """
+ wrapperCount = 0
+ unwrapped = obj
+
+ for i in range(10):
+ bases = getattr(unwrapped, '__bases__', None)
+ if bases is not None:
+ raise TypeError("mapply() can not call class constructors")
+
+ im_func = getattr(unwrapped, 'im_func', None)
+ if im_func is not None:
+ unwrapped = im_func
+ wrapperCount += 1
+ elif getattr(unwrapped, 'func_code', None) is not None:
+ break
+ else:
+ unwrapped = getattr(unwrapped, '__call__' , None)
+ if unwrapped is None:
+ raise TypeError("mapply() can not call %s" % repr(obj))
+ else:
+ raise TypeError("couldn't find callable metadata, mapply() error on %s"
+ % repr(obj))
+
+ return unwrapped, wrapperCount
+
+
+def mapply(obj, positional=(), request={}):
+ __traceback_info__ = obj
+
+ # we need deep access for introspection. Waaa.
+ unwrapped = removeAllProxies(obj)
+
+ unwrapped, wrapperCount = unwrapMethod(unwrapped)
+
+ code = unwrapped.func_code
+ defaults = unwrapped.func_defaults
+ names = code.co_varnames[wrapperCount:code.co_argcount]
+
+ nargs = len(names)
+ if not positional:
+ args = []
+ else:
+ args = list(positional)
+ if len(args) > nargs:
+ given = len(args)
+ if wrapperCount:
+ given += wrapperCount
+ raise TypeError('%s() takes at most %d argument%s(%d given)' % (
+ getattr(unwrapped, '__name__', repr(obj)),
+ code.co_argcount,
+ (code.co_argcount > 1 and 's ' or ' '),
+ given))
+
+ get = request.get
+ nrequired = len(names)
+ if defaults:
+ nrequired -= len(defaults)
+
+ for index in range(len(args), nargs):
+ name = names[index]
+ v = get(name, _marker)
+ if v is _marker:
+ if name == 'REQUEST':
+ v = request
+ elif index < nrequired:
+ raise TypeError('Missing argument to %s(): %s' % (
+ getattr(unwrapped, '__name__', repr(obj)), name))
+ else:
+ v = defaults[index - nrequired]
+ args.append(v)
+
+ args = tuple(args)
+
+ if __debug__:
+ return debug_call(obj, args)
+
+ return obj(*args)
+
+def debug_call(obj, args):
+ # The presence of this function allows us to set a pdb breakpoint
+ return obj(*args)
Deleted: Sandbox/shane/republish/zope/pipeline/apps/notify.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/notify.py 2009-02-13 01:04:24 UTC (rev 96484)
+++ Sandbox/shane/republish/zope/pipeline/apps/notify.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -1,48 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2009 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-
-from zope.event import notify
-from zope.interface import adapts
-from zope.interface import implements
-from zope.publisher.interfaces import IWSGIApplication
-from zope.publisher.interfaces.event import BeforeTraverseEvent
-from zope.publisher.interfaces.event import EndRequestEvent
-
-
-class EventNotifier(object):
- """Fires request-related events.
-
- Fires are BeforeTraverseEvent and EndRequestEvent at the appropriate
- times.
- """
- implements(IWSGIApplication)
- adapts(IWSGIApplication)
-
- def __init__(self, app):
- self.app = app
-
- def __call__(self, environ, start_response):
- request = environ['zope.request']
- request.traversal_hooks.append(fireBeforeTraverse)
- try:
- return self.app(environ, start_response)
- finally:
- if request.traversed:
- name, ob = request.traversed[-1]
- else:
- ob = None
- notify(EndRequestEvent(ob, request))
-
-def fireBeforeTraverse(request, ob):
- notify(BeforeTraverseEvent(ob, request))
Copied: Sandbox/shane/republish/zope/pipeline/apps/openroot.py (from rev 96459, Sandbox/shane/republish/zope/pipeline/apps/rootopen.py)
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/openroot.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/apps/openroot.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -0,0 +1,69 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+from zope.component import getUtility
+from zope.interface import adapts
+from zope.interface import implements
+from zope.publisher.interfaces import IWSGIApplication
+from zope.security.checker import ProxyFactory
+
+
+class RootOpener(object):
+ """Puts a root object in 'zope.request' of the WSGI environment.
+
+ Requires the environment to contain 'zope.database',
+ which is normally a ZODB.DB.DB object.
+ Sets request.traversed to a list with one element.
+ Also closes the database connection on the way out.
+
+ Special case: if the traversal stack contains "++etc++process",
+ instead of opening the database, this uses the utility by that
+ name as the root object.
+ """
+ implements(IWSGIApplication)
+ adapts(IWSGIApplication)
+
+ root_name = 'Application'
+ app_controller_name = '++etc++process'
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ request = environ['zope.request']
+
+ # If the traversal stack contains self.app_controller_name,
+ # then we should get the app controller rather than look
+ # in the database.
+ if self.app_controller_name in request.traversal_stack:
+ root = getUtility(name=self.app_controller_name)
+ request.traversed = [(self.app_controller_name, root)]
+ return self.app(environ, start_response)
+
+ # Open the database.
+ db = environ['zope.database']
+ conn = db.open()
+
+ request.annotations['ZODB.interfaces.IConnection'] = conn
+ root = conn.root()
+ app = root.get(self.root_name, None)
+ if app is None:
+ raise SystemError("Zope Application Not Found")
+
+ request.traversed = [(self.root_name, ProxyFactory(app))]
+
+ try:
+ return self.app(environ, start_response)
+ finally:
+ conn.close()
Modified: Sandbox/shane/republish/zope/pipeline/apps/requestsetup.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/requestsetup.py 2009-02-13 01:04:24 UTC (rev 96484)
+++ Sandbox/shane/republish/zope/pipeline/apps/requestsetup.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -16,6 +16,8 @@
__docformat__ = 'restructuredtext'
from zope.configuration.exceptions import ConfigurationError
+from zope.httpform import FormParser
+from zope.i18n.interfaces import IUserPreferredCharsets
from zope.interface import adapts
from zope.interface import implements
from zope.publisher.interfaces import IWSGIApplication
@@ -54,6 +56,9 @@
class ProcessForm(object):
"""WSGI middleware that processes HTML form data.
+ This step is separate from request creation so that the
+ error handling step can catch form data errors.
+
Requires the environment to contain a 'zope.request' that
is an IHTTPRequest, not just an IRequest.
"""
@@ -85,7 +90,6 @@
parser = FormParser(environ, to_unicode=to_unicode)
request.form = parser.parse()
-
if parser.action:
request.traversal_stack.insert(0, parser.action)
@@ -93,8 +97,7 @@
class RequestFactoryRegistry(object):
- """The registry implements a four stage lookup for registered factories
- that have to deal with requests::
+ """This registry implements a four stage lookup for request factories::
{scheme -> {method > { mimetype ->
[
@@ -141,7 +144,7 @@
'priorities. Please check your ZCML '
'configuration')
- def getFactoriesFor(self, scheme, method, mimetype):
+ def get_factories_for(self, scheme, method, mimetype):
if ';' in mimetype:
# `mimetype` might be something like 'text/xml; charset=utf8'. In
# this case we are only interested in the first part.
@@ -152,7 +155,6 @@
except KeyError:
return None
-
def make_request(self, scheme, method, mimetype, environment):
"""Get a factory given scheme+method+mimetype and an environment."""
@@ -162,7 +164,7 @@
(scheme, '*', '*')
]
for s, m, mt in variations:
- factory_lst = self.getFactoriesFor(s, m, mt)
+ factory_lst = self.get_factories_for(s, m, mt)
if factory_lst:
# Try to call each factory. If the factory can't or
# doesn't want to handle the given environment, it should
@@ -178,4 +180,3 @@
factoryRegistry = RequestFactoryRegistry()
cleanup.addCleanUp(lambda : factoryRegistry.__init__())
-
Deleted: Sandbox/shane/republish/zope/pipeline/apps/rootopen.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/rootopen.py 2009-02-13 01:04:24 UTC (rev 96484)
+++ Sandbox/shane/republish/zope/pipeline/apps/rootopen.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -1,68 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2009 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-
-from zope.component import getUtility
-from zope.interface import adapts
-from zope.interface import implements
-from zope.publisher.interfaces import IWSGIApplication
-from zope.security.checker import ProxyFactory
-
-
-class RootOpener(object):
- """Puts a root object in 'zope.request' of the WSGI environment.
-
- Sets request.traversed to a list with one element.
- Also closes the database connection on the way out.
-
- Special case: if the traversal stack contains "++etc++process",
- instead of opening the database, this uses the utility by that
- name as the root object.
- """
- implements(IWSGIApplication)
- adapts(IWSGIApplication, IZopeConfiguration)
-
- database_name = 'main'
- root_name = 'Application'
- app_controller_name = '++etc++process'
-
- def __init__(self, app, zope_conf):
- self.app = app
- self.db = zope_conf.databases[self.database_name]
-
- def __call__(self, environ, start_response):
- request = environ['zope.request']
-
- # If the traversal stack contains self.app_controller_name,
- # then we should get the app controller rather than look
- # in the database.
- if self.app_controller_name in request.traversal_stack:
- root = getUtility(name=self.app_controller_name)
- request.traversed = [(self.app_controller_name, root)]
- return self.app(environ, start_response)
-
- # Open the database.
- conn = self.db.open()
-
- request.annotations['ZODB.interfaces.IConnection'] = conn
- root = conn.root()
- app = root.get(self.root_name, None)
- if app is None:
- raise SystemError("Zope Application Not Found")
-
- request.traversed = [(self.root_name, ProxyFactory(app))]
-
- try:
- return self.app(environ, start_response)
- finally:
- conn.close()
Modified: Sandbox/shane/republish/zope/pipeline/apps/switch.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/switch.py 2009-02-13 01:04:24 UTC (rev 96484)
+++ Sandbox/shane/republish/zope/pipeline/apps/switch.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -27,7 +27,7 @@
def __call__(self, environ, start_response):
request = environ['zope.request']
- app = get_pipeline(request)
+ app = get_pipeline(request=request)
return app(environ, start_response)
def __repr__(self):
Copied: Sandbox/shane/republish/zope/pipeline/apps/traversal.py (from rev 96459, Sandbox/shane/republish/zope/pipeline/apps/traverser.py)
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/traversal.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/apps/traversal.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -0,0 +1,68 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+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
+
Deleted: Sandbox/shane/republish/zope/pipeline/apps/traverser.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/apps/traverser.py 2009-02-13 01:04:24 UTC (rev 96484)
+++ Sandbox/shane/republish/zope/pipeline/apps/traverser.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -1,68 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2009 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-
-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
-
Modified: Sandbox/shane/republish/zope/pipeline/configure.zcml
===================================================================
--- Sandbox/shane/republish/zope/pipeline/configure.zcml 2009-02-13 01:04:24 UTC (rev 96484)
+++ Sandbox/shane/republish/zope/pipeline/configure.zcml 2009-02-13 05:21:32 UTC (rev 96485)
@@ -39,8 +39,8 @@
for=".interfaces.INoRequest" />
<wsgi:middleware
- factory="..."
- name="request_setup"
+ factory=".apps.requestsetup.CreateRequest"
+ name="create_request"
for=".interfaces.INoRequest" />
<!-- a wsgi:application directive registers an application for use
@@ -59,39 +59,51 @@
<wsgi:middleware
factory="..."
- name="logger" />
+ name="log" />
<wsgi:middleware
- factory=".apps.rootopen.RootOpener"
- name="root_opener" />
+ factory=".apps.openroot.RootOpener"
+ name="open_root" />
<wsgi:middleware
factory=".apps.txnctl.TransactionController"
- name="transaction_controller" />
+ name="control_transaction" />
<wsgi:middleware
factory=".apps.notify.EventNotifier"
- name="notifier" />
+ name="event" />
<wsgi:middleware
factory="..."
- name="error_handler" />
+ name="handle_error" />
+<!-- no form processing for non-browser requests -->
<wsgi:middleware
- factory=".apps.authenticator.Authenticator"
- name="authenticator" />
+ factory=".apps.passthrough"
+ name="process_form"
+ for="zope.publisher.interfaces.IRequest" />
+<!-- process forms for browser requests -->
<wsgi:middleware
- factory="..."
- name="traverser" />
+ factory=".apps.requestsetup.ProcessForm"
+ name="process_form"
+ for="zope.publisher.interfaces.browser.IBrowserRequest" />
<wsgi:middleware
+ factory=".apps.authenticate.Authenticator"
+ name="authenticate" />
+
+<wsgi:middleware
+ factory=".apps.traversal.Traverser"
+ name="traverse" />
+
+<wsgi:middleware
factory=".apps.txnctl.TransactionAnnotator"
- name="transaction_annotator" />
+ name="annotate_transaction" />
<wsgi:application
- factory=".apps.caller.Caller"
- name="caller" />
+ factory=".apps.mapply.Caller"
+ name="call" />
</configure>
Modified: Sandbox/shane/republish/zope/pipeline/entry.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/entry.py 2009-02-13 01:04:24 UTC (rev 96484)
+++ Sandbox/shane/republish/zope/pipeline/entry.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -13,24 +13,40 @@
##############################################################################
"""The main entry point for the zope.pipeline package.
-Use get_pipeline() to get a WSGI application built from a Zope pipeline.
+Use get_database_pipeline() or get_pipeline() to get a WSGI
+application built from a pipeline.
"""
+from zope.component import getMultiAdapter
+from zope.interface import directlyProvides
+from zope.interface import providedBy
+from zope.pipeline.interfaces import IPipelineApplicationList
+from zope.pipeline.interfaces import IUndecidedRequest
+from zope.publisher import IWSGIApplication
+from zope.testing import cleanup
+
# _pipeline_cache: {(interfaces provided by the request) -> WSGI application}
_pipeline_cache = {}
+cleanup.addCleanUp(_pipeline_cache.clear)
-def get_pipeline(request=None):
+def get_database_pipeline(database, global_environ=None):
+ if global_environ is None:
+ global_environ = {}
+ global_environ['zope.database'] = database
+ return get_pipeline(global_environ=global_environ)
+
+def get_pipeline(request=None, global_environ=None):
if request is None:
provided = (IUndecidedRequest,)
else:
provided = tuple(providedBy(request))
pipeline = _pipeline_cache.get(provided)
if pipeline is None:
- pipeline = make_pipeline(provided)
+ pipeline = make_pipeline(provided, global_environ)
_pipeline_cache[provided] = pipeline
return pipeline
-def make_pipeline(provided):
+def make_pipeline(provided, global_environ=None):
marker_req = MarkerRequest(provided)
app_list = IPipelineApplicationList(marker_req)
names = list(app_list.names) # make a copy
@@ -42,7 +58,15 @@
name = names.pop()
app = getMultiAdapter(
(app, marker_req), IWSGIApplication, name=name)
- return app
+ if global_environ:
+ # augment the WSGI environment with some data
+ def add_global_environ(environ, start_response):
+ environ.update(global_environ)
+ return inner_app(environ, start_response)
+ directlyProvides(add_global_environ, IWSGIApplication)
+ return add_global_environ
+ else:
+ return app
class MarkerRequest(object):
"""A marker object that claims to provide a request type.
Added: Sandbox/shane/republish/zope/pipeline/interfaces.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/interfaces.py (rev 0)
+++ Sandbox/shane/republish/zope/pipeline/interfaces.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -0,0 +1,61 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Interfaces specific to zope.pipeline.
+
+$Id: metadirectives.py 96177 2009-02-06 07:50:13Z shane $
+"""
+
+from zope.interface import Attribute
+from zope.interface import Interface
+
+class IUndecidedRequest(Interface):
+ """Indicates that no request has been created yet.
+
+ Use this interface to register WSGI applications in the
+ pipeline that executes before the request creation step.
+ """
+
+class IPipelineApplicationList(Interface):
+ """Container of a list of pipeline application names.
+
+ The wsgi:pipeline directive creates an object that
+ provides this interface and registers it as an adapter.
+ """
+ names = Attribute("Application names to use in a pipeline")
+
+class IRequestFactoryRegistry(Interface):
+ """A registry of request factories.
+
+ Chooses factories based on the wsgi.url_scheme, the
+ REQUEST_METHOD, and the CONTENT_TYPE. Multiple factories
+ can be configured schema, method, and content type.
+ The factory itself can introspect the environment to decide
+ if it can handle the request as given by the environment or not.
+ Factories are sorted in descending order of priority, so a
+ factory with priority 10 will be used before a factory with
+ priority 5.
+ """
+
+ def register(scheme, method, mimetype, name, priority, factory):
+ """Registers a factory for scheme + method + mimetype."""
+
+ def get_factories_for(scheme, method, mimetype):
+ """Return the internal datastructure representing the configured
+ factories (basically for testing, not for introspection).
+ """
+
+ def lookup(method, mimetype, environment):
+ """Lookup a factory for a given method+mimetype and a
+ environment.
+ """
Modified: Sandbox/shane/republish/zope/pipeline/zcml.py
===================================================================
--- Sandbox/shane/republish/zope/pipeline/zcml.py 2009-02-13 01:04:24 UTC (rev 96484)
+++ Sandbox/shane/republish/zope/pipeline/zcml.py 2009-02-13 05:21:32 UTC (rev 96485)
@@ -30,15 +30,17 @@
from zope.schema import Int
from zope.schema import TextLine
+from zope.pipeline.interfaces import IPipelineApplicationList
from zope.pipeline.apps.requestsetup import factoryRegistry
class IPipelineDirective(Interface):
"""Declare a list of application names in a WSGI pipeline"""
- for_ = GlobalObject(
- title=u'Request type',
- description=u'The type of request that should use this app list',
+ for_ = Tokens(
+ title=u'Request types',
+ description=u'The request types that should use this pipeline',
+ value_type=GlobalObject(),
required=True)
names = Tokens(
@@ -54,7 +56,8 @@
factory = GlobalObject(
title=u"Application factory",
- description=u"A factory that creates the WSGI application.",
+ description=(u"A factory that creates the WSGI application. "
+ u"The factory will be called with no parameters."),
required=True,
)
@@ -64,23 +67,24 @@
required=True,
)
- for_ = GlobalObject(
- title=u"Request type",
- description=u"The request type that triggers use of this application",
- required=False,
- )
+ for_ = Tokens(
+ title=u'Request types',
+ description=u'The request types that should use this application',
+ value_type=GlobalObject(),
+ required=False)
class IMiddlewareDirective(IApplicationDirective):
"""Declare a middleware WSGI application for use in a pipeline"""
- # same schema as IApplicationDirective
- pass
+ factory = GlobalObject(
+ title=u"Application factory",
+ description=(u"A factory that creates the WSGI application. "
+ u"The factory will be called with one parameter: "
+ u"the next application in the pipeline."),
+ required=True,
+ )
-class IPipelineApplicationList(Interface):
- names = Attribute("Application names to use in a pipeline")
-
-
class PipelineApplicationList(object):
implements(IPipelineApplicationList)
@@ -96,12 +100,12 @@
"""Register a pipeline application list"""
obj = PipelineApplicationList(names)
adapter(_context, factory=obj.adapt,
- provides=[IPipelineApplicationList], for_=[for_])
+ provides=[IPipelineApplicationList], for_=for_)
-def application(_context, factory, name, for_=None):
+def application(_context, factory, name, for_=()):
"""Register a simple WSGI app for use at the end of pipeline"""
- if for_ is None:
- for_ = getattr(factory, 'request_type', IRequest)
+ if not for_:
+ for_ = [getattr(factory, 'request_type', IRequest)]
def app_factory(marker_request):
res = factory()
@@ -110,12 +114,13 @@
return res
adapter(_context, factory=[app_factory], provides=[IWSGIApplication],
- for_=[for_], name=name)
+ for_=for_, name=name)
-def middleware(_context, factory, name, for_=None):
+def middleware(_context, factory, name, for_=()):
"""Register a middleware WSGI app for use in a pipeline"""
- if for_ is None:
- for_ = getattr(factory, 'request_type', IRequest)
+ if not for_:
+ for_ = [getattr(factory, 'request_type', IRequest)]
+ for_ = [IWSGIApplication] + list(for_)
def app_factory(app, marker_request):
res = factory(app)
@@ -124,7 +129,7 @@
return res
adapter(_context, factory=[app_factory], provides=[IWSGIApplication],
- for_=[IWSGIApplication, for_], name=name)
+ for_=for_, name=name)
class IRequestFactoryDirective(Interface):
@@ -139,8 +144,8 @@
description=(u'The request factory, which is a callable '
u'that accepts one parameter, the WSGI environment, '
u'and returns a request object. '
- u'The factory can return None to defer to the '
- u'next registered factory.'),
+ u'The factory can return None if it decides it can '
+ u'not handle the given environment.'),
required=True)
schemes = Tokens(
@@ -167,8 +172,9 @@
priority = Int(
title=u'Priority',
- description=(u'A priority key used to decide between coexistent'
- ' request factories.'),
+ description=(u'A priority number used to decide between coexistent '
+ u'request factories. A higher priority number '
+ u'is chosen before a lower priority number.'),
required=False)
def request_factory(_context, name, factory,
More information about the Checkins
mailing list