app.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. """ A minimal application using the ZMQ-based terminal IPython frontend.
  2. This is not a complete console app, as subprocess will not be able to receive
  3. input, there is no real readline support, among other limitations.
  4. """
  5. # Copyright (c) IPython Development Team.
  6. # Distributed under the terms of the Modified BSD License.
  7. from __future__ import print_function
  8. import logging
  9. import signal
  10. import sys
  11. from traitlets import (
  12. Dict, Any
  13. )
  14. from traitlets.config import catch_config_error, boolean_flag
  15. from jupyter_core.application import JupyterApp, base_aliases, base_flags, NoStart
  16. from jupyter_client.consoleapp import (
  17. JupyterConsoleApp, app_aliases, app_flags,
  18. )
  19. from jupyter_console.ptshell import ZMQTerminalInteractiveShell
  20. from jupyter_console import __version__
  21. #-----------------------------------------------------------------------------
  22. # Globals
  23. #-----------------------------------------------------------------------------
  24. _examples = """
  25. jupyter console # start the ZMQ-based console
  26. jupyter console --existing # connect to an existing ipython session
  27. """
  28. #-----------------------------------------------------------------------------
  29. # Flags and Aliases
  30. #-----------------------------------------------------------------------------
  31. # copy flags from mixin:
  32. flags = dict(base_flags)
  33. # start with mixin frontend flags:
  34. frontend_flags = dict(app_flags)
  35. # update full dict with frontend flags:
  36. flags.update(frontend_flags)
  37. flags.update(boolean_flag(
  38. 'simple-prompt', 'ZMQTerminalInteractiveShell.simple_prompt',
  39. "Force simple minimal prompt using `raw_input`",
  40. "Use a rich interactive prompt with prompt_toolkit"
  41. ))
  42. # copy flags from mixin
  43. aliases = dict(base_aliases)
  44. # start with mixin frontend flags
  45. frontend_aliases = dict(app_aliases)
  46. # load updated frontend flags into full dict
  47. aliases.update(frontend_aliases)
  48. # get flags&aliases into sets, and remove a couple that
  49. # shouldn't be scrubbed from backend flags:
  50. frontend_aliases = set(frontend_aliases.keys())
  51. frontend_flags = set(frontend_flags.keys())
  52. #-----------------------------------------------------------------------------
  53. # Classes
  54. #-----------------------------------------------------------------------------
  55. class ZMQTerminalIPythonApp(JupyterApp, JupyterConsoleApp):
  56. name = "jupyter-console"
  57. version = __version__
  58. """Start a terminal frontend to the IPython zmq kernel."""
  59. description = """
  60. The Jupyter terminal-based Console.
  61. This launches a Console application inside a terminal.
  62. The Console supports various extra features beyond the traditional
  63. single-process Terminal IPython shell, such as connecting to an
  64. existing ipython session, via:
  65. jupyter console --existing
  66. where the previous session could have been created by another ipython
  67. console, an ipython qtconsole, or by opening an ipython notebook.
  68. """
  69. examples = _examples
  70. classes = [ZMQTerminalInteractiveShell] + JupyterConsoleApp.classes
  71. flags = Dict(flags)
  72. aliases = Dict(aliases)
  73. frontend_aliases = Any(frontend_aliases)
  74. frontend_flags = Any(frontend_flags)
  75. subcommands = Dict()
  76. force_interact = True
  77. def parse_command_line(self, argv=None):
  78. super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
  79. self.build_kernel_argv(self.extra_args)
  80. def init_shell(self):
  81. JupyterConsoleApp.initialize(self)
  82. # relay sigint to kernel
  83. signal.signal(signal.SIGINT, self.handle_sigint)
  84. self.shell = ZMQTerminalInteractiveShell.instance(parent=self,
  85. manager=self.kernel_manager,
  86. client=self.kernel_client,
  87. )
  88. self.shell.own_kernel = not self.existing
  89. def init_gui_pylab(self):
  90. # no-op, because we don't want to import matplotlib in the frontend.
  91. pass
  92. def handle_sigint(self, *args):
  93. if self.shell._executing:
  94. if self.kernel_manager:
  95. self.kernel_manager.interrupt_kernel()
  96. else:
  97. print("ERROR: Cannot interrupt kernels we didn't start.",
  98. file = sys.stderr)
  99. else:
  100. # raise the KeyboardInterrupt if we aren't waiting for execution,
  101. # so that the interact loop advances, and prompt is redrawn, etc.
  102. raise KeyboardInterrupt
  103. @catch_config_error
  104. def initialize(self, argv=None):
  105. """Do actions after construct, but before starting the app."""
  106. super(ZMQTerminalIPythonApp, self).initialize(argv)
  107. if self._dispatching:
  108. return
  109. # create the shell
  110. self.init_shell()
  111. # and draw the banner
  112. self.init_banner()
  113. def init_banner(self):
  114. """optionally display the banner"""
  115. self.shell.show_banner()
  116. # Make sure there is a space below the banner.
  117. if self.log_level <= logging.INFO: print()
  118. def start(self):
  119. # JupyterApp.start dispatches on NoStart
  120. super(ZMQTerminalIPythonApp, self).start()
  121. self.log.debug("Starting the jupyter console mainloop...")
  122. self.shell.mainloop()
  123. main = launch_new_instance = ZMQTerminalIPythonApp.launch_instance
  124. if __name__ == '__main__':
  125. main()