#!/usr/bin/env python # encoding: utf-8 """ The :class:`~IPython.core.newapplication.Application` object for the command line :command:`ipython` program. Authors ------- * Brian Granger * Fernando Perez * Min Ragan-Kelley """ #----------------------------------------------------------------------------- # Copyright (C) 2008-2010 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- from __future__ import absolute_import import logging import os import sys from IPython.config.loader import ( Config, PyFileConfigLoader ) from IPython.config.application import boolean_flag from IPython.core import release from IPython.core import usage from IPython.core.crashhandler import CrashHandler from IPython.core.formatters import PlainTextFormatter from IPython.core.newapplication import ( ProfileDir, BaseIPythonApplication, base_flags, base_aliases ) from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell from IPython.lib import inputhook from IPython.utils.path import filefind, get_ipython_dir, check_for_old_config from IPython.utils.traitlets import ( Bool, Unicode, Dict, Instance, List,CaselessStrEnum ) #----------------------------------------------------------------------------- # Globals, utilities and helpers #----------------------------------------------------------------------------- #: The default config file name for this application. default_config_file_name = u'ipython_config.py' #----------------------------------------------------------------------------- # Crash handler for this application #----------------------------------------------------------------------------- _message_template = """\ Oops, $self.app_name crashed. We do our best to make it stable, but... A crash report was automatically generated with the following information: - A verbatim copy of the crash traceback. - A copy of your input history during this session. - Data on your current $self.app_name configuration. It was left in the file named: \t'$self.crash_report_fname' If you can email this file to the developers, the information in it will help them in understanding and correcting the problem. You can mail it to: $self.contact_name at $self.contact_email with the subject '$self.app_name Crash Report'. If you want to do it now, the following command will work (under Unix): mail -s '$self.app_name Crash Report' $self.contact_email < $self.crash_report_fname To ensure accurate tracking of this issue, please file a report about it at: $self.bug_tracker """ class IPAppCrashHandler(CrashHandler): """sys.excepthook for IPython itself, leaves a detailed report on disk.""" message_template = _message_template def __init__(self, app): contact_name = release.authors['Fernando'][0] contact_email = release.authors['Fernando'][1] bug_tracker = 'http://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({ }) addflag = lambda *args: flags.update(boolean_flag(*args)) addflag('autoindent', 'InteractiveShell.autoindent', 'Turn on autoindenting.', 'Turn off autoindenting.' ) addflag('automagic', 'InteractiveShell.automagic', """Turn on the auto calling of magic commands. Type %%magic at the IPython prompt for more information.""", 'Turn off the auto calling of magic commands.' ) 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('banner', 'IPythonApp.display_banner', "Display a banner upon starting IPython.", "Don't display a banner upon starting IPython." ) addflag('pdb', 'InteractiveShell.pdb', "Enable auto calling the pdb debugger after every exception.", "Disable auto calling the pdb debugger after every exception." ) addflag('pprint', 'PlainTextFormatter.pprint', "Enable auto pretty printing of results.", "Disable auto auto pretty printing of results." ) addflag('color-info', 'InteractiveShell.color_info', """IPython can display information about objects via a set of func- tions, and optionally can use colors for this, syntax highlighting source code and various other elements. However, because this information is passed through a pager (like 'less') and many pagers get confused with color codes, this option is off by default. You can test it and turn it on permanently in your ipython_config.py file if it works for you. Test it and turn it on permanently if it works with your system. The magic function %%color_info allows you to toggle this inter- actively for testing.""", "Disable using colors for info related things." ) addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit', """Set to confirm when you try to exit 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('deep-reload', 'InteractiveShell.deep_reload', """Enable deep (recursive) reloading by default. IPython can use the deep_reload module which reloads changes in modules recursively (it replaces the reload() function, so you don't need to change anything to use it). deep_reload() forces a full reload of modules whose code may have changed, which the default reload() function does not. When deep_reload is off, IPython will use the normal reload(), but deep_reload will still be available as dreload(). This feature is off by default [which means that you have both normal reload() and dreload()].""", "Disable deep (recursive) reloading by default." ) addflag('readline', 'InteractiveShell.readline_use', "Enable readline for command line usage.", "Disable readline for command line usage." ) 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.InteractiveShell.prompt_in1 = '>>> ' classic_config.InteractiveShell.prompt_in2 = '... ' classic_config.InteractiveShell.prompt_out = '' 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' flags['classic']=( classic_config, "Gives 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 flags['quick']=( {'IPythonApp' : {'quick' : True}}, "Enable quick startup with no config files." ) nosep_config = Config() nosep_config.InteractiveShell.separate_in = '' nosep_config.InteractiveShell.separate_out = '' nosep_config.InteractiveShell.separate_out2 = '' flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.") flags['i'] = ( {'IPythonApp' : {'force_interact' : True}}, "If running code from the command line, become interactive afterwards." ) flags['pylab'] = ( {'IPythonApp' : {'pylab' : 'auto'}}, """Pre-load matplotlib and numpy for interactive use with the default matplotlib backend.""" ) aliases = dict(base_aliases) # it's possible we don't want short aliases for *all* of these: aliases.update(dict( autocall='InteractiveShell.autocall', cache_size='InteractiveShell.cache_size', colors='InteractiveShell.colors', editor='TerminalInteractiveShell.editor', logfile='InteractiveShell.logfile', log_append='InteractiveShell.logappend', pi1='InteractiveShell.prompt_in1', pi2='InteractiveShell.prompt_in1', po='InteractiveShell.prompt_out', sl='TerminalInteractiveShell.screen_length', si='InteractiveShell.separate_in', so='InteractiveShell.separate_out', so2='InteractiveShell.separate_out2', xmode='InteractiveShell.xmode', c='IPythonApp.code_to_run', ext='IPythonApp.extra_extension', gui='IPythonApp.gui', pylab='IPythonApp.pylab', )) #----------------------------------------------------------------------------- # Main classes and functions #----------------------------------------------------------------------------- class IPythonApp(BaseIPythonApplication): name = u'ipython' description = usage.cl_usage # command_line_loader = IPAppConfigLoader default_config_file_name = default_config_file_name crash_handler_class = IPAppCrashHandler flags = Dict(flags) aliases = Dict(aliases) classes = [TerminalInteractiveShell, ProfileDir, PlainTextFormatter] # *do* autocreate requested profile auto_create=Bool(True) copy_config_files=Bool(True) # configurables ignore_old_config=Bool(False, config=True, help="Suppress warning messages about legacy config files" ) quick = Bool(False, config=True, help="""Start IPython quickly by skipping the loading of config files.""" ) def _quick_changed(self, name, old, new): if new: self.load_config_file = lambda *a, **kw: None self.ignore_old_config=True gui = CaselessStrEnum(('qt','wx','gtk'), config=True, help="Enable GUI event loop integration ('qt', 'wx', 'gtk')." ) pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'auto'], config=True, help="""Pre-load matplotlib and numpy for interactive use, selecting a particular matplotlib backend and loop integration. """ ) display_banner = Bool(True, config=True, help="Whether to display a banner upon starting IPython." ) extensions = List(Unicode, config=True, help="A list of dotted module names of IPython extensions to load." ) extra_extension = Unicode('', config=True, help="dotted module name of an IPython extension to load." ) def _extra_extension_changed(self, name, old, new): if new: # add to self.extensions self.extensions.append(new) # 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, config=True, help="""If a command or file is given via the command-line, e.g. 'ipython foo.py""" ) def _force_interact_changed(self, name, old, new): if new: self.interact = True exec_files = List(Unicode, config=True, help="""List of files to run at IPython startup.""" ) file_to_run = Unicode('', config=True, help="""A file to be run""") def _file_to_run_changed(self, name, old, new): if new and not self.force_interact: self.interact = False exec_lines = List(Unicode, config=True, help="""lines of code to run at IPython startup.""" ) code_to_run = Unicode('', config=True, help="Execute the given command string." ) _code_to_run_changed = _file_to_run_changed # internal, not-configurable interact=Bool(True) def initialize(self, argv=None): """Do actions after construct, but before starting the app.""" super(IPythonApp, self).initialize(argv) if not self.ignore_old_config: check_for_old_config(self.ipython_dir) # print self.extra_args if self.extra_args: self.file_to_run = self.extra_args[0] # 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""" # I am a little hesitant to put these into InteractiveShell itself. # But that might be the place for them sys.path.insert(0, '') # 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 = TerminalInteractiveShell.instance(config=self.config, display_banner=False, profile_dir=self.profile_dir, ipython_dir=self.ipython_dir) 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 init_gui_pylab(self): """Enable GUI event loop integration, taking pylab into account.""" gui = self.gui # Using `pylab` will also require gui activation, though which toolkit # to use may be chosen automatically based on mpl configuration. if self.pylab: activate = self.shell.enable_pylab if self.pylab == 'auto': gui = None else: gui = self.pylab else: # Enable only GUI integration, no pylab activate = inputhook.enable_gui if gui or self.pylab: try: self.log.info("Enabling GUI event loop integration, " "toolkit=%s, pylab=%s" % (gui, self.pylab) ) activate(gui) except: self.log.warn("Error in enabling GUI event loop integration:") self.shell.showtraceback() def init_extensions(self): """Load all IPython extensions in IPythonApp.extensions. This uses the :meth:`ExtensionManager.load_extensions` to load all the extensions listed in ``self.extensions``. """ if not self.extensions: return try: self.log.debug("Loading IPython extensions...") extensions = self.extensions for ext in extensions: try: self.log.info("Loading IPython extension: %s" % ext) self.shell.extension_manager.load_extension(ext) except: self.log.warn("Error in loading extension: %s" % ext) self.shell.showtraceback() except: self.log.warn("Unknown error in loading extensions:") self.shell.showtraceback() def init_code(self): """run the pre-flight code, specified via exec_lines""" self._run_exec_lines() self._run_exec_files() self._run_cmd_line_code() def _run_exec_lines(self): """Run lines of code in IPythonApp.exec_lines in the user's namespace.""" if not self.exec_lines: return try: self.log.debug("Running code from IPythonApp.exec_lines...") for line in self.exec_lines: try: self.log.info("Running code in user namespace: %s" % line) self.shell.run_cell(line, store_history=False) except: self.log.warn("Error in executing line in user " "namespace: %s" % line) self.shell.showtraceback() except: self.log.warn("Unknown error in handling IPythonApp.exec_lines:") self.shell.showtraceback() def _exec_file(self, fname): full_filename = filefind(fname, [u'.', self.ipython_dir]) if os.path.isfile(full_filename): if full_filename.endswith(u'.py'): self.log.info("Running file in user namespace: %s" % full_filename) # Ensure that __file__ is always defined to match Python behavior self.shell.user_ns['__file__'] = fname try: self.shell.safe_execfile(full_filename, self.shell.user_ns) finally: del self.shell.user_ns['__file__'] elif full_filename.endswith('.ipy'): self.log.info("Running file in user namespace: %s" % full_filename) self.shell.safe_execfile_ipy(full_filename) else: self.log.warn("File does not have a .py or .ipy extension: <%s>" % full_filename) def _run_exec_files(self): """Run files from IPythonApp.exec_files""" if not self.exec_files: return self.log.debug("Running files in IPythonApp.exec_files...") try: for fname in self.exec_files: self._exec_file(fname) except: self.log.warn("Unknown error in handling IPythonApp.exec_files:") self.shell.showtraceback() def _run_cmd_line_code(self): """Run code or file specified at the command-line""" if self.code_to_run: line = self.code_to_run try: self.log.info("Running code given at command line (c=): %s" % line) self.shell.run_cell(line, store_history=False) except: self.log.warn("Error in executing line in user namespace: %s" % line) self.shell.showtraceback() # Like Python itself, ignore the second if the first of these is present elif self.file_to_run: fname = self.file_to_run try: self._exec_file(fname) except: self.log.warn("Error in executing file in user namespace: %s" % fname) self.shell.showtraceback() def start(self): # perform any prexec steps: if self.interact: self.log.debug("Starting IPython's mainloop...") self.shell.mainloop() else: self.log.debug("IPython not interactive...") 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') cl = PyFileConfigLoader(default_config_file_name, profile_dir) config = cl.load_config() return config def launch_new_instance(): """Create and run a full blown IPython instance""" app = IPythonApp() app.initialize() # print app.config # print app.profile_dir.location app.start() if __name__ == '__main__': launch_new_instance()