Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Implement EmbeddedKernel.

  • Loading branch information...
commit 8d53a55f0c6e62b43ab8adf6ef63944715ed6910 1 parent e367f3e
Evan Patterson authored September 10, 2012
111  IPython/embedded/ipkernel.py
@@ -11,7 +11,15 @@
11 11
 # Imports
12 12
 #-----------------------------------------------------------------------------
13 13
 
  14
+# Standard library imports
  15
+from contextlib import contextmanager
  16
+import logging
  17
+import sys
  18
+
14 19
 # Local imports.
  20
+from IPython.embedded.socket import DummySocket
  21
+from IPython.utils.jsonutil import json_clean
  22
+from IPython.utils.traitlets import Any, Instance, List
15 23
 from IPython.zmq.ipkernel import Kernel
16 24
 
17 25
 #-----------------------------------------------------------------------------
@@ -19,4 +27,105 @@
19 27
 #-----------------------------------------------------------------------------
20 28
 
21 29
 class EmbeddedKernel(Kernel):
22  
-    pass
  30
+
  31
+    #-------------------------------------------------------------------------
  32
+    # EmbeddedKernel interface
  33
+    #-------------------------------------------------------------------------
  34
+
  35
+    frontends = List(
  36
+        Instance('IPython.embedded.kernelmanager.EmbeddedKernelManager'))
  37
+
  38
+    raw_input_str = Any()
  39
+    stdout = Any()
  40
+    stderr = Any()
  41
+
  42
+    #-------------------------------------------------------------------------
  43
+    # Kernel interface
  44
+    #-------------------------------------------------------------------------
  45
+
  46
+    shell_streams = List()
  47
+    control_stream = Any()
  48
+    iopub_socket = Instance(DummySocket, ())
  49
+    stdin_socket = Instance(DummySocket, ())
  50
+
  51
+    def __init__(self, **traits):
  52
+        # When an InteractiveShell is instantiated by our base class, it binds
  53
+        # the current values of sys.stdout and sys.stderr.
  54
+        with self._redirected_io():
  55
+            super(EmbeddedKernel, self).__init__(**traits)
  56
+
  57
+        self.iopub_socket.on_trait_change(self._io_dispatch, 'message_sent')
  58
+
  59
+    def execute_request(self, stream, ident, parent):
  60
+        """ Override for temporary IO redirection. """
  61
+        with self._redirected_io():
  62
+            super(EmbeddedKernel, self).execute_request(stream, ident, parent)
  63
+
  64
+    def start(self):
  65
+        """ Override registration of dispatchers for streams. """
  66
+        self.shell.exit_now = False
  67
+
  68
+    def _abort_queue(self, stream):
  69
+        """ The embedded kernel don't abort requests. """
  70
+        pass
  71
+
  72
+    def _raw_input(self, prompt, ident, parent):
  73
+        # Flush output before making the request.
  74
+        self.raw_input_str = None
  75
+        sys.stderr.flush()
  76
+        sys.stdout.flush()
  77
+
  78
+        # Send the input request.
  79
+        content = json_clean(dict(prompt=prompt))
  80
+        msg = self.session.msg(u'input_request', content, parent)
  81
+        for frontend in self.frontends:
  82
+            if frontend.session.session == parent['header']['session']:
  83
+                frontend.stdin_channel.call_handlers(msg)
  84
+                break
  85
+        else:
  86
+            log.error('No frontend found for raw_input request')
  87
+            return str()
  88
+
  89
+        # Await a response.
  90
+        while self.raw_input_str is None:
  91
+            frontend.stdin_channel.process_events()
  92
+        return self.raw_input_str
  93
+
  94
+    #-------------------------------------------------------------------------
  95
+    # Protected interface
  96
+    #-------------------------------------------------------------------------
  97
+
  98
+    @contextmanager
  99
+    def _redirected_io(self):
  100
+        """ Temporarily redirect IO to the kernel.
  101
+        """
  102
+        sys_stdout, sys_stderr = sys.stdout, sys.stderr
  103
+        sys.stdout, sys.stderr = self.stdout, self.stderr
  104
+        yield
  105
+        sys.stdout, sys.stderr = sys_stdout, sys_stderr
  106
+
  107
+    #------ Trait change handlers --------------------------------------------
  108
+
  109
+    def _io_dispatch(self):
  110
+        """ Called when a message is sent to the IO socket.
  111
+        """
  112
+        ident, msg = self.session.recv(self.iopub_socket, copy=False)
  113
