##// END OF EJS Templates
Rework messaging to better conform to our spec....
Fernando Perez -
Show More
1 NO CONTENT: new file 100644
@@ -0,0 +1,40 b''
1 """Test suite for our zeromq-based messaging specification.
2 """
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2010 The IPython Development Team
5 #
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.
8 #-----------------------------------------------------------------------------
9
10 import sys
11 import time
12
13 import nose.tools as nt
14
15 from ..blockingkernelmanager import BlockingKernelManager
16
17 from IPython.utils import io
18
19 def setup():
20 global KM
21 KM = BlockingKernelManager()
22
23 KM.start_kernel()
24 KM.start_channels()
25 # Give the kernel a chance to come up.
26 time.sleep(1)
27
28 def teardown():
29 io.rprint('Entering teardown...') # dbg
30 io.rprint('Stopping channels and kernel...') # dbg
31 KM.stop_channels()
32 KM.kill_kernel()
33
34
35 # Actual tests
36
37 def test_execute():
38 KM.xreq_channel.execute(code='x=1')
39 KM.xreq_channel.execute(code='print 1')
40
@@ -1704,6 +1704,45 b' class InteractiveShell(Configurable, Magic):'
1704 1704 self.prefilter = self.prefilter_manager.prefilter_lines
1705 1705
1706 1706 #-------------------------------------------------------------------------
1707 # Things related to extracting values/expressions from kernel and user_ns
1708 #-------------------------------------------------------------------------
1709
1710 def _simple_error(self):
1711 etype, value = sys.exc_info()[:2]
1712 return u'[ERROR] {e.__name__}: {v}'.format(e=etype, v=value)
1713
1714 def get_user_variables(self, names):
1715 """Get a list of variable names from the user's namespace.
1716
1717 The return value is a dict with the repr() of each value.
1718 """
1719 out = {}
1720 user_ns = self.user_ns
1721 for varname in names:
1722 try:
1723 value = repr(user_ns[varname])
1724 except:
1725 value = self._simple_error()
1726 out[varname] = value
1727 return out
1728
1729 def eval_expressions(self, expressions):
1730 """Evaluate a dict of expressions in the user's namespace.
1731
1732 The return value is a dict with the repr() of each value.
1733 """
1734 out = {}
1735 user_ns = self.user_ns
1736 global_ns = self.user_global_ns
1737 for key, expr in expressions.iteritems():
1738 try:
1739 value = repr(eval(expr, global_ns, user_ns))
1740 except:
1741 value = self._simple_error()
1742 out[key] = value
1743 return out
1744
1745 #-------------------------------------------------------------------------
1707 1746 # Things related to the running of code
1708 1747 #-------------------------------------------------------------------------
1709 1748
@@ -131,10 +131,27 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
131 131 complete = not self._input_splitter.push_accepts_more()
132 132 return complete
133 133
134 def _execute(self, source, hidden):
134 def _execute(self, source, hidden, user_variables=None,
135 user_expressions=None):
135 136 """ Execute 'source'. If 'hidden', do not show any output.
136 """
137 self.kernel_manager.xreq_channel.execute(source, hidden)
137
138 See parent class :meth:`execute` docstring for full details.
139 """
140 # tmp code for testing, disable in real use with 'if 0'. Only delete
141 # this code once we have automated tests for these fields.
142 if 0:
143 user_variables = ['x', 'y', 'z']
144 user_expressions = {'sum' : '1+1',
145 'bad syntax' : 'klsdafj kasd f',
146 'bad call' : 'range("hi")',
147 'time' : 'time.time()',
148 }
149 # /end tmp code
150
151 # FIXME - user_variables/expressions are not visible in API above us.
152 self.kernel_manager.xreq_channel.execute(source, hidden,
153 user_variables,
154 user_expressions)
138 155 self._hidden = hidden
139 156
140 157 def _prompt_started_hook(self):
@@ -21,6 +21,7 b' from PyQt4 import QtCore, QtGui'
21 21 # Local imports
22 22 from IPython.core.inputsplitter import IPythonInputSplitter
23 23 from IPython.core.usage import default_banner
24 from IPython.utils import io
24 25 from IPython.utils.traitlets import Bool, Str
25 26 from frontend_widget import FrontendWidget
26 27
@@ -50,9 +51,13 b" default_dark_style_sheet = '''"
50 51 '''
51 52 default_dark_syntax_style = 'monokai'
52 53
53 # Default prompts.
54 # Default strings to build and display input and output prompts (and separators
55 # in between)
54 56 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
55 57 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
58 default_input_sep = '\n'
59 default_output_sep = ''
60 default_output_sep2 = ''
56 61
57 62 #-----------------------------------------------------------------------------
58 63 # IPythonWidget class
@@ -92,6 +97,9 b' class IPythonWidget(FrontendWidget):'
92 97 # Prompts.
93 98 in_prompt = Str(default_in_prompt, config=True)
94 99 out_prompt = Str(default_out_prompt, config=True)
100 input_sep = Str(default_input_sep, config=True)
101 output_sep = Str(default_output_sep, config=True)
102 output_sep2 = Str(default_output_sep2, config=True)
95 103
96 104 # FrontendWidget protected class variables.
97 105 _input_splitter_class = IPythonInputSplitter
@@ -155,20 +163,17 b' class IPythonWidget(FrontendWidget):'
155 163 """ Implemented to handle prompt number replies, which are only
156 164 supported by the IPython kernel.
157 165 """
158 content = msg['content']
159 self._show_interpreter_prompt(content['prompt_number'],
160 content['input_sep'])
166 self._show_interpreter_prompt(msg['content']['execution_count'])
161 167
162 168 def _handle_pyout(self, msg):
163 169 """ Reimplemented for IPython-style "display hook".
164 170 """
165 171 if not self._hidden and self._is_from_this_session(msg):
166 172 content = msg['content']
167 prompt_number = content['prompt_number']
168 self._append_plain_text(content['output_sep'])
173 prompt_number = content['execution_count']
174 self._append_plain_text(self.output_sep)
169 175 self._append_html(self._make_out_prompt(prompt_number))
170 self._append_plain_text(content['data'] + '\n' +
171 content['output_sep2'])
176 self._append_plain_text(content['data']+self.output_sep2)
172 177
173 178 def _started_channels(self):
174 179 """ Reimplemented to make a history request.
@@ -244,17 +249,19 b' class IPythonWidget(FrontendWidget):'
244 249 else:
245 250 return False
246 251
247 def _show_interpreter_prompt(self, number=None, input_sep='\n'):
252 def _show_interpreter_prompt(self, number=None):
248 253 """ Reimplemented for IPython-style prompts.
249 254 """
250 255 # If a number was not specified, make a prompt number request.
251 256 if number is None:
252 self.kernel_manager.xreq_channel.prompt()
253 return
257 # FIXME - fperez: this should be a silent code request
258 number = 1
259 ##self.kernel_manager.xreq_channel.prompt()
260 ##return
254 261
255 262 # Show a new prompt and save information about it so that it can be
256 263 # updated later if the prompt number turns out to be wrong.
257 self._prompt_sep = input_sep
264 self._prompt_sep = self.input_sep
258 265 self._show_prompt(self._make_in_prompt(number), html=True)
259 266 block = self._control.document().lastBlock()
260 267 length = len(self._prompt)
@@ -269,7 +276,8 b' class IPythonWidget(FrontendWidget):'
269 276 """
270 277 # Update the old prompt number if necessary.
271 278 content = msg['content']
272 previous_prompt_number = content['prompt_number']
279 ##io.rprint('_show_interpreter_prompt_for_reply\n', content) # dbg
280 previous_prompt_number = content['execution_count']
273 281 if self._previous_prompt_obj and \
274 282 self._previous_prompt_obj.number != previous_prompt_number:
275 283 block = self._previous_prompt_obj.block
@@ -293,9 +301,7 b' class IPythonWidget(FrontendWidget):'
293 301 self._previous_prompt_obj = None
294 302
295 303 # Show a new prompt with the kernel's estimated prompt number.
296 next_prompt = content['next_prompt']
297 self._show_interpreter_prompt(next_prompt['prompt_number'],
298 next_prompt['input_sep'])
304 self._show_interpreter_prompt(previous_prompt_number+1)
299 305
300 306 #---------------------------------------------------------------------------
301 307 # 'IPythonWidget' interface
@@ -63,7 +63,8 b' def process_handler(cmd, callback, stderr=subprocess.PIPE):'
63 63 """
64 64 sys.stdout.flush()
65 65 sys.stderr.flush()
66 close_fds = False if sys.platform=='win32' else True
66 # On win32, close_fds can't be true when using pipes for stdin/out/err
67 close_fds = sys.platform != 'win32'
67 68 p = subprocess.Popen(cmd, shell=True,
68 69 stdin=subprocess.PIPE,
69 70 stdout=subprocess.PIPE,
@@ -1,10 +1,32 b''
1 from kernelmanager import SubSocketChannel
1 """Implement a fully blocking kernel manager.
2
3 Useful for test suites and blocking terminal interfaces.
4 """
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2010 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING.txt, distributed as part of this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15 from __future__ import print_function
16
17 # Stdlib
2 18 from Queue import Queue, Empty
3 19
20 # Our own
21 from IPython.utils import io
22 from IPython.utils.traitlets import Type
4 23
5 class MsgNotReady(Exception):
6 pass
24 from .kernelmanager import (KernelManager, SubSocketChannel,
25 XReqSocketChannel, RepSocketChannel, HBSocketChannel)
7 26
27 #-----------------------------------------------------------------------------
28 # Functions and classes
29 #-----------------------------------------------------------------------------
8 30
9 31 class BlockingSubSocketChannel(SubSocketChannel):
10 32
@@ -13,6 +35,7 b' class BlockingSubSocketChannel(SubSocketChannel):'
13 35 self._in_queue = Queue()
14 36
15 37 def call_handlers(self, msg):
38 io.rprint('[[Sub]]', msg) # dbg
16 39 self._in_queue.put(msg)
17 40
18 41 def msg_ready(self):
@@ -24,21 +47,68 b' class BlockingSubSocketChannel(SubSocketChannel):'
24 47
25 48 def get_msg(self, block=True, timeout=None):
26 49 """Get a message if there is one that is ready."""
27 try:
28 msg = self.in_queue.get(block, timeout)
29 except Empty:
30 raise MsgNotReady('No message has been received.')
50 return self.in_queue.get(block, timeout)
51
52 def get_msgs(self):
53 """Get all messages that are currently ready."""
54 msgs = []
55 while True:
56 try:
57 msgs.append(self.get_msg(block=False))
58 except Empty:
59 break
60 return msgs
61
62
63
64 class BlockingXReqSocketChannel(XReqSocketChannel):
65
66 def __init__(self, context, session, address=None):
67 super(BlockingXReqSocketChannel, self).__init__(context, session, address)
68 self._in_queue = Queue()
69
70 def call_handlers(self, msg):
71 io.rprint('[[XReq]]', msg) # dbg
72
73 def msg_ready(self):
74 """Is there a message that has been received?"""
75 if self._in_queue.qsize() == 0:
76 return False
31 77 else:
32 return msg
78 return True
79
80 def get_msg(self, block=True, timeout=None):
81 """Get a message if there is one that is ready."""
82 return self.in_queue.get(block, timeout)
33 83
34 84 def get_msgs(self):
35 85 """Get all messages that are currently ready."""
36 86 msgs = []
37 87 while True:
38 88 try:
39 msg = self.get_msg(block=False)
40 except MsgNotReady:
89 msgs.append(self.get_msg(block=False))
90 except Empty:
41 91 break
42 else:
43 msgs.append(msg)
44 return msgs No newline at end of file
92 return msgs
93
94 class BlockingRepSocketChannel(RepSocketChannel):
95 def call_handlers(self, msg):
96 io.rprint('[[Rep]]', msg) # dbg
97
98
99 class BlockingHBSocketChannel(HBSocketChannel):
100 # This kernel needs rapid monitoring capabilities
101 time_to_dead = 0.2
102
103 def call_handlers(self, since_last_heartbeat):
104 io.rprint('[[Heart]]', since_last_heartbeat) # dbg
105
106
107 class BlockingKernelManager(KernelManager):
108
109 # The classes to use for the various channels.
110 xreq_channel_class = Type(BlockingXReqSocketChannel)
111 sub_channel_class = Type(BlockingSubSocketChannel)
112 rep_channel_class = Type(BlockingRepSocketChannel)
113 hb_channel_class = Type(BlockingHBSocketChannel)
114
@@ -35,6 +35,7 b' from iostream import OutStream'
35 35 from session import Session, Message
36 36 from zmqshell import ZMQInteractiveShell
37 37
38
38 39 #-----------------------------------------------------------------------------
39 40 # Main kernel class
40 41 #-----------------------------------------------------------------------------
@@ -64,8 +65,7 b' class Kernel(Configurable):'
64 65
65 66 # Build dict of handlers for message types
66 67 msg_types = [ 'execute_request', 'complete_request',
67 'object_info_request', 'prompt_request',
68 'history_request' ]
68 'object_info_request', 'history_request' ]
69 69 self.handlers = {}
70 70 for msg_type in msg_types:
71 71 self.handlers[msg_type] = getattr(self, msg_type)
@@ -81,14 +81,21 b' class Kernel(Configurable):'
81 81 # FIXME: Bug in pyzmq/zmq?
82 82 # assert self.reply_socket.rcvmore(), "Missing message part."
83 83 msg = self.reply_socket.recv_json()
84 omsg = Message(msg)
85 io.raw_print('\n')
86 io.raw_print(omsg)
87 handler = self.handlers.get(omsg.msg_type, None)
84
85 # Print some info about this message and leave a '--->' marker, so it's
86 # easier to trace visually the message chain when debugging. Each
87 # handler prints its message at the end.
88 # Eventually we'll move these from stdout to a logger.
89 io.raw_print('\n*** MESSAGE TYPE:', msg['msg_type'], '***')
90 io.raw_print(' Content: ', msg['content'],
91 '\n --->\n ', sep='', end='')
92
93 # Find and call actual handler for message
94 handler = self.handlers.get(msg['msg_type'], None)
88 95 if handler is None:
89 io.raw_print_err("UNKNOWN MESSAGE TYPE:", omsg)
96 io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg)
90 97 else:
91 handler(ident, omsg)
98 handler(ident, msg)
92 99
93 100 def start(self):
94 101 """ Start the kernel main loop.
@@ -97,37 +104,56 b' class Kernel(Configurable):'
97 104 time.sleep(0.05)
98 105 self.do_one_iteration()
99 106
100
101 107 #---------------------------------------------------------------------------
102 108 # Kernel request handlers
103 109 #---------------------------------------------------------------------------
104 110
111 def _publish_pyin(self, code, parent):
112 """Publish the code request on the pyin stream."""
113
114 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
115 self.pub_socket.send_json(pyin_msg)
116
105 117 def execute_request(self, ident, parent):
106 118 try:
107 code = parent[u'content'][u'code']
119 content = parent[u'content']
120 code = content[u'code']
121 silent = content[u'silent']
108 122 except:
109 123 io.raw_print_err("Got bad msg: ")
110 124 io.raw_print_err(Message(parent))
111 125 return
112 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
113 self.pub_socket.send_json(pyin_msg)
114 126
127 shell = self.shell # we'll need this a lot here
128
129 # Replace raw_input. Note that is not sufficient to replace
130 # raw_input in the user namespace.
131 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
132 __builtin__.raw_input = raw_input
133
134 # Set the parent message of the display hook and out streams.
135 shell.displayhook.set_parent(parent)
136 sys.stdout.set_parent(parent)
137 sys.stderr.set_parent(parent)
138
139 # Re-broadcast our input for the benefit of listening clients, and
140 # start computing output
141 if not silent:
142 self._publish_pyin(code, parent)
143
144 reply_content = {}
115 145 try:
116 # Replace raw_input. Note that is not sufficient to replace
117 # raw_input in the user namespace.
118 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
119 __builtin__.raw_input = raw_input
120
121 # Set the parent message of the display hook and out streams.
122 self.shell.displayhook.set_parent(parent)
123 sys.stdout.set_parent(parent)
124 sys.stderr.set_parent(parent)
125
126 # FIXME: runlines calls the exception handler itself. We should
127 # clean this up.
128 self.shell._reply_content = None
129 self.shell.runlines(code)
146 if silent:
147 # runcode uses 'exec' mode, so no displayhook will fire, and it
148 # doesn't call logging or history manipulations. Print
149 # statements in that code will obviously still execute.
150 shell.runcode(code)
151 else:
152 # FIXME: runlines calls the exception handler itself.
153 shell._reply_content = None
154 shell.runlines(code)
130 155 except:
156 status = u'error'
131 157 # FIXME: this code right now isn't being used yet by default,
132 158 # because the runlines() call above directly fires off exception
133 159 # reporting. This code, therefore, is only active in the scenario
@@ -136,35 +162,39 b' class Kernel(Configurable):'
136 162 # single location in the codbase.
137 163 etype, evalue, tb = sys.exc_info()
138 164 tb_list = traceback.format_exception(etype, evalue, tb)
139 reply_content = self.shell._showtraceback(etype, evalue, tb_list)
165 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
140 166 else:
141 payload = self.shell.payload_manager.read_payload()
167 status = u'ok'
168 reply_content[u'payload'] = shell.payload_manager.read_payload()
142 169 # Be agressive about clearing the payload because we don't want
143 170 # it to sit in memory until the next execute_request comes in.
144 self.shell.payload_manager.clear_payload()
145 reply_content = { 'status' : 'ok', 'payload' : payload }
146
147 # Compute the prompt information
148 prompt_number = self.shell.displayhook.prompt_count
149 reply_content['prompt_number'] = prompt_number
150 prompt_string = self.shell.displayhook.prompt1.peek_next_prompt()
151 next_prompt = {'prompt_string' : prompt_string,
152 'prompt_number' : prompt_number+1,
153 'input_sep' : self.shell.displayhook.input_sep}
154 reply_content['next_prompt'] = next_prompt
155
156 # TMP - fish exception info out of shell, possibly left there by
157 # runlines
158 if self.shell._reply_content is not None:
159 reply_content.update(self.shell._reply_content)
160
161 # Flush output before sending the reply.
162 sys.stderr.flush()
163 sys.stdout.flush()
164
171 shell.payload_manager.clear_payload()
172
173 reply_content[u'status'] = status
174 # Compute the execution counter so clients can display prompts
175 reply_content['execution_count'] = shell.displayhook.prompt_count
176
177 # FIXME - fish exception info out of shell, possibly left there by
178 # runlines. We'll need to clean up this logic later.
179 if shell._reply_content is not None:
180 reply_content.update(shell._reply_content)
181
182 # At this point, we can tell whether the main code execution succeeded
183 # or not. If it did, we proceed to evaluate user_variables/expressions
184 if reply_content['status'] == 'ok':
185 reply_content[u'user_variables'] = \
186 shell.get_user_variables(content[u'user_variables'])
187 reply_content[u'user_expressions'] = \
188 shell.eval_expressions(content[u'user_expressions'])
189 else:
190 # If there was an error, don't even try to compute variables or
191 # expressions
192 reply_content[u'user_variables'] = {}
193 reply_content[u'user_expressions'] = {}
194
165 195 # Send the reply.
166 196 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
167 io.raw_print(Message(reply_msg))
197 io.raw_print(reply_msg)
168 198 self.reply_socket.send(ident, zmq.SNDMORE)
169 199 self.reply_socket.send_json(reply_msg)
170 200 if reply_msg['content']['status'] == u'error':
@@ -186,16 +216,6 b' class Kernel(Configurable):'
186 216 object_info, parent, ident)
187 217 io.raw_print(msg)
188 218
189 def prompt_request(self, ident, parent):
190 prompt_number = self.shell.displayhook.prompt_count
191 prompt_string = self.shell.displayhook.prompt1.peek_next_prompt()
192 content = {'prompt_string' : prompt_string,
193 'prompt_number' : prompt_number+1,
194 'input_sep' : self.shell.displayhook.input_sep}
195 msg = self.session.send(self.reply_socket, 'prompt_reply',
196 content, parent, ident)
197 io.raw_print(msg)
198
199 219 def history_request(self, ident, parent):
200 220 output = parent['content']['output']
201 221 index = parent['content']['index']
@@ -218,13 +238,14 b' class Kernel(Configurable):'
218 238 if e.errno == zmq.EAGAIN:
219 239 break
220 240 else:
221 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
241 assert self.reply_socket.rcvmore(), \
242 "Unexpected missing message part."
222 243 msg = self.reply_socket.recv_json()
223 244 io.raw_print("Aborting:\n", Message(msg))
224 245 msg_type = msg['msg_type']
225 246 reply_type = msg_type.split('_')[0] + '_reply'
226 247 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
227 io.raw_print(Message(reply_msg))
248 io.raw_print(reply_msg)
228 249 self.reply_socket.send(ident,zmq.SNDMORE)
229 250 self.reply_socket.send_json(reply_msg)
230 251 # We need to wait a bit for requests to come in. This can probably
@@ -312,6 +333,7 b' class QtKernel(Kernel):'
312 333 self.timer.start(50)
313 334 start_event_loop_qt4(self.app)
314 335
336
315 337 class WxKernel(Kernel):
316 338 """A Kernel subclass with Wx support."""
317 339
@@ -421,6 +443,7 b' def launch_kernel(xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
421 443 xrep_port, pub_port, req_port, hb_port,
422 444 independent, extra_arguments)
423 445
446
424 447 def main():
425 448 """ The IPython kernel main entry point.
426 449 """
@@ -458,5 +481,6 b" given, the GUI backend is matplotlib's, otherwise use one of: \\"
458 481
459 482 start_kernel(namespace, kernel)
460 483
484
461 485 if __name__ == '__main__':
462 486 main()
@@ -29,6 +29,7 b' from zmq import POLLIN, POLLOUT, POLLERR'
29 29 from zmq.eventloop import ioloop
30 30
31 31 # Local imports.
32 from IPython.utils import io
32 33 from IPython.utils.traitlets import HasTraits, Any, Instance, Type, TCPAddress
33 34 from session import Session
34 35
@@ -42,6 +43,35 b' class InvalidPortNumber(Exception):'
42 43 pass
43 44
44 45 #-----------------------------------------------------------------------------
46 # Utility functions
47 #-----------------------------------------------------------------------------
48
49 # some utilities to validate message structure, these might get moved elsewhere
50 # if they prove to have more generic utility
51
52 def validate_string_list(lst):
53 """Validate that the input is a list of strings.
54
55 Raises ValueError if not."""
56 if not isinstance(lst, list):
57 raise ValueError('input %r must be a list' % lst)
58 for x in lst:
59 if not isinstance(x, basestring):
60 raise ValueError('element %r in list must be a string' % x)
61
62
63 def validate_string_dict(dct):
64 """Validate that the input is a dict with string keys and values.
65
66 Raises ValueError if not."""
67 for k,v in dct.iteritems():
68 if not isinstance(k, basestring):
69 raise ValueError('key %r in dict must be a string' % k)
70 if not isinstance(v, basestring):
71 raise ValueError('value %r in dict must be a string' % v)
72
73
74 #-----------------------------------------------------------------------------
45 75 # ZMQ Socket Channel classes
46 76 #-----------------------------------------------------------------------------
47 77
@@ -163,23 +193,49 b' class XReqSocketChannel(ZmqSocketChannel):'
163 193 """
164 194 raise NotImplementedError('call_handlers must be defined in a subclass.')
165 195
166 def execute(self, code, silent=False):
196 def execute(self, code, silent=False,
197 user_variables=None, user_expressions=None):
167 198 """Execute code in the kernel.
168 199
169 200 Parameters
170 201 ----------
171 202 code : str
172 203 A string of Python code.
204
173 205 silent : bool, optional (default False)
174 206 If set, the kernel will execute the code as quietly possible.
175 207
208 user_variables : list, optional
209
210 A list of variable names to pull from the user's namespace. They
211 will come back as a dict with these names as keys and their
212 :func:`repr` as values.
213
214 user_expressions : dict, optional
215 A dict with string keys and to pull from the user's
216 namespace. They will come back as a dict with these names as keys
217 and their :func:`repr` as values.
218
176 219 Returns
177 220 -------
178 221 The msg_id of the message sent.
179 222 """
223 if user_variables is None:
224 user_variables = []
225 if user_expressions is None:
226 user_expressions = {}
227
228 # Don't waste network traffic if inputs are invalid
229 if not isinstance(code, basestring):
230 raise ValueError('code %r must be a string' % code)
231 validate_string_list(user_variables)
232 validate_string_dict(user_expressions)
233
180 234 # Create class for content/msg creation. Related to, but possibly
181 235 # not in Session.
182 content = dict(code=code, silent=silent)
236 content = dict(code=code, silent=silent,
237 user_variables=user_variables,
238 user_expressions=user_expressions)
183 239 msg = self.session.msg('execute_request', content)
184 240 self._queue_request(msg)
185 241 return msg['header']['msg_id']
@@ -249,17 +305,6 b' class XReqSocketChannel(ZmqSocketChannel):'
249 305 self._queue_request(msg)
250 306 return msg['header']['msg_id']
251 307
252 def prompt(self):
253 """Requests a prompt number from the kernel.
254
255 Returns
256 -------
257 The msg_id of the message sent.
258 """
259 msg = self.session.msg('prompt_request')
260 self._queue_request(msg)
261 return msg['header']['msg_id']
262
263 308 def _handle_events(self, socket, events):
264 309 if events & POLLERR:
265 310 self._handle_err()
@@ -479,9 +524,12 b' class HBSocketChannel(ZmqSocketChannel):'
479 524 since_last_heartbeat = 0.0
480 525 request_time = time.time()
481 526 try:
527 #io.rprint('Ping from HB channel') # dbg
482 528 self.socket.send_json('ping')
483 529 except zmq.ZMQError, e:
530 #io.rprint('*** HB Error:', e) # dbg
484 531 if e.errno == zmq.EFSM:
532 #io.rprint('sleep...', self.time_to_dead) # dbg
485 533 time.sleep(self.time_to_dead)
486 534 self._create_socket()
487 535 else:
@@ -489,13 +537,21 b' class HBSocketChannel(ZmqSocketChannel):'
489 537 else:
490 538 while True:
491 539 try:
492 reply = self.socket.recv_json(zmq.NOBLOCK)
540 self.socket.recv_json(zmq.NOBLOCK)
493 541 except zmq.ZMQError, e:
542 #io.rprint('*** HB Error 2:', e) # dbg
494 543 if e.errno == zmq.EAGAIN:
495 until_dead = self.time_to_dead - (time.time() -
544 before_poll = time.time()
545 until_dead = self.time_to_dead - (before_poll -
496 546 request_time)
497 # poll timeout is in milliseconds.
498 poll_result = self.poller.poll(1000*until_dead)
547
548 # When the return value of poll() is an empty list,
549 # that is when things have gone wrong (zeromq bug).
550 # As long as it is not an empty list, poll is
551 # working correctly even if it returns quickly.
552 # Note: poll timeout is in milliseconds.
553 self.poller.poll(1000*until_dead)
554
499 555 since_last_heartbeat = time.time() - request_time
500 556 if since_last_heartbeat > self.time_to_dead:
501 557 self.call_handlers(since_last_heartbeat)
@@ -507,6 +563,7 b' class HBSocketChannel(ZmqSocketChannel):'
507 563 until_dead = self.time_to_dead - (time.time() -
508 564 request_time)
509 565 if until_dead > 0.0:
566 #io.rprint('sleep...', self.time_to_dead) # dbg
510 567 time.sleep(until_dead)
511 568 break
512 569
@@ -61,10 +61,7 b' class ZMQDisplayHook(DisplayHook):'
61 61 def write_output_prompt(self):
62 62 """Write the output prompt."""
63 63 if self.do_full_cache:
64 self.msg['content']['output_sep'] = self.output_sep
65 self.msg['content']['prompt_string'] = str(self.prompt_out)
66 self.msg['content']['prompt_number'] = self.prompt_count
67 self.msg['content']['output_sep2'] = self.output_sep2
64 self.msg['content']['execution_count'] = self.prompt_count
68 65
69 66 def write_result_repr(self, result_repr):
70 67 self.msg['content']['data'] = result_repr
@@ -383,7 +380,6 b' class ZMQInteractiveShell(InteractiveShell):'
383 380 def _showtraceback(self, etype, evalue, stb):
384 381
385 382 exc_content = {
386 u'status' : u'error',
387 383 u'traceback' : stb,
388 384 u'ename' : unicode(etype.__name__),
389 385 u'evalue' : unicode(evalue)
@@ -397,7 +393,10 b' class ZMQInteractiveShell(InteractiveShell):'
397 393
398 394 # FIXME - Hack: store exception info in shell object. Right now, the
399 395 # caller is reading this info after the fact, we need to fix this logic
400 # to remove this hack.
396 # to remove this hack. Even uglier, we need to store the error status
397 # here, because in the main loop, the logic that sets it is being
398 # skipped because runlines swallows the exceptions.
399 exc_content[u'status'] = u'error'
401 400 self._reply_content = exc_content
402 401 # /FIXME
403 402
@@ -242,28 +242,27 b' Message type: ``execute_reply``::'
242 242 # prompt numbers to the user. If the request was a silent one, this will
243 243 # be the current value of the counter in the kernel.
244 244 'execution_count' : int,
245
246 # If the state_template was provided, this will contain the evaluated
247 # form of the template.
248 'state' : str,
249 245 }
250 246
251 247 When status is 'ok', the following extra fields are present::
252 248
253 249 {
254 # The kernel will often transform the input provided to it. If the
255 # '---->' transform had been applied, this is filled, otherwise it's the
256 # empty string. So transformations like magics don't appear here, only
257 # autocall ones.
258
259 'transformed_code' : str,
260
261 250 # The execution payload is a dict with string keys that may have been
262 251 # produced by the code being executed. It is retrieved by the kernel at
263 252 # the end of the execution and sent back to the front end, which can take
264 253 # action on it as needed. See main text for further details.
265 254 'payload' : dict,
266 }
255
256 # Results for the user_variables and user_expressions.
257 'user_variables' : dict,
258 'user_expressions' : dict,
259
260 # The kernel will often transform the input provided to it. If the
261 # '---->' transform had been applied, this is filled, otherwise it's the
262 # empty string. So transformations like magics don't appear here, only
263 # autocall ones.
264 'transformed_code' : str,
265 }
267 266
268 267 .. admonition:: Execution payloads
269 268
General Comments 0
You need to be logged in to leave comments. Login now