displayhook.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. """Replacements for sys.displayhook that publish over ZMQ."""
  2. # Copyright (c) IPython Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. from __future__ import annotations
  5. import builtins
  6. import sys
  7. import typing as t
  8. from contextvars import ContextVar
  9. from IPython.core.displayhook import DisplayHook
  10. from jupyter_client.session import Session, extract_header
  11. from traitlets import Any, Instance
  12. from ipykernel.jsonutil import encode_images, json_clean
  13. class ZMQDisplayHook:
  14. """A simple displayhook that publishes the object's repr over a ZeroMQ
  15. socket."""
  16. topic = b"execute_result"
  17. def __init__(self, session, pub_socket):
  18. """Initialize the hook."""
  19. self.session = session
  20. self.pub_socket = pub_socket
  21. self._parent_header: ContextVar[dict[str, Any]] = ContextVar("parent_header")
  22. self._parent_header.set({})
  23. self._parent_header_global = {}
  24. def get_execution_count(self):
  25. """This method is replaced in kernelapp"""
  26. return 0
  27. def __call__(self, obj):
  28. """Handle a hook call."""
  29. if obj is None:
  30. return
  31. builtins._ = obj # type:ignore[attr-defined]
  32. sys.stdout.flush()
  33. sys.stderr.flush()
  34. contents = {
  35. "execution_count": self.get_execution_count(),
  36. "data": {"text/plain": repr(obj)},
  37. "metadata": {},
  38. }
  39. self.session.send(
  40. self.pub_socket,
  41. "execute_result",
  42. contents,
  43. parent=self.parent_header,
  44. ident=self.topic,
  45. )
  46. @property
  47. def parent_header(self):
  48. try:
  49. return self._parent_header.get()
  50. except LookupError:
  51. return self._parent_header_global
  52. def set_parent(self, parent):
  53. """Set the parent header."""
  54. parent_header = extract_header(parent)
  55. self._parent_header.set(parent_header)
  56. self._parent_header_global = parent_header
  57. class ZMQShellDisplayHook(DisplayHook):
  58. """A displayhook subclass that publishes data using ZeroMQ. This is intended
  59. to work with an InteractiveShell instance. It sends a dict of different
  60. representations of the object."""
  61. topic = None
  62. session = Instance(Session, allow_none=True)
  63. pub_socket = Any(allow_none=True)
  64. _parent_header: ContextVar[dict[str, Any]]
  65. msg: dict[str, t.Any] | None
  66. def __init__(self, *args, **kwargs):
  67. super().__init__(*args, **kwargs)
  68. self._parent_header = ContextVar("parent_header")
  69. self._parent_header.set({})
  70. @property
  71. def parent_header(self):
  72. try:
  73. return self._parent_header.get()
  74. except LookupError:
  75. return self._parent_header_global
  76. def set_parent(self, parent):
  77. """Set the parent header."""
  78. parent_header = extract_header(parent)
  79. self._parent_header.set(parent_header)
  80. self._parent_header_global = parent_header
  81. def start_displayhook(self):
  82. """Start the display hook."""
  83. if self.session:
  84. self.msg = self.session.msg(
  85. "execute_result",
  86. {
  87. "data": {},
  88. "metadata": {},
  89. },
  90. parent=self.parent_header,
  91. )
  92. def write_output_prompt(self):
  93. """Write the output prompt."""
  94. if self.msg:
  95. self.msg["content"]["execution_count"] = self.prompt_count
  96. def write_format_data(self, format_dict, md_dict=None):
  97. """Write format data to the message."""
  98. if self.msg:
  99. self.msg["content"]["data"] = json_clean(encode_images(format_dict))
  100. self.msg["content"]["metadata"] = md_dict
  101. def finish_displayhook(self):
  102. """Finish up all displayhook activities."""
  103. sys.stdout.flush()
  104. sys.stderr.flush()
  105. if self.msg and self.msg["content"]["data"] and self.session:
  106. self.session.send(self.pub_socket, self.msg, ident=self.topic)
  107. self.msg = None