Commit 966f7dfa authored by Hanno Schlichting's avatar Hanno Schlichting

flake8

parent 8e52f973
import sys, asyncore, time
import asyncore
import time
_shutdown_phase = 0
_shutdown_timeout = 30 # seconds per phase
_shutdown_timeout = 30 # seconds per phase
# The shutdown phase counts up from 0 to 4.
#
......@@ -23,7 +24,8 @@ _shutdown_timeout = 30 # seconds per phase
# of time that it has currently been in that phase. This method should return
# true if it does not yet want shutdown to proceed to the next phase.
def shutdown(exit_code,fast = 0):
def shutdown(exit_code, fast=0):
global _shutdown_phase
global _shutdown_timeout
if _shutdown_phase == 0:
......@@ -38,6 +40,7 @@ def shutdown(exit_code,fast = 0):
# enough, but still clean.
_shutdown_timeout = 1.0
def loop():
# Run the main loop until someone calls shutdown()
lifetime_loop()
......@@ -45,6 +48,7 @@ def loop():
# loop to allow remaining requests to trickle away.
graceful_shutdown_loop()
def lifetime_loop():
# The main loop. Stay in here until we need to shutdown
map = asyncore.socket_map
......@@ -52,7 +56,7 @@ def lifetime_loop():
while map and _shutdown_phase == 0:
asyncore.poll(timeout, map)
def graceful_shutdown_loop():
# The shutdown loop. Allow various services to shutdown gradually.
global _shutdown_phase
......@@ -60,19 +64,19 @@ def graceful_shutdown_loop():
timeout = 1.0
map = asyncore.socket_map
while map and _shutdown_phase < 4:
time_in_this_phase = time.time()-timestamp
time_in_this_phase = time.time() - timestamp
veto = 0
for fd,obj in map.items():
for fd, obj in map.items():
try:
fn = getattr(obj,'clean_shutdown_control')
fn = getattr(obj, 'clean_shutdown_control')
except AttributeError:
pass
else:
try:
veto = veto or fn(_shutdown_phase,time_in_this_phase)
veto = veto or fn(_shutdown_phase, time_in_this_phase)
except:
obj.handle_error()
if veto and time_in_this_phase<_shutdown_timeout:
if veto and time_in_this_phase < _shutdown_timeout:
# Any open socket handler can veto moving on to the next shutdown
# phase. (but not forever)
asyncore.poll(timeout, map)
......@@ -80,4 +84,3 @@ def graceful_shutdown_loop():
# No vetos? That is one step closer to shutting down
_shutdown_phase += 1
timestamp = time.time()
......@@ -12,13 +12,15 @@
##############################################################################
"""Signal handling dispatcher."""
import sys, os
import os
import sys
import signal
from logging import getLogger
LOG = getLogger('SignalHandler')
LOG = getLogger('SignalHandler')
class SignalHandler:
class SignalHandler(object):
def __init__(self):
self.registry = {}
......@@ -52,7 +54,8 @@ class SignalHandler:
for handler in self.registry.get(signum, []):
# Never let a bad handler prevent the standard signal
# handlers from running.
try: handler()
try:
handler()
except SystemExit:
# if we trap SystemExit, we can't restart
raise
......@@ -62,6 +65,7 @@ class SignalHandler:
_signals = None
def get_signal_name(n):
"""Return the symbolic name for signal n.
......
......@@ -22,7 +22,6 @@ import Lifetime
from .threads import dump_threads
logger = logging.getLogger("Z2")
if os.name == 'nt':
......@@ -37,11 +36,12 @@ if os.name == 'nt':
else:
from SignalHandler import SignalHandler
def shutdownFastHandler():
"""Shutdown cleanly on SIGTERM. This is registered first,
so it should be called after all other handlers."""
logger.info("Shutting down fast")
Lifetime.shutdown(0,fast=1)
Lifetime.shutdown(0, fast=1)
def shutdownHandler():
......@@ -50,6 +50,7 @@ def shutdownHandler():
logger.info("Shutting down")
sys.exit(0)
def restartHandler():
"""Restart cleanly on SIGHUP. This is registered first, so it
should be called after all other SIGHUP handlers."""
......@@ -59,11 +60,11 @@ def restartHandler():
def showStacks():
"""Dump a stracktrace of all threads on the console."""
print dump_threads()
print(dump_threads())
sys.stdout.flush()
class LogfileReopenHandler:
class LogfileReopenHandler(object):
"""Reopen log files on SIGUSR2.
This is registered first, so it should be called after all other
......@@ -77,12 +78,15 @@ class LogfileReopenHandler:
log.reopen()
logger.info("Log files reopened successfully")
# On Windows, a 'reopen' is useless - the file can not be renamed
# while open, so we perform a trivial 'rotate'.
class LogfileRotateHandler:
"""Rotate log files on SIGUSR2. Only called on Windows. This is
registered first, so it should be called after all other SIGUSR2
handlers."""
class LogfileRotateHandler(object):
"""
Rotate log files on SIGUSR2. Only called on Windows. This is
registered first, so it should be called after all other SIGUSR2
handlers.
"""
def __init__(self, loggers):
self.loggers = [log for log in loggers if log is not None]
......@@ -95,6 +99,7 @@ class LogfileRotateHandler:
handler.rotate()
logger.info("Log files rotation complete")
def registerZopeSignals(loggers):
from signal import SIGTERM, SIGINT
try:
......@@ -111,7 +116,7 @@ def registerZopeSignals(loggers):
mod_wsgi = True
try:
from mod_wsgi import version
from mod_wsgi import version # NOQA
except ImportError:
mod_wsgi = False
......
......@@ -20,7 +20,7 @@
#
# One event is used per signal, and the event name is based on both the
# Zope process ID and the signal number. For example, assuming a process
# ID of 123, a SIGINT handler would create an event called "Zope-123-2"
# ID of 123, a SIGINT handler would create an event called "Zope-123-2"
# (as signal.SIGINT==2). The logfile reopen handler uses an event named
# "Zope-123-12" (as the logfile handler uses SIGUSR2, which == 12)
......@@ -35,13 +35,14 @@
# NOTE: There is one huge semantic difference between these "signals"
# and signals on Unix. On Windows, the signals are delivered asynchronously
# to a thread inside this module. This thread calls the event handler
# to a thread inside this module. This thread calls the event handler
# directly - there is no magic to switch the call back to the main thread.
# If this is a problem (not currently, but likely later), one option may be
# to add yet another asyncore handler - the thread in this module could
# If this is a problem (not currently, but likely later), one option may be
# to add yet another asyncore handler - the thread in this module could
# then "post" the request to the main thread via this asyncore handler.
import sys, os
import os
import sys
import signal
import threading
import asyncore
......@@ -62,7 +63,7 @@ import win32event
import ntsecuritycon
import logging
logger=logging.getLogger("WinSignalHandler")
logger = logging.getLogger("WinSignalHandler")
# We simulate signals via win32 named events. This is the event name
# prefix we use - the "signal number" is appended to this name.
......@@ -76,15 +77,16 @@ winver = sys.getwindowsversion()
if winver[0] >= 5 and winver[3] == 2:
event_name_prefix = "Global\\" + event_name_prefix
def createEventSecurityObject():
# Create a security object giving World read/write access,
# but only "Owner" modify access.
sa = pywintypes.SECURITY_ATTRIBUTES()
sidEveryone = pywintypes.SID()
sidEveryone.Initialize(ntsecuritycon.SECURITY_WORLD_SID_AUTHORITY,1)
sidEveryone.Initialize(ntsecuritycon.SECURITY_WORLD_SID_AUTHORITY, 1)
sidEveryone.SetSubAuthority(0, ntsecuritycon.SECURITY_WORLD_RID)
sidCreator = pywintypes.SID()
sidCreator.Initialize(ntsecuritycon.SECURITY_CREATOR_SID_AUTHORITY,1)
sidCreator.Initialize(ntsecuritycon.SECURITY_CREATOR_SID_AUTHORITY, 1)
sidCreator.SetSubAuthority(0, ntsecuritycon.SECURITY_CREATOR_OWNER_RID)
acl = pywintypes.ACL()
......@@ -94,9 +96,10 @@ def createEventSecurityObject():
sa.SetSecurityDescriptorDacl(1, acl, 0)
return sa
def wakeSelect():
"""Interrupt a sleeping asyncore 'select' call"""
# What is the right thing to do here?
# What is the right thing to do here?
# asyncore.close_all() works, but I fear that would
# prevent the poll based graceful cleanup code from working.
# This seems to work :)
......@@ -104,7 +107,8 @@ def wakeSelect():
if hasattr(obj, "pull_trigger"):
obj.pull_trigger()
class SignalHandler:
class SignalHandler(object):
def __init__(self):
self.registry = {}
......@@ -113,7 +117,7 @@ class SignalHandler:
self.shutdown_requested = False
# Register a "console control handler" for Ctrl+C/Break notification.
SetConsoleCtrlHandler(consoleCtrlHandler)
# Start the thread that is watching for events.
thread = threading.Thread(target=self.signalCheckerThread)
# If something goes terribly wrong, don't wait for this thread!
......@@ -126,12 +130,7 @@ class SignalHandler:
logger.debug("signal handler shutdown starting.")
self.shutdown_requested = 1
win32event.SetEvent(self.admin_event_handle)
# sadly, this can deadlock at shutdown when Ctrl+C is used
# (although not then the event is used to trigger shutdown)
# at least in build 204. Further updates as they come to hand...
# Remove the Windows control handler
#SetConsoleCtrlHandler(consoleCtrlHandler, 0)
self.signal_thread.join(5) # should never block for long!
self.signal_thread.join(5) # should never block for long!
self.registry = None
self.event_handles = None
......@@ -139,7 +138,7 @@ class SignalHandler:
logger.debug("signal handler shutdown complete.")
def consoleCtrlHandler(self, ctrlType):
"""Called by Windows on a new thread whenever a console control
"""Called by Windows on a new thread whenever a console control
event is raised."""
logger.debug("Windows control event %d" % ctrlType)
sig = None
......@@ -153,13 +152,13 @@ class SignalHandler:
# CTRL_CLOSE_EVENT gives us 5 seconds before displaying
# the "End process" dialog - so treat as 'fast'
sig = signal.SIGTERM
elif ctrlType in (win32con.CTRL_LOGOFF_EVENT,
elif ctrlType in (win32con.CTRL_LOGOFF_EVENT,
win32con.CTRL_SHUTDOWN_EVENT):
# MSDN says:
# "Note that this signal is received only by services.
# Interactive applications are terminated at logoff, so
# "Note that this signal is received only by services.
# Interactive applications are terminated at logoff, so
# they are not present when the system sends this signal."
# We can therefore ignore it (our service framework
# We can therefore ignore it (our service framework
# manages shutdown in this case)
pass
else:
......@@ -169,9 +168,9 @@ class SignalHandler:
# that we don't wake the select loop until after the shutdown
# flags have been set.
result = 0
if sig is not None and self.registry.has_key(sig):
if sig is not None and sig in self.registry:
self.signalHandler(sig, None)
result = 1 # don't call other handlers.
result = 1 # don't call other handlers.
return result
def signalCheckerThread(self):
......@@ -210,7 +209,8 @@ class SignalHandler:
# Let the worker thread know there is a new handle.
win32event.SetEvent(self.admin_event_handle)
signame = get_signal_name(signum)
logger.debug("Installed sighandler for %s (%s)" % (signame, event_name))
logger.debug(
"Installed sighandler for %s (%s)" % (signame, event_name))
items.insert(0, handler)
def getRegisteredSignals(self):
......@@ -226,20 +226,23 @@ class SignalHandler:
for handler in self.registry.get(signum, []):
# Never let a bad handler prevent the standard signal
# handlers from running.
try: handler()
except SystemExit, rc:
# On Unix, signals are delivered to the main thread, so a
try:
handler()
except SystemExit as rc:
# On Unix, signals are delivered to the main thread, so a
# SystemExit does the right thing. On Windows, we are on
# our own thread, so throwing SystemExit there isn't a great
# idea. Just shutdown the main loop.
logger.debug("Trapped SystemExit(%s) - doing Lifetime shutdown" % (rc,))
logger.debug(
"Trapped SystemExit(%s) - doing Lifetime shutdown" % rc)
Lifetime.shutdown(rc)
except:
logger.exception("A handler for %s failed!'" % signame)
wakeSelect() # trigger a walk around the Lifetime loop.
wakeSelect() # trigger a walk around the Lifetime loop.
_signals = None
def get_signal_name(n):
"""Return the symbolic name for signal n.
......@@ -257,19 +260,20 @@ def get_signal_name(n):
_signals[v] = k
# extra ones that aren't (weren't?) in Windows.
for name, val in ("SIGHUP", 1), ("SIGUSR1", 10), ("SIGUSR2", 12):
if not _signals.has_key(name):
if name not in _signals:
_signals[val] = name
return _signals.get(n, 'signal %d' % n)
# The win32 ConsoleCtrlHandler
def consoleCtrlHandler(ctrlType):
# The win32 ConsoleCtrlHandler
return SignalHandler.consoleCtrlHandler(ctrlType)
# The SignalHandler is actually a singleton.
SignalHandler = SignalHandler()
# Need to be careful at shutdown - the 'signal watcher' thread which triggers
# the shutdown may still be running when the main thread terminates and
# Need to be careful at shutdown - the 'signal watcher' thread which triggers
# the shutdown may still be running when the main thread terminates and
# Python starts cleaning up.
atexit.register(SignalHandler.shutdown)
"""
Signals package __init__.py
"""
......@@ -26,10 +26,10 @@ def dump_threads():
request = f.f_locals.get('request')
if request is not None:
reqinfo += (request.get('REQUEST_METHOD', '') + ' ' +
request.get('PATH_INFO', ''))
request.get('PATH_INFO', ''))
qs = request.get('QUERY_STRING')
if qs:
reqinfo += '?'+qs
reqinfo += '?' + qs
break
f = f.f_back
if reqinfo:
......@@ -37,8 +37,8 @@ def dump_threads():
output = StringIO()
traceback.print_stack(frame, file=output)
res.append("Thread %s%s:\n%s" %
(thread_id, reqinfo, output.getvalue()))
res.append(
"Thread %s%s:\n%s" % (thread_id, reqinfo, output.getvalue()))
frames = None
res.append("End of dump")
......
......@@ -45,7 +45,7 @@ def get_starter():
return UnixZopeStarter()
class ZopeStarter:
class ZopeStarter(object):
"""This is a class which starts a Zope server.
Making it a class makes it easier to test.
......@@ -72,7 +72,6 @@ class ZopeStarter:
def setConfiguration(self, cfg):
self.cfg = cfg
def sendEvents(self):
notify(ProcessStarting())
......@@ -174,16 +173,14 @@ class ZopeStarter:
'The locale module could not be imported.\n'
'To use localization options, you must ensure\n'
'that the locale module is compiled into your\n'
'Python installation.'
)
'Python installation.')
try:
locale.setlocale(locale.LC_ALL, locale_id)
except:
raise ZConfig.ConfigurationError(
'The specified locale "%s" is not supported by your'
'system.\nSee your operating system documentation for '
'more\ninformation on locale support.' % locale_id
)
'more\ninformation on locale support.' % locale_id)
def setupZServer(self):
# Increase the number of threads
......@@ -197,7 +194,8 @@ class ZopeStarter:
# This one has the delayed listening feature
if not server.fast_listen:
server.fast_listen = True
server.listen(1024) # same value as defined in medusa.http_server.py
# same value as defined in medusa.http_server.py
server.listen(1024)
def setupServers(self):
socket_err = (
......@@ -205,17 +203,16 @@ class ZopeStarter:
'This may mean that your user does not have permission to '
'bind to the port which the server is trying to use or the '
'port may already be in use by another application. '
'(%s)'
)
'(%s)')
servers = []
for server in self.cfg.servers:
# create the server from the server factory
# set up in the config
try:
servers.append(server.create())
except socket.error,e:
raise ZConfig.ConfigurationError(socket_err
% (server.servertype(),e[1]))
except socket.error as e:
raise ZConfig.ConfigurationError(
socket_err % (server.servertype(), e[1]))
self.cfg.servers = servers
def dropPrivileges(self):
......@@ -372,7 +369,7 @@ def dropPrivileges(cfg):
import pwd
effective_user = cfg.effective_user
effective_user = cfg.effective_user
if effective_user is None:
msg = ('A user was not specified to setuid to; fix this to '
'start as root (change the effective-user directive '
......@@ -413,15 +410,15 @@ def dropPrivileges(cfg):
os.setuid(uid)
logger.info('Set effective user to "%s"' % effective_user)
return 1 # for unit testing purposes
return 1 # for unit testing purposes
# DM 2004-11-24: added
def _name2Ips(host, isIp_=compile(r'(\d+\.){3}').match):
'''map a name *host* to the sequence of its ip addresses;
use *host* itself (as sequence) if it already is an ip address.
Thus, if only a specific interface on a host is trusted,
identify it by its ip (and not the host name).
'''
if isIp_(host): return [host]
if isIp_(host):
return [host]
return gethostbyaddr(host)[2]
......@@ -34,6 +34,7 @@ from App.config import getConfiguration, setConfiguration
TEMPNAME = tempfile.mktemp()
TEMPPRODUCTS = os.path.join(TEMPNAME, "Products")
def getSchema():
startup = os.path.dirname(Zope2.Startup.__file__)
schemafile = os.path.join(startup, 'zopeschema.xml')
......@@ -45,10 +46,11 @@ def getSchema():
logger_states = {}
for name in (None, 'trace', 'access'):
logger = logging.getLogger(name)
logger_states[name] = {'level':logger.level,
'propagate':logger.propagate,
'handlers':logger.handlers,
'filters':logger.filters}
logger_states[name] = {'level': logger.level,
'propagate': logger.propagate,
'handlers': logger.handlers,
'filters': logger.filters}
class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
......@@ -94,7 +96,7 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
try:
os.mkdir(TEMPNAME)
os.mkdir(TEMPPRODUCTS)
except OSError, why:
except OSError as why:
if why == 17:
# already exists
pass
......@@ -110,10 +112,10 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
locale en_GB""")
except ZConfig.DataConversionError, e:
except ZConfig.DataConversionError as e:
# Skip this test if we don't have support.
if e.message.startswith(
'The specified locale "en_GB" is not supported'):
'The specified locale "en_GB" is not supported'):
return
raise
starter = self.get_starter(conf)
......@@ -146,16 +148,12 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
# startup handler should take on the level of the event log handler
# with the lowest level
logger = starter.event_logger
self.assertEqual(starter.startup_handler.level, 15) # 15 is BLATHER
self.assertEqual(starter.startup_handler.level, 15) # 15 is BLATHER
self.assert_(starter.startup_handler in logger.handlers)
self.assertEqual(logger.level, 15)
# We expect a debug handler and the startup handler:
self.assertEqual(len(logger.handlers), 2)
# XXX need to check that log messages get written to
# sys.stderr, not that the stream identity for the startup
# handler matches
#self.assertEqual(starter.startup_handler.stream, sys.stderr)
conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
debug-mode off
......@@ -168,10 +166,6 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
</eventlog>""")
starter = self.get_starter(conf)
starter.setupInitialLogging()
# XXX need to check that log messages get written to
# sys.stderr, not that the stream identity for the startup
# handler matches
#self.assertNotEqual(starter.startup_handler.stream, sys.stderr)
def testSetupZServerThreads(self):
conf = self.load_config_text("""
......@@ -193,7 +187,7 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
</http-server>
<ftp-server>
address %(ftp)s
</ftp-server>""" % dict(http=port, ftp=port+1)
</ftp-server>""" % dict(http=port, ftp=port + 1)
)
starter = self.get_starter(conf)
# do the job the 'handler' would have done (call prepare)
......@@ -207,7 +201,7 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
self.assertEqual(conf.servers[1].__class__,
ZServer.FTPServer)
finally:
del conf.servers # should release servers
del conf.servers # should release servers
pass
def testDropPrivileges(self):
......@@ -216,8 +210,10 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
if os.name != 'posix':
return
_old_getuid = os.getuid
def _return0():
return 0
def make_starter(conf):
# remove the debug handler, since we don't want junk on
# stderr for the tests
......@@ -292,8 +288,10 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
self.assertEqual(logger.level, logging.INFO)
l = open(os.path.join(TEMPNAME, 'event.log')).read()
self.assertTrue(l.find('hello') > -1)
self.assertTrue(os.path.exists(os.path.join(TEMPNAME, 'Z2.log')))
self.assertTrue(os.path.exists(os.path.join(TEMPNAME,'trace.log')))
self.assertTrue(
os.path.exists(os.path.join(TEMPNAME, 'Z2.log')))
self.assertTrue(
os.path.exists(os.path.join(TEMPNAME, 'trace.log')))
finally:
for name in ('event.log', 'Z2.log', 'trace.log'):
try:
......@@ -355,12 +353,11 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
conf = self.load_config_text("""
instancehome <<INSTANCE_HOME>>
python-check-interval %d
""" % newcheckinterval
)
""" % newcheckinterval)
try:
starter = self.get_starter(conf)
starter.setupInterpreter()
self.assertEqual( sys.getcheckinterval() , newcheckinterval )
self.assertEqual(sys.getcheckinterval(), newcheckinterval)
finally:
sys.setcheckinterval(oldcheckinterval)
......@@ -369,7 +366,7 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
try:
os.mkdir(TEMPNAME)
os.mkdir(TEMPPRODUCTS)
except OSError, why:
except OSError as why:
if why == errno.EEXIST:
# already exists
pass
......@@ -392,7 +389,3 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
except:
pass
setConfiguration(old_config)
def test_suite():
return unittest.makeSuite(ZopeStarterTestCase)
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