diff --git a/IPython/config/application.py b/IPython/config/application.py index 3b848c3..bde3200 100644 --- a/IPython/config/application.py +++ b/IPython/config/application.py @@ -118,6 +118,9 @@ class Application(SingletonConfigurable): option_description = Unicode(option_description) keyvalue_description = Unicode(keyvalue_description) subcommand_description = Unicode(subcommand_description) + + python_config_loader_class = PyFileConfigLoader + json_config_loader_class = JSONFileConfigLoader # The usage and example string that goes at the end of the help string. examples = Unicode() @@ -507,8 +510,8 @@ class Application(SingletonConfigurable): 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) + pyloader = cls.python_config_loader_class(basefilename+'.py', path=path, log=log) + jsonloader = cls.json_config_loader_class(basefilename+'.json', path=path, log=log) config = None for loader in [pyloader, jsonloader]: try: diff --git a/IPython/config/loader.py b/IPython/config/loader.py index e57cc7e..fdd193f 100644 --- a/IPython/config/loader.py +++ b/IPython/config/loader.py @@ -436,52 +436,29 @@ class PyFileConfigLoader(FileConfigLoader): raise ConfigFileNotFound(str(e)) self._read_file_as_dict() return self.config - - + + def load_subconfig(self, fname, path=None): + """Injected into config file namespace as load_subconfig""" + if path is None: + path = self.path + + loader = self.__class__(fname, path) + try: + sub_config = loader.load_config() + except ConfigFileNotFound: + # Pass silently if the sub config is not there, + # treat it as an empty config file. + pass + else: + self.config.merge(sub_config) + def _read_file_as_dict(self): """Load the config file into self.config, with recursive loading.""" - # This closure is made available in the namespace that is used - # to exec the config file. It allows users to call - # load_subconfig('myconfig.py') to load config files recursively. - # It needs to be a closure because it has references to self.path - # and self.config. The sub-config is loaded with the same path - # as the parent, but it uses an empty config which is then merged - # with the parents. - - # If a profile is specified, the config file will be loaded - # from that profile - - def load_subconfig(fname, profile=None): - # import here to prevent circular imports - from IPython.core.profiledir import ProfileDir, ProfileDirError - if profile is not None: - try: - profile_dir = ProfileDir.find_profile_dir_by_name( - get_ipython_dir(), - profile, - ) - except ProfileDirError: - return - path = profile_dir.location - else: - path = self.path - loader = PyFileConfigLoader(fname, path) - try: - sub_config = loader.load_config() - except ConfigFileNotFound: - # Pass silently if the sub config is not there. This happens - # when a user s using a profile, but not the default config. - pass - else: - self.config.merge(sub_config) - - # Again, this needs to be a closure and should be used in config - # files to get the config being loaded. def get_config(): return self.config - + namespace = dict( - load_subconfig=load_subconfig, + load_subconfig=self.load_subconfig, get_config=get_config, __file__=self.full_filename, ) diff --git a/IPython/core/application.py b/IPython/core/application.py index 9741baa..87999d7 100644 --- a/IPython/core/application.py +++ b/IPython/core/application.py @@ -20,7 +20,7 @@ import shutil import sys from IPython.config.application import Application, catch_config_error -from IPython.config.loader import ConfigFileNotFound +from IPython.config.loader import ConfigFileNotFound, PyFileConfigLoader from IPython.core import release, crashhandler from IPython.core.profiledir import ProfileDir, ProfileDirError from IPython.utils.path import get_ipython_dir, get_ipython_package_dir, ensure_dir_exists @@ -63,6 +63,19 @@ base_flags = dict( """) ) +class ProfileAwareConfigLoader(PyFileConfigLoader): + """A Python file config loader that is aware of IPython profiles.""" + def load_subconfig(self, fname, path=None, profile=None): + if profile is not None: + try: + profile_dir = ProfileDir.find_profile_dir_by_name( + get_ipython_dir(), + profile, + ) + except ProfileDirError: + return + path = profile_dir.location + return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path) class BaseIPythonApplication(Application): @@ -73,6 +86,9 @@ class BaseIPythonApplication(Application): aliases = Dict(base_aliases) flags = Dict(base_flags) classes = List([ProfileDir]) + + # enable `load_subconfig('cfg.py', profile='name')` + python_config_loader_class = ProfileAwareConfigLoader # Track whether the config_file has changed, # because some logic happens only if we aren't using the default.