+        for frontend in self.frontends:
  114
+            frontend.sub_channel.call_handlers(msg)
  115
+        
  116
+    #------ Trait initializers -----------------------------------------------
  117
+
  118
+    def _log_default(self):
  119
+        return logging.getLogger(__name__)
  120
+
  121
+    def _session_default(self):
  122
+        from IPython.zmq.session import Session
  123
+        return Session(config=self.config)
  124
+
  125
+    def _stdout_default(self):
  126
+        from IPython.zmq.iostream import OutStream
  127
+        return OutStream(self.session, self.iopub_socket, u'stdout')
  128
+
  129
+    def _stderr_default(self):
  130
+        from IPython.zmq.iostream import OutStream
  131
+        return OutStream(self.session, self.iopub_socket, u'stderr')
55  IPython/embedded/kernelmanager.py
@@ -13,6 +13,7 @@
13 13
 
14 14
 # Local imports.
15 15
 from IPython.config.loader import Config
  16
+from IPython.embedded.socket import DummySocket
16 17
 from IPython.utils.traitlets import HasTraits, Any, Instance, Type
17 18
 
18 19
 #-----------------------------------------------------------------------------
@@ -77,6 +78,10 @@ class ShellEmbeddedChannel(EmbeddedChannel):
77 78
     # flag for whether execute requests should be allowed to call raw_input
78 79
     allow_stdin = True
79 80
 
  81
+    #--------------------------------------------------------------------------
  82
+    # ShellChannel interface
  83
+    #--------------------------------------------------------------------------
  84
+
80 85
     def execute(self, code, silent=False, store_history=True,
81 86
                 user_variables=[], user_expressions={}, allow_stdin=None):
82 87
         """Execute code in the kernel.
@@ -115,7 +120,15 @@ def execute(self, code, silent=False, store_history=True,
115 120
         -------
116 121
         The msg_id of the message sent.
117 122
         """
118  
-        raise NotImplementedError
  123
+        if allow_stdin is None:
  124
+            allow_stdin = self.allow_stdin
  125
+        content = dict(code=code, silent=silent, store_history=store_history,
  126
+                       user_variables=user_variables,
  127
+                       user_expressions=user_expressions,
  128
+                       allow_stdin=allow_stdin)
  129
+        msg = self.manager.session.msg('execute_request', content)
  130
+        self._dispatch_to_kernel(msg)
  131
+        return msg['header']['msg_id']
119 132
 
120 133
     def complete(self, text, line, cursor_pos, block=None):
121 134
         """Tab complete text in the kernel's namespace.
@@ -137,7 +150,10 @@ def complete(self, text, line, cursor_pos, block=None):
137 150
         -------
138 151
         The msg_id of the message sent.
139 152
         """
140  
-        raise NotImplementedError
  153
+        content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
  154
+        msg = self.manager.session.msg('complete_request', content)
  155
+        self._dispatch_to_kernel(msg)
  156
+        return msg['header']['msg_id']
141 157
 
142 158
     def object_info(self, oname, detail_level=0):
143 159
         """Get metadata information about an object.
@@ -153,7 +169,10 @@ def object_info(self, oname, detail_level=0):
153 169
         -------
154 170
         The msg_id of the message sent.
155 171
         """
156  
-        raise NotImplementedError
  172
+        content = dict(oname=oname, detail_level=detail_level)
  173
+        msg = self.manager.session.msg('object_info_request', content)
  174
+        self._dispatch_to_kernel(msg)
  175
+        return msg['header']['msg_id']
157 176
 
158 177
     def history(self, raw=True, output=False, hist_access_type='range', **kwds):
159 178
         """Get entries from the history list.
@@ -187,7 +206,11 @@ def history(self, raw=True, output=False, hist_access_type='range', **kwds):
187 206
         -------
188 207
         The msg_id of the message sent.
189 208
         """
190  
-        raise NotImplementedError
  209
+        content = dict(raw=raw, output=output,
  210
+                       hist_access_type=hist_access_type, **kwds)
  211
+        msg = self.manager.session.msg('history_request', content)
  212
+        self._dispatch_to_kernel(msg)
  213
+        return msg['header']['msg_id']
191 214
 
192 215
     def shutdown(self, restart=False):
