#!/usr/bin/env python # encoding: utf-8 """ An application for IPython Authors: * Brian Granger * Fernando Perez Notes ----- """ #----------------------------------------------------------------------------- # Copyright (C) 2008-2009 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 #----------------------------------------------------------------------------- import os import sys import traceback from copy import deepcopy from IPython.utils.ipstruct import Struct from IPython.utils.genutils import get_ipython_dir, filefind from IPython.config.loader import ( IPythonArgParseConfigLoader, PyFileConfigLoader ) #----------------------------------------------------------------------------- # Classes and functions #----------------------------------------------------------------------------- class ApplicationError(Exception): pass class Application(object): """Load a config, construct an app and run it. """ config_file_name = 'ipython_config.py' name = 'ipython' debug = False def __init__(self): pass def start(self): """Start the application.""" self.attempt(self.create_default_config) self.attempt(self.pre_load_command_line_config) self.attempt(self.load_command_line_config, action='abort') self.attempt(self.post_load_command_line_config) self.attempt(self.find_ipythondir) self.attempt(self.find_config_file_name) self.attempt(self.find_config_file_paths) self.attempt(self.pre_load_file_config) self.attempt(self.load_file_config) self.attempt(self.post_load_file_config) self.attempt(self.merge_configs) self.attempt(self.pre_construct) self.attempt(self.construct) self.attempt(self.post_construct) self.attempt(self.start_app) #------------------------------------------------------------------------- # Various stages of Application creation #------------------------------------------------------------------------- def create_default_config(self): """Create defaults that can't be set elsewhere.""" self.default_config = Struct() self.default_config.IPYTHONDIR = get_ipython_dir() def create_command_line_config(self): """Create and return a command line config loader.""" return IPythonArgParseConfigLoader(description=self.name) def pre_load_command_line_config(self): """Do actions just before loading the command line config.""" pass def load_command_line_config(self): """Load the command line config. This method also sets ``self.debug``. """ loader = self.create_command_line_config() self.command_line_config = loader.load_config() try: self.debug = self.command_line_config.DEBUG except AttributeError: pass # use class default self.log("Default config loaded:", self.default_config) self.log("Command line config loaded:", self.command_line_config) def post_load_command_line_config(self): """Do actions just after loading the command line config.""" pass def find_ipythondir(self): """Set the IPython directory. This sets ``self.ipythondir``, but the actual value that is passed to the application is kept in either ``self.default_config`` or ``self.command_line_config``. This also added ``self.ipythondir`` to ``sys.path`` so config files there can be references by other config files. """ try: self.ipythondir = self.command_line_config.IPYTHONDIR except AttributeError: self.ipythondir = self.default_config.IPYTHONDIR sys.path.append(os.path.abspath(self.ipythondir)) if not os.path.isdir(self.ipythondir): os.makedirs(self.ipythondir, mode = 0777) self.log("IPYTHONDIR set to: %s" % self.ipythondir) def find_config_file_name(self): """Find the config file name for this application. If a profile has been set at the command line, this will resolve it. The search paths for the config file are set in :meth:`find_config_file_paths` and then passed to the config file loader where they are resolved to an absolute path. """ try: self.config_file_name = self.command_line_config.CONFIG_FILE except AttributeError: pass try: self.profile_name = self.command_line_config.PROFILE name_parts = self.config_file_name.split('.') name_parts.insert(1, '_' + self.profile_name + '.') self.config_file_name = ''.join(name_parts) except AttributeError: pass def find_config_file_paths(self): """Set the search paths for resolving the config file.""" self.config_file_paths = (os.getcwd(), self.ipythondir) def pre_load_file_config(self): """Do actions before the config file is loaded.""" pass def load_file_config(self): """Load the config file. This tries to load the config file from disk. If successful, the ``CONFIG_FILE`` config variable is set to the resolved config file location. If not successful, an empty config is used. """ loader = PyFileConfigLoader(self.config_file_name, self.config_file_paths) try: self.file_config = loader.load_config() self.file_config.CONFIG_FILE = loader.full_filename except IOError: self.log("Config file not found, skipping: %s" % \ self.config_file_name) self.file_config = Struct() else: self.log("Config file loaded: %s" % loader.full_filename, self.file_config) def post_load_file_config(self): """Do actions after the config file is loaded.""" pass def merge_configs(self): """Merge the default, command line and file config objects.""" config = Struct() config.update(self.default_config) config.update(self.file_config) config.update(self.command_line_config) self.master_config = config self.log("Master config created:", self.master_config) def pre_construct(self): """Do actions after the config has been built, but before construct.""" pass def construct(self): """Construct the main components that make up this app.""" self.log("Constructing components for application...") def post_construct(self): """Do actions after construct, but before starting the app.""" pass def start_app(self): """Actually start the app.""" self.log("Starting application...") #------------------------------------------------------------------------- # Utility methods #------------------------------------------------------------------------- def abort(self): """Abort the starting of the application.""" print "Aborting application: ", self.name sys.exit(1) def exit(self): print "Exiting application: ", self.name sys.exit(1) def attempt(self, func, action='abort'): try: func() except: if action == 'abort': self.print_traceback() self.abort() elif action == 'exit': self.exit() def print_traceback(self): print "Error in appliction startup: ", self.name print traceback.print_exc() def log(self, *args): if self.debug: for arg in args: print "[%s] %s" % (self.name, arg)