コード例 #1
0
ファイル: test_traitlets.py プロジェクト: wyorgb/ipython
class TCPAddressTrait(HasTraits):

    value = TCPAddress()
コード例 #2
0
class KernelManager(HasTraits):
    """ Manages a kernel for a frontend.

    The SUB channel is for the frontend to receive messages published by the
    kernel.
        
    The REQ channel is for the frontend to make requests of the kernel.
    
    The REP channel is for the kernel to request stdin (raw_input) from the
    frontend.
    """
    # The PyZMQ Context to use for communication with the kernel.
    context = Instance(zmq.Context, (), {})

    # The Session to use for communication with the kernel.
    session = Instance(Session, (), {})

    # The kernel process with which the KernelManager is communicating.
    kernel = Instance(Popen)

    # The addresses for the communication channels.
    xreq_address = TCPAddress((LOCALHOST, 0))
    sub_address = TCPAddress((LOCALHOST, 0))
    rep_address = TCPAddress((LOCALHOST, 0))
    hb_address = TCPAddress((LOCALHOST, 0))

    # The classes to use for the various channels.
    xreq_channel_class = Type(XReqSocketChannel)
    sub_channel_class = Type(SubSocketChannel)
    rep_channel_class = Type(RepSocketChannel)
    hb_channel_class = Type(HBSocketChannel)

    # Protected traits.
    _launch_args = Any
    _xreq_channel = Any
    _sub_channel = Any
    _rep_channel = Any
    _hb_channel = Any

    def __init__(self, **kwargs):
        super(KernelManager, self).__init__(**kwargs)
        # Uncomment this to try closing the context.
        # atexit.register(self.context.close)

    #--------------------------------------------------------------------------
    # Channel management methods:
    #--------------------------------------------------------------------------

    def start_channels(self, xreq=True, sub=True, rep=True, hb=True):
        """Starts the channels for this kernel.

        This will create the channels if they do not exist and then start
        them. If port numbers of 0 are being used (random ports) then you
        must first call :method:`start_kernel`. If the channels have been
        stopped and you call this, :class:`RuntimeError` will be raised.
        """
        if xreq:
            self.xreq_channel.start()
        if sub:
            self.sub_channel.start()
        if rep:
            self.rep_channel.start()
        if hb:
            self.hb_channel.start()

    def stop_channels(self):
        """Stops all the running channels for this kernel.
        """
        if self.xreq_channel.is_alive():
            self.xreq_channel.stop()
        if self.sub_channel.is_alive():
            self.sub_channel.stop()
        if self.rep_channel.is_alive():
            self.rep_channel.stop()
        if self.hb_channel.is_alive():
            self.hb_channel.stop()

    @property
    def channels_running(self):
        """Are any of the channels created and running?"""
        return (self.xreq_channel.is_alive() or self.sub_channel.is_alive()
                or self.rep_channel.is_alive() or self.hb_channel.is_alive())

    #--------------------------------------------------------------------------
    # Kernel process management methods:
    #--------------------------------------------------------------------------

    def start_kernel(self, **kw):
        """Starts a kernel process and configures the manager to use it.

        If random ports (port=0) are being used, this method must be called
        before the channels are created.

        Parameters:
        -----------
        ipython : bool, optional (default True)
             Whether to use an IPython kernel instead of a plain Python kernel.
        """
        xreq, sub, rep, hb = self.xreq_address, self.sub_address, \
            self.rep_address, self.hb_address
        if xreq[0] not in LOCAL_IPS or sub[0] not in LOCAL_IPS or \
                rep[0] not in LOCAL_IPS or hb[0] not in LOCAL_IPS:
            raise RuntimeError(
                "Can only launch a kernel on a local interface. "
                "Make sure that the '*_address' attributes are "
                "configured properly. "
                "Currently valid addresses are: %s" % LOCAL_IPS)

        self._launch_args = kw.copy()
        if kw.pop('ipython', True):
            from ipkernel import launch_kernel
        else:
            from pykernel import launch_kernel
        self.kernel, xrep, pub, req, _hb = launch_kernel(xrep_port=xreq[1],
                                                         pub_port=sub[1],
                                                         req_port=rep[1],
                                                         hb_port=hb[1],
                                                         **kw)
        self.xreq_address = (xreq[0], xrep)
        self.sub_address = (sub[0], pub)
        self.rep_address = (rep[0], req)
        self.hb_address = (hb[0], _hb)

    def shutdown_kernel(self, restart=False):
        """ Attempts to the stop the kernel process cleanly. If the kernel
        cannot be stopped, it is killed, if possible.
        """
        # FIXME: Shutdown does not work on Windows due to ZMQ errors!
        if sys.platform == 'win32':
            self.kill_kernel()
            return

        # Pause the heart beat channel if it exists.
        if self._hb_channel is not None:
            self._hb_channel.pause()

        # Don't send any additional kernel kill messages immediately, to give
        # the kernel a chance to properly execute shutdown actions. Wait for at
        # most 1s, checking every 0.1s.
        self.xreq_channel.shutdown(restart=restart)
        for i in range(10):
            if self.is_alive:
                time.sleep(0.1)
            else:
                break
        else:
            # OK, we've waited long enough.
            if self.has_kernel:
                self.kill_kernel()

    def restart_kernel(self, now=False):
        """Restarts a kernel with the same arguments that were used to launch
        it. If the old kernel was launched with random ports, the same ports
        will be used for the new kernel.

        Parameters
        ----------
        now : bool, optional
          If True, the kernel is forcefully restarted *immediately*, without
          having a chance to do any cleanup action.  Otherwise the kernel is
          given 1s to clean up before a forceful restart is issued.

          In all cases the kernel is restarted, the only difference is whether
          it is given a chance to perform a clean shutdown or not.
        """
        if self._launch_args is None:
            raise RuntimeError("Cannot restart the kernel. "
                               "No previous call to 'start_kernel'.")
        else:
            if self.has_kernel:
                if now:
                    self.kill_kernel()
                else:
                    self.shutdown_kernel(restart=True)
            self.start_kernel(**self._launch_args)

            # FIXME: Messages get dropped in Windows due to probable ZMQ bug
            # unless there is some delay here.
            if sys.platform == 'win32':
                time.sleep(0.2)

    @property
    def has_kernel(self):
        """Returns whether a kernel process has been specified for the kernel
        manager.
        """
        return self.kernel is not None

    def kill_kernel(self):
        """ Kill the running kernel. """
        if self.has_kernel:
            # Pause the heart beat channel if it exists.
            if self._hb_channel is not None:
                self._hb_channel.pause()

            # Attempt to kill the kernel.
            try:
                self.kernel.kill()
            except OSError, e:
                # In Windows, we will get an Access Denied error if the process
                # has already terminated. Ignore it.
                if not (sys.platform == 'win32' and e.winerror == 5):
                    raise
            self.kernel = None
        else: