Skip to content
This repository

Two-process terminal frontend #708

Closed
wants to merge 23 commits into from

4 participants

Min RK Paul Ivanov Omar Andres Zapata Mesa Thomas Kluyver
Min RK
Owner

This extends #433 to use TerminalInteractiveShell/TerminalIPythonApp subclasses, so all our configuration, etc. is inherited as you would expect.

Not ready for merge because, among other things, ctrl-C crashes the kernel. Mostly posted as a starting point for someone else to take over finishing this client.

As far as I can tell, it's already quite functional as long as you never hit ctrl-C :).

and others added some commits January 29, 2011
Omar Andres Zapata Mesa basic kernelmanager and frontend wrote, support indentation but it do…
…nt run code yet
55a32de
Omar Andres Zapata Mesa working in handlers 0a7161e
Omar Andres Zapata Mesa working in tab completion 1e1f981
Omar Andres Zapata Mesa completer not working fine 83f4e6f
Omar Andres Zapata Mesa little bug fixed in kernelmanager's queues eb57363
Omar Andres Zapata Mesa raw_input captured and working fine 6051b91
Omar Andres Zapata Mesa -mworking in tab-completion 22843e0
Omar Andres Zapata Mesa tab completion is not working yet, unknow error bfc9430
Thomas Kluyver Make readline tab-completion work in two-process terminal frontend. 2e6f2ac
Omar Andres Zapata Mesa little bug fixed in pyout message print. cd21306
Omar Andres Zapata Mesa bug fixed prompt count using differents clients 79048f7
Omar Andres Zapata Mesa traceback support added be18348
Omar Andres Zapata Mesa bug fixed in prompt count after traceback cb7e461
Thomas Kluyver Replace tabs with spaces 020805c
Thomas Kluyver Separate out frontend.zmqterminal package. 127d459
Thomas Kluyver zmqterminal frontend now uses IPythonInputSplitter, and non-ascii cha…
…racters work.
22a8fe3
Thomas Kluyver Minor tidying up of zmqterminal.frontend ef40f1f
Thomas Kluyver Minor tidy up of zmqterminal.completer 8d4a68b
Thomas Kluyver Refactor and simplification of zmqterminal. 3d4b21e
Thomas Kluyver Simplify handling of messaging in zmqterminal. 3b16045
Thomas Kluyver Nicer prompt formatting in zmqterminal, and use print_function. e3f9bc7
Min RK rebased and updated to master d8d6bdf
Min RK zmqterminal subclasses TerminalInteractiveShell/IPApp
this does a lot of the boiler plate for us, so Omar's communication
code is used in place of run_cell / complete.

Now colors, prompts, indentation, cl-args, etc. are all inherited
from regular existing code.

keyboard interrupts don't work yet, the kernel just dies...
9bb377b
Paul Ivanov
Collaborator

this would be really nice to get back plain terminal ipython with the two-process connection capability. For one, it would enable Django-shell embedding to work with vim-ipython in a simlpe and straightfoward manner - which was recently requested in ivanov/vim-ipython#11 and I pointed them to this PR.

Paul Ivanov
Collaborator

closing this one, since #864 will supersede it as

Paul Ivanov ivanov closed this October 13, 2011
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 23 unique commits by 3 authors.

Aug 17, 2011
Omar Andres Zapata Mesa basic kernelmanager and frontend wrote, support indentation but it do…
…nt run code yet
55a32de
Omar Andres Zapata Mesa working in handlers 0a7161e
Omar Andres Zapata Mesa working in tab completion 1e1f981
Omar Andres Zapata Mesa completer not working fine 83f4e6f
Omar Andres Zapata Mesa little bug fixed in kernelmanager's queues eb57363
Omar Andres Zapata Mesa raw_input captured and working fine 6051b91
Omar Andres Zapata Mesa -mworking in tab-completion 22843e0
Omar Andres Zapata Mesa tab completion is not working yet, unknow error bfc9430
Thomas Kluyver Make readline tab-completion work in two-process terminal frontend. 2e6f2ac
Omar Andres Zapata Mesa little bug fixed in pyout message print. cd21306
Omar Andres Zapata Mesa bug fixed prompt count using differents clients 79048f7
Omar Andres Zapata Mesa traceback support added be18348
Omar Andres Zapata Mesa bug fixed in prompt count after traceback cb7e461
Thomas Kluyver Replace tabs with spaces 020805c
Thomas Kluyver Separate out frontend.zmqterminal package. 127d459
Thomas Kluyver zmqterminal frontend now uses IPythonInputSplitter, and non-ascii cha…
…racters work.
22a8fe3
Thomas Kluyver Minor tidying up of zmqterminal.frontend ef40f1f
Thomas Kluyver Minor tidy up of zmqterminal.completer 8d4a68b
Thomas Kluyver Refactor and simplification of zmqterminal. 3d4b21e
Thomas Kluyver Simplify handling of messaging in zmqterminal. 3b16045
Thomas Kluyver Nicer prompt formatting in zmqterminal, and use print_function. e3f9bc7
Min RK rebased and updated to master d8d6bdf
Min RK zmqterminal subclasses TerminalInteractiveShell/IPApp
this does a lot of the boiler plate for us, so Omar's communication
code is used in place of run_cell / complete.

Now colors, prompts, indentation, cl-args, etc. are all inherited
from regular existing code.

keyboard interrupts don't work yet, the kernel just dies...
9bb377b
This page is out of date. Refresh to see the latest.
3  IPython/frontend/terminal/ipapp.py
@@ -210,6 +210,9 @@ def _classes_default(self):
210 210
         kernel = ("IPython.zmq.ipkernel.IPKernelApp",
211 211
             "Start a kernel without an attached frontend."
212 212
         ),
  213
+        zmq=('IPython.frontend.zmqterminal.app.ZMQTerminalIPythonApp',
  214
+            """Launch two-process Terminal session with 0MQ."""
  215
+        ),
213 216
     ))
214 217
     
215 218
     # *do* autocreate requested profile, but don't create the config file.
0  IPython/frontend/zmqterminal/__init__.py
No changes.
161  IPython/frontend/zmqterminal/app.py
... ...
@@ -0,0 +1,161 @@
  1
+from __future__ import print_function
  2
+
  3
+import signal
  4
+import sys
  5
+import time
  6
+
  7
+from IPython.frontend.terminal.ipapp import TerminalIPythonApp
  8
+
  9
+from IPython.utils.traitlets import (
  10
+    Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
  11
+)
  12
+from IPython.zmq.ipkernel import (
  13
+    flags as ipkernel_flags,
  14
+    aliases as ipkernel_aliases,
  15
+    IPKernelApp
  16
+)
  17
+from IPython.zmq.session import Session
  18
+from IPython.zmq.zmqshell import ZMQInteractiveShell
  19
+from IPython.zmq.blockingkernelmanager import BlockingKernelManager
  20
+from IPython.zmq.ipkernel import (
  21
+    flags as ipkernel_flags,
  22
+    aliases as ipkernel_aliases,
  23
+    IPKernelApp
  24
+)
  25
+from IPython.frontend.zmqterminal.interactiveshell import ZMQTerminalInteractiveShell
  26
+
  27
+#-----------------------------------------------------------------------------
  28
+# Network Constants
  29
+#-----------------------------------------------------------------------------
  30
+
  31
+from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
  32
+
  33
+#-----------------------------------------------------------------------------
  34
+# Flags and Aliases
  35
+#-----------------------------------------------------------------------------
  36
+
  37
+
  38
+flags = dict(ipkernel_flags)
  39
+frontend_flags = {
  40
+    'existing' : ({'ZMQTerminalIPythonApp' : {'existing' : True}},
  41
+            "Connect to an existing kernel."),
  42
+}
  43
+flags.update(frontend_flags)
  44
+# the flags that are specific to the frontend
  45
+# these must be scrubbed before being passed to the kernel,
  46
+# or it will raise an error on unrecognized flags
  47
+frontend_flags = frontend_flags.keys()
  48
+
  49
+aliases = dict(ipkernel_aliases)
  50
+
  51
+frontend_aliases = dict(
  52
+    hb = 'ZMQTerminalIPythonApp.hb_port',
  53
+    shell = 'ZMQTerminalIPythonApp.shell_port',
  54
+    iopub = 'ZMQTerminalIPythonApp.iopub_port',
  55
+    stdin = 'ZMQTerminalIPythonApp.stdin_port',
  56
+    ip = 'ZMQTerminalIPythonApp.ip',
  57
+)
  58
+aliases.update(frontend_aliases)
  59
+# also scrub aliases from the frontend
  60
+frontend_flags.extend(frontend_aliases.keys())
  61
+
  62
+#-----------------------------------------------------------------------------
  63
+# Classes
  64
+#-----------------------------------------------------------------------------
  65
+
  66
+
  67
+class ZMQTerminalIPythonApp(TerminalIPythonApp):
  68
+    """Start a terminal frontend to the IPython zmq kernel."""
  69
+    
  70
+    kernel_argv = List(Unicode)
  71
+    flags = Dict(flags)
  72
+    aliases = Dict(aliases)
  73
+    classes = List([IPKernelApp, ZMQTerminalInteractiveShell])
  74
+    
  75
+    # connection info:
  76
+    ip = Unicode(LOCALHOST, config=True,
  77
+        help="""Set the kernel\'s IP address [default localhost].
  78
+        If the IP address is something other than localhost, then
  79
+        Consoles on other machines will be able to connect
  80
+        to the Kernel, so be careful!"""
  81
+    )
  82
