##// END OF EJS Templates
don't blacklist builtin names...
MinRK -
Show More
@@ -1,722 +1,718 b''
1 1 """A simple configuration system.
2 2
3 3 Inheritance diagram:
4 4
5 5 .. inheritance-diagram:: IPython.config.loader
6 6 :parts: 3
7 7
8 8 Authors
9 9 -------
10 10 * Brian Granger
11 11 * Fernando Perez
12 12 * Min RK
13 13 """
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Copyright (C) 2008-2011 The IPython Development Team
17 17 #
18 18 # Distributed under the terms of the BSD License. The full license is in
19 19 # the file COPYING, distributed as part of this software.
20 20 #-----------------------------------------------------------------------------
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Imports
24 24 #-----------------------------------------------------------------------------
25 25
26 26 import __builtin__ as builtin_mod
27 27 import os
28 28 import re
29 29 import sys
30 30
31 31 from IPython.external import argparse
32 32 from IPython.utils.path import filefind, get_ipython_dir
33 33 from IPython.utils import py3compat, warn
34 34 from IPython.utils.encoding import DEFAULT_ENCODING
35 35
36 36 #-----------------------------------------------------------------------------
37 37 # Exceptions
38 38 #-----------------------------------------------------------------------------
39 39
40 40
41 41 class ConfigError(Exception):
42 42 pass
43 43
44 44 class ConfigLoaderError(ConfigError):
45 45 pass
46 46
47 47 class ConfigFileNotFound(ConfigError):
48 48 pass
49 49
50 50 class ArgumentError(ConfigLoaderError):
51 51 pass
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # Argparse fix
55 55 #-----------------------------------------------------------------------------
56 56
57 57 # Unfortunately argparse by default prints help messages to stderr instead of
58 58 # stdout. This makes it annoying to capture long help screens at the command
59 59 # line, since one must know how to pipe stderr, which many users don't know how
60 60 # to do. So we override the print_help method with one that defaults to
61 61 # stdout and use our class instead.
62 62
63 63 class ArgumentParser(argparse.ArgumentParser):
64 64 """Simple argparse subclass that prints help to stdout by default."""
65 65
66 66 def print_help(self, file=None):
67 67 if file is None:
68 68 file = sys.stdout
69 69 return super(ArgumentParser, self).print_help(file)
70 70
71 71 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
72 72
73 73 #-----------------------------------------------------------------------------
74 74 # Config class for holding config information
75 75 #-----------------------------------------------------------------------------
76 76
77 77
78 78 class Config(dict):
79 79 """An attribute based dict that can do smart merges."""
80 80
81 81 def __init__(self, *args, **kwds):
82 82 dict.__init__(self, *args, **kwds)
83 83 # This sets self.__dict__ = self, but it has to be done this way
84 84 # because we are also overriding __setattr__.
85 85 dict.__setattr__(self, '__dict__', self)
86 86 self._ensure_subconfig()
87 87
88 88 def _ensure_subconfig(self):
89 89 """ensure that sub-dicts that should be Config objects are
90 90
91 91 casts dicts that are under section keys to Config objects,
92 92 which is necessary for constructing Config objects from dict literals.
93 93 """
94 94 for key in self:
95 95 obj = self[key]
96 96 if self._is_section_key(key) \
97 97 and isinstance(obj, dict) \
98 98 and not isinstance(obj, Config):
99 99 dict.__setattr__(self, key, Config(obj))
100 100
101 101 def _merge(self, other):
102 102 """deprecated alias, use Config.merge()"""
103 103 self.merge(other)
104 104
105 105 def merge(self, other):
106 106 """merge another config object into this one"""
107 107 to_update = {}
108 108 for k, v in other.iteritems():
109 109 if k not in self:
110 110 to_update[k] = v
111 111 else: # I have this key
112 112 if isinstance(v, Config) and isinstance(self[k], Config):
113 113 # Recursively merge common sub Configs
114 114 self[k].merge(v)
115 115 else:
116 116 # Plain updates for non-Configs
117 117 to_update[k] = v
118 118
119 119 self.update(to_update)
120 120
121 121 def _is_section_key(self, key):
122 122 if key[0].upper()==key[0] and not key.startswith('_'):
123 123 return True
124 124 else:
125 125 return False
126 126
127 127 def __contains__(self, key):
128 128 if self._is_section_key(key):
129 129 return True
130 130 else:
131 131 return super(Config, self).__contains__(key)
132 132 # .has_key is deprecated for dictionaries.
133 133 has_key = __contains__
134 134
135 135 def _has_section(self, key):
136 136 if self._is_section_key(key):
137 137 if super(Config, self).__contains__(key):
138 138 return True
139 139 return False
140 140
141 141 def copy(self):
142 142 return type(self)(dict.copy(self))
143 143
144 144 def __copy__(self):
145 145 return self.copy()
146 146
147 147 def __deepcopy__(self, memo):
148 148 import copy
149 149 return type(self)(copy.deepcopy(self.items()))
150 150
151 151 def __getitem__(self, key):
152 152 # We cannot use directly self._is_section_key, because it triggers
153 153 # infinite recursion on top of PyPy. Instead, we manually fish the
154 154 # bound method.
155 155 is_section_key = self.__class__._is_section_key.__get__(self)
156 156
157 157 # Because we use this for an exec namespace, we need to delegate
158 158 # the lookup of names in __builtin__ to itself. This means
159 159 # that you can't have section or attribute names that are
160 160 # builtins.
161 161 try:
162 162 return getattr(builtin_mod, key)
163 163 except AttributeError:
164 164 pass
165 165 if is_section_key(key):
166 166 try:
167 167 return dict.__getitem__(self, key)
168 168 except KeyError:
169 169 c = Config()
170 170 dict.__setitem__(self, key, c)
171 171 return c
172 172 else:
173 173 return dict.__getitem__(self, key)
174 174
175 175 def __setitem__(self, key, value):
176 # Don't allow names in __builtin__ to be modified.
177 if hasattr(builtin_mod, key):
178 raise ConfigError('Config variable names cannot have the same name '
179 'as a Python builtin: %s' % key)
180 176 if self._is_section_key(key):
181 177 if not isinstance(value, Config):
182 178 raise ValueError('values whose keys begin with an uppercase '
183 179 'char must be Config instances: %r, %r' % (key, value))
184 180 else:
185 181 dict.__setitem__(self, key, value)
186 182
187 183 def __getattr__(self, key):
188 184 try:
189 185 return self.__getitem__(key)
190 186 except KeyError as e:
191 187 raise AttributeError(e)
192 188
193 189 def __setattr__(self, key, value):
194 190 try:
195 191 self.__setitem__(key, value)
196 192 except KeyError as e:
197 193 raise AttributeError(e)
198 194
199 195 def __delattr__(self, key):
200 196 try:
201 197 dict.__delitem__(self, key)
202 198 except KeyError as e:
203 199 raise AttributeError(e)
204 200
205 201
206 202 #-----------------------------------------------------------------------------
207 203 # Config loading classes
208 204 #-----------------------------------------------------------------------------
209 205
210 206
211 207 class ConfigLoader(object):
212 208 """A object for loading configurations from just about anywhere.
213 209
214 210 The resulting configuration is packaged as a :class:`Struct`.
215 211
216 212 Notes
217 213 -----
218 214 A :class:`ConfigLoader` does one thing: load a config from a source
219 215 (file, command line arguments) and returns the data as a :class:`Struct`.
220 216 There are lots of things that :class:`ConfigLoader` does not do. It does
221 217 not implement complex logic for finding config files. It does not handle
222 218 default values or merge multiple configs. These things need to be
223 219 handled elsewhere.
224 220 """
225 221
226 222 def __init__(self):
227 223 """A base class for config loaders.
228 224
229 225 Examples
230 226 --------
231 227
232 228 >>> cl = ConfigLoader()
233 229 >>> config = cl.load_config()
234 230 >>> config
235 231 {}
236 232 """
237 233 self.clear()
238 234
239 235 def clear(self):
240 236 self.config = Config()
241 237
242 238 def load_config(self):
243 239 """Load a config from somewhere, return a :class:`Config` instance.
244 240
245 241 Usually, this will cause self.config to be set and then returned.
246 242 However, in most cases, :meth:`ConfigLoader.clear` should be called
247 243 to erase any previous state.
248 244 """
249 245 self.clear()
250 246 return self.config
251 247
252 248
253 249 class FileConfigLoader(ConfigLoader):
254 250 """A base class for file based configurations.
255 251
256 252 As we add more file based config loaders, the common logic should go
257 253 here.
258 254 """
259 255 pass
260 256
261 257
262 258 class PyFileConfigLoader(FileConfigLoader):
263 259 """A config loader for pure python files.
264 260
265 261 This calls execfile on a plain python file and looks for attributes
266 262 that are all caps. These attribute are added to the config Struct.
267 263 """
268 264
269 265 def __init__(self, filename, path=None):
270 266 """Build a config loader for a filename and path.
271 267
272 268 Parameters
273 269 ----------
274 270 filename : str
275 271 The file name of the config file.
276 272 path : str, list, tuple
277 273 The path to search for the config file on, or a sequence of
278 274 paths to try in order.
279 275 """
280 276 super(PyFileConfigLoader, self).__init__()
281 277 self.filename = filename
282 278 self.path = path
283 279 self.full_filename = ''
284 280 self.data = None
285 281
286 282 def load_config(self):
287 283 """Load the config from a file and return it as a Struct."""
288 284 self.clear()
289 285 try:
290 286 self._find_file()
291 287 except IOError as e:
292 288 raise ConfigFileNotFound(str(e))
293 289 self._read_file_as_dict()
294 290 self._convert_to_config()
295 291 return self.config
296 292
297 293 def _find_file(self):
298 294 """Try to find the file by searching the paths."""
299 295 self.full_filename = filefind(self.filename, self.path)
300 296
301 297 def _read_file_as_dict(self):
302 298 """Load the config file into self.config, with recursive loading."""
303 299 # This closure is made available in the namespace that is used
304 300 # to exec the config file. It allows users to call
305 301 # load_subconfig('myconfig.py') to load config files recursively.
306 302 # It needs to be a closure because it has references to self.path
307 303 # and self.config. The sub-config is loaded with the same path
308 304 # as the parent, but it uses an empty config which is then merged
309 305 # with the parents.
310 306
311 307 # If a profile is specified, the config file will be loaded
312 308 # from that profile
313 309
314 310 def load_subconfig(fname, profile=None):
315 311 # import here to prevent circular imports
316 312 from IPython.core.profiledir import ProfileDir, ProfileDirError
317 313 if profile is not None:
318 314 try:
319 315 profile_dir = ProfileDir.find_profile_dir_by_name(
320 316 get_ipython_dir(),
321 317 profile,
322 318 )
323 319 except ProfileDirError:
324 320 return
325 321 path = profile_dir.location
326 322 else:
327 323 path = self.path
328 324 loader = PyFileConfigLoader(fname, path)
329 325 try:
330 326 sub_config = loader.load_config()
331 327 except ConfigFileNotFound:
332 328 # Pass silently if the sub config is not there. This happens
333 329 # when a user s using a profile, but not the default config.
334 330 pass
335 331 else:
336 332 self.config.merge(sub_config)
337 333
338 334 # Again, this needs to be a closure and should be used in config
339 335 # files to get the config being loaded.
340 336 def get_config():
341 337 return self.config
342 338
343 339 namespace = dict(
344 340 load_subconfig=load_subconfig,
345 341 get_config=get_config,
346 342 __file__=self.full_filename,
347 343 )
348 344 fs_encoding = sys.getfilesystemencoding() or 'ascii'
349 345 conf_filename = self.full_filename.encode(fs_encoding)
350 346 py3compat.execfile(conf_filename, namespace)
351 347
352 348 def _convert_to_config(self):
353 349 if self.data is None:
354 350 ConfigLoaderError('self.data does not exist')
355 351
356 352
357 353 class CommandLineConfigLoader(ConfigLoader):
358 354 """A config loader for command line arguments.
359 355
360 356 As we add more command line based loaders, the common logic should go
361 357 here.
362 358 """
363 359
364 360 def _exec_config_str(self, lhs, rhs):
365 361 """execute self.config.<lhs> = <rhs>
366 362
367 363 * expands ~ with expanduser
368 364 * tries to assign with raw eval, otherwise assigns with just the string,
369 365 allowing `--C.a=foobar` and `--C.a="foobar"` to be equivalent. *Not*
370 366 equivalent are `--C.a=4` and `--C.a='4'`.
371 367 """
372 368 rhs = os.path.expanduser(rhs)
373 369 try:
374 370 # Try to see if regular Python syntax will work. This
375 371 # won't handle strings as the quote marks are removed
376 372 # by the system shell.
377 373 value = eval(rhs)
378 374 except (NameError, SyntaxError):
379 375 # This case happens if the rhs is a string.
380 376 value = rhs
381 377
382 378 exec u'self.config.%s = value' % lhs
383 379
384 380 def _load_flag(self, cfg):
385 381 """update self.config from a flag, which can be a dict or Config"""
386 382 if isinstance(cfg, (dict, Config)):
387 383 # don't clobber whole config sections, update
388 384 # each section from config:
389 385 for sec,c in cfg.iteritems():
390 386 self.config[sec].update(c)
391 387 else:
392 388 raise TypeError("Invalid flag: %r" % cfg)
393 389
394 390 # raw --identifier=value pattern
395 391 # but *also* accept '-' as wordsep, for aliases
396 392 # accepts: --foo=a
397 393 # --Class.trait=value
398 394 # --alias-name=value
399 395 # rejects: -foo=value
400 396 # --foo
401 397 # --Class.trait
402 398 kv_pattern = re.compile(r'\-\-[A-Za-z][\w\-]*(\.[\w\-]+)*\=.*')
403 399
404 400 # just flags, no assignments, with two *or one* leading '-'
405 401 # accepts: --foo
406 402 # -foo-bar-again
407 403 # rejects: --anything=anything
408 404 # --two.word
409 405
410 406 flag_pattern = re.compile(r'\-\-?\w+[\-\w]*$')
411 407
412 408 class KeyValueConfigLoader(CommandLineConfigLoader):
413 409 """A config loader that loads key value pairs from the command line.
414 410
415 411 This allows command line options to be gives in the following form::
416 412
417 413 ipython --profile="foo" --InteractiveShell.autocall=False
418 414 """
419 415
420 416 def __init__(self, argv=None, aliases=None, flags=None):
421 417 """Create a key value pair config loader.
422 418
423 419 Parameters
424 420 ----------
425 421 argv : list
426 422 A list that has the form of sys.argv[1:] which has unicode
427 423 elements of the form u"key=value". If this is None (default),
428 424 then sys.argv[1:] will be used.
429 425 aliases : dict
430 426 A dict of aliases for configurable traits.
431 427 Keys are the short aliases, Values are the resolved trait.
432 428 Of the form: `{'alias' : 'Configurable.trait'}`
433 429 flags : dict
434 430 A dict of flags, keyed by str name. Vaues can be Config objects,
435 431 dicts, or "key=value" strings. If Config or dict, when the flag
436 432 is triggered, The flag is loaded as `self.config.update(m)`.
437 433
438 434 Returns
439 435 -------
440 436 config : Config
441 437 The resulting Config object.
442 438
443 439 Examples
444 440 --------
445 441
446 442 >>> from IPython.config.loader import KeyValueConfigLoader
447 443 >>> cl = KeyValueConfigLoader()
448 444 >>> d = cl.load_config(["--A.name='brian'","--B.number=0"])
449 445 >>> sorted(d.items())
450 446 [('A', {'name': 'brian'}), ('B', {'number': 0})]
451 447 """
452 448 self.clear()
453 449 if argv is None:
454 450 argv = sys.argv[1:]
455 451 self.argv = argv
456 452 self.aliases = aliases or {}
457 453 self.flags = flags or {}
458 454
459 455
460 456 def clear(self):
461 457 super(KeyValueConfigLoader, self).clear()
462 458 self.extra_args = []
463 459
464 460
465 461 def _decode_argv(self, argv, enc=None):
466 462 """decode argv if bytes, using stin.encoding, falling back on default enc"""
467 463 uargv = []
468 464 if enc is None:
469 465 enc = DEFAULT_ENCODING
470 466 for arg in argv:
471 467 if not isinstance(arg, unicode):
472 468 # only decode if not already decoded
473 469 arg = arg.decode(enc)
474 470 uargv.append(arg)
475 471 return uargv
476 472
477 473
478 474 def load_config(self, argv=None, aliases=None, flags=None):
479 475 """Parse the configuration and generate the Config object.
480 476
481 477 After loading, any arguments that are not key-value or
482 478 flags will be stored in self.extra_args - a list of
483 479 unparsed command-line arguments. This is used for
484 480 arguments such as input files or subcommands.
485 481
486 482 Parameters
487 483 ----------
488 484 argv : list, optional
489 485 A list that has the form of sys.argv[1:] which has unicode
490 486 elements of the form u"key=value". If this is None (default),
491 487 then self.argv will be used.
492 488 aliases : dict
493 489 A dict of aliases for configurable traits.
494 490 Keys are the short aliases, Values are the resolved trait.
495 491 Of the form: `{'alias' : 'Configurable.trait'}`
496 492 flags : dict
497 493 A dict of flags, keyed by str name. Values can be Config objects
498 494 or dicts. When the flag is triggered, The config is loaded as
499 495 `self.config.update(cfg)`.
500 496 """
501 497 self.clear()
502 498 if argv is None:
503 499 argv = self.argv
504 500 if aliases is None:
505 501 aliases = self.aliases
506 502 if flags is None:
507 503 flags = self.flags
508 504
509 505 # ensure argv is a list of unicode strings:
510 506 uargv = self._decode_argv(argv)
511 507 for idx,raw in enumerate(uargv):
512 508 # strip leading '-'
513 509 item = raw.lstrip('-')
514 510
515 511 if raw == '--':
516 512 # don't parse arguments after '--'
517 513 # this is useful for relaying arguments to scripts, e.g.
518 514 # ipython -i foo.py --pylab=qt -- args after '--' go-to-foo.py
519 515 self.extra_args.extend(uargv[idx+1:])
520 516 break
521 517
522 518 if kv_pattern.match(raw):
523 519 lhs,rhs = item.split('=',1)
524 520 # Substitute longnames for aliases.
525 521 if lhs in aliases:
526 522 lhs = aliases[lhs]
527 523 if '.' not in lhs:
528 524 # probably a mistyped alias, but not technically illegal
529 525 warn.warn("Unrecognized alias: '%s', it will probably have no effect."%lhs)
530 526 try:
531 527 self._exec_config_str(lhs, rhs)
532 528 except Exception:
533 529 raise ArgumentError("Invalid argument: '%s'" % raw)
534 530
535 531 elif flag_pattern.match(raw):
536 532 if item in flags:
537 533 cfg,help = flags[item]
538 534 self._load_flag(cfg)
539 535 else:
540 536 raise ArgumentError("Unrecognized flag: '%s'"%raw)
541 537 elif raw.startswith('-'):
542 538 kv = '--'+item
543 539 if kv_pattern.match(kv):
544 540 raise ArgumentError("Invalid argument: '%s', did you mean '%s'?"%(raw, kv))
545 541 else:
546 542 raise ArgumentError("Invalid argument: '%s'"%raw)
547 543 else:
548 544 # keep all args that aren't valid in a list,
549 545 # in case our parent knows what to do with them.
550 546 self.extra_args.append(item)
551 547 return self.config
552 548
553 549 class ArgParseConfigLoader(CommandLineConfigLoader):
554 550 """A loader that uses the argparse module to load from the command line."""
555 551
556 552 def __init__(self, argv=None, aliases=None, flags=None, *parser_args, **parser_kw):
557 553 """Create a config loader for use with argparse.
558 554
559 555 Parameters
560 556 ----------
561 557
562 558 argv : optional, list
563 559 If given, used to read command-line arguments from, otherwise
564 560 sys.argv[1:] is used.
565 561
566 562 parser_args : tuple
567 563 A tuple of positional arguments that will be passed to the
568 564 constructor of :class:`argparse.ArgumentParser`.
569 565
570 566 parser_kw : dict
571 567 A tuple of keyword arguments that will be passed to the
572 568 constructor of :class:`argparse.ArgumentParser`.
573 569
574 570 Returns
575 571 -------
576 572 config : Config
577 573 The resulting Config object.
578 574 """
579 575 super(CommandLineConfigLoader, self).__init__()
580 576 self.clear()
581 577 if argv is None:
582 578 argv = sys.argv[1:]
583 579 self.argv = argv
584 580 self.aliases = aliases or {}
585 581 self.flags = flags or {}
586 582
587 583 self.parser_args = parser_args
588 584 self.version = parser_kw.pop("version", None)
589 585 kwargs = dict(argument_default=argparse.SUPPRESS)
590 586 kwargs.update(parser_kw)
591 587 self.parser_kw = kwargs
592 588
593 589 def load_config(self, argv=None, aliases=None, flags=None):
594 590 """Parse command line arguments and return as a Config object.
595 591
596 592 Parameters
597 593 ----------
598 594
599 595 args : optional, list
600 596 If given, a list with the structure of sys.argv[1:] to parse
601 597 arguments from. If not given, the instance's self.argv attribute
602 598 (given at construction time) is used."""
603 599 self.clear()
604 600 if argv is None:
605 601 argv = self.argv
606 602 if aliases is None:
607 603 aliases = self.aliases
608 604 if flags is None:
609 605 flags = self.flags
610 606 self._create_parser(aliases, flags)
611 607 self._parse_args(argv)
612 608 self._convert_to_config()
613 609 return self.config
614 610
615 611 def get_extra_args(self):
616 612 if hasattr(self, 'extra_args'):
617 613 return self.extra_args
618 614 else:
619 615 return []
620 616
621 617 def _create_parser(self, aliases=None, flags=None):
622 618 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
623 619 self._add_arguments(aliases, flags)
624 620
625 621 def _add_arguments(self, aliases=None, flags=None):
626 622 raise NotImplementedError("subclasses must implement _add_arguments")
627 623
628 624 def _parse_args(self, args):
629 625 """self.parser->self.parsed_data"""
630 626 # decode sys.argv to support unicode command-line options
631 627 enc = DEFAULT_ENCODING
632 628 uargs = [py3compat.cast_unicode(a, enc) for a in args]
633 629 self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
634 630
635 631 def _convert_to_config(self):
636 632 """self.parsed_data->self.config"""
637 633 for k, v in vars(self.parsed_data).iteritems():
638 634 exec "self.config.%s = v"%k in locals(), globals()
639 635
640 636 class KVArgParseConfigLoader(ArgParseConfigLoader):
641 637 """A config loader that loads aliases and flags with argparse,
642 638 but will use KVLoader for the rest. This allows better parsing
643 639 of common args, such as `ipython -c 'print 5'`, but still gets
644 640 arbitrary config with `ipython --InteractiveShell.use_readline=False`"""
645 641
646 642 def _add_arguments(self, aliases=None, flags=None):
647 643 self.alias_flags = {}
648 644 # print aliases, flags
649 645 if aliases is None:
650 646 aliases = self.aliases
651 647 if flags is None:
652 648 flags = self.flags
653 649 paa = self.parser.add_argument
654 650 for key,value in aliases.iteritems():
655 651 if key in flags:
656 652 # flags
657 653 nargs = '?'
658 654 else:
659 655 nargs = None
660 656 if len(key) is 1:
661 657 paa('-'+key, '--'+key, type=unicode, dest=value, nargs=nargs)
662 658 else:
663 659 paa('--'+key, type=unicode, dest=value, nargs=nargs)
664 660 for key, (value, help) in flags.iteritems():
665 661 if key in self.aliases:
666 662 #
667 663 self.alias_flags[self.aliases[key]] = value
668 664 continue
669 665 if len(key) is 1:
670 666 paa('-'+key, '--'+key, action='append_const', dest='_flags', const=value)
671 667 else:
672 668 paa('--'+key, action='append_const', dest='_flags', const=value)
673 669
674 670 def _convert_to_config(self):
675 671 """self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
676 672 # remove subconfigs list from namespace before transforming the Namespace
677 673 if '_flags' in self.parsed_data:
678 674 subcs = self.parsed_data._flags
679 675 del self.parsed_data._flags
680 676 else:
681 677 subcs = []
682 678
683 679 for k, v in vars(self.parsed_data).iteritems():
684 680 if v is None:
685 681 # it was a flag that shares the name of an alias
686 682 subcs.append(self.alias_flags[k])
687 683 else:
688 684 # eval the KV assignment
689 685 self._exec_config_str(k, v)
690 686
691 687 for subc in subcs:
692 688 self._load_flag(subc)
693 689
694 690 if self.extra_args:
695 691 sub_parser = KeyValueConfigLoader()
696 692 sub_parser.load_config(self.extra_args)
697 693 self.config.merge(sub_parser.config)
698 694 self.extra_args = sub_parser.extra_args
699 695
700 696
701 697 def load_pyconfig_files(config_files, path):
702 698 """Load multiple Python config files, merging each of them in turn.
703 699
704 700 Parameters
705 701 ==========
706 702 config_files : list of str
707 703 List of config files names to load and merge into the config.
708 704 path : unicode
709 705 The full path to the location of the config files.
710 706 """
711 707 config = Config()
712 708 for cf in config_files:
713 709 loader = PyFileConfigLoader(cf, path=path)
714 710 try:
715 711 next_config = loader.load_config()
716 712 except ConfigFileNotFound:
717 713 pass
718 714 except:
719 715 raise
720 716 else:
721 717 config.merge(next_config)
722 718 return config
General Comments 0
You need to be logged in to leave comments. Login now