2626from IPython .utils .warn import warn , error
2727from IPython .utils import io
2828from 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
3030from IPython .utils .tempdir import NamedFileInTemporaryDirectory
3131
3232from IPython .terminal .interactiveshell import TerminalInteractiveShell
@@ -219,8 +219,37 @@ def handle_execute_reply(self, msg_id, timeout=None):
219219 print (frame , file = io .stderr )
220220
221221 self .execution_count = int (content ["execution_count" ] + 1 )
222-
223-
222+
223+ include_other_output = Bool (False , config = True ,
224+ help = """Whether to include output from clients
225+ other than this one sharing the same kernel.
226+
227+ Outputs are not displayed
228+ """
229+ )
230+ other_output_prefix = Unicode ("[remote]" , config = True ,
231+ help = """Prefix to add to outputs coming from clients other than this one.
232+
233+ Only relevant if include_other_output is True.
234+ """
235+ )
236+
237+ def from_here (self , msg ):
238+ """Return whether a message is from this session"""
239+ return msg ['parent_header' ].get ("session" , self .session_id ) == self .session_id
240+
241+ def include_output (self , msg ):
242+ """Return whether we should include a given output message"""
243+ from_here = self .from_here (msg )
244+ if msg ['msg_type' ] == 'execute_input' :
245+ # only echo inputs not from here
246+ return self .include_other_output and not from_here
247+
248+ if self .include_other_output :
249+ return True
250+ else :
251+ return from_here
252+
224253 def handle_iopub (self , msg_id = '' ):
225254 """Process messages on the IOPub channel
226255
@@ -234,7 +263,7 @@ def handle_iopub(self, msg_id=''):
234263 msg_type = sub_msg ['header' ]['msg_type' ]
235264 parent = sub_msg ["parent_header" ]
236265
237- if parent . get ( "session" , self .session_id ) == self . session_id :
266+ if self .include_output ( sub_msg ) :
238267 if msg_type == 'status' :
239268 self ._execution_state = sub_msg ["content" ]["execution_state" ]
240269 elif msg_type == 'stream' :
@@ -256,8 +285,11 @@ def handle_iopub(self, msg_id=''):
256285 print ("\r " , file = io .stdout , end = "" )
257286 self ._pending_clearoutput = False
258287 self .execution_count = int (sub_msg ["content" ]["execution_count" ])
288+ if not self .from_here (sub_msg ):
289+ sys .stdout .write (self .other_output_prefix )
259290 format_dict = sub_msg ["content" ]["data" ]
260291 self .handle_rich_data (format_dict )
292+
261293 # taken from DisplayHook.__call__:
262294 hook = self .displayhook
263295 hook .start_displayhook ()
@@ -270,10 +302,20 @@ def handle_iopub(self, msg_id=''):
270302 data = sub_msg ["content" ]["data" ]
271303 handled = self .handle_rich_data (data )
272304 if not handled :
305+ if not self .from_here (sub_msg ):
306+ sys .stdout .write (self .other_output_prefix )
273307 # if it was an image, we handled it by now
274308 if 'text/plain' in data :
275309 print (data ['text/plain' ])
276-
310+
311+ elif msg_type == 'execute_input' :
312+ content = sub_msg ['content' ]
313+ self .execution_count = content ['execution_count' ]
314+ if not self .from_here (sub_msg ):
315+ sys .stdout .write (self .other_output_prefix )
316+ sys .stdout .write (self .prompt_manager .render ('in' ))
317+ sys .stdout .write (content ['code' ])
318+
277319 elif msg_type == 'clear_output' :
278320 if sub_msg ["content" ]["wait" ]:
279321 self ._pending_clearoutput = True
0 commit comments