application.py
317 lines
| 12.3 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r2185 | # encoding: utf-8 | ||
""" | ||||
Brian Granger
|
r2301 | An application for IPython. | ||
All top-level applications should use the classes in this module for | ||||
handling configuration and creating componenets. | ||||
The job of an :class:`Application` is to create the master configuration | ||||
Brian Granger
|
r2731 | object and then create the configurable objects, passing the config to them. | ||
Brian Granger
|
r2185 | |||
Authors: | ||||
* Brian Granger | ||||
* Fernando Perez | ||||
MinRK
|
r4023 | * Min RK | ||
Brian Granger
|
r2185 | |||
""" | ||||
#----------------------------------------------------------------------------- | ||||
MinRK
|
r4024 | # Copyright (C) 2008-2011 The IPython Development Team | ||
Brian Granger
|
r2185 | # | ||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
MinRK
|
r4122 | import glob | ||
Brian Granger
|
r2252 | import logging | ||
Brian Granger
|
r2200 | import os | ||
MinRK
|
r4023 | import shutil | ||
Brian Granger
|
r2185 | import sys | ||
Brian Granger
|
r2245 | |||
MinRK
|
r4023 | from IPython.config.application import Application | ||
from IPython.config.configurable import Configurable | ||||
from IPython.config.loader import Config | ||||
Fernando Perez
|
r2403 | from IPython.core import release, crashhandler | ||
MinRK
|
r4024 | from IPython.core.profiledir import ProfileDir, ProfileDirError | ||
from IPython.utils.path import get_ipython_dir, get_ipython_package_dir | ||||
MinRK
|
r4023 | from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict | ||
Thomas Kluyver
|
r4738 | from IPython.utils import py3compat | ||
Brian Granger
|
r2185 | |||
#----------------------------------------------------------------------------- | ||||
# Classes and functions | ||||
#----------------------------------------------------------------------------- | ||||
MinRK
|
r4023 | |||
#----------------------------------------------------------------------------- | ||||
# Base Application Class | ||||
#----------------------------------------------------------------------------- | ||||
# aliases and flags | ||||
MinRK
|
r4214 | base_aliases = { | ||
'profile' : 'BaseIPythonApplication.profile', | ||||
'ipython-dir' : 'BaseIPythonApplication.ipython_dir', | ||||
'log-level' : 'Application.log_level', | ||||
} | ||||
Brian Granger
|
r2294 | |||
MinRK
|
r4023 | base_flags = dict( | ||
debug = ({'Application' : {'log_level' : logging.DEBUG}}, | ||||
"set log level to logging.DEBUG (maximize logging output)"), | ||||
quiet = ({'Application' : {'log_level' : logging.CRITICAL}}, | ||||
"set log level to logging.CRITICAL (minimize logging output)"), | ||||
init = ({'BaseIPythonApplication' : { | ||||
'copy_config_files' : True, | ||||
'auto_create' : True} | ||||
MinRK
|
r4247 | }, """Initialize profile with default config files. This is equivalent | ||
to running `ipython profile create <profile>` prior to startup. | ||||
""") | ||||
MinRK
|
r4023 | ) | ||
class BaseIPythonApplication(Application): | ||||
name = Unicode(u'ipython') | ||||
description = Unicode(u'IPython: an enhanced interactive Python shell.') | ||||
version = Unicode(release.version) | ||||
aliases = Dict(base_aliases) | ||||
flags = Dict(base_flags) | ||||
MinRK
|
r4114 | classes = List([ProfileDir]) | ||
MinRK
|
r4023 | |||
# Track whether the config_file has changed, | ||||
# because some logic happens only if we aren't using the default. | ||||
config_file_specified = Bool(False) | ||||
config_file_name = Unicode(u'ipython_config.py') | ||||
MinRK
|
r4025 | def _config_file_name_default(self): | ||
return self.name.replace('-','_') + u'_config.py' | ||||
MinRK
|
r4023 | def _config_file_name_changed(self, name, old, new): | ||
if new != old: | ||||
self.config_file_specified = True | ||||
# The directory that contains IPython's builtin profiles. | ||||
builtin_profile_dir = Unicode( | ||||
os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default') | ||||
) | ||||
config_file_paths = List(Unicode) | ||||
def _config_file_paths_default(self): | ||||
return [os.getcwdu()] | ||||
Thomas Kluyver
|
r4738 | profile = Unicode(u'', config=True, | ||
MinRK
|
r4023 | help="""The IPython profile to use.""" | ||
) | ||||
Thomas Kluyver
|
r4738 | def _profile_default(self): | ||
return "python3" if py3compat.PY3 else "default" | ||||
MinRK
|
r4023 | def _profile_changed(self, name, old, new): | ||
self.builtin_profile_dir = os.path.join( | ||||
get_ipython_package_dir(), u'config', u'profile', new | ||||
) | ||||
MinRK
|
r4025 | |||
MinRK
|
r4023 | ipython_dir = Unicode(get_ipython_dir(), config=True, | ||
help=""" | ||||
The name of the IPython directory. This directory is used for logging | ||||
configuration (through profiles), history storage, etc. The default | ||||
is usually $HOME/.ipython. This options can also be specified through | ||||
the environment variable IPYTHON_DIR. | ||||
Brian Granger
|
r2294 | """ | ||
MinRK
|
r4023 | ) | ||
overwrite = Bool(False, config=True, | ||||
help="""Whether to overwrite existing config files when copying""") | ||||
auto_create = Bool(False, config=True, | ||||
help="""Whether to create profile dir if it doesn't exist""") | ||||
config_files = List(Unicode) | ||||
def _config_files_default(self): | ||||
return [u'ipython_config.py'] | ||||
copy_config_files = Bool(False, config=True, | ||||
MinRK
|
r4025 | help="""Whether to install the default config files into the profile dir. | ||
If a new profile is being created, and IPython contains config files for that | ||||
profile, then they will be staged into the new directory. Otherwise, | ||||
default config files will be automatically generated. | ||||
""") | ||||
MinRK
|
r4023 | |||
# The class to use as the crash handler. | ||||
crash_handler_class = Type(crashhandler.CrashHandler) | ||||
def __init__(self, **kwargs): | ||||
super(BaseIPythonApplication, self).__init__(**kwargs) | ||||
# ensure even default IPYTHON_DIR exists | ||||
if not os.path.exists(self.ipython_dir): | ||||
self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir) | ||||
#------------------------------------------------------------------------- | ||||
# Various stages of Application creation | ||||
#------------------------------------------------------------------------- | ||||
Brian Granger
|
r2200 | |||
MinRK
|
r4023 | def init_crash_handler(self): | ||
"""Create a crash handler, typically setting sys.excepthook to it.""" | ||||
self.crash_handler = self.crash_handler_class(self) | ||||
sys.excepthook = self.crash_handler | ||||
Brian Granger
|
r2185 | |||
MinRK
|
r4023 | def _ipython_dir_changed(self, name, old, new): | ||
if old in sys.path: | ||||
sys.path.remove(old) | ||||
sys.path.append(os.path.abspath(new)) | ||||
if not os.path.isdir(new): | ||||
os.makedirs(new, mode=0777) | ||||
readme = os.path.join(new, 'README') | ||||
if not os.path.exists(readme): | ||||
path = os.path.join(get_ipython_package_dir(), u'config', u'profile') | ||||
shutil.copy(os.path.join(path, 'README'), readme) | ||||
self.log.debug("IPYTHON_DIR set to: %s" % new) | ||||
def load_config_file(self, suppress_errors=True): | ||||
Brian Granger
|
r2200 | """Load the config file. | ||
MinRK
|
r4023 | |||
Thomas Kluyver
|
r3458 | By default, errors in loading config are handled, and a warning | ||
printed on screen. For testing, the suppress_errors option is set | ||||
to False, so errors will make tests fail. | ||||
Brian Granger
|
r2200 | """ | ||
MinRK
|
r4564 | self.log.debug("Searching path %s for config files", self.config_file_paths) | ||
MinRK
|
r4025 | base_config = 'ipython_config.py' | ||
self.log.debug("Attempting to load config file: %s" % | ||||
base_config) | ||||
try: | ||||
Application.load_config_file( | ||||
self, | ||||
base_config, | ||||
path=self.config_file_paths | ||||
) | ||||
except IOError: | ||||
# ignore errors loading parent | ||||
MinRK
|
r4564 | self.log.debug("Config file %s not found", base_config) | ||
MinRK
|
r4025 | pass | ||
if self.config_file_name == base_config: | ||||
# don't load secondary config | ||||
return | ||||
Fernando Perez
|
r2357 | self.log.debug("Attempting to load config file: %s" % | ||
self.config_file_name) | ||||
Brian Granger
|
r2200 | try: | ||
MinRK
|
r4023 | Application.load_config_file( | ||
self, | ||||
self.config_file_name, | ||||
path=self.config_file_paths | ||||
) | ||||
Brian Granger
|
r2200 | except IOError: | ||
Brian Granger
|
r2257 | # Only warn if the default config file was NOT being used. | ||
MinRK
|
r4023 | if self.config_file_specified: | ||
MinRK
|
r4564 | msg = self.log.warn | ||
else: | ||||
msg = self.log.debug | ||||
msg("Config file not found, skipping: %s", self.config_file_name) | ||||
Brian Granger
|
r2252 | except: | ||
MinRK
|
r4023 | # For testing purposes. | ||
if not suppress_errors: | ||||
Thomas Kluyver
|
r3451 | raise | ||
Fernando Perez
|
r2357 | self.log.warn("Error loading config file: %s" % | ||
Brian Granger
|
r2328 | self.config_file_name, exc_info=True) | ||
Brian Granger
|
r2294 | |||
MinRK
|
r4023 | def init_profile_dir(self): | ||
"""initialize the profile dir""" | ||||
try: | ||||
# location explicitly specified: | ||||
location = self.config.ProfileDir.location | ||||
except AttributeError: | ||||
# location not specified, find by profile name | ||||
Brian Granger
|
r2270 | try: | ||
MinRK
|
r4023 | p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config) | ||
except ProfileDirError: | ||||
# not found, maybe create it (always create default profile) | ||||
Thomas Kluyver
|
r4738 | if self.auto_create or self.profile==self._profile_default(): | ||
MinRK
|
r4023 | try: | ||
p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config) | ||||
except ProfileDirError: | ||||
self.log.fatal("Could not create profile: %r"%self.profile) | ||||
self.exit(1) | ||||
else: | ||||
self.log.info("Created profile dir: %r"%p.location) | ||||
else: | ||||
self.log.fatal("Profile %r not found."%self.profile) | ||||
self.exit(1) | ||||
else: | ||||
self.log.info("Using existing profile dir: %r"%p.location) | ||||
Brian Granger
|
r2303 | else: | ||
MinRK
|
r4023 | # location is fully specified | ||
try: | ||||
p = ProfileDir.find_profile_dir(location, self.config) | ||||
except ProfileDirError: | ||||
# not found, maybe create it | ||||
if self.auto_create: | ||||
try: | ||||
p = ProfileDir.create_profile_dir(location, self.config) | ||||
except ProfileDirError: | ||||
self.log.fatal("Could not create profile directory: %r"%location) | ||||
self.exit(1) | ||||
else: | ||||
self.log.info("Creating new profile dir: %r"%location) | ||||
else: | ||||
self.log.fatal("Profile directory %r not found."%location) | ||||
self.exit(1) | ||||
else: | ||||
self.log.info("Using existing profile dir: %r"%location) | ||||
self.profile_dir = p | ||||
self.config_file_paths.append(p.location) | ||||
def init_config_files(self): | ||||
"""[optionally] copy default config files into profile dir.""" | ||||
# copy config files | ||||
MinRK
|
r4122 | path = self.builtin_profile_dir | ||
MinRK
|
r4023 | if self.copy_config_files: | ||
src = self.profile | ||||
MinRK
|
r4025 | cfg = self.config_file_name | ||
if path and os.path.exists(os.path.join(path, cfg)): | ||||
self.log.warn("Staging %r from %s into %r [overwrite=%s]"%( | ||||
cfg, src, self.profile_dir.location, self.overwrite) | ||||
) | ||||
MinRK
|
r4023 | self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite) | ||
MinRK
|
r4025 | else: | ||
self.stage_default_config_file() | ||||
MinRK
|
r4122 | else: | ||
# Still stage *bundled* config files, but not generated ones | ||||
# This is necessary for `ipython profile=sympy` to load the profile | ||||
# on the first go | ||||
files = glob.glob(os.path.join(path, '*.py')) | ||||
for fullpath in files: | ||||
cfg = os.path.basename(fullpath) | ||||
if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False): | ||||
# file was copied | ||||
self.log.warn("Staging bundled %s from %s into %r"%( | ||||
cfg, self.profile, self.profile_dir.location) | ||||
) | ||||
MinRK
|
r4025 | |||
def stage_default_config_file(self): | ||||
"""auto generate default config file, and stage it into the profile.""" | ||||
s = self.generate_config_file() | ||||
fname = os.path.join(self.profile_dir.location, self.config_file_name) | ||||
if self.overwrite or not os.path.exists(fname): | ||||
self.log.warn("Generating default config file: %r"%(fname)) | ||||
with open(fname, 'w') as f: | ||||
f.write(s) | ||||
MinRK
|
r4023 | |||
def initialize(self, argv=None): | ||||
MinRK
|
r4106 | # don't hook up crash handler before parsing command-line | ||
MinRK
|
r4023 | self.parse_command_line(argv) | ||
MinRK
|
r4106 | self.init_crash_handler() | ||
MinRK
|
r4025 | if self.subapp is not None: | ||
# stop here if subapp is taking over | ||||
return | ||||
MinRK
|
r4023 | cl_config = self.config | ||
self.init_profile_dir() | ||||
self.init_config_files() | ||||
self.load_config_file() | ||||
# enforce cl-opts override configfile opts: | ||||
self.update_config(cl_config) | ||||
Brian Granger
|
r2296 | |||