Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions ipykernel/displayhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,24 @@ def finish_displayhook(self):
if self.msg['content']['data']:
self.session.send(self.pub_socket, self.msg, ident=self.topic)
self.msg = None


class ZMQMessageHook(object):
"""
A Display Hook for intercepting certain named
message types.
"""
def __init__(self, name, callback):
"""
Pass the message type to start intercepting, and
a callback which will get passed any item which
is hooked.
"""
self._name = name
self._callback = callback

def __call__(self, msg):
if msg['msg_type'] == self._name:
self._callback(msg)
else:
return msg
73 changes: 73 additions & 0 deletions ipykernel/hookmanager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
"""
A context manager that will register a hook on a
Display Publisher for storing messages.
"""

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

from io import StringIO
import sys

from IPython.core.getipython import get_ipython

from .displayhook import ZMQMessageHook


class MessageHookFor(object):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context managers that are typically treated as functions, I think it's more conventional to use function_style_name rather than ObjectStyleName, even though it's technically a type.

"""
A context manager which takes the name of a message field,
and installs a hook to intercept messages of that type.

Usage
-----
>>> with MessageHookFor('display_data'):
clear_output()
"""
def __init__(self, message_name, parent=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring and signature suggest that this is a message for a single type, but it also captures stream messages unconditionally.

self._parent = parent
self._message_name = message_name
self._pub = get_ipython().display_pub
self._hook = ZMQMessageHook(message_name, parent.store)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is parent expected to be? Where is this store method defined?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also makes parent a required arg, because parent.store is accessed unconditionally, so parent=None default will never be valid.

self._callback = parent.store
self._std_buffer = StringIO()

def clear_output(self, *args, **kwargs):
self._std_buffer.truncate(0)
self._parent.clear()

def __enter__(self):
"""
Called on entry to the context.

Installs the message hook on the current ipython
display publisher.
"""
self._pub.register_hook(self._hook)
self._old_clear = self._pub.clear_output
self._pub.clear_output = self.clear_output

self._old_stdout = sys.stdout
self._old_stderr = sys.stderr
sys.stdout = self._std_buffer
sys.stderr = self._std_buffer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it deliberate to eliminate the distinction between stdout and stderr?


def __exit__(self, tp, value, tb):
if tp is not None:
# Exception occurred... log and continue.
pass

self._pub.unregister_hook(self._hook)
self._pub.clear_output = self._old_clear
sys.stdout = self._old_stdout
sys.stderr = self._old_stderr

std = self._std_buffer.getvalue()
if std:
# TODO : update this once rendermime is available here.
temp = {'content': {'data': {'text/plain': std}}}
self._callback(temp)
self._std_buffer.truncate(0)

return False