ptshell.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  1. """IPython terminal interface using prompt_toolkit in place of readline"""
  2. from __future__ import print_function
  3. import base64
  4. import errno
  5. from getpass import getpass
  6. from io import BytesIO
  7. import os
  8. import signal
  9. import subprocess
  10. import sys
  11. import time
  12. from warnings import warn
  13. try:
  14. from queue import Empty # Py 3
  15. except ImportError:
  16. from Queue import Empty # Py 2
  17. from zmq import ZMQError
  18. from IPython.core import page
  19. from IPython.utils.py3compat import cast_unicode_py2, input
  20. from ipython_genutils.tempdir import NamedFileInTemporaryDirectory
  21. from traitlets import (Bool, Integer, Float, Unicode, List, Dict, Enum,
  22. Instance, Any)
  23. from traitlets.config import SingletonConfigurable
  24. from .completer import ZMQCompleter
  25. from .zmqhistory import ZMQHistoryManager
  26. from . import __version__
  27. from prompt_toolkit.completion import Completer, Completion
  28. from prompt_toolkit.document import Document
  29. from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
  30. from prompt_toolkit.filters import HasFocus, HasSelection, ViInsertMode, EmacsInsertMode
  31. from prompt_toolkit.history import InMemoryHistory
  32. from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_output
  33. from prompt_toolkit.interface import CommandLineInterface
  34. from prompt_toolkit.key_binding.manager import KeyBindingManager
  35. from prompt_toolkit.key_binding.vi_state import InputMode
  36. from prompt_toolkit.key_binding.bindings.vi import ViStateFilter
  37. from prompt_toolkit.keys import Keys
  38. from prompt_toolkit.layout.lexers import PygmentsLexer
  39. from prompt_toolkit.styles import PygmentsStyle
  40. from pygments.styles import get_style_by_name
  41. from pygments.lexers import get_lexer_by_name
  42. from pygments.util import ClassNotFound
  43. from pygments.token import Token
  44. def ask_yes_no(prompt, default=None, interrupt=None):
  45. """Asks a question and returns a boolean (y/n) answer.
  46. If default is given (one of 'y','n'), it is used if the user input is
  47. empty. If interrupt is given (one of 'y','n'), it is used if the user
  48. presses Ctrl-C. Otherwise the question is repeated until an answer is
  49. given.
  50. An EOF is treated as the default answer. If there is no default, an
  51. exception is raised to prevent infinite loops.
  52. Valid answers are: y/yes/n/no (match is not case sensitive)."""
  53. answers = {'y':True,'n':False,'yes':True,'no':False}
  54. ans = None
  55. while ans not in answers.keys():
  56. try:
  57. ans = input(prompt+' ').lower()
  58. if not ans: # response was an empty string
  59. ans = default
  60. except KeyboardInterrupt:
  61. if interrupt:
  62. ans = interrupt
  63. except EOFError:
  64. if default in answers.keys():
  65. ans = default
  66. print()
  67. else:
  68. raise
  69. return answers[ans]
  70. def get_pygments_lexer(name):
  71. name = name.lower()
  72. if name == 'ipython2':
  73. from IPython.lib.lexers import IPythonLexer
  74. return IPythonLexer
  75. elif name == 'ipython3':
  76. from IPython.lib.lexers import IPython3Lexer
  77. return IPython3Lexer
  78. else:
  79. try:
  80. return get_lexer_by_name(name).__class__
  81. except ClassNotFound:
  82. warn("No lexer found for language %r. Treating as plain text." % name)
  83. from pygments.lexers.special import TextLexer
  84. return TextLexer
  85. class JupyterPTCompleter(Completer):
  86. """Adaptor to provide kernel completions to prompt_toolkit"""
  87. def __init__(self, jup_completer):
  88. self.jup_completer = jup_completer
  89. def get_completions(self, document, complete_event):
  90. if not document.current_line.strip():
  91. return
  92. content = self.jup_completer.complete_request(
  93. code=document.text,
  94. cursor_pos=document.cursor_position
  95. )
  96. start_pos = content['cursor_start'] - document.cursor_position
  97. for m in content['matches']:
  98. yield Completion(m, start_position=start_pos)
  99. class ZMQTerminalInteractiveShell(SingletonConfigurable):
  100. readline_use = False
  101. pt_cli = None
  102. _executing = False
  103. _execution_state = Unicode('')
  104. _pending_clearoutput = False
  105. _eventloop = None
  106. own_kernel = False # Changed by ZMQTerminalIPythonApp
  107. editing_mode = Unicode('emacs', config=True,
  108. help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
  109. )
  110. highlighting_style = Unicode('', config=True,
  111. help="The name of a Pygments style to use for syntax highlighting"
  112. )
  113. highlighting_style_overrides = Dict(config=True,
  114. help="Override highlighting format for specific tokens"
  115. )
  116. true_color = Bool(False, config=True,
  117. help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
  118. "If your terminal supports true color, the following command "
  119. "should print 'TRUECOLOR' in orange: "
  120. "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
  121. )
  122. history_load_length = Integer(1000, config=True,
  123. help="How many history items to load into memory"
  124. )
  125. banner = Unicode('Jupyter console {version}\n\n{kernel_banner}', config=True,
  126. help=("Text to display before the first prompt. Will be formatted with "
  127. "variables {version} and {kernel_banner}.")
  128. )
  129. kernel_timeout = Float(60, config=True,
  130. help="""Timeout for giving up on a kernel (in seconds).
  131. On first connect and restart, the console tests whether the
  132. kernel is running and responsive by sending kernel_info_requests.
  133. This sets the timeout in seconds for how long the kernel can take
  134. before being presumed dead.
  135. """
  136. )
  137. image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
  138. 'PIL', config=True, allow_none=True, help=
  139. """
  140. Handler for image type output. This is useful, for example,
  141. when connecting to the kernel in which pylab inline backend is
  142. activated. There are four handlers defined. 'PIL': Use
  143. Python Imaging Library to popup image; 'stream': Use an
  144. external program to show the image. Image will be fed into
  145. the STDIN of the program. You will need to configure
  146. `stream_image_handler`; 'tempfile': Use an external program to
  147. show the image. Image will be saved in a temporally file and
  148. the program is called with the temporally file. You will need
  149. to configure `tempfile_image_handler`; 'callable': You can set
  150. any Python callable which is called with the image data. You
  151. will need to configure `callable_image_handler`.
  152. """
  153. )
  154. stream_image_handler = List(config=True, help=
  155. """
  156. Command to invoke an image viewer program when you are using
  157. 'stream' image handler. This option is a list of string where
  158. the first element is the command itself and reminders are the
  159. options for the command. Raw image data is given as STDIN to
  160. the program.
  161. """
  162. )
  163. tempfile_image_handler = List(config=True, help=
  164. """
  165. Command to invoke an image viewer program when you are using
  166. 'tempfile' image handler. This option is a list of string
  167. where the first element is the command itself and reminders
  168. are the options for the command. You can use {file} and
  169. {format} in the string to represent the location of the
  170. generated image file and image format.
  171. """
  172. )
  173. callable_image_handler = Any(config=True, help=
  174. """
  175. Callable object called via 'callable' image handler with one
  176. argument, `data`, which is `msg["content"]["data"]` where
  177. `msg` is the message from iopub channel. For exmaple, you can
  178. find base64 encoded PNG data as `data['image/png']`. If your function
  179. can't handle the data supplied, it should return `False` to indicate
  180. this.
  181. """
  182. )
  183. mime_preference = List(
  184. default_value=['image/png', 'image/jpeg', 'image/svg+xml'],
  185. config=True, help=
  186. """
  187. Preferred object representation MIME type in order. First
  188. matched MIME type will be used.
  189. """
  190. )
  191. use_kernel_is_complete = Bool(True, config=True,
  192. help="""Whether to use the kernel's is_complete message
  193. handling. If False, then the frontend will use its
  194. own is_complete handler.
  195. """
  196. )
  197. kernel_is_complete_timeout = Float(1, config=True,
  198. help="""Timeout (in seconds) for giving up on a kernel's is_complete
  199. response.
  200. If the kernel does not respond at any point within this time,
  201. the kernel will no longer be asked if code is complete, and the
  202. console will default to the built-in is_complete test.
  203. """
  204. )
  205. confirm_exit = Bool(True, config=True,
  206. help="""Set to display confirmation dialog on exit.
  207. You can always use 'exit' or 'quit', to force a
  208. direct exit without any confirmation.
  209. """
  210. )
  211. manager = Instance('jupyter_client.KernelManager', allow_none=True)
  212. client = Instance('jupyter_client.KernelClient', allow_none=True)
  213. def _client_changed(self, name, old, new):
  214. self.session_id = new.session.session
  215. session_id = Unicode()
  216. def _banner1_default(self):
  217. return "Jupyter Console {version}\n".format(version=__version__)
  218. simple_prompt = Bool(False,
  219. help="""Use simple fallback prompt. Features may be limited."""
  220. ).tag(config=True)
  221. def __init__(self, **kwargs):
  222. # This is where traits with a config_key argument are updated
  223. # from the values on config.
  224. super(ZMQTerminalInteractiveShell, self).__init__(**kwargs)
  225. self.configurables = [self]
  226. self.init_history()
  227. self.init_completer()
  228. self.init_io()
  229. self.init_kernel_info()
  230. self.init_prompt_toolkit_cli()
  231. self.keep_running = True
  232. self.execution_count = 1
  233. def init_completer(self):
  234. """Initialize the completion machinery.
  235. This creates completion machinery that can be used by client code,
  236. either interactively in-process (typically triggered by the readline
  237. library), programmatically (such as in test suites) or out-of-process
  238. (typically over the network by remote frontends).
  239. """
  240. self.Completer = ZMQCompleter(self, self.client, config=self.config)
  241. def init_history(self):
  242. """Sets up the command history. """
  243. self.history_manager = ZMQHistoryManager(client=self.client)
  244. self.configurables.append(self.history_manager)
  245. def get_prompt_tokens(self, cli):
  246. return [
  247. (Token.Prompt, 'In ['),
  248. (Token.PromptNum, str(self.execution_count)),
  249. (Token.Prompt, ']: '),
  250. ]
  251. def get_continuation_tokens(self, cli, width):
  252. return [
  253. (Token.Prompt, (' ' * (width - 2)) + ': '),
  254. ]
  255. def get_out_prompt_tokens(self):
  256. return [
  257. (Token.OutPrompt, 'Out['),
  258. (Token.OutPromptNum, str(self.execution_count)),
  259. (Token.OutPrompt, ']: ')
  260. ]
  261. def print_out_prompt(self):
  262. self.pt_cli.print_tokens(self.get_out_prompt_tokens())
  263. kernel_info = {}
  264. def init_kernel_info(self):
  265. """Wait for a kernel to be ready, and store kernel info"""
  266. timeout = self.kernel_timeout
  267. tic = time.time()
  268. self.client.hb_channel.unpause()
  269. msg_id = self.client.kernel_info()
  270. while True:
  271. try:
  272. reply = self.client.get_shell_msg(timeout=1)
  273. except Empty:
  274. if (time.time() - tic) > timeout:
  275. raise RuntimeError("Kernel didn't respond to kernel_info_request")
  276. else:
  277. if reply['parent_header'].get('msg_id') == msg_id:
  278. self.kernel_info = reply['content']
  279. return
  280. def show_banner(self):
  281. print(self.banner.format(version=__version__,
  282. kernel_banner=self.kernel_info.get('banner', '')))
  283. def init_prompt_toolkit_cli(self):
  284. if self.simple_prompt or ('JUPYTER_CONSOLE_TEST' in os.environ):
  285. # Simple restricted interface for tests so we can find prompts with
  286. # pexpect. Multi-line input not supported.
  287. def prompt():
  288. return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
  289. self.prompt_for_code = prompt
  290. self.print_out_prompt = \
  291. lambda: print('Out[%d]: ' % self.execution_count, end='')
  292. return
  293. kbmanager = KeyBindingManager.for_prompt()
  294. insert_mode = ViInsertMode() | EmacsInsertMode()
  295. # Ctrl+J == Enter, seemingly
  296. @kbmanager.registry.add_binding(Keys.ControlJ,
  297. filter=(HasFocus(DEFAULT_BUFFER)
  298. & ~HasSelection()
  299. & insert_mode
  300. ))
  301. def _(event):
  302. b = event.current_buffer
  303. d = b.document
  304. if not (d.on_last_line or d.cursor_position_row >= d.line_count
  305. - d.empty_line_count_at_the_end()):
  306. b.newline()
  307. return
  308. # Pressing enter flushes any pending display. This also ensures
  309. # the displayed execution_count is correct.
  310. self.handle_iopub()
  311. more, indent = self.check_complete(d.text)
  312. if (not more) and b.accept_action.is_returnable:
  313. b.accept_action.validate_and_handle(event.cli, b)
  314. else:
  315. b.insert_text('\n' + indent)
  316. @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
  317. def _(event):
  318. event.current_buffer.reset()
  319. @kbmanager.registry.add_binding(Keys.ControlBackslash, filter=HasFocus(DEFAULT_BUFFER))
  320. def _(event):
  321. raise EOFError
  322. # Pre-populate history from IPython's history database
  323. history = InMemoryHistory()
  324. last_cell = u""
  325. for _, _, cell in self.history_manager.get_tail(self.history_load_length,
  326. include_latest=True):
  327. # Ignore blank lines and consecutive duplicates
  328. cell = cast_unicode_py2(cell.rstrip())
  329. if cell and (cell != last_cell):
  330. history.append(cell)
  331. style_overrides = {
  332. Token.Prompt: '#009900',
  333. Token.PromptNum: '#00ff00 bold',
  334. Token.OutPrompt: '#ff2200',
  335. Token.OutPromptNum: '#ff0000 bold',
  336. }
  337. if self.highlighting_style:
  338. style_cls = get_style_by_name(self.highlighting_style)
  339. else:
  340. style_cls = get_style_by_name('default')
  341. # The default theme needs to be visible on both a dark background
  342. # and a light background, because we can't tell what the terminal
  343. # looks like. These tweaks to the default theme help with that.
  344. style_overrides.update({
  345. Token.Number: '#007700',
  346. Token.Operator: 'noinherit',
  347. Token.String: '#BB6622',
  348. Token.Name.Function: '#2080D0',
  349. Token.Name.Class: 'bold #2080D0',
  350. Token.Name.Namespace: 'bold #2080D0',
  351. })
  352. style_overrides.update(self.highlighting_style_overrides)
  353. style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
  354. style_dict=style_overrides)
  355. editing_mode = getattr(EditingMode, self.editing_mode.upper())
  356. langinfo = self.kernel_info.get('language_info', {})
  357. lexer = langinfo.get('pygments_lexer', langinfo.get('name', 'text'))
  358. app = create_prompt_application(multiline=True,
  359. editing_mode=editing_mode,
  360. lexer=PygmentsLexer(get_pygments_lexer(lexer)),
  361. get_prompt_tokens=self.get_prompt_tokens,
  362. get_continuation_tokens=self.get_continuation_tokens,
  363. key_bindings_registry=kbmanager.registry,
  364. history=history,
  365. completer=JupyterPTCompleter(self.Completer),
  366. enable_history_search=True,
  367. style=style,
  368. )
  369. self._eventloop = create_eventloop()
  370. self.pt_cli = CommandLineInterface(app,
  371. eventloop=self._eventloop,
  372. output=create_output(true_color=self.true_color),
  373. )
  374. def prompt_for_code(self):
  375. document = self.pt_cli.run(pre_run=self.pre_prompt,
  376. reset_current_buffer=True)
  377. return document.text
  378. def init_io(self):
  379. if sys.platform not in {'win32', 'cli'}:
  380. return
  381. import colorama
  382. colorama.init()
  383. def check_complete(self, code):
  384. if self.use_kernel_is_complete:
  385. msg_id = self.client.is_complete(code)
  386. try:
  387. return self.handle_is_complete_reply(msg_id, timeout=self.kernel_is_complete_timeout)
  388. except SyntaxError:
  389. return False, ""
  390. else:
  391. lines = code.splitlines()
  392. if len(lines):
  393. more = (lines[-1] != "")
  394. return more, ""
  395. else:
  396. return False, ""
  397. def ask_exit(self):
  398. self.keep_running = False
  399. # This is set from payloads in handle_execute_reply
  400. next_input = None
  401. def pre_prompt(self):
  402. if self.next_input:
  403. # We can't set the buffer here, because it will be reset just after
  404. # this. Adding a callable to pre_run_callables does what we need
  405. # after the buffer is reset.
  406. s = cast_unicode_py2(self.next_input)
  407. def set_doc():
  408. self.pt_cli.application.buffer.document = Document(s)
  409. if hasattr(self.pt_cli, 'pre_run_callables'):
  410. self.pt_cli.pre_run_callables.append(set_doc)
  411. else:
  412. # Older version of prompt_toolkit; it's OK to set the document
  413. # directly here.
  414. set_doc()
  415. self.next_input = None
  416. def interact(self, display_banner=None):
  417. while self.keep_running:
  418. print('\n', end='')
  419. try:
  420. code = self.prompt_for_code()
  421. except EOFError:
  422. if (not self.confirm_exit) \
  423. or ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
  424. self.ask_exit()
  425. else:
  426. if code:
  427. self.run_cell(code, store_history=True)
  428. def mainloop(self):
  429. self.keepkernel = not self.own_kernel
  430. # An extra layer of protection in case someone mashing Ctrl-C breaks
  431. # out of our internal code.
  432. while True:
  433. try:
  434. self.interact()
  435. break
  436. except KeyboardInterrupt:
  437. print("\nKeyboardInterrupt escaped interact()\n")
  438. if self._eventloop:
  439. self._eventloop.close()
  440. if self.keepkernel and not self.own_kernel:
  441. print('keeping kernel alive')
  442. elif self.keepkernel and self.own_kernel :
  443. print("owning kernel, cannot keep it alive")
  444. self.client.shutdown()
  445. else :
  446. print("Shutting down kernel")
  447. self.client.shutdown()
  448. def run_cell(self, cell, store_history=True):
  449. """Run a complete IPython cell.
  450. Parameters
  451. ----------
  452. cell : str
  453. The code (including IPython code such as %magic functions) to run.
  454. store_history : bool
  455. If True, the raw and translated cell will be stored in IPython's
  456. history. For user code calling back into IPython's machinery, this
  457. should be set to False.
  458. """
  459. if (not cell) or cell.isspace():
  460. # pressing enter flushes any pending display
  461. self.handle_iopub()
  462. return
  463. # flush stale replies, which could have been ignored, due to missed heartbeats
  464. while self.client.shell_channel.msg_ready():
  465. self.client.shell_channel.get_msg()
  466. # execute takes 'hidden', which is the inverse of store_hist
  467. msg_id = self.client.execute(cell, not store_history)
  468. # first thing is wait for any side effects (output, stdin, etc.)
  469. self._executing = True
  470. self._execution_state = "busy"
  471. while self._execution_state != 'idle' and self.client.is_alive():
  472. try:
  473. self.handle_input_request(msg_id, timeout=0.05)
  474. except Empty:
  475. # display intermediate print statements, etc.
  476. self.handle_iopub(msg_id)
  477. except ZMQError as e:
  478. # Carry on if polling was interrupted by a signal
  479. if e.errno != errno.EINTR:
  480. raise
  481. # after all of that is done, wait for the execute reply
  482. while self.client.is_alive():
  483. try:
  484. self.handle_execute_reply(msg_id, timeout=0.05)
  485. except Empty:
  486. pass
  487. else:
  488. break
  489. self._executing = False
  490. #-----------------
  491. # message handlers
  492. #-----------------
  493. def handle_execute_reply(self, msg_id, timeout=None):
  494. msg = self.client.shell_channel.get_msg(block=False, timeout=timeout)
  495. if msg["parent_header"].get("msg_id", None) == msg_id:
  496. self.handle_iopub(msg_id)
  497. content = msg["content"]
  498. status = content['status']
  499. if status == 'aborted':
  500. self.write('Aborted\n')
  501. return
  502. elif status == 'ok':
  503. # handle payloads
  504. for item in content.get("payload", []):
  505. source = item['source']
  506. if source == 'page':
  507. page.page(item['data']['text/plain'])
  508. elif source == 'set_next_input':
  509. self.next_input = item['text']
  510. elif source == 'ask_exit':
  511. self.keepkernel = item.get('keepkernel', False)
  512. self.ask_exit()
  513. elif status == 'error':
  514. pass
  515. self.execution_count = int(content["execution_count"] + 1)
  516. def handle_is_complete_reply(self, msg_id, timeout=None):
  517. """
  518. Wait for a repsonse from the kernel, and return two values:
  519. more? - (boolean) should the frontend ask for more input
  520. indent - an indent string to prefix the input
  521. Overloaded methods may want to examine the comeplete source. Its is
  522. in the self._source_lines_buffered list.
  523. """
  524. ## Get the is_complete response:
  525. msg = None
  526. try:
  527. msg = self.client.shell_channel.get_msg(block=True, timeout=timeout)
  528. except Empty:
  529. warn('The kernel did not respond to an is_complete_request. '
  530. 'Setting `use_kernel_is_complete` to False.')
  531. self.use_kernel_is_complete = False
  532. return False, ""
  533. ## Handle response:
  534. if msg["parent_header"].get("msg_id", None) != msg_id:
  535. warn('The kernel did not respond properly to an is_complete_request: %s.' % str(msg))
  536. return False, ""
  537. else:
  538. status = msg["content"].get("status", None)
  539. indent = msg["content"].get("indent", "")
  540. ## Return more? and indent string
  541. if status == "complete":
  542. return False, indent
  543. elif status == "incomplete":
  544. return True, indent
  545. elif status == "invalid":
  546. raise SyntaxError()
  547. elif status == "unknown":
  548. return False, indent
  549. else:
  550. warn('The kernel sent an invalid is_complete_reply status: "%s".' % status)
  551. return False, indent
  552. include_other_output = Bool(False, config=True,
  553. help="""Whether to include output from clients
  554. other than this one sharing the same kernel.
  555. Outputs are not displayed until enter is pressed.
  556. """
  557. )
  558. other_output_prefix = Unicode("[remote] ", config=True,
  559. help="""Prefix to add to outputs coming from clients other than this one.
  560. Only relevant if include_other_output is True.
  561. """
  562. )
  563. def from_here(self, msg):
  564. """Return whether a message is from this session"""
  565. return msg['parent_header'].get("session", self.session_id) == self.session_id
  566. def include_output(self, msg):
  567. """Return whether we should include a given output message"""
  568. from_here = self.from_here(msg)
  569. if msg['msg_type'] == 'execute_input':
  570. # only echo inputs not from here
  571. return self.include_other_output and not from_here
  572. if self.include_other_output:
  573. return True
  574. else:
  575. return from_here
  576. def handle_iopub(self, msg_id=''):
  577. """Process messages on the IOPub channel
  578. This method consumes and processes messages on the IOPub channel,
  579. such as stdout, stderr, execute_result and status.
  580. It only displays output that is caused by this session.
  581. """
  582. while self.client.iopub_channel.msg_ready():
  583. sub_msg = self.client.iopub_channel.get_msg()
  584. msg_type = sub_msg['header']['msg_type']
  585. parent = sub_msg["parent_header"]
  586. # Update execution_count in case it changed in another session
  587. if msg_type == "execute_input":
  588. self.execution_count = int(sub_msg["content"]["execution_count"]) + 1
  589. if self.include_output(sub_msg):
  590. if msg_type == 'status':
  591. self._execution_state = sub_msg["content"]["execution_state"]
  592. elif msg_type == 'stream':
  593. if sub_msg["content"]["name"] == "stdout":
  594. if self._pending_clearoutput:
  595. print("\r", end="")
  596. self._pending_clearoutput = False
  597. print(sub_msg["content"]["text"], end="")
  598. sys.stdout.flush()
  599. elif sub_msg["content"]["name"] == "stderr":
  600. if self._pending_clearoutput:
  601. print("\r", file=sys.stderr, end="")
  602. self._pending_clearoutput = False
  603. print(sub_msg["content"]["text"], file=sys.stderr, end="")
  604. sys.stderr.flush()
  605. elif msg_type == 'execute_result':
  606. if self._pending_clearoutput:
  607. print("\r", end="")
  608. self._pending_clearoutput = False
  609. self.execution_count = int(sub_msg["content"]["execution_count"])
  610. if not self.from_here(sub_msg):
  611. sys.stdout.write(self.other_output_prefix)
  612. format_dict = sub_msg["content"]["data"]
  613. self.handle_rich_data(format_dict)
  614. if 'text/plain' not in format_dict:
  615. continue
  616. # prompt_toolkit writes the prompt at a slightly lower level,
  617. # so flush streams first to ensure correct ordering.
  618. sys.stdout.flush()
  619. sys.stderr.flush()
  620. self.print_out_prompt()
  621. text_repr = format_dict['text/plain']
  622. if '\n' in text_repr:
  623. # For multi-line results, start a new line after prompt
  624. print()
  625. print(text_repr)
  626. elif msg_type == 'display_data':
  627. data = sub_msg["content"]["data"]
  628. handled = self.handle_rich_data(data)
  629. if not handled:
  630. if not self.from_here(sub_msg):
  631. sys.stdout.write(self.other_output_prefix)
  632. # if it was an image, we handled it by now
  633. if 'text/plain' in data:
  634. print(data['text/plain'])
  635. elif msg_type == 'execute_input':
  636. content = sub_msg['content']
  637. if not self.from_here(sub_msg):
  638. sys.stdout.write(self.other_output_prefix)
  639. sys.stdout.write('In [{}]: '.format(content['execution_count']))
  640. sys.stdout.write(content['code']+'\n')
  641. elif msg_type == 'clear_output':
  642. if sub_msg["content"]["wait"]:
  643. self._pending_clearoutput = True
  644. else:
  645. print("\r", end="")
  646. elif msg_type == 'error':
  647. for frame in sub_msg["content"]["traceback"]:
  648. print(frame, file=sys.stderr)
  649. _imagemime = {
  650. 'image/png': 'png',
  651. 'image/jpeg': 'jpeg',
  652. 'image/svg+xml': 'svg',
  653. }
  654. def handle_rich_data(self, data):
  655. for mime in self.mime_preference:
  656. if mime in data and mime in self._imagemime:
  657. if self.handle_image(data, mime):
  658. return True
  659. return False
  660. def handle_image(self, data, mime):
  661. handler = getattr(
  662. self, 'handle_image_{0}'.format(self.image_handler), None)
  663. if handler:
  664. return handler(data, mime)
  665. def handle_image_PIL(self, data, mime):
  666. if mime not in ('image/png', 'image/jpeg'):
  667. return False
  668. try:
  669. from PIL import Image, ImageShow
  670. except ImportError:
  671. return False
  672. raw = base64.decodestring(data[mime].encode('ascii'))
  673. img = Image.open(BytesIO(raw))
  674. return ImageShow.show(img)
  675. def handle_image_stream(self, data, mime):
  676. raw = base64.decodestring(data[mime].encode('ascii'))
  677. imageformat = self._imagemime[mime]
  678. fmt = dict(format=imageformat)
  679. args = [s.format(**fmt) for s in self.stream_image_handler]
  680. with open(os.devnull, 'w') as devnull:
  681. proc = subprocess.Popen(
  682. args, stdin=subprocess.PIPE,
  683. stdout=devnull, stderr=devnull)
  684. proc.communicate(raw)
  685. return (proc.returncode == 0)
  686. def handle_image_tempfile(self, data, mime):
  687. raw = base64.decodestring(data[mime].encode('ascii'))
  688. imageformat = self._imagemime[mime]
  689. filename = 'tmp.{0}'.format(imageformat)
  690. with NamedFileInTemporaryDirectory(filename) as f, \
  691. open(os.devnull, 'w') as devnull:
  692. f.write(raw)
  693. f.flush()
  694. fmt = dict(file=f.name, format=imageformat)
  695. args = [s.format(**fmt) for s in self.tempfile_image_handler]
  696. rc = subprocess.call(args, stdout=devnull, stderr=devnull)
  697. return (rc == 0)
  698. def handle_image_callable(self, data, mime):
  699. res = self.callable_image_handler(data)
  700. if res is not False:
  701. # If handler func returns e.g. None, assume it has handled the data.
  702. res = True
  703. return res
  704. def handle_input_request(self, msg_id, timeout=0.1):
  705. """ Method to capture raw_input
  706. """
  707. req = self.client.stdin_channel.get_msg(timeout=timeout)
  708. # in case any iopub came while we were waiting:
  709. self.handle_iopub(msg_id)
  710. if msg_id == req["parent_header"].get("msg_id"):
  711. # wrap SIGINT handler
  712. real_handler = signal.getsignal(signal.SIGINT)
  713. def double_int(sig,frame):
  714. # call real handler (forwards sigint to kernel),
  715. # then raise local interrupt, stopping local raw_input
  716. real_handler(sig,frame)
  717. raise KeyboardInterrupt
  718. signal.signal(signal.SIGINT, double_int)
  719. content = req['content']
  720. read = getpass if content.get('password', False) else input
  721. try:
  722. raw_data = read(content["prompt"])
  723. except EOFError:
  724. # turn EOFError into EOF character
  725. raw_data = '\x04'
  726. except KeyboardInterrupt:
  727. sys.stdout.write('\n')
  728. return
  729. finally:
  730. # restore SIGINT handler
  731. signal.signal(signal.SIGINT, real_handler)
  732. # only send stdin reply if there *was not* another request
  733. # or execution finished while we were reading.
  734. if not (self.client.stdin_channel.msg_ready() or self.client.shell_channel.msg_ready()):
  735. self.client.input(raw_data)