ipkernel.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. """An in-process kernel"""
  2. # Copyright (c) IPython Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. from contextlib import contextmanager
  5. import logging
  6. import sys
  7. from IPython.core.interactiveshell import InteractiveShellABC
  8. from ipykernel.jsonutil import json_clean
  9. from traitlets import Any, Enum, Instance, List, Type, default
  10. from ipykernel.ipkernel import IPythonKernel
  11. from ipykernel.zmqshell import ZMQInteractiveShell
  12. from .constants import INPROCESS_KEY
  13. from .socket import DummySocket
  14. from ..iostream import OutStream, BackgroundSocket, IOPubThread
  15. #-----------------------------------------------------------------------------
  16. # Main kernel class
  17. #-----------------------------------------------------------------------------
  18. class InProcessKernel(IPythonKernel):
  19. #-------------------------------------------------------------------------
  20. # InProcessKernel interface
  21. #-------------------------------------------------------------------------
  22. # The frontends connected to this kernel.
  23. frontends = List(
  24. Instance('ipykernel.inprocess.client.InProcessKernelClient',
  25. allow_none=True)
  26. )
  27. # The GUI environment that the kernel is running under. This need not be
  28. # specified for the normal operation for the kernel, but is required for
  29. # IPython's GUI support (including pylab). The default is 'inline' because
  30. # it is safe under all GUI toolkits.
  31. gui = Enum(('tk', 'gtk', 'wx', 'qt', 'qt4', 'inline'),
  32. default_value='inline')
  33. raw_input_str = Any()
  34. stdout = Any()
  35. stderr = Any()
  36. #-------------------------------------------------------------------------
  37. # Kernel interface
  38. #-------------------------------------------------------------------------
  39. shell_class = Type(allow_none=True)
  40. shell_streams = List()
  41. control_stream = Any()
  42. _underlying_iopub_socket = Instance(DummySocket, ())
  43. iopub_thread = Instance(IOPubThread)
  44. @default('iopub_thread')
  45. def _default_iopub_thread(self):
  46. thread = IOPubThread(self._underlying_iopub_socket)
  47. thread.start()
  48. return thread
  49. iopub_socket = Instance(BackgroundSocket)
  50. @default('iopub_socket')
  51. def _default_iopub_socket(self):
  52. return self.iopub_thread.background_socket
  53. stdin_socket = Instance(DummySocket, ())
  54. def __init__(self, **traits):
  55. super(InProcessKernel, self).__init__(**traits)
  56. self._underlying_iopub_socket.observe(self._io_dispatch, names=['message_sent'])
  57. self.shell.kernel = self
  58. def execute_request(self, stream, ident, parent):
  59. """ Override for temporary IO redirection. """
  60. with self._redirected_io():
  61. super(InProcessKernel, self).execute_request(stream, ident, parent)
  62. def start(self):
  63. """ Override registration of dispatchers for streams. """
  64. self.shell.exit_now = False
  65. def _abort_queue(self, stream):
  66. """ The in-process kernel doesn't abort requests. """
  67. pass
  68. def _input_request(self, prompt, ident, parent, password=False):
  69. # Flush output before making the request.
  70. self.raw_input_str = None
  71. sys.stderr.flush()
  72. sys.stdout.flush()
  73. # Send the input request.
  74. content = json_clean(dict(prompt=prompt, password=password))
  75. msg = self.session.msg(u'input_request', content, parent)
  76. for frontend in self.frontends:
  77. if frontend.session.session == parent['header']['session']:
  78. frontend.stdin_channel.call_handlers(msg)
  79. break
  80. else:
  81. logging.error('No frontend found for raw_input request')
  82. return str()
  83. # Await a response.
  84. while self.raw_input_str is None:
  85. frontend.stdin_channel.process_events()
  86. return self.raw_input_str
  87. #-------------------------------------------------------------------------
  88. # Protected interface
  89. #-------------------------------------------------------------------------
  90. @contextmanager
  91. def _redirected_io(self):
  92. """ Temporarily redirect IO to the kernel.
  93. """
  94. sys_stdout, sys_stderr = sys.stdout, sys.stderr
  95. sys.stdout, sys.stderr = self.stdout, self.stderr
  96. yield
  97. sys.stdout, sys.stderr = sys_stdout, sys_stderr
  98. #------ Trait change handlers --------------------------------------------
  99. def _io_dispatch(self, change):
  100. """ Called when a message is sent to the IO socket.
  101. """
  102. ident, msg = self.session.recv(self.iopub_socket, copy=False)
  103. for frontend in self.frontends:
  104. frontend.iopub_channel.call_handlers(msg)
  105. #------ Trait initializers -----------------------------------------------
  106. @default('log')
  107. def _default_log(self):
  108. return logging.getLogger(__name__)
  109. @default('session')
  110. def _default_session(self):
  111. from jupyter_client.session import Session
  112. return Session(parent=self, key=INPROCESS_KEY)
  113. @default('shell_class')
  114. def _default_shell_class(self):
  115. return InProcessInteractiveShell
  116. @default('stdout')
  117. def _default_stdout(self):
  118. return OutStream(self.session, self.iopub_thread, u'stdout')
  119. @default('stderr')
  120. def _default_stderr(self):
  121. return OutStream(self.session, self.iopub_thread, u'stderr')
  122. #-----------------------------------------------------------------------------
  123. # Interactive shell subclass
  124. #-----------------------------------------------------------------------------
  125. class InProcessInteractiveShell(ZMQInteractiveShell):
  126. kernel = Instance('ipykernel.inprocess.ipkernel.InProcessKernel',
  127. allow_none=True)
  128. #-------------------------------------------------------------------------
  129. # InteractiveShell interface
  130. #-------------------------------------------------------------------------
  131. def enable_gui(self, gui=None):
  132. """Enable GUI integration for the kernel."""
  133. from ipykernel.eventloops import enable_gui
  134. if not gui:
  135. gui = self.kernel.gui
  136. enable_gui(gui, kernel=self.kernel)
  137. self.active_eventloop = gui
  138. def enable_matplotlib(self, gui=None):
  139. """Enable matplotlib integration for the kernel."""
  140. if not gui:
  141. gui = self.kernel.gui
  142. return super(InProcessInteractiveShell, self).enable_matplotlib(gui)
  143. def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
  144. """Activate pylab support at runtime."""
  145. if not gui:
  146. gui = self.kernel.gui
  147. return super(InProcessInteractiveShell, self).enable_pylab(gui, import_all,
  148. welcome_message)
  149. InteractiveShellABC.register(InProcessInteractiveShell)