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[' |
|
|
168 |
self._append_plain_text( |
|
|
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'] |
|
|
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 |
|
|
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 |
|
|
|
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.""" |
|
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: | |
|
27 | 56 | try: |
|
28 | msg = self.in_queue.get(block, timeout) | |
|
57 | msgs.append(self.get_msg(block=False)) | |
|
29 | 58 | except Empty: |
|
30 | raise MsgNotReady('No message has been received.') | |
|
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 |
|
|
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 |
|
|
40 |
except |
|
|
89 | msgs.append(self.get_msg(block=False)) | |
|
90 | except Empty: | |
|
41 | 91 | break |
|
42 | else: | |
|
43 | msgs.append(msg) | |
|
44 | 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', ' |
|
|
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:", |
|
|
96 | io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg) | |
|
90 | 97 | else: |
|
91 |
handler(ident, |
|
|
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 |
co |
|
|
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 | |
|
115 | try: | |
|
127 | shell = self.shell # we'll need this a lot here | |
|
128 | ||
|
116 | 129 |
|
|
117 | 130 |
|
|
118 | 131 |
|
|
119 | 132 |
|
|
120 | 133 | |
|
121 | 134 |
|
|
122 |
|
|
|
135 | shell.displayhook.set_parent(parent) | |
|
123 | 136 |
|
|
124 | 137 |
|
|
125 | 138 | |
|
126 | # FIXME: runlines calls the exception handler itself. We should | |
|
127 | # clean this up. | |
|
128 | self.shell._reply_content = None | |
|
129 |
self. |
|
|
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 = {} | |
|
145 | try: | |
|
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 |
|
|
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 |
|
|
|
145 | reply_content = { 'status' : 'ok', 'payload' : payload } | |
|
146 | ||
|
147 | # Compute the prompt information | |
|
148 |
|
|
|
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() | |
|
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'] = {} | |
|
164 | 194 | |
|
165 | 195 | # Send the reply. |
|
166 | 196 | reply_msg = self.session.msg(u'execute_reply', reply_content, parent) |
|
167 |
io.raw_print( |
|
|
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(), |
|
|
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( |
|
|
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 |
|
|
|
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 |
|
|
|
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'][' |
|
|
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,27 +242,26 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, |
|
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, | |
|
266 | 265 | } |
|
267 | 266 | |
|
268 | 267 | .. admonition:: Execution payloads |
General Comments 0
You need to be logged in to leave comments.
Login now