+    pure = False
  83
+    hb_port = Int(0, config=True,
  84
+        help="set the heartbeat port [default: random]")
  85
+    shell_port = Int(0, config=True,
  86
+        help="set the shell (XREP) port [default: random]")
  87
+    iopub_port = Int(0, config=True,
  88
+        help="set the iopub (PUB) port [default: random]")
  89
+    stdin_port = Int(0, config=True,
  90
+        help="set the stdin (XREQ) port [default: random]")
  91
+
  92
+    existing = CBool(False, config=True,
  93
+        help="Whether to connect to an already running Kernel.")
  94
+
  95
+    # from qtconsoleapp:
  96
+    def parse_command_line(self, argv=None):
  97
+        super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
  98
+        if argv is None:
  99
+            argv = sys.argv[1:]
  100
+
  101
+        self.kernel_argv = list(argv) # copy
  102
+        # kernel should inherit default config file from frontend
  103
+        self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
  104
+        # scrub frontend-specific flags
  105
+        for a in argv:
  106
+            
  107
+            if a.startswith('-'):
  108
+                key = a.lstrip('-').split('=')[0]
  109
+                if key in frontend_flags:
  110
+                    self.kernel_argv.remove(a)
  111
+    
  112
+    def init_kernel_manager(self):
  113
+        """init kernel manager (from qtconsole)"""
  114
+        # Don't let Qt or ZMQ swallow KeyboardInterupts.
  115
+        # signal.signal(signal.SIGINT, signal.SIG_DFL)
  116
+
  117
+        # Create a KernelManager and start a kernel.
  118
+        self.kernel_manager = BlockingKernelManager(
  119
+                                shell_address=(self.ip, self.shell_port),
  120
+                                sub_address=(self.ip, self.iopub_port),
  121
+                                stdin_address=(self.ip, self.stdin_port),
  122
+                                hb_address=(self.ip, self.hb_port),
  123
+                                config=self.config
  124
+        )
  125
+        # start the kernel
  126
+        if not self.existing:
  127
+            kwargs = dict(ip=self.ip, ipython=not self.pure)
  128
+            kwargs['extra_arguments'] = self.kernel_argv
  129
+            self.kernel_manager.start_kernel(**kwargs)
  130
+            # wait for kernel to start
  131
+            time.sleep(0.5)
  132
+        self.kernel_manager.start_channels()
  133
+        # relay sigint to kernel
  134
+        signal.signal(signal.SIGINT, self.handle_sigint)
  135
+
  136
+    def init_shell(self):
  137
+        self.init_kernel_manager()
  138
+        self.shell = ZMQTerminalInteractiveShell.instance(config=self.config,
  139
+                        display_banner=False, profile_dir=self.profile_dir,
  140
+                        ipython_dir=self.ipython_dir, kernel_manager=self.kernel_manager)
  141
+    
  142
+    def handle_sigint(self, *args):
  143
+        # FIXME: this doesn't work, the kernel just dies every time
  144
+        self.shell.write('KeyboardInterrupt\n')
  145
+        self.kernel_manager.interrupt_kernel()
  146
+    
  147
+    def init_code(self):
  148
+        # no-op in the frontend, code gets run in the backend
  149
+        pass
  150
+        
  151
+
  152
+def launch_new_instance():
  153
+    """Create and run a full blown IPython instance"""
  154
+    app = ZMQTerminalIPythonApp.instance()
  155
+    app.initialize()
  156
+    app.start()
  157
+
  158
+
  159
+if __name__ == '__main__':
  160
+    launch_new_instance()
  161
+
44  IPython/frontend/zmqterminal/completer.py
... ...
@@ -0,0 +1,44 @@
  1
+# -*- coding: utf-8 -*-
  2
+import readline
  3
+from Queue import Empty
  4
+
  5
+class ZMQCompleter(object):
  6
+    """Client-side completion machinery.
  7
+
  8
+    How it works: self.complete will be called multiple times, with
  9
+    state=0,1,2,... When state=0 it should compute ALL the completion matches,
  10
+    and then return them for each value of state."""
  11
+    
  12
+    def __init__(self, shell, km):
  13
+        self.shell = shell
  14
+        self.km =  km
  15
+        self.matches = []
  16
+        
  17
+    def complete_request(self,text):
  18
+        line = readline.get_line_buffer()
  19
+        cursor_pos = readline.get_endidx()
  20
+        
  21
+        # send completion request to kernel
  22
+        # Give the kernel up to 0.5s to respond
  23
+        msg_id = self.km.shell_channel.complete(text=text, line=line,
  24
+                                                        cursor_pos=cursor_pos)
  25
