From 3c996c366af2730833c663b4490ffc7854cacbfe 2011-06-20 23:40:20
From: MinRK <benjaminrk@gmail.com>
Date: 2011-06-20 23:40:20
Subject: [PATCH] scrub twisted/deferred references from launchers

also extract daemonize function from twisted.scripts._twistd_unix

---

diff --git a/IPython/parallel/apps/ipclusterapp.py b/IPython/parallel/apps/ipclusterapp.py
index 4b59516..efb3652 100755
--- a/IPython/parallel/apps/ipclusterapp.py
+++ b/IPython/parallel/apps/ipclusterapp.py
@@ -34,6 +34,7 @@ from zmq.eventloop import ioloop
 from IPython.config.application import Application, boolean_flag
 from IPython.config.loader import Config
 from IPython.core.newapplication import BaseIPythonApplication, ProfileDir
+from IPython.utils.daemonize import daemonize
 from IPython.utils.importstring import import_item
 from IPython.utils.traitlets import Int, Unicode, Bool, CFloat, Dict, List
 
@@ -368,7 +369,6 @@ class IPClusterEngines(BaseParallelApplication):
         # TODO: Get daemonize working on Windows or as a Windows Server.
         if self.daemonize:
             if os.name=='posix':
-                from twisted.scripts._twistd_unix import daemonize
                 daemonize()
 
         dc = ioloop.DelayedCallback(self.start_engines, 0, self.loop)
@@ -468,7 +468,6 @@ class IPClusterStart(IPClusterEngines):
         # TODO: Get daemonize working on Windows or as a Windows Server.
         if self.daemonize:
             if os.name=='posix':
-                from twisted.scripts._twistd_unix import daemonize
                 daemonize()
 
         dc = ioloop.DelayedCallback(self.start_controller, 0, self.loop)
diff --git a/IPython/parallel/apps/launcher.py b/IPython/parallel/apps/launcher.py
index 3f63b1a..285c349 100644
--- a/IPython/parallel/apps/launcher.py
+++ b/IPython/parallel/apps/launcher.py
@@ -157,29 +157,20 @@ class BaseLauncher(LoggingConfigurable):
             return False
 
     def start(self):
-        """Start the process.
-
-        This must return a deferred that fires with information about the
-        process starting (like a pid, job id, etc.).
-        """
+        """Start the process."""
         raise NotImplementedError('start must be implemented in a subclass')
 
     def stop(self):
         """Stop the process and notify observers of stopping.
 
-        This must return a deferred that fires with information about the
-        processing stopping, like errors that occur while the process is
-        attempting to be shut down. This deferred won't fire when the process
-        actually stops. To observe the actual process stopping, see
-        :func:`observe_stop`.
+        This method will return None immediately.
+        To observe the actual process stopping, see :meth:`on_stop`.
         """
         raise NotImplementedError('stop must be implemented in a subclass')
 
     def on_stop(self, f):
-        """Get a deferred that will fire when the process stops.
-
-        The deferred will fire with data that contains information about
-        the exit status of the process.
+        """Register a callback to be called with this Launcher's stop_data
+        when the process actually finishes.
         """
         if self.state=='after':
             return f(self.stop_data)
@@ -202,7 +193,7 @@ class BaseLauncher(LoggingConfigurable):
         """Call this to trigger process stop actions.
 
         This logs the process stopping and sets the state to 'after'. Call
-        this to trigger all the deferreds from :func:`observe_stop`."""
+        this to trigger callbacks registered via :meth:`on_stop`."""
 
         self.log.info('Process %r stopped: %r' % (self.args[0], data))
         self.stop_data = data
@@ -215,8 +206,6 @@ class BaseLauncher(LoggingConfigurable):
     def signal(self, sig):
         """Signal the process.
 
-        Return a semi-meaningless deferred after signaling the process.
-
         Parameters
         ----------
         sig : str or int
@@ -247,7 +236,6 @@ class LocalProcessLauncher(BaseLauncher):
             work_dir=work_dir, config=config, **kwargs
         )
         self.process = None
-        self.start_deferred = None
         self.poller = None
 
     def find_args(self):
@@ -520,7 +508,7 @@ class MPIExecEngineSetLauncher(MPIExecLauncher):
 # SSH launchers
 #-----------------------------------------------------------------------------
 
-# TODO: Get SSH Launcher working again.
+# TODO: Get SSH Launcher back to level of sshx in 0.10.2
 
 class SSHLauncher(LocalProcessLauncher):
     """A minimal launcher for ssh.
@@ -700,7 +688,7 @@ class WindowsHPCLauncher(BaseLauncher):
             '/scheduler:%s' % self.scheduler
         ]
         self.log.info("Starting Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
-        # Twisted will raise DeprecationWarnings if we try to pass unicode to this
+
         output = check_output([self.job_cmd]+args,
             env=os.environ,
             cwd=self.work_dir,
@@ -1072,3 +1060,4 @@ sge_launchers = [
 ]
 all_launchers = local_launchers + mpi_launchers + ssh_launchers + winhpc_launchers\
                 + pbs_launchers + sge_launchers
+
diff --git a/IPython/utils/daemonize.py b/IPython/utils/daemonize.py
new file mode 100644
index 0000000..1c6d02d
--- /dev/null
+++ b/IPython/utils/daemonize.py
@@ -0,0 +1,26 @@
+"""daemonize function from twisted.scripts._twistd_unix."""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) Twisted Matrix Laboratories.
+# See Twisted's LICENSE for details.
+# http://twistedmatrix.com/
+#-----------------------------------------------------------------------------
+
+import os, errno
+
+def daemonize():
+    # See http://www.erlenstar.demon.co.uk/unix/faq_toc.html#TOC16
+    if os.fork():   # launch child and...
+        os._exit(0) # kill off parent
+    os.setsid()
+    if os.fork():   # launch child and...
+        os._exit(0) # kill off parent again.
+    null = os.open('/dev/null', os.O_RDWR)
+    for i in range(3):
+        try:
+            os.dup2(null, i)
+        except OSError, e:
+            if e.errno != errno.EBADF:
+                raise
+    os.close(null)
+