193 216
         """ Request an immediate kernel shutdown.
@@ -197,6 +220,25 @@ def shutdown(self, restart=False):
197 220
         # FIXME: What to do here?
198 221
         raise NotImplementedError('Shutdown not supported for embedded kernel')
199 222
 
  223
+    #--------------------------------------------------------------------------
  224
+    # Protected interface
  225
+    #--------------------------------------------------------------------------
  226
+
  227
+    def _dispatch_to_kernel(self, msg):
  228
+        """ Send a message to the kernel and handle a reply.
  229
+        """
  230
+        kernel = self.manager.kernel
  231
+        if kernel is None:
  232
+            raise RuntimeError('Cannot send request. No kernel exists.')
  233
+
  234
+        stream = DummySocket()
  235
+        self.manager.session.send(stream, msg)
  236
+        msg_parts = stream.recv_multipart()
  237
+        kernel.dispatch_shell(stream, msg_parts)
  238
+
  239
+        idents, reply_msg = self.manager.session.recv(stream, copy=False)
  240
+        self.call_handlers_later(reply_msg)
  241
+
200 242
 
201 243
 class SubEmbeddedChannel(EmbeddedChannel):
202 244
     """The SUB channel which listens for messages that the kernel publishes. 
@@ -216,7 +258,10 @@ class StdInEmbeddedChannel(EmbeddedChannel):
216 258
     def input(self, string):
217 259
         """ Send a string of raw input to the kernel. 
218 260
         """
219  
-        raise NotImplementedError
  261
+        kernel = self.manager.kernel
  262
+        if kernel is None:
  263
+            raise RuntimeError('Cannot send input reply. No kernel exists.')
  264
+        kernel.raw_input_str = string
220 265
 
221 266
 
222 267
 class HBEmbeddedChannel(EmbeddedChannel):
43  IPython/embedded/socket.py
... ...
@@ -0,0 +1,43 @@
  1
+""" Defines a dummy socket implementing (part of) the zmq.Socket interface. """
  2
+
  3
+#-----------------------------------------------------------------------------
  4
+#  Copyright (C) 2012  The IPython Development Team
  5
+#
  6
+#  Distributed under the terms of the BSD License.  The full license is in
  7
+#  the file COPYING, distributed as part of this software.
  8
+#-----------------------------------------------------------------------------
  9
+
  10
+#-----------------------------------------------------------------------------
  11
+# Imports
  12
+#-----------------------------------------------------------------------------
  13
+
  14
+# Standard library imports.
  15
+import Queue
  16
+
  17
+# System library imports.
  18
+import zmq
  19
+
  20
+# Local imports.
  21
+from IPython.utils.traitlets import HasTraits, Instance, Int
  22
+
  23
+#-----------------------------------------------------------------------------
  24
+# Dummy socket class
  25
+#-----------------------------------------------------------------------------
  26
+
  27
+class DummySocket(HasTraits):
  28
+    """ A dummy socket implementing (part of) the zmq.Socket interface. """
  29
+    
  30
+    queue = Instance(Queue.Queue, ())
  31
+    message_sent = Int(0) # Should be an Event
  32
+
  33
+    #-------------------------------------------------------------------------
  34
+    # zmq.Socket interface
  35
+    #-------------------------------------------------------------------------
  36
+
  37
+    def recv_multipart(self, flags=0, copy=True, track=False):
  38
+        return self.queue.get_nowait()
  39
+
  40
+    def send_multipart(self, msg_parts, flags=0, copy=True, track=False):
  41
+        msg_parts = map(zmq.Message, msg_parts)
  42
+        self.queue.put_nowait(msg_parts)
  43
+        self.message_sent += 1
4  IPython/zmq/datapub.py
@@ -15,7 +15,7 @@
15 15
 from IPython.config import Configurable
16 16
 
17 17
 from IPython.utils.jsonutil import json_clean
18  
-from IPython.utils.traitlets import Instance, Dict, CBytes
  18
+from IPython.utils.traitlets import Any, Instance, Dict, CBytes
19 19
 
20 20
 from IPython.zmq.serialize import serialize_object
21 21
 from IPython.zmq.session import Session, extract_header
@@ -29,7 +29,7 @@ class ZMQDataPublisher(Configurable):
29 29
 
30 30
     topic = topic = CBytes(b'datapub')
31 31
     session = Instance(Session)
32  
-    pub_socket = Instance('zmq.Socket')
  32
+    pub_socket = Any()
33 33
     parent_header = Dict({})
34 34
 
35 35
     def set_parent(self, parent):
4  IPython/zmq/displayhook.py
@@ -3,7 +3,7 @@
3 3
 
4 4
 from IPython.core.displayhook import DisplayHook