+        
  26
+        msg = self.km.shell_channel.get_msg(timeout=0.5)
  27
+        if msg['parent_header']['msg_id'] == msg_id:
  28
+            return msg["content"]["matches"]
  29
+        return []
  30
+    
  31
+    def rlcomplete(self, text, state):
  32
+        if state == 0:
  33
+            try:
  34
+                self.matches = self.complete_request(text)
  35
+            except Empty:
  36
+                print('WARNING: Kernel timeout on tab completion.')
  37
+        
  38
+        try:
  39
+            return self.matches[state]
  40
+        except IndexError:
  41
+            return None
  42
+    
  43
+    def complete(self, text, line, cursor_pos=None):
  44
+        return self.rlcomplete(text, 0)
253  IPython/frontend/zmqterminal/frontend.py
... ...
@@ -0,0 +1,253 @@
  1
+# -*- coding: utf-8 -*-
  2
+"""Frontend of ipython working with python-zmq
  3
+
  4
+Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
  5
+
  6
+For more details, see the ipython-zmq design
  7
+"""
  8
+#-----------------------------------------------------------------------------
  9
+# Copyright (C) 2010 The IPython Development Team
  10
+#
  11
+# Distributed under the terms of the BSD License. The full license is in
  12
+# the file COPYING, distributed as part of this software.
  13
+#-----------------------------------------------------------------------------
  14
+
  15
+#-----------------------------------------------------------------------------
  16
+# Imports
  17
+#-----------------------------------------------------------------------------
  18
+from __future__ import print_function
  19
+
  20
+import __builtin__
  21
+import sys
  22
+import os
  23
+from Queue import Empty
  24
+import readline
  25
+import rlcompleter
  26
+
  27
+#-----------------------------------------------------------------------------
  28
+# Imports from ipython
  29
+#-----------------------------------------------------------------------------
  30
+from IPython.external.argparse import ArgumentParser
  31
+from IPython.core.inputsplitter import IPythonInputSplitter
  32
+from IPython.zmq.blockingkernelmanager import BlockingKernelManager as KernelManager
  33
+from IPython.frontend.zmqterminal.completer import ClientCompleter2p 
  34
+
  35
+#-----------------------------------------------------------------------------
  36
+# Network Constants
  37
+#-----------------------------------------------------------------------------
  38
+
  39
+from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
  40
+class Frontend(object):
  41
+    """This class is a simple frontend to ipython-zmq 
  42
+       
  43
+      NOTE: this class uses kernelmanager to manipulate sockets
  44
+      
  45
+      Parameters:
  46
+      -----------
  47
+      kernelmanager : object
  48
+        instantiated object from class KernelManager in module kernelmanager
  49
+        
  50
+    """
  51
+
  52
+    def __init__(self, kernelmanager):
  53
+       self.km = kernelmanager
  54
+       self.session_id = self.km.session.session
  55
+       self.completer = ClientCompleter2p(self, self.km)
  56
+       readline.parse_and_bind("tab: complete")
  57
+       readline.parse_and_bind('set show-all-if-ambiguous on')
  58
+       readline.set_completer(self.completer.complete)
  59
+
  60
+       history_path = os.path.expanduser('~/.ipython/history')
  61
+       if os.path.isfile(history_path):
  62
+           rlcompleter.readline.read_history_file(history_path)
  63
+       else:
  64
+           print("history file cannot be read.")   
  65
+
  66
+       self.messages = {}
  67
+
  68
+       self._splitter = IPythonInputSplitter()
  69
+       self.code = ""
  70
+ 
  71
+       self.prompt_count = 0
  72
+       self._get_initial_prompt()
  73
+ 
  74
+    def _get_initial_prompt(self):
  75
+       self._execute('', hidden=True)
  76
+   
  77
+    def interact(self):
  78
+       """Gets input from console using inputsplitter, then
  79
+       while you enter code it can indent and set index id to any input
  80
+       """    
  81
+       
  82
+       try:
  83
+           print()
  84
+           self._splitter.push(raw_input('In [%i]: '%self.prompt_count+self.code))
  85
+           while self._splitter.push_accepts_more():
  86
+              self.code = raw_input('.....: '+' '*self._splitter.indent_spaces)
  87
+              self._splitter.push(' '*self._splitter.indent_spaces+self.code)
  88
+           self._execute(self._splitter.source,False)
  89
+           self._splitter.reset()
  90
+       except KeyboardInterrupt:
  91
+           print('\nKeyboardInterrupt\n')
  92
+           pass
  93
+          
  94
+       
  95
+    def start(self):
  96
+       """Start the interaction loop, calling the .interact() method for each
  97
+       input cell.
  98
+       """
  99
+       while True:
  100
+           try:
  101
+               self.interact()
  102
