Show More
@@ -0,0 +1,3 b'' | |||
|
1 | * IPython config objects can be loaded from and serialized to JSON. | |
|
2 | JSON config file have the same base name as their ``.py`` counterpart, | |
|
3 | and will be loaded with higher priority if found. |
@@ -31,7 +31,7 b' from IPython.external.decorator import decorator' | |||
|
31 | 31 | |
|
32 | 32 | from IPython.config.configurable import SingletonConfigurable |
|
33 | 33 | from IPython.config.loader import ( |
|
34 | KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, | |
|
34 | KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, JSONFileConfigLoader | |
|
35 | 35 | ) |
|
36 | 36 | |
|
37 | 37 | from IPython.utils.traitlets import ( |
@@ -492,34 +492,52 b' class Application(SingletonConfigurable):' | |||
|
492 | 492 | |
|
493 | 493 | # flatten flags&aliases, so cl-args get appropriate priority: |
|
494 | 494 | flags,aliases = self.flatten_flags() |
|
495 | ||
|
496 | 495 | loader = KVArgParseConfigLoader(argv=argv, aliases=aliases, |
|
497 | flags=flags) | |
|
496 | flags=flags, log=self.log) | |
|
498 | 497 | config = loader.load_config() |
|
499 | 498 | self.update_config(config) |
|
500 | 499 | # store unparsed args in extra_args |
|
501 | 500 | self.extra_args = loader.extra_args |
|
502 | 501 | |
|
502 | @classmethod | |
|
503 | def _load_config_file(cls, basefilename, path=None, log=None): | |
|
504 | """Load config files (json/py) by filename and path.""" | |
|
505 | ||
|
506 | pyloader = PyFileConfigLoader(basefilename+'.py', path=path, log=log) | |
|
507 | jsonloader = JSONFileConfigLoader(basefilename+'.json', path=path, log=log) | |
|
508 | config_found = False | |
|
509 | config = None | |
|
510 | for loader in [pyloader, jsonloader]: | |
|
511 | try: | |
|
512 | config = loader.load_config() | |
|
513 | config_found = True | |
|
514 | except ConfigFileNotFound: | |
|
515 | pass | |
|
516 | except Exception: | |
|
517 | # try to get the full filename, but it will be empty in the | |
|
518 | # unlikely event that the error raised before filefind finished | |
|
519 | filename = loader.full_filename or filename | |
|
520 | # problem while running the file | |
|
521 | log.error("Exception while loading config file %s", | |
|
522 | filename, exc_info=True) | |
|
523 | else: | |
|
524 | log.debug("Loaded config file: %s", loader.full_filename) | |
|
525 | if config : | |
|
526 | yield config | |
|
527 | ||
|
528 | if not config_found : | |
|
529 | raise ConfigFileNotFound('Neither .json, not .py file found.') | |
|
530 | raise StopIteration | |
|
531 | ||
|
532 | ||
|
503 | 533 | @catch_config_error |
|
504 | 534 | def load_config_file(self, filename, path=None): |
|
505 |
"""Load |
|
|
506 | loader = PyFileConfigLoader(filename, path=path) | |
|
507 | try: | |
|
508 | config = loader.load_config() | |
|
509 | except ConfigFileNotFound: | |
|
510 | # problem finding the file, raise | |
|
511 | raise | |
|
512 | except Exception: | |
|
513 | # try to get the full filename, but it will be empty in the | |
|
514 | # unlikely event that the error raised before filefind finished | |
|
515 | filename = loader.full_filename or filename | |
|
516 | # problem while running the file | |
|
517 | self.log.error("Exception while loading config file %s", | |
|
518 | filename, exc_info=True) | |
|
519 | else: | |
|
520 | self.log.debug("Loaded config file: %s", loader.full_filename) | |
|
535 | """Load config files (json/py) by filename and path.""" | |
|
536 | filename, ext = os.path.splitext(filename) | |
|
537 | for config in self._load_config_file(filename, path=path , log=self.log): | |
|
521 | 538 | self.update_config(config) |
|
522 | 539 | |
|
540 | ||
|
523 | 541 | def generate_config_file(self): |
|
524 | 542 | """generate default config file from Configurables""" |
|
525 | 543 | lines = ["# Configuration file for %s."%self.name] |
@@ -28,9 +28,10 b' import copy' | |||
|
28 | 28 | import os |
|
29 | 29 | import re |
|
30 | 30 | import sys |
|
31 | import json | |
|
31 | 32 | |
|
32 | 33 | from IPython.utils.path import filefind, get_ipython_dir |
|
33 |
from IPython.utils import py3compat |
|
|
34 | from IPython.utils import py3compat | |
|
34 | 35 | from IPython.utils.encoding import DEFAULT_ENCODING |
|
35 | 36 | from IPython.utils.py3compat import unicode_type, iteritems |
|
36 | 37 | from IPython.utils.traitlets import HasTraits, List, Any, TraitError |
@@ -303,9 +304,17 b' class ConfigLoader(object):' | |||
|
303 | 304 | handled elsewhere. |
|
304 | 305 | """ |
|
305 | 306 | |
|
306 |
def _ |
|
|
307 | def _log_default(self): | |
|
308 | from IPython.config.application import Application | |
|
309 | return Application.instance().log | |
|
310 | ||
|
311 | def __init__(self, log=None): | |
|
307 | 312 | """A base class for config loaders. |
|
308 | 313 | |
|
314 | log : instance of :class:`logging.Logger` to use. | |
|
315 | By default loger of :meth:`IPython.config.application.Application.instance()` | |
|
316 | will be used | |
|
317 | ||
|
309 | 318 | Examples |
|
310 | 319 | -------- |
|
311 | 320 | |
@@ -315,6 +324,11 b' class ConfigLoader(object):' | |||
|
315 | 324 | {} |
|
316 | 325 | """ |
|
317 | 326 | self.clear() |
|
327 | if log is None : | |
|
328 | self.log = self._log_default() | |
|
329 | self.log.debug('Using default logger') | |
|
330 | else : | |
|
331 | self.log = log | |
|
318 | 332 | |
|
319 | 333 | def clear(self): |
|
320 | 334 | self.config = Config() |
@@ -336,17 +350,8 b' class FileConfigLoader(ConfigLoader):' | |||
|
336 | 350 | As we add more file based config loaders, the common logic should go |
|
337 | 351 | here. |
|
338 | 352 | """ |
|
339 | pass | |
|
340 | ||
|
341 | ||
|
342 | class PyFileConfigLoader(FileConfigLoader): | |
|
343 | """A config loader for pure python files. | |
|
344 | ||
|
345 | This calls execfile on a plain python file and looks for attributes | |
|
346 | that are all caps. These attribute are added to the config Struct. | |
|
347 | """ | |
|
348 | 353 | |
|
349 | def __init__(self, filename, path=None): | |
|
354 | def __init__(self, filename, path=None, **kw): | |
|
350 | 355 | """Build a config loader for a filename and path. |
|
351 | 356 | |
|
352 | 357 | Parameters |
@@ -357,11 +362,53 b' class PyFileConfigLoader(FileConfigLoader):' | |||
|
357 | 362 | The path to search for the config file on, or a sequence of |
|
358 | 363 | paths to try in order. |
|
359 | 364 | """ |
|
360 |
super( |
|
|
365 | super(FileConfigLoader, self).__init__(**kw) | |
|
361 | 366 | self.filename = filename |
|
362 | 367 | self.path = path |
|
363 | 368 | self.full_filename = '' |
|
364 | self.data = None | |
|
369 | ||
|
370 | def _find_file(self): | |
|
371 | """Try to find the file by searching the paths.""" | |
|
372 | self.full_filename = filefind(self.filename, self.path) | |
|
373 | ||
|
374 | class JSONFileConfigLoader(FileConfigLoader): | |
|
375 | """A Json file loader for config""" | |
|
376 | ||
|
377 | def load_config(self): | |
|
378 | """Load the config from a file and return it as a Struct.""" | |
|
379 | self.clear() | |
|
380 | try: | |
|
381 | self._find_file() | |
|
382 | except IOError as e: | |
|
383 | raise ConfigFileNotFound(str(e)) | |
|
384 | dct = self._read_file_as_dict() | |
|
385 | self.config = self._convert_to_config(dct) | |
|
386 | return self.config | |
|
387 | ||
|
388 | def _read_file_as_dict(self): | |
|
389 | with open(self.full_filename) as f : | |
|
390 | return json.load(f) | |
|
391 | ||
|
392 | def _convert_to_config(self, dictionary): | |
|
393 | if 'version' in dictionary: | |
|
394 | version = dictionary.pop('version') | |
|
395 | else : | |
|
396 | version = 1 | |
|
397 | self.log.warn("Unrecognized JSON config file version, assuming version : {}".format(version)) | |
|
398 | ||
|
399 | if version == 1: | |
|
400 | return Config(dictionary) | |
|
401 | else : | |
|
402 | raise ValueError('Unknown version of JSON config file : version number {version}'.format(version=version)) | |
|
403 | ||
|
404 | ||
|
405 | class PyFileConfigLoader(FileConfigLoader): | |
|
406 | """A config loader for pure python files. | |
|
407 | ||
|
408 | This is responsible for locating a Python config file by filename and | |
|
409 | profile name, then executing it in a namespace where it could have access | |
|
410 | to subconfigs. | |
|
411 | """ | |
|
365 | 412 | |
|
366 | 413 | def load_config(self): |
|
367 | 414 | """Load the config from a file and return it as a Struct.""" |
@@ -371,12 +418,8 b' class PyFileConfigLoader(FileConfigLoader):' | |||
|
371 | 418 | except IOError as e: |
|
372 | 419 | raise ConfigFileNotFound(str(e)) |
|
373 | 420 | self._read_file_as_dict() |
|
374 | self._convert_to_config() | |
|
375 | 421 | return self.config |
|
376 | 422 | |
|
377 | def _find_file(self): | |
|
378 | """Try to find the file by searching the paths.""" | |
|
379 | self.full_filename = filefind(self.filename, self.path) | |
|
380 | 423 | |
|
381 | 424 | def _read_file_as_dict(self): |
|
382 | 425 | """Load the config file into self.config, with recursive loading.""" |
@@ -429,10 +472,6 b' class PyFileConfigLoader(FileConfigLoader):' | |||
|
429 | 472 | conf_filename = self.full_filename.encode(fs_encoding) |
|
430 | 473 | py3compat.execfile(conf_filename, namespace) |
|
431 | 474 | |
|
432 | def _convert_to_config(self): | |
|
433 | if self.data is None: | |
|
434 | ConfigLoaderError('self.data does not exist') | |
|
435 | ||
|
436 | 475 | |
|
437 | 476 | class CommandLineConfigLoader(ConfigLoader): |
|
438 | 477 | """A config loader for command line arguments. |
@@ -497,7 +536,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):' | |||
|
497 | 536 | ipython --profile="foo" --InteractiveShell.autocall=False |
|
498 | 537 | """ |
|
499 | 538 | |
|
500 | def __init__(self, argv=None, aliases=None, flags=None): | |
|
539 | def __init__(self, argv=None, aliases=None, flags=None, **kw): | |
|
501 | 540 | """Create a key value pair config loader. |
|
502 | 541 | |
|
503 | 542 | Parameters |
@@ -529,7 +568,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):' | |||
|
529 | 568 | >>> sorted(d.items()) |
|
530 | 569 | [('A', {'name': 'brian'}), ('B', {'number': 0})] |
|
531 | 570 | """ |
|
532 | self.clear() | |
|
571 | super(KeyValueConfigLoader, self).__init__(**kw) | |
|
533 | 572 | if argv is None: |
|
534 | 573 | argv = sys.argv[1:] |
|
535 | 574 | self.argv = argv |
@@ -606,7 +645,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):' | |||
|
606 | 645 | lhs = aliases[lhs] |
|
607 | 646 | if '.' not in lhs: |
|
608 | 647 | # probably a mistyped alias, but not technically illegal |
|
609 |
|
|
|
648 | self.log.warn("Unrecognized alias: '%s', it will probably have no effect. %s,-- %s"%(lhs,raw, aliases)) | |
|
610 | 649 | try: |
|
611 | 650 | self._exec_config_str(lhs, rhs) |
|
612 | 651 | except Exception: |
@@ -633,7 +672,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):' | |||
|
633 | 672 | class ArgParseConfigLoader(CommandLineConfigLoader): |
|
634 | 673 | """A loader that uses the argparse module to load from the command line.""" |
|
635 | 674 | |
|
636 | def __init__(self, argv=None, aliases=None, flags=None, *parser_args, **parser_kw): | |
|
675 | def __init__(self, argv=None, aliases=None, flags=None, log=None, *parser_args, **parser_kw): | |
|
637 | 676 | """Create a config loader for use with argparse. |
|
638 | 677 | |
|
639 | 678 | Parameters |
@@ -656,7 +695,7 b' class ArgParseConfigLoader(CommandLineConfigLoader):' | |||
|
656 | 695 | config : Config |
|
657 | 696 | The resulting Config object. |
|
658 | 697 | """ |
|
659 | super(CommandLineConfigLoader, self).__init__() | |
|
698 | super(CommandLineConfigLoader, self).__init__(log=log) | |
|
660 | 699 | self.clear() |
|
661 | 700 | if argv is None: |
|
662 | 701 | argv = sys.argv[1:] |
@@ -772,7 +811,7 b' class KVArgParseConfigLoader(ArgParseConfigLoader):' | |||
|
772 | 811 | self._load_flag(subc) |
|
773 | 812 | |
|
774 | 813 | if self.extra_args: |
|
775 | sub_parser = KeyValueConfigLoader() | |
|
814 | sub_parser = KeyValueConfigLoader(log=self.log) | |
|
776 | 815 | sub_parser.load_config(self.extra_args) |
|
777 | 816 | self.config.merge(sub_parser.config) |
|
778 | 817 | self.extra_args = sub_parser.extra_args |
@@ -22,17 +22,21 b' Authors:' | |||
|
22 | 22 | import os |
|
23 | 23 | import pickle |
|
24 | 24 | import sys |
|
25 | import json | |
|
26 | ||
|
25 | 27 | from tempfile import mkstemp |
|
26 | 28 | from unittest import TestCase |
|
27 | 29 | |
|
28 | 30 | from nose import SkipTest |
|
31 | import nose.tools as nt | |
|
32 | ||
|
29 | 33 | |
|
30 | from IPython.testing.tools import mute_warn | |
|
31 | 34 | |
|
32 | 35 | from IPython.config.loader import ( |
|
33 | 36 | Config, |
|
34 | 37 | LazyConfigValue, |
|
35 | 38 | PyFileConfigLoader, |
|
39 | JSONFileConfigLoader, | |
|
36 | 40 | KeyValueConfigLoader, |
|
37 | 41 | ArgParseConfigLoader, |
|
38 | 42 | KVArgParseConfigLoader, |
@@ -53,21 +57,77 b" c.Foo.Bam.value=list(range(10)) # list() is just so it's the same on Python 3" | |||
|
53 | 57 | c.D.C.value='hi there' |
|
54 | 58 | """ |
|
55 | 59 | |
|
56 | class TestPyFileCL(TestCase): | |
|
60 | json1file = """ | |
|
61 | { | |
|
62 | "version": 1, | |
|
63 | "a": 10, | |
|
64 | "b": 20, | |
|
65 | "Foo": { | |
|
66 | "Bam": { | |
|
67 | "value": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] | |
|
68 | }, | |
|
69 | "Bar": { | |
|
70 | "value": 10 | |
|
71 | } | |
|
72 | }, | |
|
73 | "D": { | |
|
74 | "C": { | |
|
75 | "value": "hi there" | |
|
76 | } | |
|
77 | } | |
|
78 | } | |
|
79 | """ | |
|
57 | 80 | |
|
58 | def test_basic(self): | |
|
81 | # should not load | |
|
82 | json2file = """ | |
|
83 | { | |
|
84 | "version": 2 | |
|
85 | } | |
|
86 | """ | |
|
87 | ||
|
88 | import logging | |
|
89 | log = logging.getLogger('devnull') | |
|
90 | log.setLevel(0) | |
|
91 | ||
|
92 | class TestFileCL(TestCase): | |
|
93 | ||
|
94 | def _check_conf(self, config): | |
|
95 | self.assertEqual(config.a, 10) | |
|
96 | self.assertEqual(config.b, 20) | |
|
97 | self.assertEqual(config.Foo.Bar.value, 10) | |
|
98 | self.assertEqual(config.Foo.Bam.value, list(range(10))) | |
|
99 | self.assertEqual(config.D.C.value, 'hi there') | |
|
100 | ||
|
101 | def test_python(self): | |
|
59 | 102 | fd, fname = mkstemp('.py') |
|
60 | 103 | f = os.fdopen(fd, 'w') |
|
61 | 104 | f.write(pyfile) |
|
62 | 105 | f.close() |
|
63 | 106 | # Unlink the file |
|
64 | cl = PyFileConfigLoader(fname) | |
|
107 | cl = PyFileConfigLoader(fname, log=log) | |
|
65 | 108 | config = cl.load_config() |
|
66 |
self. |
|
|
67 | self.assertEqual(config.b, 20) | |
|
68 | self.assertEqual(config.Foo.Bar.value, 10) | |
|
69 | self.assertEqual(config.Foo.Bam.value, list(range(10))) | |
|
70 | self.assertEqual(config.D.C.value, 'hi there') | |
|
109 | self._check_conf(config) | |
|
110 | ||
|
111 | def test_json(self): | |
|
112 | fd, fname = mkstemp('.json') | |
|
113 | f = os.fdopen(fd, 'w') | |
|
114 | f.write(json1file) | |
|
115 | f.close() | |
|
116 | # Unlink the file | |
|
117 | cl = JSONFileConfigLoader(fname, log=log) | |
|
118 | config = cl.load_config() | |
|
119 | self._check_conf(config) | |
|
120 | ||
|
121 | def test_v2raise(self): | |
|
122 | fd, fname = mkstemp('.json') | |
|
123 | f = os.fdopen(fd, 'w') | |
|
124 | f.write(json2file) | |
|
125 | f.close() | |
|
126 | # Unlink the file | |
|
127 | cl = JSONFileConfigLoader(fname, log=log) | |
|
128 | with nt.assert_raises(ValueError): | |
|
129 | cl.load_config() | |
|
130 | ||
|
71 | 131 | |
|
72 | 132 | class MyLoader1(ArgParseConfigLoader): |
|
73 | 133 | def _add_arguments(self, aliases=None, flags=None): |
@@ -121,10 +181,9 b' class TestKeyValueCL(TestCase):' | |||
|
121 | 181 | klass = KeyValueConfigLoader |
|
122 | 182 | |
|
123 | 183 | def test_basic(self): |
|
124 | cl = self.klass() | |
|
184 | cl = self.klass(log=log) | |
|
125 | 185 | argv = ['--'+s.strip('c.') for s in pyfile.split('\n')[2:-1]] |
|
126 | with mute_warn(): | |
|
127 | config = cl.load_config(argv) | |
|
186 | config = cl.load_config(argv) | |
|
128 | 187 | self.assertEqual(config.a, 10) |
|
129 | 188 | self.assertEqual(config.b, 20) |
|
130 | 189 | self.assertEqual(config.Foo.Bar.value, 10) |
@@ -132,31 +191,27 b' class TestKeyValueCL(TestCase):' | |||
|
132 | 191 | self.assertEqual(config.D.C.value, 'hi there') |
|
133 | 192 | |
|
134 | 193 | def test_expanduser(self): |
|
135 | cl = self.klass() | |
|
194 | cl = self.klass(log=log) | |
|
136 | 195 | argv = ['--a=~/1/2/3', '--b=~', '--c=~/', '--d="~/"'] |
|
137 | with mute_warn(): | |
|
138 | config = cl.load_config(argv) | |
|
196 | config = cl.load_config(argv) | |
|
139 | 197 | self.assertEqual(config.a, os.path.expanduser('~/1/2/3')) |
|
140 | 198 | self.assertEqual(config.b, os.path.expanduser('~')) |
|
141 | 199 | self.assertEqual(config.c, os.path.expanduser('~/')) |
|
142 | 200 | self.assertEqual(config.d, '~/') |
|
143 | 201 | |
|
144 | 202 | def test_extra_args(self): |
|
145 | cl = self.klass() | |
|
146 | with mute_warn(): | |
|
147 | config = cl.load_config(['--a=5', 'b', '--c=10', 'd']) | |
|
203 | cl = self.klass(log=log) | |
|
204 | config = cl.load_config(['--a=5', 'b', '--c=10', 'd']) | |
|
148 | 205 | self.assertEqual(cl.extra_args, ['b', 'd']) |
|
149 | 206 | self.assertEqual(config.a, 5) |
|
150 | 207 | self.assertEqual(config.c, 10) |
|
151 | with mute_warn(): | |
|
152 | config = cl.load_config(['--', '--a=5', '--c=10']) | |
|
208 | config = cl.load_config(['--', '--a=5', '--c=10']) | |
|
153 | 209 | self.assertEqual(cl.extra_args, ['--a=5', '--c=10']) |
|
154 | 210 | |
|
155 | 211 | def test_unicode_args(self): |
|
156 | cl = self.klass() | |
|
212 | cl = self.klass(log=log) | |
|
157 | 213 | argv = [u'--a=épsîlön'] |
|
158 | with mute_warn(): | |
|
159 | config = cl.load_config(argv) | |
|
214 | config = cl.load_config(argv) | |
|
160 | 215 | self.assertEqual(config.a, u'épsîlön') |
|
161 | 216 | |
|
162 | 217 | def test_unicode_bytes_args(self): |
@@ -166,16 +221,14 b' class TestKeyValueCL(TestCase):' | |||
|
166 | 221 | except (TypeError, UnicodeEncodeError): |
|
167 | 222 | raise SkipTest("sys.stdin.encoding can't handle 'é'") |
|
168 | 223 | |
|
169 | cl = self.klass() | |
|
170 | with mute_warn(): | |
|
171 | config = cl.load_config([barg]) | |
|
224 | cl = self.klass(log=log) | |
|
225 | config = cl.load_config([barg]) | |
|
172 | 226 | self.assertEqual(config.a, u'é') |
|
173 | 227 | |
|
174 | 228 | def test_unicode_alias(self): |
|
175 | cl = self.klass() | |
|
229 | cl = self.klass(log=log) | |
|
176 | 230 | argv = [u'--a=épsîlön'] |
|
177 | with mute_warn(): | |
|
178 | config = cl.load_config(argv, aliases=dict(a='A.a')) | |
|
231 | config = cl.load_config(argv, aliases=dict(a='A.a')) | |
|
179 | 232 | self.assertEqual(config.A.a, u'épsîlön') |
|
180 | 233 | |
|
181 | 234 | |
@@ -183,18 +236,16 b' class TestArgParseKVCL(TestKeyValueCL):' | |||
|
183 | 236 | klass = KVArgParseConfigLoader |
|
184 | 237 | |
|
185 | 238 | def test_expanduser2(self): |
|
186 | cl = self.klass() | |
|
239 | cl = self.klass(log=log) | |
|
187 | 240 | argv = ['-a', '~/1/2/3', '--b', "'~/1/2/3'"] |
|
188 | with mute_warn(): | |
|
189 | config = cl.load_config(argv, aliases=dict(a='A.a', b='A.b')) | |
|
241 | config = cl.load_config(argv, aliases=dict(a='A.a', b='A.b')) | |
|
190 | 242 | self.assertEqual(config.A.a, os.path.expanduser('~/1/2/3')) |
|
191 | 243 | self.assertEqual(config.A.b, '~/1/2/3') |
|
192 | 244 | |
|
193 | 245 | def test_eval(self): |
|
194 | cl = self.klass() | |
|
246 | cl = self.klass(log=log) | |
|
195 | 247 | argv = ['-c', 'a=5'] |
|
196 | with mute_warn(): | |
|
197 | config = cl.load_config(argv, aliases=dict(c='A.c')) | |
|
248 | config = cl.load_config(argv, aliases=dict(c='A.c')) | |
|
198 | 249 | self.assertEqual(config.A.c, u"a=5") |
|
199 | 250 | |
|
200 | 251 |
@@ -33,7 +33,7 b' import sys' | |||
|
33 | 33 | from IPython.config.loader import ( |
|
34 | 34 | Config, PyFileConfigLoader, ConfigFileNotFound |
|
35 | 35 | ) |
|
36 | from IPython.config.application import boolean_flag, catch_config_error | |
|
36 | from IPython.config.application import boolean_flag, catch_config_error, Application | |
|
37 | 37 | from IPython.core import release |
|
38 | 38 | from IPython.core import usage |
|
39 | 39 | from IPython.core.completer import IPCompleter |
@@ -364,7 +364,6 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):' | |||
|
364 | 364 | else: |
|
365 | 365 | self.log.debug("IPython not interactive...") |
|
366 | 366 | |
|
367 | ||
|
368 | 367 | def load_default_config(ipython_dir=None): |
|
369 | 368 | """Load the default config file from the default ipython_dir. |
|
370 | 369 | |
@@ -372,15 +371,14 b' def load_default_config(ipython_dir=None):' | |||
|
372 | 371 | """ |
|
373 | 372 | if ipython_dir is None: |
|
374 | 373 | ipython_dir = get_ipython_dir() |
|
374 | ||
|
375 | 375 | profile_dir = os.path.join(ipython_dir, 'profile_default') |
|
376 | cl = PyFileConfigLoader("ipython_config.py", profile_dir) | |
|
377 | try: | |
|
378 | config = cl.load_config() | |
|
379 | except ConfigFileNotFound: | |
|
380 | # no config found | |
|
381 | config = Config() | |
|
382 | return config | |
|
383 | 376 | |
|
377 | config = Config() | |
|
378 | for cf in Application._load_config_file(filename[:-3], path=profile_dir, log=None): | |
|
379 | config.update(cf) | |
|
380 | ||
|
381 | return config | |
|
384 | 382 | |
|
385 | 383 | launch_new_instance = TerminalIPythonApp.launch_instance |
|
386 | 384 |
@@ -15,10 +15,10 b' Each of these abstractions is represented by a Python class.' | |||
|
15 | 15 | Configuration object: :class:`~IPython.config.loader.Config` |
|
16 | 16 | A configuration object is a simple dictionary-like class that holds |
|
17 | 17 | configuration attributes and sub-configuration objects. These classes |
|
18 | support dotted attribute style access (``Foo.bar``) in addition to the | |
|
19 |
regular dictionary style access (``Foo['bar']``). |
|
|
20 | are smart. They know how to merge themselves with other configuration | |
|
21 | objects and they automatically create sub-configuration objects. | |
|
18 | support dotted attribute style access (``cfg.Foo.bar``) in addition to the | |
|
19 | regular dictionary style access (``cfg['Foo']['bar']``). | |
|
20 | The Config object is a wrapper around a simple dictionary with some convenience methods, | |
|
21 | such as merging and automatic section creation. | |
|
22 | 22 | |
|
23 | 23 | Application: :class:`~IPython.config.application.Application` |
|
24 | 24 | An application is a process that does a specific job. The most obvious |
@@ -85,12 +85,24 b' Now, we show what our configuration objects and files look like.' | |||
|
85 | 85 | Configuration objects and files |
|
86 | 86 | =============================== |
|
87 | 87 | |
|
88 | A configuration file is simply a pure Python file that sets the attributes | |
|
89 | of a global, pre-created configuration object. This configuration object is a | |
|
90 | :class:`~IPython.config.loader.Config` instance. While in a configuration | |
|
91 | file, to get a reference to this object, simply call the :func:`get_config` | |
|
92 | function. We inject this function into the global namespace that the | |
|
93 | configuration file is executed in. | |
|
88 | A configuration object is little more than a wrapper around a dictionary. | |
|
89 | A configuration *file* is simply a mechanism for producing that object. | |
|
90 | The main IPython configuration file is a plain Python script, | |
|
91 | which can perform extensive logic to populate the config object. | |
|
92 | IPython 2.0 introduces a JSON configuration file, | |
|
93 | which is just a direct JSON serialization of the config dictionary. | |
|
94 | The JSON format is easily processed by external software. | |
|
95 | ||
|
96 | When both Python and JSON configuration file are present, both will be loaded, | |
|
97 | with JSON configuration having higher priority. | |
|
98 | ||
|
99 | Python configuration Files | |
|
100 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
|
101 | ||
|
102 | A Python configuration file is a pure Python file that populates a configuration object. | |
|
103 | This configuration object is a :class:`~IPython.config.loader.Config` instance. | |
|
104 | While in a configuration file, to get a reference to this object, simply call the :func:`get_config` | |
|
105 | function, which is available in the global namespace of the script. | |
|
94 | 106 | |
|
95 | 107 | Here is an example of a super simple configuration file that does nothing:: |
|
96 | 108 | |
@@ -99,10 +111,11 b' Here is an example of a super simple configuration file that does nothing::' | |||
|
99 | 111 | Once you get a reference to the configuration object, you simply set |
|
100 | 112 | attributes on it. All you have to know is: |
|
101 | 113 | |
|
102 |
* The name of |
|
|
114 | * The name of the class to configure. | |
|
115 | * The name of the attribute. | |
|
103 | 116 | * The type of each attribute. |
|
104 | 117 | |
|
105 |
The answers to these |
|
|
118 | The answers to these questions are provided by the various | |
|
106 | 119 | :class:`~IPython.config.configurable.Configurable` subclasses that an |
|
107 | 120 | application uses. Let's look at how this would work for a simple configurable |
|
108 | 121 | subclass:: |
@@ -118,7 +131,7 b' subclass::' | |||
|
118 | 131 | # The rest of the class implementation would go here.. |
|
119 | 132 | |
|
120 | 133 | In this example, we see that :class:`MyClass` has three attributes, two |
|
121 |
of |
|
|
134 | of (``name``, ``ranking``) can be configured. All of the attributes | |
|
122 | 135 | are given types and default values. If a :class:`MyClass` is instantiated, |
|
123 | 136 | but not configured, these default values will be used. But let's see how |
|
124 | 137 | to configure this class in a configuration file:: |
@@ -173,9 +186,38 b' attribute of ``c`` is not the actual class, but instead is another' | |||
|
173 | 186 | instance is dynamically created for that attribute. This allows deeply |
|
174 | 187 | hierarchical information created easily (``c.Foo.Bar.value``) on the fly. |
|
175 | 188 | |
|
189 | JSON configuration Files | |
|
190 | ~~~~~~~~~~~~~~~~~~~~~~~~ | |
|
191 | ||
|
192 | A JSON configuration file is simply a file that contain a | |
|
193 | :class:`~IPython.config.loader.Config` dictionary serialized to JSON. | |
|
194 | A JSON configuration file has the same base name as a Python configuration file, | |
|
195 | just with a .json extension. | |
|
196 | ||
|
197 | Configuration described in previous section could be written as follow in a | |
|
198 | JSON configuration file: | |
|
199 | ||
|
200 | .. sourcecode:: json | |
|
201 | ||
|
202 | { | |
|
203 | "version": "1.0", | |
|
204 | "MyClass": { | |
|
205 | "name": "coolname", | |
|
206 | "ranking": 10 | |
|
207 | } | |
|
208 | } | |
|
209 | ||
|
210 | JSON configuration files can be more easily generated or processed by programs | |
|
211 | or other languages. | |
|
212 | ||
|
213 | ||
|
176 | 214 | Configuration files inheritance |
|
177 | 215 | =============================== |
|
178 | 216 | |
|
217 | .. note:: | |
|
218 | ||
|
219 | This section only apply to Python configuration files. | |
|
220 | ||
|
179 | 221 | Let's say you want to have different configuration files for various purposes. |
|
180 | 222 | Our configuration system makes it easy for one configuration file to inherit |
|
181 | 223 | the information in another configuration file. The :func:`load_subconfig` |
General Comments 0
You need to be logged in to leave comments.
Login now