##// END OF EJS Templates
Merge pull request #2649 from tkf/kernel-version...
Min RK -
r9095:22c83b3c merge
parent child Browse files
Show More
@@ -1,148 +1,151 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Release data for the IPython project."""
2 """Release data for the IPython project."""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2008, IPython Development Team.
5 # Copyright (c) 2008, IPython Development Team.
6 # Copyright (c) 2001, Fernando Perez <fernando.perez@colorado.edu>
6 # Copyright (c) 2001, Fernando Perez <fernando.perez@colorado.edu>
7 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
7 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
8 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
8 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
9 #
9 #
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11 #
11 #
12 # The full license is in the file COPYING.txt, distributed with this software.
12 # The full license is in the file COPYING.txt, distributed with this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # Name of the package for release purposes. This is the name which labels
15 # Name of the package for release purposes. This is the name which labels
16 # the tarballs and RPMs made by distutils, so it's best to lowercase it.
16 # the tarballs and RPMs made by distutils, so it's best to lowercase it.
17 name = 'ipython'
17 name = 'ipython'
18
18
19 # IPython version information. An empty _version_extra corresponds to a full
19 # IPython version information. An empty _version_extra corresponds to a full
20 # release. 'dev' as a _version_extra string means this is a development
20 # release. 'dev' as a _version_extra string means this is a development
21 # version
21 # version
22 _version_major = 0
22 _version_major = 0
23 _version_minor = 14
23 _version_minor = 14
24 _version_micro = 0 # use 0 for first of series, number for 1 and above
24 _version_micro = 0 # use 0 for first of series, number for 1 and above
25 _version_extra = 'dev'
25 _version_extra = 'dev'
26 #_version_extra = 'rc1'
26 #_version_extra = 'rc1'
27 # _version_extra = '' # Uncomment this for full releases
27 # _version_extra = '' # Uncomment this for full releases
28
28
29 # Construct full version string from these.
29 # Construct full version string from these.
30 _ver = [_version_major, _version_minor]
30 _ver = [_version_major, _version_minor]
31 if _version_micro:
31 if _version_micro:
32 _ver.append(_version_micro)
32 _ver.append(_version_micro)
33 if _version_extra:
33 if _version_extra:
34 _ver.append(_version_extra)
34 _ver.append(_version_extra)
35
35
36 __version__ = '.'.join(map(str, _ver))
36 __version__ = '.'.join(map(str, _ver))
37
37
38 version = __version__ # backwards compatibility name
38 version = __version__ # backwards compatibility name
39 version_info = (_version_major, _version_minor, _version_micro, _version_extra)
39 version_info = (_version_major, _version_minor, _version_micro, _version_extra)
40
40
41 # Change this when incrementing the kernel protocol version
42 kernel_protocol_version_info = (4, 0)
43
41 description = "IPython: Productive Interactive Computing"
44 description = "IPython: Productive Interactive Computing"
42
45
43 long_description = \
46 long_description = \
44 """
47 """
45 IPython provides a rich toolkit to help you make the most out of using Python
48 IPython provides a rich toolkit to help you make the most out of using Python
46 interactively. Its main components are:
49 interactively. Its main components are:
47
50
48 * Powerful interactive Python shells (terminal- and Qt-based).
51 * Powerful interactive Python shells (terminal- and Qt-based).
49 * A web-based interactive notebook environment with all shell features plus
52 * A web-based interactive notebook environment with all shell features plus
50 support for embedded figures, animations and rich media.
53 support for embedded figures, animations and rich media.
51 * Support for interactive data visualization and use of GUI toolkits.
54 * Support for interactive data visualization and use of GUI toolkits.
52 * Flexible, embeddable interpreters to load into your own projects.
55 * Flexible, embeddable interpreters to load into your own projects.
53 * A high-performance library for high level and interactive parallel computing
56 * A high-performance library for high level and interactive parallel computing
54 that works in multicore systems, clusters, supercomputing and cloud scenarios.
57 that works in multicore systems, clusters, supercomputing and cloud scenarios.
55
58
56 The enhanced interactive Python shells have the following main features:
59 The enhanced interactive Python shells have the following main features:
57
60
58 * Comprehensive object introspection.
61 * Comprehensive object introspection.
59
62
60 * Input history, persistent across sessions.
63 * Input history, persistent across sessions.
61
64
62 * Caching of output results during a session with automatically generated
65 * Caching of output results during a session with automatically generated
63 references.
66 references.
64
67
65 * Extensible tab completion, with support by default for completion of python
68 * Extensible tab completion, with support by default for completion of python
66 variables and keywords, filenames and function keywords.
69 variables and keywords, filenames and function keywords.
67
70
68 * Extensible system of 'magic' commands for controlling the environment and
71 * Extensible system of 'magic' commands for controlling the environment and
69 performing many tasks related either to IPython or the operating system.
72 performing many tasks related either to IPython or the operating system.
70
73
71 * A rich configuration system with easy switching between different setups
74 * A rich configuration system with easy switching between different setups
72 (simpler than changing $PYTHONSTARTUP environment variables every time).
75 (simpler than changing $PYTHONSTARTUP environment variables every time).
73
76
74 * Session logging and reloading.
77 * Session logging and reloading.
75
78
76 * Extensible syntax processing for special purpose situations.
79 * Extensible syntax processing for special purpose situations.
77
80
78 * Access to the system shell with user-extensible alias system.
81 * Access to the system shell with user-extensible alias system.
79
82
80 * Easily embeddable in other Python programs and GUIs.
83 * Easily embeddable in other Python programs and GUIs.
81
84
82 * Integrated access to the pdb debugger and the Python profiler.
85 * Integrated access to the pdb debugger and the Python profiler.
83
86
84 The parallel computing architecture has the following main features:
87 The parallel computing architecture has the following main features:
85
88
86 * Quickly parallelize Python code from an interactive Python/IPython session.
89 * Quickly parallelize Python code from an interactive Python/IPython session.
87
90
88 * A flexible and dynamic process model that be deployed on anything from
91 * A flexible and dynamic process model that be deployed on anything from
89 multicore workstations to supercomputers.
92 multicore workstations to supercomputers.
90
93
91 * An architecture that supports many different styles of parallelism, from
94 * An architecture that supports many different styles of parallelism, from
92 message passing to task farming.
95 message passing to task farming.
93
96
94 * Both blocking and fully asynchronous interfaces.
97 * Both blocking and fully asynchronous interfaces.
95
98
96 * High level APIs that enable many things to be parallelized in a few lines
99 * High level APIs that enable many things to be parallelized in a few lines
97 of code.
100 of code.
98
101
99 * Share live parallel jobs with other users securely.
102 * Share live parallel jobs with other users securely.
100
103
101 * Dynamically load balanced task farming system.
104 * Dynamically load balanced task farming system.
102
105
103 * Robust error handling in parallel code.
106 * Robust error handling in parallel code.
104
107
105 The latest development version is always available from IPython's `GitHub
108 The latest development version is always available from IPython's `GitHub
106 site <http://github.com/ipython>`_.
109 site <http://github.com/ipython>`_.
107 """
110 """
108
111
109 license = 'BSD'
112 license = 'BSD'
110
113
111 authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'),
114 authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'),
112 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
115 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
113 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'),
116 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'),
114 'Ville' : ('Ville Vainio','vivainio@gmail.com'),
117 'Ville' : ('Ville Vainio','vivainio@gmail.com'),
115 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'),
118 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'),
116 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com'),
119 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com'),
117 'Thomas' : ('Thomas A. Kluyver', 'takowl@gmail.com'),
120 'Thomas' : ('Thomas A. Kluyver', 'takowl@gmail.com'),
118 'Jorgen' : ('Jorgen Stenarson', 'jorgen.stenarson@bostream.nu'),
121 'Jorgen' : ('Jorgen Stenarson', 'jorgen.stenarson@bostream.nu'),
119 'Matthias' : ('Matthias Bussonnier', 'bussonniermatthias@gmail.com'),
122 'Matthias' : ('Matthias Bussonnier', 'bussonniermatthias@gmail.com'),
120 }
123 }
121
124
122 author = 'The IPython Development Team'
125 author = 'The IPython Development Team'
123
126
124 author_email = 'ipython-dev@scipy.org'
127 author_email = 'ipython-dev@scipy.org'
125
128
126 url = 'http://ipython.org'
129 url = 'http://ipython.org'
127
130
128 download_url = 'https://github.com/ipython/ipython/downloads'
131 download_url = 'https://github.com/ipython/ipython/downloads'
129
132
130 platforms = ['Linux','Mac OSX','Windows XP/2000/NT/Vista/7']
133 platforms = ['Linux','Mac OSX','Windows XP/2000/NT/Vista/7']
131
134
132 keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed',
135 keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed',
133 'Web-based computing', 'Qt console', 'Embedding']
136 'Web-based computing', 'Qt console', 'Embedding']
134
137
135 classifiers = [
138 classifiers = [
136 'Intended Audience :: Developers',
139 'Intended Audience :: Developers',
137 'Intended Audience :: Science/Research',
140 'Intended Audience :: Science/Research',
138 'License :: OSI Approved :: BSD License',
141 'License :: OSI Approved :: BSD License',
139 'Programming Language :: Python',
142 'Programming Language :: Python',
140 'Programming Language :: Python :: 2',
143 'Programming Language :: Python :: 2',
141 'Programming Language :: Python :: 2.6',
144 'Programming Language :: Python :: 2.6',
142 'Programming Language :: Python :: 2.7',
145 'Programming Language :: Python :: 2.7',
143 'Programming Language :: Python :: 3',
146 'Programming Language :: Python :: 3',
144 'Programming Language :: Python :: 3.1',
147 'Programming Language :: Python :: 3.1',
145 'Programming Language :: Python :: 3.2',
148 'Programming Language :: Python :: 3.2',
146 'Topic :: System :: Distributed Computing',
149 'Topic :: System :: Distributed Computing',
147 'Topic :: System :: Shells'
150 'Topic :: System :: Shells'
148 ]
151 ]
@@ -1,932 +1,950 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Standard library imports
18 # Standard library imports
19 import __builtin__
19 import __builtin__
20 import atexit
20 import atexit
21 import sys
21 import sys
22 import time
22 import time
23 import traceback
23 import traceback
24 import logging
24 import logging
25 import uuid
25 import uuid
26
26
27 from datetime import datetime
27 from datetime import datetime
28 from signal import (
28 from signal import (
29 signal, getsignal, default_int_handler, SIGINT, SIG_IGN
29 signal, getsignal, default_int_handler, SIGINT, SIG_IGN
30 )
30 )
31
31
32 # System library imports
32 # System library imports
33 import zmq
33 import zmq
34 from zmq.eventloop import ioloop
34 from zmq.eventloop import ioloop
35 from zmq.eventloop.zmqstream import ZMQStream
35 from zmq.eventloop.zmqstream import ZMQStream
36
36
37 # Local imports
37 # Local imports
38 from IPython.config.configurable import Configurable
38 from IPython.config.configurable import Configurable
39 from IPython.config.application import boolean_flag, catch_config_error
39 from IPython.config.application import boolean_flag, catch_config_error
40 from IPython.core.application import ProfileDir
40 from IPython.core.application import ProfileDir
41 from IPython.core.error import StdinNotImplementedError
41 from IPython.core.error import StdinNotImplementedError
42 from IPython.core import release
42 from IPython.core.shellapp import (
43 from IPython.core.shellapp import (
43 InteractiveShellApp, shell_flags, shell_aliases
44 InteractiveShellApp, shell_flags, shell_aliases
44 )
45 )
45 from IPython.utils import io
46 from IPython.utils import io
46 from IPython.utils import py3compat
47 from IPython.utils import py3compat
47 from IPython.utils.frame import extract_module_locals
48 from IPython.utils.frame import extract_module_locals
48 from IPython.utils.jsonutil import json_clean
49 from IPython.utils.jsonutil import json_clean
49 from IPython.utils.traitlets import (
50 from IPython.utils.traitlets import (
50 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode
51 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode
51 )
52 )
52
53
53 from entry_point import base_launch_kernel
54 from entry_point import base_launch_kernel
54 from kernelapp import KernelApp, kernel_flags, kernel_aliases
55 from kernelapp import KernelApp, kernel_flags, kernel_aliases
55 from serialize import serialize_object, unpack_apply_message
56 from serialize import serialize_object, unpack_apply_message
56 from session import Session, Message
57 from session import Session, Message
57 from zmqshell import ZMQInteractiveShell
58 from zmqshell import ZMQInteractiveShell
58
59
59
60
60 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
61 # Main kernel class
62 # Main kernel class
62 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
63
64
65 protocol_version = list(release.kernel_protocol_version_info)
66 ipython_version = list(release.version_info)
67 language_version = list(sys.version_info[:3])
68
69
64 class Kernel(Configurable):
70 class Kernel(Configurable):
65
71
66 #---------------------------------------------------------------------------
72 #---------------------------------------------------------------------------
67 # Kernel interface
73 # Kernel interface
68 #---------------------------------------------------------------------------
74 #---------------------------------------------------------------------------
69
75
70 # attribute to override with a GUI
76 # attribute to override with a GUI
71 eventloop = Any(None)
77 eventloop = Any(None)
72 def _eventloop_changed(self, name, old, new):
78 def _eventloop_changed(self, name, old, new):
73 """schedule call to eventloop from IOLoop"""
79 """schedule call to eventloop from IOLoop"""
74 loop = ioloop.IOLoop.instance()
80 loop = ioloop.IOLoop.instance()
75 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
81 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
76
82
77 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
83 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
78 session = Instance(Session)
84 session = Instance(Session)
79 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
85 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
80 shell_streams = List()
86 shell_streams = List()
81 control_stream = Instance(ZMQStream)
87 control_stream = Instance(ZMQStream)
82 iopub_socket = Instance(zmq.Socket)
88 iopub_socket = Instance(zmq.Socket)
83 stdin_socket = Instance(zmq.Socket)
89 stdin_socket = Instance(zmq.Socket)
84 log = Instance(logging.Logger)
90 log = Instance(logging.Logger)
85
91
86 user_module = Any()
92 user_module = Any()
87 def _user_module_changed(self, name, old, new):
93 def _user_module_changed(self, name, old, new):
88 if self.shell is not None:
94 if self.shell is not None:
89 self.shell.user_module = new
95 self.shell.user_module = new
90
96
91 user_ns = Dict(default_value=None)
97 user_ns = Dict(default_value=None)
92 def _user_ns_changed(self, name, old, new):
98 def _user_ns_changed(self, name, old, new):
93 if self.shell is not None:
99 if self.shell is not None:
94 self.shell.user_ns = new
100 self.shell.user_ns = new
95 self.shell.init_user_ns()
101 self.shell.init_user_ns()
96
102
97 # identities:
103 # identities:
98 int_id = Integer(-1)
104 int_id = Integer(-1)
99 ident = Unicode()
105 ident = Unicode()
100
106
101 def _ident_default(self):
107 def _ident_default(self):
102 return unicode(uuid.uuid4())
108 return unicode(uuid.uuid4())
103
109
104
110
105 # Private interface
111 # Private interface
106
112
107 # Time to sleep after flushing the stdout/err buffers in each execute
113 # Time to sleep after flushing the stdout/err buffers in each execute
108 # cycle. While this introduces a hard limit on the minimal latency of the
114 # cycle. While this introduces a hard limit on the minimal latency of the
109 # execute cycle, it helps prevent output synchronization problems for
115 # execute cycle, it helps prevent output synchronization problems for
110 # clients.
116 # clients.
111 # Units are in seconds. The minimum zmq latency on local host is probably
117 # Units are in seconds. The minimum zmq latency on local host is probably
112 # ~150 microseconds, set this to 500us for now. We may need to increase it
118 # ~150 microseconds, set this to 500us for now. We may need to increase it
113 # a little if it's not enough after more interactive testing.
119 # a little if it's not enough after more interactive testing.
114 _execute_sleep = Float(0.0005, config=True)
120 _execute_sleep = Float(0.0005, config=True)
115
121
116 # Frequency of the kernel's event loop.
122 # Frequency of the kernel's event loop.
117 # Units are in seconds, kernel subclasses for GUI toolkits may need to
123 # Units are in seconds, kernel subclasses for GUI toolkits may need to
118 # adapt to milliseconds.
124 # adapt to milliseconds.
119 _poll_interval = Float(0.05, config=True)
125 _poll_interval = Float(0.05, config=True)
120
126
121 # If the shutdown was requested over the network, we leave here the
127 # If the shutdown was requested over the network, we leave here the
122 # necessary reply message so it can be sent by our registered atexit
128 # necessary reply message so it can be sent by our registered atexit
123 # handler. This ensures that the reply is only sent to clients truly at
129 # handler. This ensures that the reply is only sent to clients truly at
124 # the end of our shutdown process (which happens after the underlying
130 # the end of our shutdown process (which happens after the underlying
125 # IPython shell's own shutdown).
131 # IPython shell's own shutdown).
126 _shutdown_message = None
132 _shutdown_message = None
127
133
128 # This is a dict of port number that the kernel is listening on. It is set
134 # This is a dict of port number that the kernel is listening on. It is set
129 # by record_ports and used by connect_request.
135 # by record_ports and used by connect_request.
130 _recorded_ports = Dict()
136 _recorded_ports = Dict()
131
137
132 # set of aborted msg_ids
138 # set of aborted msg_ids
133 aborted = Set()
139 aborted = Set()
134
140
135
141
136 def __init__(self, **kwargs):
142 def __init__(self, **kwargs):
137 super(Kernel, self).__init__(**kwargs)
143 super(Kernel, self).__init__(**kwargs)
138
144
139 # Initialize the InteractiveShell subclass
145 # Initialize the InteractiveShell subclass
140 self.shell = ZMQInteractiveShell.instance(config=self.config,
146 self.shell = ZMQInteractiveShell.instance(config=self.config,
141 profile_dir = self.profile_dir,
147 profile_dir = self.profile_dir,
142 user_module = self.user_module,
148 user_module = self.user_module,
143 user_ns = self.user_ns,
149 user_ns = self.user_ns,
144 )
150 )
145 self.shell.displayhook.session = self.session
151 self.shell.displayhook.session = self.session
146 self.shell.displayhook.pub_socket = self.iopub_socket
152 self.shell.displayhook.pub_socket = self.iopub_socket
147 self.shell.displayhook.topic = self._topic('pyout')
153 self.shell.displayhook.topic = self._topic('pyout')
148 self.shell.display_pub.session = self.session
154 self.shell.display_pub.session = self.session
149 self.shell.display_pub.pub_socket = self.iopub_socket
155 self.shell.display_pub.pub_socket = self.iopub_socket
150 self.shell.data_pub.session = self.session
156 self.shell.data_pub.session = self.session
151 self.shell.data_pub.pub_socket = self.iopub_socket
157 self.shell.data_pub.pub_socket = self.iopub_socket
152
158
153 # TMP - hack while developing
159 # TMP - hack while developing
154 self.shell._reply_content = None
160 self.shell._reply_content = None
155
161
156 # Build dict of handlers for message types
162 # Build dict of handlers for message types
157 msg_types = [ 'execute_request', 'complete_request',
163 msg_types = [ 'execute_request', 'complete_request',
158 'object_info_request', 'history_request',
164 'object_info_request', 'history_request',
165 'kernel_info_request',
159 'connect_request', 'shutdown_request',
166 'connect_request', 'shutdown_request',
160 'apply_request',
167 'apply_request',
161 ]
168 ]
162 self.shell_handlers = {}
169 self.shell_handlers = {}
163 for msg_type in msg_types:
170 for msg_type in msg_types:
164 self.shell_handlers[msg_type] = getattr(self, msg_type)
171 self.shell_handlers[msg_type] = getattr(self, msg_type)
165
172
166 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
173 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
167 self.control_handlers = {}
174 self.control_handlers = {}
168 for msg_type in control_msg_types:
175 for msg_type in control_msg_types:
169 self.control_handlers[msg_type] = getattr(self, msg_type)
176 self.control_handlers[msg_type] = getattr(self, msg_type)
170
177
171 def dispatch_control(self, msg):
178 def dispatch_control(self, msg):
172 """dispatch control requests"""
179 """dispatch control requests"""
173 idents,msg = self.session.feed_identities(msg, copy=False)
180 idents,msg = self.session.feed_identities(msg, copy=False)
174 try:
181 try:
175 msg = self.session.unserialize(msg, content=True, copy=False)
182 msg = self.session.unserialize(msg, content=True, copy=False)
176 except:
183 except:
177 self.log.error("Invalid Control Message", exc_info=True)
184 self.log.error("Invalid Control Message", exc_info=True)
178 return
185 return
179
186
180 self.log.debug("Control received: %s", msg)
187 self.log.debug("Control received: %s", msg)
181
188
182 header = msg['header']
189 header = msg['header']
183 msg_id = header['msg_id']
190 msg_id = header['msg_id']
184 msg_type = header['msg_type']
191 msg_type = header['msg_type']
185
192
186 handler = self.control_handlers.get(msg_type, None)
193 handler = self.control_handlers.get(msg_type, None)
187 if handler is None:
194 if handler is None:
188 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
195 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
189 else:
196 else:
190 try:
197 try:
191 handler(self.control_stream, idents, msg)
198 handler(self.control_stream, idents, msg)
192 except Exception:
199 except Exception:
193 self.log.error("Exception in control handler:", exc_info=True)
200 self.log.error("Exception in control handler:", exc_info=True)
194
201
195 def dispatch_shell(self, stream, msg):
202 def dispatch_shell(self, stream, msg):
196 """dispatch shell requests"""
203 """dispatch shell requests"""
197 # flush control requests first
204 # flush control requests first
198 if self.control_stream:
205 if self.control_stream:
199 self.control_stream.flush()
206 self.control_stream.flush()
200
207
201 idents,msg = self.session.feed_identities(msg, copy=False)
208 idents,msg = self.session.feed_identities(msg, copy=False)
202 try:
209 try:
203 msg = self.session.unserialize(msg, content=True, copy=False)
210 msg = self.session.unserialize(msg, content=True, copy=False)
204 except:
211 except:
205 self.log.error("Invalid Message", exc_info=True)
212 self.log.error("Invalid Message", exc_info=True)
206 return
213 return
207
214
208 header = msg['header']
215 header = msg['header']
209 msg_id = header['msg_id']
216 msg_id = header['msg_id']
210 msg_type = msg['header']['msg_type']
217 msg_type = msg['header']['msg_type']
211
218
212 # Print some info about this message and leave a '--->' marker, so it's
219 # Print some info about this message and leave a '--->' marker, so it's
213 # easier to trace visually the message chain when debugging. Each
220 # easier to trace visually the message chain when debugging. Each
214 # handler prints its message at the end.
221 # handler prints its message at the end.
215 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
222 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
216 self.log.debug(' Content: %s\n --->\n ', msg['content'])
223 self.log.debug(' Content: %s\n --->\n ', msg['content'])
217
224
218 if msg_id in self.aborted:
225 if msg_id in self.aborted:
219 self.aborted.remove(msg_id)
226 self.aborted.remove(msg_id)
220 # is it safe to assume a msg_id will not be resubmitted?
227 # is it safe to assume a msg_id will not be resubmitted?
221 reply_type = msg_type.split('_')[0] + '_reply'
228 reply_type = msg_type.split('_')[0] + '_reply'
222 status = {'status' : 'aborted'}
229 status = {'status' : 'aborted'}
223 md = {'engine' : self.ident}
230 md = {'engine' : self.ident}
224 md.update(status)
231 md.update(status)
225 reply_msg = self.session.send(stream, reply_type, metadata=md,
232 reply_msg = self.session.send(stream, reply_type, metadata=md,
226 content=status, parent=msg, ident=idents)
233 content=status, parent=msg, ident=idents)
227 return
234 return
228
235
229 handler = self.shell_handlers.get(msg_type, None)
236 handler = self.shell_handlers.get(msg_type, None)
230 if handler is None:
237 if handler is None:
231 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
238 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
232 else:
239 else:
233 # ensure default_int_handler during handler call
240 # ensure default_int_handler during handler call
234 sig = signal(SIGINT, default_int_handler)
241 sig = signal(SIGINT, default_int_handler)
235 try:
242 try:
236 handler(stream, idents, msg)
243 handler(stream, idents, msg)
237 except Exception:
244 except Exception:
238 self.log.error("Exception in message handler:", exc_info=True)
245 self.log.error("Exception in message handler:", exc_info=True)
239 finally:
246 finally:
240 signal(SIGINT, sig)
247 signal(SIGINT, sig)
241
248
242 def enter_eventloop(self):
249 def enter_eventloop(self):
243 """enter eventloop"""
250 """enter eventloop"""
244 self.log.info("entering eventloop")
251 self.log.info("entering eventloop")
245 # restore default_int_handler
252 # restore default_int_handler
246 signal(SIGINT, default_int_handler)
253 signal(SIGINT, default_int_handler)
247 while self.eventloop is not None:
254 while self.eventloop is not None:
248 try:
255 try:
249 self.eventloop(self)
256 self.eventloop(self)
250 except KeyboardInterrupt:
257 except KeyboardInterrupt:
251 # Ctrl-C shouldn't crash the kernel
258 # Ctrl-C shouldn't crash the kernel
252 self.log.error("KeyboardInterrupt caught in kernel")
259 self.log.error("KeyboardInterrupt caught in kernel")
253 continue
260 continue
254 else:
261 else:
255 # eventloop exited cleanly, this means we should stop (right?)
262 # eventloop exited cleanly, this means we should stop (right?)
256 self.eventloop = None
263 self.eventloop = None
257 break
264 break
258 self.log.info("exiting eventloop")
265 self.log.info("exiting eventloop")
259
266
260 def start(self):
267 def start(self):
261 """register dispatchers for streams"""
268 """register dispatchers for streams"""
262 self.shell.exit_now = False
269 self.shell.exit_now = False
263 if self.control_stream:
270 if self.control_stream:
264 self.control_stream.on_recv(self.dispatch_control, copy=False)
271 self.control_stream.on_recv(self.dispatch_control, copy=False)
265
272
266 def make_dispatcher(stream):
273 def make_dispatcher(stream):
267 def dispatcher(msg):
274 def dispatcher(msg):
268 return self.dispatch_shell(stream, msg)
275 return self.dispatch_shell(stream, msg)
269 return dispatcher
276 return dispatcher
270
277
271 for s in self.shell_streams:
278 for s in self.shell_streams:
272 s.on_recv(make_dispatcher(s), copy=False)
279 s.on_recv(make_dispatcher(s), copy=False)
273
280
274 def do_one_iteration(self):
281 def do_one_iteration(self):
275 """step eventloop just once"""
282 """step eventloop just once"""
276 if self.control_stream:
283 if self.control_stream:
277 self.control_stream.flush()
284 self.control_stream.flush()
278 for stream in self.shell_streams:
285 for stream in self.shell_streams:
279 # handle at most one request per iteration
286 # handle at most one request per iteration
280 stream.flush(zmq.POLLIN, 1)
287 stream.flush(zmq.POLLIN, 1)
281 stream.flush(zmq.POLLOUT)
288 stream.flush(zmq.POLLOUT)
282
289
283
290
284 def record_ports(self, ports):
291 def record_ports(self, ports):
285 """Record the ports that this kernel is using.
292 """Record the ports that this kernel is using.
286
293
287 The creator of the Kernel instance must call this methods if they
294 The creator of the Kernel instance must call this methods if they
288 want the :meth:`connect_request` method to return the port numbers.
295 want the :meth:`connect_request` method to return the port numbers.
289 """
296 """
290 self._recorded_ports = ports
297 self._recorded_ports = ports
291
298
292 #---------------------------------------------------------------------------
299 #---------------------------------------------------------------------------
293 # Kernel request handlers
300 # Kernel request handlers
294 #---------------------------------------------------------------------------
301 #---------------------------------------------------------------------------
295
302
296 def _make_metadata(self, other=None):
303 def _make_metadata(self, other=None):
297 """init metadata dict, for execute/apply_reply"""
304 """init metadata dict, for execute/apply_reply"""
298 new_md = {
305 new_md = {
299 'dependencies_met' : True,
306 'dependencies_met' : True,
300 'engine' : self.ident,
307 'engine' : self.ident,
301 'started': datetime.now(),
308 'started': datetime.now(),
302 }
309 }
303 if other:
310 if other:
304 new_md.update(other)
311 new_md.update(other)
305 return new_md
312 return new_md
306
313
307 def _publish_pyin(self, code, parent, execution_count):
314 def _publish_pyin(self, code, parent, execution_count):
308 """Publish the code request on the pyin stream."""
315 """Publish the code request on the pyin stream."""
309
316
310 self.session.send(self.iopub_socket, u'pyin',
317 self.session.send(self.iopub_socket, u'pyin',
311 {u'code':code, u'execution_count': execution_count},
318 {u'code':code, u'execution_count': execution_count},
312 parent=parent, ident=self._topic('pyin')
319 parent=parent, ident=self._topic('pyin')
313 )
320 )
314
321
315 def _publish_status(self, status, parent=None):
322 def _publish_status(self, status, parent=None):
316 """send status (busy/idle) on IOPub"""
323 """send status (busy/idle) on IOPub"""
317 self.session.send(self.iopub_socket,
324 self.session.send(self.iopub_socket,
318 u'status',
325 u'status',
319 {u'execution_state': status},
326 {u'execution_state': status},
320 parent=parent,
327 parent=parent,
321 ident=self._topic('status'),
328 ident=self._topic('status'),
322 )
329 )
323
330
324
331
325 def execute_request(self, stream, ident, parent):
332 def execute_request(self, stream, ident, parent):
326 """handle an execute_request"""
333 """handle an execute_request"""
327
334
328 self._publish_status(u'busy', parent)
335 self._publish_status(u'busy', parent)
329
336
330 try:
337 try:
331 content = parent[u'content']
338 content = parent[u'content']
332 code = content[u'code']
339 code = content[u'code']
333 silent = content[u'silent']
340 silent = content[u'silent']
334 store_history = content.get(u'store_history', not silent)
341 store_history = content.get(u'store_history', not silent)
335 except:
342 except:
336 self.log.error("Got bad msg: ")
343 self.log.error("Got bad msg: ")
337 self.log.error("%s", parent)
344 self.log.error("%s", parent)
338 return
345 return
339
346
340 md = self._make_metadata(parent['metadata'])
347 md = self._make_metadata(parent['metadata'])
341
348
342 shell = self.shell # we'll need this a lot here
349 shell = self.shell # we'll need this a lot here
343
350
344 # Replace raw_input. Note that is not sufficient to replace
351 # Replace raw_input. Note that is not sufficient to replace
345 # raw_input in the user namespace.
352 # raw_input in the user namespace.
346 if content.get('allow_stdin', False):
353 if content.get('allow_stdin', False):
347 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
354 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
348 else:
355 else:
349 raw_input = lambda prompt='' : self._no_raw_input()
356 raw_input = lambda prompt='' : self._no_raw_input()
350
357
351 if py3compat.PY3:
358 if py3compat.PY3:
352 __builtin__.input = raw_input
359 __builtin__.input = raw_input
353 else:
360 else:
354 __builtin__.raw_input = raw_input
361 __builtin__.raw_input = raw_input
355
362
356 # Set the parent message of the display hook and out streams.
363 # Set the parent message of the display hook and out streams.
357 shell.displayhook.set_parent(parent)
364 shell.displayhook.set_parent(parent)
358 shell.display_pub.set_parent(parent)
365 shell.display_pub.set_parent(parent)
359 shell.data_pub.set_parent(parent)
366 shell.data_pub.set_parent(parent)
360 sys.stdout.set_parent(parent)
367 sys.stdout.set_parent(parent)
361 sys.stderr.set_parent(parent)
368 sys.stderr.set_parent(parent)
362
369
363 # Re-broadcast our input for the benefit of listening clients, and
370 # Re-broadcast our input for the benefit of listening clients, and
364 # start computing output
371 # start computing output
365 if not silent:
372 if not silent:
366 self._publish_pyin(code, parent, shell.execution_count)
373 self._publish_pyin(code, parent, shell.execution_count)
367
374
368 reply_content = {}
375 reply_content = {}
369 try:
376 try:
370 # FIXME: the shell calls the exception handler itself.
377 # FIXME: the shell calls the exception handler itself.
371 shell.run_cell(code, store_history=store_history, silent=silent)
378 shell.run_cell(code, store_history=store_history, silent=silent)
372 except:
379 except:
373 status = u'error'
380 status = u'error'
374 # FIXME: this code right now isn't being used yet by default,
381 # FIXME: this code right now isn't being used yet by default,
375 # because the run_cell() call above directly fires off exception
382 # because the run_cell() call above directly fires off exception
376 # reporting. This code, therefore, is only active in the scenario
383 # reporting. This code, therefore, is only active in the scenario
377 # where runlines itself has an unhandled exception. We need to
384 # where runlines itself has an unhandled exception. We need to
378 # uniformize this, for all exception construction to come from a
385 # uniformize this, for all exception construction to come from a
379 # single location in the codbase.
386 # single location in the codbase.
380 etype, evalue, tb = sys.exc_info()
387 etype, evalue, tb = sys.exc_info()
381 tb_list = traceback.format_exception(etype, evalue, tb)
388 tb_list = traceback.format_exception(etype, evalue, tb)
382 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
389 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
383 else:
390 else:
384 status = u'ok'
391 status = u'ok'
385
392
386 reply_content[u'status'] = status
393 reply_content[u'status'] = status
387
394
388 # Return the execution counter so clients can display prompts
395 # Return the execution counter so clients can display prompts
389 reply_content['execution_count'] = shell.execution_count - 1
396 reply_content['execution_count'] = shell.execution_count - 1
390
397
391 # FIXME - fish exception info out of shell, possibly left there by
398 # FIXME - fish exception info out of shell, possibly left there by
392 # runlines. We'll need to clean up this logic later.
399 # runlines. We'll need to clean up this logic later.
393 if shell._reply_content is not None:
400 if shell._reply_content is not None:
394 reply_content.update(shell._reply_content)
401 reply_content.update(shell._reply_content)
395 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
402 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
396 reply_content['engine_info'] = e_info
403 reply_content['engine_info'] = e_info
397 # reset after use
404 # reset after use
398 shell._reply_content = None
405 shell._reply_content = None
399
406
400 # At this point, we can tell whether the main code execution succeeded
407 # At this point, we can tell whether the main code execution succeeded
401 # or not. If it did, we proceed to evaluate user_variables/expressions
408 # or not. If it did, we proceed to evaluate user_variables/expressions
402 if reply_content['status'] == 'ok':
409 if reply_content['status'] == 'ok':
403 reply_content[u'user_variables'] = \
410 reply_content[u'user_variables'] = \
404 shell.user_variables(content.get(u'user_variables', []))
411 shell.user_variables(content.get(u'user_variables', []))
405 reply_content[u'user_expressions'] = \
412 reply_content[u'user_expressions'] = \
406 shell.user_expressions(content.get(u'user_expressions', {}))
413 shell.user_expressions(content.get(u'user_expressions', {}))
407 else:
414 else:
408 # If there was an error, don't even try to compute variables or
415 # If there was an error, don't even try to compute variables or
409 # expressions
416 # expressions
410 reply_content[u'user_variables'] = {}
417 reply_content[u'user_variables'] = {}
411 reply_content[u'user_expressions'] = {}
418 reply_content[u'user_expressions'] = {}
412
419
413 # Payloads should be retrieved regardless of outcome, so we can both
420 # Payloads should be retrieved regardless of outcome, so we can both
414 # recover partial output (that could have been generated early in a
421 # recover partial output (that could have been generated early in a
415 # block, before an error) and clear the payload system always.
422 # block, before an error) and clear the payload system always.
416 reply_content[u'payload'] = shell.payload_manager.read_payload()
423 reply_content[u'payload'] = shell.payload_manager.read_payload()
417 # Be agressive about clearing the payload because we don't want
424 # Be agressive about clearing the payload because we don't want
418 # it to sit in memory until the next execute_request comes in.
425 # it to sit in memory until the next execute_request comes in.
419 shell.payload_manager.clear_payload()
426 shell.payload_manager.clear_payload()
420
427
421 # Flush output before sending the reply.
428 # Flush output before sending the reply.
422 sys.stdout.flush()
429 sys.stdout.flush()
423 sys.stderr.flush()
430 sys.stderr.flush()
424 # FIXME: on rare occasions, the flush doesn't seem to make it to the
431 # FIXME: on rare occasions, the flush doesn't seem to make it to the
425 # clients... This seems to mitigate the problem, but we definitely need
432 # clients... This seems to mitigate the problem, but we definitely need
426 # to better understand what's going on.
433 # to better understand what's going on.
427 if self._execute_sleep:
434 if self._execute_sleep:
428 time.sleep(self._execute_sleep)
435 time.sleep(self._execute_sleep)
429
436
430 # Send the reply.
437 # Send the reply.
431 reply_content = json_clean(reply_content)
438 reply_content = json_clean(reply_content)
432
439
433 md['status'] = reply_content['status']
440 md['status'] = reply_content['status']
434 if reply_content['status'] == 'error' and \
441 if reply_content['status'] == 'error' and \
435 reply_content['ename'] == 'UnmetDependency':
442 reply_content['ename'] == 'UnmetDependency':
436 md['dependencies_met'] = False
443 md['dependencies_met'] = False
437
444
438 reply_msg = self.session.send(stream, u'execute_reply',
445 reply_msg = self.session.send(stream, u'execute_reply',
439 reply_content, parent, metadata=md,
446 reply_content, parent, metadata=md,
440 ident=ident)
447 ident=ident)
441
448
442 self.log.debug("%s", reply_msg)
449 self.log.debug("%s", reply_msg)
443
450
444 if not silent and reply_msg['content']['status'] == u'error':
451 if not silent and reply_msg['content']['status'] == u'error':
445 self._abort_queues()
452 self._abort_queues()
446
453
447 self._publish_status(u'idle', parent)
454 self._publish_status(u'idle', parent)
448
455
449 def complete_request(self, stream, ident, parent):
456 def complete_request(self, stream, ident, parent):
450 txt, matches = self._complete(parent)
457 txt, matches = self._complete(parent)
451 matches = {'matches' : matches,
458 matches = {'matches' : matches,
452 'matched_text' : txt,
459 'matched_text' : txt,
453 'status' : 'ok'}
460 'status' : 'ok'}
454 matches = json_clean(matches)
461 matches = json_clean(matches)
455 completion_msg = self.session.send(stream, 'complete_reply',
462 completion_msg = self.session.send(stream, 'complete_reply',
456 matches, parent, ident)
463 matches, parent, ident)
457 self.log.debug("%s", completion_msg)
464 self.log.debug("%s", completion_msg)
458
465
459 def object_info_request(self, stream, ident, parent):
466 def object_info_request(self, stream, ident, parent):
460 content = parent['content']
467 content = parent['content']
461 object_info = self.shell.object_inspect(content['oname'],
468 object_info = self.shell.object_inspect(content['oname'],
462 detail_level = content.get('detail_level', 0)
469 detail_level = content.get('detail_level', 0)
463 )
470 )
464 # Before we send this object over, we scrub it for JSON usage
471 # Before we send this object over, we scrub it for JSON usage
465 oinfo = json_clean(object_info)
472 oinfo = json_clean(object_info)
466 msg = self.session.send(stream, 'object_info_reply',
473 msg = self.session.send(stream, 'object_info_reply',
467 oinfo, parent, ident)
474 oinfo, parent, ident)
468 self.log.debug("%s", msg)
475 self.log.debug("%s", msg)
469
476
470 def history_request(self, stream, ident, parent):
477 def history_request(self, stream, ident, parent):
471 # We need to pull these out, as passing **kwargs doesn't work with
478 # We need to pull these out, as passing **kwargs doesn't work with
472 # unicode keys before Python 2.6.5.
479 # unicode keys before Python 2.6.5.
473 hist_access_type = parent['content']['hist_access_type']
480 hist_access_type = parent['content']['hist_access_type']
474 raw = parent['content']['raw']
481 raw = parent['content']['raw']
475 output = parent['content']['output']
482 output = parent['content']['output']
476 if hist_access_type == 'tail':
483 if hist_access_type == 'tail':
477 n = parent['content']['n']
484 n = parent['content']['n']
478 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
485 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
479 include_latest=True)
486 include_latest=True)
480
487
481 elif hist_access_type == 'range':
488 elif hist_access_type == 'range':
482 session = parent['content']['session']
489 session = parent['content']['session']
483 start = parent['content']['start']
490 start = parent['content']['start']
484 stop = parent['content']['stop']
491 stop = parent['content']['stop']
485 hist = self.shell.history_manager.get_range(session, start, stop,
492 hist = self.shell.history_manager.get_range(session, start, stop,
486 raw=raw, output=output)
493 raw=raw, output=output)
487
494
488 elif hist_access_type == 'search':
495 elif hist_access_type == 'search':
489 n = parent['content'].get('n')
496 n = parent['content'].get('n')
490 pattern = parent['content']['pattern']
497 pattern = parent['content']['pattern']
491 hist = self.shell.history_manager.search(pattern, raw=raw,
498 hist = self.shell.history_manager.search(pattern, raw=raw,
492 output=output, n=n)
499 output=output, n=n)
493
500
494 else:
501 else:
495 hist = []
502 hist = []
496 hist = list(hist)
503 hist = list(hist)
497 content = {'history' : hist}
504 content = {'history' : hist}
498 content = json_clean(content)
505 content = json_clean(content)
499 msg = self.session.send(stream, 'history_reply',
506 msg = self.session.send(stream, 'history_reply',
500 content, parent, ident)
507 content, parent, ident)
501 self.log.debug("Sending history reply with %i entries", len(hist))
508 self.log.debug("Sending history reply with %i entries", len(hist))
502
509
503 def connect_request(self, stream, ident, parent):
510 def connect_request(self, stream, ident, parent):
504 if self._recorded_ports is not None:
511 if self._recorded_ports is not None:
505 content = self._recorded_ports.copy()
512 content = self._recorded_ports.copy()
506 else:
513 else:
507 content = {}
514 content = {}
508 msg = self.session.send(stream, 'connect_reply',
515 msg = self.session.send(stream, 'connect_reply',
509 content, parent, ident)
516 content, parent, ident)
510 self.log.debug("%s", msg)
517 self.log.debug("%s", msg)
511
518
519 def kernel_info_request(self, stream, ident, parent):
520 vinfo = {
521 'protocol_version': protocol_version,
522 'ipython_version': ipython_version,
523 'language_version': language_version,
524 'language': 'python',
525 }
526 msg = self.session.send(stream, 'kernel_info_reply',
527 vinfo, parent, ident)
528 self.log.debug("%s", msg)
529
512 def shutdown_request(self, stream, ident, parent):
530 def shutdown_request(self, stream, ident, parent):
513 self.shell.exit_now = True
531 self.shell.exit_now = True
514 content = dict(status='ok')
532 content = dict(status='ok')
515 content.update(parent['content'])
533 content.update(parent['content'])
516 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
534 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
517 # same content, but different msg_id for broadcasting on IOPub
535 # same content, but different msg_id for broadcasting on IOPub
518 self._shutdown_message = self.session.msg(u'shutdown_reply',
536 self._shutdown_message = self.session.msg(u'shutdown_reply',
519 content, parent
537 content, parent
520 )
538 )
521
539
522 self._at_shutdown()
540 self._at_shutdown()
523 # call sys.exit after a short delay
541 # call sys.exit after a short delay
524 loop = ioloop.IOLoop.instance()
542 loop = ioloop.IOLoop.instance()
525 loop.add_timeout(time.time()+0.1, loop.stop)
543 loop.add_timeout(time.time()+0.1, loop.stop)
526
544
527 #---------------------------------------------------------------------------
545 #---------------------------------------------------------------------------
528 # Engine methods
546 # Engine methods
529 #---------------------------------------------------------------------------
547 #---------------------------------------------------------------------------
530
548
531 def apply_request(self, stream, ident, parent):
549 def apply_request(self, stream, ident, parent):
532 try:
550 try:
533 content = parent[u'content']
551 content = parent[u'content']
534 bufs = parent[u'buffers']
552 bufs = parent[u'buffers']
535 msg_id = parent['header']['msg_id']
553 msg_id = parent['header']['msg_id']
536 except:
554 except:
537 self.log.error("Got bad msg: %s", parent, exc_info=True)
555 self.log.error("Got bad msg: %s", parent, exc_info=True)
538 return
556 return
539
557
540 self._publish_status(u'busy', parent)
558 self._publish_status(u'busy', parent)
541
559
542 # Set the parent message of the display hook and out streams.
560 # Set the parent message of the display hook and out streams.
543 shell = self.shell
561 shell = self.shell
544 shell.displayhook.set_parent(parent)
562 shell.displayhook.set_parent(parent)
545 shell.display_pub.set_parent(parent)
563 shell.display_pub.set_parent(parent)
546 shell.data_pub.set_parent(parent)
564 shell.data_pub.set_parent(parent)
547 sys.stdout.set_parent(parent)
565 sys.stdout.set_parent(parent)
548 sys.stderr.set_parent(parent)
566 sys.stderr.set_parent(parent)
549
567
550 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
568 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
551 # self.iopub_socket.send(pyin_msg)
569 # self.iopub_socket.send(pyin_msg)
552 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
570 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
553 md = self._make_metadata(parent['metadata'])
571 md = self._make_metadata(parent['metadata'])
554 try:
572 try:
555 working = shell.user_ns
573 working = shell.user_ns
556
574
557 prefix = "_"+str(msg_id).replace("-","")+"_"
575 prefix = "_"+str(msg_id).replace("-","")+"_"
558
576
559 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
577 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
560
578
561 fname = getattr(f, '__name__', 'f')
579 fname = getattr(f, '__name__', 'f')
562
580
563 fname = prefix+"f"
581 fname = prefix+"f"
564 argname = prefix+"args"
582 argname = prefix+"args"
565 kwargname = prefix+"kwargs"
583 kwargname = prefix+"kwargs"
566 resultname = prefix+"result"
584 resultname = prefix+"result"
567
585
568 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
586 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
569 # print ns
587 # print ns
570 working.update(ns)
588 working.update(ns)
571 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
589 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
572 try:
590 try:
573 exec code in shell.user_global_ns, shell.user_ns
591 exec code in shell.user_global_ns, shell.user_ns
574 result = working.get(resultname)
592 result = working.get(resultname)
575 finally:
593 finally:
576 for key in ns.iterkeys():
594 for key in ns.iterkeys():
577 working.pop(key)
595 working.pop(key)
578
596
579 result_buf = serialize_object(result,
597 result_buf = serialize_object(result,
580 buffer_threshold=self.session.buffer_threshold,
598 buffer_threshold=self.session.buffer_threshold,
581 item_threshold=self.session.item_threshold,
599 item_threshold=self.session.item_threshold,
582 )
600 )
583
601
584 except:
602 except:
585 # invoke IPython traceback formatting
603 # invoke IPython traceback formatting
586 shell.showtraceback()
604 shell.showtraceback()
587 # FIXME - fish exception info out of shell, possibly left there by
605 # FIXME - fish exception info out of shell, possibly left there by
588 # run_code. We'll need to clean up this logic later.
606 # run_code. We'll need to clean up this logic later.
589 reply_content = {}
607 reply_content = {}
590 if shell._reply_content is not None:
608 if shell._reply_content is not None:
591 reply_content.update(shell._reply_content)
609 reply_content.update(shell._reply_content)
592 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
610 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
593 reply_content['engine_info'] = e_info
611 reply_content['engine_info'] = e_info
594 # reset after use
612 # reset after use
595 shell._reply_content = None
613 shell._reply_content = None
596
614
597 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
615 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
598 ident=self._topic('pyerr'))
616 ident=self._topic('pyerr'))
599 result_buf = []
617 result_buf = []
600
618
601 if reply_content['ename'] == 'UnmetDependency':
619 if reply_content['ename'] == 'UnmetDependency':
602 md['dependencies_met'] = False
620 md['dependencies_met'] = False
603 else:
621 else:
604 reply_content = {'status' : 'ok'}
622 reply_content = {'status' : 'ok'}
605
623
606 # put 'ok'/'error' status in header, for scheduler introspection:
624 # put 'ok'/'error' status in header, for scheduler introspection:
607 md['status'] = reply_content['status']
625 md['status'] = reply_content['status']
608
626
609 # flush i/o
627 # flush i/o
610 sys.stdout.flush()
628 sys.stdout.flush()
611 sys.stderr.flush()
629 sys.stderr.flush()
612
630
613 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
631 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
614 parent=parent, ident=ident,buffers=result_buf, metadata=md)
632 parent=parent, ident=ident,buffers=result_buf, metadata=md)
615
633
616 self._publish_status(u'idle', parent)
634 self._publish_status(u'idle', parent)
617
635
618 #---------------------------------------------------------------------------
636 #---------------------------------------------------------------------------
619 # Control messages
637 # Control messages
620 #---------------------------------------------------------------------------
638 #---------------------------------------------------------------------------
621
639
622 def abort_request(self, stream, ident, parent):
640 def abort_request(self, stream, ident, parent):
623 """abort a specifig msg by id"""
641 """abort a specifig msg by id"""
624 msg_ids = parent['content'].get('msg_ids', None)
642 msg_ids = parent['content'].get('msg_ids', None)
625 if isinstance(msg_ids, basestring):
643 if isinstance(msg_ids, basestring):
626 msg_ids = [msg_ids]
644 msg_ids = [msg_ids]
627 if not msg_ids:
645 if not msg_ids:
628 self.abort_queues()
646 self.abort_queues()
629 for mid in msg_ids:
647 for mid in msg_ids:
630 self.aborted.add(str(mid))
648 self.aborted.add(str(mid))
631
649
632 content = dict(status='ok')
650 content = dict(status='ok')
633 reply_msg = self.session.send(stream, 'abort_reply', content=content,
651 reply_msg = self.session.send(stream, 'abort_reply', content=content,
634 parent=parent, ident=ident)
652 parent=parent, ident=ident)
635 self.log.debug("%s", reply_msg)
653 self.log.debug("%s", reply_msg)
636
654
637 def clear_request(self, stream, idents, parent):
655 def clear_request(self, stream, idents, parent):
638 """Clear our namespace."""
656 """Clear our namespace."""
639 self.shell.reset(False)
657 self.shell.reset(False)
640 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
658 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
641 content = dict(status='ok'))
659 content = dict(status='ok'))
642
660
643
661
644 #---------------------------------------------------------------------------
662 #---------------------------------------------------------------------------
645 # Protected interface
663 # Protected interface
646 #---------------------------------------------------------------------------
664 #---------------------------------------------------------------------------
647
665
648
666
649 def _wrap_exception(self, method=None):
667 def _wrap_exception(self, method=None):
650 # import here, because _wrap_exception is only used in parallel,
668 # import here, because _wrap_exception is only used in parallel,
651 # and parallel has higher min pyzmq version
669 # and parallel has higher min pyzmq version
652 from IPython.parallel.error import wrap_exception
670 from IPython.parallel.error import wrap_exception
653 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
671 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
654 content = wrap_exception(e_info)
672 content = wrap_exception(e_info)
655 return content
673 return content
656
674
657 def _topic(self, topic):
675 def _topic(self, topic):
658 """prefixed topic for IOPub messages"""
676 """prefixed topic for IOPub messages"""
659 if self.int_id >= 0:
677 if self.int_id >= 0:
660 base = "engine.%i" % self.int_id
678 base = "engine.%i" % self.int_id
661 else:
679 else:
662 base = "kernel.%s" % self.ident
680 base = "kernel.%s" % self.ident
663
681
664 return py3compat.cast_bytes("%s.%s" % (base, topic))
682 return py3compat.cast_bytes("%s.%s" % (base, topic))
665
683
666 def _abort_queues(self):
684 def _abort_queues(self):
667 for stream in self.shell_streams:
685 for stream in self.shell_streams:
668 if stream:
686 if stream:
669 self._abort_queue(stream)
687 self._abort_queue(stream)
670
688
671 def _abort_queue(self, stream):
689 def _abort_queue(self, stream):
672 poller = zmq.Poller()
690 poller = zmq.Poller()
673 poller.register(stream.socket, zmq.POLLIN)
691 poller.register(stream.socket, zmq.POLLIN)
674 while True:
692 while True:
675 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
693 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
676 if msg is None:
694 if msg is None:
677 return
695 return
678
696
679 self.log.info("Aborting:")
697 self.log.info("Aborting:")
680 self.log.info("%s", msg)
698 self.log.info("%s", msg)
681 msg_type = msg['header']['msg_type']
699 msg_type = msg['header']['msg_type']
682 reply_type = msg_type.split('_')[0] + '_reply'
700 reply_type = msg_type.split('_')[0] + '_reply'
683
701
684 status = {'status' : 'aborted'}
702 status = {'status' : 'aborted'}
685 md = {'engine' : self.ident}
703 md = {'engine' : self.ident}
686 md.update(status)
704 md.update(status)
687 reply_msg = self.session.send(stream, reply_type, metadata=md,
705 reply_msg = self.session.send(stream, reply_type, metadata=md,
688 content=status, parent=msg, ident=idents)
706 content=status, parent=msg, ident=idents)
689 self.log.debug("%s", reply_msg)
707 self.log.debug("%s", reply_msg)
690 # We need to wait a bit for requests to come in. This can probably
708 # We need to wait a bit for requests to come in. This can probably
691 # be set shorter for true asynchronous clients.
709 # be set shorter for true asynchronous clients.
692 poller.poll(50)
710 poller.poll(50)
693
711
694
712
695 def _no_raw_input(self):
713 def _no_raw_input(self):
696 """Raise StdinNotImplentedError if active frontend doesn't support
714 """Raise StdinNotImplentedError if active frontend doesn't support
697 stdin."""
715 stdin."""
698 raise StdinNotImplementedError("raw_input was called, but this "
716 raise StdinNotImplementedError("raw_input was called, but this "
699 "frontend does not support stdin.")
717 "frontend does not support stdin.")
700
718
701 def _raw_input(self, prompt, ident, parent):
719 def _raw_input(self, prompt, ident, parent):
702 # Flush output before making the request.
720 # Flush output before making the request.
703 sys.stderr.flush()
721 sys.stderr.flush()
704 sys.stdout.flush()
722 sys.stdout.flush()
705
723
706 # Send the input request.
724 # Send the input request.
707 content = json_clean(dict(prompt=prompt))
725 content = json_clean(dict(prompt=prompt))
708 self.session.send(self.stdin_socket, u'input_request', content, parent,
726 self.session.send(self.stdin_socket, u'input_request', content, parent,
709 ident=ident)
727 ident=ident)
710
728
711 # Await a response.
729 # Await a response.
712 while True:
730 while True:
713 try:
731 try:
714 ident, reply = self.session.recv(self.stdin_socket, 0)
732 ident, reply = self.session.recv(self.stdin_socket, 0)
715 except Exception:
733 except Exception:
716 self.log.warn("Invalid Message:", exc_info=True)
734 self.log.warn("Invalid Message:", exc_info=True)
717 else:
735 else:
718 break
736 break
719 try:
737 try:
720 value = reply['content']['value']
738 value = reply['content']['value']
721 except:
739 except:
722 self.log.error("Got bad raw_input reply: ")
740 self.log.error("Got bad raw_input reply: ")
723 self.log.error("%s", parent)
741 self.log.error("%s", parent)
724 value = ''
742 value = ''
725 if value == '\x04':
743 if value == '\x04':
726 # EOF
744 # EOF
727 raise EOFError
745 raise EOFError
728 return value
746 return value
729
747
730 def _complete(self, msg):
748 def _complete(self, msg):
731 c = msg['content']
749 c = msg['content']
732 try:
750 try:
733 cpos = int(c['cursor_pos'])
751 cpos = int(c['cursor_pos'])
734 except:
752 except:
735 # If we don't get something that we can convert to an integer, at
753 # If we don't get something that we can convert to an integer, at
736 # least attempt the completion guessing the cursor is at the end of
754 # least attempt the completion guessing the cursor is at the end of
737 # the text, if there's any, and otherwise of the line
755 # the text, if there's any, and otherwise of the line
738 cpos = len(c['text'])
756 cpos = len(c['text'])
739 if cpos==0:
757 if cpos==0:
740 cpos = len(c['line'])
758 cpos = len(c['line'])
741 return self.shell.complete(c['text'], c['line'], cpos)
759 return self.shell.complete(c['text'], c['line'], cpos)
742
760
743 def _object_info(self, context):
761 def _object_info(self, context):
744 symbol, leftover = self._symbol_from_context(context)
762 symbol, leftover = self._symbol_from_context(context)
745 if symbol is not None and not leftover:
763 if symbol is not None and not leftover:
746 doc = getattr(symbol, '__doc__', '')
764 doc = getattr(symbol, '__doc__', '')
747 else:
765 else:
748 doc = ''
766 doc = ''
749 object_info = dict(docstring = doc)
767 object_info = dict(docstring = doc)
750 return object_info
768 return object_info
751
769
752 def _symbol_from_context(self, context):
770 def _symbol_from_context(self, context):
753 if not context:
771 if not context:
754 return None, context
772 return None, context
755
773
756 base_symbol_string = context[0]
774 base_symbol_string = context[0]
757 symbol = self.shell.user_ns.get(base_symbol_string, None)
775 symbol = self.shell.user_ns.get(base_symbol_string, None)
758 if symbol is None:
776 if symbol is None:
759 symbol = __builtin__.__dict__.get(base_symbol_string, None)
777 symbol = __builtin__.__dict__.get(base_symbol_string, None)
760 if symbol is None:
778 if symbol is None:
761 return None, context
779 return None, context
762
780
763 context = context[1:]
781 context = context[1:]
764 for i, name in enumerate(context):
782 for i, name in enumerate(context):
765 new_symbol = getattr(symbol, name, None)
783 new_symbol = getattr(symbol, name, None)
766 if new_symbol is None:
784 if new_symbol is None:
767 return symbol, context[i:]
785 return symbol, context[i:]
768 else:
786 else:
769 symbol = new_symbol
787 symbol = new_symbol
770
788
771 return symbol, []
789 return symbol, []
772
790
773 def _at_shutdown(self):
791 def _at_shutdown(self):
774 """Actions taken at shutdown by the kernel, called by python's atexit.
792 """Actions taken at shutdown by the kernel, called by python's atexit.
775 """
793 """
776 # io.rprint("Kernel at_shutdown") # dbg
794 # io.rprint("Kernel at_shutdown") # dbg
777 if self._shutdown_message is not None:
795 if self._shutdown_message is not None:
778 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
796 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
779 self.log.debug("%s", self._shutdown_message)
797 self.log.debug("%s", self._shutdown_message)
780 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
798 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
781
799
782 #-----------------------------------------------------------------------------
800 #-----------------------------------------------------------------------------
783 # Aliases and Flags for the IPKernelApp
801 # Aliases and Flags for the IPKernelApp
784 #-----------------------------------------------------------------------------
802 #-----------------------------------------------------------------------------
785
803
786 flags = dict(kernel_flags)
804 flags = dict(kernel_flags)
787 flags.update(shell_flags)
805 flags.update(shell_flags)
788
806
789 addflag = lambda *args: flags.update(boolean_flag(*args))
807 addflag = lambda *args: flags.update(boolean_flag(*args))
790
808
791 flags['pylab'] = (
809 flags['pylab'] = (
792 {'IPKernelApp' : {'pylab' : 'auto'}},
810 {'IPKernelApp' : {'pylab' : 'auto'}},
793 """Pre-load matplotlib and numpy for interactive use with
811 """Pre-load matplotlib and numpy for interactive use with
794 the default matplotlib backend."""
812 the default matplotlib backend."""
795 )
813 )
796
814
797 aliases = dict(kernel_aliases)
815 aliases = dict(kernel_aliases)
798 aliases.update(shell_aliases)
816 aliases.update(shell_aliases)
799
817
800 #-----------------------------------------------------------------------------
818 #-----------------------------------------------------------------------------
801 # The IPKernelApp class
819 # The IPKernelApp class
802 #-----------------------------------------------------------------------------
820 #-----------------------------------------------------------------------------
803
821
804 class IPKernelApp(KernelApp, InteractiveShellApp):
822 class IPKernelApp(KernelApp, InteractiveShellApp):
805 name = 'ipkernel'
823 name = 'ipkernel'
806
824
807 aliases = Dict(aliases)
825 aliases = Dict(aliases)
808 flags = Dict(flags)
826 flags = Dict(flags)
809 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
827 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
810
828
811 @catch_config_error
829 @catch_config_error
812 def initialize(self, argv=None):
830 def initialize(self, argv=None):
813 super(IPKernelApp, self).initialize(argv)
831 super(IPKernelApp, self).initialize(argv)
814 self.init_path()
832 self.init_path()
815 self.init_shell()
833 self.init_shell()
816 self.init_gui_pylab()
834 self.init_gui_pylab()
817 self.init_extensions()
835 self.init_extensions()
818 self.init_code()
836 self.init_code()
819
837
820 def init_kernel(self):
838 def init_kernel(self):
821
839
822 shell_stream = ZMQStream(self.shell_socket)
840 shell_stream = ZMQStream(self.shell_socket)
823
841
824 kernel = Kernel(config=self.config, session=self.session,
842 kernel = Kernel(config=self.config, session=self.session,
825 shell_streams=[shell_stream],
843 shell_streams=[shell_stream],
826 iopub_socket=self.iopub_socket,
844 iopub_socket=self.iopub_socket,
827 stdin_socket=self.stdin_socket,
845 stdin_socket=self.stdin_socket,
828 log=self.log,
846 log=self.log,
829 profile_dir=self.profile_dir,
847 profile_dir=self.profile_dir,
830 )
848 )
831 self.kernel = kernel
849 self.kernel = kernel
832 kernel.record_ports(self.ports)
850 kernel.record_ports(self.ports)
833 shell = kernel.shell
851 shell = kernel.shell
834
852
835 def init_gui_pylab(self):
853 def init_gui_pylab(self):
836 """Enable GUI event loop integration, taking pylab into account."""
854 """Enable GUI event loop integration, taking pylab into account."""
837
855
838 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
856 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
839 # to ensure that any exception is printed straight to stderr.
857 # to ensure that any exception is printed straight to stderr.
840 # Normally _showtraceback associates the reply with an execution,
858 # Normally _showtraceback associates the reply with an execution,
841 # which means frontends will never draw it, as this exception
859 # which means frontends will never draw it, as this exception
842 # is not associated with any execute request.
860 # is not associated with any execute request.
843
861
844 shell = self.shell
862 shell = self.shell
845 _showtraceback = shell._showtraceback
863 _showtraceback = shell._showtraceback
846 try:
864 try:
847 # replace pyerr-sending traceback with stderr
865 # replace pyerr-sending traceback with stderr
848 def print_tb(etype, evalue, stb):
866 def print_tb(etype, evalue, stb):
849 print ("GUI event loop or pylab initialization failed",
867 print ("GUI event loop or pylab initialization failed",
850 file=io.stderr)
868 file=io.stderr)
851 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
869 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
852 shell._showtraceback = print_tb
870 shell._showtraceback = print_tb
853 InteractiveShellApp.init_gui_pylab(self)
871 InteractiveShellApp.init_gui_pylab(self)
854 finally:
872 finally:
855 shell._showtraceback = _showtraceback
873 shell._showtraceback = _showtraceback
856
874
857 def init_shell(self):
875 def init_shell(self):
858 self.shell = self.kernel.shell
876 self.shell = self.kernel.shell
859 self.shell.configurables.append(self)
877 self.shell.configurables.append(self)
860
878
861
879
862 #-----------------------------------------------------------------------------
880 #-----------------------------------------------------------------------------
863 # Kernel main and launch functions
881 # Kernel main and launch functions
864 #-----------------------------------------------------------------------------
882 #-----------------------------------------------------------------------------
865
883
866 def launch_kernel(*args, **kwargs):
884 def launch_kernel(*args, **kwargs):
867 """Launches a localhost IPython kernel, binding to the specified ports.
885 """Launches a localhost IPython kernel, binding to the specified ports.
868
886
869 This function simply calls entry_point.base_launch_kernel with the right
887 This function simply calls entry_point.base_launch_kernel with the right
870 first command to start an ipkernel. See base_launch_kernel for arguments.
888 first command to start an ipkernel. See base_launch_kernel for arguments.
871
889
872 Returns
890 Returns
873 -------
891 -------
874 A tuple of form:
892 A tuple of form:
875 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
893 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
876 where kernel_process is a Popen object and the ports are integers.
894 where kernel_process is a Popen object and the ports are integers.
877 """
895 """
878 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
896 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
879 *args, **kwargs)
897 *args, **kwargs)
880
898
881
899
882 def embed_kernel(module=None, local_ns=None, **kwargs):
900 def embed_kernel(module=None, local_ns=None, **kwargs):
883 """Embed and start an IPython kernel in a given scope.
901 """Embed and start an IPython kernel in a given scope.
884
902
885 Parameters
903 Parameters
886 ----------
904 ----------
887 module : ModuleType, optional
905 module : ModuleType, optional
888 The module to load into IPython globals (default: caller)
906 The module to load into IPython globals (default: caller)
889 local_ns : dict, optional
907 local_ns : dict, optional
890 The namespace to load into IPython user namespace (default: caller)
908 The namespace to load into IPython user namespace (default: caller)
891
909
892 kwargs : various, optional
910 kwargs : various, optional
893 Further keyword args are relayed to the KernelApp constructor,
911 Further keyword args are relayed to the KernelApp constructor,
894 allowing configuration of the Kernel. Will only have an effect
912 allowing configuration of the Kernel. Will only have an effect
895 on the first embed_kernel call for a given process.
913 on the first embed_kernel call for a given process.
896
914
897 """
915 """
898 # get the app if it exists, or set it up if it doesn't
916 # get the app if it exists, or set it up if it doesn't
899 if IPKernelApp.initialized():
917 if IPKernelApp.initialized():
900 app = IPKernelApp.instance()
918 app = IPKernelApp.instance()
901 else:
919 else:
902 app = IPKernelApp.instance(**kwargs)
920 app = IPKernelApp.instance(**kwargs)
903 app.initialize([])
921 app.initialize([])
904 # Undo unnecessary sys module mangling from init_sys_modules.
922 # Undo unnecessary sys module mangling from init_sys_modules.
905 # This would not be necessary if we could prevent it
923 # This would not be necessary if we could prevent it
906 # in the first place by using a different InteractiveShell
924 # in the first place by using a different InteractiveShell
907 # subclass, as in the regular embed case.
925 # subclass, as in the regular embed case.
908 main = app.kernel.shell._orig_sys_modules_main_mod
926 main = app.kernel.shell._orig_sys_modules_main_mod
909 if main is not None:
927 if main is not None:
910 sys.modules[app.kernel.shell._orig_sys_modules_main_name] = main
928 sys.modules[app.kernel.shell._orig_sys_modules_main_name] = main
911
929
912 # load the calling scope if not given
930 # load the calling scope if not given
913 (caller_module, caller_locals) = extract_module_locals(1)
931 (caller_module, caller_locals) = extract_module_locals(1)
914 if module is None:
932 if module is None:
915 module = caller_module
933 module = caller_module
916 if local_ns is None:
934 if local_ns is None:
917 local_ns = caller_locals
935 local_ns = caller_locals
918
936
919 app.kernel.user_module = module
937 app.kernel.user_module = module
920 app.kernel.user_ns = local_ns
938 app.kernel.user_ns = local_ns
921 app.shell.set_completer_frame()
939 app.shell.set_completer_frame()
922 app.start()
940 app.start()
923
941
924 def main():
942 def main():
925 """Run an IPKernel as an application"""
943 """Run an IPKernel as an application"""
926 app = IPKernelApp.instance()
944 app = IPKernelApp.instance()
927 app.initialize()
945 app.initialize()
928 app.start()
946 app.start()
929
947
930
948
931 if __name__ == '__main__':
949 if __name__ == '__main__':
932 main()
950 main()
@@ -1,1042 +1,1048 b''
1 """Base classes to manage the interaction with a running kernel.
1 """Base classes to manage the interaction with a running kernel.
2
2
3 TODO
3 TODO
4 * Create logger to handle debugging and console messages.
4 * Create logger to handle debugging and console messages.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2011 The IPython Development Team
8 # Copyright (C) 2008-2011 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import atexit
19 import atexit
20 import errno
20 import errno
21 import json
21 import json
22 from subprocess import Popen
22 from subprocess import Popen
23 import os
23 import os
24 import signal
24 import signal
25 import sys
25 import sys
26 from threading import Thread
26 from threading import Thread
27 import time
27 import time
28
28
29 # System library imports.
29 # System library imports.
30 import zmq
30 import zmq
31 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
31 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
32 # during garbage collection of threads at exit:
32 # during garbage collection of threads at exit:
33 from zmq import ZMQError
33 from zmq import ZMQError
34 from zmq.eventloop import ioloop, zmqstream
34 from zmq.eventloop import ioloop, zmqstream
35
35
36 # Local imports.
36 # Local imports.
37 from IPython.config.loader import Config
37 from IPython.config.loader import Config
38 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
38 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
39 from IPython.utils.traitlets import (
39 from IPython.utils.traitlets import (
40 HasTraits, Any, Instance, Type, Unicode, Integer, Bool, CaselessStrEnum
40 HasTraits, Any, Instance, Type, Unicode, Integer, Bool, CaselessStrEnum
41 )
41 )
42 from IPython.utils.py3compat import str_to_bytes
42 from IPython.utils.py3compat import str_to_bytes
43 from IPython.zmq.entry_point import write_connection_file
43 from IPython.zmq.entry_point import write_connection_file
44 from session import Session
44 from session import Session
45
45
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Constants and exceptions
47 # Constants and exceptions
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49
49
50 class InvalidPortNumber(Exception):
50 class InvalidPortNumber(Exception):
51 pass
51 pass
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Utility functions
54 # Utility functions
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 # some utilities to validate message structure, these might get moved elsewhere
57 # some utilities to validate message structure, these might get moved elsewhere
58 # if they prove to have more generic utility
58 # if they prove to have more generic utility
59
59
60 def validate_string_list(lst):
60 def validate_string_list(lst):
61 """Validate that the input is a list of strings.
61 """Validate that the input is a list of strings.
62
62
63 Raises ValueError if not."""
63 Raises ValueError if not."""
64 if not isinstance(lst, list):
64 if not isinstance(lst, list):
65 raise ValueError('input %r must be a list' % lst)
65 raise ValueError('input %r must be a list' % lst)
66 for x in lst:
66 for x in lst:
67 if not isinstance(x, basestring):
67 if not isinstance(x, basestring):
68 raise ValueError('element %r in list must be a string' % x)
68 raise ValueError('element %r in list must be a string' % x)
69
69
70
70
71 def validate_string_dict(dct):
71 def validate_string_dict(dct):
72 """Validate that the input is a dict with string keys and values.
72 """Validate that the input is a dict with string keys and values.
73
73
74 Raises ValueError if not."""
74 Raises ValueError if not."""
75 for k,v in dct.iteritems():
75 for k,v in dct.iteritems():
76 if not isinstance(k, basestring):
76 if not isinstance(k, basestring):
77 raise ValueError('key %r in dict must be a string' % k)
77 raise ValueError('key %r in dict must be a string' % k)
78 if not isinstance(v, basestring):
78 if not isinstance(v, basestring):
79 raise ValueError('value %r in dict must be a string' % v)
79 raise ValueError('value %r in dict must be a string' % v)
80
80
81
81
82 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
83 # ZMQ Socket Channel classes
83 # ZMQ Socket Channel classes
84 #-----------------------------------------------------------------------------
84 #-----------------------------------------------------------------------------
85
85
86 class ZMQSocketChannel(Thread):
86 class ZMQSocketChannel(Thread):
87 """The base class for the channels that use ZMQ sockets.
87 """The base class for the channels that use ZMQ sockets.
88 """
88 """
89 context = None
89 context = None
90 session = None
90 session = None
91 socket = None
91 socket = None
92 ioloop = None
92 ioloop = None
93 stream = None
93 stream = None
94 _address = None
94 _address = None
95 _exiting = False
95 _exiting = False
96
96
97 def __init__(self, context, session, address):
97 def __init__(self, context, session, address):
98 """Create a channel
98 """Create a channel
99
99
100 Parameters
100 Parameters
101 ----------
101 ----------
102 context : :class:`zmq.Context`
102 context : :class:`zmq.Context`
103 The ZMQ context to use.
103 The ZMQ context to use.
104 session : :class:`session.Session`
104 session : :class:`session.Session`
105 The session to use.
105 The session to use.
106 address : zmq url
106 address : zmq url
107 Standard (ip, port) tuple that the kernel is listening on.
107 Standard (ip, port) tuple that the kernel is listening on.
108 """
108 """
109 super(ZMQSocketChannel, self).__init__()
109 super(ZMQSocketChannel, self).__init__()
110 self.daemon = True
110 self.daemon = True
111
111
112 self.context = context
112 self.context = context
113 self.session = session
113 self.session = session
114 if isinstance(address, tuple):
114 if isinstance(address, tuple):
115 if address[1] == 0:
115 if address[1] == 0:
116 message = 'The port number for a channel cannot be 0.'
116 message = 'The port number for a channel cannot be 0.'
117 raise InvalidPortNumber(message)
117 raise InvalidPortNumber(message)
118 address = "tcp://%s:%i" % address
118 address = "tcp://%s:%i" % address
119 self._address = address
119 self._address = address
120 atexit.register(self._notice_exit)
120 atexit.register(self._notice_exit)
121
121
122 def _notice_exit(self):
122 def _notice_exit(self):
123 self._exiting = True
123 self._exiting = True
124
124
125 def _run_loop(self):
125 def _run_loop(self):
126 """Run my loop, ignoring EINTR events in the poller"""
126 """Run my loop, ignoring EINTR events in the poller"""
127 while True:
127 while True:
128 try:
128 try:
129 self.ioloop.start()
129 self.ioloop.start()
130 except ZMQError as e:
130 except ZMQError as e:
131 if e.errno == errno.EINTR:
131 if e.errno == errno.EINTR:
132 continue
132 continue
133 else:
133 else:
134 raise
134 raise
135 except Exception:
135 except Exception:
136 if self._exiting:
136 if self._exiting:
137 break
137 break
138 else:
138 else:
139 raise
139 raise
140 else:
140 else:
141 break
141 break
142
142
143 def stop(self):
143 def stop(self):
144 """Stop the channel's activity.
144 """Stop the channel's activity.
145
145
146 This calls :method:`Thread.join` and returns when the thread
146 This calls :method:`Thread.join` and returns when the thread
147 terminates. :class:`RuntimeError` will be raised if
147 terminates. :class:`RuntimeError` will be raised if
148 :method:`self.start` is called again.
148 :method:`self.start` is called again.
149 """
149 """
150 self.join()
150 self.join()
151
151
152 @property
152 @property
153 def address(self):
153 def address(self):
154 """Get the channel's address as a zmq url string ('tcp://127.0.0.1:5555').
154 """Get the channel's address as a zmq url string ('tcp://127.0.0.1:5555').
155 """
155 """
156 return self._address
156 return self._address
157
157
158 def _queue_send(self, msg):
158 def _queue_send(self, msg):
159 """Queue a message to be sent from the IOLoop's thread.
159 """Queue a message to be sent from the IOLoop's thread.
160
160
161 Parameters
161 Parameters
162 ----------
162 ----------
163 msg : message to send
163 msg : message to send
164
164
165 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
165 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
166 thread control of the action.
166 thread control of the action.
167 """
167 """
168 def thread_send():
168 def thread_send():
169 self.session.send(self.stream, msg)
169 self.session.send(self.stream, msg)
170 self.ioloop.add_callback(thread_send)
170 self.ioloop.add_callback(thread_send)
171
171
172 def _handle_recv(self, msg):
172 def _handle_recv(self, msg):
173 """callback for stream.on_recv
173 """callback for stream.on_recv
174
174
175 unpacks message, and calls handlers with it.
175 unpacks message, and calls handlers with it.
176 """
176 """
177 ident,smsg = self.session.feed_identities(msg)
177 ident,smsg = self.session.feed_identities(msg)
178 self.call_handlers(self.session.unserialize(smsg))
178 self.call_handlers(self.session.unserialize(smsg))
179
179
180
180
181
181
182 class ShellSocketChannel(ZMQSocketChannel):
182 class ShellSocketChannel(ZMQSocketChannel):
183 """The DEALER channel for issues request/replies to the kernel.
183 """The DEALER channel for issues request/replies to the kernel.
184 """
184 """
185
185
186 command_queue = None
186 command_queue = None
187 # flag for whether execute requests should be allowed to call raw_input:
187 # flag for whether execute requests should be allowed to call raw_input:
188 allow_stdin = True
188 allow_stdin = True
189
189
190 def __init__(self, context, session, address):
190 def __init__(self, context, session, address):
191 super(ShellSocketChannel, self).__init__(context, session, address)
191 super(ShellSocketChannel, self).__init__(context, session, address)
192 self.ioloop = ioloop.IOLoop()
192 self.ioloop = ioloop.IOLoop()
193
193
194 def run(self):
194 def run(self):
195 """The thread's main activity. Call start() instead."""
195 """The thread's main activity. Call start() instead."""
196 self.socket = self.context.socket(zmq.DEALER)
196 self.socket = self.context.socket(zmq.DEALER)
197 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
197 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
198 self.socket.connect(self.address)
198 self.socket.connect(self.address)
199 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
199 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
200 self.stream.on_recv(self._handle_recv)
200 self.stream.on_recv(self._handle_recv)
201 self._run_loop()
201 self._run_loop()
202 try:
202 try:
203 self.socket.close()
203 self.socket.close()
204 except:
204 except:
205 pass
205 pass
206
206
207 def stop(self):
207 def stop(self):
208 self.ioloop.stop()
208 self.ioloop.stop()
209 super(ShellSocketChannel, self).stop()
209 super(ShellSocketChannel, self).stop()
210
210
211 def call_handlers(self, msg):
211 def call_handlers(self, msg):
212 """This method is called in the ioloop thread when a message arrives.
212 """This method is called in the ioloop thread when a message arrives.
213
213
214 Subclasses should override this method to handle incoming messages.
214 Subclasses should override this method to handle incoming messages.
215 It is important to remember that this method is called in the thread
215 It is important to remember that this method is called in the thread
216 so that some logic must be done to ensure that the application leve
216 so that some logic must be done to ensure that the application leve
217 handlers are called in the application thread.
217 handlers are called in the application thread.
218 """
218 """
219 raise NotImplementedError('call_handlers must be defined in a subclass.')
219 raise NotImplementedError('call_handlers must be defined in a subclass.')
220
220
221 def execute(self, code, silent=False, store_history=True,
221 def execute(self, code, silent=False, store_history=True,
222 user_variables=None, user_expressions=None, allow_stdin=None):
222 user_variables=None, user_expressions=None, allow_stdin=None):
223 """Execute code in the kernel.
223 """Execute code in the kernel.
224
224
225 Parameters
225 Parameters
226 ----------
226 ----------
227 code : str
227 code : str
228 A string of Python code.
228 A string of Python code.
229
229
230 silent : bool, optional (default False)
230 silent : bool, optional (default False)
231 If set, the kernel will execute the code as quietly possible, and
231 If set, the kernel will execute the code as quietly possible, and
232 will force store_history to be False.
232 will force store_history to be False.
233
233
234 store_history : bool, optional (default True)
234 store_history : bool, optional (default True)
235 If set, the kernel will store command history. This is forced
235 If set, the kernel will store command history. This is forced
236 to be False if silent is True.
236 to be False if silent is True.
237
237
238 user_variables : list, optional
238 user_variables : list, optional
239 A list of variable names to pull from the user's namespace. They
239 A list of variable names to pull from the user's namespace. They
240 will come back as a dict with these names as keys and their
240 will come back as a dict with these names as keys and their
241 :func:`repr` as values.
241 :func:`repr` as values.
242
242
243 user_expressions : dict, optional
243 user_expressions : dict, optional
244 A dict mapping names to expressions to be evaluated in the user's
244 A dict mapping names to expressions to be evaluated in the user's
245 dict. The expression values are returned as strings formatted using
245 dict. The expression values are returned as strings formatted using
246 :func:`repr`.
246 :func:`repr`.
247
247
248 allow_stdin : bool, optional (default self.allow_stdin)
248 allow_stdin : bool, optional (default self.allow_stdin)
249 Flag for whether the kernel can send stdin requests to frontends.
249 Flag for whether the kernel can send stdin requests to frontends.
250
250
251 Some frontends (e.g. the Notebook) do not support stdin requests.
251 Some frontends (e.g. the Notebook) do not support stdin requests.
252 If raw_input is called from code executed from such a frontend, a
252 If raw_input is called from code executed from such a frontend, a
253 StdinNotImplementedError will be raised.
253 StdinNotImplementedError will be raised.
254
254
255 Returns
255 Returns
256 -------
256 -------
257 The msg_id of the message sent.
257 The msg_id of the message sent.
258 """
258 """
259 if user_variables is None:
259 if user_variables is None:
260 user_variables = []
260 user_variables = []
261 if user_expressions is None:
261 if user_expressions is None:
262 user_expressions = {}
262 user_expressions = {}
263 if allow_stdin is None:
263 if allow_stdin is None:
264 allow_stdin = self.allow_stdin
264 allow_stdin = self.allow_stdin
265
265
266
266
267 # Don't waste network traffic if inputs are invalid
267 # Don't waste network traffic if inputs are invalid
268 if not isinstance(code, basestring):
268 if not isinstance(code, basestring):
269 raise ValueError('code %r must be a string' % code)
269 raise ValueError('code %r must be a string' % code)
270 validate_string_list(user_variables)
270 validate_string_list(user_variables)
271 validate_string_dict(user_expressions)
271 validate_string_dict(user_expressions)
272
272
273 # Create class for content/msg creation. Related to, but possibly
273 # Create class for content/msg creation. Related to, but possibly
274 # not in Session.
274 # not in Session.
275 content = dict(code=code, silent=silent, store_history=store_history,
275 content = dict(code=code, silent=silent, store_history=store_history,
276 user_variables=user_variables,
276 user_variables=user_variables,
277 user_expressions=user_expressions,
277 user_expressions=user_expressions,
278 allow_stdin=allow_stdin,
278 allow_stdin=allow_stdin,
279 )
279 )
280 msg = self.session.msg('execute_request', content)
280 msg = self.session.msg('execute_request', content)
281 self._queue_send(msg)
281 self._queue_send(msg)
282 return msg['header']['msg_id']
282 return msg['header']['msg_id']
283
283
284 def complete(self, text, line, cursor_pos, block=None):
284 def complete(self, text, line, cursor_pos, block=None):
285 """Tab complete text in the kernel's namespace.
285 """Tab complete text in the kernel's namespace.
286
286
287 Parameters
287 Parameters
288 ----------
288 ----------
289 text : str
289 text : str
290 The text to complete.
290 The text to complete.
291 line : str
291 line : str
292 The full line of text that is the surrounding context for the
292 The full line of text that is the surrounding context for the
293 text to complete.
293 text to complete.
294 cursor_pos : int
294 cursor_pos : int
295 The position of the cursor in the line where the completion was
295 The position of the cursor in the line where the completion was
296 requested.
296 requested.
297 block : str, optional
297 block : str, optional
298 The full block of code in which the completion is being requested.
298 The full block of code in which the completion is being requested.
299
299
300 Returns
300 Returns
301 -------
301 -------
302 The msg_id of the message sent.
302 The msg_id of the message sent.
303 """
303 """
304 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
304 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
305 msg = self.session.msg('complete_request', content)
305 msg = self.session.msg('complete_request', content)
306 self._queue_send(msg)
306 self._queue_send(msg)
307 return msg['header']['msg_id']
307 return msg['header']['msg_id']
308
308
309 def object_info(self, oname, detail_level=0):
309 def object_info(self, oname, detail_level=0):
310 """Get metadata information about an object.
310 """Get metadata information about an object.
311
311
312 Parameters
312 Parameters
313 ----------
313 ----------
314 oname : str
314 oname : str
315 A string specifying the object name.
315 A string specifying the object name.
316 detail_level : int, optional
316 detail_level : int, optional
317 The level of detail for the introspection (0-2)
317 The level of detail for the introspection (0-2)
318
318
319 Returns
319 Returns
320 -------
320 -------
321 The msg_id of the message sent.
321 The msg_id of the message sent.
322 """
322 """
323 content = dict(oname=oname, detail_level=detail_level)
323 content = dict(oname=oname, detail_level=detail_level)
324 msg = self.session.msg('object_info_request', content)
324 msg = self.session.msg('object_info_request', content)
325 self._queue_send(msg)
325 self._queue_send(msg)
326 return msg['header']['msg_id']
326 return msg['header']['msg_id']
327
327
328 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
328 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
329 """Get entries from the history list.
329 """Get entries from the history list.
330
330
331 Parameters
331 Parameters
332 ----------
332 ----------
333 raw : bool
333 raw : bool
334 If True, return the raw input.
334 If True, return the raw input.
335 output : bool
335 output : bool
336 If True, then return the output as well.
336 If True, then return the output as well.
337 hist_access_type : str
337 hist_access_type : str
338 'range' (fill in session, start and stop params), 'tail' (fill in n)
338 'range' (fill in session, start and stop params), 'tail' (fill in n)
339 or 'search' (fill in pattern param).
339 or 'search' (fill in pattern param).
340
340
341 session : int
341 session : int
342 For a range request, the session from which to get lines. Session
342 For a range request, the session from which to get lines. Session
343 numbers are positive integers; negative ones count back from the
343 numbers are positive integers; negative ones count back from the
344 current session.
344 current session.
345 start : int
345 start : int
346 The first line number of a history range.
346 The first line number of a history range.
347 stop : int
347 stop : int
348 The final (excluded) line number of a history range.
348 The final (excluded) line number of a history range.
349
349
350 n : int
350 n : int
351 The number of lines of history to get for a tail request.
351 The number of lines of history to get for a tail request.
352
352
353 pattern : str
353 pattern : str
354 The glob-syntax pattern for a search request.
354 The glob-syntax pattern for a search request.
355
355
356 Returns
356 Returns
357 -------
357 -------
358 The msg_id of the message sent.
358 The msg_id of the message sent.
359 """
359 """
360 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
360 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
361 **kwargs)
361 **kwargs)
362 msg = self.session.msg('history_request', content)
362 msg = self.session.msg('history_request', content)
363 self._queue_send(msg)
363 self._queue_send(msg)
364 return msg['header']['msg_id']
364 return msg['header']['msg_id']
365
365
366 def kernel_info(self):
367 """Request kernel info."""
368 msg = self.session.msg('kernel_info_request')
369 self._queue_send(msg)
370 return msg['header']['msg_id']
371
366 def shutdown(self, restart=False):
372 def shutdown(self, restart=False):
367 """Request an immediate kernel shutdown.
373 """Request an immediate kernel shutdown.
368
374
369 Upon receipt of the (empty) reply, client code can safely assume that
375 Upon receipt of the (empty) reply, client code can safely assume that
370 the kernel has shut down and it's safe to forcefully terminate it if
376 the kernel has shut down and it's safe to forcefully terminate it if
371 it's still alive.
377 it's still alive.
372
378
373 The kernel will send the reply via a function registered with Python's
379 The kernel will send the reply via a function registered with Python's
374 atexit module, ensuring it's truly done as the kernel is done with all
380 atexit module, ensuring it's truly done as the kernel is done with all
375 normal operation.
381 normal operation.
376 """
382 """
377 # Send quit message to kernel. Once we implement kernel-side setattr,
383 # Send quit message to kernel. Once we implement kernel-side setattr,
378 # this should probably be done that way, but for now this will do.
384 # this should probably be done that way, but for now this will do.
379 msg = self.session.msg('shutdown_request', {'restart':restart})
385 msg = self.session.msg('shutdown_request', {'restart':restart})
380 self._queue_send(msg)
386 self._queue_send(msg)
381 return msg['header']['msg_id']
387 return msg['header']['msg_id']
382
388
383
389
384
390
385 class SubSocketChannel(ZMQSocketChannel):
391 class SubSocketChannel(ZMQSocketChannel):
386 """The SUB channel which listens for messages that the kernel publishes.
392 """The SUB channel which listens for messages that the kernel publishes.
387 """
393 """
388
394
389 def __init__(self, context, session, address):
395 def __init__(self, context, session, address):
390 super(SubSocketChannel, self).__init__(context, session, address)
396 super(SubSocketChannel, self).__init__(context, session, address)
391 self.ioloop = ioloop.IOLoop()
397 self.ioloop = ioloop.IOLoop()
392
398
393 def run(self):
399 def run(self):
394 """The thread's main activity. Call start() instead."""
400 """The thread's main activity. Call start() instead."""
395 self.socket = self.context.socket(zmq.SUB)
401 self.socket = self.context.socket(zmq.SUB)
396 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
402 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
397 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
403 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
398 self.socket.connect(self.address)
404 self.socket.connect(self.address)
399 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
405 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
400 self.stream.on_recv(self._handle_recv)
406 self.stream.on_recv(self._handle_recv)
401 self._run_loop()
407 self._run_loop()
402 try:
408 try:
403 self.socket.close()
409 self.socket.close()
404 except:
410 except:
405 pass
411 pass
406
412
407 def stop(self):
413 def stop(self):
408 self.ioloop.stop()
414 self.ioloop.stop()
409 super(SubSocketChannel, self).stop()
415 super(SubSocketChannel, self).stop()
410
416
411 def call_handlers(self, msg):
417 def call_handlers(self, msg):
412 """This method is called in the ioloop thread when a message arrives.
418 """This method is called in the ioloop thread when a message arrives.
413
419
414 Subclasses should override this method to handle incoming messages.
420 Subclasses should override this method to handle incoming messages.
415 It is important to remember that this method is called in the thread
421 It is important to remember that this method is called in the thread
416 so that some logic must be done to ensure that the application leve
422 so that some logic must be done to ensure that the application leve
417 handlers are called in the application thread.
423 handlers are called in the application thread.
418 """
424 """
419 raise NotImplementedError('call_handlers must be defined in a subclass.')
425 raise NotImplementedError('call_handlers must be defined in a subclass.')
420
426
421 def flush(self, timeout=1.0):
427 def flush(self, timeout=1.0):
422 """Immediately processes all pending messages on the SUB channel.
428 """Immediately processes all pending messages on the SUB channel.
423
429
424 Callers should use this method to ensure that :method:`call_handlers`
430 Callers should use this method to ensure that :method:`call_handlers`
425 has been called for all messages that have been received on the
431 has been called for all messages that have been received on the
426 0MQ SUB socket of this channel.
432 0MQ SUB socket of this channel.
427
433
428 This method is thread safe.
434 This method is thread safe.
429
435
430 Parameters
436 Parameters
431 ----------
437 ----------
432 timeout : float, optional
438 timeout : float, optional
433 The maximum amount of time to spend flushing, in seconds. The
439 The maximum amount of time to spend flushing, in seconds. The
434 default is one second.
440 default is one second.
435 """
441 """
436 # We do the IOLoop callback process twice to ensure that the IOLoop
442 # We do the IOLoop callback process twice to ensure that the IOLoop
437 # gets to perform at least one full poll.
443 # gets to perform at least one full poll.
438 stop_time = time.time() + timeout
444 stop_time = time.time() + timeout
439 for i in xrange(2):
445 for i in xrange(2):
440 self._flushed = False
446 self._flushed = False
441 self.ioloop.add_callback(self._flush)
447 self.ioloop.add_callback(self._flush)
442 while not self._flushed and time.time() < stop_time:
448 while not self._flushed and time.time() < stop_time:
443 time.sleep(0.01)
449 time.sleep(0.01)
444
450
445 def _flush(self):
451 def _flush(self):
446 """Callback for :method:`self.flush`."""
452 """Callback for :method:`self.flush`."""
447 self.stream.flush()
453 self.stream.flush()
448 self._flushed = True
454 self._flushed = True
449
455
450
456
451 class StdInSocketChannel(ZMQSocketChannel):
457 class StdInSocketChannel(ZMQSocketChannel):
452 """A reply channel to handle raw_input requests that the kernel makes."""
458 """A reply channel to handle raw_input requests that the kernel makes."""
453
459
454 msg_queue = None
460 msg_queue = None
455
461
456 def __init__(self, context, session, address):
462 def __init__(self, context, session, address):
457 super(StdInSocketChannel, self).__init__(context, session, address)
463 super(StdInSocketChannel, self).__init__(context, session, address)
458 self.ioloop = ioloop.IOLoop()
464 self.ioloop = ioloop.IOLoop()
459
465
460 def run(self):
466 def run(self):
461 """The thread's main activity. Call start() instead."""
467 """The thread's main activity. Call start() instead."""
462 self.socket = self.context.socket(zmq.DEALER)
468 self.socket = self.context.socket(zmq.DEALER)
463 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
469 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
464 self.socket.connect(self.address)
470 self.socket.connect(self.address)
465 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
471 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
466 self.stream.on_recv(self._handle_recv)
472 self.stream.on_recv(self._handle_recv)
467 self._run_loop()
473 self._run_loop()
468 try:
474 try:
469 self.socket.close()
475 self.socket.close()
470 except:
476 except:
471 pass
477 pass
472
478
473
479
474 def stop(self):
480 def stop(self):
475 self.ioloop.stop()
481 self.ioloop.stop()
476 super(StdInSocketChannel, self).stop()
482 super(StdInSocketChannel, self).stop()
477
483
478 def call_handlers(self, msg):
484 def call_handlers(self, msg):
479 """This method is called in the ioloop thread when a message arrives.
485 """This method is called in the ioloop thread when a message arrives.
480
486
481 Subclasses should override this method to handle incoming messages.
487 Subclasses should override this method to handle incoming messages.
482 It is important to remember that this method is called in the thread
488 It is important to remember that this method is called in the thread
483 so that some logic must be done to ensure that the application leve
489 so that some logic must be done to ensure that the application leve
484 handlers are called in the application thread.
490 handlers are called in the application thread.
485 """
491 """
486 raise NotImplementedError('call_handlers must be defined in a subclass.')
492 raise NotImplementedError('call_handlers must be defined in a subclass.')
487
493
488 def input(self, string):
494 def input(self, string):
489 """Send a string of raw input to the kernel."""
495 """Send a string of raw input to the kernel."""
490 content = dict(value=string)
496 content = dict(value=string)
491 msg = self.session.msg('input_reply', content)
497 msg = self.session.msg('input_reply', content)
492 self._queue_send(msg)
498 self._queue_send(msg)
493
499
494
500
495 class HBSocketChannel(ZMQSocketChannel):
501 class HBSocketChannel(ZMQSocketChannel):
496 """The heartbeat channel which monitors the kernel heartbeat.
502 """The heartbeat channel which monitors the kernel heartbeat.
497
503
498 Note that the heartbeat channel is paused by default. As long as you start
504 Note that the heartbeat channel is paused by default. As long as you start
499 this channel, the kernel manager will ensure that it is paused and un-paused
505 this channel, the kernel manager will ensure that it is paused and un-paused
500 as appropriate.
506 as appropriate.
501 """
507 """
502
508
503 time_to_dead = 3.0
509 time_to_dead = 3.0
504 socket = None
510 socket = None
505 poller = None
511 poller = None
506 _running = None
512 _running = None
507 _pause = None
513 _pause = None
508 _beating = None
514 _beating = None
509
515
510 def __init__(self, context, session, address):
516 def __init__(self, context, session, address):
511 super(HBSocketChannel, self).__init__(context, session, address)
517 super(HBSocketChannel, self).__init__(context, session, address)
512 self._running = False
518 self._running = False
513 self._pause =True
519 self._pause =True
514 self.poller = zmq.Poller()
520 self.poller = zmq.Poller()
515
521
516 def _create_socket(self):
522 def _create_socket(self):
517 if self.socket is not None:
523 if self.socket is not None:
518 # close previous socket, before opening a new one
524 # close previous socket, before opening a new one
519 self.poller.unregister(self.socket)
525 self.poller.unregister(self.socket)
520 self.socket.close()
526 self.socket.close()
521 self.socket = self.context.socket(zmq.REQ)
527 self.socket = self.context.socket(zmq.REQ)
522 self.socket.setsockopt(zmq.LINGER, 0)
528 self.socket.setsockopt(zmq.LINGER, 0)
523 self.socket.connect(self.address)
529 self.socket.connect(self.address)
524
530
525 self.poller.register(self.socket, zmq.POLLIN)
531 self.poller.register(self.socket, zmq.POLLIN)
526
532
527 def _poll(self, start_time):
533 def _poll(self, start_time):
528 """poll for heartbeat replies until we reach self.time_to_dead
534 """poll for heartbeat replies until we reach self.time_to_dead
529
535
530 Ignores interrupts, and returns the result of poll(), which
536 Ignores interrupts, and returns the result of poll(), which
531 will be an empty list if no messages arrived before the timeout,
537 will be an empty list if no messages arrived before the timeout,
532 or the event tuple if there is a message to receive.
538 or the event tuple if there is a message to receive.
533 """
539 """
534
540
535 until_dead = self.time_to_dead - (time.time() - start_time)
541 until_dead = self.time_to_dead - (time.time() - start_time)
536 # ensure poll at least once
542 # ensure poll at least once
537 until_dead = max(until_dead, 1e-3)
543 until_dead = max(until_dead, 1e-3)
538 events = []
544 events = []
539 while True:
545 while True:
540 try:
546 try:
541 events = self.poller.poll(1000 * until_dead)
547 events = self.poller.poll(1000 * until_dead)
542 except ZMQError as e:
548 except ZMQError as e:
543 if e.errno == errno.EINTR:
549 if e.errno == errno.EINTR:
544 # ignore interrupts during heartbeat
550 # ignore interrupts during heartbeat
545 # this may never actually happen
551 # this may never actually happen
546 until_dead = self.time_to_dead - (time.time() - start_time)
552 until_dead = self.time_to_dead - (time.time() - start_time)
547 until_dead = max(until_dead, 1e-3)
553 until_dead = max(until_dead, 1e-3)
548 pass
554 pass
549 else:
555 else:
550 raise
556 raise
551 except Exception:
557 except Exception:
552 if self._exiting:
558 if self._exiting:
553 break
559 break
554 else:
560 else:
555 raise
561 raise
556 else:
562 else:
557 break
563 break
558 return events
564 return events
559
565
560 def run(self):
566 def run(self):
561 """The thread's main activity. Call start() instead."""
567 """The thread's main activity. Call start() instead."""
562 self._create_socket()
568 self._create_socket()
563 self._running = True
569 self._running = True
564 self._beating = True
570 self._beating = True
565
571
566 while self._running:
572 while self._running:
567 if self._pause:
573 if self._pause:
568 # just sleep, and skip the rest of the loop
574 # just sleep, and skip the rest of the loop
569 time.sleep(self.time_to_dead)
575 time.sleep(self.time_to_dead)
570 continue
576 continue
571
577
572 since_last_heartbeat = 0.0
578 since_last_heartbeat = 0.0
573 # io.rprint('Ping from HB channel') # dbg
579 # io.rprint('Ping from HB channel') # dbg
574 # no need to catch EFSM here, because the previous event was
580 # no need to catch EFSM here, because the previous event was
575 # either a recv or connect, which cannot be followed by EFSM
581 # either a recv or connect, which cannot be followed by EFSM
576 self.socket.send(b'ping')
582 self.socket.send(b'ping')
577 request_time = time.time()
583 request_time = time.time()
578 ready = self._poll(request_time)
584 ready = self._poll(request_time)
579 if ready:
585 if ready:
580 self._beating = True
586 self._beating = True
581 # the poll above guarantees we have something to recv
587 # the poll above guarantees we have something to recv
582 self.socket.recv()
588 self.socket.recv()
583 # sleep the remainder of the cycle
589 # sleep the remainder of the cycle
584 remainder = self.time_to_dead - (time.time() - request_time)
590 remainder = self.time_to_dead - (time.time() - request_time)
585 if remainder > 0:
591 if remainder > 0:
586 time.sleep(remainder)
592 time.sleep(remainder)
587 continue
593 continue
588 else:
594 else:
589 # nothing was received within the time limit, signal heart failure
595 # nothing was received within the time limit, signal heart failure
590 self._beating = False
596 self._beating = False
591 since_last_heartbeat = time.time() - request_time
597 since_last_heartbeat = time.time() - request_time
592 self.call_handlers(since_last_heartbeat)
598 self.call_handlers(since_last_heartbeat)
593 # and close/reopen the socket, because the REQ/REP cycle has been broken
599 # and close/reopen the socket, because the REQ/REP cycle has been broken
594 self._create_socket()
600 self._create_socket()
595 continue
601 continue
596 try:
602 try:
597 self.socket.close()
603 self.socket.close()
598 except:
604 except:
599 pass
605 pass
600
606
601 def pause(self):
607 def pause(self):
602 """Pause the heartbeat."""
608 """Pause the heartbeat."""
603 self._pause = True
609 self._pause = True
604
610
605 def unpause(self):
611 def unpause(self):
606 """Unpause the heartbeat."""
612 """Unpause the heartbeat."""
607 self._pause = False
613 self._pause = False
608
614
609 def is_beating(self):
615 def is_beating(self):
610 """Is the heartbeat running and responsive (and not paused)."""
616 """Is the heartbeat running and responsive (and not paused)."""
611 if self.is_alive() and not self._pause and self._beating:
617 if self.is_alive() and not self._pause and self._beating:
612 return True
618 return True
613 else:
619 else:
614 return False
620 return False
615
621
616 def stop(self):
622 def stop(self):
617 self._running = False
623 self._running = False
618 super(HBSocketChannel, self).stop()
624 super(HBSocketChannel, self).stop()
619
625
620 def call_handlers(self, since_last_heartbeat):
626 def call_handlers(self, since_last_heartbeat):
621 """This method is called in the ioloop thread when a message arrives.
627 """This method is called in the ioloop thread when a message arrives.
622
628
623 Subclasses should override this method to handle incoming messages.
629 Subclasses should override this method to handle incoming messages.
624 It is important to remember that this method is called in the thread
630 It is important to remember that this method is called in the thread
625 so that some logic must be done to ensure that the application level
631 so that some logic must be done to ensure that the application level
626 handlers are called in the application thread.
632 handlers are called in the application thread.
627 """
633 """
628 raise NotImplementedError('call_handlers must be defined in a subclass.')
634 raise NotImplementedError('call_handlers must be defined in a subclass.')
629
635
630
636
631 #-----------------------------------------------------------------------------
637 #-----------------------------------------------------------------------------
632 # Main kernel manager class
638 # Main kernel manager class
633 #-----------------------------------------------------------------------------
639 #-----------------------------------------------------------------------------
634
640
635 class KernelManager(HasTraits):
641 class KernelManager(HasTraits):
636 """ Manages a kernel for a frontend.
642 """ Manages a kernel for a frontend.
637
643
638 The SUB channel is for the frontend to receive messages published by the
644 The SUB channel is for the frontend to receive messages published by the
639 kernel.
645 kernel.
640
646
641 The REQ channel is for the frontend to make requests of the kernel.
647 The REQ channel is for the frontend to make requests of the kernel.
642
648
643 The REP channel is for the kernel to request stdin (raw_input) from the
649 The REP channel is for the kernel to request stdin (raw_input) from the
644 frontend.
650 frontend.
645 """
651 """
646 # config object for passing to child configurables
652 # config object for passing to child configurables
647 config = Instance(Config)
653 config = Instance(Config)
648
654
649 # The PyZMQ Context to use for communication with the kernel.
655 # The PyZMQ Context to use for communication with the kernel.
650 context = Instance(zmq.Context)
656 context = Instance(zmq.Context)
651 def _context_default(self):
657 def _context_default(self):
652 return zmq.Context.instance()
658 return zmq.Context.instance()
653
659
654 # The Session to use for communication with the kernel.
660 # The Session to use for communication with the kernel.
655 session = Instance(Session)
661 session = Instance(Session)
656
662
657 # The kernel process with which the KernelManager is communicating.
663 # The kernel process with which the KernelManager is communicating.
658 kernel = Instance(Popen)
664 kernel = Instance(Popen)
659
665
660 # The addresses for the communication channels.
666 # The addresses for the communication channels.
661 connection_file = Unicode('')
667 connection_file = Unicode('')
662
668
663 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp')
669 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp')
664
670
665
671
666 ip = Unicode(LOCALHOST)
672 ip = Unicode(LOCALHOST)
667 def _ip_changed(self, name, old, new):
673 def _ip_changed(self, name, old, new):
668 if new == '*':
674 if new == '*':
669 self.ip = '0.0.0.0'
675 self.ip = '0.0.0.0'
670 shell_port = Integer(0)
676 shell_port = Integer(0)
671 iopub_port = Integer(0)
677 iopub_port = Integer(0)
672 stdin_port = Integer(0)
678 stdin_port = Integer(0)
673 hb_port = Integer(0)
679 hb_port = Integer(0)
674
680
675 # The classes to use for the various channels.
681 # The classes to use for the various channels.
676 shell_channel_class = Type(ShellSocketChannel)
682 shell_channel_class = Type(ShellSocketChannel)
677 sub_channel_class = Type(SubSocketChannel)
683 sub_channel_class = Type(SubSocketChannel)
678 stdin_channel_class = Type(StdInSocketChannel)
684 stdin_channel_class = Type(StdInSocketChannel)
679 hb_channel_class = Type(HBSocketChannel)
685 hb_channel_class = Type(HBSocketChannel)
680
686
681 # Protected traits.
687 # Protected traits.
682 _launch_args = Any
688 _launch_args = Any
683 _shell_channel = Any
689 _shell_channel = Any
684 _sub_channel = Any
690 _sub_channel = Any
685 _stdin_channel = Any
691 _stdin_channel = Any
686 _hb_channel = Any
692 _hb_channel = Any
687 _connection_file_written=Bool(False)
693 _connection_file_written=Bool(False)
688
694
689 def __init__(self, **kwargs):
695 def __init__(self, **kwargs):
690 super(KernelManager, self).__init__(**kwargs)
696 super(KernelManager, self).__init__(**kwargs)
691 if self.session is None:
697 if self.session is None:
692 self.session = Session(config=self.config)
698 self.session = Session(config=self.config)
693
699
694 def __del__(self):
700 def __del__(self):
695 self.cleanup_connection_file()
701 self.cleanup_connection_file()
696
702
697
703
698 #--------------------------------------------------------------------------
704 #--------------------------------------------------------------------------
699 # Channel management methods:
705 # Channel management methods:
700 #--------------------------------------------------------------------------
706 #--------------------------------------------------------------------------
701
707
702 def start_channels(self, shell=True, sub=True, stdin=True, hb=True):
708 def start_channels(self, shell=True, sub=True, stdin=True, hb=True):
703 """Starts the channels for this kernel.
709 """Starts the channels for this kernel.
704
710
705 This will create the channels if they do not exist and then start
711 This will create the channels if they do not exist and then start
706 them. If port numbers of 0 are being used (random ports) then you
712 them. If port numbers of 0 are being used (random ports) then you
707 must first call :method:`start_kernel`. If the channels have been
713 must first call :method:`start_kernel`. If the channels have been
708 stopped and you call this, :class:`RuntimeError` will be raised.
714 stopped and you call this, :class:`RuntimeError` will be raised.
709 """
715 """
710 if shell:
716 if shell:
711 self.shell_channel.start()
717 self.shell_channel.start()
712 if sub:
718 if sub:
713 self.sub_channel.start()
719 self.sub_channel.start()
714 if stdin:
720 if stdin:
715 self.stdin_channel.start()
721 self.stdin_channel.start()
716 self.shell_channel.allow_stdin = True
722 self.shell_channel.allow_stdin = True
717 else:
723 else:
718 self.shell_channel.allow_stdin = False
724 self.shell_channel.allow_stdin = False
719 if hb:
725 if hb:
720 self.hb_channel.start()
726 self.hb_channel.start()
721
727
722 def stop_channels(self):
728 def stop_channels(self):
723 """Stops all the running channels for this kernel.
729 """Stops all the running channels for this kernel.
724 """
730 """
725 if self.shell_channel.is_alive():
731 if self.shell_channel.is_alive():
726 self.shell_channel.stop()
732 self.shell_channel.stop()
727 if self.sub_channel.is_alive():
733 if self.sub_channel.is_alive():
728 self.sub_channel.stop()
734 self.sub_channel.stop()
729 if self.stdin_channel.is_alive():
735 if self.stdin_channel.is_alive():
730 self.stdin_channel.stop()
736 self.stdin_channel.stop()
731 if self.hb_channel.is_alive():
737 if self.hb_channel.is_alive():
732 self.hb_channel.stop()
738 self.hb_channel.stop()
733
739
734 @property
740 @property
735 def channels_running(self):
741 def channels_running(self):
736 """Are any of the channels created and running?"""
742 """Are any of the channels created and running?"""
737 return (self.shell_channel.is_alive() or self.sub_channel.is_alive() or
743 return (self.shell_channel.is_alive() or self.sub_channel.is_alive() or
738 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
744 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
739
745
740 #--------------------------------------------------------------------------
746 #--------------------------------------------------------------------------
741 # Kernel process management methods:
747 # Kernel process management methods:
742 #--------------------------------------------------------------------------
748 #--------------------------------------------------------------------------
743
749
744 def cleanup_connection_file(self):
750 def cleanup_connection_file(self):
745 """cleanup connection file *if we wrote it*
751 """cleanup connection file *if we wrote it*
746
752
747 Will not raise if the connection file was already removed somehow.
753 Will not raise if the connection file was already removed somehow.
748 """
754 """
749 if self._connection_file_written:
755 if self._connection_file_written:
750 # cleanup connection files on full shutdown of kernel we started
756 # cleanup connection files on full shutdown of kernel we started
751 self._connection_file_written = False
757 self._connection_file_written = False
752 try:
758 try:
753 os.remove(self.connection_file)
759 os.remove(self.connection_file)
754 except (IOError, OSError):
760 except (IOError, OSError):
755 pass
761 pass
756
762
757 self._cleanup_ipc_files()
763 self._cleanup_ipc_files()
758
764
759 def _cleanup_ipc_files(self):
765 def _cleanup_ipc_files(self):
760 """cleanup ipc files if we wrote them"""
766 """cleanup ipc files if we wrote them"""
761 if self.transport != 'ipc':
767 if self.transport != 'ipc':
762 return
768 return
763 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port):
769 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port):
764 ipcfile = "%s-%i" % (self.ip, port)
770 ipcfile = "%s-%i" % (self.ip, port)
765 try:
771 try:
766 os.remove(ipcfile)
772 os.remove(ipcfile)
767 except (IOError, OSError):
773 except (IOError, OSError):
768 pass
774 pass
769
775
770 def load_connection_file(self):
776 def load_connection_file(self):
771 """load connection info from JSON dict in self.connection_file"""
777 """load connection info from JSON dict in self.connection_file"""
772 with open(self.connection_file) as f:
778 with open(self.connection_file) as f:
773 cfg = json.loads(f.read())
779 cfg = json.loads(f.read())
774
780
775 from pprint import pprint
781 from pprint import pprint
776 pprint(cfg)
782 pprint(cfg)
777 self.transport = cfg.get('transport', 'tcp')
783 self.transport = cfg.get('transport', 'tcp')
778 self.ip = cfg['ip']
784 self.ip = cfg['ip']
779 self.shell_port = cfg['shell_port']
785 self.shell_port = cfg['shell_port']
780 self.stdin_port = cfg['stdin_port']
786 self.stdin_port = cfg['stdin_port']
781 self.iopub_port = cfg['iopub_port']
787 self.iopub_port = cfg['iopub_port']
782 self.hb_port = cfg['hb_port']
788 self.hb_port = cfg['hb_port']
783 self.session.key = str_to_bytes(cfg['key'])
789 self.session.key = str_to_bytes(cfg['key'])
784
790
785 def write_connection_file(self):
791 def write_connection_file(self):
786 """write connection info to JSON dict in self.connection_file"""
792 """write connection info to JSON dict in self.connection_file"""
787 if self._connection_file_written:
793 if self._connection_file_written:
788 return
794 return
789 self.connection_file,cfg = write_connection_file(self.connection_file,
795 self.connection_file,cfg = write_connection_file(self.connection_file,
790 transport=self.transport, ip=self.ip, key=self.session.key,
796 transport=self.transport, ip=self.ip, key=self.session.key,
791 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
797 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
792 shell_port=self.shell_port, hb_port=self.hb_port)
798 shell_port=self.shell_port, hb_port=self.hb_port)
793 # write_connection_file also sets default ports:
799 # write_connection_file also sets default ports:
794 self.shell_port = cfg['shell_port']
800 self.shell_port = cfg['shell_port']
795 self.stdin_port = cfg['stdin_port']
801 self.stdin_port = cfg['stdin_port']
796 self.iopub_port = cfg['iopub_port']
802 self.iopub_port = cfg['iopub_port']
797 self.hb_port = cfg['hb_port']
803 self.hb_port = cfg['hb_port']
798
804
799 self._connection_file_written = True
805 self._connection_file_written = True
800
806
801 def start_kernel(self, **kw):
807 def start_kernel(self, **kw):
802 """Starts a kernel process and configures the manager to use it.
808 """Starts a kernel process and configures the manager to use it.
803
809
804 If random ports (port=0) are being used, this method must be called
810 If random ports (port=0) are being used, this method must be called
805 before the channels are created.
811 before the channels are created.
806
812
807 Parameters:
813 Parameters:
808 -----------
814 -----------
809 launcher : callable, optional (default None)
815 launcher : callable, optional (default None)
810 A custom function for launching the kernel process (generally a
816 A custom function for launching the kernel process (generally a
811 wrapper around ``entry_point.base_launch_kernel``). In most cases,
817 wrapper around ``entry_point.base_launch_kernel``). In most cases,
812 it should not be necessary to use this parameter.
818 it should not be necessary to use this parameter.
813
819
814 **kw : optional
820 **kw : optional
815 See respective options for IPython and Python kernels.
821 See respective options for IPython and Python kernels.
816 """
822 """
817 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
823 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
818 raise RuntimeError("Can only launch a kernel on a local interface. "
824 raise RuntimeError("Can only launch a kernel on a local interface. "
819 "Make sure that the '*_address' attributes are "
825 "Make sure that the '*_address' attributes are "
820 "configured properly. "
826 "configured properly. "
821 "Currently valid addresses are: %s"%LOCAL_IPS
827 "Currently valid addresses are: %s"%LOCAL_IPS
822 )
828 )
823
829
824 # write connection file / get default ports
830 # write connection file / get default ports
825 self.write_connection_file()
831 self.write_connection_file()
826
832
827 self._launch_args = kw.copy()
833 self._launch_args = kw.copy()
828 launch_kernel = kw.pop('launcher', None)
834 launch_kernel = kw.pop('launcher', None)
829 if launch_kernel is None:
835 if launch_kernel is None:
830 from ipkernel import launch_kernel
836 from ipkernel import launch_kernel
831 self.kernel = launch_kernel(fname=self.connection_file, **kw)
837 self.kernel = launch_kernel(fname=self.connection_file, **kw)
832
838
833 def shutdown_kernel(self, restart=False):
839 def shutdown_kernel(self, restart=False):
834 """ Attempts to the stop the kernel process cleanly.
840 """ Attempts to the stop the kernel process cleanly.
835
841
836 If the kernel cannot be stopped and the kernel is local, it is killed.
842 If the kernel cannot be stopped and the kernel is local, it is killed.
837 """
843 """
838 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
844 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
839 if sys.platform == 'win32':
845 if sys.platform == 'win32':
840 self.kill_kernel()
846 self.kill_kernel()
841 return
847 return
842
848
843 # Pause the heart beat channel if it exists.
849 # Pause the heart beat channel if it exists.
844 if self._hb_channel is not None:
850 if self._hb_channel is not None:
845 self._hb_channel.pause()
851 self._hb_channel.pause()
846
852
847 # Don't send any additional kernel kill messages immediately, to give
853 # Don't send any additional kernel kill messages immediately, to give
848 # the kernel a chance to properly execute shutdown actions. Wait for at
854 # the kernel a chance to properly execute shutdown actions. Wait for at
849 # most 1s, checking every 0.1s.
855 # most 1s, checking every 0.1s.
850 self.shell_channel.shutdown(restart=restart)
856 self.shell_channel.shutdown(restart=restart)
851 for i in range(10):
857 for i in range(10):
852 if self.is_alive:
858 if self.is_alive:
853 time.sleep(0.1)
859 time.sleep(0.1)
854 else:
860 else:
855 break
861 break
856 else:
862 else:
857 # OK, we've waited long enough.
863 # OK, we've waited long enough.
858 if self.has_kernel:
864 if self.has_kernel:
859 self.kill_kernel()
865 self.kill_kernel()
860
866
861 if not restart and self._connection_file_written:
867 if not restart and self._connection_file_written:
862 # cleanup connection files on full shutdown of kernel we started
868 # cleanup connection files on full shutdown of kernel we started
863 self._connection_file_written = False
869 self._connection_file_written = False
864 try:
870 try:
865 os.remove(self.connection_file)
871 os.remove(self.connection_file)
866 except IOError:
872 except IOError:
867 pass
873 pass
868
874
869 def restart_kernel(self, now=False, **kw):
875 def restart_kernel(self, now=False, **kw):
870 """Restarts a kernel with the arguments that were used to launch it.
876 """Restarts a kernel with the arguments that were used to launch it.
871
877
872 If the old kernel was launched with random ports, the same ports will be
878 If the old kernel was launched with random ports, the same ports will be
873 used for the new kernel.
879 used for the new kernel.
874
880
875 Parameters
881 Parameters
876 ----------
882 ----------
877 now : bool, optional
883 now : bool, optional
878 If True, the kernel is forcefully restarted *immediately*, without
884 If True, the kernel is forcefully restarted *immediately*, without
879 having a chance to do any cleanup action. Otherwise the kernel is
885 having a chance to do any cleanup action. Otherwise the kernel is
880 given 1s to clean up before a forceful restart is issued.
886 given 1s to clean up before a forceful restart is issued.
881
887
882 In all cases the kernel is restarted, the only difference is whether
888 In all cases the kernel is restarted, the only difference is whether
883 it is given a chance to perform a clean shutdown or not.
889 it is given a chance to perform a clean shutdown or not.
884
890
885 **kw : optional
891 **kw : optional
886 Any options specified here will replace those used to launch the
892 Any options specified here will replace those used to launch the
887 kernel.
893 kernel.
888 """
894 """
889 if self._launch_args is None:
895 if self._launch_args is None:
890 raise RuntimeError("Cannot restart the kernel. "
896 raise RuntimeError("Cannot restart the kernel. "
891 "No previous call to 'start_kernel'.")
897 "No previous call to 'start_kernel'.")
892 else:
898 else:
893 # Stop currently running kernel.
899 # Stop currently running kernel.
894 if self.has_kernel:
900 if self.has_kernel:
895 if now:
901 if now:
896 self.kill_kernel()
902 self.kill_kernel()
897 else:
903 else:
898 self.shutdown_kernel(restart=True)
904 self.shutdown_kernel(restart=True)
899
905
900 # Start new kernel.
906 # Start new kernel.
901 self._launch_args.update(kw)
907 self._launch_args.update(kw)
902 self.start_kernel(**self._launch_args)
908 self.start_kernel(**self._launch_args)
903
909
904 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
910 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
905 # unless there is some delay here.
911 # unless there is some delay here.
906 if sys.platform == 'win32':
912 if sys.platform == 'win32':
907 time.sleep(0.2)
913 time.sleep(0.2)
908
914
909 @property
915 @property
910 def has_kernel(self):
916 def has_kernel(self):
911 """Returns whether a kernel process has been specified for the kernel
917 """Returns whether a kernel process has been specified for the kernel
912 manager.
918 manager.
913 """
919 """
914 return self.kernel is not None
920 return self.kernel is not None
915
921
916 def kill_kernel(self):
922 def kill_kernel(self):
917 """ Kill the running kernel.
923 """ Kill the running kernel.
918
924
919 This method blocks until the kernel process has terminated.
925 This method blocks until the kernel process has terminated.
920 """
926 """
921 if self.has_kernel:
927 if self.has_kernel:
922 # Pause the heart beat channel if it exists.
928 # Pause the heart beat channel if it exists.
923 if self._hb_channel is not None:
929 if self._hb_channel is not None:
924 self._hb_channel.pause()
930 self._hb_channel.pause()
925
931
926 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
932 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
927 # TerminateProcess() on Win32).
933 # TerminateProcess() on Win32).
928 try:
934 try:
929 self.kernel.kill()
935 self.kernel.kill()
930 except OSError as e:
936 except OSError as e:
931 # In Windows, we will get an Access Denied error if the process
937 # In Windows, we will get an Access Denied error if the process
932 # has already terminated. Ignore it.
938 # has already terminated. Ignore it.
933 if sys.platform == 'win32':
939 if sys.platform == 'win32':
934 if e.winerror != 5:
940 if e.winerror != 5:
935 raise
941 raise
936 # On Unix, we may get an ESRCH error if the process has already
942 # On Unix, we may get an ESRCH error if the process has already
937 # terminated. Ignore it.
943 # terminated. Ignore it.
938 else:
944 else:
939 from errno import ESRCH
945 from errno import ESRCH
940 if e.errno != ESRCH:
946 if e.errno != ESRCH:
941 raise
947 raise
942
948
943 # Block until the kernel terminates.
949 # Block until the kernel terminates.
944 self.kernel.wait()
950 self.kernel.wait()
945 self.kernel = None
951 self.kernel = None
946 else:
952 else:
947 raise RuntimeError("Cannot kill kernel. No kernel is running!")
953 raise RuntimeError("Cannot kill kernel. No kernel is running!")
948
954
949 def interrupt_kernel(self):
955 def interrupt_kernel(self):
950 """ Interrupts the kernel.
956 """ Interrupts the kernel.
951
957
952 Unlike ``signal_kernel``, this operation is well supported on all
958 Unlike ``signal_kernel``, this operation is well supported on all
953 platforms.
959 platforms.
954 """
960 """
955 if self.has_kernel:
961 if self.has_kernel:
956 if sys.platform == 'win32':
962 if sys.platform == 'win32':
957 from parentpoller import ParentPollerWindows as Poller
963 from parentpoller import ParentPollerWindows as Poller
958 Poller.send_interrupt(self.kernel.win32_interrupt_event)
964 Poller.send_interrupt(self.kernel.win32_interrupt_event)
959 else:
965 else:
960 self.kernel.send_signal(signal.SIGINT)
966 self.kernel.send_signal(signal.SIGINT)
961 else:
967 else:
962 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
968 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
963
969
964 def signal_kernel(self, signum):
970 def signal_kernel(self, signum):
965 """ Sends a signal to the kernel.
971 """ Sends a signal to the kernel.
966
972
967 Note that since only SIGTERM is supported on Windows, this function is
973 Note that since only SIGTERM is supported on Windows, this function is
968 only useful on Unix systems.
974 only useful on Unix systems.
969 """
975 """
970 if self.has_kernel:
976 if self.has_kernel:
971 self.kernel.send_signal(signum)
977 self.kernel.send_signal(signum)
972 else:
978 else:
973 raise RuntimeError("Cannot signal kernel. No kernel is running!")
979 raise RuntimeError("Cannot signal kernel. No kernel is running!")
974
980
975 @property
981 @property
976 def is_alive(self):
982 def is_alive(self):
977 """Is the kernel process still running?"""
983 """Is the kernel process still running?"""
978 if self.has_kernel:
984 if self.has_kernel:
979 if self.kernel.poll() is None:
985 if self.kernel.poll() is None:
980 return True
986 return True
981 else:
987 else:
982 return False
988 return False
983 elif self._hb_channel is not None:
989 elif self._hb_channel is not None:
984 # We didn't start the kernel with this KernelManager so we
990 # We didn't start the kernel with this KernelManager so we
985 # use the heartbeat.
991 # use the heartbeat.
986 return self._hb_channel.is_beating()
992 return self._hb_channel.is_beating()
987 else:
993 else:
988 # no heartbeat and not local, we can't tell if it's running,
994 # no heartbeat and not local, we can't tell if it's running,
989 # so naively return True
995 # so naively return True
990 return True
996 return True
991
997
992 #--------------------------------------------------------------------------
998 #--------------------------------------------------------------------------
993 # Channels used for communication with the kernel:
999 # Channels used for communication with the kernel:
994 #--------------------------------------------------------------------------
1000 #--------------------------------------------------------------------------
995
1001
996 def _make_url(self, port):
1002 def _make_url(self, port):
997 """make a zmq url with a port"""
1003 """make a zmq url with a port"""
998 if self.transport == 'tcp':
1004 if self.transport == 'tcp':
999 return "tcp://%s:%i" % (self.ip, port)
1005 return "tcp://%s:%i" % (self.ip, port)
1000 else:
1006 else:
1001 return "%s://%s-%s" % (self.transport, self.ip, port)
1007 return "%s://%s-%s" % (self.transport, self.ip, port)
1002
1008
1003 @property
1009 @property
1004 def shell_channel(self):
1010 def shell_channel(self):
1005 """Get the REQ socket channel object to make requests of the kernel."""
1011 """Get the REQ socket channel object to make requests of the kernel."""
1006 if self._shell_channel is None:
1012 if self._shell_channel is None:
1007 self._shell_channel = self.shell_channel_class(self.context,
1013 self._shell_channel = self.shell_channel_class(self.context,
1008 self.session,
1014 self.session,
1009 self._make_url(self.shell_port),
1015 self._make_url(self.shell_port),
1010 )
1016 )
1011 return self._shell_channel
1017 return self._shell_channel
1012
1018
1013 @property
1019 @property
1014 def sub_channel(self):
1020 def sub_channel(self):
1015 """Get the SUB socket channel object."""
1021 """Get the SUB socket channel object."""
1016 if self._sub_channel is None:
1022 if self._sub_channel is None:
1017 self._sub_channel = self.sub_channel_class(self.context,
1023 self._sub_channel = self.sub_channel_class(self.context,
1018 self.session,
1024 self.session,
1019 self._make_url(self.iopub_port),
1025 self._make_url(self.iopub_port),
1020 )
1026 )
1021 return self._sub_channel
1027 return self._sub_channel
1022
1028
1023 @property
1029 @property
1024 def stdin_channel(self):
1030 def stdin_channel(self):
1025 """Get the REP socket channel object to handle stdin (raw_input)."""
1031 """Get the REP socket channel object to handle stdin (raw_input)."""
1026 if self._stdin_channel is None:
1032 if self._stdin_channel is None:
1027 self._stdin_channel = self.stdin_channel_class(self.context,
1033 self._stdin_channel = self.stdin_channel_class(self.context,
1028 self.session,
1034 self.session,
1029 self._make_url(self.stdin_port),
1035 self._make_url(self.stdin_port),
1030 )
1036 )
1031 return self._stdin_channel
1037 return self._stdin_channel
1032
1038
1033 @property
1039 @property
1034 def hb_channel(self):
1040 def hb_channel(self):
1035 """Get the heartbeat socket channel object to check that the
1041 """Get the heartbeat socket channel object to check that the
1036 kernel is alive."""
1042 kernel is alive."""
1037 if self._hb_channel is None:
1043 if self._hb_channel is None:
1038 self._hb_channel = self.hb_channel_class(self.context,
1044 self._hb_channel = self.hb_channel_class(self.context,
1039 self.session,
1045 self.session,
1040 self._make_url(self.hb_port),
1046 self._make_url(self.hb_port),
1041 )
1047 )
1042 return self._hb_channel
1048 return self._hb_channel
@@ -1,769 +1,766 b''
1 """Session object for building, serializing, sending, and receiving messages in
1 """Session object for building, serializing, sending, and receiving messages in
2 IPython. The Session object supports serialization, HMAC signatures, and
2 IPython. The Session object supports serialization, HMAC signatures, and
3 metadata on messages.
3 metadata on messages.
4
4
5 Also defined here are utilities for working with Sessions:
5 Also defined here are utilities for working with Sessions:
6 * A SessionFactory to be used as a base class for configurables that work with
6 * A SessionFactory to be used as a base class for configurables that work with
7 Sessions.
7 Sessions.
8 * A Message object for convenience that allows attribute-access to the msg dict.
8 * A Message object for convenience that allows attribute-access to the msg dict.
9
9
10 Authors:
10 Authors:
11
11
12 * Min RK
12 * Min RK
13 * Brian Granger
13 * Brian Granger
14 * Fernando Perez
14 * Fernando Perez
15 """
15 """
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Copyright (C) 2010-2011 The IPython Development Team
17 # Copyright (C) 2010-2011 The IPython Development Team
18 #
18 #
19 # Distributed under the terms of the BSD License. The full license is in
19 # Distributed under the terms of the BSD License. The full license is in
20 # the file COPYING, distributed as part of this software.
20 # the file COPYING, distributed as part of this software.
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Imports
24 # Imports
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 import hmac
27 import hmac
28 import logging
28 import logging
29 import os
29 import os
30 import pprint
30 import pprint
31 import uuid
31 import uuid
32 from datetime import datetime
32 from datetime import datetime
33
33
34 try:
34 try:
35 import cPickle
35 import cPickle
36 pickle = cPickle
36 pickle = cPickle
37 except:
37 except:
38 cPickle = None
38 cPickle = None
39 import pickle
39 import pickle
40
40
41 import zmq
41 import zmq
42 from zmq.utils import jsonapi
42 from zmq.utils import jsonapi
43 from zmq.eventloop.ioloop import IOLoop
43 from zmq.eventloop.ioloop import IOLoop
44 from zmq.eventloop.zmqstream import ZMQStream
44 from zmq.eventloop.zmqstream import ZMQStream
45
45
46 import IPython
47 from IPython.config.application import Application, boolean_flag
46 from IPython.config.application import Application, boolean_flag
48 from IPython.config.configurable import Configurable, LoggingConfigurable
47 from IPython.config.configurable import Configurable, LoggingConfigurable
49 from IPython.utils.importstring import import_item
48 from IPython.utils.importstring import import_item
50 from IPython.utils.jsonutil import extract_dates, squash_dates, date_default
49 from IPython.utils.jsonutil import extract_dates, squash_dates, date_default
51 from IPython.utils.py3compat import str_to_bytes
50 from IPython.utils.py3compat import str_to_bytes
52 from IPython.utils.traitlets import (CBytes, Unicode, Bool, Any, Instance, Set,
51 from IPython.utils.traitlets import (CBytes, Unicode, Bool, Any, Instance, Set,
53 DottedObjectName, CUnicode, Dict, Integer)
52 DottedObjectName, CUnicode, Dict, Integer)
54 from IPython.zmq.serialize import MAX_ITEMS, MAX_BYTES
53 from IPython.zmq.serialize import MAX_ITEMS, MAX_BYTES
55
54
56 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
57 # utility functions
56 # utility functions
58 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
59
58
60 def squash_unicode(obj):
59 def squash_unicode(obj):
61 """coerce unicode back to bytestrings."""
60 """coerce unicode back to bytestrings."""
62 if isinstance(obj,dict):
61 if isinstance(obj,dict):
63 for key in obj.keys():
62 for key in obj.keys():
64 obj[key] = squash_unicode(obj[key])
63 obj[key] = squash_unicode(obj[key])
65 if isinstance(key, unicode):
64 if isinstance(key, unicode):
66 obj[squash_unicode(key)] = obj.pop(key)
65 obj[squash_unicode(key)] = obj.pop(key)
67 elif isinstance(obj, list):
66 elif isinstance(obj, list):
68 for i,v in enumerate(obj):
67 for i,v in enumerate(obj):
69 obj[i] = squash_unicode(v)
68 obj[i] = squash_unicode(v)
70 elif isinstance(obj, unicode):
69 elif isinstance(obj, unicode):
71 obj = obj.encode('utf8')
70 obj = obj.encode('utf8')
72 return obj
71 return obj
73
72
74 #-----------------------------------------------------------------------------
73 #-----------------------------------------------------------------------------
75 # globals and defaults
74 # globals and defaults
76 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
77
76
78 _version_info_list = list(IPython.version_info)
79 # ISO8601-ify datetime objects
77 # ISO8601-ify datetime objects
80 json_packer = lambda obj: jsonapi.dumps(obj, default=date_default)
78 json_packer = lambda obj: jsonapi.dumps(obj, default=date_default)
81 json_unpacker = lambda s: extract_dates(jsonapi.loads(s))
79 json_unpacker = lambda s: extract_dates(jsonapi.loads(s))
82
80
83 pickle_packer = lambda o: pickle.dumps(o,-1)
81 pickle_packer = lambda o: pickle.dumps(o,-1)
84 pickle_unpacker = pickle.loads
82 pickle_unpacker = pickle.loads
85
83
86 default_packer = json_packer
84 default_packer = json_packer
87 default_unpacker = json_unpacker
85 default_unpacker = json_unpacker
88
86
89 DELIM = b"<IDS|MSG>"
87 DELIM = b"<IDS|MSG>"
90 # singleton dummy tracker, which will always report as done
88 # singleton dummy tracker, which will always report as done
91 DONE = zmq.MessageTracker()
89 DONE = zmq.MessageTracker()
92
90
93 #-----------------------------------------------------------------------------
91 #-----------------------------------------------------------------------------
94 # Mixin tools for apps that use Sessions
92 # Mixin tools for apps that use Sessions
95 #-----------------------------------------------------------------------------
93 #-----------------------------------------------------------------------------
96
94
97 session_aliases = dict(
95 session_aliases = dict(
98 ident = 'Session.session',
96 ident = 'Session.session',
99 user = 'Session.username',
97 user = 'Session.username',
100 keyfile = 'Session.keyfile',
98 keyfile = 'Session.keyfile',
101 )
99 )
102
100
103 session_flags = {
101 session_flags = {
104 'secure' : ({'Session' : { 'key' : str_to_bytes(str(uuid.uuid4())),
102 'secure' : ({'Session' : { 'key' : str_to_bytes(str(uuid.uuid4())),
105 'keyfile' : '' }},
103 'keyfile' : '' }},
106 """Use HMAC digests for authentication of messages.
104 """Use HMAC digests for authentication of messages.
107 Setting this flag will generate a new UUID to use as the HMAC key.
105 Setting this flag will generate a new UUID to use as the HMAC key.
108 """),
106 """),
109 'no-secure' : ({'Session' : { 'key' : b'', 'keyfile' : '' }},
107 'no-secure' : ({'Session' : { 'key' : b'', 'keyfile' : '' }},
110 """Don't authenticate messages."""),
108 """Don't authenticate messages."""),
111 }
109 }
112
110
113 def default_secure(cfg):
111 def default_secure(cfg):
114 """Set the default behavior for a config environment to be secure.
112 """Set the default behavior for a config environment to be secure.
115
113
116 If Session.key/keyfile have not been set, set Session.key to
114 If Session.key/keyfile have not been set, set Session.key to
117 a new random UUID.
115 a new random UUID.
118 """
116 """
119
117
120 if 'Session' in cfg:
118 if 'Session' in cfg:
121 if 'key' in cfg.Session or 'keyfile' in cfg.Session:
119 if 'key' in cfg.Session or 'keyfile' in cfg.Session:
122 return
120 return
123 # key/keyfile not specified, generate new UUID:
121 # key/keyfile not specified, generate new UUID:
124 cfg.Session.key = str_to_bytes(str(uuid.uuid4()))
122 cfg.Session.key = str_to_bytes(str(uuid.uuid4()))
125
123
126
124
127 #-----------------------------------------------------------------------------
125 #-----------------------------------------------------------------------------
128 # Classes
126 # Classes
129 #-----------------------------------------------------------------------------
127 #-----------------------------------------------------------------------------
130
128
131 class SessionFactory(LoggingConfigurable):
129 class SessionFactory(LoggingConfigurable):
132 """The Base class for configurables that have a Session, Context, logger,
130 """The Base class for configurables that have a Session, Context, logger,
133 and IOLoop.
131 and IOLoop.
134 """
132 """
135
133
136 logname = Unicode('')
134 logname = Unicode('')
137 def _logname_changed(self, name, old, new):
135 def _logname_changed(self, name, old, new):
138 self.log = logging.getLogger(new)
136 self.log = logging.getLogger(new)
139
137
140 # not configurable:
138 # not configurable:
141 context = Instance('zmq.Context')
139 context = Instance('zmq.Context')
142 def _context_default(self):
140 def _context_default(self):
143 return zmq.Context.instance()
141 return zmq.Context.instance()
144
142
145 session = Instance('IPython.zmq.session.Session')
143 session = Instance('IPython.zmq.session.Session')
146
144
147 loop = Instance('zmq.eventloop.ioloop.IOLoop', allow_none=False)
145 loop = Instance('zmq.eventloop.ioloop.IOLoop', allow_none=False)
148 def _loop_default(self):
146 def _loop_default(self):
149 return IOLoop.instance()
147 return IOLoop.instance()
150
148
151 def __init__(self, **kwargs):
149 def __init__(self, **kwargs):
152 super(SessionFactory, self).__init__(**kwargs)
150 super(SessionFactory, self).__init__(**kwargs)
153
151
154 if self.session is None:
152 if self.session is None:
155 # construct the session
153 # construct the session
156 self.session = Session(**kwargs)
154 self.session = Session(**kwargs)
157
155
158
156
159 class Message(object):
157 class Message(object):
160 """A simple message object that maps dict keys to attributes.
158 """A simple message object that maps dict keys to attributes.
161
159
162 A Message can be created from a dict and a dict from a Message instance
160 A Message can be created from a dict and a dict from a Message instance
163 simply by calling dict(msg_obj)."""
161 simply by calling dict(msg_obj)."""
164
162
165 def __init__(self, msg_dict):
163 def __init__(self, msg_dict):
166 dct = self.__dict__
164 dct = self.__dict__
167 for k, v in dict(msg_dict).iteritems():
165 for k, v in dict(msg_dict).iteritems():
168 if isinstance(v, dict):
166 if isinstance(v, dict):
169 v = Message(v)
167 v = Message(v)
170 dct[k] = v
168 dct[k] = v
171
169
172 # Having this iterator lets dict(msg_obj) work out of the box.
170 # Having this iterator lets dict(msg_obj) work out of the box.
173 def __iter__(self):
171 def __iter__(self):
174 return iter(self.__dict__.iteritems())
172 return iter(self.__dict__.iteritems())
175
173
176 def __repr__(self):
174 def __repr__(self):
177 return repr(self.__dict__)
175 return repr(self.__dict__)
178
176
179 def __str__(self):
177 def __str__(self):
180 return pprint.pformat(self.__dict__)
178 return pprint.pformat(self.__dict__)
181
179
182 def __contains__(self, k):
180 def __contains__(self, k):
183 return k in self.__dict__
181 return k in self.__dict__
184
182
185 def __getitem__(self, k):
183 def __getitem__(self, k):
186 return self.__dict__[k]
184 return self.__dict__[k]
187
185
188
186
189 def msg_header(msg_id, msg_type, username, session):
187 def msg_header(msg_id, msg_type, username, session):
190 date = datetime.now()
188 date = datetime.now()
191 version = _version_info_list
192 return locals()
189 return locals()
193
190
194 def extract_header(msg_or_header):
191 def extract_header(msg_or_header):
195 """Given a message or header, return the header."""
192 """Given a message or header, return the header."""
196 if not msg_or_header:
193 if not msg_or_header:
197 return {}
194 return {}
198 try:
195 try:
199 # See if msg_or_header is the entire message.
196 # See if msg_or_header is the entire message.
200 h = msg_or_header['header']
197 h = msg_or_header['header']
201 except KeyError:
198 except KeyError:
202 try:
199 try:
203 # See if msg_or_header is just the header
200 # See if msg_or_header is just the header
204 h = msg_or_header['msg_id']
201 h = msg_or_header['msg_id']
205 except KeyError:
202 except KeyError:
206 raise
203 raise
207 else:
204 else:
208 h = msg_or_header
205 h = msg_or_header
209 if not isinstance(h, dict):
206 if not isinstance(h, dict):
210 h = dict(h)
207 h = dict(h)
211 return h
208 return h
212
209
213 class Session(Configurable):
210 class Session(Configurable):
214 """Object for handling serialization and sending of messages.
211 """Object for handling serialization and sending of messages.
215
212
216 The Session object handles building messages and sending them
213 The Session object handles building messages and sending them
217 with ZMQ sockets or ZMQStream objects. Objects can communicate with each
214 with ZMQ sockets or ZMQStream objects. Objects can communicate with each
218 other over the network via Session objects, and only need to work with the
215 other over the network via Session objects, and only need to work with the
219 dict-based IPython message spec. The Session will handle
216 dict-based IPython message spec. The Session will handle
220 serialization/deserialization, security, and metadata.
217 serialization/deserialization, security, and metadata.
221
218
222 Sessions support configurable serialiization via packer/unpacker traits,
219 Sessions support configurable serialiization via packer/unpacker traits,
223 and signing with HMAC digests via the key/keyfile traits.
220 and signing with HMAC digests via the key/keyfile traits.
224
221
225 Parameters
222 Parameters
226 ----------
223 ----------
227
224
228 debug : bool
225 debug : bool
229 whether to trigger extra debugging statements
226 whether to trigger extra debugging statements
230 packer/unpacker : str : 'json', 'pickle' or import_string
227 packer/unpacker : str : 'json', 'pickle' or import_string
231 importstrings for methods to serialize message parts. If just
228 importstrings for methods to serialize message parts. If just
232 'json' or 'pickle', predefined JSON and pickle packers will be used.
229 'json' or 'pickle', predefined JSON and pickle packers will be used.
233 Otherwise, the entire importstring must be used.
230 Otherwise, the entire importstring must be used.
234
231
235 The functions must accept at least valid JSON input, and output *bytes*.
232 The functions must accept at least valid JSON input, and output *bytes*.
236
233
237 For example, to use msgpack:
234 For example, to use msgpack:
238 packer = 'msgpack.packb', unpacker='msgpack.unpackb'
235 packer = 'msgpack.packb', unpacker='msgpack.unpackb'
239 pack/unpack : callables
236 pack/unpack : callables
240 You can also set the pack/unpack callables for serialization directly.
237 You can also set the pack/unpack callables for serialization directly.
241 session : bytes
238 session : bytes
242 the ID of this Session object. The default is to generate a new UUID.
239 the ID of this Session object. The default is to generate a new UUID.
243 username : unicode
240 username : unicode
244 username added to message headers. The default is to ask the OS.
241 username added to message headers. The default is to ask the OS.
245 key : bytes
242 key : bytes
246 The key used to initialize an HMAC signature. If unset, messages
243 The key used to initialize an HMAC signature. If unset, messages
247 will not be signed or checked.
244 will not be signed or checked.
248 keyfile : filepath
245 keyfile : filepath
249 The file containing a key. If this is set, `key` will be initialized
246 The file containing a key. If this is set, `key` will be initialized
250 to the contents of the file.
247 to the contents of the file.
251
248
252 """
249 """
253
250
254 debug=Bool(False, config=True, help="""Debug output in the Session""")
251 debug=Bool(False, config=True, help="""Debug output in the Session""")
255
252
256 packer = DottedObjectName('json',config=True,
253 packer = DottedObjectName('json',config=True,
257 help="""The name of the packer for serializing messages.
254 help="""The name of the packer for serializing messages.
258 Should be one of 'json', 'pickle', or an import name
255 Should be one of 'json', 'pickle', or an import name
259 for a custom callable serializer.""")
256 for a custom callable serializer.""")
260 def _packer_changed(self, name, old, new):
257 def _packer_changed(self, name, old, new):
261 if new.lower() == 'json':
258 if new.lower() == 'json':
262 self.pack = json_packer
259 self.pack = json_packer
263 self.unpack = json_unpacker
260 self.unpack = json_unpacker
264 self.unpacker = new
261 self.unpacker = new
265 elif new.lower() == 'pickle':
262 elif new.lower() == 'pickle':
266 self.pack = pickle_packer
263 self.pack = pickle_packer
267 self.unpack = pickle_unpacker
264 self.unpack = pickle_unpacker
268 self.unpacker = new
265 self.unpacker = new
269 else:
266 else:
270 self.pack = import_item(str(new))
267 self.pack = import_item(str(new))
271
268
272 unpacker = DottedObjectName('json', config=True,
269 unpacker = DottedObjectName('json', config=True,
273 help="""The name of the unpacker for unserializing messages.
270 help="""The name of the unpacker for unserializing messages.
274 Only used with custom functions for `packer`.""")
271 Only used with custom functions for `packer`.""")
275 def _unpacker_changed(self, name, old, new):
272 def _unpacker_changed(self, name, old, new):
276 if new.lower() == 'json':
273 if new.lower() == 'json':
277 self.pack = json_packer
274 self.pack = json_packer
278 self.unpack = json_unpacker
275 self.unpack = json_unpacker
279 self.packer = new
276 self.packer = new
280 elif new.lower() == 'pickle':
277 elif new.lower() == 'pickle':
281 self.pack = pickle_packer
278 self.pack = pickle_packer
282 self.unpack = pickle_unpacker
279 self.unpack = pickle_unpacker
283 self.packer = new
280 self.packer = new
284 else:
281 else:
285 self.unpack = import_item(str(new))
282 self.unpack = import_item(str(new))
286
283
287 session = CUnicode(u'', config=True,
284 session = CUnicode(u'', config=True,
288 help="""The UUID identifying this session.""")
285 help="""The UUID identifying this session.""")
289 def _session_default(self):
286 def _session_default(self):
290 u = unicode(uuid.uuid4())
287 u = unicode(uuid.uuid4())
291 self.bsession = u.encode('ascii')
288 self.bsession = u.encode('ascii')
292 return u
289 return u
293
290
294 def _session_changed(self, name, old, new):
291 def _session_changed(self, name, old, new):
295 self.bsession = self.session.encode('ascii')
292 self.bsession = self.session.encode('ascii')
296
293
297 # bsession is the session as bytes
294 # bsession is the session as bytes
298 bsession = CBytes(b'')
295 bsession = CBytes(b'')
299
296
300 username = Unicode(os.environ.get('USER',u'username'), config=True,
297 username = Unicode(os.environ.get('USER',u'username'), config=True,
301 help="""Username for the Session. Default is your system username.""")
298 help="""Username for the Session. Default is your system username.""")
302
299
303 metadata = Dict({}, config=True,
300 metadata = Dict({}, config=True,
304 help="""Metadata dictionary, which serves as the default top-level metadata dict for each message.""")
301 help="""Metadata dictionary, which serves as the default top-level metadata dict for each message.""")
305
302
306 # message signature related traits:
303 # message signature related traits:
307
304
308 key = CBytes(b'', config=True,
305 key = CBytes(b'', config=True,
309 help="""execution key, for extra authentication.""")
306 help="""execution key, for extra authentication.""")
310 def _key_changed(self, name, old, new):
307 def _key_changed(self, name, old, new):
311 if new:
308 if new:
312 self.auth = hmac.HMAC(new)
309 self.auth = hmac.HMAC(new)
313 else:
310 else:
314 self.auth = None
311 self.auth = None
315 auth = Instance(hmac.HMAC)
312 auth = Instance(hmac.HMAC)
316 digest_history = Set()
313 digest_history = Set()
317
314
318 keyfile = Unicode('', config=True,
315 keyfile = Unicode('', config=True,
319 help="""path to file containing execution key.""")
316 help="""path to file containing execution key.""")
320 def _keyfile_changed(self, name, old, new):
317 def _keyfile_changed(self, name, old, new):
321 with open(new, 'rb') as f:
318 with open(new, 'rb') as f:
322 self.key = f.read().strip()
319 self.key = f.read().strip()
323
320
324 # serialization traits:
321 # serialization traits:
325
322
326 pack = Any(default_packer) # the actual packer function
323 pack = Any(default_packer) # the actual packer function
327 def _pack_changed(self, name, old, new):
324 def _pack_changed(self, name, old, new):
328 if not callable(new):
325 if not callable(new):
329 raise TypeError("packer must be callable, not %s"%type(new))
326 raise TypeError("packer must be callable, not %s"%type(new))
330
327
331 unpack = Any(default_unpacker) # the actual packer function
328 unpack = Any(default_unpacker) # the actual packer function
332 def _unpack_changed(self, name, old, new):
329 def _unpack_changed(self, name, old, new):
333 # unpacker is not checked - it is assumed to be
330 # unpacker is not checked - it is assumed to be
334 if not callable(new):
331 if not callable(new):
335 raise TypeError("unpacker must be callable, not %s"%type(new))
332 raise TypeError("unpacker must be callable, not %s"%type(new))
336
333
337 # thresholds:
334 # thresholds:
338 copy_threshold = Integer(2**16, config=True,
335 copy_threshold = Integer(2**16, config=True,
339 help="Threshold (in bytes) beyond which a buffer should be sent without copying.")
336 help="Threshold (in bytes) beyond which a buffer should be sent without copying.")
340 buffer_threshold = Integer(MAX_BYTES, config=True,
337 buffer_threshold = Integer(MAX_BYTES, config=True,
341 help="Threshold (in bytes) beyond which an object's buffer should be extracted to avoid pickling.")
338 help="Threshold (in bytes) beyond which an object's buffer should be extracted to avoid pickling.")
342 item_threshold = Integer(MAX_ITEMS, config=True,
339 item_threshold = Integer(MAX_ITEMS, config=True,
343 help="""The maximum number of items for a container to be introspected for custom serialization.
340 help="""The maximum number of items for a container to be introspected for custom serialization.
344 Containers larger than this are pickled outright.
341 Containers larger than this are pickled outright.
345 """
342 """
346 )
343 )
347
344
348 def __init__(self, **kwargs):
345 def __init__(self, **kwargs):
349 """create a Session object
346 """create a Session object
350
347
351 Parameters
348 Parameters
352 ----------
349 ----------
353
350
354 debug : bool
351 debug : bool
355 whether to trigger extra debugging statements
352 whether to trigger extra debugging statements
356 packer/unpacker : str : 'json', 'pickle' or import_string
353 packer/unpacker : str : 'json', 'pickle' or import_string
357 importstrings for methods to serialize message parts. If just
354 importstrings for methods to serialize message parts. If just
358 'json' or 'pickle', predefined JSON and pickle packers will be used.
355 'json' or 'pickle', predefined JSON and pickle packers will be used.
359 Otherwise, the entire importstring must be used.
356 Otherwise, the entire importstring must be used.
360
357
361 The functions must accept at least valid JSON input, and output
358 The functions must accept at least valid JSON input, and output
362 *bytes*.
359 *bytes*.
363
360
364 For example, to use msgpack:
361 For example, to use msgpack:
365 packer = 'msgpack.packb', unpacker='msgpack.unpackb'
362 packer = 'msgpack.packb', unpacker='msgpack.unpackb'
366 pack/unpack : callables
363 pack/unpack : callables
367 You can also set the pack/unpack callables for serialization
364 You can also set the pack/unpack callables for serialization
368 directly.
365 directly.
369 session : unicode (must be ascii)
366 session : unicode (must be ascii)
370 the ID of this Session object. The default is to generate a new
367 the ID of this Session object. The default is to generate a new
371 UUID.
368 UUID.
372 bsession : bytes
369 bsession : bytes
373 The session as bytes
370 The session as bytes
374 username : unicode
371 username : unicode
375 username added to message headers. The default is to ask the OS.
372 username added to message headers. The default is to ask the OS.
376 key : bytes
373 key : bytes
377 The key used to initialize an HMAC signature. If unset, messages
374 The key used to initialize an HMAC signature. If unset, messages
378 will not be signed or checked.
375 will not be signed or checked.
379 keyfile : filepath
376 keyfile : filepath
380 The file containing a key. If this is set, `key` will be
377 The file containing a key. If this is set, `key` will be
381 initialized to the contents of the file.
378 initialized to the contents of the file.
382 """
379 """
383 super(Session, self).__init__(**kwargs)
380 super(Session, self).__init__(**kwargs)
384 self._check_packers()
381 self._check_packers()
385 self.none = self.pack({})
382 self.none = self.pack({})
386 # ensure self._session_default() if necessary, so bsession is defined:
383 # ensure self._session_default() if necessary, so bsession is defined:
387 self.session
384 self.session
388
385
389 @property
386 @property
390 def msg_id(self):
387 def msg_id(self):
391 """always return new uuid"""
388 """always return new uuid"""
392 return str(uuid.uuid4())
389 return str(uuid.uuid4())
393
390
394 def _check_packers(self):
391 def _check_packers(self):
395 """check packers for binary data and datetime support."""
392 """check packers for binary data and datetime support."""
396 pack = self.pack
393 pack = self.pack
397 unpack = self.unpack
394 unpack = self.unpack
398
395
399 # check simple serialization
396 # check simple serialization
400 msg = dict(a=[1,'hi'])
397 msg = dict(a=[1,'hi'])
401 try:
398 try:
402 packed = pack(msg)
399 packed = pack(msg)
403 except Exception:
400 except Exception:
404 raise ValueError("packer could not serialize a simple message")
401 raise ValueError("packer could not serialize a simple message")
405
402
406 # ensure packed message is bytes
403 # ensure packed message is bytes
407 if not isinstance(packed, bytes):
404 if not isinstance(packed, bytes):
408 raise ValueError("message packed to %r, but bytes are required"%type(packed))
405 raise ValueError("message packed to %r, but bytes are required"%type(packed))
409
406
410 # check that unpack is pack's inverse
407 # check that unpack is pack's inverse
411 try:
408 try:
412 unpacked = unpack(packed)
409 unpacked = unpack(packed)
413 except Exception:
410 except Exception:
414 raise ValueError("unpacker could not handle the packer's output")
411 raise ValueError("unpacker could not handle the packer's output")
415
412
416 # check datetime support
413 # check datetime support
417 msg = dict(t=datetime.now())
414 msg = dict(t=datetime.now())
418 try:
415 try:
419 unpacked = unpack(pack(msg))
416 unpacked = unpack(pack(msg))
420 except Exception:
417 except Exception:
421 self.pack = lambda o: pack(squash_dates(o))
418 self.pack = lambda o: pack(squash_dates(o))
422 self.unpack = lambda s: extract_dates(unpack(s))
419 self.unpack = lambda s: extract_dates(unpack(s))
423
420
424 def msg_header(self, msg_type):
421 def msg_header(self, msg_type):
425 return msg_header(self.msg_id, msg_type, self.username, self.session)
422 return msg_header(self.msg_id, msg_type, self.username, self.session)
426
423
427 def msg(self, msg_type, content=None, parent=None, header=None, metadata=None):
424 def msg(self, msg_type, content=None, parent=None, header=None, metadata=None):
428 """Return the nested message dict.
425 """Return the nested message dict.
429
426
430 This format is different from what is sent over the wire. The
427 This format is different from what is sent over the wire. The
431 serialize/unserialize methods converts this nested message dict to the wire
428 serialize/unserialize methods converts this nested message dict to the wire
432 format, which is a list of message parts.
429 format, which is a list of message parts.
433 """
430 """
434 msg = {}
431 msg = {}
435 header = self.msg_header(msg_type) if header is None else header
432 header = self.msg_header(msg_type) if header is None else header
436 msg['header'] = header
433 msg['header'] = header
437 msg['msg_id'] = header['msg_id']
434 msg['msg_id'] = header['msg_id']
438 msg['msg_type'] = header['msg_type']
435 msg['msg_type'] = header['msg_type']
439 msg['parent_header'] = {} if parent is None else extract_header(parent)
436 msg['parent_header'] = {} if parent is None else extract_header(parent)
440 msg['content'] = {} if content is None else content
437 msg['content'] = {} if content is None else content
441 msg['metadata'] = self.metadata.copy()
438 msg['metadata'] = self.metadata.copy()
442 if metadata is not None:
439 if metadata is not None:
443 msg['metadata'].update(metadata)
440 msg['metadata'].update(metadata)
444 return msg
441 return msg
445
442
446 def sign(self, msg_list):
443 def sign(self, msg_list):
447 """Sign a message with HMAC digest. If no auth, return b''.
444 """Sign a message with HMAC digest. If no auth, return b''.
448
445
449 Parameters
446 Parameters
450 ----------
447 ----------
451 msg_list : list
448 msg_list : list
452 The [p_header,p_parent,p_content] part of the message list.
449 The [p_header,p_parent,p_content] part of the message list.
453 """
450 """
454 if self.auth is None:
451 if self.auth is None:
455 return b''
452 return b''
456 h = self.auth.copy()
453 h = self.auth.copy()
457 for m in msg_list:
454 for m in msg_list:
458 h.update(m)
455 h.update(m)
459 return str_to_bytes(h.hexdigest())
456 return str_to_bytes(h.hexdigest())
460
457
461 def serialize(self, msg, ident=None):
458 def serialize(self, msg, ident=None):
462 """Serialize the message components to bytes.
459 """Serialize the message components to bytes.
463
460
464 This is roughly the inverse of unserialize. The serialize/unserialize
461 This is roughly the inverse of unserialize. The serialize/unserialize
465 methods work with full message lists, whereas pack/unpack work with
462 methods work with full message lists, whereas pack/unpack work with
466 the individual message parts in the message list.
463 the individual message parts in the message list.
467
464
468 Parameters
465 Parameters
469 ----------
466 ----------
470 msg : dict or Message
467 msg : dict or Message
471 The nexted message dict as returned by the self.msg method.
468 The nexted message dict as returned by the self.msg method.
472
469
473 Returns
470 Returns
474 -------
471 -------
475 msg_list : list
472 msg_list : list
476 The list of bytes objects to be sent with the format:
473 The list of bytes objects to be sent with the format:
477 [ident1,ident2,...,DELIM,HMAC,p_header,p_parent,p_metadata,p_content,
474 [ident1,ident2,...,DELIM,HMAC,p_header,p_parent,p_metadata,p_content,
478 buffer1,buffer2,...]. In this list, the p_* entities are
475 buffer1,buffer2,...]. In this list, the p_* entities are
479 the packed or serialized versions, so if JSON is used, these
476 the packed or serialized versions, so if JSON is used, these
480 are utf8 encoded JSON strings.
477 are utf8 encoded JSON strings.
481 """
478 """
482 content = msg.get('content', {})
479 content = msg.get('content', {})
483 if content is None:
480 if content is None:
484 content = self.none
481 content = self.none
485 elif isinstance(content, dict):
482 elif isinstance(content, dict):
486 content = self.pack(content)
483 content = self.pack(content)
487 elif isinstance(content, bytes):
484 elif isinstance(content, bytes):
488 # content is already packed, as in a relayed message
485 # content is already packed, as in a relayed message
489 pass
486 pass
490 elif isinstance(content, unicode):
487 elif isinstance(content, unicode):
491 # should be bytes, but JSON often spits out unicode
488 # should be bytes, but JSON often spits out unicode
492 content = content.encode('utf8')
489 content = content.encode('utf8')
493 else:
490 else:
494 raise TypeError("Content incorrect type: %s"%type(content))
491 raise TypeError("Content incorrect type: %s"%type(content))
495
492
496 real_message = [self.pack(msg['header']),
493 real_message = [self.pack(msg['header']),
497 self.pack(msg['parent_header']),
494 self.pack(msg['parent_header']),
498 self.pack(msg['metadata']),
495 self.pack(msg['metadata']),
499 content,
496 content,
500 ]
497 ]
501
498
502 to_send = []
499 to_send = []
503
500
504 if isinstance(ident, list):
501 if isinstance(ident, list):
505 # accept list of idents
502 # accept list of idents
506 to_send.extend(ident)
503 to_send.extend(ident)
507 elif ident is not None:
504 elif ident is not None:
508 to_send.append(ident)
505 to_send.append(ident)
509 to_send.append(DELIM)
506 to_send.append(DELIM)
510
507
511 signature = self.sign(real_message)
508 signature = self.sign(real_message)
512 to_send.append(signature)
509 to_send.append(signature)
513
510
514 to_send.extend(real_message)
511 to_send.extend(real_message)
515
512
516 return to_send
513 return to_send
517
514
518 def send(self, stream, msg_or_type, content=None, parent=None, ident=None,
515 def send(self, stream, msg_or_type, content=None, parent=None, ident=None,
519 buffers=None, track=False, header=None, metadata=None):
516 buffers=None, track=False, header=None, metadata=None):
520 """Build and send a message via stream or socket.
517 """Build and send a message via stream or socket.
521
518
522 The message format used by this function internally is as follows:
519 The message format used by this function internally is as follows:
523
520
524 [ident1,ident2,...,DELIM,HMAC,p_header,p_parent,p_content,
521 [ident1,ident2,...,DELIM,HMAC,p_header,p_parent,p_content,
525 buffer1,buffer2,...]
522 buffer1,buffer2,...]
526
523
527 The serialize/unserialize methods convert the nested message dict into this
524 The serialize/unserialize methods convert the nested message dict into this
528 format.
525 format.
529
526
530 Parameters
527 Parameters
531 ----------
528 ----------
532
529
533 stream : zmq.Socket or ZMQStream
530 stream : zmq.Socket or ZMQStream
534 The socket-like object used to send the data.
531 The socket-like object used to send the data.
535 msg_or_type : str or Message/dict
532 msg_or_type : str or Message/dict
536 Normally, msg_or_type will be a msg_type unless a message is being
533 Normally, msg_or_type will be a msg_type unless a message is being
537 sent more than once. If a header is supplied, this can be set to
534 sent more than once. If a header is supplied, this can be set to
538 None and the msg_type will be pulled from the header.
535 None and the msg_type will be pulled from the header.
539
536
540 content : dict or None
537 content : dict or None
541 The content of the message (ignored if msg_or_type is a message).
538 The content of the message (ignored if msg_or_type is a message).
542 header : dict or None
539 header : dict or None
543 The header dict for the message (ignored if msg_to_type is a message).
540 The header dict for the message (ignored if msg_to_type is a message).
544 parent : Message or dict or None
541 parent : Message or dict or None
545 The parent or parent header describing the parent of this message
542 The parent or parent header describing the parent of this message
546 (ignored if msg_or_type is a message).
543 (ignored if msg_or_type is a message).
547 ident : bytes or list of bytes
544 ident : bytes or list of bytes
548 The zmq.IDENTITY routing path.
545 The zmq.IDENTITY routing path.
549 metadata : dict or None
546 metadata : dict or None
550 The metadata describing the message
547 The metadata describing the message
551 buffers : list or None
548 buffers : list or None
552 The already-serialized buffers to be appended to the message.
549 The already-serialized buffers to be appended to the message.
553 track : bool
550 track : bool
554 Whether to track. Only for use with Sockets, because ZMQStream
551 Whether to track. Only for use with Sockets, because ZMQStream
555 objects cannot track messages.
552 objects cannot track messages.
556
553
557
554
558 Returns
555 Returns
559 -------
556 -------
560 msg : dict
557 msg : dict
561 The constructed message.
558 The constructed message.
562 """
559 """
563
560
564 if not isinstance(stream, (zmq.Socket, ZMQStream)):
561 if not isinstance(stream, (zmq.Socket, ZMQStream)):
565 raise TypeError("stream must be Socket or ZMQStream, not %r"%type(stream))
562 raise TypeError("stream must be Socket or ZMQStream, not %r"%type(stream))
566 elif track and isinstance(stream, ZMQStream):
563 elif track and isinstance(stream, ZMQStream):
567 raise TypeError("ZMQStream cannot track messages")
564 raise TypeError("ZMQStream cannot track messages")
568
565
569 if isinstance(msg_or_type, (Message, dict)):
566 if isinstance(msg_or_type, (Message, dict)):
570 # We got a Message or message dict, not a msg_type so don't
567 # We got a Message or message dict, not a msg_type so don't
571 # build a new Message.
568 # build a new Message.
572 msg = msg_or_type
569 msg = msg_or_type
573 else:
570 else:
574 msg = self.msg(msg_or_type, content=content, parent=parent,
571 msg = self.msg(msg_or_type, content=content, parent=parent,
575 header=header, metadata=metadata)
572 header=header, metadata=metadata)
576
573
577 buffers = [] if buffers is None else buffers
574 buffers = [] if buffers is None else buffers
578 to_send = self.serialize(msg, ident)
575 to_send = self.serialize(msg, ident)
579 to_send.extend(buffers)
576 to_send.extend(buffers)
580 longest = max([ len(s) for s in to_send ])
577 longest = max([ len(s) for s in to_send ])
581 copy = (longest < self.copy_threshold)
578 copy = (longest < self.copy_threshold)
582
579
583 if buffers and track and not copy:
580 if buffers and track and not copy:
584 # only really track when we are doing zero-copy buffers
581 # only really track when we are doing zero-copy buffers
585 tracker = stream.send_multipart(to_send, copy=False, track=True)
582 tracker = stream.send_multipart(to_send, copy=False, track=True)
586 else:
583 else:
587 # use dummy tracker, which will be done immediately
584 # use dummy tracker, which will be done immediately
588 tracker = DONE
585 tracker = DONE
589 stream.send_multipart(to_send, copy=copy)
586 stream.send_multipart(to_send, copy=copy)
590
587
591 if self.debug:
588 if self.debug:
592 pprint.pprint(msg)
589 pprint.pprint(msg)
593 pprint.pprint(to_send)
590 pprint.pprint(to_send)
594 pprint.pprint(buffers)
591 pprint.pprint(buffers)
595
592
596 msg['tracker'] = tracker
593 msg['tracker'] = tracker
597
594
598 return msg
595 return msg
599
596
600 def send_raw(self, stream, msg_list, flags=0, copy=True, ident=None):
597 def send_raw(self, stream, msg_list, flags=0, copy=True, ident=None):
601 """Send a raw message via ident path.
598 """Send a raw message via ident path.
602
599
603 This method is used to send a already serialized message.
600 This method is used to send a already serialized message.
604
601
605 Parameters
602 Parameters
606 ----------
603 ----------
607 stream : ZMQStream or Socket
604 stream : ZMQStream or Socket
608 The ZMQ stream or socket to use for sending the message.
605 The ZMQ stream or socket to use for sending the message.
609 msg_list : list
606 msg_list : list
610 The serialized list of messages to send. This only includes the
607 The serialized list of messages to send. This only includes the
611 [p_header,p_parent,p_metadata,p_content,buffer1,buffer2,...] portion of
608 [p_header,p_parent,p_metadata,p_content,buffer1,buffer2,...] portion of
612 the message.
609 the message.
613 ident : ident or list
610 ident : ident or list
614 A single ident or a list of idents to use in sending.
611 A single ident or a list of idents to use in sending.
615 """
612 """
616 to_send = []
613 to_send = []
617 if isinstance(ident, bytes):
614 if isinstance(ident, bytes):
618 ident = [ident]
615 ident = [ident]
619 if ident is not None:
616 if ident is not None:
620 to_send.extend(ident)
617 to_send.extend(ident)
621
618
622 to_send.append(DELIM)
619 to_send.append(DELIM)
623 to_send.append(self.sign(msg_list))
620 to_send.append(self.sign(msg_list))
624 to_send.extend(msg_list)
621 to_send.extend(msg_list)
625 stream.send_multipart(msg_list, flags, copy=copy)
622 stream.send_multipart(msg_list, flags, copy=copy)
626
623
627 def recv(self, socket, mode=zmq.NOBLOCK, content=True, copy=True):
624 def recv(self, socket, mode=zmq.NOBLOCK, content=True, copy=True):
628 """Receive and unpack a message.
625 """Receive and unpack a message.
629
626
630 Parameters
627 Parameters
631 ----------
628 ----------
632 socket : ZMQStream or Socket
629 socket : ZMQStream or Socket
633 The socket or stream to use in receiving.
630 The socket or stream to use in receiving.
634
631
635 Returns
632 Returns
636 -------
633 -------
637 [idents], msg
634 [idents], msg
638 [idents] is a list of idents and msg is a nested message dict of
635 [idents] is a list of idents and msg is a nested message dict of
639 same format as self.msg returns.
636 same format as self.msg returns.
640 """
637 """
641 if isinstance(socket, ZMQStream):
638 if isinstance(socket, ZMQStream):
642 socket = socket.socket
639 socket = socket.socket
643 try:
640 try:
644 msg_list = socket.recv_multipart(mode, copy=copy)
641 msg_list = socket.recv_multipart(mode, copy=copy)
645 except zmq.ZMQError as e:
642 except zmq.ZMQError as e:
646 if e.errno == zmq.EAGAIN:
643 if e.errno == zmq.EAGAIN:
647 # We can convert EAGAIN to None as we know in this case
644 # We can convert EAGAIN to None as we know in this case
648 # recv_multipart won't return None.
645 # recv_multipart won't return None.
649 return None,None
646 return None,None
650 else:
647 else:
651 raise
648 raise
652 # split multipart message into identity list and message dict
649 # split multipart message into identity list and message dict
653 # invalid large messages can cause very expensive string comparisons
650 # invalid large messages can cause very expensive string comparisons
654 idents, msg_list = self.feed_identities(msg_list, copy)
651 idents, msg_list = self.feed_identities(msg_list, copy)
655 try:
652 try:
656 return idents, self.unserialize(msg_list, content=content, copy=copy)
653 return idents, self.unserialize(msg_list, content=content, copy=copy)
657 except Exception as e:
654 except Exception as e:
658 # TODO: handle it
655 # TODO: handle it
659 raise e
656 raise e
660
657
661 def feed_identities(self, msg_list, copy=True):
658 def feed_identities(self, msg_list, copy=True):
662 """Split the identities from the rest of the message.
659 """Split the identities from the rest of the message.
663
660
664 Feed until DELIM is reached, then return the prefix as idents and
661 Feed until DELIM is reached, then return the prefix as idents and
665 remainder as msg_list. This is easily broken by setting an IDENT to DELIM,
662 remainder as msg_list. This is easily broken by setting an IDENT to DELIM,
666 but that would be silly.
663 but that would be silly.
667
664
668 Parameters
665 Parameters
669 ----------
666 ----------
670 msg_list : a list of Message or bytes objects
667 msg_list : a list of Message or bytes objects
671 The message to be split.
668 The message to be split.
672 copy : bool
669 copy : bool
673 flag determining whether the arguments are bytes or Messages
670 flag determining whether the arguments are bytes or Messages
674
671
675 Returns
672 Returns
676 -------
673 -------
677 (idents, msg_list) : two lists
674 (idents, msg_list) : two lists
678 idents will always be a list of bytes, each of which is a ZMQ
675 idents will always be a list of bytes, each of which is a ZMQ
679 identity. msg_list will be a list of bytes or zmq.Messages of the
676 identity. msg_list will be a list of bytes or zmq.Messages of the
680 form [HMAC,p_header,p_parent,p_content,buffer1,buffer2,...] and
677 form [HMAC,p_header,p_parent,p_content,buffer1,buffer2,...] and
681 should be unpackable/unserializable via self.unserialize at this
678 should be unpackable/unserializable via self.unserialize at this
682 point.
679 point.
683 """
680 """
684 if copy:
681 if copy:
685 idx = msg_list.index(DELIM)
682 idx = msg_list.index(DELIM)
686 return msg_list[:idx], msg_list[idx+1:]
683 return msg_list[:idx], msg_list[idx+1:]
687 else:
684 else:
688 failed = True
685 failed = True
689 for idx,m in enumerate(msg_list):
686 for idx,m in enumerate(msg_list):
690 if m.bytes == DELIM:
687 if m.bytes == DELIM:
691 failed = False
688 failed = False
692 break
689 break
693 if failed:
690 if failed:
694 raise ValueError("DELIM not in msg_list")
691 raise ValueError("DELIM not in msg_list")
695 idents, msg_list = msg_list[:idx], msg_list[idx+1:]
692 idents, msg_list = msg_list[:idx], msg_list[idx+1:]
696 return [m.bytes for m in idents], msg_list
693 return [m.bytes for m in idents], msg_list
697
694
698 def unserialize(self, msg_list, content=True, copy=True):
695 def unserialize(self, msg_list, content=True, copy=True):
699 """Unserialize a msg_list to a nested message dict.
696 """Unserialize a msg_list to a nested message dict.
700
697
701 This is roughly the inverse of serialize. The serialize/unserialize
698 This is roughly the inverse of serialize. The serialize/unserialize
702 methods work with full message lists, whereas pack/unpack work with
699 methods work with full message lists, whereas pack/unpack work with
703 the individual message parts in the message list.
700 the individual message parts in the message list.
704
701
705 Parameters:
702 Parameters:
706 -----------
703 -----------
707 msg_list : list of bytes or Message objects
704 msg_list : list of bytes or Message objects
708 The list of message parts of the form [HMAC,p_header,p_parent,
705 The list of message parts of the form [HMAC,p_header,p_parent,
709 p_metadata,p_content,buffer1,buffer2,...].
706 p_metadata,p_content,buffer1,buffer2,...].
710 content : bool (True)
707 content : bool (True)
711 Whether to unpack the content dict (True), or leave it packed
708 Whether to unpack the content dict (True), or leave it packed
712 (False).
709 (False).
713 copy : bool (True)
710 copy : bool (True)
714 Whether to return the bytes (True), or the non-copying Message
711 Whether to return the bytes (True), or the non-copying Message
715 object in each place (False).
712 object in each place (False).
716
713
717 Returns
714 Returns
718 -------
715 -------
719 msg : dict
716 msg : dict
720 The nested message dict with top-level keys [header, parent_header,
717 The nested message dict with top-level keys [header, parent_header,
721 content, buffers].
718 content, buffers].
722 """
719 """
723 minlen = 5
720 minlen = 5
724 message = {}
721 message = {}
725 if not copy:
722 if not copy:
726 for i in range(minlen):
723 for i in range(minlen):
727 msg_list[i] = msg_list[i].bytes
724 msg_list[i] = msg_list[i].bytes
728 if self.auth is not None:
725 if self.auth is not None:
729 signature = msg_list[0]
726 signature = msg_list[0]
730 if not signature:
727 if not signature:
731 raise ValueError("Unsigned Message")
728 raise ValueError("Unsigned Message")
732 if signature in self.digest_history:
729 if signature in self.digest_history:
733 raise ValueError("Duplicate Signature: %r"%signature)
730 raise ValueError("Duplicate Signature: %r"%signature)
734 self.digest_history.add(signature)
731 self.digest_history.add(signature)
735 check = self.sign(msg_list[1:5])
732 check = self.sign(msg_list[1:5])
736 if not signature == check:
733 if not signature == check:
737 raise ValueError("Invalid Signature: %r" % signature)
734 raise ValueError("Invalid Signature: %r" % signature)
738 if not len(msg_list) >= minlen:
735 if not len(msg_list) >= minlen:
739 raise TypeError("malformed message, must have at least %i elements"%minlen)
736 raise TypeError("malformed message, must have at least %i elements"%minlen)
740 header = self.unpack(msg_list[1])
737 header = self.unpack(msg_list[1])
741 message['header'] = header
738 message['header'] = header
742 message['msg_id'] = header['msg_id']
739 message['msg_id'] = header['msg_id']
743 message['msg_type'] = header['msg_type']
740 message['msg_type'] = header['msg_type']
744 message['parent_header'] = self.unpack(msg_list[2])
741 message['parent_header'] = self.unpack(msg_list[2])
745 message['metadata'] = self.unpack(msg_list[3])
742 message['metadata'] = self.unpack(msg_list[3])
746 if content:
743 if content:
747 message['content'] = self.unpack(msg_list[4])
744 message['content'] = self.unpack(msg_list[4])
748 else:
745 else:
749 message['content'] = msg_list[4]
746 message['content'] = msg_list[4]
750
747
751 message['buffers'] = msg_list[5:]
748 message['buffers'] = msg_list[5:]
752 return message
749 return message
753
750
754 def test_msg2obj():
751 def test_msg2obj():
755 am = dict(x=1)
752 am = dict(x=1)
756 ao = Message(am)
753 ao = Message(am)
757 assert ao.x == am['x']
754 assert ao.x == am['x']
758
755
759 am['y'] = dict(z=1)
756 am['y'] = dict(z=1)
760 ao = Message(am)
757 ao = Message(am)
761 assert ao.y.z == am['y']['z']
758 assert ao.y.z == am['y']['z']
762
759
763 k1, k2 = 'y', 'z'
760 k1, k2 = 'y', 'z'
764 assert ao[k1][k2] == am[k1][k2]
761 assert ao[k1][k2] == am[k1][k2]
765
762
766 am2 = dict(ao)
763 am2 = dict(ao)
767 assert am['x'] == am2['x']
764 assert am['x'] == am2['x']
768 assert am['y']['z'] == am2['y']['z']
765 assert am['y']['z'] == am2['y']['z']
769
766
@@ -1,452 +1,497 b''
1 """Test suite for our zeromq-based messaging specification.
1 """Test suite for our zeromq-based messaging specification.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2010-2011 The IPython Development Team
4 # Copyright (C) 2010-2011 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING.txt, distributed as part of this software.
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 import re
10 import re
11 import sys
11 import sys
12 import time
12 import time
13 from subprocess import PIPE
13 from subprocess import PIPE
14 from Queue import Empty
14 from Queue import Empty
15
15
16 import nose.tools as nt
16 import nose.tools as nt
17
17
18 from ..blockingkernelmanager import BlockingKernelManager
18 from ..blockingkernelmanager import BlockingKernelManager
19
19
20
20
21 from IPython.testing import decorators as dec
21 from IPython.testing import decorators as dec
22 from IPython.utils import io
22 from IPython.utils import io
23 from IPython.utils.traitlets import (
23 from IPython.utils.traitlets import (
24 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum,
24 HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any,
25 )
25 )
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Global setup and utilities
28 # Global setup and utilities
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 def setup():
31 def setup():
32 global KM
32 global KM
33 KM = BlockingKernelManager()
33 KM = BlockingKernelManager()
34
34
35 KM.start_kernel(stdout=PIPE, stderr=PIPE)
35 KM.start_kernel(stdout=PIPE, stderr=PIPE)
36 KM.start_channels()
36 KM.start_channels()
37
37
38 # wait for kernel to be ready
38 # wait for kernel to be ready
39 KM.shell_channel.execute("pass")
39 KM.shell_channel.execute("pass")
40 KM.shell_channel.get_msg(block=True, timeout=5)
40 KM.shell_channel.get_msg(block=True, timeout=5)
41 flush_channels()
41 flush_channels()
42
42
43
43
44 def teardown():
44 def teardown():
45 KM.stop_channels()
45 KM.stop_channels()
46 KM.shutdown_kernel()
46 KM.shutdown_kernel()
47
47
48
48
49 def flush_channels():
49 def flush_channels():
50 """flush any messages waiting on the queue"""
50 """flush any messages waiting on the queue"""
51 for channel in (KM.shell_channel, KM.sub_channel):
51 for channel in (KM.shell_channel, KM.sub_channel):
52 while True:
52 while True:
53 try:
53 try:
54 msg = channel.get_msg(block=True, timeout=0.1)
54 msg = channel.get_msg(block=True, timeout=0.1)
55 except Empty:
55 except Empty:
56 break
56 break
57 else:
57 else:
58 list(validate_message(msg))
58 list(validate_message(msg))
59
59
60
60
61 def execute(code='', **kwargs):
61 def execute(code='', **kwargs):
62 """wrapper for doing common steps for validating an execution request"""
62 """wrapper for doing common steps for validating an execution request"""
63 shell = KM.shell_channel
63 shell = KM.shell_channel
64 sub = KM.sub_channel
64 sub = KM.sub_channel
65
65
66 msg_id = shell.execute(code=code, **kwargs)
66 msg_id = shell.execute(code=code, **kwargs)
67 reply = shell.get_msg(timeout=2)
67 reply = shell.get_msg(timeout=2)
68 list(validate_message(reply, 'execute_reply', msg_id))
68 list(validate_message(reply, 'execute_reply', msg_id))
69 busy = sub.get_msg(timeout=2)
69 busy = sub.get_msg(timeout=2)
70 list(validate_message(busy, 'status', msg_id))
70 list(validate_message(busy, 'status', msg_id))
71 nt.assert_equal(busy['content']['execution_state'], 'busy')
71 nt.assert_equal(busy['content']['execution_state'], 'busy')
72
72
73 if not kwargs.get('silent'):
73 if not kwargs.get('silent'):
74 pyin = sub.get_msg(timeout=2)
74 pyin = sub.get_msg(timeout=2)
75 list(validate_message(pyin, 'pyin', msg_id))
75 list(validate_message(pyin, 'pyin', msg_id))
76 nt.assert_equal(pyin['content']['code'], code)
76 nt.assert_equal(pyin['content']['code'], code)
77
77
78 return msg_id, reply['content']
78 return msg_id, reply['content']
79
79
80 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
81 # MSG Spec References
81 # MSG Spec References
82 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
83
83
84
84
85 class Reference(HasTraits):
85 class Reference(HasTraits):
86
86
87 """
88 Base class for message spec specification testing.
89
90 This class is the core of the message specification test. The
91 idea is that child classes implement trait attributes for each
92 message keys, so that message keys can be tested against these
93 traits using :meth:`check` method.
94
95 """
96
87 def check(self, d):
97 def check(self, d):
88 """validate a dict against our traits"""
98 """validate a dict against our traits"""
89 for key in self.trait_names():
99 for key in self.trait_names():
90 yield nt.assert_true(key in d, "Missing key: %r, should be found in %s" % (key, d))
100 yield nt.assert_true(key in d, "Missing key: %r, should be found in %s" % (key, d))
91 # FIXME: always allow None, probably not a good idea
101 # FIXME: always allow None, probably not a good idea
92 if d[key] is None:
102 if d[key] is None:
93 continue
103 continue
94 try:
104 try:
95 setattr(self, key, d[key])
105 setattr(self, key, d[key])
96 except TraitError as e:
106 except TraitError as e:
97 yield nt.assert_true(False, str(e))
107 yield nt.assert_true(False, str(e))
98
108
99
109
100 class RMessage(Reference):
110 class RMessage(Reference):
101 msg_id = Unicode()
111 msg_id = Unicode()
102 msg_type = Unicode()
112 msg_type = Unicode()
103 header = Dict()
113 header = Dict()
104 parent_header = Dict()
114 parent_header = Dict()
105 content = Dict()
115 content = Dict()
106
116
107 class RHeader(Reference):
117 class RHeader(Reference):
108 msg_id = Unicode()
118 msg_id = Unicode()
109 msg_type = Unicode()
119 msg_type = Unicode()
110 session = Unicode()
120 session = Unicode()
111 username = Unicode()
121 username = Unicode()
112
122
113 class RContent(Reference):
123 class RContent(Reference):
114 status = Enum((u'ok', u'error'))
124 status = Enum((u'ok', u'error'))
115
125
116
126
117 class ExecuteReply(Reference):
127 class ExecuteReply(Reference):
118 execution_count = Integer()
128 execution_count = Integer()
119 status = Enum((u'ok', u'error'))
129 status = Enum((u'ok', u'error'))
120
130
121 def check(self, d):
131 def check(self, d):
122 for tst in Reference.check(self, d):
132 for tst in Reference.check(self, d):
123 yield tst
133 yield tst
124 if d['status'] == 'ok':
134 if d['status'] == 'ok':
125 for tst in ExecuteReplyOkay().check(d):
135 for tst in ExecuteReplyOkay().check(d):
126 yield tst
136 yield tst
127 elif d['status'] == 'error':
137 elif d['status'] == 'error':
128 for tst in ExecuteReplyError().check(d):
138 for tst in ExecuteReplyError().check(d):
129 yield tst
139 yield tst
130
140
131
141
132 class ExecuteReplyOkay(Reference):
142 class ExecuteReplyOkay(Reference):
133 payload = List(Dict)
143 payload = List(Dict)
134 user_variables = Dict()
144 user_variables = Dict()
135 user_expressions = Dict()
145 user_expressions = Dict()
136
146
137
147
138 class ExecuteReplyError(Reference):
148 class ExecuteReplyError(Reference):
139 ename = Unicode()
149 ename = Unicode()
140 evalue = Unicode()
150 evalue = Unicode()
141 traceback = List(Unicode)
151 traceback = List(Unicode)
142
152
143
153
144 class OInfoReply(Reference):
154 class OInfoReply(Reference):
145 name = Unicode()
155 name = Unicode()
146 found = Bool()
156 found = Bool()
147 ismagic = Bool()
157 ismagic = Bool()
148 isalias = Bool()
158 isalias = Bool()
149 namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive'))
159 namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive'))
150 type_name = Unicode()
160 type_name = Unicode()
151 string_form = Unicode()
161 string_form = Unicode()
152 base_class = Unicode()
162 base_class = Unicode()
153 length = Integer()
163 length = Integer()
154 file = Unicode()
164 file = Unicode()
155 definition = Unicode()
165 definition = Unicode()
156 argspec = Dict()
166 argspec = Dict()
157 init_definition = Unicode()
167 init_definition = Unicode()
158 docstring = Unicode()
168 docstring = Unicode()
159 init_docstring = Unicode()
169 init_docstring = Unicode()
160 class_docstring = Unicode()
170 class_docstring = Unicode()
161 call_def = Unicode()
171 call_def = Unicode()
162 call_docstring = Unicode()
172 call_docstring = Unicode()
163 source = Unicode()
173 source = Unicode()
164
174
165 def check(self, d):
175 def check(self, d):
166 for tst in Reference.check(self, d):
176 for tst in Reference.check(self, d):
167 yield tst
177 yield tst
168 if d['argspec'] is not None:
178 if d['argspec'] is not None:
169 for tst in ArgSpec().check(d['argspec']):
179 for tst in ArgSpec().check(d['argspec']):
170 yield tst
180 yield tst
171
181
172
182
173 class ArgSpec(Reference):
183 class ArgSpec(Reference):
174 args = List(Unicode)
184 args = List(Unicode)
175 varargs = Unicode()
185 varargs = Unicode()
176 varkw = Unicode()
186 varkw = Unicode()
177 defaults = List()
187 defaults = List()
178
188
179
189
180 class Status(Reference):
190 class Status(Reference):
181 execution_state = Enum((u'busy', u'idle'))
191 execution_state = Enum((u'busy', u'idle'))
182
192
183
193
184 class CompleteReply(Reference):
194 class CompleteReply(Reference):
185 matches = List(Unicode)
195 matches = List(Unicode)
186
196
187
197
198 def Version(num, trait=Integer):
199 return List(trait, default_value=[0] * num, minlen=num, maxlen=num)
200
201
202 class KernelInfoReply(Reference):
203
204 protocol_version = Version(2)
205 ipython_version = Version(4, Any)
206 language_version = Version(3)
207 language = Unicode()
208
209 def _ipython_version_changed(self, name, old, new):
210 for v in new:
211 nt.assert_true(
212 isinstance(v, int) or isinstance(v, basestring),
213 'expected int or string as version component, got {0!r}'
214 .format(v))
215
216
188 # IOPub messages
217 # IOPub messages
189
218
190 class PyIn(Reference):
219 class PyIn(Reference):
191 code = Unicode()
220 code = Unicode()
192 execution_count = Integer()
221 execution_count = Integer()
193
222
194
223
195 PyErr = ExecuteReplyError
224 PyErr = ExecuteReplyError
196
225
197
226
198 class Stream(Reference):
227 class Stream(Reference):
199 name = Enum((u'stdout', u'stderr'))
228 name = Enum((u'stdout', u'stderr'))
200 data = Unicode()
229 data = Unicode()
201
230
202
231
203 mime_pat = re.compile(r'\w+/\w+')
232 mime_pat = re.compile(r'\w+/\w+')
204
233
205 class DisplayData(Reference):
234 class DisplayData(Reference):
206 source = Unicode()
235 source = Unicode()
207 metadata = Dict()
236 metadata = Dict()
208 data = Dict()
237 data = Dict()
209 def _data_changed(self, name, old, new):
238 def _data_changed(self, name, old, new):
210 for k,v in new.iteritems():
239 for k,v in new.iteritems():
211 nt.assert_true(mime_pat.match(k))
240 nt.assert_true(mime_pat.match(k))
212 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
241 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
213
242
214
243
215 class PyOut(Reference):
244 class PyOut(Reference):
216 execution_count = Integer()
245 execution_count = Integer()
217 data = Dict()
246 data = Dict()
218 def _data_changed(self, name, old, new):
247 def _data_changed(self, name, old, new):
219 for k,v in new.iteritems():
248 for k,v in new.iteritems():
220 nt.assert_true(mime_pat.match(k))
249 nt.assert_true(mime_pat.match(k))
221 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
250 nt.assert_true(isinstance(v, basestring), "expected string data, got %r" % v)
222
251
223
252
224 references = {
253 references = {
225 'execute_reply' : ExecuteReply(),
254 'execute_reply' : ExecuteReply(),
226 'object_info_reply' : OInfoReply(),
255 'object_info_reply' : OInfoReply(),
227 'status' : Status(),
256 'status' : Status(),
228 'complete_reply' : CompleteReply(),
257 'complete_reply' : CompleteReply(),
258 'kernel_info_reply': KernelInfoReply(),
229 'pyin' : PyIn(),
259 'pyin' : PyIn(),
230 'pyout' : PyOut(),
260 'pyout' : PyOut(),
231 'pyerr' : PyErr(),
261 'pyerr' : PyErr(),
232 'stream' : Stream(),
262 'stream' : Stream(),
233 'display_data' : DisplayData(),
263 'display_data' : DisplayData(),
234 }
264 }
265 """
266 Specifications of `content` part of the reply messages.
267 """
235
268
236
269
237 def validate_message(msg, msg_type=None, parent=None):
270 def validate_message(msg, msg_type=None, parent=None):
238 """validate a message
271 """validate a message
239
272
240 This is a generator, and must be iterated through to actually
273 This is a generator, and must be iterated through to actually
241 trigger each test.
274 trigger each test.
242
275
243 If msg_type and/or parent are given, the msg_type and/or parent msg_id
276 If msg_type and/or parent are given, the msg_type and/or parent msg_id
244 are compared with the given values.
277 are compared with the given values.
245 """
278 """
246 RMessage().check(msg)
279 RMessage().check(msg)
247 if msg_type:
280 if msg_type:
248 yield nt.assert_equal(msg['msg_type'], msg_type)
281 yield nt.assert_equal(msg['msg_type'], msg_type)
249 if parent:
282 if parent:
250 yield nt.assert_equal(msg['parent_header']['msg_id'], parent)
283 yield nt.assert_equal(msg['parent_header']['msg_id'], parent)
251 content = msg['content']
284 content = msg['content']
252 ref = references[msg['msg_type']]
285 ref = references[msg['msg_type']]
253 for tst in ref.check(content):
286 for tst in ref.check(content):
254 yield tst
287 yield tst
255
288
256
289
257 #-----------------------------------------------------------------------------
290 #-----------------------------------------------------------------------------
258 # Tests
291 # Tests
259 #-----------------------------------------------------------------------------
292 #-----------------------------------------------------------------------------
260
293
261 # Shell channel
294 # Shell channel
262
295
263 @dec.parametric
296 @dec.parametric
264 def test_execute():
297 def test_execute():
265 flush_channels()
298 flush_channels()
266
299
267 shell = KM.shell_channel
300 shell = KM.shell_channel
268 msg_id = shell.execute(code='x=1')
301 msg_id = shell.execute(code='x=1')
269 reply = shell.get_msg(timeout=2)
302 reply = shell.get_msg(timeout=2)
270 for tst in validate_message(reply, 'execute_reply', msg_id):
303 for tst in validate_message(reply, 'execute_reply', msg_id):
271 yield tst
304 yield tst
272
305
273
306
274 @dec.parametric
307 @dec.parametric
275 def test_execute_silent():
308 def test_execute_silent():
276 flush_channels()
309 flush_channels()
277 msg_id, reply = execute(code='x=1', silent=True)
310 msg_id, reply = execute(code='x=1', silent=True)
278
311
279 # flush status=idle
312 # flush status=idle
280 status = KM.sub_channel.get_msg(timeout=2)
313 status = KM.sub_channel.get_msg(timeout=2)
281 for tst in validate_message(status, 'status', msg_id):
314 for tst in validate_message(status, 'status', msg_id):
282 yield tst
315 yield tst
283 nt.assert_equal(status['content']['execution_state'], 'idle')
316 nt.assert_equal(status['content']['execution_state'], 'idle')
284
317
285 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
318 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
286 count = reply['execution_count']
319 count = reply['execution_count']
287
320
288 msg_id, reply = execute(code='x=2', silent=True)
321 msg_id, reply = execute(code='x=2', silent=True)
289
322
290 # flush status=idle
323 # flush status=idle
291 status = KM.sub_channel.get_msg(timeout=2)
324 status = KM.sub_channel.get_msg(timeout=2)
292 for tst in validate_message(status, 'status', msg_id):
325 for tst in validate_message(status, 'status', msg_id):
293 yield tst
326 yield tst
294 yield nt.assert_equal(status['content']['execution_state'], 'idle')
327 yield nt.assert_equal(status['content']['execution_state'], 'idle')
295
328
296 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
329 yield nt.assert_raises(Empty, KM.sub_channel.get_msg, timeout=0.1)
297 count_2 = reply['execution_count']
330 count_2 = reply['execution_count']
298 yield nt.assert_equal(count_2, count)
331 yield nt.assert_equal(count_2, count)
299
332
300
333
301 @dec.parametric
334 @dec.parametric
302 def test_execute_error():
335 def test_execute_error():
303 flush_channels()
336 flush_channels()
304
337
305 msg_id, reply = execute(code='1/0')
338 msg_id, reply = execute(code='1/0')
306 yield nt.assert_equal(reply['status'], 'error')
339 yield nt.assert_equal(reply['status'], 'error')
307 yield nt.assert_equal(reply['ename'], 'ZeroDivisionError')
340 yield nt.assert_equal(reply['ename'], 'ZeroDivisionError')
308
341
309 pyerr = KM.sub_channel.get_msg(timeout=2)
342 pyerr = KM.sub_channel.get_msg(timeout=2)
310 for tst in validate_message(pyerr, 'pyerr', msg_id):
343 for tst in validate_message(pyerr, 'pyerr', msg_id):
311 yield tst
344 yield tst
312
345
313
346
314 def test_execute_inc():
347 def test_execute_inc():
315 """execute request should increment execution_count"""
348 """execute request should increment execution_count"""
316 flush_channels()
349 flush_channels()
317
350
318 msg_id, reply = execute(code='x=1')
351 msg_id, reply = execute(code='x=1')
319 count = reply['execution_count']
352 count = reply['execution_count']
320
353
321 flush_channels()
354 flush_channels()
322
355
323 msg_id, reply = execute(code='x=2')
356 msg_id, reply = execute(code='x=2')
324 count_2 = reply['execution_count']
357 count_2 = reply['execution_count']
325 nt.assert_equal(count_2, count+1)
358 nt.assert_equal(count_2, count+1)
326
359
327
360
328 def test_user_variables():
361 def test_user_variables():
329 flush_channels()
362 flush_channels()
330
363
331 msg_id, reply = execute(code='x=1', user_variables=['x'])
364 msg_id, reply = execute(code='x=1', user_variables=['x'])
332 user_variables = reply['user_variables']
365 user_variables = reply['user_variables']
333 nt.assert_equal(user_variables, {u'x' : u'1'})
366 nt.assert_equal(user_variables, {u'x' : u'1'})
334
367
335
368
336 def test_user_expressions():
369 def test_user_expressions():
337 flush_channels()
370 flush_channels()
338
371
339 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
372 msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1'))
340 user_expressions = reply['user_expressions']
373 user_expressions = reply['user_expressions']
341 nt.assert_equal(user_expressions, {u'foo' : u'2'})
374 nt.assert_equal(user_expressions, {u'foo' : u'2'})
342
375
343
376
344 @dec.parametric
377 @dec.parametric
345 def test_oinfo():
378 def test_oinfo():
346 flush_channels()
379 flush_channels()
347
380
348 shell = KM.shell_channel
381 shell = KM.shell_channel
349
382
350 msg_id = shell.object_info('a')
383 msg_id = shell.object_info('a')
351 reply = shell.get_msg(timeout=2)
384 reply = shell.get_msg(timeout=2)
352 for tst in validate_message(reply, 'object_info_reply', msg_id):
385 for tst in validate_message(reply, 'object_info_reply', msg_id):
353 yield tst
386 yield tst
354
387
355
388
356 @dec.parametric
389 @dec.parametric
357 def test_oinfo_found():
390 def test_oinfo_found():
358 flush_channels()
391 flush_channels()
359
392
360 shell = KM.shell_channel
393 shell = KM.shell_channel
361
394
362 msg_id, reply = execute(code='a=5')
395 msg_id, reply = execute(code='a=5')
363
396
364 msg_id = shell.object_info('a')
397 msg_id = shell.object_info('a')
365 reply = shell.get_msg(timeout=2)
398 reply = shell.get_msg(timeout=2)
366 for tst in validate_message(reply, 'object_info_reply', msg_id):
399 for tst in validate_message(reply, 'object_info_reply', msg_id):
367 yield tst
400 yield tst
368 content = reply['content']
401 content = reply['content']
369 yield nt.assert_true(content['found'])
402 yield nt.assert_true(content['found'])
370 argspec = content['argspec']
403 argspec = content['argspec']
371 yield nt.assert_true(argspec is None, "didn't expect argspec dict, got %r" % argspec)
404 yield nt.assert_true(argspec is None, "didn't expect argspec dict, got %r" % argspec)
372
405
373
406
374 @dec.parametric
407 @dec.parametric
375 def test_oinfo_detail():
408 def test_oinfo_detail():
376 flush_channels()
409 flush_channels()
377
410
378 shell = KM.shell_channel
411 shell = KM.shell_channel
379
412
380 msg_id, reply = execute(code='ip=get_ipython()')
413 msg_id, reply = execute(code='ip=get_ipython()')
381
414
382 msg_id = shell.object_info('ip.object_inspect', detail_level=2)
415 msg_id = shell.object_info('ip.object_inspect', detail_level=2)
383 reply = shell.get_msg(timeout=2)
416 reply = shell.get_msg(timeout=2)
384 for tst in validate_message(reply, 'object_info_reply', msg_id):
417 for tst in validate_message(reply, 'object_info_reply', msg_id):
385 yield tst
418 yield tst
386 content = reply['content']
419 content = reply['content']
387 yield nt.assert_true(content['found'])
420 yield nt.assert_true(content['found'])
388 argspec = content['argspec']
421 argspec = content['argspec']
389 yield nt.assert_true(isinstance(argspec, dict), "expected non-empty argspec dict, got %r" % argspec)
422 yield nt.assert_true(isinstance(argspec, dict), "expected non-empty argspec dict, got %r" % argspec)
390 yield nt.assert_equal(argspec['defaults'], [0])
423 yield nt.assert_equal(argspec['defaults'], [0])
391
424
392
425
393 @dec.parametric
426 @dec.parametric
394 def test_oinfo_not_found():
427 def test_oinfo_not_found():
395 flush_channels()
428 flush_channels()
396
429
397 shell = KM.shell_channel
430 shell = KM.shell_channel
398
431
399 msg_id = shell.object_info('dne')
432 msg_id = shell.object_info('dne')
400 reply = shell.get_msg(timeout=2)
433 reply = shell.get_msg(timeout=2)
401 for tst in validate_message(reply, 'object_info_reply', msg_id):
434 for tst in validate_message(reply, 'object_info_reply', msg_id):
402 yield tst
435 yield tst
403 content = reply['content']
436 content = reply['content']
404 yield nt.assert_false(content['found'])
437 yield nt.assert_false(content['found'])
405
438
406
439
407 @dec.parametric
440 @dec.parametric
408 def test_complete():
441 def test_complete():
409 flush_channels()
442 flush_channels()
410
443
411 shell = KM.shell_channel
444 shell = KM.shell_channel
412
445
413 msg_id, reply = execute(code="alpha = albert = 5")
446 msg_id, reply = execute(code="alpha = albert = 5")
414
447
415 msg_id = shell.complete('al', 'al', 2)
448 msg_id = shell.complete('al', 'al', 2)
416 reply = shell.get_msg(timeout=2)
449 reply = shell.get_msg(timeout=2)
417 for tst in validate_message(reply, 'complete_reply', msg_id):
450 for tst in validate_message(reply, 'complete_reply', msg_id):
418 yield tst
451 yield tst
419 matches = reply['content']['matches']
452 matches = reply['content']['matches']
420 for name in ('alpha', 'albert'):
453 for name in ('alpha', 'albert'):
421 yield nt.assert_true(name in matches, "Missing match: %r" % name)
454 yield nt.assert_true(name in matches, "Missing match: %r" % name)
422
455
423
456
457 @dec.parametric
458 def test_kernel_info_request():
459 flush_channels()
460
461 shell = KM.shell_channel
462
463 msg_id = shell.kernel_info()
464 reply = shell.get_msg(timeout=2)
465 for tst in validate_message(reply, 'kernel_info_reply', msg_id):
466 yield tst
467
468
424 # IOPub channel
469 # IOPub channel
425
470
426
471
427 @dec.parametric
472 @dec.parametric
428 def test_stream():
473 def test_stream():
429 flush_channels()
474 flush_channels()
430
475
431 msg_id, reply = execute("print('hi')")
476 msg_id, reply = execute("print('hi')")
432
477
433 stdout = KM.sub_channel.get_msg(timeout=2)
478 stdout = KM.sub_channel.get_msg(timeout=2)
434 for tst in validate_message(stdout, 'stream', msg_id):
479 for tst in validate_message(stdout, 'stream', msg_id):
435 yield tst
480 yield tst
436 content = stdout['content']
481 content = stdout['content']
437 yield nt.assert_equal(content['name'], u'stdout')
482 yield nt.assert_equal(content['name'], u'stdout')
438 yield nt.assert_equal(content['data'], u'hi\n')
483 yield nt.assert_equal(content['data'], u'hi\n')
439
484
440
485
441 @dec.parametric
486 @dec.parametric
442 def test_display_data():
487 def test_display_data():
443 flush_channels()
488 flush_channels()
444
489
445 msg_id, reply = execute("from IPython.core.display import display; display(1)")
490 msg_id, reply = execute("from IPython.core.display import display; display(1)")
446
491
447 display = KM.sub_channel.get_msg(timeout=2)
492 display = KM.sub_channel.get_msg(timeout=2)
448 for tst in validate_message(display, 'display_data', parent=msg_id):
493 for tst in validate_message(display, 'display_data', parent=msg_id):
449 yield tst
494 yield tst
450 data = display['content']['data']
495 data = display['content']['data']
451 yield nt.assert_equal(data['text/plain'], u'1')
496 yield nt.assert_equal(data['text/plain'], u'1')
452
497
@@ -1,1002 +1,1043 b''
1 .. _messaging:
1 .. _messaging:
2
2
3 ======================
3 ======================
4 Messaging in IPython
4 Messaging in IPython
5 ======================
5 ======================
6
6
7
7
8 Introduction
8 Introduction
9 ============
9 ============
10
10
11 This document explains the basic communications design and messaging
11 This document explains the basic communications design and messaging
12 specification for how the various IPython objects interact over a network
12 specification for how the various IPython objects interact over a network
13 transport. The current implementation uses the ZeroMQ_ library for messaging
13 transport. The current implementation uses the ZeroMQ_ library for messaging
14 within and between hosts.
14 within and between hosts.
15
15
16 .. Note::
16 .. Note::
17
17
18 This document should be considered the authoritative description of the
18 This document should be considered the authoritative description of the
19 IPython messaging protocol, and all developers are strongly encouraged to
19 IPython messaging protocol, and all developers are strongly encouraged to
20 keep it updated as the implementation evolves, so that we have a single
20 keep it updated as the implementation evolves, so that we have a single
21 common reference for all protocol details.
21 common reference for all protocol details.
22
22
23 The basic design is explained in the following diagram:
23 The basic design is explained in the following diagram:
24
24
25 .. image:: figs/frontend-kernel.png
25 .. image:: figs/frontend-kernel.png
26 :width: 450px
26 :width: 450px
27 :alt: IPython kernel/frontend messaging architecture.
27 :alt: IPython kernel/frontend messaging architecture.
28 :align: center
28 :align: center
29 :target: ../_images/frontend-kernel.png
29 :target: ../_images/frontend-kernel.png
30
30
31 A single kernel can be simultaneously connected to one or more frontends. The
31 A single kernel can be simultaneously connected to one or more frontends. The
32 kernel has three sockets that serve the following functions:
32 kernel has three sockets that serve the following functions:
33
33
34 1. stdin: this ROUTER socket is connected to all frontends, and it allows
34 1. stdin: this ROUTER socket is connected to all frontends, and it allows
35 the kernel to request input from the active frontend when :func:`raw_input` is called.
35 the kernel to request input from the active frontend when :func:`raw_input` is called.
36 The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard'
36 The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard'
37 for the kernel while this communication is happening (illustrated in the
37 for the kernel while this communication is happening (illustrated in the
38 figure by the black outline around the central keyboard). In practice,
38 figure by the black outline around the central keyboard). In practice,
39 frontends may display such kernel requests using a special input widget or
39 frontends may display such kernel requests using a special input widget or
40 otherwise indicating that the user is to type input for the kernel instead
40 otherwise indicating that the user is to type input for the kernel instead
41 of normal commands in the frontend.
41 of normal commands in the frontend.
42
42
43 2. Shell: this single ROUTER socket allows multiple incoming connections from
43 2. Shell: this single ROUTER socket allows multiple incoming connections from
44 frontends, and this is the socket where requests for code execution, object
44 frontends, and this is the socket where requests for code execution, object
45 information, prompts, etc. are made to the kernel by any frontend. The
45 information, prompts, etc. are made to the kernel by any frontend. The
46 communication on this socket is a sequence of request/reply actions from
46 communication on this socket is a sequence of request/reply actions from
47 each frontend and the kernel.
47 each frontend and the kernel.
48
48
49 3. IOPub: this socket is the 'broadcast channel' where the kernel publishes all
49 3. IOPub: this socket is the 'broadcast channel' where the kernel publishes all
50 side effects (stdout, stderr, etc.) as well as the requests coming from any
50 side effects (stdout, stderr, etc.) as well as the requests coming from any
51 client over the shell socket and its own requests on the stdin socket. There
51 client over the shell socket and its own requests on the stdin socket. There
52 are a number of actions in Python which generate side effects: :func:`print`
52 are a number of actions in Python which generate side effects: :func:`print`
53 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
53 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
54 a multi-client scenario, we want all frontends to be able to know what each
54 a multi-client scenario, we want all frontends to be able to know what each
55 other has sent to the kernel (this can be useful in collaborative scenarios,
55 other has sent to the kernel (this can be useful in collaborative scenarios,
56 for example). This socket allows both side effects and the information
56 for example). This socket allows both side effects and the information
57 about communications taking place with one client over the shell channel
57 about communications taking place with one client over the shell channel
58 to be made available to all clients in a uniform manner.
58 to be made available to all clients in a uniform manner.
59
59
60 All messages are tagged with enough information (details below) for clients
60 All messages are tagged with enough information (details below) for clients
61 to know which messages come from their own interaction with the kernel and
61 to know which messages come from their own interaction with the kernel and
62 which ones are from other clients, so they can display each type
62 which ones are from other clients, so they can display each type
63 appropriately.
63 appropriately.
64
64
65 The actual format of the messages allowed on each of these channels is
65 The actual format of the messages allowed on each of these channels is
66 specified below. Messages are dicts of dicts with string keys and values that
66 specified below. Messages are dicts of dicts with string keys and values that
67 are reasonably representable in JSON. Our current implementation uses JSON
67 are reasonably representable in JSON. Our current implementation uses JSON
68 explicitly as its message format, but this shouldn't be considered a permanent
68 explicitly as its message format, but this shouldn't be considered a permanent
69 feature. As we've discovered that JSON has non-trivial performance issues due
69 feature. As we've discovered that JSON has non-trivial performance issues due
70 to excessive copying, we may in the future move to a pure pickle-based raw
70 to excessive copying, we may in the future move to a pure pickle-based raw
71 message format. However, it should be possible to easily convert from the raw
71 message format. However, it should be possible to easily convert from the raw
72 objects to JSON, since we may have non-python clients (e.g. a web frontend).
72 objects to JSON, since we may have non-python clients (e.g. a web frontend).
73 As long as it's easy to make a JSON version of the objects that is a faithful
73 As long as it's easy to make a JSON version of the objects that is a faithful
74 representation of all the data, we can communicate with such clients.
74 representation of all the data, we can communicate with such clients.
75
75
76 .. Note::
76 .. Note::
77
77
78 Not all of these have yet been fully fleshed out, but the key ones are, see
78 Not all of these have yet been fully fleshed out, but the key ones are, see
79 kernel and frontend files for actual implementation details.
79 kernel and frontend files for actual implementation details.
80
80
81 General Message Format
81 General Message Format
82 ======================
82 ======================
83
83
84 A message is defined by the following four-dictionary structure::
84 A message is defined by the following four-dictionary structure::
85
85
86 {
86 {
87 # The message header contains a pair of unique identifiers for the
87 # The message header contains a pair of unique identifiers for the
88 # originating session and the actual message id, in addition to the
88 # originating session and the actual message id, in addition to the
89 # username for the process that generated the message. This is useful in
89 # username for the process that generated the message. This is useful in
90 # collaborative settings where multiple users may be interacting with the
90 # collaborative settings where multiple users may be interacting with the
91 # same kernel simultaneously, so that frontends can label the various
91 # same kernel simultaneously, so that frontends can label the various
92 # messages in a meaningful way.
92 # messages in a meaningful way.
93 'header' : {
93 'header' : {
94 'msg_id' : uuid,
94 'msg_id' : uuid,
95 'username' : str,
95 'username' : str,
96 'session' : uuid
96 'session' : uuid
97 # All recognized message type strings are listed below.
97 # All recognized message type strings are listed below.
98 'msg_type' : str,
98 'msg_type' : str,
99 },
99 },
100
100
101 # In a chain of messages, the header from the parent is copied so that
101 # In a chain of messages, the header from the parent is copied so that
102 # clients can track where messages come from.
102 # clients can track where messages come from.
103 'parent_header' : dict,
103 'parent_header' : dict,
104
104
105 # The actual content of the message must be a dict, whose structure
105 # The actual content of the message must be a dict, whose structure
106 # depends on the message type.
106 # depends on the message type.
107 'content' : dict,
107 'content' : dict,
108
108
109 # Any metadata associated with the message.
109 # Any metadata associated with the message.
110 'metadata' : dict,
110 'metadata' : dict,
111 }
111 }
112
112
113
113
114 Python functional API
114 Python functional API
115 =====================
115 =====================
116
116
117 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
117 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
118 should develop, at a few key points, functional forms of all the requests that
118 should develop, at a few key points, functional forms of all the requests that
119 take arguments in this manner and automatically construct the necessary dict
119 take arguments in this manner and automatically construct the necessary dict
120 for sending.
120 for sending.
121
121
122 In addition, the Python implementation of the message specification extends
122 In addition, the Python implementation of the message specification extends
123 messages upon deserialization to the following form for convenience::
123 messages upon deserialization to the following form for convenience::
124
124
125 {
125 {
126 'header' : dict,
126 'header' : dict,
127 # The msg's unique identifier and type are always stored in the header,
127 # The msg's unique identifier and type are always stored in the header,
128 # but the Python implementation copies them to the top level.
128 # but the Python implementation copies them to the top level.
129 'msg_id' : uuid,
129 'msg_id' : uuid,
130 'msg_type' : str,
130 'msg_type' : str,
131 'parent_header' : dict,
131 'parent_header' : dict,
132 'content' : dict,
132 'content' : dict,
133 'metadata' : dict,
133 'metadata' : dict,
134 }
134 }
135
135
136 All messages sent to or received by any IPython process should have this
136 All messages sent to or received by any IPython process should have this
137 extended structure.
137 extended structure.
138
138
139
139
140 Messages on the shell ROUTER/DEALER sockets
140 Messages on the shell ROUTER/DEALER sockets
141 ===========================================
141 ===========================================
142
142
143 .. _execute:
143 .. _execute:
144
144
145 Execute
145 Execute
146 -------
146 -------
147
147
148 This message type is used by frontends to ask the kernel to execute code on
148 This message type is used by frontends to ask the kernel to execute code on
149 behalf of the user, in a namespace reserved to the user's variables (and thus
149 behalf of the user, in a namespace reserved to the user's variables (and thus
150 separate from the kernel's own internal code and variables).
150 separate from the kernel's own internal code and variables).
151
151
152 Message type: ``execute_request``::
152 Message type: ``execute_request``::
153
153
154 content = {
154 content = {
155 # Source code to be executed by the kernel, one or more lines.
155 # Source code to be executed by the kernel, one or more lines.
156 'code' : str,
156 'code' : str,
157
157
158 # A boolean flag which, if True, signals the kernel to execute
158 # A boolean flag which, if True, signals the kernel to execute
159 # this code as quietly as possible. This means that the kernel
159 # this code as quietly as possible. This means that the kernel
160 # will compile the code with 'exec' instead of 'single' (so
160 # will compile the code with 'exec' instead of 'single' (so
161 # sys.displayhook will not fire), forces store_history to be False,
161 # sys.displayhook will not fire), forces store_history to be False,
162 # and will *not*:
162 # and will *not*:
163 # - broadcast exceptions on the PUB socket
163 # - broadcast exceptions on the PUB socket
164 # - do any logging
164 # - do any logging
165 #
165 #
166 # The default is False.
166 # The default is False.
167 'silent' : bool,
167 'silent' : bool,
168
168
169 # A boolean flag which, if True, signals the kernel to populate history
169 # A boolean flag which, if True, signals the kernel to populate history
170 # The default is True if silent is False. If silent is True, store_history
170 # The default is True if silent is False. If silent is True, store_history
171 # is forced to be False.
171 # is forced to be False.
172 'store_history' : bool,
172 'store_history' : bool,
173
173
174 # A list of variable names from the user's namespace to be retrieved. What
174 # A list of variable names from the user's namespace to be retrieved. What
175 # returns is a JSON string of the variable's repr(), not a python object.
175 # returns is a JSON string of the variable's repr(), not a python object.
176 'user_variables' : list,
176 'user_variables' : list,
177
177
178 # Similarly, a dict mapping names to expressions to be evaluated in the
178 # Similarly, a dict mapping names to expressions to be evaluated in the
179 # user's dict.
179 # user's dict.
180 'user_expressions' : dict,
180 'user_expressions' : dict,
181
181
182 # Some frontends (e.g. the Notebook) do not support stdin requests. If
182 # Some frontends (e.g. the Notebook) do not support stdin requests. If
183 # raw_input is called from code executed from such a frontend, a
183 # raw_input is called from code executed from such a frontend, a
184 # StdinNotImplementedError will be raised.
184 # StdinNotImplementedError will be raised.
185 'allow_stdin' : True,
185 'allow_stdin' : True,
186
186
187 }
187 }
188
188
189 The ``code`` field contains a single string (possibly multiline). The kernel
189 The ``code`` field contains a single string (possibly multiline). The kernel
190 is responsible for splitting this into one or more independent execution blocks
190 is responsible for splitting this into one or more independent execution blocks
191 and deciding whether to compile these in 'single' or 'exec' mode (see below for
191 and deciding whether to compile these in 'single' or 'exec' mode (see below for
192 detailed execution semantics).
192 detailed execution semantics).
193
193
194 The ``user_`` fields deserve a detailed explanation. In the past, IPython had
194 The ``user_`` fields deserve a detailed explanation. In the past, IPython had
195 the notion of a prompt string that allowed arbitrary code to be evaluated, and
195 the notion of a prompt string that allowed arbitrary code to be evaluated, and
196 this was put to good use by many in creating prompts that displayed system
196 this was put to good use by many in creating prompts that displayed system
197 status, path information, and even more esoteric uses like remote instrument
197 status, path information, and even more esoteric uses like remote instrument
198 status aqcuired over the network. But now that IPython has a clean separation
198 status aqcuired over the network. But now that IPython has a clean separation
199 between the kernel and the clients, the kernel has no prompt knowledge; prompts
199 between the kernel and the clients, the kernel has no prompt knowledge; prompts
200 are a frontend-side feature, and it should be even possible for different
200 are a frontend-side feature, and it should be even possible for different
201 frontends to display different prompts while interacting with the same kernel.
201 frontends to display different prompts while interacting with the same kernel.
202
202
203 The kernel now provides the ability to retrieve data from the user's namespace
203 The kernel now provides the ability to retrieve data from the user's namespace
204 after the execution of the main ``code``, thanks to two fields in the
204 after the execution of the main ``code``, thanks to two fields in the
205 ``execute_request`` message:
205 ``execute_request`` message:
206
206
207 - ``user_variables``: If only variables from the user's namespace are needed, a
207 - ``user_variables``: If only variables from the user's namespace are needed, a
208 list of variable names can be passed and a dict with these names as keys and
208 list of variable names can be passed and a dict with these names as keys and
209 their :func:`repr()` as values will be returned.
209 their :func:`repr()` as values will be returned.
210
210
211 - ``user_expressions``: For more complex expressions that require function
211 - ``user_expressions``: For more complex expressions that require function
212 evaluations, a dict can be provided with string keys and arbitrary python
212 evaluations, a dict can be provided with string keys and arbitrary python
213 expressions as values. The return message will contain also a dict with the
213 expressions as values. The return message will contain also a dict with the
214 same keys and the :func:`repr()` of the evaluated expressions as value.
214 same keys and the :func:`repr()` of the evaluated expressions as value.
215
215
216 With this information, frontends can display any status information they wish
216 With this information, frontends can display any status information they wish
217 in the form that best suits each frontend (a status line, a popup, inline for a
217 in the form that best suits each frontend (a status line, a popup, inline for a
218 terminal, etc).
218 terminal, etc).
219
219
220 .. Note::
220 .. Note::
221
221
222 In order to obtain the current execution counter for the purposes of
222 In order to obtain the current execution counter for the purposes of
223 displaying input prompts, frontends simply make an execution request with an
223 displaying input prompts, frontends simply make an execution request with an
224 empty code string and ``silent=True``.
224 empty code string and ``silent=True``.
225
225
226 Execution semantics
226 Execution semantics
227 ~~~~~~~~~~~~~~~~~~~
227 ~~~~~~~~~~~~~~~~~~~
228
228
229 When the silent flag is false, the execution of use code consists of the
229 When the silent flag is false, the execution of use code consists of the
230 following phases (in silent mode, only the ``code`` field is executed):
230 following phases (in silent mode, only the ``code`` field is executed):
231
231
232 1. Run the ``pre_runcode_hook``.
232 1. Run the ``pre_runcode_hook``.
233
233
234 2. Execute the ``code`` field, see below for details.
234 2. Execute the ``code`` field, see below for details.
235
235
236 3. If #2 succeeds, compute ``user_variables`` and ``user_expressions`` are
236 3. If #2 succeeds, compute ``user_variables`` and ``user_expressions`` are
237 computed. This ensures that any error in the latter don't harm the main
237 computed. This ensures that any error in the latter don't harm the main
238 code execution.
238 code execution.
239
239
240 4. Call any method registered with :meth:`register_post_execute`.
240 4. Call any method registered with :meth:`register_post_execute`.
241
241
242 .. warning::
242 .. warning::
243
243
244 The API for running code before/after the main code block is likely to
244 The API for running code before/after the main code block is likely to
245 change soon. Both the ``pre_runcode_hook`` and the
245 change soon. Both the ``pre_runcode_hook`` and the
246 :meth:`register_post_execute` are susceptible to modification, as we find a
246 :meth:`register_post_execute` are susceptible to modification, as we find a
247 consistent model for both.
247 consistent model for both.
248
248
249 To understand how the ``code`` field is executed, one must know that Python
249 To understand how the ``code`` field is executed, one must know that Python
250 code can be compiled in one of three modes (controlled by the ``mode`` argument
250 code can be compiled in one of three modes (controlled by the ``mode`` argument
251 to the :func:`compile` builtin):
251 to the :func:`compile` builtin):
252
252
253 *single*
253 *single*
254 Valid for a single interactive statement (though the source can contain
254 Valid for a single interactive statement (though the source can contain
255 multiple lines, such as a for loop). When compiled in this mode, the
255 multiple lines, such as a for loop). When compiled in this mode, the
256 generated bytecode contains special instructions that trigger the calling of
256 generated bytecode contains special instructions that trigger the calling of
257 :func:`sys.displayhook` for any expression in the block that returns a value.
257 :func:`sys.displayhook` for any expression in the block that returns a value.
258 This means that a single statement can actually produce multiple calls to
258 This means that a single statement can actually produce multiple calls to
259 :func:`sys.displayhook`, if for example it contains a loop where each
259 :func:`sys.displayhook`, if for example it contains a loop where each
260 iteration computes an unassigned expression would generate 10 calls::
260 iteration computes an unassigned expression would generate 10 calls::
261
261
262 for i in range(10):
262 for i in range(10):
263 i**2
263 i**2
264
264
265 *exec*
265 *exec*
266 An arbitrary amount of source code, this is how modules are compiled.
266 An arbitrary amount of source code, this is how modules are compiled.
267 :func:`sys.displayhook` is *never* implicitly called.
267 :func:`sys.displayhook` is *never* implicitly called.
268
268
269 *eval*
269 *eval*
270 A single expression that returns a value. :func:`sys.displayhook` is *never*
270 A single expression that returns a value. :func:`sys.displayhook` is *never*
271 implicitly called.
271 implicitly called.
272
272
273
273
274 The ``code`` field is split into individual blocks each of which is valid for
274 The ``code`` field is split into individual blocks each of which is valid for
275 execution in 'single' mode, and then:
275 execution in 'single' mode, and then:
276
276
277 - If there is only a single block: it is executed in 'single' mode.
277 - If there is only a single block: it is executed in 'single' mode.
278
278
279 - If there is more than one block:
279 - If there is more than one block:
280
280
281 * if the last one is a single line long, run all but the last in 'exec' mode
281 * if the last one is a single line long, run all but the last in 'exec' mode
282 and the very last one in 'single' mode. This makes it easy to type simple
282 and the very last one in 'single' mode. This makes it easy to type simple
283 expressions at the end to see computed values.
283 expressions at the end to see computed values.
284
284
285 * if the last one is no more than two lines long, run all but the last in
285 * if the last one is no more than two lines long, run all but the last in
286 'exec' mode and the very last one in 'single' mode. This makes it easy to
286 'exec' mode and the very last one in 'single' mode. This makes it easy to
287 type simple expressions at the end to see computed values. - otherwise
287 type simple expressions at the end to see computed values. - otherwise
288 (last one is also multiline), run all in 'exec' mode
288 (last one is also multiline), run all in 'exec' mode
289
289
290 * otherwise (last one is also multiline), run all in 'exec' mode as a single
290 * otherwise (last one is also multiline), run all in 'exec' mode as a single
291 unit.
291 unit.
292
292
293 Any error in retrieving the ``user_variables`` or evaluating the
293 Any error in retrieving the ``user_variables`` or evaluating the
294 ``user_expressions`` will result in a simple error message in the return fields
294 ``user_expressions`` will result in a simple error message in the return fields
295 of the form::
295 of the form::
296
296
297 [ERROR] ExceptionType: Exception message
297 [ERROR] ExceptionType: Exception message
298
298
299 The user can simply send the same variable name or expression for evaluation to
299 The user can simply send the same variable name or expression for evaluation to
300 see a regular traceback.
300 see a regular traceback.
301
301
302 Errors in any registered post_execute functions are also reported similarly,
302 Errors in any registered post_execute functions are also reported similarly,
303 and the failing function is removed from the post_execution set so that it does
303 and the failing function is removed from the post_execution set so that it does
304 not continue triggering failures.
304 not continue triggering failures.
305
305
306 Upon completion of the execution request, the kernel *always* sends a reply,
306 Upon completion of the execution request, the kernel *always* sends a reply,
307 with a status code indicating what happened and additional data depending on
307 with a status code indicating what happened and additional data depending on
308 the outcome. See :ref:`below <execution_results>` for the possible return
308 the outcome. See :ref:`below <execution_results>` for the possible return
309 codes and associated data.
309 codes and associated data.
310
310
311
311
312 Execution counter (old prompt number)
312 Execution counter (old prompt number)
313 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
313 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
314
314
315 The kernel has a single, monotonically increasing counter of all execution
315 The kernel has a single, monotonically increasing counter of all execution
316 requests that are made with ``store_history=True``. This counter is used to populate
316 requests that are made with ``store_history=True``. This counter is used to populate
317 the ``In[n]``, ``Out[n]`` and ``_n`` variables, so clients will likely want to
317 the ``In[n]``, ``Out[n]`` and ``_n`` variables, so clients will likely want to
318 display it in some form to the user, which will typically (but not necessarily)
318 display it in some form to the user, which will typically (but not necessarily)
319 be done in the prompts. The value of this counter will be returned as the
319 be done in the prompts. The value of this counter will be returned as the
320 ``execution_count`` field of all ``execute_reply`` messages.
320 ``execution_count`` field of all ``execute_reply`` messages.
321
321
322 .. _execution_results:
322 .. _execution_results:
323
323
324 Execution results
324 Execution results
325 ~~~~~~~~~~~~~~~~~
325 ~~~~~~~~~~~~~~~~~
326
326
327 Message type: ``execute_reply``::
327 Message type: ``execute_reply``::
328
328
329 content = {
329 content = {
330 # One of: 'ok' OR 'error' OR 'abort'
330 # One of: 'ok' OR 'error' OR 'abort'
331 'status' : str,
331 'status' : str,
332
332
333 # The global kernel counter that increases by one with each request that
333 # The global kernel counter that increases by one with each request that
334 # stores history. This will typically be used by clients to display
334 # stores history. This will typically be used by clients to display
335 # prompt numbers to the user. If the request did not store history, this will
335 # prompt numbers to the user. If the request did not store history, this will
336 # be the current value of the counter in the kernel.
336 # be the current value of the counter in the kernel.
337 'execution_count' : int,
337 'execution_count' : int,
338 }
338 }
339
339
340 When status is 'ok', the following extra fields are present::
340 When status is 'ok', the following extra fields are present::
341
341
342 {
342 {
343 # 'payload' will be a list of payload dicts.
343 # 'payload' will be a list of payload dicts.
344 # Each execution payload is a dict with string keys that may have been
344 # Each execution payload is a dict with string keys that may have been
345 # produced by the code being executed. It is retrieved by the kernel at
345 # produced by the code being executed. It is retrieved by the kernel at
346 # the end of the execution and sent back to the front end, which can take
346 # the end of the execution and sent back to the front end, which can take
347 # action on it as needed. See main text for further details.
347 # action on it as needed. See main text for further details.
348 'payload' : list(dict),
348 'payload' : list(dict),
349
349
350 # Results for the user_variables and user_expressions.
350 # Results for the user_variables and user_expressions.
351 'user_variables' : dict,
351 'user_variables' : dict,
352 'user_expressions' : dict,
352 'user_expressions' : dict,
353 }
353 }
354
354
355 .. admonition:: Execution payloads
355 .. admonition:: Execution payloads
356
356
357 The notion of an 'execution payload' is different from a return value of a
357 The notion of an 'execution payload' is different from a return value of a
358 given set of code, which normally is just displayed on the pyout stream
358 given set of code, which normally is just displayed on the pyout stream
359 through the PUB socket. The idea of a payload is to allow special types of
359 through the PUB socket. The idea of a payload is to allow special types of
360 code, typically magics, to populate a data container in the IPython kernel
360 code, typically magics, to populate a data container in the IPython kernel
361 that will be shipped back to the caller via this channel. The kernel
361 that will be shipped back to the caller via this channel. The kernel
362 has an API for this in the PayloadManager::
362 has an API for this in the PayloadManager::
363
363
364 ip.payload_manager.write_payload(payload_dict)
364 ip.payload_manager.write_payload(payload_dict)
365
365
366 which appends a dictionary to the list of payloads.
366 which appends a dictionary to the list of payloads.
367
367
368
368
369 When status is 'error', the following extra fields are present::
369 When status is 'error', the following extra fields are present::
370
370
371 {
371 {
372 'ename' : str, # Exception name, as a string
372 'ename' : str, # Exception name, as a string
373 'evalue' : str, # Exception value, as a string
373 'evalue' : str, # Exception value, as a string
374
374
375 # The traceback will contain a list of frames, represented each as a
375 # The traceback will contain a list of frames, represented each as a
376 # string. For now we'll stick to the existing design of ultraTB, which
376 # string. For now we'll stick to the existing design of ultraTB, which
377 # controls exception level of detail statefully. But eventually we'll
377 # controls exception level of detail statefully. But eventually we'll
378 # want to grow into a model where more information is collected and
378 # want to grow into a model where more information is collected and
379 # packed into the traceback object, with clients deciding how little or
379 # packed into the traceback object, with clients deciding how little or
380 # how much of it to unpack. But for now, let's start with a simple list
380 # how much of it to unpack. But for now, let's start with a simple list
381 # of strings, since that requires only minimal changes to ultratb as
381 # of strings, since that requires only minimal changes to ultratb as
382 # written.
382 # written.
383 'traceback' : list,
383 'traceback' : list,
384 }
384 }
385
385
386
386
387 When status is 'abort', there are for now no additional data fields. This
387 When status is 'abort', there are for now no additional data fields. This
388 happens when the kernel was interrupted by a signal.
388 happens when the kernel was interrupted by a signal.
389
389
390 Kernel attribute access
390 Kernel attribute access
391 -----------------------
391 -----------------------
392
392
393 .. warning::
393 .. warning::
394
394
395 This part of the messaging spec is not actually implemented in the kernel
395 This part of the messaging spec is not actually implemented in the kernel
396 yet.
396 yet.
397
397
398 While this protocol does not specify full RPC access to arbitrary methods of
398 While this protocol does not specify full RPC access to arbitrary methods of
399 the kernel object, the kernel does allow read (and in some cases write) access
399 the kernel object, the kernel does allow read (and in some cases write) access
400 to certain attributes.
400 to certain attributes.
401
401
402 The policy for which attributes can be read is: any attribute of the kernel, or
402 The policy for which attributes can be read is: any attribute of the kernel, or
403 its sub-objects, that belongs to a :class:`Configurable` object and has been
403 its sub-objects, that belongs to a :class:`Configurable` object and has been
404 declared at the class-level with Traits validation, is in principle accessible
404 declared at the class-level with Traits validation, is in principle accessible
405 as long as its name does not begin with a leading underscore. The attribute
405 as long as its name does not begin with a leading underscore. The attribute
406 itself will have metadata indicating whether it allows remote read and/or write
406 itself will have metadata indicating whether it allows remote read and/or write
407 access. The message spec follows for attribute read and write requests.
407 access. The message spec follows for attribute read and write requests.
408
408
409 Message type: ``getattr_request``::
409 Message type: ``getattr_request``::
410
410
411 content = {
411 content = {
412 # The (possibly dotted) name of the attribute
412 # The (possibly dotted) name of the attribute
413 'name' : str,
413 'name' : str,
414 }
414 }
415
415
416 When a ``getattr_request`` fails, there are two possible error types:
416 When a ``getattr_request`` fails, there are two possible error types:
417
417
418 - AttributeError: this type of error was raised when trying to access the
418 - AttributeError: this type of error was raised when trying to access the
419 given name by the kernel itself. This means that the attribute likely
419 given name by the kernel itself. This means that the attribute likely
420 doesn't exist.
420 doesn't exist.
421
421
422 - AccessError: the attribute exists but its value is not readable remotely.
422 - AccessError: the attribute exists but its value is not readable remotely.
423
423
424
424
425 Message type: ``getattr_reply``::
425 Message type: ``getattr_reply``::
426
426
427 content = {
427 content = {
428 # One of ['ok', 'AttributeError', 'AccessError'].
428 # One of ['ok', 'AttributeError', 'AccessError'].
429 'status' : str,
429 'status' : str,
430 # If status is 'ok', a JSON object.
430 # If status is 'ok', a JSON object.
431 'value' : object,
431 'value' : object,
432 }
432 }
433
433
434 Message type: ``setattr_request``::
434 Message type: ``setattr_request``::
435
435
436 content = {
436 content = {
437 # The (possibly dotted) name of the attribute
437 # The (possibly dotted) name of the attribute
438 'name' : str,
438 'name' : str,
439
439
440 # A JSON-encoded object, that will be validated by the Traits
440 # A JSON-encoded object, that will be validated by the Traits
441 # information in the kernel
441 # information in the kernel
442 'value' : object,
442 'value' : object,
443 }
443 }
444
444
445 When a ``setattr_request`` fails, there are also two possible error types with
445 When a ``setattr_request`` fails, there are also two possible error types with
446 similar meanings as those of the ``getattr_request`` case, but for writing.
446 similar meanings as those of the ``getattr_request`` case, but for writing.
447
447
448 Message type: ``setattr_reply``::
448 Message type: ``setattr_reply``::
449
449
450 content = {
450 content = {
451 # One of ['ok', 'AttributeError', 'AccessError'].
451 # One of ['ok', 'AttributeError', 'AccessError'].
452 'status' : str,
452 'status' : str,
453 }
453 }
454
454
455
455
456
456
457 Object information
457 Object information
458 ------------------
458 ------------------
459
459
460 One of IPython's most used capabilities is the introspection of Python objects
460 One of IPython's most used capabilities is the introspection of Python objects
461 in the user's namespace, typically invoked via the ``?`` and ``??`` characters
461 in the user's namespace, typically invoked via the ``?`` and ``??`` characters
462 (which in reality are shorthands for the ``%pinfo`` magic). This is used often
462 (which in reality are shorthands for the ``%pinfo`` magic). This is used often
463 enough that it warrants an explicit message type, especially because frontends
463 enough that it warrants an explicit message type, especially because frontends
464 may want to get object information in response to user keystrokes (like Tab or
464 may want to get object information in response to user keystrokes (like Tab or
465 F1) besides from the user explicitly typing code like ``x??``.
465 F1) besides from the user explicitly typing code like ``x??``.
466
466
467 Message type: ``object_info_request``::
467 Message type: ``object_info_request``::
468
468
469 content = {
469 content = {
470 # The (possibly dotted) name of the object to be searched in all
470 # The (possibly dotted) name of the object to be searched in all
471 # relevant namespaces
471 # relevant namespaces
472 'name' : str,
472 'name' : str,
473
473
474 # The level of detail desired. The default (0) is equivalent to typing
474 # The level of detail desired. The default (0) is equivalent to typing
475 # 'x?' at the prompt, 1 is equivalent to 'x??'.
475 # 'x?' at the prompt, 1 is equivalent to 'x??'.
476 'detail_level' : int,
476 'detail_level' : int,
477 }
477 }
478
478
479 The returned information will be a dictionary with keys very similar to the
479 The returned information will be a dictionary with keys very similar to the
480 field names that IPython prints at the terminal.
480 field names that IPython prints at the terminal.
481
481
482 Message type: ``object_info_reply``::
482 Message type: ``object_info_reply``::
483
483
484 content = {
484 content = {
485 # The name the object was requested under
485 # The name the object was requested under
486 'name' : str,
486 'name' : str,
487
487
488 # Boolean flag indicating whether the named object was found or not. If
488 # Boolean flag indicating whether the named object was found or not. If
489 # it's false, all other fields will be empty.
489 # it's false, all other fields will be empty.
490 'found' : bool,
490 'found' : bool,
491
491
492 # Flags for magics and system aliases
492 # Flags for magics and system aliases
493 'ismagic' : bool,
493 'ismagic' : bool,
494 'isalias' : bool,
494 'isalias' : bool,
495
495
496 # The name of the namespace where the object was found ('builtin',
496 # The name of the namespace where the object was found ('builtin',
497 # 'magics', 'alias', 'interactive', etc.)
497 # 'magics', 'alias', 'interactive', etc.)
498 'namespace' : str,
498 'namespace' : str,
499
499
500 # The type name will be type.__name__ for normal Python objects, but it
500 # The type name will be type.__name__ for normal Python objects, but it
501 # can also be a string like 'Magic function' or 'System alias'
501 # can also be a string like 'Magic function' or 'System alias'
502 'type_name' : str,
502 'type_name' : str,
503
503
504 # The string form of the object, possibly truncated for length if
504 # The string form of the object, possibly truncated for length if
505 # detail_level is 0
505 # detail_level is 0
506 'string_form' : str,
506 'string_form' : str,
507
507
508 # For objects with a __class__ attribute this will be set
508 # For objects with a __class__ attribute this will be set
509 'base_class' : str,
509 'base_class' : str,
510
510
511 # For objects with a __len__ attribute this will be set
511 # For objects with a __len__ attribute this will be set
512 'length' : int,
512 'length' : int,
513
513
514 # If the object is a function, class or method whose file we can find,
514 # If the object is a function, class or method whose file we can find,
515 # we give its full path
515 # we give its full path
516 'file' : str,
516 'file' : str,
517
517
518 # For pure Python callable objects, we can reconstruct the object
518 # For pure Python callable objects, we can reconstruct the object
519 # definition line which provides its call signature. For convenience this
519 # definition line which provides its call signature. For convenience this
520 # is returned as a single 'definition' field, but below the raw parts that
520 # is returned as a single 'definition' field, but below the raw parts that
521 # compose it are also returned as the argspec field.
521 # compose it are also returned as the argspec field.
522 'definition' : str,
522 'definition' : str,
523
523
524 # The individual parts that together form the definition string. Clients
524 # The individual parts that together form the definition string. Clients
525 # with rich display capabilities may use this to provide a richer and more
525 # with rich display capabilities may use this to provide a richer and more
526 # precise representation of the definition line (e.g. by highlighting
526 # precise representation of the definition line (e.g. by highlighting
527 # arguments based on the user's cursor position). For non-callable
527 # arguments based on the user's cursor position). For non-callable
528 # objects, this field is empty.
528 # objects, this field is empty.
529 'argspec' : { # The names of all the arguments
529 'argspec' : { # The names of all the arguments
530 args : list,
530 args : list,
531 # The name of the varargs (*args), if any
531 # The name of the varargs (*args), if any
532 varargs : str,
532 varargs : str,
533 # The name of the varkw (**kw), if any
533 # The name of the varkw (**kw), if any
534 varkw : str,
534 varkw : str,
535 # The values (as strings) of all default arguments. Note
535 # The values (as strings) of all default arguments. Note
536 # that these must be matched *in reverse* with the 'args'
536 # that these must be matched *in reverse* with the 'args'
537 # list above, since the first positional args have no default
537 # list above, since the first positional args have no default
538 # value at all.
538 # value at all.
539 defaults : list,
539 defaults : list,
540 },
540 },
541
541
542 # For instances, provide the constructor signature (the definition of
542 # For instances, provide the constructor signature (the definition of
543 # the __init__ method):
543 # the __init__ method):
544 'init_definition' : str,
544 'init_definition' : str,
545
545
546 # Docstrings: for any object (function, method, module, package) with a
546 # Docstrings: for any object (function, method, module, package) with a
547 # docstring, we show it. But in addition, we may provide additional
547 # docstring, we show it. But in addition, we may provide additional
548 # docstrings. For example, for instances we will show the constructor
548 # docstrings. For example, for instances we will show the constructor
549 # and class docstrings as well, if available.
549 # and class docstrings as well, if available.
550 'docstring' : str,
550 'docstring' : str,
551
551
552 # For instances, provide the constructor and class docstrings
552 # For instances, provide the constructor and class docstrings
553 'init_docstring' : str,
553 'init_docstring' : str,
554 'class_docstring' : str,
554 'class_docstring' : str,
555
555
556 # If it's a callable object whose call method has a separate docstring and
556 # If it's a callable object whose call method has a separate docstring and
557 # definition line:
557 # definition line:
558 'call_def' : str,
558 'call_def' : str,
559 'call_docstring' : str,
559 'call_docstring' : str,
560
560
561 # If detail_level was 1, we also try to find the source code that
561 # If detail_level was 1, we also try to find the source code that
562 # defines the object, if possible. The string 'None' will indicate
562 # defines the object, if possible. The string 'None' will indicate
563 # that no source was found.
563 # that no source was found.
564 'source' : str,
564 'source' : str,
565 }
565 }
566
566
567
567
568 Complete
568 Complete
569 --------
569 --------
570
570
571 Message type: ``complete_request``::
571 Message type: ``complete_request``::
572
572
573 content = {
573 content = {
574 # The text to be completed, such as 'a.is'
574 # The text to be completed, such as 'a.is'
575 'text' : str,
575 'text' : str,
576
576
577 # The full line, such as 'print a.is'. This allows completers to
577 # The full line, such as 'print a.is'. This allows completers to
578 # make decisions that may require information about more than just the
578 # make decisions that may require information about more than just the
579 # current word.
579 # current word.
580 'line' : str,
580 'line' : str,
581
581
582 # The entire block of text where the line is. This may be useful in the
582 # The entire block of text where the line is. This may be useful in the
583 # case of multiline completions where more context may be needed. Note: if
583 # case of multiline completions where more context may be needed. Note: if
584 # in practice this field proves unnecessary, remove it to lighten the
584 # in practice this field proves unnecessary, remove it to lighten the
585 # messages.
585 # messages.
586
586
587 'block' : str,
587 'block' : str,
588
588
589 # The position of the cursor where the user hit 'TAB' on the line.
589 # The position of the cursor where the user hit 'TAB' on the line.
590 'cursor_pos' : int,
590 'cursor_pos' : int,
591 }
591 }
592
592
593 Message type: ``complete_reply``::
593 Message type: ``complete_reply``::
594
594
595 content = {
595 content = {
596 # The list of all matches to the completion request, such as
596 # The list of all matches to the completion request, such as
597 # ['a.isalnum', 'a.isalpha'] for the above example.
597 # ['a.isalnum', 'a.isalpha'] for the above example.
598 'matches' : list
598 'matches' : list
599 }
599 }
600
600
601
601
602 History
602 History
603 -------
603 -------
604
604
605 For clients to explicitly request history from a kernel. The kernel has all
605 For clients to explicitly request history from a kernel. The kernel has all
606 the actual execution history stored in a single location, so clients can
606 the actual execution history stored in a single location, so clients can
607 request it from the kernel when needed.
607 request it from the kernel when needed.
608
608
609 Message type: ``history_request``::
609 Message type: ``history_request``::
610
610
611 content = {
611 content = {
612
612
613 # If True, also return output history in the resulting dict.
613 # If True, also return output history in the resulting dict.
614 'output' : bool,
614 'output' : bool,
615
615
616 # If True, return the raw input history, else the transformed input.
616 # If True, return the raw input history, else the transformed input.
617 'raw' : bool,
617 'raw' : bool,
618
618
619 # So far, this can be 'range', 'tail' or 'search'.
619 # So far, this can be 'range', 'tail' or 'search'.
620 'hist_access_type' : str,
620 'hist_access_type' : str,
621
621
622 # If hist_access_type is 'range', get a range of input cells. session can
622 # If hist_access_type is 'range', get a range of input cells. session can
623 # be a positive session number, or a negative number to count back from
623 # be a positive session number, or a negative number to count back from
624 # the current session.
624 # the current session.
625 'session' : int,
625 'session' : int,
626 # start and stop are line numbers within that session.
626 # start and stop are line numbers within that session.
627 'start' : int,
627 'start' : int,
628 'stop' : int,
628 'stop' : int,
629
629
630 # If hist_access_type is 'tail' or 'search', get the last n cells.
630 # If hist_access_type is 'tail' or 'search', get the last n cells.
631 'n' : int,
631 'n' : int,
632
632
633 # If hist_access_type is 'search', get cells matching the specified glob
633 # If hist_access_type is 'search', get cells matching the specified glob
634 # pattern (with * and ? as wildcards).
634 # pattern (with * and ? as wildcards).
635 'pattern' : str,
635 'pattern' : str,
636
636
637 }
637 }
638
638
639 Message type: ``history_reply``::
639 Message type: ``history_reply``::
640
640
641 content = {
641 content = {
642 # A list of 3 tuples, either:
642 # A list of 3 tuples, either:
643 # (session, line_number, input) or
643 # (session, line_number, input) or
644 # (session, line_number, (input, output)),
644 # (session, line_number, (input, output)),
645 # depending on whether output was False or True, respectively.
645 # depending on whether output was False or True, respectively.
646 'history' : list,
646 'history' : list,
647 }
647 }
648
648
649
649
650 Connect
650 Connect
651 -------
651 -------
652
652
653 When a client connects to the request/reply socket of the kernel, it can issue
653 When a client connects to the request/reply socket of the kernel, it can issue
654 a connect request to get basic information about the kernel, such as the ports
654 a connect request to get basic information about the kernel, such as the ports
655 the other ZeroMQ sockets are listening on. This allows clients to only have
655 the other ZeroMQ sockets are listening on. This allows clients to only have
656 to know about a single port (the shell channel) to connect to a kernel.
656 to know about a single port (the shell channel) to connect to a kernel.
657
657
658 Message type: ``connect_request``::
658 Message type: ``connect_request``::
659
659
660 content = {
660 content = {
661 }
661 }
662
662
663 Message type: ``connect_reply``::
663 Message type: ``connect_reply``::
664
664
665 content = {
665 content = {
666 'shell_port' : int # The port the shell ROUTER socket is listening on.
666 'shell_port' : int # The port the shell ROUTER socket is listening on.
667 'iopub_port' : int # The port the PUB socket is listening on.
667 'iopub_port' : int # The port the PUB socket is listening on.
668 'stdin_port' : int # The port the stdin ROUTER socket is listening on.
668 'stdin_port' : int # The port the stdin ROUTER socket is listening on.
669 'hb_port' : int # The port the heartbeat socket is listening on.
669 'hb_port' : int # The port the heartbeat socket is listening on.
670 }
670 }
671
671
672
672
673 Kernel info
674 -----------
675
676 If a client needs to know what protocol the kernel supports, it can
677 ask version number of the messaging protocol supported by the kernel.
678 This message can be used to fetch other core information of the
679 kernel, including language (e.g., Python), language version number and
680 IPython version number.
681
682 Message type: ``kernel_info_request``::
683
684 content = {
685 }
686
687 Message type: ``kernel_info_reply``::
688
689 content = {
690 # Version of messaging protocol (mandatory).
691 # The first integer indicates major version. It is incremented when
692 # there is any backward incompatible change.
693 # The second integer indicates minor version. It is incremented when
694 # there is any backward compatible change.
695 'protocol_version': [int, int],
696
697 # IPython version number (optional).
698 # Non-python kernel backend may not have this version number.
699 # The last component is an extra field, which may be 'dev' or
700 # 'rc1' in development version. It is an empty string for
701 # released version.
702 'ipython_version': [int, int, int, str],
703
704 # Language version number (mandatory).
705 # It is Python version number (e.g., [2, 7, 3]) for the kernel
706 # included in IPython.
707 'language_version': [int, ...],
708
709 # Programming language in which kernel is implemented (mandatory).
710 # Kernel included in IPython returns 'python'.
711 'language': str,
712 }
713
673
714
674 Kernel shutdown
715 Kernel shutdown
675 ---------------
716 ---------------
676
717
677 The clients can request the kernel to shut itself down; this is used in
718 The clients can request the kernel to shut itself down; this is used in
678 multiple cases:
719 multiple cases:
679
720
680 - when the user chooses to close the client application via a menu or window
721 - when the user chooses to close the client application via a menu or window
681 control.
722 control.
682 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
723 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
683 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
724 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
684 IPythonQt client) to force a kernel restart to get a clean kernel without
725 IPythonQt client) to force a kernel restart to get a clean kernel without
685 losing client-side state like history or inlined figures.
726 losing client-side state like history or inlined figures.
686
727
687 The client sends a shutdown request to the kernel, and once it receives the
728 The client sends a shutdown request to the kernel, and once it receives the
688 reply message (which is otherwise empty), it can assume that the kernel has
729 reply message (which is otherwise empty), it can assume that the kernel has
689 completed shutdown safely.
730 completed shutdown safely.
690
731
691 Upon their own shutdown, client applications will typically execute a last
732 Upon their own shutdown, client applications will typically execute a last
692 minute sanity check and forcefully terminate any kernel that is still alive, to
733 minute sanity check and forcefully terminate any kernel that is still alive, to
693 avoid leaving stray processes in the user's machine.
734 avoid leaving stray processes in the user's machine.
694
735
695 For both shutdown request and reply, there is no actual content that needs to
736 For both shutdown request and reply, there is no actual content that needs to
696 be sent, so the content dict is empty.
737 be sent, so the content dict is empty.
697
738
698 Message type: ``shutdown_request``::
739 Message type: ``shutdown_request``::
699
740
700 content = {
741 content = {
701 'restart' : bool # whether the shutdown is final, or precedes a restart
742 'restart' : bool # whether the shutdown is final, or precedes a restart
702 }
743 }
703
744
704 Message type: ``shutdown_reply``::
745 Message type: ``shutdown_reply``::
705
746
706 content = {
747 content = {
707 'restart' : bool # whether the shutdown is final, or precedes a restart
748 'restart' : bool # whether the shutdown is final, or precedes a restart
708 }
749 }
709
750
710 .. Note::
751 .. Note::
711
752
712 When the clients detect a dead kernel thanks to inactivity on the heartbeat
753 When the clients detect a dead kernel thanks to inactivity on the heartbeat
713 socket, they simply send a forceful process termination signal, since a dead
754 socket, they simply send a forceful process termination signal, since a dead
714 process is unlikely to respond in any useful way to messages.
755 process is unlikely to respond in any useful way to messages.
715
756
716
757
717 Messages on the PUB/SUB socket
758 Messages on the PUB/SUB socket
718 ==============================
759 ==============================
719
760
720 Streams (stdout, stderr, etc)
761 Streams (stdout, stderr, etc)
721 ------------------------------
762 ------------------------------
722
763
723 Message type: ``stream``::
764 Message type: ``stream``::
724
765
725 content = {
766 content = {
726 # The name of the stream is one of 'stdin', 'stdout', 'stderr'
767 # The name of the stream is one of 'stdin', 'stdout', 'stderr'
727 'name' : str,
768 'name' : str,
728
769
729 # The data is an arbitrary string to be written to that stream
770 # The data is an arbitrary string to be written to that stream
730 'data' : str,
771 'data' : str,
731 }
772 }
732
773
733 When a kernel receives a raw_input call, it should also broadcast it on the pub
774 When a kernel receives a raw_input call, it should also broadcast it on the pub
734 socket with the names 'stdin' and 'stdin_reply'. This will allow other clients
775 socket with the names 'stdin' and 'stdin_reply'. This will allow other clients
735 to monitor/display kernel interactions and possibly replay them to their user
776 to monitor/display kernel interactions and possibly replay them to their user
736 or otherwise expose them.
777 or otherwise expose them.
737
778
738 Display Data
779 Display Data
739 ------------
780 ------------
740
781
741 This type of message is used to bring back data that should be diplayed (text,
782 This type of message is used to bring back data that should be diplayed (text,
742 html, svg, etc.) in the frontends. This data is published to all frontends.
783 html, svg, etc.) in the frontends. This data is published to all frontends.
743 Each message can have multiple representations of the data; it is up to the
784 Each message can have multiple representations of the data; it is up to the
744 frontend to decide which to use and how. A single message should contain all
785 frontend to decide which to use and how. A single message should contain all
745 possible representations of the same information. Each representation should
786 possible representations of the same information. Each representation should
746 be a JSON'able data structure, and should be a valid MIME type.
787 be a JSON'able data structure, and should be a valid MIME type.
747
788
748 Some questions remain about this design:
789 Some questions remain about this design:
749
790
750 * Do we use this message type for pyout/displayhook? Probably not, because
791 * Do we use this message type for pyout/displayhook? Probably not, because
751 the displayhook also has to handle the Out prompt display. On the other hand
792 the displayhook also has to handle the Out prompt display. On the other hand
752 we could put that information into the metadata secion.
793 we could put that information into the metadata secion.
753
794
754 Message type: ``display_data``::
795 Message type: ``display_data``::
755
796
756 content = {
797 content = {
757
798
758 # Who create the data
799 # Who create the data
759 'source' : str,
800 'source' : str,
760
801
761 # The data dict contains key/value pairs, where the kids are MIME
802 # The data dict contains key/value pairs, where the kids are MIME
762 # types and the values are the raw data of the representation in that
803 # types and the values are the raw data of the representation in that
763 # format. The data dict must minimally contain the ``text/plain``
804 # format. The data dict must minimally contain the ``text/plain``
764 # MIME type which is used as a backup representation.
805 # MIME type which is used as a backup representation.
765 'data' : dict,
806 'data' : dict,
766
807
767 # Any metadata that describes the data
808 # Any metadata that describes the data
768 'metadata' : dict
809 'metadata' : dict
769 }
810 }
770
811
771
812
772 Raw Data Publication
813 Raw Data Publication
773 --------------------
814 --------------------
774
815
775 ``display_data`` lets you publish *representations* of data, such as images and html.
816 ``display_data`` lets you publish *representations* of data, such as images and html.
776 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
817 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
777
818
778 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
819 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
779
820
780 .. sourcecode:: python
821 .. sourcecode:: python
781
822
782 from IPython.zmq.datapub import publish_data
823 from IPython.zmq.datapub import publish_data
783 ns = dict(x=my_array)
824 ns = dict(x=my_array)
784 publish_data(ns)
825 publish_data(ns)
785
826
786
827
787 Message type: ``data_pub``::
828 Message type: ``data_pub``::
788
829
789 content = {
830 content = {
790 # the keys of the data dict, after it has been unserialized
831 # the keys of the data dict, after it has been unserialized
791 keys = ['a', 'b']
832 keys = ['a', 'b']
792 }
833 }
793 # the namespace dict will be serialized in the message buffers,
834 # the namespace dict will be serialized in the message buffers,
794 # which will have a length of at least one
835 # which will have a length of at least one
795 buffers = ['pdict', ...]
836 buffers = ['pdict', ...]
796
837
797
838
798 The interpretation of a sequence of data_pub messages for a given parent request should be
839 The interpretation of a sequence of data_pub messages for a given parent request should be
799 to update a single namespace with subsequent results.
840 to update a single namespace with subsequent results.
800
841
801 .. note::
842 .. note::
802
843
803 No frontends directly handle data_pub messages at this time.
844 No frontends directly handle data_pub messages at this time.
804 It is currently only used by the client/engines in :mod:`IPython.parallel`,
845 It is currently only used by the client/engines in :mod:`IPython.parallel`,
805 where engines may publish *data* to the Client,
846 where engines may publish *data* to the Client,
806 of which the Client can then publish *representations* via ``display_data``
847 of which the Client can then publish *representations* via ``display_data``
807 to various frontends.
848 to various frontends.
808
849
809 Python inputs
850 Python inputs
810 -------------
851 -------------
811
852
812 These messages are the re-broadcast of the ``execute_request``.
853 These messages are the re-broadcast of the ``execute_request``.
813
854
814 Message type: ``pyin``::
855 Message type: ``pyin``::
815
856
816 content = {
857 content = {
817 'code' : str, # Source code to be executed, one or more lines
858 'code' : str, # Source code to be executed, one or more lines
818
859
819 # The counter for this execution is also provided so that clients can
860 # The counter for this execution is also provided so that clients can
820 # display it, since IPython automatically creates variables called _iN
861 # display it, since IPython automatically creates variables called _iN
821 # (for input prompt In[N]).
862 # (for input prompt In[N]).
822 'execution_count' : int
863 'execution_count' : int
823 }
864 }
824
865
825 Python outputs
866 Python outputs
826 --------------
867 --------------
827
868
828 When Python produces output from code that has been compiled in with the
869 When Python produces output from code that has been compiled in with the
829 'single' flag to :func:`compile`, any expression that produces a value (such as
870 'single' flag to :func:`compile`, any expression that produces a value (such as
830 ``1+1``) is passed to ``sys.displayhook``, which is a callable that can do with
871 ``1+1``) is passed to ``sys.displayhook``, which is a callable that can do with
831 this value whatever it wants. The default behavior of ``sys.displayhook`` in
872 this value whatever it wants. The default behavior of ``sys.displayhook`` in
832 the Python interactive prompt is to print to ``sys.stdout`` the :func:`repr` of
873 the Python interactive prompt is to print to ``sys.stdout`` the :func:`repr` of
833 the value as long as it is not ``None`` (which isn't printed at all). In our
874 the value as long as it is not ``None`` (which isn't printed at all). In our
834 case, the kernel instantiates as ``sys.displayhook`` an object which has
875 case, the kernel instantiates as ``sys.displayhook`` an object which has
835 similar behavior, but which instead of printing to stdout, broadcasts these
876 similar behavior, but which instead of printing to stdout, broadcasts these
836 values as ``pyout`` messages for clients to display appropriately.
877 values as ``pyout`` messages for clients to display appropriately.
837
878
838 IPython's displayhook can handle multiple simultaneous formats depending on its
879 IPython's displayhook can handle multiple simultaneous formats depending on its
839 configuration. The default pretty-printed repr text is always given with the
880 configuration. The default pretty-printed repr text is always given with the
840 ``data`` entry in this message. Any other formats are provided in the
881 ``data`` entry in this message. Any other formats are provided in the
841 ``extra_formats`` list. Frontends are free to display any or all of these
882 ``extra_formats`` list. Frontends are free to display any or all of these
842 according to its capabilities. ``extra_formats`` list contains 3-tuples of an ID
883 according to its capabilities. ``extra_formats`` list contains 3-tuples of an ID
843 string, a type string, and the data. The ID is unique to the formatter
884 string, a type string, and the data. The ID is unique to the formatter
844 implementation that created the data. Frontends will typically ignore the ID
885 implementation that created the data. Frontends will typically ignore the ID
845 unless if it has requested a particular formatter. The type string tells the
886 unless if it has requested a particular formatter. The type string tells the
846 frontend how to interpret the data. It is often, but not always a MIME type.
887 frontend how to interpret the data. It is often, but not always a MIME type.
847 Frontends should ignore types that it does not understand. The data itself is
888 Frontends should ignore types that it does not understand. The data itself is
848 any JSON object and depends on the format. It is often, but not always a string.
889 any JSON object and depends on the format. It is often, but not always a string.
849
890
850 Message type: ``pyout``::
891 Message type: ``pyout``::
851
892
852 content = {
893 content = {
853
894
854 # The counter for this execution is also provided so that clients can
895 # The counter for this execution is also provided so that clients can
855 # display it, since IPython automatically creates variables called _N
896 # display it, since IPython automatically creates variables called _N
856 # (for prompt N).
897 # (for prompt N).
857 'execution_count' : int,
898 'execution_count' : int,
858
899
859 # The data dict contains key/value pairs, where the kids are MIME
900 # The data dict contains key/value pairs, where the kids are MIME
860 # types and the values are the raw data of the representation in that
901 # types and the values are the raw data of the representation in that
861 # format. The data dict must minimally contain the ``text/plain``
902 # format. The data dict must minimally contain the ``text/plain``
862 # MIME type which is used as a backup representation.
903 # MIME type which is used as a backup representation.
863 'data' : dict,
904 'data' : dict,
864
905
865 }
906 }
866
907
867 Python errors
908 Python errors
868 -------------
909 -------------
869
910
870 When an error occurs during code execution
911 When an error occurs during code execution
871
912
872 Message type: ``pyerr``::
913 Message type: ``pyerr``::
873
914
874 content = {
915 content = {
875 # Similar content to the execute_reply messages for the 'error' case,
916 # Similar content to the execute_reply messages for the 'error' case,
876 # except the 'status' field is omitted.
917 # except the 'status' field is omitted.
877 }
918 }
878
919
879 Kernel status
920 Kernel status
880 -------------
921 -------------
881
922
882 This message type is used by frontends to monitor the status of the kernel.
923 This message type is used by frontends to monitor the status of the kernel.
883
924
884 Message type: ``status``::
925 Message type: ``status``::
885
926
886 content = {
927 content = {
887 # When the kernel starts to execute code, it will enter the 'busy'
928 # When the kernel starts to execute code, it will enter the 'busy'
888 # state and when it finishes, it will enter the 'idle' state.
929 # state and when it finishes, it will enter the 'idle' state.
889 execution_state : ('busy', 'idle')
930 execution_state : ('busy', 'idle')
890 }
931 }
891
932
892 Kernel crashes
933 Kernel crashes
893 --------------
934 --------------
894
935
895 When the kernel has an unexpected exception, caught by the last-resort
936 When the kernel has an unexpected exception, caught by the last-resort
896 sys.excepthook, we should broadcast the crash handler's output before exiting.
937 sys.excepthook, we should broadcast the crash handler's output before exiting.
897 This will allow clients to notice that a kernel died, inform the user and
938 This will allow clients to notice that a kernel died, inform the user and
898 propose further actions.
939 propose further actions.
899
940
900 Message type: ``crash``::
941 Message type: ``crash``::
901
942
902 content = {
943 content = {
903 # Similarly to the 'error' case for execute_reply messages, this will
944 # Similarly to the 'error' case for execute_reply messages, this will
904 # contain ename, etype and traceback fields.
945 # contain ename, etype and traceback fields.
905
946
906 # An additional field with supplementary information such as where to
947 # An additional field with supplementary information such as where to
907 # send the crash message
948 # send the crash message
908 'info' : str,
949 'info' : str,
909 }
950 }
910
951
911
952
912 Future ideas
953 Future ideas
913 ------------
954 ------------
914
955
915 Other potential message types, currently unimplemented, listed below as ideas.
956 Other potential message types, currently unimplemented, listed below as ideas.
916
957
917 Message type: ``file``::
958 Message type: ``file``::
918
959
919 content = {
960 content = {
920 'path' : 'cool.jpg',
961 'path' : 'cool.jpg',
921 'mimetype' : str,
962 'mimetype' : str,
922 'data' : str,
963 'data' : str,
923 }
964 }
924
965
925
966
926 Messages on the stdin ROUTER/DEALER sockets
967 Messages on the stdin ROUTER/DEALER sockets
927 ===========================================
968 ===========================================
928
969
929 This is a socket where the request/reply pattern goes in the opposite direction:
970 This is a socket where the request/reply pattern goes in the opposite direction:
930 from the kernel to a *single* frontend, and its purpose is to allow
971 from the kernel to a *single* frontend, and its purpose is to allow
931 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
972 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
932 to be fulfilled by the client. The request should be made to the frontend that
973 to be fulfilled by the client. The request should be made to the frontend that
933 made the execution request that prompted ``raw_input`` to be called. For now we
974 made the execution request that prompted ``raw_input`` to be called. For now we
934 will keep these messages as simple as possible, since they only mean to convey
975 will keep these messages as simple as possible, since they only mean to convey
935 the ``raw_input(prompt)`` call.
976 the ``raw_input(prompt)`` call.
936
977
937 Message type: ``input_request``::
978 Message type: ``input_request``::
938
979
939 content = { 'prompt' : str }
980 content = { 'prompt' : str }
940
981
941 Message type: ``input_reply``::
982 Message type: ``input_reply``::
942
983
943 content = { 'value' : str }
984 content = { 'value' : str }
944
985
945 .. Note::
986 .. Note::
946
987
947 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
988 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
948 practice the kernel should behave like an interactive program. When a
989 practice the kernel should behave like an interactive program. When a
949 program is opened on the console, the keyboard effectively takes over the
990 program is opened on the console, the keyboard effectively takes over the
950 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
991 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
951 Since the IPython kernel effectively behaves like a console program (albeit
992 Since the IPython kernel effectively behaves like a console program (albeit
952 one whose "keyboard" is actually living in a separate process and
993 one whose "keyboard" is actually living in a separate process and
953 transported over the zmq connection), raw ``stdin`` isn't expected to be
994 transported over the zmq connection), raw ``stdin`` isn't expected to be
954 available.
995 available.
955
996
956
997
957 Heartbeat for kernels
998 Heartbeat for kernels
958 =====================
999 =====================
959
1000
960 Initially we had considered using messages like those above over ZMQ for a
1001 Initially we had considered using messages like those above over ZMQ for a
961 kernel 'heartbeat' (a way to detect quickly and reliably whether a kernel is
1002 kernel 'heartbeat' (a way to detect quickly and reliably whether a kernel is
962 alive at all, even if it may be busy executing user code). But this has the
1003 alive at all, even if it may be busy executing user code). But this has the
963 problem that if the kernel is locked inside extension code, it wouldn't execute
1004 problem that if the kernel is locked inside extension code, it wouldn't execute
964 the python heartbeat code. But it turns out that we can implement a basic
1005 the python heartbeat code. But it turns out that we can implement a basic
965 heartbeat with pure ZMQ, without using any Python messaging at all.
1006 heartbeat with pure ZMQ, without using any Python messaging at all.
966
1007
967 The monitor sends out a single zmq message (right now, it is a str of the
1008 The monitor sends out a single zmq message (right now, it is a str of the
968 monitor's lifetime in seconds), and gets the same message right back, prefixed
1009 monitor's lifetime in seconds), and gets the same message right back, prefixed
969 with the zmq identity of the DEALER socket in the heartbeat process. This can be
1010 with the zmq identity of the DEALER socket in the heartbeat process. This can be
970 a uuid, or even a full message, but there doesn't seem to be a need for packing
1011 a uuid, or even a full message, but there doesn't seem to be a need for packing
971 up a message when the sender and receiver are the exact same Python object.
1012 up a message when the sender and receiver are the exact same Python object.
972
1013
973 The model is this::
1014 The model is this::
974
1015
975 monitor.send(str(self.lifetime)) # '1.2345678910'
1016 monitor.send(str(self.lifetime)) # '1.2345678910'
976
1017
977 and the monitor receives some number of messages of the form::
1018 and the monitor receives some number of messages of the form::
978
1019
979 ['uuid-abcd-dead-beef', '1.2345678910']
1020 ['uuid-abcd-dead-beef', '1.2345678910']
980
1021
981 where the first part is the zmq.IDENTITY of the heart's DEALER on the engine, and
1022 where the first part is the zmq.IDENTITY of the heart's DEALER on the engine, and
982 the rest is the message sent by the monitor. No Python code ever has any
1023 the rest is the message sent by the monitor. No Python code ever has any
983 access to the message between the monitor's send, and the monitor's recv.
1024 access to the message between the monitor's send, and the monitor's recv.
984
1025
985
1026
986 ToDo
1027 ToDo
987 ====
1028 ====
988
1029
989 Missing things include:
1030 Missing things include:
990
1031
991 * Important: finish thinking through the payload concept and API.
1032 * Important: finish thinking through the payload concept and API.
992
1033
993 * Important: ensure that we have a good solution for magics like %edit. It's
1034 * Important: ensure that we have a good solution for magics like %edit. It's
994 likely that with the payload concept we can build a full solution, but not
1035 likely that with the payload concept we can build a full solution, but not
995 100% clear yet.
1036 100% clear yet.
996
1037
997 * Finishing the details of the heartbeat protocol.
1038 * Finishing the details of the heartbeat protocol.
998
1039
999 * Signal handling: specify what kind of information kernel should broadcast (or
1040 * Signal handling: specify what kind of information kernel should broadcast (or
1000 not) when it receives signals.
1041 not) when it receives signals.
1001
1042
1002 .. include:: ../links.rst
1043 .. include:: ../links.rst
General Comments 0
You need to be logged in to leave comments. Login now