Show More
@@ -136,15 +136,23 b' class BaseFrontendMixin(object):' | |||||
136 | handler = getattr(self, '_handle_' + msg_type, None) |
|
136 | handler = getattr(self, '_handle_' + msg_type, None) | |
137 | if handler: |
|
137 | if handler: | |
138 | handler(msg) |
|
138 | handler(msg) | |
139 |
|
139 | |||
140 |
def |
|
140 | def from_here(self, msg): | |
141 | """ Returns whether a reply from the kernel originated from a request |
|
141 | """Return whether a message is from this session""" | |
142 | from this frontend. |
|
142 | session_id = self._kernel_client.session.session | |
143 | """ |
|
143 | return msg['parent_header'].get("session", session_id) == session_id | |
144 | session = self._kernel_client.session.session |
|
144 | ||
145 | parent = msg['parent_header'] |
|
145 | def include_output(self, msg): | |
146 | if not parent: |
|
146 | """Return whether we should include a given output message""" | |
147 | # if the message has no parent, assume it is meant for all frontends |
|
147 | if self._hidden: | |
|
148 | return False | |||
|
149 | from_here = self.from_here(msg) | |||
|
150 | if msg['msg_type'] == 'execute_input': | |||
|
151 | # only echo inputs not from here | |||
|
152 | return self.include_other_output and not from_here | |||
|
153 | ||||
|
154 | if self.include_other_output: | |||
148 | return True |
|
155 | return True | |
149 | else: |
|
156 | else: | |
150 | return parent.get('session') == session |
|
157 | return from_here | |
|
158 |
@@ -519,7 +519,15 b" class ConsoleWidget(MetaQObjectHasTraits('NewBase', (LoggingConfigurable, QtGui." | |||||
519 | #--------------------------------------------------------------------------- |
|
519 | #--------------------------------------------------------------------------- | |
520 | # 'ConsoleWidget' public interface |
|
520 | # 'ConsoleWidget' public interface | |
521 | #--------------------------------------------------------------------------- |
|
521 | #--------------------------------------------------------------------------- | |
522 |
|
522 | |||
|
523 | include_other_output = Bool(False, config=True, | |||
|
524 | help="""Whether to include output from clients | |||
|
525 | other than this one sharing the same kernel. | |||
|
526 | ||||
|
527 | Outputs are not displayed until enter is pressed. | |||
|
528 | """ | |||
|
529 | ) | |||
|
530 | ||||
523 | def can_copy(self): |
|
531 | def can_copy(self): | |
524 | """ Returns whether text can be copied to the clipboard. |
|
532 | """ Returns whether text can be copied to the clipboard. | |
525 | """ |
|
533 | """ |
@@ -348,7 +348,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
348 | #--------------------------------------------------------------------------- |
|
348 | #--------------------------------------------------------------------------- | |
349 | def _handle_clear_output(self, msg): |
|
349 | def _handle_clear_output(self, msg): | |
350 | """Handle clear output messages.""" |
|
350 | """Handle clear output messages.""" | |
351 | if not self._hidden and self._is_from_this_session(msg): |
|
351 | if include_output(msg): | |
352 | wait = msg['content'].get('wait', True) |
|
352 | wait = msg['content'].get('wait', True) | |
353 | if wait: |
|
353 | if wait: | |
354 | self._pending_clearoutput = True |
|
354 | self._pending_clearoutput = True | |
@@ -509,7 +509,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
509 | """ Handle display hook output. |
|
509 | """ Handle display hook output. | |
510 | """ |
|
510 | """ | |
511 | self.log.debug("execute_result: %s", msg.get('content', '')) |
|
511 | self.log.debug("execute_result: %s", msg.get('content', '')) | |
512 | if not self._hidden and self._is_from_this_session(msg): |
|
512 | if self.include_output(msg): | |
513 | self.flush_clearoutput() |
|
513 | self.flush_clearoutput() | |
514 | text = msg['content']['data'] |
|
514 | text = msg['content']['data'] | |
515 | self._append_plain_text(text + '\n', before_prompt=True) |
|
515 | self._append_plain_text(text + '\n', before_prompt=True) | |
@@ -518,7 +518,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
518 | """ Handle stdout, stderr, and stdin. |
|
518 | """ Handle stdout, stderr, and stdin. | |
519 | """ |
|
519 | """ | |
520 | self.log.debug("stream: %s", msg.get('content', '')) |
|
520 | self.log.debug("stream: %s", msg.get('content', '')) | |
521 | if not self._hidden and self._is_from_this_session(msg): |
|
521 | if self.include_output(msg): | |
522 | self.flush_clearoutput() |
|
522 | self.flush_clearoutput() | |
523 | self.append_stream(msg['content']['text']) |
|
523 | self.append_stream(msg['content']['text']) | |
524 |
|
524 | |||
@@ -527,7 +527,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
527 | """ |
|
527 | """ | |
528 | self.log.info("shutdown: %s", msg.get('content', '')) |
|
528 | self.log.info("shutdown: %s", msg.get('content', '')) | |
529 | restart = msg.get('content', {}).get('restart', False) |
|
529 | restart = msg.get('content', {}).get('restart', False) | |
530 |
if not self._hidden and not self. |
|
530 | if not self._hidden and not self.from_here(msg): | |
531 | # got shutdown reply, request came from session other than ours |
|
531 | # got shutdown reply, request came from session other than ours | |
532 | if restart: |
|
532 | if restart: | |
533 | # someone restarted the kernel, handle it |
|
533 | # someone restarted the kernel, handle it |
@@ -219,12 +219,30 b' class IPythonWidget(FrontendWidget):' | |||||
219 | items.append(cell) |
|
219 | items.append(cell) | |
220 | last_cell = cell |
|
220 | last_cell = cell | |
221 | self._set_history(items) |
|
221 | self._set_history(items) | |
|
222 | ||||
|
223 | def _insert_other_input(self, cursor, content): | |||
|
224 | """Insert function for input from other frontends""" | |||
|
225 | cursor.beginEditBlock() | |||
|
226 | start = cursor.position() | |||
|
227 | n = content.get('execution_count', 0) | |||
|
228 | cursor.insertText('\n') | |||
|
229 | self._insert_html(cursor, self._make_in_prompt(n)) | |||
|
230 | cursor.insertText(content['code']) | |||
|
231 | self._highlighter.rehighlightBlock(cursor.block()) | |||
|
232 | cursor.endEditBlock() | |||
|
233 | ||||
|
234 | def _handle_execute_input(self, msg): | |||
|
235 | """Handle an execute_input message""" | |||
|
236 | self.log.debug("execute_input: %s", msg.get('content', '')) | |||
|
237 | if self.include_output(msg): | |||
|
238 | self._append_custom(self._insert_other_input, msg['content'], before_prompt=True) | |||
222 |
|
239 | |||
|
240 | ||||
223 | def _handle_execute_result(self, msg): |
|
241 | def _handle_execute_result(self, msg): | |
224 | """ Reimplemented for IPython-style "display hook". |
|
242 | """ Reimplemented for IPython-style "display hook". | |
225 | """ |
|
243 | """ | |
226 | self.log.debug("execute_result: %s", msg.get('content', '')) |
|
244 | self.log.debug("execute_result: %s", msg.get('content', '')) | |
227 | if not self._hidden and self._is_from_this_session(msg): |
|
245 | if self.include_output(msg): | |
228 | self.flush_clearoutput() |
|
246 | self.flush_clearoutput() | |
229 | content = msg['content'] |
|
247 | content = msg['content'] | |
230 | prompt_number = content.get('execution_count', 0) |
|
248 | prompt_number = content.get('execution_count', 0) | |
@@ -246,7 +264,7 b' class IPythonWidget(FrontendWidget):' | |||||
246 | # For now, we don't display data from other frontends, but we |
|
264 | # For now, we don't display data from other frontends, but we | |
247 | # eventually will as this allows all frontends to monitor the display |
|
265 | # eventually will as this allows all frontends to monitor the display | |
248 | # data. But we need to figure out how to handle this in the GUI. |
|
266 | # data. But we need to figure out how to handle this in the GUI. | |
249 | if not self._hidden and self._is_from_this_session(msg): |
|
267 | if self.include_output(msg): | |
250 | self.flush_clearoutput() |
|
268 | self.flush_clearoutput() | |
251 | data = msg['content']['data'] |
|
269 | data = msg['content']['data'] | |
252 | metadata = msg['content']['metadata'] |
|
270 | metadata = msg['content']['metadata'] |
@@ -107,7 +107,7 b' class RichIPythonWidget(IPythonWidget):' | |||||
107 | def _handle_execute_result(self, msg): |
|
107 | def _handle_execute_result(self, msg): | |
108 | """ Overridden to handle rich data types, like SVG. |
|
108 | """ Overridden to handle rich data types, like SVG. | |
109 | """ |
|
109 | """ | |
110 | if not self._hidden and self._is_from_this_session(msg): |
|
110 | if self.include_output(msg): | |
111 | self.flush_clearoutput() |
|
111 | self.flush_clearoutput() | |
112 | content = msg['content'] |
|
112 | content = msg['content'] | |
113 | prompt_number = content.get('execution_count', 0) |
|
113 | prompt_number = content.get('execution_count', 0) | |
@@ -146,7 +146,7 b' class RichIPythonWidget(IPythonWidget):' | |||||
146 | def _handle_display_data(self, msg): |
|
146 | def _handle_display_data(self, msg): | |
147 | """ Overridden to handle rich data types, like SVG. |
|
147 | """ Overridden to handle rich data types, like SVG. | |
148 | """ |
|
148 | """ | |
149 | if not self._hidden and self._is_from_this_session(msg): |
|
149 | if self.include_output(msg): | |
150 | self.flush_clearoutput() |
|
150 | self.flush_clearoutput() | |
151 | data = msg['content']['data'] |
|
151 | data = msg['content']['data'] | |
152 | metadata = msg['content']['metadata'] |
|
152 | metadata = msg['content']['metadata'] |
@@ -26,7 +26,7 b' from IPython.core import release' | |||||
26 | from IPython.utils.warn import warn, error |
|
26 | from IPython.utils.warn import warn, error | |
27 | from IPython.utils import io |
|
27 | from IPython.utils import io | |
28 | from IPython.utils.py3compat import string_types, input |
|
28 | from IPython.utils.py3compat import string_types, input | |
29 | from IPython.utils.traitlets import List, Enum, Any, Instance, Unicode, Float |
|
29 | from IPython.utils.traitlets import List, Enum, Any, Instance, Unicode, Float, Bool | |
30 | from IPython.utils.tempdir import NamedFileInTemporaryDirectory |
|
30 | from IPython.utils.tempdir import NamedFileInTemporaryDirectory | |
31 |
|
31 | |||
32 | from IPython.terminal.interactiveshell import TerminalInteractiveShell |
|
32 | from IPython.terminal.interactiveshell import TerminalInteractiveShell | |
@@ -212,8 +212,37 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||||
212 | print(frame, file=io.stderr) |
|
212 | print(frame, file=io.stderr) | |
213 |
|
213 | |||
214 | self.execution_count = int(content["execution_count"] + 1) |
|
214 | self.execution_count = int(content["execution_count"] + 1) | |
215 |
|
215 | |||
216 |
|
216 | include_other_output = Bool(False, config=True, | ||
|
217 | help="""Whether to include output from clients | |||
|
218 | other than this one sharing the same kernel. | |||
|
219 | ||||
|
220 | Outputs are not displayed until enter is pressed. | |||
|
221 | """ | |||
|
222 | ) | |||
|
223 | other_output_prefix = Unicode("[remote] ", config=True, | |||
|
224 | help="""Prefix to add to outputs coming from clients other than this one. | |||
|
225 | ||||
|
226 | Only relevant if include_other_output is True. | |||
|
227 | """ | |||
|
228 | ) | |||
|
229 | ||||
|
230 | def from_here(self, msg): | |||
|
231 | """Return whether a message is from this session""" | |||
|
232 | return msg['parent_header'].get("session", self.session_id) == self.session_id | |||
|
233 | ||||
|
234 | def include_output(self, msg): | |||
|
235 | """Return whether we should include a given output message""" | |||
|
236 | from_here = self.from_here(msg) | |||
|
237 | if msg['msg_type'] == 'execute_input': | |||
|
238 | # only echo inputs not from here | |||
|
239 | return self.include_other_output and not from_here | |||
|
240 | ||||
|
241 | if self.include_other_output: | |||
|
242 | return True | |||
|
243 | else: | |||
|
244 | return from_here | |||
|
245 | ||||
217 | def handle_iopub(self, msg_id=''): |
|
246 | def handle_iopub(self, msg_id=''): | |
218 | """Process messages on the IOPub channel |
|
247 | """Process messages on the IOPub channel | |
219 |
|
248 | |||
@@ -227,7 +256,7 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||||
227 | msg_type = sub_msg['header']['msg_type'] |
|
256 | msg_type = sub_msg['header']['msg_type'] | |
228 | parent = sub_msg["parent_header"] |
|
257 | parent = sub_msg["parent_header"] | |
229 |
|
258 | |||
230 | if parent.get("session", self.session_id) == self.session_id: |
|
259 | if self.include_output(sub_msg): | |
231 | if msg_type == 'status': |
|
260 | if msg_type == 'status': | |
232 | self._execution_state = sub_msg["content"]["execution_state"] |
|
261 | self._execution_state = sub_msg["content"]["execution_state"] | |
233 | elif msg_type == 'stream': |
|
262 | elif msg_type == 'stream': | |
@@ -249,8 +278,11 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||||
249 | print("\r", file=io.stdout, end="") |
|
278 | print("\r", file=io.stdout, end="") | |
250 | self._pending_clearoutput = False |
|
279 | self._pending_clearoutput = False | |
251 | self.execution_count = int(sub_msg["content"]["execution_count"]) |
|
280 | self.execution_count = int(sub_msg["content"]["execution_count"]) | |
|
281 | if not self.from_here(sub_msg): | |||
|
282 | sys.stdout.write(self.other_output_prefix) | |||
252 | format_dict = sub_msg["content"]["data"] |
|
283 | format_dict = sub_msg["content"]["data"] | |
253 | self.handle_rich_data(format_dict) |
|
284 | self.handle_rich_data(format_dict) | |
|
285 | ||||
254 | # taken from DisplayHook.__call__: |
|
286 | # taken from DisplayHook.__call__: | |
255 | hook = self.displayhook |
|
287 | hook = self.displayhook | |
256 | hook.start_displayhook() |
|
288 | hook.start_displayhook() | |
@@ -263,10 +295,20 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||||
263 | data = sub_msg["content"]["data"] |
|
295 | data = sub_msg["content"]["data"] | |
264 | handled = self.handle_rich_data(data) |
|
296 | handled = self.handle_rich_data(data) | |
265 | if not handled: |
|
297 | if not handled: | |
|
298 | if not self.from_here(sub_msg): | |||
|
299 | sys.stdout.write(self.other_output_prefix) | |||
266 | # if it was an image, we handled it by now |
|
300 | # if it was an image, we handled it by now | |
267 | if 'text/plain' in data: |
|
301 | if 'text/plain' in data: | |
268 | print(data['text/plain']) |
|
302 | print(data['text/plain']) | |
269 |
|
303 | |||
|
304 | elif msg_type == 'execute_input': | |||
|
305 | content = sub_msg['content'] | |||
|
306 | self.execution_count = content['execution_count'] | |||
|
307 | if not self.from_here(sub_msg): | |||
|
308 | sys.stdout.write(self.other_output_prefix) | |||
|
309 | sys.stdout.write(self.prompt_manager.render('in')) | |||
|
310 | sys.stdout.write(content['code']) | |||
|
311 | ||||
270 | elif msg_type == 'clear_output': |
|
312 | elif msg_type == 'clear_output': | |
271 | if sub_msg["content"]["wait"]: |
|
313 | if sub_msg["content"]["wait"]: | |
272 | self._pending_clearoutput = True |
|
314 | self._pending_clearoutput = True |
General Comments 0
You need to be logged in to leave comments.
Login now