381 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			381 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python
 | |
| # encoding: utf-8
 | |
| """
 | |
| The :class:`~yap_ipython.core.application.Application` object for the command
 | |
| line :command:`ipython` program.
 | |
| """
 | |
| 
 | |
| # Copyright (c) yap_ipython Development Team.
 | |
| # Distributed under the terms of the Modified BSD License.
 | |
| 
 | |
| 
 | |
| import logging
 | |
| import os
 | |
| import sys
 | |
| import warnings
 | |
| 
 | |
| from traitlets.config.loader import Config
 | |
| from traitlets.config.application import boolean_flag, catch_config_error
 | |
| from yap_ipython.core import release
 | |
| from yap_ipython.core import usage
 | |
| from yap_ipython.yapi import YAPCompleter
 | |
| from yap_ipython.core.crashhandler import CrashHandler
 | |
| from yap_ipython.core.formatters import PlainTextFormatter
 | |
| from yap_ipython.core.history import HistoryManager
 | |
| from yap_ipython.core.application import (
 | |
|     ProfileDir, BaseYAPApplication, base_flags, base_aliases
 | |
| )
 | |
| from yap_ipython.core.magics import (
 | |
|     ScriptMagics, LoggingMagics
 | |
| )
 | |
| from yap_ipython.core.shellapp import (
 | |
|     InteractiveShellApp, shell_flags, shell_aliases
 | |
| )
 | |
| from yap_ipython.extensions.storemagic import StoreMagics
 | |
| from .interactiveshell import TerminalInteractiveShell
 | |
| from yap_ipython.paths import get_ipython_dir
 | |
| from traitlets import (
 | |
|     Bool, List, default, observe, Type
 | |
| )
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| # Globals, utilities and helpers
 | |
| #-----------------------------------------------------------------------------
 | |
| 
 | |
| _examples = """
 | |
| ipython --matplotlib       # enable matplotlib integration
 | |
| ipython --matplotlib=qt    # enable matplotlib integration with qt4 backend
 | |
| 
 | |
| ipython --log-level=DEBUG  # set logging to DEBUG
 | |
| ipython --profile=foo      # start with profile foo
 | |
| 
 | |
| ipython profile create foo # create profile foo w/ default config files
 | |
| ipython help profile       # show the help for the profile subcmd
 | |
| 
 | |
| ipython locate             # print the path to the yap_ipython directory
 | |
| ipython locate profile foo # print the path to the directory for profile `foo`
 | |
| """
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| # Crash handler for this application
 | |
| #-----------------------------------------------------------------------------
 | |
| 
 | |
| class IPAppCrashHandler(CrashHandler):
 | |
|     """sys.excepthook for yap_ipython itself, leaves a detailed report on disk."""
 | |
| 
 | |
|     def __init__(self, app):
 | |
|         contact_name = release.author
 | |
|         contact_email = release.author_email
 | |
|         bug_tracker = 'https://github.com/ipython/ipython/issues'
 | |
|         super(IPAppCrashHandler,self).__init__(
 | |
|             app, contact_name, contact_email, bug_tracker
 | |
|         )
 | |
| 
 | |
|     def make_report(self,traceback):
 | |
|         """Return a string containing a crash report."""
 | |
| 
 | |
|         sec_sep = self.section_sep
 | |
|         # Start with parent report
 | |
|         report = [super(IPAppCrashHandler, self).make_report(traceback)]
 | |
|         # Add interactive-specific info we may have
 | |
|         rpt_add = report.append
 | |
|         try:
 | |
|             rpt_add(sec_sep+"History of session input:")
 | |
|             for line in self.app.shell.user_ns['_ih']:
 | |
|                 rpt_add(line)
 | |
|             rpt_add('\n*** Last line of input (may not be in above history):\n')
 | |
|             rpt_add(self.app.shell._last_input_line+'\n')
 | |
|         except:
 | |
|             pass
 | |
| 
 | |
|         return ''.join(report)
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| # Aliases and Flags
 | |
| #-----------------------------------------------------------------------------
 | |
| flags = dict(base_flags)
 | |
| flags.update(shell_flags)
 | |
| frontend_flags = {}
 | |
| addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
 | |
| addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
 | |
|         'Turn on auto editing of files with syntax errors.',
 | |
|         'Turn off auto editing of files with syntax errors.'
 | |
| )
 | |