+           except KeyboardInterrupt:
  103
+                print('\nKeyboardInterrupt\n')
  104
+                pass
  105
+           except EOFError:
  106
+               answer = ''    
  107
+               while True:
  108
+                   answer = raw_input('\nDo you really want to exit ([y]/n)?')
  109
+                   if answer == 'y' or answer == '' :
  110
+                       self.km.shutdown_kernel()
  111
+                       sys.exit()
  112
+                   elif answer == 'n':
  113
+                       break
  114
+
  115
+    def _execute(self, source, hidden = True):
  116
+        """ Execute 'source'. If 'hidden', do not show any output.
  117
+
  118
+        See parent class :meth:`execute` docstring for full details.
  119
+        """
  120
+        msg_id = self.km.shell_channel.execute(source, hidden)
  121
+        while not self.km.shell_channel.msg_ready():
  122
+            try:
  123
+                self.handle_stdin_channel(timeout=0.1)
  124
+            except Empty:
  125
+                pass
  126
+        self.handle_execute_reply(msg_id)
  127
+
  128
+    def handle_execute_reply(self, msg_id):
  129
+        msg = self.km.shell_channel.get_msg()
  130
+        if msg["parent_header"]["msg_id"] == msg_id:
  131
+            if msg["content"]["status"] == 'ok' :
  132
+                self.handle_sub_channel()
  133
+               
  134
+            elif msg["content"]["status"] == 'error':
  135
+                for frame in msg["content"]["traceback"]:
  136
+                    print(frame, file=sys.stderr)
  137
+            
  138
+            self.prompt_count = msg["content"]["execution_count"] + 1
  139
+
  140
+
  141
+    def handle_sub_channel(self):
  142
+       """ Method to procces subscribe channel's messages
  143
+
  144
+           This method reads a message and processes the content in different
  145
+           outputs like stdout, stderr, pyout and status
  146
+
  147
+           Arguments:
  148
+           sub_msg:  message receive from kernel in the sub socket channel
  149
+                     capture by kernel manager.
  150
+       """
  151
+       while self.km.sub_channel.msg_ready():
  152
+           sub_msg = self.km.sub_channel.get_msg()
  153
+           if self.session_id == sub_msg['parent_header']['session']:
  154
+               if sub_msg['msg_type'] == 'status' :
  155
+                    if sub_msg["content"]["execution_state"] == "busy" :
  156
+                        pass
  157
+
  158
+               elif sub_msg['msg_type'] == 'stream' :
  159
+                  if sub_msg["content"]["name"] == "stdout":
  160
+                    print(sub_msg["content"]["data"], file=sys.stdout, end="")
  161
+                    sys.stdout.flush()
  162
+                  elif sub_msg["content"]["name"] == "stderr" :
  163
+                    print(sub_msg["content"]["data"], file=sys.stderr, end="")
  164
+                    sys.stderr.flush()
  165
+                
  166
+               elif sub_msg['msg_type'] == 'pyout' :
  167
+                    print("Out[%i]:"%sub_msg["content"]["execution_count"],
  168
+                            sub_msg["content"]["data"]["text/plain"],
  169
+                            file=sys.stdout)
  170
+                    sys.stdout.flush()
  171
+
  172
+    def handle_stdin_channel(self, timeout=0.1):
  173
+        """ Method to capture raw_input
  174
+        """
  175
+        msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
  176
+        if self.session_id == msg_rep["parent_header"]["session"] :
  177
+            raw_data = raw_input(msg_rep["content"]["prompt"])
  178
+            self.km.stdin_channel.input(raw_data)
  179
+             
  180
+       
  181
+
  182
+       
  183
+def start_frontend():
  184
+    """ Entry point for application.
  185
+    
  186
+    """
  187
+    # Parse command line arguments.
  188
+    parser = ArgumentParser()
  189
+    kgroup = parser.add_argument_group('kernel options')
  190
+    kgroup.add_argument('-e', '--existing', action='store_true',
  191
+                        help='connect to an existing kernel')
  192
+    kgroup.add_argument('--ip', type=str, default=LOCALHOST,
  193
+                        help=\
  194
+            "set the kernel\'s IP address [default localhost].\
  195
+            If the IP address is something other than localhost, then \
  196
+            Consoles on other machines will be able to connect\
  197
+            to the Kernel, so be careful!")
  198
+    kgroup.add_argument('--shell', type=int, metavar='PORT', default=0,
  199
+                        help='set the XREQ channel port [default random]')
  200
+    kgroup.add_argument('--iopub', type=int, metavar='PORT', default=0,
  201
+                        help='set the SUB channel port [default random]')
  202
+    kgroup.add_argument('--stdin', type=int, metavar='PORT', default=0,
  203
+                        help='set the REP channel port [default random]')
  204
+    kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
  205
+                        help='set the heartbeat port [default random]')
  206
