Show More
@@ -118,6 +118,9 b' class Application(SingletonConfigurable):' | |||||
118 | option_description = Unicode(option_description) |
|
118 | option_description = Unicode(option_description) | |
119 | keyvalue_description = Unicode(keyvalue_description) |
|
119 | keyvalue_description = Unicode(keyvalue_description) | |
120 | subcommand_description = Unicode(subcommand_description) |
|
120 | subcommand_description = Unicode(subcommand_description) | |
|
121 | ||||
|
122 | python_config_loader_class = PyFileConfigLoader | |||
|
123 | json_config_loader_class = JSONFileConfigLoader | |||
121 |
|
124 | |||
122 | # The usage and example string that goes at the end of the help string. |
|
125 | # The usage and example string that goes at the end of the help string. | |
123 | examples = Unicode() |
|
126 | examples = Unicode() | |
@@ -507,8 +510,8 b' class Application(SingletonConfigurable):' | |||||
507 | path = [path] |
|
510 | path = [path] | |
508 | for path in path[::-1]: |
|
511 | for path in path[::-1]: | |
509 | # path list is in descending priority order, so load files backwards: |
|
512 | # path list is in descending priority order, so load files backwards: | |
510 |
pyloader = |
|
513 | pyloader = cls.python_config_loader_class(basefilename+'.py', path=path, log=log) | |
511 |
jsonloader = |
|
514 | jsonloader = cls.json_config_loader_class(basefilename+'.json', path=path, log=log) | |
512 | config = None |
|
515 | config = None | |
513 | for loader in [pyloader, jsonloader]: |
|
516 | for loader in [pyloader, jsonloader]: | |
514 | try: |
|
517 | try: | |
@@ -551,9 +554,7 b' class Application(SingletonConfigurable):' | |||||
551 |
|
554 | |||
552 | def generate_config_file(self): |
|
555 | def generate_config_file(self): | |
553 | """generate default config file from Configurables""" |
|
556 | """generate default config file from Configurables""" | |
554 | lines = ["# Configuration file for %s."%self.name] |
|
557 | lines = ["# Configuration file for %s." % self.name] | |
555 | lines.append('') |
|
|||
556 | lines.append('c = get_config()') |
|
|||
557 | lines.append('') |
|
558 | lines.append('') | |
558 | for cls in self._config_classes: |
|
559 | for cls in self._config_classes: | |
559 | lines.append(cls.class_config_section()) |
|
560 | lines.append(cls.class_config_section()) |
@@ -436,52 +436,31 b' class PyFileConfigLoader(FileConfigLoader):' | |||||
436 | raise ConfigFileNotFound(str(e)) |
|
436 | raise ConfigFileNotFound(str(e)) | |
437 | self._read_file_as_dict() |
|
437 | self._read_file_as_dict() | |
438 | return self.config |
|
438 | return self.config | |
439 |
|
439 | |||
440 |
|
440 | def load_subconfig(self, fname, path=None): | ||
|
441 | """Injected into config file namespace as load_subconfig""" | |||
|
442 | if path is None: | |||
|
443 | path = self.path | |||
|
444 | ||||
|
445 | loader = self.__class__(fname, path) | |||
|
446 | try: | |||
|
447 | sub_config = loader.load_config() | |||
|
448 | except ConfigFileNotFound: | |||
|
449 | # Pass silently if the sub config is not there, | |||
|
450 | # treat it as an empty config file. | |||
|
451 | pass | |||
|
452 | else: | |||
|
453 | self.config.merge(sub_config) | |||
|
454 | ||||
441 | def _read_file_as_dict(self): |
|
455 | def _read_file_as_dict(self): | |
442 | """Load the config file into self.config, with recursive loading.""" |
|
456 | """Load the config file into self.config, with recursive loading.""" | |
443 | # This closure is made available in the namespace that is used |
|
|||
444 | # to exec the config file. It allows users to call |
|
|||
445 | # load_subconfig('myconfig.py') to load config files recursively. |
|
|||
446 | # It needs to be a closure because it has references to self.path |
|
|||
447 | # and self.config. The sub-config is loaded with the same path |
|
|||
448 | # as the parent, but it uses an empty config which is then merged |
|
|||
449 | # with the parents. |
|
|||
450 |
|
||||
451 | # If a profile is specified, the config file will be loaded |
|
|||
452 | # from that profile |
|
|||
453 |
|
||||
454 | def load_subconfig(fname, profile=None): |
|
|||
455 | # import here to prevent circular imports |
|
|||
456 | from IPython.core.profiledir import ProfileDir, ProfileDirError |
|
|||
457 | if profile is not None: |
|
|||
458 | try: |
|
|||
459 | profile_dir = ProfileDir.find_profile_dir_by_name( |
|
|||
460 | get_ipython_dir(), |
|
|||
461 | profile, |
|
|||
462 | ) |
|
|||
463 | except ProfileDirError: |
|
|||
464 | return |
|
|||
465 | path = profile_dir.location |
|
|||
466 | else: |
|
|||
467 | path = self.path |
|
|||
468 | loader = PyFileConfigLoader(fname, path) |
|
|||
469 | try: |
|
|||
470 | sub_config = loader.load_config() |
|
|||
471 | except ConfigFileNotFound: |
|
|||
472 | # Pass silently if the sub config is not there. This happens |
|
|||
473 | # when a user s using a profile, but not the default config. |
|
|||
474 | pass |
|
|||
475 | else: |
|
|||
476 | self.config.merge(sub_config) |
|
|||
477 |
|
||||
478 | # Again, this needs to be a closure and should be used in config |
|
|||
479 | # files to get the config being loaded. |
|
|||
480 | def get_config(): |
|
457 | def get_config(): | |
|
458 | """Unnecessary now, but a deprecation warning is more trouble than it's worth.""" | |||
481 | return self.config |
|
459 | return self.config | |
482 |
|
460 | |||
483 | namespace = dict( |
|
461 | namespace = dict( | |
484 |
|
|
462 | c=self.config, | |
|
463 | load_subconfig=self.load_subconfig, | |||
485 | get_config=get_config, |
|
464 | get_config=get_config, | |
486 | __file__=self.full_filename, |
|
465 | __file__=self.full_filename, | |
487 | ) |
|
466 | ) |
@@ -8,7 +8,6 b' import json' | |||||
8 | import os |
|
8 | import os | |
9 |
|
9 | |||
10 | from IPython.config import LoggingConfigurable |
|
10 | from IPython.config import LoggingConfigurable | |
11 | from IPython.utils.path import locate_profile |
|
|||
12 | from IPython.utils.py3compat import PY3 |
|
11 | from IPython.utils.py3compat import PY3 | |
13 | from IPython.utils.traitlets import Unicode |
|
12 | from IPython.utils.traitlets import Unicode | |
14 |
|
13 | |||
@@ -35,22 +34,12 b' def recursive_update(target, new):' | |||||
35 |
|
34 | |||
36 |
|
35 | |||
37 | class BaseJSONConfigManager(LoggingConfigurable): |
|
36 | class BaseJSONConfigManager(LoggingConfigurable): | |
38 |
"""General config manager |
|
37 | """General JSON config manager | |
39 |
|
38 | |||
40 | Deals with persisting/storing config in a json file |
|
39 | Deals with persisting/storing config in a json file | |
41 | in IPython profile |
|
|||
42 | """ |
|
40 | """ | |
43 |
|
41 | |||
44 |
|
|
42 | config_dir = Unicode('.') | |
45 | def _profile_dir_default(self): |
|
|||
46 | return locate_profile() |
|
|||
47 |
|
||||
48 | @property |
|
|||
49 | def config_dir(self): |
|
|||
50 | return self._config_dir() |
|
|||
51 |
|
||||
52 | def _config_dir(self): |
|
|||
53 | return self.profile_dir |
|
|||
54 |
|
43 | |||
55 | def ensure_config_dir_exists(self): |
|
44 | def ensure_config_dir_exists(self): | |
56 | try: |
|
45 | try: |
@@ -20,7 +20,7 b' import shutil' | |||||
20 | import sys |
|
20 | import sys | |
21 |
|
21 | |||
22 | from IPython.config.application import Application, catch_config_error |
|
22 | from IPython.config.application import Application, catch_config_error | |
23 | from IPython.config.loader import ConfigFileNotFound |
|
23 | from IPython.config.loader import ConfigFileNotFound, PyFileConfigLoader | |
24 | from IPython.core import release, crashhandler |
|
24 | from IPython.core import release, crashhandler | |
25 | from IPython.core.profiledir import ProfileDir, ProfileDirError |
|
25 | from IPython.core.profiledir import ProfileDir, ProfileDirError | |
26 | from IPython.utils.path import get_ipython_dir, get_ipython_package_dir, ensure_dir_exists |
|
26 | from IPython.utils.path import get_ipython_dir, get_ipython_package_dir, ensure_dir_exists | |
@@ -63,6 +63,19 b' base_flags = dict(' | |||||
63 | """) |
|
63 | """) | |
64 | ) |
|
64 | ) | |
65 |
|
65 | |||
|
66 | class ProfileAwareConfigLoader(PyFileConfigLoader): | |||
|
67 | """A Python file config loader that is aware of IPython profiles.""" | |||
|
68 | def load_subconfig(self, fname, path=None, profile=None): | |||
|
69 | if profile is not None: | |||
|
70 | try: | |||
|
71 | profile_dir = ProfileDir.find_profile_dir_by_name( | |||
|
72 | get_ipython_dir(), | |||
|
73 | profile, | |||
|
74 | ) | |||
|
75 | except ProfileDirError: | |||
|
76 | return | |||
|
77 | path = profile_dir.location | |||
|
78 | return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path) | |||
66 |
|
79 | |||
67 | class BaseIPythonApplication(Application): |
|
80 | class BaseIPythonApplication(Application): | |
68 |
|
81 | |||
@@ -73,6 +86,9 b' class BaseIPythonApplication(Application):' | |||||
73 | aliases = Dict(base_aliases) |
|
86 | aliases = Dict(base_aliases) | |
74 | flags = Dict(base_flags) |
|
87 | flags = Dict(base_flags) | |
75 | classes = List([ProfileDir]) |
|
88 | classes = List([ProfileDir]) | |
|
89 | ||||
|
90 | # enable `load_subconfig('cfg.py', profile='name')` | |||
|
91 | python_config_loader_class = ProfileAwareConfigLoader | |||
76 |
|
92 | |||
77 | # Track whether the config_file has changed, |
|
93 | # Track whether the config_file has changed, | |
78 | # because some logic happens only if we aren't using the default. |
|
94 | # because some logic happens only if we aren't using the default. |
1 | NO CONTENT: file renamed from IPython/config/profile/README_STARTUP to IPython/core/profile/README_STARTUP |
|
NO CONTENT: file renamed from IPython/config/profile/README_STARTUP to IPython/core/profile/README_STARTUP |
@@ -112,7 +112,7 b' def list_profiles_in(path):' | |||||
112 |
|
112 | |||
113 | def list_bundled_profiles(): |
|
113 | def list_bundled_profiles(): | |
114 | """list profiles that are bundled with IPython.""" |
|
114 | """list profiles that are bundled with IPython.""" | |
115 |
path = os.path.join(get_ipython_package_dir(), u'co |
|
115 | path = os.path.join(get_ipython_package_dir(), u'core', u'profile') | |
116 | files = os.listdir(path) |
|
116 | files = os.listdir(path) | |
117 | profiles = [] |
|
117 | profiles = [] | |
118 | for profile in files: |
|
118 | for profile in files: |
@@ -114,7 +114,7 b' class ProfileDir(LoggingConfigurable):' | |||||
114 | self._mkdir(self.startup_dir) |
|
114 | self._mkdir(self.startup_dir) | |
115 |
|
115 | |||
116 | readme = os.path.join(self.startup_dir, 'README') |
|
116 | readme = os.path.join(self.startup_dir, 'README') | |
117 |
src = os.path.join(get_ipython_package_dir(), u'co |
|
117 | src = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'README_STARTUP') | |
118 |
|
118 | |||
119 | if not os.path.exists(src): |
|
119 | if not os.path.exists(src): | |
120 | self.log.warn("Could not copy README_STARTUP to startup dir. Source file %s does not exist.", src) |
|
120 | self.log.warn("Could not copy README_STARTUP to startup dir. Source file %s does not exist.", src) | |
@@ -169,7 +169,7 b' class ProfileDir(LoggingConfigurable):' | |||||
169 | if os.path.isfile(dst) and not overwrite: |
|
169 | if os.path.isfile(dst) and not overwrite: | |
170 | return False |
|
170 | return False | |
171 | if path is None: |
|
171 | if path is None: | |
172 |
path = os.path.join(get_ipython_package_dir(), u'co |
|
172 | path = os.path.join(get_ipython_package_dir(), u'core', u'profile', u'default') | |
173 | src = os.path.join(path, config_file) |
|
173 | src = os.path.join(path, config_file) | |
174 | shutil.copy(src, dst) |
|
174 | shutil.copy(src, dst) | |
175 | return True |
|
175 | return True |
@@ -149,9 +149,8 b' def test_list_profiles_in():' | |||||
149 |
|
149 | |||
150 | def test_list_bundled_profiles(): |
|
150 | def test_list_bundled_profiles(): | |
151 | # This variable will need to be updated when a new profile gets bundled |
|
151 | # This variable will need to be updated when a new profile gets bundled | |
152 | bundled_true = [u'cluster', u'math', u'pysh', u'sympy'] |
|
|||
153 | bundled = sorted(list_bundled_profiles()) |
|
152 | bundled = sorted(list_bundled_profiles()) | |
154 |
nt.assert_equal(bundled, |
|
153 | nt.assert_equal(bundled, []) | |
155 |
|
154 | |||
156 |
|
155 | |||
157 | def test_profile_create_ipython_dir(): |
|
156 | def test_profile_create_ipython_dir(): |
@@ -3,12 +3,19 b'' | |||||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | import os |
|
|||
7 |
|
||||
8 | from IPython.config.manager import BaseJSONConfigManager |
|
6 | from IPython.config.manager import BaseJSONConfigManager | |
|
7 | from IPython.utils.path import locate_profile | |||
|
8 | from IPython.utils.traitlets import Unicode | |||
9 |
|
9 | |||
10 | class ConfigManager(BaseJSONConfigManager): |
|
10 | class ConfigManager(BaseJSONConfigManager): | |
11 | """Config Manager used for storing notebook frontend config""" |
|
11 | """Config Manager used for storing notebook frontend config""" | |
|
12 | ||||
|
13 | profile = Unicode('default', config=True) | |||
|
14 | ||||
|
15 | profile_dir = Unicode(config=True) | |||
|
16 | ||||
|
17 | def _profile_dir_default(self): | |||
|
18 | return locate_profile(self.profile) | |||
12 |
|
19 | |||
13 | def _config_dir(self): |
|
20 | def _config_dir_default(self): | |
14 |
return |
|
21 | return self.profile_dir |
@@ -183,7 +183,7 b' def find_package_data():' | |||||
183 | os.chdir(cwd) |
|
183 | os.chdir(cwd) | |
184 |
|
184 | |||
185 | package_data = { |
|
185 | package_data = { | |
186 |
'IPython.co |
|
186 | 'IPython.core' : ['profile/README*'], | |
187 | 'IPython.core.tests' : ['*.png', '*.jpg'], |
|
187 | 'IPython.core.tests' : ['*.png', '*.jpg'], | |
188 | 'IPython.lib.tests' : ['*.wav'], |
|
188 | 'IPython.lib.tests' : ['*.wav'], | |
189 | 'IPython.testing.plugin' : ['*.txt'], |
|
189 | 'IPython.testing.plugin' : ['*.txt'], |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now