Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved error reporting on comms #906

Merged
merged 7 commits into from Oct 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions holoviews/plotting/bokeh/callbacks.py
Expand Up @@ -79,6 +79,9 @@ class Callback(object):
var comm = HoloViewsWidget.comms["{comms_target}"];
var comm_state = HoloViewsWidget.comm_state["{comms_target}"];
if (msg.msg_type == "Ready") {{
if (msg.content) {{
console.log("Python callback returned following output:", msg.content);
}}
if (comm_state.event) {{
comm.send(comm_state.event);
}} else {{
Expand Down
48 changes: 45 additions & 3 deletions holoviews/plotting/comms.py
@@ -1,10 +1,38 @@
import json
import uuid
import sys
import os
import traceback
try:
from StringIO import StringIO
except:
from io import StringIO

from ipykernel.comm import Comm as IPyComm
from IPython import get_ipython


class StandardOutput(list):
"""
Context manager to capture standard output for any code it
is wrapping and make it available as a list, e.g.:

>>> with StandardOutput() as stdout:
... print('This gets captured')
>>> print(stdout[0])
This gets captured
"""

def __enter__(self):
self._stdout = sys.stdout
sys.stdout = self._stringio = StringIO()
return self

def __exit__(self, *args):
self.extend(self._stringio.getvalue().splitlines())
sys.stdout = self._stdout


class Comm(object):
"""
Comm encompasses any uni- or bi-directional connection between
Expand Down Expand Up @@ -70,12 +98,26 @@ def _handle_msg(self, msg):
if it has been defined.
"""
try:
stdout = []
msg = self.decode(msg)
if self._on_msg:
self._on_msg(self.decode(msg))
# Comm swallows standard output so we need to capture
# it and then send it to the frontend
with StandardOutput() as stdout:
self._on_msg(msg)
except Exception as e:
msg = {'msg_type': "Error", 'traceback': str(e)}
frame =traceback.extract_tb(sys.exc_info()[2])[-2]
fname,lineno,fn,text = frame
error_kwargs = dict(type=type(e).__name__, fn=fn, fname=fname,
line=lineno, error=str(e))
error = '{fname} {fn} L{line}\n\t{type}: {error}'.format(**error_kwargs)
if stdout:
stdout = '\n\t'+'\n\t'.join(stdout)
error = '\n'.join([stdout, error])
msg = {'msg_type': "Error", 'traceback': error}
else:
msg = {'msg_type': "Ready"}
stdout = '\n\t'+'\n\t'.join(stdout) if stdout else ''
msg = {'msg_type': "Ready", 'content': stdout}
self.comm.send(json.dumps(msg))


Expand Down