| addflag('simple-prompt', 'TerminalInteractiveShell.simple_prompt',
 | |
|         "Force simple minimal prompt using `raw_input`",
 | |
|         "Use a rich interactive prompt with prompt_toolkit",
 | |
| )
 | |
| 
 | |
| addflag('banner', 'TerminalIPythonApp.display_banner',
 | |
|         "Display a banner upon starting yap_ipython.",
 | |
|         "Don't display a banner upon starting yap_ipython."
 | |
| )
 | |
| addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
 | |
|     """Set to confirm when you try to exit yap_ipython with an EOF (Control-D
 | |
|     in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
 | |
|     you can force a direct exit without any confirmation.""",
 | |
|     "Don't prompt the user when exiting."
 | |
| )
 | |
| addflag('term-title', 'TerminalInteractiveShell.term_title',
 | |
|     "Enable auto setting the terminal title.",
 | |
|     "Disable auto setting the terminal title."
 | |
| )
 | |
| classic_config = Config()
 | |
| classic_config.InteractiveShell.cache_size = 0
 | |
| classic_config.PlainTextFormatter.pprint = False
 | |
| classic_config.TerminalInteractiveShell.prompts_class='yap_ipython.terminal.prompts.ClassicPrompts'
 | |
| classic_config.InteractiveShell.separate_in = ''
 | |
| classic_config.InteractiveShell.separate_out = ''
 | |
| classic_config.InteractiveShell.separate_out2 = ''
 | |
| classic_config.InteractiveShell.colors = 'NoColor'
 | |
| classic_config.InteractiveShell.xmode = 'Plain'
 | |
| 
 | |
| frontend_flags['classic']=(
 | |
|     classic_config,
 | |
|     "Gives yap_ipython a similar feel to the classic Python prompt."
 | |
| )
 | |
| # # log doesn't make so much sense this way anymore
 | |
| # paa('--log','-l',
 | |
| #     action='store_true', dest='InteractiveShell.logstart',
 | |
| #     help="Start logging to the default log file (./ipython_log.py).")
 | |
| #
 | |
| # # quick is harder to implement
 | |
| frontend_flags['quick']=(
 | |
|     {'TerminalIPythonApp' : {'quick' : True}},
 | |
|     "Enable quick startup with no config files."
 | |
| )
 | |
| 
 | |
| frontend_flags['i'] = (
 | |
|     {'TerminalIPythonApp' : {'force_interact' : True}},
 | |
|     """If running code from the command line, become interactive afterwards.
 | |
|     It is often useful to follow this with `--` to treat remaining flags as
 | |
|     script arguments.
 | |
|     """
 | |
| )
 | |
| flags.update(frontend_flags)
 | |
| 
 | |
| aliases = dict(base_aliases)
 | |
| aliases.update(shell_aliases)
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| # Main classes and functions
 | |
| #-----------------------------------------------------------------------------
 | |
| 
 | |
| 
 | |
| class LocateIPythonApp(BaseYAPApplication):
 | |
|     description = """print the path to the yap_ipython dir"""
 | |
|     subcommands = dict(
 | |
|         profile=('yap_ipython.core.profileapp.ProfileLocate',
 | |
|             "print the path to an yap_ipython profile directory",
 | |
|         ),
 | |
|     )
 | |
|     def start(self):
 | |
|         if self.subapp is not None:
 | |
|             return self.subapp.start()
 | |
|         else:
 | |
|             print(self.ipython_dir)
 | |
| 
 | |
| 
 | |
| class TerminalIPythonApp(BaseYAPApplication, InteractiveShellApp):
 | |
|     name = u'yap'
 | |
|     description = usage.cl_usage
 | |
|     crash_handler_class = IPAppCrashHandler
 | |
|     examples = _examples
 | |
| 
 | |
|     flags = flags
 | |
|     aliases = aliases
 | |
|     classes = List()
 | |
| 
 | |
|     interactive_shell_class = Type(
 | |
|         klass=object,   # use default_value otherwise which only allow subclasses.
 | |
|         default_value=TerminalInteractiveShell,
 | |
|         help="Class to use to instantiate the TerminalInteractiveShell object. Useful for custom Frontends"
 | |
|     ).tag(config=True)
 | |
| 
 | |
|     @default('classes')
 | |
|     def _classes_default(self):
 | |
|         """This has to be in a method, for TerminalIPythonApp to be available."""
 | |
