application.py
232 lines
| 7.8 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r2185 | #!/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 | |||
#----------------------------------------------------------------------------- | |||
Brian Granger
|
r2200 | import os | |
Brian Granger
|
r2185 | import sys | |
Brian Granger
|
r2200 | import traceback | |
Brian Granger
|
r2185 | from copy import deepcopy | |
from IPython.utils.ipstruct import Struct | |||
Brian Granger
|
r2200 | from IPython.utils.genutils import get_ipython_dir, filefind | |
from IPython.config.loader import ( | |||
IPythonArgParseConfigLoader, | |||
PyFileConfigLoader | |||
) | |||
Brian Granger
|
r2185 | ||
#----------------------------------------------------------------------------- | |||
# Classes and functions | |||
#----------------------------------------------------------------------------- | |||
class ApplicationError(Exception): | |||
pass | |||
class Application(object): | |||
Brian Granger
|
r2200 | """Load a config, construct an app and run it. | |
""" | |||
Brian Granger
|
r2185 | ||
Brian Granger
|
r2200 | config_file_name = 'ipython_config.py' | |
name = 'ipython' | |||
debug = False | |||
Brian Granger
|
r2185 | ||
def __init__(self): | |||
pass | |||
Brian Granger
|
r2187 | def start(self): | |
"""Start the application.""" | |||
Brian Granger
|
r2200 | self.attempt(self.create_default_config) | |
self.attempt(self.pre_load_command_line_config) | |||
self.attempt(self.load_command_line_config, action='exit') | |||
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) | |||
Brian Granger
|
r2187 | self.attempt(self.merge_configs) | |
Brian Granger
|
r2200 | self.attempt(self.pre_construct) | |
Brian Granger
|
r2187 | self.attempt(self.construct) | |
Brian Granger
|
r2200 | self.attempt(self.post_construct) | |
Brian Granger
|
r2187 | self.attempt(self.start_app) | |
#------------------------------------------------------------------------- | |||
# Various stages of Application creation | |||
#------------------------------------------------------------------------- | |||
Brian Granger
|
r2200 | def create_default_config(self): | |
"""Create defaults that can't be set elsewhere.""" | |||
self.default_config = Struct() | |||
self.default_config.IPYTHONDIR = get_ipython_dir() | |||
Brian Granger
|
r2187 | def create_command_line_config(self): | |
Brian Granger
|
r2200 | """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 | |||
Brian Granger
|
r2187 | ||
Brian Granger
|
r2200 | def load_command_line_config(self): | |
"""Load the command line config. | |||
Brian Granger
|
r2185 | ||
Brian Granger
|
r2200 | This method also sets ``self.debug``. | |
Brian Granger
|
r2185 | """ | |
Brian Granger
|
r2200 | 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.""" | |||
Brian Granger
|
r2185 | pass | |
Brian Granger
|
r2200 | 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)) | |||
Brian Granger
|
r2201 | if not os.path.isdir(self.ipythondir): | |
os.makedirs(self.ipythondir, mode = 0777) | |||
Brian Granger
|
r2200 | 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. | |||
""" | |||
if self.command_line_config.PROFILE_NAME is not None: | |||
self.profile_name = self.command_line_config.PROFILE_NAME | |||
name_parts = self.config_file_name.split('.') | |||
name_parts.insert(1, '_' + self.profile_name + '.') | |||
self.config_file_name = ''.join(name_parts) | |||
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.""" | |||
Brian Granger
|
r2185 | pass | |
Brian Granger
|
r2200 | 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) | |||
def post_load_file_config(self): | |||
"""Do actions after the config file is loaded.""" | |||
pass | |||
Brian Granger
|
r2185 | ||
Brian Granger
|
r2187 | def merge_configs(self): | |
Brian Granger
|
r2200 | """Merge the default, command line and file config objects.""" | |
Brian Granger
|
r2187 | config = Struct() | |
Brian Granger
|
r2200 | config.update(self.default_config) | |
config.update(self.command_line_config) | |||
config.update(self.file_config) | |||
Brian Granger
|
r2187 | self.master_config = config | |
Brian Granger
|
r2200 | self.log("Master config created:", self.master_config) | |
Brian Granger
|
r2185 | ||
Brian Granger
|
r2200 | def pre_construct(self): | |
"""Do actions after the config has been built, but before construct.""" | |||
Brian Granger
|
r2185 | pass | |
Brian Granger
|
r2200 | 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.""" | |||
Brian Granger
|
r2185 | pass | |
def start_app(self): | |||
"""Actually start the app.""" | |||
Brian Granger
|
r2200 | self.log("Starting application...") | |
Brian Granger
|
r2185 | ||
Brian Granger
|
r2187 | #------------------------------------------------------------------------- | |
# Utility methods | |||
#------------------------------------------------------------------------- | |||
Brian Granger
|
r2185 | def abort(self): | |
"""Abort the starting of the application.""" | |||
print "Aborting application: ", self.name | |||
sys.exit(1) | |||
Brian Granger
|
r2200 | def exit(self): | |
print "Exiting application: ", self.name | |||
sys.exit(1) | |||
def attempt(self, func, action='abort'): | |||
Brian Granger
|
r2185 | try: | |
func() | |||
except: | |||
Brian Granger
|
r2200 | if action == 'abort': | |
self.print_traceback() | |||
self.abort() | |||
elif action == 'exit': | |||
self.exit() | |||
def print_traceback(self): | |||
print "Error in appliction startup: ", self.name | |||
traceback.print_exc() | |||
def log(self, *args): | |||
if self.debug: | |||
for arg in args: | |||
print "[%s] %s" % (self.name, arg) |