[Checkins] SVN: Zope/trunk/ merged haufe-legacy-integration branch
Andreas Jung
andreas at andreas-jung.com
Tue May 12 04:49:29 EDT 2009
Log message for revision 99866:
merged haufe-legacy-integration branch
Changed:
U Zope/trunk/doc/CHANGES.rst
U Zope/trunk/src/App/ZApplication.py
U Zope/trunk/src/Products/ZODBMountPoint/MountedObject.py
U Zope/trunk/src/Products/ZODBMountPoint/tests/testMountPoint.py
U Zope/trunk/src/ZPublisher/HTTPRequest.py
U Zope/trunk/src/ZPublisher/HTTPResponse.py
U Zope/trunk/src/ZPublisher/Publish.py
A Zope/trunk/src/ZPublisher/interfaces.py
A Zope/trunk/src/ZPublisher/pubevents.py
A Zope/trunk/src/ZPublisher/tests/testpubevents.py
U Zope/trunk/src/ZServer/PubCore/ZServerPublisher.py
U Zope/trunk/src/Zope2/Startup/__init__.py
U Zope/trunk/src/Zope2/Startup/handlers.py
U Zope/trunk/src/Zope2/Startup/tests/test_schema.py
U Zope/trunk/src/Zope2/Startup/zopeschema.xml
-=-
Modified: Zope/trunk/doc/CHANGES.rst
===================================================================
--- Zope/trunk/doc/CHANGES.rst 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/doc/CHANGES.rst 2009-05-12 08:49:29 UTC (rev 99866)
@@ -14,9 +14,32 @@
- zExceptions.convertExceptionType: new API, breaking out conversion of
exception names to exception types from 'upgradeException'.
+- Launchpad #375322: the <environment> section within the zope.conf
+ file is now a multisection in order to provide a more modular configuration
+ support.
+
+- Launchpad #374719: introducing new ZPublisher events:
+ PubStart, PubSuccess, PubFailure, PubAfterTraversal and PubBeforeCommit.
+
Bugs Fixed
++++++++++
+- Launchpad #374729: Encoding cookie values to avoid issues with
+ firewalls and security proxies.
+
+- Launchpad #373583: ZODBMountPoint - fixed broken mount support and
+ extended the test suite.
+
+- Launchpad #373621: catching and logging exceptions that could cause
+ leaking of worker threads.
+
+- Launchpad #373577: setting up standard logging earlier within the startup
+ phase for improving the analysis of startup errors.
+
+- Launchpad #373601: abort transaction before connection close in order to
+ prevent connection leaks in case of persistent changes after the main
+ transaction is closed.
+
- Fix BBB regression which prevented setting browser ID cookies from
browser ID managers created before the 'HTTPOnly' feature landed.
https://bugs.launchpad.net/bugs/374816
@@ -50,11 +73,13 @@
Features Added
++++++++++++++
+- zExceptions.convertExceptionType: new API, breaking out conversion of
+ exception names to exception types from 'upgradeException'.
- Extended BrowserIdManager to expose the 'HTTPOnly' attribute for its
cookie. Also via https://bugs.launchpad.net/zope2/+bug/367393 .
-- Addeed support for an optional 'HTTPOnly' attribute of cookies (see
+- Added support for an optional 'HTTPOnly' attribute of cookies (see
http://www.owasp.org/index.php/HTTPOnly). Patch from Stephan Hofmockel,
via https://bugs.launchpad.net/zope2/+bug/367393 .
Modified: Zope/trunk/src/App/ZApplication.py
===================================================================
--- Zope/trunk/src/App/ZApplication.py 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/App/ZApplication.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -80,4 +80,5 @@
self._jar = jar
def __del__(self):
+ transaction.abort()
self._jar.close()
Modified: Zope/trunk/src/Products/ZODBMountPoint/MountedObject.py
===================================================================
--- Zope/trunk/src/Products/ZODBMountPoint/MountedObject.py 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/Products/ZODBMountPoint/MountedObject.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -59,9 +59,7 @@
factory = guarded_getattr(dispatcher, 'manage_addFolder')
factory(id)
o = context.restrictedTraverse(id)
- # Commit a subtransaction to assign the new object to
- # the correct database.
- transaction.savepoint(optimistic=True)
+ context._p_jar.add(o.aq_base)
return o
def traverseOrConstruct(self, path, omit_final=0):
Modified: Zope/trunk/src/Products/ZODBMountPoint/tests/testMountPoint.py
===================================================================
--- Zope/trunk/src/Products/ZODBMountPoint/tests/testMountPoint.py 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/Products/ZODBMountPoint/tests/testMountPoint.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -83,6 +83,7 @@
databases = [TestDBConfig('test_main.fs', ['/']).getDB(),
TestDBConfig('test_mount1.fs', ['/mount1']).getDB(),
TestDBConfig('test_mount2.fs', ['/mount2']).getDB(),
+ TestDBConfig('test_mount3.fs', ['/i/mount3']).getDB(),
]
mount_points = {}
mount_factories = {}
@@ -102,13 +103,21 @@
root = conn.root()
root['Application'] = app = Application()
self.app = app
+ install_products(app, 'ZCatalog', 'PluginIndexes', 'OFSP')
+ # login
+ from AccessControl.User import system
+ from AccessControl.SecurityManagement import newSecurityManager
+ newSecurityManager(None, system)
transaction.commit() # Get app._p_jar set
- manage_addMounts(app, ('/mount1', '/mount2'))
+ manage_addMounts(app, ('/mount1', '/mount2', '/i/mount3'))
transaction.commit() # Get the mount points ready
def tearDown(self):
+ # logout
+ from AccessControl.SecurityManagement import noSecurityManager
+ noSecurityManager()
App.config.setConfiguration(original_config)
transaction.abort()
self.app._p_jar.close()
@@ -120,6 +129,7 @@
def testRead(self):
self.assertEqual(self.app.mount1.id, 'mount1')
self.assertEqual(self.app.mount2.id, 'mount2')
+ self.assertEqual(self.app.i.mount3.id, 'mount3')
def testWrite(self):
app = self.app
@@ -144,6 +154,7 @@
self.assertEqual(getMountPoint(self.app.mount1)._path, '/mount1')
self.assert_(getMountPoint(self.app.mount2) is not None)
self.assertEqual(getMountPoint(self.app.mount2)._path, '/mount2')
+ self.assertEqual(getMountPoint(self.app.i.mount3)._path, '/i/mount3')
del self.app.mount2
self.app.mount2 = Folder()
self.app.mount2.id = 'mount2'
@@ -160,8 +171,13 @@
{'status': 'Ok',
'path': '/mount2',
'name': 'test_mount2.fs',
- 'exists': 1}]
- self.assertEqual(expected, status)
+ 'exists': 1},
+ {'status': 'Ok',
+ 'path': '/i/mount3',
+ 'name': 'test_mount3.fs',
+ 'exists': 1},
+ ]
+ self.assertEqual(sorted(expected), sorted(status))
del self.app.mount2
status = manage_getMountStatus(self.app)
expected = [{'status': 'Ok',
@@ -171,8 +187,14 @@
{'status': 'Ready to create',
'path': '/mount2',
'name': 'test_mount2.fs',
- 'exists': 0}]
- self.assertEqual(expected, status)
+ 'exists': 0},
+ {'status': 'Ok',
+ 'path': '/i/mount3',
+ 'name': 'test_mount3.fs',
+ 'exists': 1},
+
+ ]
+ self.assertEqual(sorted(expected), sorted(status))
self.app.mount2 = Folder('mount2')
status = manage_getMountStatus(self.app)
expected = [{'status': 'Ok',
@@ -182,8 +204,13 @@
{'status': '** Something is in the way **',
'path': '/mount2',
'name': 'test_mount2.fs',
- 'exists': 1}]
- self.assertEqual(expected, status)
+ 'exists': 1},
+ {'status': 'Ok',
+ 'path': '/i/mount3',
+ 'name': 'test_mount3.fs',
+ 'exists': 1},
+ ]
+ self.assertEqual(sorted(expected), sorted(status))
def test_close(self):
app = self.app
@@ -192,6 +219,7 @@
app.a3 = '3'
conn1 = app.mount1._p_jar
conn2 = app.mount2._p_jar
+ conn3 = app.i.mount3._p_jar
transaction.abort()
# Close the main connection
app._p_jar.close()
@@ -199,8 +227,24 @@
# Check that secondary connections have been closed too
self.assertEqual(conn1.opened, None)
self.assertEqual(conn2.opened, None)
+ self.assertEqual(conn3.opened, None)
+def install_products(app, *prod):
+ """auxiliary function to install products *prod* (by names)."""
+ from OFS.Application import get_folder_permissions, get_products, install_product
+ folder_permissions = get_folder_permissions()
+ meta_types=[]
+ done={}
+ products = get_products()
+ for priority, product_name, index, product_dir in products:
+ if product_name not in prod or product_name in done: continue
+ done[product_name]=1
+ install_product(app, product_dir, product_name, meta_types,
+ folder_permissions, raise_exc=True)
+
+
+
def test_suite():
return unittest.makeSuite(MountingTests, 'test')
Modified: Zope/trunk/src/ZPublisher/HTTPRequest.py
===================================================================
--- Zope/trunk/src/ZPublisher/HTTPRequest.py 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/ZPublisher/HTTPRequest.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -1684,7 +1684,7 @@
release()
if not already_have(name):
- result[name] = value
+ result[name] = unquote(value)
return apply(parse_cookie,(text[l:],result))
Modified: Zope/trunk/src/ZPublisher/HTTPResponse.py
===================================================================
--- Zope/trunk/src/ZPublisher/HTTPResponse.py 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/ZPublisher/HTTPResponse.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -23,6 +23,7 @@
from zExceptions.ExceptionFormatter import format_exception
from ZPublisher import BadRequest, InternalError, NotFound
from cgi import escape
+from urllib import quote
nl2sp = maketrans('\n',' ')
@@ -842,7 +843,7 @@
# quoted cookie attr values, so only the value part
# of name=value pairs may be quoted.
- cookie = 'Set-Cookie: %s="%s"' % (name, attrs['value'])
+ cookie = 'Set-Cookie: %s="%s"' % (name, quote(attrs['value']))
for name, v in attrs.items():
name = name.lower()
if name == 'expires':
Modified: Zope/trunk/src/ZPublisher/Publish.py
===================================================================
--- Zope/trunk/src/ZPublisher/Publish.py 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/ZPublisher/Publish.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -24,7 +24,11 @@
from zope.publisher.interfaces import ISkinnable
from zope.publisher.skinnable import setDefaultSkin
from zope.security.management import newInteraction, endInteraction
+from zope.event import notify
+from pubevents import PubStart, PubSuccess, PubFailure, \
+ PubBeforeCommit, PubAfterTraversal
+
class Retry(Exception):
"""Raise this to retry a request
"""
@@ -76,6 +80,7 @@
response=None
try:
+ notify(PubStart(request))
# TODO pass request here once BaseRequest implements IParticipation
newInteraction()
@@ -110,6 +115,8 @@
object=request.traverse(path, validated_hook=validated_hook)
+ notify(PubAfterTraversal(request))
+
if transactions_manager:
transactions_manager.recordMetaData(object, request)
@@ -122,12 +129,18 @@
if result is not response:
response.setBody(result)
+ notify(PubBeforeCommit(request))
+
if transactions_manager:
transactions_manager.commit()
endInteraction()
+ notify(PubSuccess(request))
+
return response
except:
+ # save in order to give 'PubFailure' the original exception info
+ exc_info = sys.exc_info()
# DM: provide nicer error message for FTP
sm = None
if response is not None:
@@ -141,6 +154,7 @@
debug_mode and compact_traceback()[-1] or ''))
if err_hook is not None:
+ retry = False
if parents:
parents=parents[0]
try:
@@ -157,10 +171,15 @@
sys.exc_info()[1],
sys.exc_info()[2],
)
+ retry = True
finally:
- if transactions_manager:
- transactions_manager.abort()
- endInteraction()
+ # Note: 'abort's can fail. Nevertheless, we want end request handling
+ try:
+ if transactions_manager:
+ transactions_manager.abort()
+ finally:
+ endInteraction()
+ notify(PubFailure(request, exc_info, retry))
# Only reachable if Retry is raised and request supports retry.
newrequest=request.retry()
@@ -175,9 +194,13 @@
newrequest.close()
else:
- if transactions_manager:
- transactions_manager.abort()
- endInteraction()
+ # Note: 'abort's can fail. Nevertheless, we want end request handling
+ try:
+ if transactions_manager:
+ transactions_manager.abort()
+ finally:
+ endInteraction()
+ notify(PubFailure(request, exc_info, False))
raise
Copied: Zope/trunk/src/ZPublisher/interfaces.py (from rev 99865, Zope/branches/haufe-legacy-integration/src/ZPublisher/interfaces.py)
===================================================================
--- Zope/trunk/src/ZPublisher/interfaces.py (rev 0)
+++ Zope/trunk/src/ZPublisher/interfaces.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -0,0 +1,45 @@
+from zope.interface import Interface, Attribute
+
+#############################################################################
+# Publication events
+# These are events notified in 'ZPublisher.Publish.publish'.
+
+class IPubEvent(Interface):
+ '''Base class for publication events.
+
+ Publication events are notified in 'ZPublisher.Publish.publish' to
+ inform about publications (aka requests) and their fate.
+ '''
+ request = Attribute('The request being affected')
+
+class IPubStart(IPubEvent):
+ '''Event notified at the beginning of 'ZPublisher.Publish.publish'.'''
+
+class IPubEnd(IPubEvent):
+ '''Event notified after request processing.
+
+ Note that a retried request ends before the retrieal, the retrial
+ itself is considered a new event.
+ '''
+
+class IPubSuccess(IPubEnd):
+ '''A successful request processing.'''
+
+class IPubFailure(IPubEnd):
+ '''A failed request processing.
+
+ Note: If a subscriber to 'IPubSuccess' raises an exception,
+ then 'IPubFailure' may be notified in addtion to 'IPubSuccess'.
+ '''
+ exc_info = Attribute('''The exception info as returned by 'sys.exc_info()'.''')
+ retry = Attribute('Whether the request will be retried')
+
+
+class IPubAfterTraversal(IPubEvent):
+ """notified after traversal and an (optional) authentication."""
+
+
+class IPubBeforeCommit(IPubEvent):
+ """notified immediately before the transaction commit (i.e. after the main
+ request processing is finished.
+ """
Copied: Zope/trunk/src/ZPublisher/pubevents.py (from rev 99865, Zope/branches/haufe-legacy-integration/src/ZPublisher/pubevents.py)
===================================================================
--- Zope/trunk/src/ZPublisher/pubevents.py (rev 0)
+++ Zope/trunk/src/ZPublisher/pubevents.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -0,0 +1,44 @@
+'''Publication events.
+
+They are notified in 'ZPublisher.Publish.publish' and
+inform about publications and their fate.
+
+Subscriptions can be used for all kinds of request supervision,
+e.g. request and error rate determination, writing high resolution logfiles
+for detailed time related analysis, inline request monitoring.
+'''
+from zope.interface import implements
+
+from interfaces import IPubStart, IPubSuccess, IPubFailure, \
+ IPubAfterTraversal, IPubBeforeCommit
+
+class _Base(object):
+ """PubEvent base class."""
+
+ def __init__(self, request):
+ self.request = request
+
+class PubStart(_Base):
+ '''notified at the beginning of 'ZPublisher.Publish.publish'.'''
+ implements(IPubStart)
+
+class PubSuccess(_Base):
+ '''notified at successful request end.'''
+ implements(IPubSuccess)
+
+class PubFailure(object):
+ '''notified at failed request end.'''
+ implements(IPubFailure)
+
+ def __init__(self, request, exc_info, retry):
+ self.request, self.exc_info, self.retry = request, exc_info, retry
+
+
+class PubAfterTraversal(_Base):
+ """notified after traversal and an (optional) authentication."""
+ implements(IPubAfterTraversal)
+
+
+class PubBeforeCommit(_Base):
+ """notified immediately before the commit."""
+ implements(IPubBeforeCommit)
Copied: Zope/trunk/src/ZPublisher/tests/testpubevents.py (from rev 99865, Zope/branches/haufe-legacy-integration/src/ZPublisher/tests/testpubevents.py)
===================================================================
--- Zope/trunk/src/ZPublisher/tests/testpubevents.py (rev 0)
+++ Zope/trunk/src/ZPublisher/tests/testpubevents.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -0,0 +1,166 @@
+from sys import modules, exc_info
+from unittest import TestCase, TestSuite, makeSuite, main
+
+from ZODB.POSException import ConflictError
+from zope.interface.verify import verifyObject
+from zope.event import subscribers
+
+from ZPublisher.Publish import publish, Retry
+from ZPublisher.BaseRequest import BaseRequest
+from ZPublisher.pubevents import PubStart, PubSuccess, PubFailure, \
+ PubAfterTraversal, PubBeforeCommit
+from ZPublisher.interfaces import \
+ IPubStart, IPubEnd, IPubSuccess, IPubFailure, \
+ IPubAfterTraversal, IPubBeforeCommit
+
+PUBMODULE = 'TEST_testpubevents'
+
+_g=globals()
+
+class TestInterface(TestCase):
+ def testPubStart(self):
+ verifyObject(IPubStart, PubStart(_Request()))
+
+ def testPubSuccess(self):
+ e = PubSuccess(_Request())
+ verifyObject(IPubSuccess, e)
+ verifyObject(IPubEnd, e)
+
+ def testPubFailure(self):
+ # get some exc info
+ try: raise ValueError()
+ except: exc = exc_info()
+ e = PubFailure(_Request(), exc, False)
+ verifyObject(IPubFailure, e)
+ verifyObject(IPubEnd, e)
+
+ def testAfterTraversal(self):
+ e = PubAfterTraversal(_Request())
+ verifyObject(IPubAfterTraversal, e)
+
+ def testBeforeCommit(self):
+ e = PubBeforeCommit(_Request())
+ verifyObject(IPubBeforeCommit, e)
+
+
+class TestPubEvents(TestCase):
+ def setUp(self):
+ self._saved_subscribers = subscribers[:]
+ self.reporter = r = _Reporter()
+ subscribers[:] = [r]
+ modules[PUBMODULE] = __import__(__name__, _g, _g, ('__doc__', ))
+ self.request = _Request()
+
+ def tearDown(self):
+ if PUBMODULE in modules: del modules[PUBMODULE]
+ subscribers[:] = self._saved_subscribers
+
+ def testSuccess(self):
+ r = self.request; r.action = 'succeed'
+ publish(r, PUBMODULE, [None])
+ events = self.reporter.events
+ self.assertEqual(len(events), 4)
+ self.assert_(isinstance(events[0], PubStart))
+ self.assertEqual(events[0].request, r)
+ self.assert_(isinstance(events[-1], PubSuccess))
+ self.assertEqual(events[-1].request, r)
+ # test AfterTraversal and BeforeCommit as well
+ self.assert_(isinstance(events[1], PubAfterTraversal))
+ self.assertEqual(events[1].request, r)
+ self.assert_(isinstance(events[2], PubBeforeCommit))
+ self.assertEqual(events[2].request, r)
+
+ def testFailureReturn(self):
+ r = self.request; r.action = 'fail_return'
+ publish(r, PUBMODULE, [None])
+ events = self.reporter.events
+ self.assertEqual(len(events), 2)
+ self.assert_(isinstance(events[0], PubStart))
+ self.assertEqual(events[0].request, r)
+ self.assert_(isinstance(events[1], PubFailure))
+ self.assertEqual(events[1].request, r)
+ self.assertEqual(events[1].retry, False)
+ self.assertEqual(len(events[1].exc_info), 3)
+
+ def testFailureException(self):
+ r = self.request; r.action = 'fail_exception'
+ self.assertRaises(Exception, publish, r, PUBMODULE, [None])
+ events = self.reporter.events
+ self.assertEqual(len(events), 2)
+ self.assert_(isinstance(events[0], PubStart))
+ self.assertEqual(events[0].request, r)
+ self.assert_(isinstance(events[1], PubFailure))
+ self.assertEqual(events[1].request, r)
+ self.assertEqual(events[1].retry, False)
+ self.assertEqual(len(events[1].exc_info), 3)
+
+ def testFailureConflict(self):
+ r = self.request; r.action = 'conflict'
+ publish(r, PUBMODULE, [None])
+ events = self.reporter.events
+ self.assertEqual(len(events), 6)
+ self.assert_(isinstance(events[0], PubStart))
+ self.assertEqual(events[0].request, r)
+ self.assert_(isinstance(events[1], PubFailure))
+ self.assertEqual(events[1].request, r)
+ self.assertEqual(events[1].retry, True)
+ self.assertEqual(len(events[1].exc_info), 3)
+ self.assert_(isinstance(events[1].exc_info[1], ConflictError))
+ self.assert_(isinstance(events[2], PubStart))
+ self.assert_(isinstance(events[5], PubSuccess))
+
+# Auxiliaries
+def _succeed():
+ ''' '''
+ return 'success'
+
+class _Application(object): pass
+
+class _Reporter(object):
+ def __init__(self): self.events = []
+ def __call__(self, event): self.events.append(event)
+
+class _Response(object):
+ def setBody(*unused): pass
+
+
+class _Request(BaseRequest):
+ response = _Response()
+ _hacked_path = False
+ args = ()
+
+ def __init__(self, *args, **kw):
+ BaseRequest.__init__(self, *args, **kw)
+ self['PATH_INFO'] = self['URL'] = ''
+ self.steps = []
+
+ def supports_retry(self): return True
+ def retry(self):
+ r = self.__class__()
+ r.action = 'succeed'
+ return r
+
+ def traverse(self, *unused, **unused_kw):
+ action = self.action
+ if action.startswith('fail'): raise Exception(action)
+ if action == 'conflict': raise ConflictError()
+ if action == 'succeed': return _succeed
+ else: raise ValueError('unknown action: %s' % action)
+
+ # override to get rid of the 'EndRequestEvent' notification
+ def close(self): pass
+
+# define things necessary for publication
+bobo_application = _Application()
+def zpublisher_exception_hook(parent, request, *unused):
+ action = request.action
+ if action == 'fail_return': return 0
+ if action == 'fail_exception': raise Exception()
+ if action == 'conflict': raise Retry()
+ raise ValueError('unknown action: %s' % action)
+
+def test_suite():
+ return TestSuite((makeSuite(c) for c in (TestPubEvents, TestInterface)))
+
+
+
Modified: Zope/trunk/src/ZServer/PubCore/ZServerPublisher.py
===================================================================
--- Zope/trunk/src/ZServer/PubCore/ZServerPublisher.py 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/ZServer/PubCore/ZServerPublisher.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -11,11 +11,17 @@
#
##############################################################################
+import logging
+
+LOG = logging.getLogger('ZServerPublisher')
+
class ZServerPublisher:
def __init__(self, accept):
+ from sys import exc_info
from ZPublisher import publish_module
from ZPublisher.WSGIPublisher import publish_module as publish_wsgi
while 1:
+ try:
name, a, b=accept()
if name == "Zope2":
try:
@@ -36,3 +42,5 @@
# TODO: Support keeping connections open.
a['wsgi.output']._close = 1
a['wsgi.output'].close()
+ except:
+ LOG.error('exception caught', exc_info=True)
Modified: Zope/trunk/src/Zope2/Startup/__init__.py
===================================================================
--- Zope/trunk/src/Zope2/Startup/__init__.py 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/Zope2/Startup/__init__.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -86,6 +86,7 @@
self.setupServers()
# drop privileges after setting up servers
self.dropPrivileges()
+ self.setupFinalLogging()
self.makeLockFile()
self.makePidFile()
self.setupInterpreter()
@@ -100,7 +101,6 @@
# after it has emitted ZServer messages.
logger.info('Ready to handle requests')
- self.setupFinalLogging()
self.sendEvents()
def run(self):
Modified: Zope/trunk/src/Zope2/Startup/handlers.py
===================================================================
--- Zope/trunk/src/Zope2/Startup/handlers.py 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/Zope2/Startup/handlers.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -184,8 +184,10 @@
"""
# Set environment variables
- for k,v in config.environment.items():
- os.environ[k] = v
+ d = {}
+ for s in config.environment:
+ d.update(s)
+ os.environ.update(d)
# Add directories to the pythonpath
instancelib = os.path.join(config.instancehome, 'lib', 'python')
Modified: Zope/trunk/src/Zope2/Startup/tests/test_schema.py
===================================================================
--- Zope/trunk/src/Zope2/Startup/tests/test_schema.py 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/Zope2/Startup/tests/test_schema.py 2009-05-12 08:49:29 UTC (rev 99866)
@@ -101,9 +101,23 @@
NSYNC doesnt
</environment>
""")
- items = conf.environment.items()
+ items = conf.environment[0].items()
items.sort()
self.assertEqual(items, [("FEARFACTORY", "rocks"), ("NSYNC","doesnt")])
+ conf, handler = self.load_config_text("""\
+ # instancehome is here since it's required
+ instancehome <<INSTANCE_HOME>>
+ <environment>
+ FEARFACTORY rocks
+ </environment>
+ <environment>
+ NSYNC doesnt
+ </environment>
+ """)
+ self.assertEqual(len(conf.environment), 2)
+ # in principle, we should test the handler as well
+ # But this would have vast side effects
+ # Thus, we test with a non regression test
def test_ms_author_via(self):
import webdav
Modified: Zope/trunk/src/Zope2/Startup/zopeschema.xml
===================================================================
--- Zope/trunk/src/Zope2/Startup/zopeschema.xml 2009-05-12 08:32:23 UTC (rev 99865)
+++ Zope/trunk/src/Zope2/Startup/zopeschema.xml 2009-05-12 08:49:29 UTC (rev 99866)
@@ -292,14 +292,14 @@
</description>
</multisection>
- <section type="environment" attribute="environment" name="*">
+ <multisection type="environment" attribute="environment" name="*">
<description>
A section which allows a user to define arbitrary key-value pairs for
use as environment variables during Zope's run cycle. It
is not recommended to set system-related environment variables such as
PYTHONPATH within this section.
</description>
- </section>
+ </multisection>
<key name="instancehome" datatype="existing-directory"
required="yes">
More information about the Checkins
mailing list