Show More
1 | NO CONTENT: new file 100644 |
|
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 | self.prefilter = self.prefilter_manager.prefilter_lines |
|
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 | # Things related to the running of code |
|
1746 | # Things related to the running of code | |
1708 | #------------------------------------------------------------------------- |
|
1747 | #------------------------------------------------------------------------- | |
1709 |
|
1748 |
@@ -131,10 +131,27 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
131 | complete = not self._input_splitter.push_accepts_more() |
|
131 | complete = not self._input_splitter.push_accepts_more() | |
132 | return complete |
|
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 | """ Execute 'source'. If 'hidden', do not show any output. |
|
136 | """ Execute 'source'. If 'hidden', do not show any output. | |
136 | """ |
|
137 | ||
137 | self.kernel_manager.xreq_channel.execute(source, hidden) |
|
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 | self._hidden = hidden |
|
155 | self._hidden = hidden | |
139 |
|
156 | |||
140 | def _prompt_started_hook(self): |
|
157 | def _prompt_started_hook(self): |
@@ -21,6 +21,7 b' from PyQt4 import QtCore, QtGui' | |||||
21 | # Local imports |
|
21 | # Local imports | |
22 | from IPython.core.inputsplitter import IPythonInputSplitter |
|
22 | from IPython.core.inputsplitter import IPythonInputSplitter | |
23 | from IPython.core.usage import default_banner |
|
23 | from IPython.core.usage import default_banner | |
|
24 | from IPython.utils import io | |||
24 | from IPython.utils.traitlets import Bool, Str |
|
25 | from IPython.utils.traitlets import Bool, Str | |
25 | from frontend_widget import FrontendWidget |
|
26 | from frontend_widget import FrontendWidget | |
26 |
|
27 | |||
@@ -50,9 +51,13 b" default_dark_style_sheet = '''" | |||||
50 | ''' |
|
51 | ''' | |
51 | default_dark_syntax_style = 'monokai' |
|
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 | default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: ' |
|
56 | default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: ' | |
55 | default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: ' |
|
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 | # IPythonWidget class |
|
63 | # IPythonWidget class | |
@@ -92,6 +97,9 b' class IPythonWidget(FrontendWidget):' | |||||
92 | # Prompts. |
|
97 | # Prompts. | |
93 | in_prompt = Str(default_in_prompt, config=True) |
|
98 | in_prompt = Str(default_in_prompt, config=True) | |
94 | out_prompt = Str(default_out_prompt, config=True) |
|
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 | # FrontendWidget protected class variables. |
|
104 | # FrontendWidget protected class variables. | |
97 | _input_splitter_class = IPythonInputSplitter |
|
105 | _input_splitter_class = IPythonInputSplitter | |
@@ -155,20 +163,17 b' class IPythonWidget(FrontendWidget):' | |||||
155 | """ Implemented to handle prompt number replies, which are only |
|
163 | """ Implemented to handle prompt number replies, which are only | |
156 | supported by the IPython kernel. |
|
164 | supported by the IPython kernel. | |
157 | """ |
|
165 | """ | |
158 | content = msg['content'] |
|
166 | self._show_interpreter_prompt(msg['content']['execution_count']) | |
159 | self._show_interpreter_prompt(content['prompt_number'], |
|
|||
160 | content['input_sep']) |
|
|||
161 |
|
167 | |||
162 | def _handle_pyout(self, msg): |
|
168 | def _handle_pyout(self, msg): | |
163 | """ Reimplemented for IPython-style "display hook". |
|
169 | """ Reimplemented for IPython-style "display hook". | |
164 | """ |
|
170 | """ | |
165 | if not self._hidden and self._is_from_this_session(msg): |
|
171 | if not self._hidden and self._is_from_this_session(msg): | |
166 | content = msg['content'] |
|
172 | content = msg['content'] | |
167 |
prompt_number = content[' |
|
173 | prompt_number = content['execution_count'] | |
168 |
self._append_plain_text( |
|
174 | self._append_plain_text(self.output_sep) | |
169 | self._append_html(self._make_out_prompt(prompt_number)) |
|
175 | self._append_html(self._make_out_prompt(prompt_number)) | |
170 |
self._append_plain_text(content['data'] |
|
176 | self._append_plain_text(content['data']+self.output_sep2) | |
171 | content['output_sep2']) |
|
|||
172 |
|
177 | |||
173 | def _started_channels(self): |
|
178 | def _started_channels(self): | |
174 | """ Reimplemented to make a history request. |
|
179 | """ Reimplemented to make a history request. | |
@@ -244,17 +249,19 b' class IPythonWidget(FrontendWidget):' | |||||
244 | else: |
|
249 | else: | |
245 | return False |
|
250 | return False | |
246 |
|
251 | |||
247 |
def _show_interpreter_prompt(self, number=None |
|
252 | def _show_interpreter_prompt(self, number=None): | |
248 | """ Reimplemented for IPython-style prompts. |
|
253 | """ Reimplemented for IPython-style prompts. | |
249 | """ |
|
254 | """ | |
250 | # If a number was not specified, make a prompt number request. |
|
255 | # If a number was not specified, make a prompt number request. | |
251 | if number is None: |
|
256 | if number is None: | |
252 | self.kernel_manager.xreq_channel.prompt() |
|
257 | # FIXME - fperez: this should be a silent code request | |
253 |
|
|
258 | number = 1 | |
|
259 | ##self.kernel_manager.xreq_channel.prompt() | |||
|
260 | ##return | |||
254 |
|
261 | |||
255 | # Show a new prompt and save information about it so that it can be |
|
262 | # Show a new prompt and save information about it so that it can be | |
256 | # updated later if the prompt number turns out to be wrong. |
|
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 | self._show_prompt(self._make_in_prompt(number), html=True) |
|
265 | self._show_prompt(self._make_in_prompt(number), html=True) | |
259 | block = self._control.document().lastBlock() |
|
266 | block = self._control.document().lastBlock() | |
260 | length = len(self._prompt) |
|
267 | length = len(self._prompt) | |
@@ -269,7 +276,8 b' class IPythonWidget(FrontendWidget):' | |||||
269 | """ |
|
276 | """ | |
270 | # Update the old prompt number if necessary. |
|
277 | # Update the old prompt number if necessary. | |
271 | content = msg['content'] |
|
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 | if self._previous_prompt_obj and \ |
|
281 | if self._previous_prompt_obj and \ | |
274 | self._previous_prompt_obj.number != previous_prompt_number: |
|
282 | self._previous_prompt_obj.number != previous_prompt_number: | |
275 | block = self._previous_prompt_obj.block |
|
283 | block = self._previous_prompt_obj.block | |
@@ -293,9 +301,7 b' class IPythonWidget(FrontendWidget):' | |||||
293 | self._previous_prompt_obj = None |
|
301 | self._previous_prompt_obj = None | |
294 |
|
302 | |||
295 | # Show a new prompt with the kernel's estimated prompt number. |
|
303 | # Show a new prompt with the kernel's estimated prompt number. | |
296 | next_prompt = content['next_prompt'] |
|
304 | self._show_interpreter_prompt(previous_prompt_number+1) | |
297 | self._show_interpreter_prompt(next_prompt['prompt_number'], |
|
|||
298 | next_prompt['input_sep']) |
|
|||
299 |
|
305 | |||
300 | #--------------------------------------------------------------------------- |
|
306 | #--------------------------------------------------------------------------- | |
301 | # 'IPythonWidget' interface |
|
307 | # 'IPythonWidget' interface |
@@ -63,7 +63,8 b' def process_handler(cmd, callback, stderr=subprocess.PIPE):' | |||||
63 | """ |
|
63 | """ | |
64 | sys.stdout.flush() |
|
64 | sys.stdout.flush() | |
65 | sys.stderr.flush() |
|
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 | p = subprocess.Popen(cmd, shell=True, |
|
68 | p = subprocess.Popen(cmd, shell=True, | |
68 | stdin=subprocess.PIPE, |
|
69 | stdin=subprocess.PIPE, | |
69 | stdout=subprocess.PIPE, |
|
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 | from Queue import Queue, Empty |
|
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): |
|
24 | from .kernelmanager import (KernelManager, SubSocketChannel, | |
6 | pass |
|
25 | XReqSocketChannel, RepSocketChannel, HBSocketChannel) | |
7 |
|
26 | |||
|
27 | #----------------------------------------------------------------------------- | |||
|
28 | # Functions and classes | |||
|
29 | #----------------------------------------------------------------------------- | |||
8 |
|
30 | |||
9 | class BlockingSubSocketChannel(SubSocketChannel): |
|
31 | class BlockingSubSocketChannel(SubSocketChannel): | |
10 |
|
32 | |||
@@ -13,6 +35,7 b' class BlockingSubSocketChannel(SubSocketChannel):' | |||||
13 | self._in_queue = Queue() |
|
35 | self._in_queue = Queue() | |
14 |
|
36 | |||
15 | def call_handlers(self, msg): |
|
37 | def call_handlers(self, msg): | |
|
38 | io.rprint('[[Sub]]', msg) # dbg | |||
16 | self._in_queue.put(msg) |
|
39 | self._in_queue.put(msg) | |
17 |
|
40 | |||
18 | def msg_ready(self): |
|
41 | def msg_ready(self): | |
@@ -24,21 +47,68 b' class BlockingSubSocketChannel(SubSocketChannel):' | |||||
24 |
|
47 | |||
25 | def get_msg(self, block=True, timeout=None): |
|
48 | def get_msg(self, block=True, timeout=None): | |
26 | """Get a message if there is one that is ready.""" |
|
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 | try: |
|
56 | try: | |
28 | msg = self.in_queue.get(block, timeout) |
|
57 | msgs.append(self.get_msg(block=False)) | |
29 | except Empty: |
|
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 | else: |
|
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 | def get_msgs(self): |
|
84 | def get_msgs(self): | |
35 | """Get all messages that are currently ready.""" |
|
85 | """Get all messages that are currently ready.""" | |
36 | msgs = [] |
|
86 | msgs = [] | |
37 | while True: |
|
87 | while True: | |
38 | try: |
|
88 | try: | |
39 |
msg |
|
89 | msgs.append(self.get_msg(block=False)) | |
40 |
except |
|
90 | except Empty: | |
41 | break |
|
91 | break | |
42 | else: |
|
|||
43 | msgs.append(msg) |
|
|||
44 | return msgs |
|
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 | from session import Session, Message |
|
35 | from session import Session, Message | |
36 | from zmqshell import ZMQInteractiveShell |
|
36 | from zmqshell import ZMQInteractiveShell | |
37 |
|
37 | |||
|
38 | ||||
38 | #----------------------------------------------------------------------------- |
|
39 | #----------------------------------------------------------------------------- | |
39 | # Main kernel class |
|
40 | # Main kernel class | |
40 | #----------------------------------------------------------------------------- |
|
41 | #----------------------------------------------------------------------------- | |
@@ -64,8 +65,7 b' class Kernel(Configurable):' | |||||
64 |
|
65 | |||
65 | # Build dict of handlers for message types |
|
66 | # Build dict of handlers for message types | |
66 | msg_types = [ 'execute_request', 'complete_request', |
|
67 | msg_types = [ 'execute_request', 'complete_request', | |
67 |
'object_info_request', ' |
|
68 | 'object_info_request', 'history_request' ] | |
68 | 'history_request' ] |
|
|||
69 | self.handlers = {} |
|
69 | self.handlers = {} | |
70 | for msg_type in msg_types: |
|
70 | for msg_type in msg_types: | |
71 | self.handlers[msg_type] = getattr(self, msg_type) |
|
71 | self.handlers[msg_type] = getattr(self, msg_type) | |
@@ -81,14 +81,21 b' class Kernel(Configurable):' | |||||
81 | # FIXME: Bug in pyzmq/zmq? |
|
81 | # FIXME: Bug in pyzmq/zmq? | |
82 | # assert self.reply_socket.rcvmore(), "Missing message part." |
|
82 | # assert self.reply_socket.rcvmore(), "Missing message part." | |
83 | msg = self.reply_socket.recv_json() |
|
83 | msg = self.reply_socket.recv_json() | |
84 | omsg = Message(msg) |
|
84 | ||
85 | io.raw_print('\n') |
|
85 | # Print some info about this message and leave a '--->' marker, so it's | |
86 | io.raw_print(omsg) |
|
86 | # easier to trace visually the message chain when debugging. Each | |
87 | handler = self.handlers.get(omsg.msg_type, None) |
|
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 | if handler is None: |
|
95 | if handler is None: | |
89 |
io.raw_print_err("UNKNOWN MESSAGE TYPE:", |
|
96 | io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg) | |
90 | else: |
|
97 | else: | |
91 |
handler(ident, |
|
98 | handler(ident, msg) | |
92 |
|
99 | |||
93 | def start(self): |
|
100 | def start(self): | |
94 | """ Start the kernel main loop. |
|
101 | """ Start the kernel main loop. | |
@@ -97,37 +104,56 b' class Kernel(Configurable):' | |||||
97 | time.sleep(0.05) |
|
104 | time.sleep(0.05) | |
98 | self.do_one_iteration() |
|
105 | self.do_one_iteration() | |
99 |
|
106 | |||
100 |
|
||||
101 | #--------------------------------------------------------------------------- |
|
107 | #--------------------------------------------------------------------------- | |
102 | # Kernel request handlers |
|
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 | def execute_request(self, ident, parent): |
|
117 | def execute_request(self, ident, parent): | |
106 | try: |
|
118 | try: | |
107 |
co |
|
119 | content = parent[u'content'] | |
|
120 | code = content[u'code'] | |||
|
121 | silent = content[u'silent'] | |||
108 | except: |
|
122 | except: | |
109 | io.raw_print_err("Got bad msg: ") |
|
123 | io.raw_print_err("Got bad msg: ") | |
110 | io.raw_print_err(Message(parent)) |
|
124 | io.raw_print_err(Message(parent)) | |
111 | return |
|
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 | # Replace raw_input. Note that is not sufficient to replace | |
117 |
|
|
130 | # raw_input in the user namespace. | |
118 |
|
|
131 | raw_input = lambda prompt='': self._raw_input(prompt, ident, parent) | |
119 |
|
|
132 | __builtin__.raw_input = raw_input | |
120 |
|
133 | |||
121 |
|
|
134 | # Set the parent message of the display hook and out streams. | |
122 |
|
|
135 | shell.displayhook.set_parent(parent) | |
123 |
|
|
136 | sys.stdout.set_parent(parent) | |
124 |
|
|
137 | sys.stderr.set_parent(parent) | |
125 |
|
138 | |||
126 | # FIXME: runlines calls the exception handler itself. We should |
|
139 | # Re-broadcast our input for the benefit of listening clients, and | |
127 | # clean this up. |
|
140 | # start computing output | |
128 | self.shell._reply_content = None |
|
141 | if not silent: | |
129 |
self. |
|
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 | except: |
|
155 | except: | |
|
156 | status = u'error' | |||
131 | # FIXME: this code right now isn't being used yet by default, |
|
157 | # FIXME: this code right now isn't being used yet by default, | |
132 | # because the runlines() call above directly fires off exception |
|
158 | # because the runlines() call above directly fires off exception | |
133 | # reporting. This code, therefore, is only active in the scenario |
|
159 | # reporting. This code, therefore, is only active in the scenario | |
@@ -136,35 +162,39 b' class Kernel(Configurable):' | |||||
136 | # single location in the codbase. |
|
162 | # single location in the codbase. | |
137 | etype, evalue, tb = sys.exc_info() |
|
163 | etype, evalue, tb = sys.exc_info() | |
138 | tb_list = traceback.format_exception(etype, evalue, tb) |
|
164 | tb_list = traceback.format_exception(etype, evalue, tb) | |
139 |
reply_content |
|
165 | reply_content.update(shell._showtraceback(etype, evalue, tb_list)) | |
140 | else: |
|
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 | # Be agressive about clearing the payload because we don't want |
|
169 | # Be agressive about clearing the payload because we don't want | |
143 | # it to sit in memory until the next execute_request comes in. |
|
170 | # it to sit in memory until the next execute_request comes in. | |
144 |
|
|
171 | shell.payload_manager.clear_payload() | |
145 | reply_content = { 'status' : 'ok', 'payload' : payload } |
|
172 | ||
146 |
|
173 | reply_content[u'status'] = status | ||
147 | # Compute the prompt information |
|
174 | # Compute the execution counter so clients can display prompts | |
148 |
|
|
175 | reply_content['execution_count'] = shell.displayhook.prompt_count | |
149 | reply_content['prompt_number'] = prompt_number |
|
176 | ||
150 | prompt_string = self.shell.displayhook.prompt1.peek_next_prompt() |
|
177 | # FIXME - fish exception info out of shell, possibly left there by | |
151 | next_prompt = {'prompt_string' : prompt_string, |
|
178 | # runlines. We'll need to clean up this logic later. | |
152 | 'prompt_number' : prompt_number+1, |
|
179 | if shell._reply_content is not None: | |
153 | 'input_sep' : self.shell.displayhook.input_sep} |
|
180 | reply_content.update(shell._reply_content) | |
154 | reply_content['next_prompt'] = next_prompt |
|
181 | ||
155 |
|
182 | # At this point, we can tell whether the main code execution succeeded | ||
156 | # TMP - fish exception info out of shell, possibly left there by |
|
183 | # or not. If it did, we proceed to evaluate user_variables/expressions | |
157 | # runlines |
|
184 | if reply_content['status'] == 'ok': | |
158 | if self.shell._reply_content is not None: |
|
185 | reply_content[u'user_variables'] = \ | |
159 | reply_content.update(self.shell._reply_content) |
|
186 | shell.get_user_variables(content[u'user_variables']) | |
160 |
|
187 | reply_content[u'user_expressions'] = \ | ||
161 | # Flush output before sending the reply. |
|
188 | shell.eval_expressions(content[u'user_expressions']) | |
162 | sys.stderr.flush() |
|
189 | else: | |
163 | sys.stdout.flush() |
|
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 | # Send the reply. |
|
195 | # Send the reply. | |
166 | reply_msg = self.session.msg(u'execute_reply', reply_content, parent) |
|
196 | reply_msg = self.session.msg(u'execute_reply', reply_content, parent) | |
167 |
io.raw_print( |
|
197 | io.raw_print(reply_msg) | |
168 | self.reply_socket.send(ident, zmq.SNDMORE) |
|
198 | self.reply_socket.send(ident, zmq.SNDMORE) | |
169 | self.reply_socket.send_json(reply_msg) |
|
199 | self.reply_socket.send_json(reply_msg) | |
170 | if reply_msg['content']['status'] == u'error': |
|
200 | if reply_msg['content']['status'] == u'error': | |
@@ -186,16 +216,6 b' class Kernel(Configurable):' | |||||
186 | object_info, parent, ident) |
|
216 | object_info, parent, ident) | |
187 | io.raw_print(msg) |
|
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 | def history_request(self, ident, parent): |
|
219 | def history_request(self, ident, parent): | |
200 | output = parent['content']['output'] |
|
220 | output = parent['content']['output'] | |
201 | index = parent['content']['index'] |
|
221 | index = parent['content']['index'] | |
@@ -218,13 +238,14 b' class Kernel(Configurable):' | |||||
218 | if e.errno == zmq.EAGAIN: |
|
238 | if e.errno == zmq.EAGAIN: | |
219 | break |
|
239 | break | |
220 | else: |
|
240 | else: | |
221 |
assert self.reply_socket.rcvmore(), |
|
241 | assert self.reply_socket.rcvmore(), \ | |
|
242 | "Unexpected missing message part." | |||
222 | msg = self.reply_socket.recv_json() |
|
243 | msg = self.reply_socket.recv_json() | |
223 | io.raw_print("Aborting:\n", Message(msg)) |
|
244 | io.raw_print("Aborting:\n", Message(msg)) | |
224 | msg_type = msg['msg_type'] |
|
245 | msg_type = msg['msg_type'] | |
225 | reply_type = msg_type.split('_')[0] + '_reply' |
|
246 | reply_type = msg_type.split('_')[0] + '_reply' | |
226 | reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg) |
|
247 | reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg) | |
227 |
io.raw_print( |
|
248 | io.raw_print(reply_msg) | |
228 | self.reply_socket.send(ident,zmq.SNDMORE) |
|
249 | self.reply_socket.send(ident,zmq.SNDMORE) | |
229 | self.reply_socket.send_json(reply_msg) |
|
250 | self.reply_socket.send_json(reply_msg) | |
230 | # We need to wait a bit for requests to come in. This can probably |
|
251 | # We need to wait a bit for requests to come in. This can probably | |
@@ -312,6 +333,7 b' class QtKernel(Kernel):' | |||||
312 | self.timer.start(50) |
|
333 | self.timer.start(50) | |
313 | start_event_loop_qt4(self.app) |
|
334 | start_event_loop_qt4(self.app) | |
314 |
|
335 | |||
|
336 | ||||
315 | class WxKernel(Kernel): |
|
337 | class WxKernel(Kernel): | |
316 | """A Kernel subclass with Wx support.""" |
|
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 | xrep_port, pub_port, req_port, hb_port, |
|
443 | xrep_port, pub_port, req_port, hb_port, | |
422 | independent, extra_arguments) |
|
444 | independent, extra_arguments) | |
423 |
|
445 | |||
|
446 | ||||
424 | def main(): |
|
447 | def main(): | |
425 | """ The IPython kernel main entry point. |
|
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 | start_kernel(namespace, kernel) |
|
482 | start_kernel(namespace, kernel) | |
460 |
|
483 | |||
|
484 | ||||
461 | if __name__ == '__main__': |
|
485 | if __name__ == '__main__': | |
462 | main() |
|
486 | main() |
@@ -29,6 +29,7 b' from zmq import POLLIN, POLLOUT, POLLERR' | |||||
29 | from zmq.eventloop import ioloop |
|
29 | from zmq.eventloop import ioloop | |
30 |
|
30 | |||
31 | # Local imports. |
|
31 | # Local imports. | |
|
32 | from IPython.utils import io | |||
32 | from IPython.utils.traitlets import HasTraits, Any, Instance, Type, TCPAddress |
|
33 | from IPython.utils.traitlets import HasTraits, Any, Instance, Type, TCPAddress | |
33 | from session import Session |
|
34 | from session import Session | |
34 |
|
35 | |||
@@ -42,6 +43,35 b' class InvalidPortNumber(Exception):' | |||||
42 | pass |
|
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 | # ZMQ Socket Channel classes |
|
75 | # ZMQ Socket Channel classes | |
46 | #----------------------------------------------------------------------------- |
|
76 | #----------------------------------------------------------------------------- | |
47 |
|
77 | |||
@@ -163,23 +193,49 b' class XReqSocketChannel(ZmqSocketChannel):' | |||||
163 | """ |
|
193 | """ | |
164 | raise NotImplementedError('call_handlers must be defined in a subclass.') |
|
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 | """Execute code in the kernel. |
|
198 | """Execute code in the kernel. | |
168 |
|
199 | |||
169 | Parameters |
|
200 | Parameters | |
170 | ---------- |
|
201 | ---------- | |
171 | code : str |
|
202 | code : str | |
172 | A string of Python code. |
|
203 | A string of Python code. | |
|
204 | ||||
173 | silent : bool, optional (default False) |
|
205 | silent : bool, optional (default False) | |
174 | If set, the kernel will execute the code as quietly possible. |
|
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 | Returns |
|
219 | Returns | |
177 | ------- |
|
220 | ------- | |
178 | The msg_id of the message sent. |
|
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 | # Create class for content/msg creation. Related to, but possibly |
|
234 | # Create class for content/msg creation. Related to, but possibly | |
181 | # not in Session. |
|
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 | msg = self.session.msg('execute_request', content) |
|
239 | msg = self.session.msg('execute_request', content) | |
184 | self._queue_request(msg) |
|
240 | self._queue_request(msg) | |
185 | return msg['header']['msg_id'] |
|
241 | return msg['header']['msg_id'] | |
@@ -249,17 +305,6 b' class XReqSocketChannel(ZmqSocketChannel):' | |||||
249 | self._queue_request(msg) |
|
305 | self._queue_request(msg) | |
250 | return msg['header']['msg_id'] |
|
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 | def _handle_events(self, socket, events): |
|
308 | def _handle_events(self, socket, events): | |
264 | if events & POLLERR: |
|
309 | if events & POLLERR: | |
265 | self._handle_err() |
|
310 | self._handle_err() | |
@@ -479,9 +524,12 b' class HBSocketChannel(ZmqSocketChannel):' | |||||
479 | since_last_heartbeat = 0.0 |
|
524 | since_last_heartbeat = 0.0 | |
480 | request_time = time.time() |
|
525 | request_time = time.time() | |
481 | try: |
|
526 | try: | |
|
527 | #io.rprint('Ping from HB channel') # dbg | |||
482 | self.socket.send_json('ping') |
|
528 | self.socket.send_json('ping') | |
483 | except zmq.ZMQError, e: |
|
529 | except zmq.ZMQError, e: | |
|
530 | #io.rprint('*** HB Error:', e) # dbg | |||
484 | if e.errno == zmq.EFSM: |
|
531 | if e.errno == zmq.EFSM: | |
|
532 | #io.rprint('sleep...', self.time_to_dead) # dbg | |||
485 | time.sleep(self.time_to_dead) |
|
533 | time.sleep(self.time_to_dead) | |
486 | self._create_socket() |
|
534 | self._create_socket() | |
487 | else: |
|
535 | else: | |
@@ -489,13 +537,21 b' class HBSocketChannel(ZmqSocketChannel):' | |||||
489 | else: |
|
537 | else: | |
490 | while True: |
|
538 | while True: | |
491 | try: |
|
539 | try: | |
492 |
|
|
540 | self.socket.recv_json(zmq.NOBLOCK) | |
493 | except zmq.ZMQError, e: |
|
541 | except zmq.ZMQError, e: | |
|
542 | #io.rprint('*** HB Error 2:', e) # dbg | |||
494 | if e.errno == zmq.EAGAIN: |
|
543 | if e.errno == zmq.EAGAIN: | |
495 |
|
|
544 | before_poll = time.time() | |
|
545 | until_dead = self.time_to_dead - (before_poll - | |||
496 | request_time) |
|
546 | request_time) | |
497 | # poll timeout is in milliseconds. |
|
547 | ||
498 | poll_result = self.poller.poll(1000*until_dead) |
|
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 | since_last_heartbeat = time.time() - request_time |
|
555 | since_last_heartbeat = time.time() - request_time | |
500 | if since_last_heartbeat > self.time_to_dead: |
|
556 | if since_last_heartbeat > self.time_to_dead: | |
501 | self.call_handlers(since_last_heartbeat) |
|
557 | self.call_handlers(since_last_heartbeat) | |
@@ -507,6 +563,7 b' class HBSocketChannel(ZmqSocketChannel):' | |||||
507 | until_dead = self.time_to_dead - (time.time() - |
|
563 | until_dead = self.time_to_dead - (time.time() - | |
508 | request_time) |
|
564 | request_time) | |
509 | if until_dead > 0.0: |
|
565 | if until_dead > 0.0: | |
|
566 | #io.rprint('sleep...', self.time_to_dead) # dbg | |||
510 | time.sleep(until_dead) |
|
567 | time.sleep(until_dead) | |
511 | break |
|
568 | break | |
512 |
|
569 |
@@ -61,10 +61,7 b' class ZMQDisplayHook(DisplayHook):' | |||||
61 | def write_output_prompt(self): |
|
61 | def write_output_prompt(self): | |
62 | """Write the output prompt.""" |
|
62 | """Write the output prompt.""" | |
63 | if self.do_full_cache: |
|
63 | if self.do_full_cache: | |
64 |
self.msg['content'][' |
|
64 | self.msg['content']['execution_count'] = self.prompt_count | |
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 |
|
|||
68 |
|
65 | |||
69 | def write_result_repr(self, result_repr): |
|
66 | def write_result_repr(self, result_repr): | |
70 | self.msg['content']['data'] = result_repr |
|
67 | self.msg['content']['data'] = result_repr | |
@@ -383,7 +380,6 b' class ZMQInteractiveShell(InteractiveShell):' | |||||
383 | def _showtraceback(self, etype, evalue, stb): |
|
380 | def _showtraceback(self, etype, evalue, stb): | |
384 |
|
381 | |||
385 | exc_content = { |
|
382 | exc_content = { | |
386 | u'status' : u'error', |
|
|||
387 | u'traceback' : stb, |
|
383 | u'traceback' : stb, | |
388 | u'ename' : unicode(etype.__name__), |
|
384 | u'ename' : unicode(etype.__name__), | |
389 | u'evalue' : unicode(evalue) |
|
385 | u'evalue' : unicode(evalue) | |
@@ -397,7 +393,10 b' class ZMQInteractiveShell(InteractiveShell):' | |||||
397 |
|
393 | |||
398 | # FIXME - Hack: store exception info in shell object. Right now, the |
|
394 | # FIXME - Hack: store exception info in shell object. Right now, the | |
399 | # caller is reading this info after the fact, we need to fix this logic |
|
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 | self._reply_content = exc_content |
|
400 | self._reply_content = exc_content | |
402 | # /FIXME |
|
401 | # /FIXME | |
403 |
|
402 |
@@ -242,27 +242,26 b' Message type: ``execute_reply``::' | |||||
242 | # prompt numbers to the user. If the request was a silent one, this will |
|
242 | # prompt numbers to the user. If the request was a silent one, this will | |
243 | # be the current value of the counter in the kernel. |
|
243 | # be the current value of the counter in the kernel. | |
244 | 'execution_count' : int, |
|
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 | When status is 'ok', the following extra fields are present:: |
|
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 | # The execution payload is a dict with string keys that may have been |
|
250 | # The execution payload is a dict with string keys that may have been | |
262 | # produced by the code being executed. It is retrieved by the kernel at |
|
251 | # produced by the code being executed. It is retrieved by the kernel at | |
263 | # the end of the execution and sent back to the front end, which can take |
|
252 | # the end of the execution and sent back to the front end, which can take | |
264 | # action on it as needed. See main text for further details. |
|
253 | # action on it as needed. See main text for further details. | |
265 | 'payload' : dict, |
|
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 | .. admonition:: Execution payloads |
|
267 | .. admonition:: Execution payloads |
General Comments 0
You need to be logged in to leave comments.
Login now