|         return [
 | |
|             InteractiveShellApp, # ShellApp comes before TerminalApp, because
 | |
|             self.__class__,      # it will also affect subclasses (e.g. QtConsole)
 | |
|             TerminalInteractiveShell,
 | |
|             HistoryManager,
 | |
|             ProfileDir,
 | |
|             PlainTextFormatter,
 | |
|             YAPCompleter,
 | |
|             ScriptMagics,
 | |
|             LoggingMagics,
 | |
|             StoreMagics,
 | |
|         ]
 | |
| 
 | |
|     deprecated_subcommands = dict(
 | |
|         qtconsole=('qtconsole.qtconsoleapp.JupyterQtConsoleApp',
 | |
|             """DEPRECATED, Will be removed in yap_ipython 6.0 : Launch the Jupyter Qt Console."""
 | |
|         ),
 | |
|         notebook=('notebook.notebookapp.NotebookApp',
 | |
|             """DEPRECATED, Will be removed in yap_ipython 6.0 : Launch the Jupyter HTML Notebook Server."""
 | |
|         ),
 | |
|         console=('jupyter_console.app.ZMQTerminalIPythonApp',
 | |
|             """DEPRECATED, Will be removed in yap_ipython 6.0 : Launch the Jupyter terminal-based Console."""
 | |
|         ),
 | |
|         nbconvert=('nbconvert.nbconvertapp.NbConvertApp',
 | |
|             "DEPRECATED, Will be removed in yap_ipython 6.0 : Convert notebooks to/from other formats."
 | |
|         ),
 | |
|         trust=('nbformat.sign.TrustNotebookApp',
 | |
|             "DEPRECATED, Will be removed in yap_ipython 6.0 : Sign notebooks to trust their potentially unsafe contents at load."
 | |
|         ),
 | |
|         kernelspec=('jupyter_client.kernelspecapp.KernelSpecApp',
 | |
|             "DEPRECATED, Will be removed in yap_ipython 6.0 : Manage Jupyter kernel specifications."
 | |
|         ),
 | |
|     )
 | |
|     subcommands = dict(
 | |
|         profile = ("yap_ipython.core.profileapp.ProfileApp",
 | |
|             "Create and manage yap_ipython profiles."
 | |
|         ),
 | |
|         kernel = ("yap_kernel.kernelapp.YAPKernelApp",
 | |
|             "Start a kernel without an attached frontend."
 | |
|         ),
 | |
|         locate=('yap_ipython.terminal.ipapp.LocateIPythonApp',
 | |
|             LocateIPythonApp.description
 | |
|         ),
 | |
|         history=('yap_ipython.core.historyapp.HistoryApp',
 | |
|             "Manage the yap_ipython history database."
 | |
|         ),
 | |
|     )
 | |
|     deprecated_subcommands['install-nbextension'] = (
 | |
|         "notebook.nbextensions.InstallNBExtensionApp",
 | |
|         "DEPRECATED, Will be removed in yap_ipython 6.0 : Install Jupyter notebook extension files"
 | |
|     )
 | |
|     subcommands.update(deprecated_subcommands)
 | |
| 
 | |
|     # *do* autocreate requested profile, but don't create the config file.
 | |
|     auto_create=Bool(True)
 | |
|     # configurables
 | |
|     quick = Bool(False,
 | |
|         help="""Start yap_ipython quickly by skipping the loading of config files."""
 | |
|     ).tag(config=True)
 | |
|     @observe('quick')
 | |
|     def _quick_changed(self, change):
 | |
|         if change['new']:
 | |
|             self.load_config_file = lambda *a, **kw: None
 | |
| 
 | |
|     display_banner = Bool(True,
 | |
|         help="Whether to display a banner upon starting yap_ipython."
 | |
|     ).tag(config=True)
 | |
| 
 | |
|     # if there is code of files to run from the cmd line, don't interact
 | |
|     # unless the --i flag (App.force_interact) is true.
 | |
|     force_interact = Bool(False,
 | |
|         help="""If a command or file is given via the command-line,
 | |
|         e.g. 'ipython foo.py', start an interactive shell after executing the
 | |
|         file or command."""
 | |
|     ).tag(config=True)
 | |
|     @observe('force_interact')
 | |
|     def _force_interact_changed(self, change):
 | |
|         if change['new']:
 | |
|             self.interact = True
 | |
| 
 | |
|     @observe('file_to_run', 'code_to_run', 'module_to_run')
 | |
|     def _file_to_run_changed(self, change):
 | |
|         new = change['new']
 | |
|         if new:
 | |
