From 91ebc3641fc4aff11dce2c9890bfddd230455930 2014-10-29 22:46:51 From: Min RK Date: 2014-10-29 22:46:51 Subject: [PATCH] add system-wide IPython config Mainly for use in multi-user administrated environments - config files in `/usr[/local]/etc/ipython` will be loaded - change priority config loading to load all found files in priority order instead of loading only the first found. --- diff --git a/IPython/config/application.py b/IPython/config/application.py index 83f171e..298b0fc 100644 --- a/IPython/config/application.py +++ b/IPython/config/application.py @@ -501,27 +501,32 @@ class Application(SingletonConfigurable): yield each config object in turn. """ - pyloader = PyFileConfigLoader(basefilename+'.py', path=path, log=log) - jsonloader = JSONFileConfigLoader(basefilename+'.json', path=path, log=log) - config = None - for loader in [pyloader, jsonloader]: - try: - config = loader.load_config() - except ConfigFileNotFound: - pass - except Exception: - # try to get the full filename, but it will be empty in the - # unlikely event that the error raised before filefind finished - filename = loader.full_filename or basefilename - # problem while running the file - if log: - log.error("Exception while loading config file %s", - filename, exc_info=True) - else: - if log: - log.debug("Loaded config file: %s", loader.full_filename) - if config: - yield config + + if not isinstance(path, list): + path = [path] + for path in path[::-1]: + # path list is in descending priority order, so load files backwards: + pyloader = PyFileConfigLoader(basefilename+'.py', path=path, log=log) + jsonloader = JSONFileConfigLoader(basefilename+'.json', path=path, log=log) + config = None + for loader in [pyloader, jsonloader]: + try: + config = loader.load_config() + except ConfigFileNotFound: + pass + except Exception: + # try to get the full filename, but it will be empty in the + # unlikely event that the error raised before filefind finished + filename = loader.full_filename or basefilename + # problem while running the file + if log: + log.error("Exception while loading config file %s", + filename, exc_info=True) + else: + if log: + log.debug("Loaded config file: %s", loader.full_filename) + if config: + yield config raise StopIteration diff --git a/IPython/config/tests/test_application.py b/IPython/config/tests/test_application.py index d40939c..a03d548 100644 --- a/IPython/config/tests/test_application.py +++ b/IPython/config/tests/test_application.py @@ -1,27 +1,18 @@ # coding: utf-8 """ Tests for IPython.config.application.Application - -Authors: - -* Brian Granger """ -#----------------------------------------------------------------------------- -# Copyright (C) 2008-2011 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 -#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. import logging +import os from io import StringIO from unittest import TestCase +pjoin = os.path.join + import nose.tools as nt from IPython.config.configurable import Configurable @@ -31,13 +22,11 @@ from IPython.config.application import ( Application ) +from IPython.utils.tempdir import TemporaryDirectory from IPython.utils.traitlets import ( Bool, Unicode, Integer, List, Dict ) -#----------------------------------------------------------------------------- -# Code -#----------------------------------------------------------------------------- class Foo(Configurable): @@ -189,5 +178,21 @@ class TestApplication(TestCase): def test_unicode_argv(self): app = MyApp() app.parse_command_line(['ünîcødé']) - + + def test_multi_file(self): + app = MyApp() + app.log = logging.getLogger() + name = 'config.py' + with TemporaryDirectory('_1') as td1: + with open(pjoin(td1, name), 'w') as f1: + f1.write("get_config().MyApp.Bar.b = 1") + with TemporaryDirectory('_2') as td2: + with open(pjoin(td2, name), 'w') as f2: + f2.write("get_config().MyApp.Bar.b = 2") + app.load_config_file(name, path=[td2, td1]) + app.init_bar() + self.assertEqual(app.bar.b, 2) + app.load_config_file(name, path=[td1, td2]) + app.init_bar() + self.assertEqual(app.bar.b, 1) diff --git a/IPython/core/application.py b/IPython/core/application.py index 7770013..613cbfb 100644 --- a/IPython/core/application.py +++ b/IPython/core/application.py @@ -7,25 +7,10 @@ handling configuration and creating configurables. The job of an :class:`Application` is to create the master configuration object and then create the configurable objects, passing the config to them. - -Authors: - -* Brian Granger -* Fernando Perez -* Min RK - """ -#----------------------------------------------------------------------------- -# Copyright (C) 2008 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 -#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. import atexit import glob @@ -42,15 +27,19 @@ from IPython.utils.path import get_ipython_dir, get_ipython_package_dir, ensure_ from IPython.utils import py3compat from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instance -#----------------------------------------------------------------------------- -# Classes and functions -#----------------------------------------------------------------------------- +if os.name == 'nt': + programdata = os.environ.get('PROGRAMDATA', None) + if programdata: + SYSTEM_CONFIG_DIRS = [pjoin(programdata, 'ipython')] + else: # PROGRAMDATA is not defined by default on XP. + SYSTEM_CONFIG_DIRS = [] +else: + SYSTEM_CONFIG_DIRS = [ + "/usr/local/etc/ipython", + "/etc/ipython", + ] -#----------------------------------------------------------------------------- -# Base Application Class -#----------------------------------------------------------------------------- - # aliases and flags base_aliases = { @@ -100,7 +89,7 @@ class BaseIPythonApplication(Application): 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 [py3compat.getcwd()] @@ -336,6 +325,7 @@ class BaseIPythonApplication(Application): def init_config_files(self): """[optionally] copy default config files into profile dir.""" + self.config_file_paths.extend(SYSTEM_CONFIG_DIRS) # copy config files path = self.builtin_profile_dir if self.copy_config_files: