From 40c73a81b6d44895557bf94fff8b095384db9210 Mon Sep 17 00:00:00 2001 From: Joseph C Wang Date: Sat, 29 Aug 2020 01:32:49 +0800 Subject: [PATCH 1/2] modify outputs to use comm This modifies the outputs widget to use comm rather than calling ipython directly. This fixes #2953 and Calysto/metakernel#217 --- ipywidgets/widgets/widget_output.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/ipywidgets/widgets/widget_output.py b/ipywidgets/widgets/widget_output.py index de6e16957b..da22f1f569 100644 --- a/ipywidgets/widgets/widget_output.py +++ b/ipywidgets/widgets/widget_output.py @@ -18,7 +18,7 @@ from IPython.core.interactiveshell import InteractiveShell from IPython.display import clear_output from IPython import get_ipython - +import traceback @register class Output(DOMWidget): @@ -107,24 +107,35 @@ def inner(*args, **kwargs): def __enter__(self): """Called upon entering output widget context manager.""" self._flush() - ip = get_ipython() - if ip and hasattr(ip, 'kernel') and hasattr(ip.kernel, '_parent_header'): - self.msg_id = ip.kernel._parent_header['header']['msg_id'] + if self.comm is not None and self.comm.kernel is not None and \ + hasattr(self.comm.kernel, '_parent_header'): + self.msg_id = self.comm.kernel._parent_header['header']['msg_id'] self.__counter += 1 def __exit__(self, etype, evalue, tb): """Called upon exiting output widget context manager.""" - ip = get_ipython() + kernel = None if etype is not None: + ip = get_ipython() if ip: + kernel = ip ip.showtraceback((etype, evalue, tb), tb_offset=0) + elif self.comm is not None and self.comm.kernel is not None: + kernel = self.comm.kernel + kernel.send_response(kernel.iopub_socket, + u'error', + { + u'traceback': ["".join(traceback.format_exception(etype, evalue, tb))], + u'evalue': repr(evalue.args), + u'ename': etype.__name__ + }) self._flush() self.__counter -= 1 if self.__counter == 0: self.msg_id = '' - # suppress exceptions when in a kernel, since they are shown above, + # suppress exceptions when in IPython, since they are shown above, # otherwise let someone else handle it - return True if ip else None + return True if kernel else None def _flush(self): """Flush stdout and stderr buffers.""" From fa9a89753846e19ea64d40e3cf53810497790e9c Mon Sep 17 00:00:00 2001 From: Joseph C Wang Date: Sat, 29 Aug 2020 11:03:28 +0800 Subject: [PATCH 2/2] add fallback to work with mock_ipython --- ipywidgets/widgets/widget_output.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ipywidgets/widgets/widget_output.py b/ipywidgets/widgets/widget_output.py index da22f1f569..a21971db67 100644 --- a/ipywidgets/widgets/widget_output.py +++ b/ipywidgets/widgets/widget_output.py @@ -107,7 +107,12 @@ def inner(*args, **kwargs): def __enter__(self): """Called upon entering output widget context manager.""" self._flush() - if self.comm is not None and self.comm.kernel is not None and \ + ip = get_ipython() + if ip and hasattr(ip, 'kernel') and \ + hasattr(ip.kernel, '_parent_header'): + self.msg_id = ip.kernel._parent_header['header']['msg_id'] + self.__counter += 1 + elif self.comm is not None and self.comm.kernel is not None and \ hasattr(self.comm.kernel, '_parent_header'): self.msg_id = self.comm.kernel._parent_header['header']['msg_id'] self.__counter += 1