+
  207
+    egroup = kgroup.add_mutually_exclusive_group()
  208
+    egroup.add_argument('--pure', action='store_true', help = \
  209
+                        'use a pure Python kernel instead of an IPython kernel')
  210
+    egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?', 
  211
+                       const='auto', help = \
  212
+        "Pre-load matplotlib and numpy for interactive use. If GUI is not \
  213
+         given, the GUI backend is matplotlib's, otherwise use one of: \
  214
+         ['tk', 'gtk', 'qt', 'wx', 'inline'].")
  215
+    egroup.add_argument('--colors', type=str,
  216
+                        help="Set the color scheme (LightBG,Linux,NoColor). This is guessed\
  217
+                        based on the pygments style if not set.")
  218
+
  219
+    args = parser.parse_args()
  220
+
  221
+    # parse the colors arg down to current known labels
  222
+    if args.colors:
  223
+        colors=args.colors.lower()
  224
+        if colors in ('lightbg', 'light'):
  225
+            colors='lightbg'
  226
+        elif colors in ('dark', 'linux'):
  227
+            colors='linux'
  228
+        else:
  229
+            colors='nocolor'
  230
+    else:
  231
+        colors=None
  232
+
  233
+    # Create a KernelManager and start a kernel.
  234
+    kernel_manager = KernelManager(shell_address=(args.ip, args.shell),
  235
+                                     sub_address=(args.ip, args.iopub),
  236
+                                     stdin_address=(args.ip, args.stdin),
  237
+                                     hb_address=(args.ip, args.hb))
  238
+    if not args.existing:
  239
+        # if not args.ip in LOCAL_IPS+ALL_ALIAS:
  240
+        #     raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
  241
+
  242
+        kwargs = dict(ip=args.ip, ipython=True)
  243
+        kernel_manager.start_kernel(**kwargs)
  244
+
  245
+    
  246
+    kernel_manager.start_channels()
  247
+ 
  248
+    frontend=Frontend(kernel_manager)
  249
+    return frontend
  250
+
  251
+if __name__ == "__main__" :
  252
+     frontend=start_frontend()
  253
+     frontend.start()
252  IPython/frontend/zmqterminal/interactiveshell.py
... ...
@@ -0,0 +1,252 @@
  1
+# -*- coding: utf-8 -*-
  2
+"""Frontend of ipython working with python-zmq
  3
+
  4
+Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
  5
+
  6
+For more details, see the ipython-zmq design
  7
+"""
  8
+#-----------------------------------------------------------------------------
  9
+# Copyright (C) 2011 The IPython Development Team
  10
+#
  11
+# Distributed under the terms of the BSD License. The full license is in
  12
+# the file COPYING, distributed as part of this software.
  13
+#-----------------------------------------------------------------------------
  14
+
  15
+#-----------------------------------------------------------------------------
  16
+# Imports
  17
+#-----------------------------------------------------------------------------
  18
+from __future__ import print_function
  19
+
  20
+import bdb
  21
+import sys
  22
+
  23
+from Queue import Empty
  24
+
  25
+from IPython.core.alias import AliasManager, AliasError
  26
+from IPython.utils.warn import warn, error, fatal
  27
+from IPython.utils import io
  28
+
  29
+from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
  30
+from IPython.frontend.zmqterminal.completer import ZMQCompleter
  31
+
  32
+
  33
+class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
  34
+    """A subclass of TerminalInteractiveShell that """
  35
+    
  36
+    def __init__(self, *args, **kwargs):
  37
+        self.km = kwargs.pop('kernel_manager')
  38
+        self.session_id = self.km.session.session
  39
+        super(ZMQTerminalInteractiveShell, self).__init__(*args, **kwargs)
  40
+        
  41
+    def init_completer(self):
  42
+        """Initialize the completion machinery.
  43
+
  44
+        This creates completion machinery that can be used by client code,
  45
+        either interactively in-process (typically triggered by the readline
  46
+        library), programatically (such as in test suites) or out-of-prcess
  47
+        (typically over the network by remote frontends).
  48
+        """
  49
+        from IPython.core.completerlib import (module_completer,
  50
+                                               magic_run_completer, cd_completer)
  51
+        
  52
+        self.Completer = ZMQCompleter(self, self.km)
  53
+        
  54
+
  55
+        self.set_hook('complete_command', module_completer, str_key = 'import')
  56
+        self.set_hook('complete_command', module_completer, str_key = 'from')
  57
+        self.set_hook('complete_command', magic_run_completer, str_key = '%run')
  58
+        self.set_hook('complete_command', cd_completer, str_key = '%cd')
  59
+
  60
+        # Only configure readline if we truly are using readline.  IPython can
  61
+        # do tab-completion over the network, in GUIs, etc, where readline
  62
+        # itself may be absent
  63
+        if self.has_readline:
  64
+            self.set_readline_completer()
  65
+    
  66
+    def run_cell(self, cell, store_history=True):
  67
+        """Run a complete IPython cell.
  68
+        
  69
+        Parameters
  70
+        ----------
  71
+        cell : str
  72
+          The code (including IPython code such as %magic functions) to run.
  73
+        store_history : bool
  74
+          If True, the raw and translated cell will be stored in IPython's
  75
+          history. For user code calling back into IPython's machinery, this
  76
+          should be set to False.
  77
+        """
  78
+        if (not cell) or cell.isspace():
  79
+            return
  80
+
  81
+        # shell_channel.execute takes 'hidden', which is the inverse of store_hist
  82
+        msg_id = self.km.shell_channel.execute(cell, not store_history)
  83
+        while not self.km.shell_channel.msg_ready():
  84
+            try:
  85
+                self.handle_stdin_request(timeout=0.05)
  86
+            except Empty:
  87
+                pass
  88
+        self.handle_execute_reply(msg_id)
  89
+
  90
+    #-----------------
  91
+    # message handlers
  92
+    #-----------------
  93
+
  94
+    def handle_execute_reply(self, msg_id):
  95
+        msg = self.km.shell_channel.get_msg()
  96
+        if msg["parent_header"]["msg_id"] == msg_id:
  97
+            if msg["content"]["status"] == 'ok' :
  98
+                self.handle_iopub()
  99
+               
  100
+            elif msg["content"]["status"] == 'error':
  101
+                for frame in msg["content"]["traceback"]:
  102
+                    print(frame, file=io.stderr)
  103
+            
  104
+            self.execution_count = msg["content"]["execution_count"] + 1
  105
+
  106
+
  107
+    def handle_iopub(self):
  108
+        """ Method to procces subscribe channel's messages
  109
+
  110
+           This method reads a message and processes the content in different
  111
+           outputs like stdout, stderr, pyout and status
  112
+
  113
+           Arguments:
  114
+           sub_msg:  message receive from kernel in the sub socket channel
  115
+                     capture by kernel manager.
  116
+        """
  117
+        while self.km.sub_channel.msg_ready():
  118
+            sub_msg = self.km.sub_channel.get_msg()
  119
+            msg_type = sub_msg['header']['msg_type']
  120
+            if self.session_id == sub_msg['parent_header']['session']:
  121
+                if msg_type == 'status' :
  122
+                    if sub_msg["content"]["execution_state"] == "busy" :
  123
+                        pass
  124
+
  125
+                elif msg_type == 'stream' :
  126
+                    if sub_msg["content"]["name"] == "stdout":
  127
+                        print(sub_msg["content"]["data"], file=io.stdout, end="")
  128
+                        io.stdout.flush()
  129
+                    elif sub_msg["content"]["name"] == "stderr" :
  130
+                        print(sub_msg["content"]["data"], file=io.stderr, end="")
  131
+                        io.stderr.flush()
  132
+                
  133
+                elif msg_type == 'pyout':
  134
+                    format_dict = sub_msg["content"]["data"]
  135
+                    # taken from DisplayHook.__call__:
  136
+                    hook = self.displayhook
  137
+                    hook.start_displayhook()
  138
+                    hook.write_output_prompt()
  139
+                    hook.write_format_data(format_dict)
  140
+                    hook.log_output(format_dict)
  141
+                    hook.finish_displayhook()
  142
+
  143
+    def handle_stdin_request(self, timeout=0.1):
  144
+        """ Method to capture raw_input
  145
+        """
  146
+        msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
  147
+        if self.session_id == msg_rep["parent_header"]["session"] :
  148
+            raw_data = raw_input(msg_rep["content"]["prompt"])
  149
+            self.km.stdin_channel.input(raw_data)
  150
+
  151
+    def mainloop(self, display_banner=False):
  152
+        while True:
  153
+            try:
  154
+                self.interact(display_banner=display_banner)
  155
+                #self.interact_with_readline()                
  156
+                # XXX for testing of a readline-decoupled repl loop, call
  157
+                # interact_with_readline above
  158
+                break
  159
+            except KeyboardInterrupt:
  160
+                # this should not be necessary, but KeyboardInterrupt
  161
+                # handling seems rather unpredictable...
  162
+                self.write("\nKeyboardInterrupt in interact()\n")
  163
+    
  164
+    def interact(self, display_banner=None):
  165
+        """Closely emulate the interactive Python console."""
  166
+
  167
+        # batch run -> do not interact
  168
+        if self.exit_now:
  169
+            return
  170
+
  171
+        if display_banner is None:
  172
+            display_banner = self.display_banner
  173
+        
  174
+        if isinstance(display_banner, basestring):
  175
+            self.show_banner(display_banner)
  176
+        elif display_banner:
  177
+            self.show_banner()
  178
+
  179
+        more = False
  180
+        
  181
+        if self.has_readline:
  182
+            self.readline_startup_hook(self.pre_readline)
  183
+        # exit_now is set by a call to %Exit or %Quit, through the
  184
+        # ask_exit callback.
  185
+
  186
+        while not self.exit_now:
  187
+            if not self.km.is_alive:
  188
+                ans = self.raw_input("kernel died, restart (y/n)?")
  189
+                if not ans.lower().startswith('n'):
  190
+                    self.km.restart_kernel(True)
  191
+                else:
  192
+                    self.exit_now=True
  193
+                continue
  194
+            self.hooks.pre_prompt_hook()
  195
+            if more:
  196
+                try:
  197
+                    prompt = self.hooks.generate_prompt(True)
  198
+                except:
  199
+                    self.showtraceback()
  200
+                if self.autoindent:
  201
+                    self.rl_do_indent = True
  202
+                    
  203
+            else:
  204
+                try:
  205
+                    prompt = self.hooks.generate_prompt(False)
  206
+                except:
  207
+                    self.showtraceback()
  208
+            try:
  209
+                line = self.raw_input(prompt)
  210
+                if self.exit_now:
  211
+                    # quick exit on sys.std[in|out] close
  212
+                    break
  213
+                if self.autoindent:
  214
+                    self.rl_do_indent = False
  215
+                    
  216
+            except KeyboardInterrupt:
  217
+                #double-guard against keyboardinterrupts during kbdint handling
  218
+                try:
  219
+                    self.write('\nKeyboardInterrupt\n')
  220
+                    self.input_splitter.reset()
  221
+                    more = False
  222
+                except KeyboardInterrupt:
  223
+                    pass
  224
+            except EOFError:
  225
+                if self.autoindent:
  226
+                    self.rl_do_indent = False
  227
+                    if self.has_readline:
  228
+                        self.readline_startup_hook(None)
  229
+                self.write('\n')
  230
+                self.exit()
  231
+            except bdb.BdbQuit:
  232
+                warn('The Python debugger has exited with a BdbQuit exception.\n'
  233
+                     'Because of how pdb handles the stack, it is impossible\n'
  234
+                     'for IPython to properly format this particular exception.\n'
  235
+                     'IPython will resume normal operation.')
  236
+            except:
  237
+                # exceptions here are VERY RARE, but they can be triggered
  238
+                # asynchronously by signal handlers, for example.
  239
+                self.showtraceback()
  240
+            else:
  241
+                self.input_splitter.push(line)
  242
+                more = self.input_splitter.push_accepts_more()
  243
+                if (self.SyntaxTB.last_syntax_error and
  244
+                    self.autoedit_syntax):
  245
+                    self.edit_syntax_error()
  246
+                if not more:
  247
+                    source_raw = self.input_splitter.source_reset()
  248
+                    self.run_cell(source_raw)
  249
+                
  250
+
  251
+        # Turn off the exit flag, so the mainloop can be restarted if desired
  252
+        self.exit_now = False
25  IPython/zmq/blockingkernelmanager.py
@@ -16,6 +16,7 @@
16 16
 
17 17
 # Stdlib
18 18
 from Queue import Queue, Empty
  19
+from threading import Event
19 20
 
20 21
 # Our own
21 22
 from IPython.utils import io
@@ -104,9 +105,31 @@ def get_msgs(self):
104 105
 
105 106
 class BlockingStdInSocketChannel(StdInSocketChannel):
106 107
     
  108
+    def __init__(self, context, session, address=None):
  109
+        super(BlockingStdInSocketChannel, self).__init__(context, session, address)
  110
+        self._in_queue = Queue()
  111
+        
107 112
     def call_handlers(self, msg):
108 113
         #io.rprint('[[Rep]]', msg) # dbg
109  
-        pass
  114
+        self._in_queue.put(msg)
  115
+        
  116
+    def get_msg(self, block=True, timeout=None):
  117
+        "Gets a message if there is one that is ready."
  118
+        return self._in_queue.get(block, timeout)
  119
+        
  120
+    def get_msgs(self):
  121
+        """Get all messages that are currently ready."""
  122
+        msgs = []
  123
+        while True:
  124
+            try:
  125
+                msgs.append(self.get_msg(block=False))
  126
+            except Empty:
  127
+                break
  128
+        return msgs
  129
+    
  130
+    def msg_ready(self):
  131
+        "Is there a message that has been received?"
  132
+        return not self._in_queue.empty()
110 133
 
111 134
 
112 135
 class BlockingHBSocketChannel(HBSocketChannel):
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.