[Twisted-Python] supporting start/stop/restart behavior

Clark C. Evans cce at clarkevans.com
Thu Mar 20 11:08:58 MST 2003


This patch adds 'stop', 'start', and 'restart' commands to twistd,
so that a simple program, say,

    from twisted.internet import app
    from twisted.web.server import Site
    from twisted.web.static import File

    application = app.Application('test')
    application.listenTCP(8080, Site(File('.')))

Can be 'started'   using:     twistd -y test.py
   and 'stopped'   using:     twistd -y test.py stop
   and 'restarted' using:     twistd -y test.py restart

This is a good way to start thinking cross-platform, as twistd
could be refactored later to, say on Win NT, use NT services
for the start/stop/restart behavior.   For now the current patch
just uses the current "PID" file approach.   Besides providing
a cross-platform iterface, it doesn't require that users new to 
unix learn "kill" or have to get involved with finding the right
pid file.  Lastly, it provides the necessary delay so that a
restart can happen using a single command.

Best,

Clark



--- twistd.py.orig	Wed Mar 19 16:21:46 2003
+++ twistd.py	Thu Mar 20 12:37:56 2003
@@ -43,7 +43,7 @@
 
 
 class ServerOptions(usage.Options):
-    synopsis = "Usage: twistd [options]"
+    synopsis = "Usage: twistd [options] [start|stop|restart]"
 
     optFlags = [['nodaemon','n',  "don't daemonize"],
                 ['savestats', None, "save the Stats object rather than the text output of the profiler."],
@@ -87,6 +87,13 @@
                    'This will only take effect if the application to be run has an application '
                    'name.']]
 
+    subCommands = [['start', None, usage.Options, 
+                    'starts the application requested (default)'],
+                   ['stop', None, usage.Options,
+                    'shuts the given application down if it is running'],
+                   ['restart',None, usage.Options,
+                    'restarts the application']] 
+
     def opt_plugin(self, pkgname):
         """read config.tac from a plugin package, as with -y
         """
@@ -211,6 +218,31 @@
     import pdb
     pdb.set_trace()
 
+def signalApp(config, signal = 0):
+    if os.path.exists(config['pidfile']):
+        try:
+            pid = int(open(config['pidfile']).read())
+        except ValueError:
+            sys.exit('Pidfile %s contains non numeric value' % config['pidfile'])
+
+        try:
+            os.kill(pid, signal)
+        except OSError, why:
+            if why[0] == errno.ESRCH:
+                # The pid doesnt exists.
+                if not config['quiet']:
+                    print 'Removing stale pidfile %s' % config['pidfile']
+                    os.remove(config['pidfile'])
+            else:
+                sys.exit('Can\'t check status of PID %s from pidfile %s: %s' % (pid, config['pidfile'], why[1]))
+        else:
+            if not(signal):
+                sys.exit("""\
+Another twistd server is running, PID %s\n
+This could either be a previously started instance of your application or a
+different application entirely. To start a new one, either run it in some other
+directory, or use my --pidfile and --logfile parameters to avoid clashes.
+""" %  pid)
 
 def runApp(config):
     global initRun
@@ -248,29 +280,7 @@
     # This will fix up accidental function definitions in evaluation spaces
     # and the like.
     initRun = 0
-    if os.path.exists(config['pidfile']):
-        try:
-            pid = int(open(config['pidfile']).read())
-        except ValueError:
-            sys.exit('Pidfile %s contains non numeric value' % config['pidfile'])
-
-        try:
-            os.kill(pid, 0)
-        except OSError, why:
-            if why[0] == errno.ESRCH:
-                # The pid doesnt exists.
-                if not config['quiet']:
-                    print 'Removing stale pidfile %s' % config['pidfile']
-                    os.remove(config['pidfile'])
-            else:
-                sys.exit('Can\'t check status of PID %s from pidfile %s: %s' % (pid, config['pidfile'], why[1]))
-        else:
-            sys.exit("""\
-Another twistd server is running, PID %s\n
-This could either be a previously started instance of your application or a
-different application entirely. To start a new one, either run it in some other
-directory, or use my --pidfile and --logfile parameters to avoid clashes.
-""" %  pid)
+    signalApp(config)
 
     if config['logfile'] == '-':
         if not config['nodaemon']:
@@ -457,6 +467,19 @@
             log.err("--report-profile specified but application has no name (--appname unspecified)")
     log.msg("Server Shut Down.")
 
+def stopApp(config):
+    from signal import SIGTERM
+    from os.path import exists
+    from time import sleep
+    signalApp(config, SIGTERM)
+    nWait = 0  # processes do not die instantly
+    while exists(config['pidfile']) and nWait < 20:
+        sleep(.1)
+        nWait += 1
+
+def restartApp(config):
+    stopApp(config)
+    runApp(config)
 
 def run():
     # make default be "--help"
@@ -471,4 +494,7 @@
         print "%s: %s" % (sys.argv[0], ue)
         os._exit(1)
 
+    cmd = getattr(config,'subCommand','start')
+    if 'stop'    == cmd: return stopApp(config)
+    if 'restart' == cmd: return restartApp(config)
     runApp(config)




More information about the Twisted-Python mailing list