test_comms.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import time
  2. import sys
  3. import unittest
  4. from ipython_genutils.py3compat import PY3
  5. from jupyter_client.blocking.channels import Empty
  6. from qtconsole.manager import QtKernelManager
  7. PY2 = sys.version[0] == '2'
  8. if PY2:
  9. TimeoutError = RuntimeError
  10. class Tests(unittest.TestCase):
  11. def setUp(self):
  12. """Open a kernel."""
  13. self.kernel_manager = QtKernelManager()
  14. self.kernel_manager.start_kernel()
  15. self.kernel_client = self.kernel_manager.client()
  16. self.kernel_client.start_channels(shell=True, iopub=True)
  17. self.blocking_client = self.kernel_client.blocking_client()
  18. self.blocking_client.start_channels(shell=True, iopub=True)
  19. self.comm_manager = self.kernel_client.comm_manager
  20. # Check if client is working
  21. self.blocking_client.execute('print(0)')
  22. try:
  23. self._get_next_msg()
  24. self._get_next_msg()
  25. except TimeoutError:
  26. # Maybe it works now?
  27. self.blocking_client.execute('print(0)')
  28. self._get_next_msg()
  29. self._get_next_msg()
  30. def tearDown(self):
  31. """Close the kernel."""
  32. if self.kernel_manager:
  33. self.kernel_manager.shutdown_kernel(now=True)
  34. if self.kernel_client:
  35. self.kernel_client.shutdown()
  36. def _get_next_msg(self, timeout=10):
  37. # Get status messages
  38. timeout_time = time.time() + timeout
  39. msg_type = 'status'
  40. while msg_type == 'status':
  41. if timeout_time < time.time():
  42. raise TimeoutError
  43. try:
  44. msg = self.blocking_client.get_iopub_msg(timeout=3)
  45. msg_type = msg['header']['msg_type']
  46. except Empty:
  47. pass
  48. return msg
  49. def test_kernel_to_frontend(self):
  50. """Communicate from the kernel to the frontend."""
  51. comm_manager = self.comm_manager
  52. blocking_client = self.blocking_client
  53. class DummyCommHandler():
  54. def __init__(self):
  55. comm_manager.register_target('test_api', self.comm_open)
  56. self.last_msg = None
  57. def comm_open(self, comm, msg):
  58. comm.on_msg(self.comm_message)
  59. comm.on_close(self.comm_message)
  60. self.last_msg = msg['content']['data']
  61. self.comm = comm
  62. def comm_message(self, msg):
  63. self.last_msg = msg['content']['data']
  64. handler = DummyCommHandler()
  65. blocking_client.execute(
  66. "from ipykernel.comm import Comm\n"
  67. "comm = Comm(target_name='test_api', data='open')\n"
  68. "comm.send('message')\n"
  69. "comm.close('close')\n"
  70. "del comm\n"
  71. "print('Done')\n"
  72. )
  73. # Get input
  74. msg = self._get_next_msg()
  75. assert msg['header']['msg_type'] == 'execute_input'
  76. # Open comm
  77. msg = self._get_next_msg()
  78. assert msg['header']['msg_type'] == 'comm_open'
  79. comm_manager._dispatch(msg)
  80. assert handler.last_msg == 'open'
  81. assert handler.comm.comm_id == msg['content']['comm_id']
  82. # Get message
  83. msg = self._get_next_msg()
  84. assert msg['header']['msg_type'] == 'comm_msg'
  85. comm_manager._dispatch(msg)
  86. assert handler.last_msg == 'message'
  87. assert handler.comm.comm_id == msg['content']['comm_id']
  88. # Get close
  89. msg = self._get_next_msg()
  90. assert msg['header']['msg_type'] == 'comm_close'
  91. comm_manager._dispatch(msg)
  92. assert handler.last_msg == 'close'
  93. assert handler.comm.comm_id == msg['content']['comm_id']
  94. # Get close
  95. msg = self._get_next_msg()
  96. assert msg['header']['msg_type'] == 'stream'
  97. def test_frontend_to_kernel(self):
  98. """Communicate from the frontend to the kernel."""
  99. comm_manager = self.comm_manager
  100. blocking_client = self.blocking_client
  101. blocking_client.execute(
  102. "class DummyCommHandler():\n"
  103. " def __init__(self):\n"
  104. " get_ipython().kernel.comm_manager.register_target(\n"
  105. " 'test_api', self.comm_open)\n"
  106. " def comm_open(self, comm, msg):\n"
  107. " comm.on_msg(self.comm_message)\n"
  108. " comm.on_close(self.comm_message)\n"
  109. " print(msg['content']['data'])\n"
  110. " def comm_message(self, msg):\n"
  111. " print(msg['content']['data'])\n"
  112. "dummy = DummyCommHandler()\n"
  113. )
  114. # Get input
  115. msg = self._get_next_msg()
  116. assert msg['header']['msg_type'] == 'execute_input'
  117. # Open comm
  118. comm = comm_manager.new_comm('test_api', data='open')
  119. msg = self._get_next_msg()
  120. assert msg['header']['msg_type'] == 'stream'
  121. assert msg['content']['text'] == 'open\n'
  122. # Get message
  123. comm.send('message')
  124. msg = self._get_next_msg()
  125. assert msg['header']['msg_type'] == 'stream'
  126. assert msg['content']['text'] == 'message\n'
  127. # Get close
  128. comm.close('close')
  129. msg = self._get_next_msg()
  130. # Received message has a header and parent header. The parent header has
  131. # the info about the close message type in Python 3
  132. if PY3:
  133. assert msg['parent_header']['msg_type'] == 'comm_close'
  134. assert msg['msg_type'] == 'stream'
  135. assert msg['content']['text'] == 'close\n'
  136. else:
  137. # For some reason ipykernel notifies me that it is closing,
  138. # even though I closed the comm
  139. assert msg['header']['msg_type'] == 'comm_close'
  140. assert comm.comm_id == msg['content']['comm_id']
  141. msg = self._get_next_msg()
  142. assert msg['header']['msg_type'] == 'stream'
  143. if __name__ == "__main__":
  144. unittest.main()