5 5
 from IPython.utils.jsonutil import encode_images
6  
-from IPython.utils.traitlets import Instance, Dict
  6
+from IPython.utils.traitlets import Any, Instance, Dict
7 7
 from session import extract_header, Session
8 8
 
9 9
 class ZMQDisplayHook(object):
@@ -37,7 +37,7 @@ class ZMQShellDisplayHook(DisplayHook):
37 37
     topic=None
38 38
 
39 39
     session = Instance(Session)
40  
-    pub_socket = Instance('zmq.Socket')
  40
+    pub_socket = Any()
41 41
     parent_header = Dict({})
42 42
 
43 43
     def set_parent(self, parent):
31  IPython/zmq/ipkernel.py
@@ -644,7 +644,6 @@ def clear_request(self, stream, idents, parent):
644 644
     # Protected interface
645 645
     #---------------------------------------------------------------------------
646 646
 
647  
-
648 647
     def _wrap_exception(self, method=None):
649 648
         # import here, because _wrap_exception is only used in parallel,
650 649
         # and parallel has higher min pyzmq version
@@ -739,36 +738,6 @@ def _complete(self, msg):
739 738
                 cpos = len(c['line'])
740 739
         return self.shell.complete(c['text'], c['line'], cpos)
741 740
 
742  
-    def _object_info(self, context):
743  
-        symbol, leftover = self._symbol_from_context(context)
744  
-        if symbol is not None and not leftover:
745  
-            doc = getattr(symbol, '__doc__', '')
746  
-        else:
747  
-            doc = ''
748  
-        object_info = dict(docstring = doc)
749  
-        return object_info
750  
-
751  
-    def _symbol_from_context(self, context):
752  
-        if not context:
753  
-            return None, context
754  
-
755  
-        base_symbol_string = context[0]
756  
-        symbol = self.shell.user_ns.get(base_symbol_string, None)
757  
-        if symbol is None:
758  
-            symbol = __builtin__.__dict__.get(base_symbol_string, None)
759  
-        if symbol is None:
760  
-            return None, context
761  
-
762  
-        context = context[1:]
763  
-        for i, name in enumerate(context):
764  
-            new_symbol = getattr(symbol, name, None)
765  
-            if new_symbol is None:
766  
-                return symbol, context[i:]
767  
-            else:
768  
-                symbol = new_symbol
769  
-
770  
-        return symbol, []
771  
-
772 741
     def _at_shutdown(self):
773 742
         """Actions taken at shutdown by the kernel, called by python's atexit.
774 743
         """
8  IPython/zmq/session.py
@@ -558,11 +558,9 @@ def send(self, stream, msg_or_type, content=None, parent=None, ident=None,
558 558
         msg : dict
559 559
             The constructed message.
560 560
         """
561  
-
562  
-        if not isinstance(stream, (zmq.Socket, ZMQStream)):
563  
-            raise TypeError("stream must be Socket or ZMQStream, not %r"%type(stream))
564  
-        elif track and isinstance(stream, ZMQStream):
565  
-            raise TypeError("ZMQStream cannot track messages")
  561
+        if not isinstance(stream, zmq.Socket):
  562
+            # ZMQStreams and dummy sockets do not support tracking.
  563
+            track = False
566 564
 
567 565
         if isinstance(msg_or_type, (Message, dict)):
568 566
             # We got a Message or message dict, not a msg_type so don't
4  IPython/zmq/zmqshell.py
@@ -42,7 +42,7 @@
42 42
 from IPython.utils.jsonutil import json_clean, encode_images
43 43
 from IPython.utils.process import arg_split
44 44
 from IPython.utils import py3compat
45  
-from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes
  45
+from IPython.utils.traitlets import Any, Instance, Type, Dict, CBool, CBytes
46 46
 from IPython.utils.warn import warn, error
47 47
 from IPython.zmq.displayhook import ZMQShellDisplayHook
48 48
 from IPython.zmq.datapub import ZMQDataPublisher
@@ -57,7 +57,7 @@ class ZMQDisplayPublisher(DisplayPublisher):
57 57
     """A display publisher that publishes data using a ZeroMQ PUB socket."""
58 58
 
59 59
     session = Instance(Session)
60  
-    pub_socket = Instance('zmq.Socket')
  60
+    pub_socket = Any()
61 61
     parent_header = Dict({})
62 62
     topic = CBytes(b'displaypub')
63 63
 

0 notes on commit 8d53a55

Please sign in to comment.
Something went wrong with that request. Please try again.