From ef5de34e2b63cbc9aa5f40597d96809b36160d89 2009-11-15 07:43:06
From: bgranger <bgranger@red>
Date: 2009-11-15 07:43:06
Subject: [PATCH] Merging upstream.

---

diff --git a/IPython/core/prefilter.py b/IPython/core/prefilter.py
old mode 100644
new mode 100755
index 4be6e5b..2a8ca69
--- a/IPython/core/prefilter.py
+++ b/IPython/core/prefilter.py
@@ -39,7 +39,7 @@ from IPython.core.splitinput import split_user_input
 from IPython.core.page import page
 
 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
-from IPython.utils.genutils import make_quoted_expr
+from IPython.utils.genutils import make_quoted_expr, Term
 from IPython.utils.autoattr import auto_attr
 
 #-----------------------------------------------------------------------------
diff --git a/IPython/kernel/ipclusterapp.py b/IPython/kernel/ipclusterapp.py
index a1f444b..e37bbf1 100644
--- a/IPython/kernel/ipclusterapp.py
+++ b/IPython/kernel/ipclusterapp.py
@@ -72,7 +72,6 @@ class IPClusterCLLoader(ArgParseConfigLoader):
         parent_parser2 = argparse.ArgumentParser(add_help=False)
         parent_parser2.add_argument('-p','--profile',
             dest='Global.profile',type=unicode,
-            default=NoConfigDefault,
             help='The string name of the profile to be used. This determines '
             'the name of the cluster dir as: cluster_<profile>. The default profile '
             'is named "default".  The cluster directory is resolve this way '
@@ -81,7 +80,6 @@ class IPClusterCLLoader(ArgParseConfigLoader):
             metavar='Global.profile')
         parent_parser2.add_argument('--cluster-dir',
             dest='Global.cluster_dir',type=unicode,
-            default=NoConfigDefault,
             help='Set the cluster dir. This overrides the logic used by the '
             '--profile option.',
             default=NoConfigDefault,
diff --git a/IPython/kernel/scripts/ipcluster.py b/IPython/kernel/scripts/ipcluster.py
deleted file mode 100644
index f7c8b21..0000000
--- a/IPython/kernel/scripts/ipcluster.py
+++ /dev/null
@@ -1,811 +0,0 @@
- #!/usr/bin/env python
-# encoding: utf-8
-
-"""Start an IPython cluster = (controller + engines)."""
-
-#-----------------------------------------------------------------------------
-#  Copyright (C) 2008-2009  The IPython Development Team
-#
-#  Distributed under the terms of the BSD License.  The full license is in
-#  the file COPYING, distributed as part of this software.
-#-----------------------------------------------------------------------------
-
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
-
-import os
-import re
-import sys
-import signal
-import tempfile
-pjoin = os.path.join
-
-from twisted.internet import reactor, defer
-from twisted.internet.protocol import ProcessProtocol
-from twisted.internet.error import ProcessDone, ProcessTerminated
-from twisted.internet.utils import getProcessOutput
-from twisted.python import log
-
-from IPython.external import argparse
-from IPython.external import Itpl
-from IPython.utils.genutils import (
-    get_ipython_dir, 
-    get_log_dir, 
-    get_security_dir, 
-    num_cpus
-)
-from IPython.kernel.fcutil import have_crypto
-
-# Create various ipython directories if they don't exist.
-# This must be done before IPython.kernel.config is imported.
-from IPython.core.oldusersetup import user_setup
-if os.name == 'posix':
-    rc_suffix = ''
-else:
-    rc_suffix = '.ini'
-user_setup(get_ipython_dir(), rc_suffix, mode='install', interactive=False)
-get_log_dir()
-get_security_dir()
-
-from IPython.kernel.config import config_manager as kernel_config_manager
-from IPython.kernel.twistedutil import gatherBoth, wait_for_file
-
-
-#-----------------------------------------------------------------------------
-# General process handling code
-#-----------------------------------------------------------------------------
-
-
-class ProcessStateError(Exception):
-    pass
-
-class UnknownStatus(Exception):
-    pass
-
-class LauncherProcessProtocol(ProcessProtocol):
-    """
-    A ProcessProtocol to go with the ProcessLauncher.
-    """
-    def __init__(self, process_launcher):
-        self.process_launcher = process_launcher
-    
-    def connectionMade(self):
-        self.process_launcher.fire_start_deferred(self.transport.pid)
-    
-    def processEnded(self, status):
-        value = status.value
-        if isinstance(value, ProcessDone):
-            self.process_launcher.fire_stop_deferred(0)
-        elif isinstance(value, ProcessTerminated):
-            self.process_launcher.fire_stop_deferred(
-                {'exit_code':value.exitCode,
-                 'signal':value.signal,
-                 'status':value.status
-                }
-            )
-        else:
-            raise UnknownStatus("unknown exit status, this is probably a bug in Twisted")
-    
-    def outReceived(self, data):
-        log.msg(data)
-    
-    def errReceived(self, data):
-        log.err(data)
-
-class ProcessLauncher(object):
-    """
-    Start and stop an external process in an asynchronous manner.
-    
-    Currently this uses deferreds to notify other parties of process state
-    changes.  This is an awkward design and should be moved to using
-    a formal NotificationCenter.
-    """
-    def __init__(self, cmd_and_args):
-        self.cmd = cmd_and_args[0]
-        self.args = cmd_and_args
-        self._reset()
-    
-    def _reset(self):
-        self.process_protocol = None
-        self.pid = None
-        self.start_deferred = None
-        self.stop_deferreds = []
-        self.state = 'before' # before, running, or after
-    
-    @property
-    def running(self):
-        if self.state == 'running':
-            return True
-        else:
-            return False
-    
-    def fire_start_deferred(self, pid):
-        self.pid = pid
-        self.state = 'running'
-        log.msg('Process %r has started with pid=%i' % (self.args, pid))
-        self.start_deferred.callback(pid)
-    
-    def start(self):
-        if self.state == 'before':
-            self.process_protocol = LauncherProcessProtocol(self)
-            self.start_deferred = defer.Deferred()
-            self.process_transport = reactor.spawnProcess(
-                self.process_protocol,
-                self.cmd,
-                self.args,
-                env=os.environ
-            )
-            return self.start_deferred
-        else:
-            s = 'The process has already been started and has state: %r' % \
-                self.state
-            return defer.fail(ProcessStateError(s))
-    
-    def get_stop_deferred(self):
-        if self.state == 'running' or self.state == 'before':
-            d = defer.Deferred()
-            self.stop_deferreds.append(d)
-            return d
-        else:
-            s = 'this process is already complete'
-            return defer.fail(ProcessStateError(s))
-    
-    def fire_stop_deferred(self, exit_code):
-        log.msg('Process %r has stopped with %r' % (self.args, exit_code))
-        self.state = 'after'
-        for d in self.stop_deferreds:
-            d.callback(exit_code)
-    
-    def signal(self, sig):
-        """
-        Send a signal to the process.
-        
-        The argument sig can be ('KILL','INT', etc.) or any signal number.
-        """
-        if self.state == 'running':
-            self.process_transport.signalProcess(sig)
-    
-    # def __del__(self):
-    #     self.signal('KILL')
-    
-    def interrupt_then_kill(self, delay=1.0):
-        self.signal('INT')
-        reactor.callLater(delay, self.signal, 'KILL')
-
-
-#-----------------------------------------------------------------------------
-# Code for launching controller and engines
-#-----------------------------------------------------------------------------
-
-
-class ControllerLauncher(ProcessLauncher):
-    
-    def __init__(self, extra_args=None):
-        if sys.platform == 'win32':
-            # This logic is needed because the ipcontroller script doesn't
-            # always get installed in the same way or in the same location.
-            from IPython.kernel.scripts import ipcontroller
-            script_location = ipcontroller.__file__.replace('.pyc', '.py')
-            # The -u option here turns on unbuffered output, which is required
-            # on Win32 to prevent wierd conflict and problems with Twisted.
-            # Also, use sys.executable to make sure we are picking up the 
-            # right python exe.
-            args = [sys.executable, '-u', script_location]
-        else:
-            args = ['ipcontroller']
-        self.extra_args = extra_args
-        if extra_args is not None:
-            args.extend(extra_args)
-        
-        ProcessLauncher.__init__(self, args)
-
-
-class EngineLauncher(ProcessLauncher):
-    
-    def __init__(self, extra_args=None):
-        if sys.platform == 'win32':
-            # This logic is needed because the ipcontroller script doesn't
-            # always get installed in the same way or in the same location.
-            from IPython.kernel.scripts import ipengine
-            script_location = ipengine.__file__.replace('.pyc', '.py')
-            # The -u option here turns on unbuffered output, which is required
-            # on Win32 to prevent wierd conflict and problems with Twisted.
-            # Also, use sys.executable to make sure we are picking up the 
-            # right python exe.
-            args = [sys.executable, '-u', script_location]
-        else:
-            args = ['ipengine']
-        self.extra_args = extra_args
-        if extra_args is not None:
-            args.extend(extra_args)
-        
-        ProcessLauncher.__init__(self, args)
-
-
-class LocalEngineSet(object):
-    
-    def __init__(self, extra_args=None):
-        self.extra_args = extra_args
-        self.launchers = []
-    
-    def start(self, n):
-        dlist = []
-        for i in range(n):
-            el = EngineLauncher(extra_args=self.extra_args)
-            d = el.start()
-            self.launchers.append(el)
-            dlist.append(d)
-        dfinal = gatherBoth(dlist, consumeErrors=True)
-        dfinal.addCallback(self._handle_start)
-        return dfinal
-    
-    def _handle_start(self, r):
-        log.msg('Engines started with pids: %r' % r)
-        return r
-    
-    def _handle_stop(self, r):
-        log.msg('Engines received signal: %r' % r)
-        return r
-    
-    def signal(self, sig):
-        dlist = []
-        for el in self.launchers:
-            d = el.get_stop_deferred()
-            dlist.append(d)
-            el.signal(sig)
-        dfinal = gatherBoth(dlist, consumeErrors=True)
-        dfinal.addCallback(self._handle_stop)
-        return dfinal
-    
-    def interrupt_then_kill(self, delay=1.0):
-        dlist = []
-        for el in self.launchers:
-            d = el.get_stop_deferred()
-            dlist.append(d)
-            el.interrupt_then_kill(delay)
-        dfinal = gatherBoth(dlist, consumeErrors=True)
-        dfinal.addCallback(self._handle_stop)
-        return dfinal
-
-
-class BatchEngineSet(object):
-    
-    # Subclasses must fill these in.  See PBSEngineSet
-    submit_command = ''
-    delete_command = ''
-    job_id_regexp = ''
-    
-    def __init__(self, template_file, **kwargs):
-        self.template_file = template_file
-        self.context = {}
-        self.context.update(kwargs)
-        self.batch_file = self.template_file+'-run'
-    
-    def parse_job_id(self, output):
-        m = re.match(self.job_id_regexp, output)
-        if m is not None:
-            job_id = m.group()
-        else:
-            raise Exception("job id couldn't be determined: %s" % output)
-        self.job_id = job_id
-        log.msg('Job started with job id: %r' % job_id)
-        return job_id
-    
-    def write_batch_script(self, n):
-        self.context['n'] = n
-        template = open(self.template_file, 'r').read()
-        log.msg('Using template for batch script: %s' % self.template_file)
-        script_as_string = Itpl.itplns(template, self.context)
-        log.msg('Writing instantiated batch script: %s' % self.batch_file)
-        f = open(self.batch_file,'w')
-        f.write(script_as_string)
-        f.close()
-    
-    def handle_error(self, f):
-        f.printTraceback()
-        f.raiseException()
-    
-    def start(self, n):
-        self.write_batch_script(n)
-        d = getProcessOutput(self.submit_command,
-            [self.batch_file],env=os.environ)
-        d.addCallback(self.parse_job_id)
-        d.addErrback(self.handle_error)
-        return d
-    
-    def kill(self):
-        d = getProcessOutput(self.delete_command,
-            [self.job_id],env=os.environ)
-        return d
-
-class PBSEngineSet(BatchEngineSet):
-    
-    submit_command = 'qsub'
-    delete_command = 'qdel'
-    job_id_regexp = '\d+'
-    
-    def __init__(self, template_file, **kwargs):
-        BatchEngineSet.__init__(self, template_file, **kwargs)
-
-
-sshx_template="""#!/bin/sh
-"$@" &> /dev/null &
-echo $!
-"""
-
-engine_killer_template="""#!/bin/sh
-ps -fu `whoami` | grep '[i]pengine' | awk '{print $2}' | xargs kill -TERM
-"""
-
-class SSHEngineSet(object):
-    sshx_template=sshx_template
-    engine_killer_template=engine_killer_template
-    
-    def __init__(self, engine_hosts, sshx=None, ipengine="ipengine"):
-        """Start a controller on localhost and engines using ssh.
-        
-        The engine_hosts argument is a dict with hostnames as keys and
-        the number of engine (int) as values.  sshx is the name of a local
-        file that will be used to run remote commands.  This file is used
-        to setup the environment properly.
-        """
-        
-        self.temp_dir = tempfile.gettempdir()
-        if sshx is not None:
-            self.sshx = sshx
-        else:
-            # Write the sshx.sh file locally from our template.
-            self.sshx = os.path.join(
-                self.temp_dir,
-                '%s-main-sshx.sh' % os.environ['USER']
-            )
-            f = open(self.sshx, 'w')
-            f.writelines(self.sshx_template)
-            f.close()
-        self.engine_command = ipengine
-        self.engine_hosts = engine_hosts
-        # Write the engine killer script file locally from our template.
-        self.engine_killer = os.path.join(
-            self.temp_dir,  
-            '%s-local-engine_killer.sh' % os.environ['USER']
-        )
-        f = open(self.engine_killer, 'w')
-        f.writelines(self.engine_killer_template)
-        f.close()
-    
-    def start(self, send_furl=False):
-        dlist = []
-        for host in self.engine_hosts.keys():
-            count = self.engine_hosts[host]
-            d = self._start(host, count, send_furl)
-            dlist.append(d)
-        return gatherBoth(dlist, consumeErrors=True)
-    
-    def _start(self, hostname, count=1, send_furl=False):
-        if send_furl:
-            d = self._scp_furl(hostname)
-        else:
-            d = defer.succeed(None)
-        d.addCallback(lambda r: self._scp_sshx(hostname))
-        d.addCallback(lambda r: self._ssh_engine(hostname, count))
-        return d
-        
-    def _scp_furl(self, hostname):
-        scp_cmd = "scp ~/.ipython/security/ipcontroller-engine.furl %s:.ipython/security/" % (hostname)
-        cmd_list = scp_cmd.split()
-        cmd_list[1] = os.path.expanduser(cmd_list[1])
-        log.msg('Copying furl file: %s' % scp_cmd)
-        d = getProcessOutput(cmd_list[0], cmd_list[1:], env=os.environ) 
-        return d
-    
-    def _scp_sshx(self, hostname):
-        scp_cmd = "scp %s %s:%s/%s-sshx.sh" % (
-            self.sshx, hostname, 
-            self.temp_dir, os.environ['USER']
-        )
-        print
-        log.msg("Copying sshx: %s" % scp_cmd)
-        sshx_scp = scp_cmd.split()
-        d = getProcessOutput(sshx_scp[0], sshx_scp[1:], env=os.environ)
-        return d
-    
-    def _ssh_engine(self, hostname, count):
-        exec_engine = "ssh %s sh %s/%s-sshx.sh %s" % (
-            hostname, self.temp_dir, 
-            os.environ['USER'], self.engine_command
-        )
-        cmds = exec_engine.split()
-        dlist = []
-        log.msg("about to start engines...")
-        for i in range(count):
-            log.msg('Starting engines: %s' % exec_engine)
-            d = getProcessOutput(cmds[0], cmds[1:], env=os.environ)
-            dlist.append(d)
-        return gatherBoth(dlist, consumeErrors=True)
-    
-    def kill(self):
-        dlist = []
-        for host in self.engine_hosts.keys():
-            d = self._killall(host)
-            dlist.append(d)
-        return gatherBoth(dlist, consumeErrors=True)
-    
-    def _killall(self, hostname):
-        d = self._scp_engine_killer(hostname)
-        d.addCallback(lambda r: self._ssh_kill(hostname))
-        # d.addErrback(self._exec_err)
-        return d
-
-    def _scp_engine_killer(self, hostname):
-        scp_cmd = "scp %s %s:%s/%s-engine_killer.sh" % (
-            self.engine_killer, 
-            hostname, 
-            self.temp_dir, 
-            os.environ['USER']
-        )
-        cmds = scp_cmd.split()
-        log.msg('Copying engine_killer: %s' % scp_cmd)
-        d = getProcessOutput(cmds[0], cmds[1:], env=os.environ)
-        return d
-    
-    def _ssh_kill(self, hostname):
-        kill_cmd = "ssh %s sh %s/%s-engine_killer.sh" % (
-            hostname,
-            self.temp_dir, 
-            os.environ['USER']
-        )
-        log.msg('Killing engine: %s' % kill_cmd)
-        kill_cmd = kill_cmd.split()
-        d = getProcessOutput(kill_cmd[0], kill_cmd[1:], env=os.environ)
-        return d
-
-    def _exec_err(self, r):
-        log.msg(r)
-
-#-----------------------------------------------------------------------------
-# Main functions for the different types of clusters
-#-----------------------------------------------------------------------------
-
-# TODO:
-# The logic in these codes should be moved into classes like LocalCluster
-# MpirunCluster, PBSCluster, etc.  This would remove alot of the duplications.
-# The main functions should then just parse the command line arguments, create
-# the appropriate class and call a 'start' method.
-
-
-def check_security(args, cont_args):
-    """Check to see if we should run with SSL support."""
-    if (not args.x or not args.y) and not have_crypto:
-        log.err("""
-OpenSSL/pyOpenSSL is not available, so we can't run in secure mode.
-Try running ipcluster with the -xy flags:  ipcluster local -xy -n 4""")
-        reactor.stop()
-        return False
-    if args.x:
-        cont_args.append('-x')
-    if args.y:
-        cont_args.append('-y')
-    return True
-
-
-def check_reuse(args, cont_args):
-    """Check to see if we should try to resuse FURL files."""
-    if args.r:
-        cont_args.append('-r')
-        if args.client_port == 0 or args.engine_port == 0:
-            log.err("""
-To reuse FURL files, you must also set the client and engine ports using
-the --client-port and --engine-port options.""")
-            reactor.stop()
-            return False
-        cont_args.append('--client-port=%i' % args.client_port)
-        cont_args.append('--engine-port=%i' % args.engine_port)
-    return True
-
-
-def _err_and_stop(f):
-    """Errback to log a failure and halt the reactor on a fatal error."""
-    log.err(f)
-    reactor.stop()
-
-
-def _delay_start(cont_pid, start_engines, furl_file, reuse):
-    """Wait for controller to create FURL files and the start the engines."""
-    if not reuse:
-        if os.path.isfile(furl_file):
-            os.unlink(furl_file)
-    log.msg('Waiting for controller to finish starting...')
-    d = wait_for_file(furl_file, delay=0.2, max_tries=50)
-    d.addCallback(lambda _: log.msg('Controller started'))
-    d.addCallback(lambda _: start_engines(cont_pid))
-    return d
-
-
-def main_local(args):
-    cont_args = []
-    cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
-    
-    # Check security settings before proceeding
-    if not check_security(args, cont_args):
-        return
-    
-    # See if we are reusing FURL files
-    if not check_reuse(args, cont_args):
-        return
-    
-    cl = ControllerLauncher(extra_args=cont_args)
-    dstart = cl.start()
-    def start_engines(cont_pid):
-        engine_args = []
-        engine_args.append('--logfile=%s' % \
-            pjoin(args.logdir,'ipengine%s-' % cont_pid))
-        eset = LocalEngineSet(extra_args=engine_args)
-        def shutdown(signum, frame):
-            log.msg('Stopping local cluster')
-            # We are still playing with the times here, but these seem
-            # to be reliable in allowing everything to exit cleanly.
-            eset.interrupt_then_kill(0.5)
-            cl.interrupt_then_kill(0.5)
-            reactor.callLater(1.0, reactor.stop)
-        signal.signal(signal.SIGINT,shutdown)
-        d = eset.start(args.n)
-        return d
-    config = kernel_config_manager.get_config_obj()
-    furl_file = config['controller']['engine_furl_file']
-    dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
-    dstart.addErrback(_err_and_stop)
-
-
-def main_mpi(args):
-    cont_args = []
-    cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
-    
-    # Check security settings before proceeding
-    if not check_security(args, cont_args):
-        return
-    
-    # See if we are reusing FURL files
-    if not check_reuse(args, cont_args):
-        return
-    
-    cl = ControllerLauncher(extra_args=cont_args)
-    dstart = cl.start()
-    def start_engines(cont_pid):
-        raw_args = [args.cmd]
-        raw_args.extend(['-n',str(args.n)])
-        raw_args.append('ipengine')
-        raw_args.append('-l')
-        raw_args.append(pjoin(args.logdir,'ipengine%s-' % cont_pid))
-        if args.mpi:
-            raw_args.append('--mpi=%s' % args.mpi)
-        eset = ProcessLauncher(raw_args)
-        def shutdown(signum, frame):
-            log.msg('Stopping local cluster')
-            # We are still playing with the times here, but these seem
-            # to be reliable in allowing everything to exit cleanly.
-            eset.interrupt_then_kill(1.0)
-            cl.interrupt_then_kill(1.0)
-            reactor.callLater(2.0, reactor.stop)
-        signal.signal(signal.SIGINT,shutdown)
-        d = eset.start()
-        return d
-    config = kernel_config_manager.get_config_obj()
-    furl_file = config['controller']['engine_furl_file']
-    dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
-    dstart.addErrback(_err_and_stop)
-
-
-def main_pbs(args):
-    cont_args = []
-    cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
-    
-    # Check security settings before proceeding
-    if not check_security(args, cont_args):
-        return
-    
-    # See if we are reusing FURL files
-    if not check_reuse(args, cont_args):
-        return
-    
-    cl = ControllerLauncher(extra_args=cont_args)
-    dstart = cl.start()
-    def start_engines(r):
-        pbs_set =  PBSEngineSet(args.pbsscript)
-        def shutdown(signum, frame):
-            log.msg('Stopping pbs cluster')
-            d = pbs_set.kill()
-            d.addBoth(lambda _: cl.interrupt_then_kill(1.0))
-            d.addBoth(lambda _: reactor.callLater(2.0, reactor.stop))
-        signal.signal(signal.SIGINT,shutdown)
-        d = pbs_set.start(args.n)
-        return d
-    config = kernel_config_manager.get_config_obj()
-    furl_file = config['controller']['engine_furl_file']
-    dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
-    dstart.addErrback(_err_and_stop)
-
-
-def main_ssh(args):
-    """Start a controller on localhost and engines using ssh.
-    
-    Your clusterfile should look like::
-    
-        send_furl = False # True, if you want 
-        engines = {
-            'engine_host1' : engine_count, 
-            'engine_host2' : engine_count2
-        } 
-    """
-    clusterfile = {}
-    execfile(args.clusterfile, clusterfile)
-    if not clusterfile.has_key('send_furl'):
-        clusterfile['send_furl'] = False
-        
-    cont_args = []
-    cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
-    
-    # Check security settings before proceeding
-    if not check_security(args, cont_args):
-        return
-    
-    # See if we are reusing FURL files
-    if not check_reuse(args, cont_args):
-        return
-    
-    cl = ControllerLauncher(extra_args=cont_args)
-    dstart = cl.start()
-    def start_engines(cont_pid):
-        ssh_set = SSHEngineSet(clusterfile['engines'], sshx=args.sshx)
-        def shutdown(signum, frame):
-            d = ssh_set.kill()
-            cl.interrupt_then_kill(1.0)
-            reactor.callLater(2.0, reactor.stop)
-        signal.signal(signal.SIGINT,shutdown)
-        d = ssh_set.start(clusterfile['send_furl'])
-        return d
-    config = kernel_config_manager.get_config_obj()
-    furl_file = config['controller']['engine_furl_file']
-    dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
-    dstart.addErrback(_err_and_stop)
-
-
-def get_args():
-    base_parser = argparse.ArgumentParser(add_help=False)
-    base_parser.add_argument(
-        '-r',
-        action='store_true',
-        dest='r',
-        help='try to reuse FURL files.  Use with --client-port and --engine-port'
-    )
-    base_parser.add_argument(
-        '--client-port',
-        type=int,
-        dest='client_port',
-        help='the port the controller will listen on for client connections',
-        default=0
-    )
-    base_parser.add_argument(
-        '--engine-port',
-        type=int,
-        dest='engine_port',
-        help='the port the controller will listen on for engine connections',
-        default=0
-    )
-    base_parser.add_argument(
-        '-x',
-        action='store_true',
-        dest='x',
-        help='turn off client security'
-    )
-    base_parser.add_argument(
-        '-y',
-        action='store_true',
-        dest='y',
-        help='turn off engine security'
-    )
-    base_parser.add_argument(
-        "--logdir", 
-        type=str,
-        dest="logdir",
-        help="directory to put log files (default=$IPYTHONDIR/log)",
-        default=pjoin(get_ipython_dir(),'log')
-    )
-    base_parser.add_argument(
-        "-n",
-        "--num", 
-        type=int,
-        dest="n",
-        default=2,
-        help="the number of engines to start"
-    )
-    
-    parser = argparse.ArgumentParser(
-        description='IPython cluster startup.  This starts a controller and\
-        engines using various approaches.  Use the IPYTHONDIR environment\
-        variable to change your IPython directory from the default of\
-        .ipython or _ipython.  The log and security subdirectories of your\
-        IPython directory will be used by this script for log files and\
-        security files.'
-    )
-    subparsers = parser.add_subparsers(
-        help='available cluster types.  For help, do "ipcluster TYPE --help"')
-    
-    parser_local = subparsers.add_parser(
-        'local',
-        help='run a local cluster',
-        parents=[base_parser]
-    )
-    parser_local.set_defaults(func=main_local)
-    
-    parser_mpirun = subparsers.add_parser(
-        'mpirun',
-        help='run a cluster using mpirun (mpiexec also works)',
-        parents=[base_parser]
-    )
-    parser_mpirun.add_argument(
-        "--mpi",
-        type=str,
-        dest="mpi", # Don't put a default here to allow no MPI support
-        help="how to call MPI_Init (default=mpi4py)"
-    )
-    parser_mpirun.set_defaults(func=main_mpi, cmd='mpirun')
-
-    parser_mpiexec = subparsers.add_parser(
-        'mpiexec',
-        help='run a cluster using mpiexec (mpirun also works)',
-        parents=[base_parser]
-    )
-    parser_mpiexec.add_argument(
-        "--mpi",
-        type=str,
-        dest="mpi", # Don't put a default here to allow no MPI support
-        help="how to call MPI_Init (default=mpi4py)"
-    )
-    parser_mpiexec.set_defaults(func=main_mpi, cmd='mpiexec')
-    
-    parser_pbs = subparsers.add_parser(
-        'pbs', 
-        help='run a pbs cluster',
-        parents=[base_parser]
-    )
-    parser_pbs.add_argument(
-        '--pbs-script',
-        type=str, 
-        dest='pbsscript',
-        help='PBS script template',
-        default='pbs.template'
-    )
-    parser_pbs.set_defaults(func=main_pbs)
-    
-    parser_ssh = subparsers.add_parser(
-        'ssh',
-        help='run a cluster using ssh, should have ssh-keys setup',
-        parents=[base_parser]
-    )
-    parser_ssh.add_argument(
-        '--clusterfile', 
-        type=str,
-        dest='clusterfile',
-        help='python file describing the cluster',
-        default='clusterfile.py',
-    )
-    parser_ssh.add_argument(
-        '--sshx', 
-        type=str,
-        dest='sshx',
-        help='sshx launcher helper'
-    )
-    parser_ssh.set_defaults(func=main_ssh)
-    
-    args = parser.parse_args()
-    return args
-
-def main():
-    args = get_args()
-    reactor.callWhenRunning(args.func, args)
-    log.startLogging(sys.stdout)
-    reactor.run()
-
-if __name__ == '__main__':
-    main()
diff --git a/docs/Makefile b/docs/Makefile
index d7e9363..9d34c01 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -14,6 +14,8 @@ ALLSPHINXOPTS   = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SRCDIR)
 
 .PHONY: help clean html web pickle htmlhelp latex changes linkcheck api
 
+default: html
+
 help:
 	@echo "Please use \`make <target>' where <target> is one of"
 	@echo "  html      to make standalone HTML files"
diff --git a/docs/autogen_api.py b/docs/autogen_api.py
index 8885b66..3a40acc 100755
--- a/docs/autogen_api.py
+++ b/docs/autogen_api.py
@@ -34,6 +34,7 @@ if __name__ == '__main__':
                                         r'\.ipdoctest',
                                         r'\.Gnuplot',
                                         r'\.frontend\.process\.winprocess',
+                                        r'\.Shell',
                                         ]
     docwriter.write_api_docs(outdir)
     docwriter.write_index(outdir, 'gen',
diff --git a/docs/source/conf.py b/docs/source/conf.py
index ea8bdfc..bb04b47 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -182,7 +182,7 @@ latex_documents = [ ('index', 'ipython.tex', 'IPython Documentation',
 #latex_appendices = []
 
 # If false, no module index is generated.
-#latex_use_modindex = True
+latex_use_modindex = True
 
 
 # Cleanup