|             self.something_to_run = True
 | |
|         if new and not self.force_interact:
 | |
|                 self.interact = False
 | |
| 
 | |
|     # internal, not-configurable
 | |
|     something_to_run=Bool(False)
 | |
| 
 | |
|     def parse_command_line(self, argv=None):
 | |
|         """override to allow old '-pylab' flag with deprecation warning"""
 | |
| 
 | |
|         argv = sys.argv[1:] if argv is None else argv
 | |
| 
 | |
|         if '-pylab' in argv:
 | |
|             # deprecated `-pylab` given,
 | |
|             # warn and transform into current syntax
 | |
|             argv = argv[:] # copy, don't clobber
 | |
|             idx = argv.index('-pylab')
 | |
|             warnings.warn("`-pylab` flag has been deprecated.\n"
 | |
|             "    Use `--matplotlib <backend>` and import pylab manually.")
 | |
|             argv[idx] = '--pylab'
 | |
| 
 | |
|         return super(TerminalIPythonApp, self).parse_command_line(argv)
 | |
| 
 | |
|     @catch_config_error
 | |
|     def initialize(self, argv=None):
 | |
|         """Do actions after construct, but before starting the app."""
 | |
|         super(TerminalIPythonApp, self).initialize(argv)
 | |
|         if self.subapp is not None:
 | |
|             # don't bother initializing further, starting subapp
 | |
|             return
 | |
|         # print self.extra_args
 | |
|         if self.extra_args and not self.something_to_run:
 | |
|             self.file_to_run = self.extra_args[0]
 | |
|         self.init_path()
 | |
|         # create the shell
 | |
|         self.init_shell()
 | |
|         # and draw the banner
 | |
|         self.init_banner()
 | |
|         # Now a variety of things that happen after the banner is printed.
 | |
|         self.init_gui_pylab()
 | |
|         self.init_extensions()
 | |
|         self.init_code()
 | |
| 
 | |
|     def init_shell(self):
 | |
|         """initialize the InteractiveShell instance"""
 | |
|         # Create an InteractiveShell instance.
 | |
|         # shell.display_banner should always be False for the terminal
 | |
|         # based app, because we call shell.show_banner() by hand below
 | |
|         # so the banner shows *before* all extension loading stuff.
 | |
|         self.shell = self.interactive_shell_class.instance(parent=self,
 | |
|                         profile_dir=self.profile_dir,
 | |
|                         ipython_dir=self.ipython_dir, user_ns=self.user_ns)
 | |
|         self.shell.configurables.append(self)
 | |
| 
 | |
|     def init_banner(self):
 | |
|         """optionally display the banner"""
 | |
|         if self.display_banner and self.interact:
 | |
|             self.shell.show_banner()
 | |
|         # Make sure there is a space below the banner.
 | |
|         if self.log_level <= logging.INFO: print()
 | |
| 
 | |
|     def _pylab_changed(self, name, old, new):
 | |
|         """Replace --pylab='inline' with --pylab='auto'"""
 | |
|         if new == 'inline':
 | |
|             warnings.warn("'inline' not available as pylab backend, "
 | |
|                       "using 'auto' instead.")
 | |
|             self.pylab = 'auto'
 | |
| 
 | |
|     def start(self):
 | |
|         if self.subapp is not None:
 | |
|             return self.subapp.start()
 | |
|         # perform any prexec steps:
 | |
|         if self.interact:
 | |
|             self.log.debug("Starting yap_ipython's mainloop...")
 | |
|             self.shell.mainloop()
 | |
|         else:
 | |
|             self.log.debug("yap_ipython not interactive...")
 | |
|             if not self.shell.last_execution_succeeded:
 | |
|                 sys.exit(1)
 | |
| 
 | |
| def load_default_config(ipython_dir=None):
 | |
|     """Load the default config file from the default ipython_dir.
 | |
| 
 | |
|     This is useful for embedded shells.
 | |
|     """
 | |
|     if ipython_dir is None:
 | |
|         ipython_dir = get_ipython_dir()
 | |
| 
 | |
|     profile_dir = os.path.join(ipython_dir, 'profile_default')
 | |
|     app = TerminalIPythonApp()
 | |
|     app.config_file_paths.append(profile_dir)
 | |
|     app.load_config_file()
 | |
|     return app.config
 | |
| 
 | |
| launch_new_instance = TerminalIPythonApp.launch_instance
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     launch_new_instance()
 |