Commit 7b5b3318 authored by Andreas Jung's avatar Andreas Jung

merged haufe-legacy-integration branch

parents 07b1a365 e2a793fb
...@@ -14,9 +14,32 @@ Features Added ...@@ -14,9 +14,32 @@ Features Added
- zExceptions.convertExceptionType: new API, breaking out conversion of - zExceptions.convertExceptionType: new API, breaking out conversion of
exception names to exception types from 'upgradeException'. 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 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 - Fix BBB regression which prevented setting browser ID cookies from
browser ID managers created before the 'HTTPOnly' feature landed. browser ID managers created before the 'HTTPOnly' feature landed.
https://bugs.launchpad.net/bugs/374816 https://bugs.launchpad.net/bugs/374816
...@@ -50,11 +73,13 @@ Restructuring ...@@ -50,11 +73,13 @@ Restructuring
Features Added 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 - Extended BrowserIdManager to expose the 'HTTPOnly' attribute for its
cookie. Also via https://bugs.launchpad.net/zope2/+bug/367393 . 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, http://www.owasp.org/index.php/HTTPOnly). Patch from Stephan Hofmockel,
via https://bugs.launchpad.net/zope2/+bug/367393 . via https://bugs.launchpad.net/zope2/+bug/367393 .
......
...@@ -80,4 +80,5 @@ class Cleanup: ...@@ -80,4 +80,5 @@ class Cleanup:
self._jar = jar self._jar = jar
def __del__(self): def __del__(self):
transaction.abort()
self._jar.close() self._jar.close()
...@@ -59,9 +59,7 @@ class SimpleTrailblazer: ...@@ -59,9 +59,7 @@ class SimpleTrailblazer:
factory = guarded_getattr(dispatcher, 'manage_addFolder') factory = guarded_getattr(dispatcher, 'manage_addFolder')
factory(id) factory(id)
o = context.restrictedTraverse(id) o = context.restrictedTraverse(id)
# Commit a subtransaction to assign the new object to context._p_jar.add(o.aq_base)
# the correct database.
transaction.savepoint(optimistic=True)
return o return o
def traverseOrConstruct(self, path, omit_final=0): def traverseOrConstruct(self, path, omit_final=0):
......
...@@ -83,6 +83,7 @@ class MountingTests(unittest.TestCase): ...@@ -83,6 +83,7 @@ class MountingTests(unittest.TestCase):
databases = [TestDBConfig('test_main.fs', ['/']).getDB(), databases = [TestDBConfig('test_main.fs', ['/']).getDB(),
TestDBConfig('test_mount1.fs', ['/mount1']).getDB(), TestDBConfig('test_mount1.fs', ['/mount1']).getDB(),
TestDBConfig('test_mount2.fs', ['/mount2']).getDB(), TestDBConfig('test_mount2.fs', ['/mount2']).getDB(),
TestDBConfig('test_mount3.fs', ['/i/mount3']).getDB(),
] ]
mount_points = {} mount_points = {}
mount_factories = {} mount_factories = {}
...@@ -102,13 +103,21 @@ class MountingTests(unittest.TestCase): ...@@ -102,13 +103,21 @@ class MountingTests(unittest.TestCase):
root = conn.root() root = conn.root()
root['Application'] = app = Application() root['Application'] = app = Application()
self.app = app 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 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 transaction.commit() # Get the mount points ready
def tearDown(self): def tearDown(self):
# logout
from AccessControl.SecurityManagement import noSecurityManager
noSecurityManager()
App.config.setConfiguration(original_config) App.config.setConfiguration(original_config)
transaction.abort() transaction.abort()
self.app._p_jar.close() self.app._p_jar.close()
...@@ -120,6 +129,7 @@ class MountingTests(unittest.TestCase): ...@@ -120,6 +129,7 @@ class MountingTests(unittest.TestCase):
def testRead(self): def testRead(self):
self.assertEqual(self.app.mount1.id, 'mount1') self.assertEqual(self.app.mount1.id, 'mount1')
self.assertEqual(self.app.mount2.id, 'mount2') self.assertEqual(self.app.mount2.id, 'mount2')
self.assertEqual(self.app.i.mount3.id, 'mount3')
def testWrite(self): def testWrite(self):
app = self.app app = self.app
...@@ -144,6 +154,7 @@ class MountingTests(unittest.TestCase): ...@@ -144,6 +154,7 @@ class MountingTests(unittest.TestCase):
self.assertEqual(getMountPoint(self.app.mount1)._path, '/mount1') self.assertEqual(getMountPoint(self.app.mount1)._path, '/mount1')
self.assert_(getMountPoint(self.app.mount2) is not None) self.assert_(getMountPoint(self.app.mount2) is not None)
self.assertEqual(getMountPoint(self.app.mount2)._path, '/mount2') self.assertEqual(getMountPoint(self.app.mount2)._path, '/mount2')
self.assertEqual(getMountPoint(self.app.i.mount3)._path, '/i/mount3')
del self.app.mount2 del self.app.mount2
self.app.mount2 = Folder() self.app.mount2 = Folder()
self.app.mount2.id = 'mount2' self.app.mount2.id = 'mount2'
...@@ -160,8 +171,13 @@ class MountingTests(unittest.TestCase): ...@@ -160,8 +171,13 @@ class MountingTests(unittest.TestCase):
{'status': 'Ok', {'status': 'Ok',
'path': '/mount2', 'path': '/mount2',
'name': 'test_mount2.fs', 'name': 'test_mount2.fs',
'exists': 1}] 'exists': 1},
self.assertEqual(expected, status) {'status': 'Ok',
'path': '/i/mount3',
'name': 'test_mount3.fs',
'exists': 1},
]
self.assertEqual(sorted(expected), sorted(status))
del self.app.mount2 del self.app.mount2
status = manage_getMountStatus(self.app) status = manage_getMountStatus(self.app)
expected = [{'status': 'Ok', expected = [{'status': 'Ok',
...@@ -171,8 +187,14 @@ class MountingTests(unittest.TestCase): ...@@ -171,8 +187,14 @@ class MountingTests(unittest.TestCase):
{'status': 'Ready to create', {'status': 'Ready to create',
'path': '/mount2', 'path': '/mount2',
'name': 'test_mount2.fs', 'name': 'test_mount2.fs',
'exists': 0}] 'exists': 0},
self.assertEqual(expected, status) {'status': 'Ok',
'path': '/i/mount3',
'name': 'test_mount3.fs',
'exists': 1},
]
self.assertEqual(sorted(expected), sorted(status))
self.app.mount2 = Folder('mount2') self.app.mount2 = Folder('mount2')
status = manage_getMountStatus(self.app) status = manage_getMountStatus(self.app)
expected = [{'status': 'Ok', expected = [{'status': 'Ok',
...@@ -182,8 +204,13 @@ class MountingTests(unittest.TestCase): ...@@ -182,8 +204,13 @@ class MountingTests(unittest.TestCase):
{'status': '** Something is in the way **', {'status': '** Something is in the way **',
'path': '/mount2', 'path': '/mount2',
'name': 'test_mount2.fs', 'name': 'test_mount2.fs',
'exists': 1}] 'exists': 1},
self.assertEqual(expected, status) {'status': 'Ok',
'path': '/i/mount3',
'name': 'test_mount3.fs',
'exists': 1},
]
self.assertEqual(sorted(expected), sorted(status))
def test_close(self): def test_close(self):
app = self.app app = self.app
...@@ -192,6 +219,7 @@ class MountingTests(unittest.TestCase): ...@@ -192,6 +219,7 @@ class MountingTests(unittest.TestCase):
app.a3 = '3' app.a3 = '3'
conn1 = app.mount1._p_jar conn1 = app.mount1._p_jar
conn2 = app.mount2._p_jar conn2 = app.mount2._p_jar
conn3 = app.i.mount3._p_jar
transaction.abort() transaction.abort()
# Close the main connection # Close the main connection
app._p_jar.close() app._p_jar.close()
...@@ -199,6 +227,22 @@ class MountingTests(unittest.TestCase): ...@@ -199,6 +227,22 @@ class MountingTests(unittest.TestCase):
# Check that secondary connections have been closed too # Check that secondary connections have been closed too
self.assertEqual(conn1.opened, None) self.assertEqual(conn1.opened, None)
self.assertEqual(conn2.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(): def test_suite():
......
...@@ -1684,7 +1684,7 @@ def parse_cookie(text, ...@@ -1684,7 +1684,7 @@ def parse_cookie(text,
release() release()
if not already_have(name): if not already_have(name):
result[name] = value result[name] = unquote(value)
return apply(parse_cookie,(text[l:],result)) return apply(parse_cookie,(text[l:],result))
......
...@@ -23,6 +23,7 @@ from zExceptions import Unauthorized, Redirect ...@@ -23,6 +23,7 @@ from zExceptions import Unauthorized, Redirect
from zExceptions.ExceptionFormatter import format_exception from zExceptions.ExceptionFormatter import format_exception
from ZPublisher import BadRequest, InternalError, NotFound from ZPublisher import BadRequest, InternalError, NotFound
from cgi import escape from cgi import escape
from urllib import quote
nl2sp = maketrans('\n',' ') nl2sp = maketrans('\n',' ')
...@@ -842,7 +843,7 @@ class HTTPResponse(BaseResponse): ...@@ -842,7 +843,7 @@ class HTTPResponse(BaseResponse):
# quoted cookie attr values, so only the value part # quoted cookie attr values, so only the value part
# of name=value pairs may be quoted. # 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(): for name, v in attrs.items():
name = name.lower() name = name.lower()
if name == 'expires': if name == 'expires':
......
...@@ -24,6 +24,10 @@ from zExceptions import Redirect ...@@ -24,6 +24,10 @@ from zExceptions import Redirect
from zope.publisher.interfaces import ISkinnable from zope.publisher.interfaces import ISkinnable
from zope.publisher.skinnable import setDefaultSkin from zope.publisher.skinnable import setDefaultSkin
from zope.security.management import newInteraction, endInteraction from zope.security.management import newInteraction, endInteraction
from zope.event import notify
from pubevents import PubStart, PubSuccess, PubFailure, \
PubBeforeCommit, PubAfterTraversal
class Retry(Exception): class Retry(Exception):
"""Raise this to retry a request """Raise this to retry a request
...@@ -76,6 +80,7 @@ def publish(request, module_name, after_list, debug=0, ...@@ -76,6 +80,7 @@ def publish(request, module_name, after_list, debug=0,
response=None response=None
try: try:
notify(PubStart(request))
# TODO pass request here once BaseRequest implements IParticipation # TODO pass request here once BaseRequest implements IParticipation
newInteraction() newInteraction()
...@@ -110,6 +115,8 @@ def publish(request, module_name, after_list, debug=0, ...@@ -110,6 +115,8 @@ def publish(request, module_name, after_list, debug=0,
object=request.traverse(path, validated_hook=validated_hook) object=request.traverse(path, validated_hook=validated_hook)
notify(PubAfterTraversal(request))
if transactions_manager: if transactions_manager:
transactions_manager.recordMetaData(object, request) transactions_manager.recordMetaData(object, request)
...@@ -122,12 +129,18 @@ def publish(request, module_name, after_list, debug=0, ...@@ -122,12 +129,18 @@ def publish(request, module_name, after_list, debug=0,
if result is not response: if result is not response:
response.setBody(result) response.setBody(result)
notify(PubBeforeCommit(request))
if transactions_manager: if transactions_manager:
transactions_manager.commit() transactions_manager.commit()
endInteraction() endInteraction()
notify(PubSuccess(request))
return response return response
except: except:
# save in order to give 'PubFailure' the original exception info
exc_info = sys.exc_info()
# DM: provide nicer error message for FTP # DM: provide nicer error message for FTP
sm = None sm = None
if response is not None: if response is not None:
...@@ -141,6 +154,7 @@ def publish(request, module_name, after_list, debug=0, ...@@ -141,6 +154,7 @@ def publish(request, module_name, after_list, debug=0,
debug_mode and compact_traceback()[-1] or '')) debug_mode and compact_traceback()[-1] or ''))
if err_hook is not None: if err_hook is not None:
retry = False
if parents: if parents:
parents=parents[0] parents=parents[0]
try: try:
...@@ -157,10 +171,15 @@ def publish(request, module_name, after_list, debug=0, ...@@ -157,10 +171,15 @@ def publish(request, module_name, after_list, debug=0,
sys.exc_info()[1], sys.exc_info()[1],
sys.exc_info()[2], sys.exc_info()[2],
) )
retry = True
finally: finally:
if transactions_manager: # Note: 'abort's can fail. Nevertheless, we want end request handling
transactions_manager.abort() try:
endInteraction() if transactions_manager:
transactions_manager.abort()
finally:
endInteraction()
notify(PubFailure(request, exc_info, retry))
# Only reachable if Retry is raised and request supports retry. # Only reachable if Retry is raised and request supports retry.
newrequest=request.retry() newrequest=request.retry()
...@@ -175,9 +194,13 @@ def publish(request, module_name, after_list, debug=0, ...@@ -175,9 +194,13 @@ def publish(request, module_name, after_list, debug=0,
newrequest.close() newrequest.close()
else: else:
if transactions_manager: # Note: 'abort's can fail. Nevertheless, we want end request handling
transactions_manager.abort() try:
endInteraction() if transactions_manager:
transactions_manager.abort()
finally:
endInteraction()
notify(PubFailure(request, exc_info, False))
raise raise
......
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.
"""
'''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)
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)))
...@@ -11,11 +11,17 @@ ...@@ -11,11 +11,17 @@
# #
############################################################################## ##############################################################################
import logging
LOG = logging.getLogger('ZServerPublisher')
class ZServerPublisher: class ZServerPublisher:
def __init__(self, accept): def __init__(self, accept):
from sys import exc_info
from ZPublisher import publish_module from ZPublisher import publish_module
from ZPublisher.WSGIPublisher import publish_module as publish_wsgi from ZPublisher.WSGIPublisher import publish_module as publish_wsgi
while 1: while 1:
try:
name, a, b=accept() name, a, b=accept()
if name == "Zope2": if name == "Zope2":
try: try:
...@@ -36,3 +42,5 @@ class ZServerPublisher: ...@@ -36,3 +42,5 @@ class ZServerPublisher:
# TODO: Support keeping connections open. # TODO: Support keeping connections open.
a['wsgi.output']._close = 1 a['wsgi.output']._close = 1
a['wsgi.output'].close() a['wsgi.output'].close()
except:
LOG.error('exception caught', exc_info=True)
...@@ -86,6 +86,7 @@ class ZopeStarter: ...@@ -86,6 +86,7 @@ class ZopeStarter:
self.setupServers() self.setupServers()
# drop privileges after setting up servers # drop privileges after setting up servers
self.dropPrivileges() self.dropPrivileges()
self.setupFinalLogging()
self.makeLockFile() self.makeLockFile()
self.makePidFile() self.makePidFile()
self.setupInterpreter() self.setupInterpreter()
...@@ -100,7 +101,6 @@ class ZopeStarter: ...@@ -100,7 +101,6 @@ class ZopeStarter:
# after it has emitted ZServer messages. # after it has emitted ZServer messages.
logger.info('Ready to handle requests') logger.info('Ready to handle requests')
self.setupFinalLogging()
self.sendEvents() self.sendEvents()
def run(self): def run(self):
......
...@@ -184,8 +184,10 @@ def root_handler(config): ...@@ -184,8 +184,10 @@ def root_handler(config):
""" """
# Set environment variables # Set environment variables
for k,v in config.environment.items(): d = {}
os.environ[k] = v for s in config.environment:
d.update(s)
os.environ.update(d)
# Add directories to the pythonpath # Add directories to the pythonpath
instancelib = os.path.join(config.instancehome, 'lib', 'python') instancelib = os.path.join(config.instancehome, 'lib', 'python')
......
...@@ -101,9 +101,23 @@ class StartupTestCase(unittest.TestCase): ...@@ -101,9 +101,23 @@ class StartupTestCase(unittest.TestCase):
NSYNC doesnt NSYNC doesnt
</environment> </environment>
""") """)
items = conf.environment.items() items = conf.environment[0].items()
items.sort() items.sort()
self.assertEqual(items, [("FEARFACTORY", "rocks"), ("NSYNC","doesnt")]) 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): def test_ms_author_via(self):
import webdav import webdav
......
...@@ -292,14 +292,14 @@ ...@@ -292,14 +292,14 @@
</description> </description>
</multisection> </multisection>
<section type="environment" attribute="environment" name="*"> <multisection type="environment" attribute="environment" name="*">
<description> <description>
A section which allows a user to define arbitrary key-value pairs for A section which allows a user to define arbitrary key-value pairs for
use as environment variables during Zope's run cycle. It use as environment variables during Zope's run cycle. It
is not recommended to set system-related environment variables such as is not recommended to set system-related environment variables such as
PYTHONPATH within this section. PYTHONPATH within this section.
</description> </description>
</section> </multisection>
<key name="instancehome" datatype="existing-directory" <key name="instancehome" datatype="existing-directory"
required="yes"> required="yes">
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment