##// END OF EJS Templates
polish pass
MinRK -
Show More
@@ -1,603 +1,603 b''
1 1 # encoding: utf-8
2 2 """
3 3 A base class for a configurable application.
4 4
5 5 Authors:
6 6
7 7 * Brian Granger
8 8 * Min RK
9 9 """
10 10 from __future__ import print_function
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Copyright (C) 2008-2011 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-----------------------------------------------------------------------------
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23 23 import logging
24 24 import os
25 25 import re
26 26 import sys
27 27 from copy import deepcopy
28 28 from collections import defaultdict
29 29
30 30 from IPython.external.decorator import decorator
31 31
32 32 from IPython.config.configurable import SingletonConfigurable
33 33 from IPython.config.loader import (
34 34 KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, JSONFileConfigLoader
35 35 )
36 36
37 37 from IPython.utils.traitlets import (
38 38 Unicode, List, Enum, Dict, Instance, TraitError
39 39 )
40 40 from IPython.utils.importstring import import_item
41 41 from IPython.utils.text import indent, wrap_paragraphs, dedent
42 42 from IPython.utils import py3compat
43 43 from IPython.utils.py3compat import string_types, iteritems
44 44
45 45 #-----------------------------------------------------------------------------
46 46 # function for re-wrapping a helpstring
47 47 #-----------------------------------------------------------------------------
48 48
49 49 #-----------------------------------------------------------------------------
50 50 # Descriptions for the various sections
51 51 #-----------------------------------------------------------------------------
52 52
53 53 # merge flags&aliases into options
54 54 option_description = """
55 55 Arguments that take values are actually convenience aliases to full
56 56 Configurables, whose aliases are listed on the help line. For more information
57 57 on full configurables, see '--help-all'.
58 58 """.strip() # trim newlines of front and back
59 59
60 60 keyvalue_description = """
61 61 Parameters are set from command-line arguments of the form:
62 62 `--Class.trait=value`.
63 63 This line is evaluated in Python, so simple expressions are allowed, e.g.::
64 64 `--C.a='range(3)'` For setting C.a=[0,1,2].
65 65 """.strip() # trim newlines of front and back
66 66
67 67 # sys.argv can be missing, for example when python is embedded. See the docs
68 68 # for details: http://docs.python.org/2/c-api/intro.html#embedding-python
69 69 if not hasattr(sys, "argv"):
70 70 sys.argv = [""]
71 71
72 72 subcommand_description = """
73 73 Subcommands are launched as `{app} cmd [args]`. For information on using
74 74 subcommand 'cmd', do: `{app} cmd -h`.
75 75 """.strip().format(app=os.path.basename(sys.argv[0]))
76 76 # get running program name
77 77
78 78 #-----------------------------------------------------------------------------
79 79 # Application class
80 80 #-----------------------------------------------------------------------------
81 81
82 82 @decorator
83 83 def catch_config_error(method, app, *args, **kwargs):
84 84 """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
85 85
86 86 On a TraitError (generally caused by bad config), this will print the trait's
87 87 message, and exit the app.
88 88
89 89 For use on init methods, to prevent invoking excepthook on invalid input.
90 90 """
91 91 try:
92 92 return method(app, *args, **kwargs)
93 93 except (TraitError, ArgumentError) as e:
94 94 app.print_help()
95 95 app.log.fatal("Bad config encountered during initialization:")
96 96 app.log.fatal(str(e))
97 97 app.log.debug("Config at the time: %s", app.config)
98 98 app.exit(1)
99 99
100 100
101 101 class ApplicationError(Exception):
102 102 pass
103 103
104 104 class LevelFormatter(logging.Formatter):
105 105 """Formatter with additional `highlevel` record
106 106
107 107 This field is empty if log level is less than highlevel_limit,
108 108 otherwise it is formatted with self.highlevel_format.
109 109
110 110 Useful for adding 'WARNING' to warning messages,
111 111 without adding 'INFO' to info, etc.
112 112 """
113 113 highlevel_limit = logging.WARN
114 114 highlevel_format = " %(levelname)s |"
115 115
116 116 def format(self, record):
117 117 if record.levelno >= self.highlevel_limit:
118 118 record.highlevel = self.highlevel_format % record.__dict__
119 119 else:
120 120 record.highlevel = ""
121 121 return super(LevelFormatter, self).format(record)
122 122
123 123
124 124 class Application(SingletonConfigurable):
125 125 """A singleton application with full configuration support."""
126 126
127 127 # The name of the application, will usually match the name of the command
128 128 # line application
129 129 name = Unicode(u'application')
130 130
131 131 # The description of the application that is printed at the beginning
132 132 # of the help.
133 133 description = Unicode(u'This is an application.')
134 134 # default section descriptions
135 135 option_description = Unicode(option_description)
136 136 keyvalue_description = Unicode(keyvalue_description)
137 137 subcommand_description = Unicode(subcommand_description)
138 138
139 139 # The usage and example string that goes at the end of the help string.
140 140 examples = Unicode()
141 141
142 142 # A sequence of Configurable subclasses whose config=True attributes will
143 143 # be exposed at the command line.
144 144 classes = List([])
145 145
146 146 # The version string of this application.
147 147 version = Unicode(u'0.0')
148 148
149 149 # the argv used to initialize the application
150 150 argv = List()
151 151
152 152 # The log level for the application
153 153 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
154 154 default_value=logging.WARN,
155 155 config=True,
156 156 help="Set the log level by value or name.")
157 157 def _log_level_changed(self, name, old, new):
158 158 """Adjust the log level when log_level is set."""
159 159 if isinstance(new, string_types):
160 160 new = getattr(logging, new)
161 161 self.log_level = new
162 162 self.log.setLevel(new)
163 163
164 164 log_datefmt = Unicode("%Y-%m-%d %H:%M:%S", config=True,
165 165 help="The date format used by logging formatters for %(asctime)s"
166 166 )
167 167 def _log_datefmt_changed(self, name, old, new):
168 168 self._log_format_changed()
169 169
170 170 log_format = Unicode("[%(name)s]%(highlevel)s %(message)s", config=True,
171 171 help="The Logging format template",
172 172 )
173 173 def _log_format_changed(self, name, old, new):
174 174 """Change the log formatter when log_format is set."""
175 175 _log_handler = self.log.handlers[0]
176 176 _log_formatter = LevelFormatter(new, datefmt=self.log_datefmt)
177 177 _log_handler.setFormatter(_log_formatter)
178 178
179 179 log = Instance(logging.Logger)
180 180 def _log_default(self):
181 181 """Start logging for this application.
182 182
183 183 The default is to log to stderr using a StreamHandler, if no default
184 184 handler already exists. The log level starts at logging.WARN, but this
185 185 can be adjusted by setting the ``log_level`` attribute.
186 186 """
187 187 log = logging.getLogger(self.__class__.__name__)
188 188 log.setLevel(self.log_level)
189 189 log.propagate = False
190 190 _log = log # copied from Logger.hasHandlers() (new in Python 3.2)
191 191 while _log:
192 192 if _log.handlers:
193 193 return log
194 194 if not _log.propagate:
195 195 break
196 196 else:
197 197 _log = _log.parent
198 198 if sys.executable.endswith('pythonw.exe'):
199 199 # this should really go to a file, but file-logging is only
200 200 # hooked up in parallel applications
201 201 _log_handler = logging.StreamHandler(open(os.devnull, 'w'))
202 202 else:
203 203 _log_handler = logging.StreamHandler()
204 204 _log_formatter = LevelFormatter(self.log_format, datefmt=self.log_datefmt)
205 205 _log_handler.setFormatter(_log_formatter)
206 206 log.addHandler(_log_handler)
207 207 return log
208 208
209 209 # the alias map for configurables
210 210 aliases = Dict({'log-level' : 'Application.log_level'})
211 211
212 212 # flags for loading Configurables or store_const style flags
213 213 # flags are loaded from this dict by '--key' flags
214 214 # this must be a dict of two-tuples, the first element being the Config/dict
215 215 # and the second being the help string for the flag
216 216 flags = Dict()
217 217 def _flags_changed(self, name, old, new):
218 218 """ensure flags dict is valid"""
219 219 for key,value in iteritems(new):
220 220 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
221 221 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
222 222 assert isinstance(value[1], string_types), "Bad flag: %r:%s"%(key,value)
223 223
224 224
225 225 # subcommands for launching other applications
226 226 # if this is not empty, this will be a parent Application
227 227 # this must be a dict of two-tuples,
228 228 # the first element being the application class/import string
229 229 # and the second being the help string for the subcommand
230 230 subcommands = Dict()
231 231 # parse_command_line will initialize a subapp, if requested
232 232 subapp = Instance('IPython.config.application.Application', allow_none=True)
233 233
234 234 # extra command-line arguments that don't set config values
235 235 extra_args = List(Unicode)
236 236
237 237
238 238 def __init__(self, **kwargs):
239 239 SingletonConfigurable.__init__(self, **kwargs)
240 240 # Ensure my class is in self.classes, so my attributes appear in command line
241 241 # options and config files.
242 242 if self.__class__ not in self.classes:
243 243 self.classes.insert(0, self.__class__)
244 244
245 245 def _config_changed(self, name, old, new):
246 246 SingletonConfigurable._config_changed(self, name, old, new)
247 247 self.log.debug('Config changed:')
248 248 self.log.debug(repr(new))
249 249
250 250 @catch_config_error
251 251 def initialize(self, argv=None):
252 252 """Do the basic steps to configure me.
253 253
254 254 Override in subclasses.
255 255 """
256 256 self.parse_command_line(argv)
257 257
258 258
259 259 def start(self):
260 260 """Start the app mainloop.
261 261
262 262 Override in subclasses.
263 263 """
264 264 if self.subapp is not None:
265 265 return self.subapp.start()
266 266
267 267 def print_alias_help(self):
268 268 """Print the alias part of the help."""
269 269 if not self.aliases:
270 270 return
271 271
272 272 lines = []
273 273 classdict = {}
274 274 for cls in self.classes:
275 275 # include all parents (up to, but excluding Configurable) in available names
276 276 for c in cls.mro()[:-3]:
277 277 classdict[c.__name__] = c
278 278
279 279 for alias, longname in iteritems(self.aliases):
280 280 classname, traitname = longname.split('.',1)
281 281 cls = classdict[classname]
282 282
283 283 trait = cls.class_traits(config=True)[traitname]
284 284 help = cls.class_get_trait_help(trait).splitlines()
285 285 # reformat first line
286 286 help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
287 287 if len(alias) == 1:
288 288 help[0] = help[0].replace('--%s='%alias, '-%s '%alias)
289 289 lines.extend(help)
290 290 # lines.append('')
291 291 print(os.linesep.join(lines))
292 292
293 293 def print_flag_help(self):
294 294 """Print the flag part of the help."""
295 295 if not self.flags:
296 296 return
297 297
298 298 lines = []
299 299 for m, (cfg,help) in iteritems(self.flags):
300 300 prefix = '--' if len(m) > 1 else '-'
301 301 lines.append(prefix+m)
302 302 lines.append(indent(dedent(help.strip())))
303 303 # lines.append('')
304 304 print(os.linesep.join(lines))
305 305
306 306 def print_options(self):
307 307 if not self.flags and not self.aliases:
308 308 return
309 309 lines = ['Options']
310 310 lines.append('-'*len(lines[0]))
311 311 lines.append('')
312 312 for p in wrap_paragraphs(self.option_description):
313 313 lines.append(p)
314 314 lines.append('')
315 315 print(os.linesep.join(lines))
316 316 self.print_flag_help()
317 317 self.print_alias_help()
318 318 print()
319 319
320 320 def print_subcommands(self):
321 321 """Print the subcommand part of the help."""
322 322 if not self.subcommands:
323 323 return
324 324
325 325 lines = ["Subcommands"]
326 326 lines.append('-'*len(lines[0]))
327 327 lines.append('')
328 328 for p in wrap_paragraphs(self.subcommand_description):
329 329 lines.append(p)
330 330 lines.append('')
331 331 for subc, (cls, help) in iteritems(self.subcommands):
332 332 lines.append(subc)
333 333 if help:
334 334 lines.append(indent(dedent(help.strip())))
335 335 lines.append('')
336 336 print(os.linesep.join(lines))
337 337
338 338 def print_help(self, classes=False):
339 339 """Print the help for each Configurable class in self.classes.
340 340
341 341 If classes=False (the default), only flags and aliases are printed.
342 342 """
343 343 self.print_description()
344 344 self.print_subcommands()
345 345 self.print_options()
346 346
347 347 if classes:
348 348 if self.classes:
349 349 print("Class parameters")
350 350 print("----------------")
351 351 print()
352 352 for p in wrap_paragraphs(self.keyvalue_description):
353 353 print(p)
354 354 print()
355 355
356 356 for cls in self.classes:
357 357 cls.class_print_help()
358 358 print()
359 359 else:
360 360 print("To see all available configurables, use `--help-all`")
361 361 print()
362 362
363 363 self.print_examples()
364 364
365 365
366 366 def print_description(self):
367 367 """Print the application description."""
368 368 for p in wrap_paragraphs(self.description):
369 369 print(p)
370 370 print()
371 371
372 372 def print_examples(self):
373 373 """Print usage and examples.
374 374
375 375 This usage string goes at the end of the command line help string
376 376 and should contain examples of the application's usage.
377 377 """
378 378 if self.examples:
379 379 print("Examples")
380 380 print("--------")
381 381 print()
382 382 print(indent(dedent(self.examples.strip())))
383 383 print()
384 384
385 385 def print_version(self):
386 386 """Print the version string."""
387 387 print(self.version)
388 388
389 389 def update_config(self, config):
390 390 """Fire the traits events when the config is updated."""
391 391 # Save a copy of the current config.
392 392 newconfig = deepcopy(self.config)
393 393 # Merge the new config into the current one.
394 394 newconfig.merge(config)
395 395 # Save the combined config as self.config, which triggers the traits
396 396 # events.
397 397 self.config = newconfig
398 398
399 399 @catch_config_error
400 400 def initialize_subcommand(self, subc, argv=None):
401 401 """Initialize a subcommand with argv."""
402 402 subapp,help = self.subcommands.get(subc)
403 403
404 404 if isinstance(subapp, string_types):
405 405 subapp = import_item(subapp)
406 406
407 407 # clear existing instances
408 408 self.__class__.clear_instance()
409 409 # instantiate
410 410 self.subapp = subapp.instance(config=self.config)
411 411 # and initialize subapp
412 412 self.subapp.initialize(argv)
413 413
414 414 def flatten_flags(self):
415 415 """flatten flags and aliases, so cl-args override as expected.
416 416
417 417 This prevents issues such as an alias pointing to InteractiveShell,
418 418 but a config file setting the same trait in TerminalInteraciveShell
419 419 getting inappropriate priority over the command-line arg.
420 420
421 421 Only aliases with exactly one descendent in the class list
422 422 will be promoted.
423 423
424 424 """
425 425 # build a tree of classes in our list that inherit from a particular
426 426 # it will be a dict by parent classname of classes in our list
427 427 # that are descendents
428 428 mro_tree = defaultdict(list)
429 429 for cls in self.classes:
430 430 clsname = cls.__name__
431 431 for parent in cls.mro()[1:-3]:
432 432 # exclude cls itself and Configurable,HasTraits,object
433 433 mro_tree[parent.__name__].append(clsname)
434 434 # flatten aliases, which have the form:
435 435 # { 'alias' : 'Class.trait' }
436 436 aliases = {}
437 437 for alias, cls_trait in iteritems(self.aliases):
438 438 cls,trait = cls_trait.split('.',1)
439 439 children = mro_tree[cls]
440 440 if len(children) == 1:
441 441 # exactly one descendent, promote alias
442 442 cls = children[0]
443 443 aliases[alias] = '.'.join([cls,trait])
444 444
445 445 # flatten flags, which are of the form:
446 446 # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
447 447 flags = {}
448 448 for key, (flagdict, help) in iteritems(self.flags):
449 449 newflag = {}
450 450 for cls, subdict in iteritems(flagdict):
451 451 children = mro_tree[cls]
452 452 # exactly one descendent, promote flag section
453 453 if len(children) == 1:
454 454 cls = children[0]
455 455 newflag[cls] = subdict
456 456 flags[key] = (newflag, help)
457 457 return flags, aliases
458 458
459 459 @catch_config_error
460 460 def parse_command_line(self, argv=None):
461 461 """Parse the command line arguments."""
462 462 argv = sys.argv[1:] if argv is None else argv
463 463 self.argv = [ py3compat.cast_unicode(arg) for arg in argv ]
464 464
465 465 if argv and argv[0] == 'help':
466 466 # turn `ipython help notebook` into `ipython notebook -h`
467 467 argv = argv[1:] + ['-h']
468 468
469 469 if self.subcommands and len(argv) > 0:
470 470 # we have subcommands, and one may have been specified
471 471 subc, subargv = argv[0], argv[1:]
472 472 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
473 473 # it's a subcommand, and *not* a flag or class parameter
474 474 return self.initialize_subcommand(subc, subargv)
475 475
476 476 # Arguments after a '--' argument are for the script IPython may be
477 477 # about to run, not IPython iteslf. For arguments parsed here (help and
478 478 # version), we want to only search the arguments up to the first
479 479 # occurrence of '--', which we're calling interpreted_argv.
480 480 try:
481 481 interpreted_argv = argv[:argv.index('--')]
482 482 except ValueError:
483 483 interpreted_argv = argv
484 484
485 485 if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')):
486 486 self.print_help('--help-all' in interpreted_argv)
487 487 self.exit(0)
488 488
489 489 if '--version' in interpreted_argv or '-V' in interpreted_argv:
490 490 self.print_version()
491 491 self.exit(0)
492 492
493 493 # flatten flags&aliases, so cl-args get appropriate priority:
494 494 flags,aliases = self.flatten_flags()
495 495 loader = KVArgParseConfigLoader(argv=argv, aliases=aliases,
496 496 flags=flags, log=self.log)
497 497 config = loader.load_config()
498 498 self.update_config(config)
499 499 # store unparsed args in extra_args
500 500 self.extra_args = loader.extra_args
501 501
502 502 @classmethod
503 503 def _load_config_files(cls, basefilename, path=None, log=None):
504 504 """Load config files (py,json) by filename and path.
505 505
506 506 yield each config object in turn.
507 507 """
508 508
509 509 pyloader = PyFileConfigLoader(basefilename+'.py', path=path, log=log)
510 510 jsonloader = JSONFileConfigLoader(basefilename+'.json', path=path, log=log)
511 511 config_found = False
512 512 config = None
513 513 for loader in [pyloader, jsonloader]:
514 514 try:
515 515 config = loader.load_config()
516 516 config_found = True
517 517 except ConfigFileNotFound:
518 518 pass
519 519 except Exception:
520 520 # try to get the full filename, but it will be empty in the
521 521 # unlikely event that the error raised before filefind finished
522 522 filename = loader.full_filename or filename
523 523 # problem while running the file
524 524 log.error("Exception while loading config file %s",
525 525 filename, exc_info=True)
526 526 else:
527 527 log.debug("Loaded config file: %s", loader.full_filename)
528 if config :
528 if config:
529 529 yield config
530 530
531 if not config_found :
532 raise ConfigFileNotFound('Neither .json, not .py file found.')
531 if not config_found:
532 raise ConfigFileNotFound('Neither .json, nor .py config file found.')
533 533 raise StopIteration
534 534
535 535
536 536 @catch_config_error
537 537 def load_config_file(self, filename, path=None):
538 """Load config files (json/py) by filename and path."""
538 """Load config files by filename and path."""
539 539 filename, ext = os.path.splitext(filename)
540 for config in self._load_config_files(filename, path=path , log=self.log):
540 for config in self._load_config_files(filename, path=path, log=self.log):
541 541 self.update_config(config)
542 542
543 543
544 544 def generate_config_file(self):
545 545 """generate default config file from Configurables"""
546 546 lines = ["# Configuration file for %s."%self.name]
547 547 lines.append('')
548 548 lines.append('c = get_config()')
549 549 lines.append('')
550 550 for cls in self.classes:
551 551 lines.append(cls.class_config_section())
552 552 return '\n'.join(lines)
553 553
554 554 def exit(self, exit_status=0):
555 555 self.log.debug("Exiting application: %s" % self.name)
556 556 sys.exit(exit_status)
557 557
558 558 @classmethod
559 559 def launch_instance(cls, argv=None, **kwargs):
560 560 """Launch a global instance of this Application
561 561
562 562 If a global instance already exists, this reinitializes and starts it
563 563 """
564 564 app = cls.instance(**kwargs)
565 565 app.initialize(argv)
566 566 app.start()
567 567
568 568 #-----------------------------------------------------------------------------
569 569 # utility functions, for convenience
570 570 #-----------------------------------------------------------------------------
571 571
572 572 def boolean_flag(name, configurable, set_help='', unset_help=''):
573 573 """Helper for building basic --trait, --no-trait flags.
574 574
575 575 Parameters
576 576 ----------
577 577
578 578 name : str
579 579 The name of the flag.
580 580 configurable : str
581 581 The 'Class.trait' string of the trait to be set/unset with the flag
582 582 set_help : unicode
583 583 help string for --name flag
584 584 unset_help : unicode
585 585 help string for --no-name flag
586 586
587 587 Returns
588 588 -------
589 589
590 590 cfg : dict
591 591 A dict with two keys: 'name', and 'no-name', for setting and unsetting
592 592 the trait, respectively.
593 593 """
594 594 # default helpstrings
595 595 set_help = set_help or "set %s=True"%configurable
596 596 unset_help = unset_help or "set %s=False"%configurable
597 597
598 598 cls,trait = configurable.split('.')
599 599
600 600 setter = {cls : {trait : True}}
601 601 unsetter = {cls : {trait : False}}
602 602 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
603 603
@@ -1,841 +1,840 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 argparse
27 27 import copy
28 28 import os
29 29 import re
30 30 import sys
31 31 import json
32 32
33 33 from IPython.utils.path import filefind, get_ipython_dir
34 34 from IPython.utils import py3compat
35 35 from IPython.utils.encoding import DEFAULT_ENCODING
36 36 from IPython.utils.py3compat import unicode_type, iteritems
37 37 from IPython.utils.traitlets import HasTraits, List, Any, TraitError
38 38
39 39 #-----------------------------------------------------------------------------
40 40 # Exceptions
41 41 #-----------------------------------------------------------------------------
42 42
43 43
44 44 class ConfigError(Exception):
45 45 pass
46 46
47 47 class ConfigLoaderError(ConfigError):
48 48 pass
49 49
50 50 class ConfigFileNotFound(ConfigError):
51 51 pass
52 52
53 53 class ArgumentError(ConfigLoaderError):
54 54 pass
55 55
56 56 #-----------------------------------------------------------------------------
57 57 # Argparse fix
58 58 #-----------------------------------------------------------------------------
59 59
60 60 # Unfortunately argparse by default prints help messages to stderr instead of
61 61 # stdout. This makes it annoying to capture long help screens at the command
62 62 # line, since one must know how to pipe stderr, which many users don't know how
63 63 # to do. So we override the print_help method with one that defaults to
64 64 # stdout and use our class instead.
65 65
66 66 class ArgumentParser(argparse.ArgumentParser):
67 67 """Simple argparse subclass that prints help to stdout by default."""
68 68
69 69 def print_help(self, file=None):
70 70 if file is None:
71 71 file = sys.stdout
72 72 return super(ArgumentParser, self).print_help(file)
73 73
74 74 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
75 75
76 76 #-----------------------------------------------------------------------------
77 77 # Config class for holding config information
78 78 #-----------------------------------------------------------------------------
79 79
80 80 class LazyConfigValue(HasTraits):
81 81 """Proxy object for exposing methods on configurable containers
82 82
83 83 Exposes:
84 84
85 85 - append, extend, insert on lists
86 86 - update on dicts
87 87 - update, add on sets
88 88 """
89 89
90 90 _value = None
91 91
92 92 # list methods
93 93 _extend = List()
94 94 _prepend = List()
95 95
96 96 def append(self, obj):
97 97 self._extend.append(obj)
98 98
99 99 def extend(self, other):
100 100 self._extend.extend(other)
101 101
102 102 def prepend(self, other):
103 103 """like list.extend, but for the front"""
104 104 self._prepend[:0] = other
105 105
106 106 _inserts = List()
107 107 def insert(self, index, other):
108 108 if not isinstance(index, int):
109 109 raise TypeError("An integer is required")
110 110 self._inserts.append((index, other))
111 111
112 112 # dict methods
113 113 # update is used for both dict and set
114 114 _update = Any()
115 115 def update(self, other):
116 116 if self._update is None:
117 117 if isinstance(other, dict):
118 118 self._update = {}
119 119 else:
120 120 self._update = set()
121 121 self._update.update(other)
122 122
123 123 # set methods
124 124 def add(self, obj):
125 125 self.update({obj})
126 126
127 127 def get_value(self, initial):
128 128 """construct the value from the initial one
129 129
130 130 after applying any insert / extend / update changes
131 131 """
132 132 if self._value is not None:
133 133 return self._value
134 134 value = copy.deepcopy(initial)
135 135 if isinstance(value, list):
136 136 for idx, obj in self._inserts:
137 137 value.insert(idx, obj)
138 138 value[:0] = self._prepend
139 139 value.extend(self._extend)
140 140
141 141 elif isinstance(value, dict):
142 142 if self._update:
143 143 value.update(self._update)
144 144 elif isinstance(value, set):
145 145 if self._update:
146 146 value.update(self._update)
147 147 self._value = value
148 148 return value
149 149
150 150 def to_dict(self):
151 151 """return JSONable dict form of my data
152 152
153 153 Currently update as dict or set, extend, prepend as lists, and inserts as list of tuples.
154 154 """
155 155 d = {}
156 156 if self._update:
157 157 d['update'] = self._update
158 158 if self._extend:
159 159 d['extend'] = self._extend
160 160 if self._prepend:
161 161 d['prepend'] = self._prepend
162 162 elif self._inserts:
163 163 d['inserts'] = self._inserts
164 164 return d
165 165
166 166
167 167 def _is_section_key(key):
168 168 """Is a Config key a section name (does it start with a capital)?"""
169 169 if key and key[0].upper()==key[0] and not key.startswith('_'):
170 170 return True
171 171 else:
172 172 return False
173 173
174 174
175 175 class Config(dict):
176 176 """An attribute based dict that can do smart merges."""
177 177
178 178 def __init__(self, *args, **kwds):
179 179 dict.__init__(self, *args, **kwds)
180 180 self._ensure_subconfig()
181 181
182 182 def _ensure_subconfig(self):
183 183 """ensure that sub-dicts that should be Config objects are
184 184
185 185 casts dicts that are under section keys to Config objects,
186 186 which is necessary for constructing Config objects from dict literals.
187 187 """
188 188 for key in self:
189 189 obj = self[key]
190 190 if _is_section_key(key) \
191 191 and isinstance(obj, dict) \
192 192 and not isinstance(obj, Config):
193 193 setattr(self, key, Config(obj))
194 194
195 195 def _merge(self, other):
196 196 """deprecated alias, use Config.merge()"""
197 197 self.merge(other)
198 198
199 199 def merge(self, other):
200 200 """merge another config object into this one"""
201 201 to_update = {}
202 202 for k, v in iteritems(other):
203 203 if k not in self:
204 204 to_update[k] = copy.deepcopy(v)
205 205 else: # I have this key
206 206 if isinstance(v, Config) and isinstance(self[k], Config):
207 207 # Recursively merge common sub Configs
208 208 self[k].merge(v)
209 209 else:
210 210 # Plain updates for non-Configs
211 211 to_update[k] = copy.deepcopy(v)
212 212
213 213 self.update(to_update)
214 214
215 215 def __contains__(self, key):
216 216 # allow nested contains of the form `"Section.key" in config`
217 217 if '.' in key:
218 218 first, remainder = key.split('.', 1)
219 219 if first not in self:
220 220 return False
221 221 return remainder in self[first]
222 222
223 223 return super(Config, self).__contains__(key)
224 224
225 225 # .has_key is deprecated for dictionaries.
226 226 has_key = __contains__
227 227
228 228 def _has_section(self, key):
229 229 return _is_section_key(key) and key in self
230 230
231 231 def copy(self):
232 232 return type(self)(dict.copy(self))
233 233
234 234 def __copy__(self):
235 235 return self.copy()
236 236
237 237 def __deepcopy__(self, memo):
238 238 import copy
239 239 return type(self)(copy.deepcopy(list(self.items())))
240 240
241 241 def __getitem__(self, key):
242 242 try:
243 243 return dict.__getitem__(self, key)
244 244 except KeyError:
245 245 if _is_section_key(key):
246 246 c = Config()
247 247 dict.__setitem__(self, key, c)
248 248 return c
249 249 else:
250 250 # undefined, create lazy value, used for container methods
251 251 v = LazyConfigValue()
252 252 dict.__setitem__(self, key, v)
253 253 return v
254 254
255 255 def __setitem__(self, key, value):
256 256 if _is_section_key(key):
257 257 if not isinstance(value, Config):
258 258 raise ValueError('values whose keys begin with an uppercase '
259 259 'char must be Config instances: %r, %r' % (key, value))
260 260 dict.__setitem__(self, key, value)
261 261
262 262 def __getattr__(self, key):
263 263 if key.startswith('__'):
264 264 return dict.__getattr__(self, key)
265 265 try:
266 266 return self.__getitem__(key)
267 267 except KeyError as e:
268 268 raise AttributeError(e)
269 269
270 270 def __setattr__(self, key, value):
271 271 if key.startswith('__'):
272 272 return dict.__setattr__(self, key, value)
273 273 try:
274 274 self.__setitem__(key, value)
275 275 except KeyError as e:
276 276 raise AttributeError(e)
277 277
278 278 def __delattr__(self, key):
279 279 if key.startswith('__'):
280 280 return dict.__delattr__(self, key)
281 281 try:
282 282 dict.__delitem__(self, key)
283 283 except KeyError as e:
284 284 raise AttributeError(e)
285 285
286 286
287 287 #-----------------------------------------------------------------------------
288 288 # Config loading classes
289 289 #-----------------------------------------------------------------------------
290 290
291 291
292 292 class ConfigLoader(object):
293 293 """A object for loading configurations from just about anywhere.
294 294
295 The resulting configuration is packaged as a :class:`Struct`.
295 The resulting configuration is packaged as a :class:`Config`.
296 296
297 297 Notes
298 298 -----
299 299 A :class:`ConfigLoader` does one thing: load a config from a source
300 (file, command line arguments) and returns the data as a :class:`Struct`.
300 (file, command line arguments) and returns the data as a :class:`Config` object.
301 301 There are lots of things that :class:`ConfigLoader` does not do. It does
302 302 not implement complex logic for finding config files. It does not handle
303 303 default values or merge multiple configs. These things need to be
304 304 handled elsewhere.
305 305 """
306 306
307 307 def _log_default(self):
308 308 from IPython.config.application import Application
309 309 return Application.instance().log
310 310
311 311 def __init__(self, log=None):
312 312 """A base class for config loaders.
313 313
314 314 log : instance of :class:`logging.Logger` to use.
315 315 By default loger of :meth:`IPython.config.application.Application.instance()`
316 316 will be used
317 317
318 318 Examples
319 319 --------
320 320
321 321 >>> cl = ConfigLoader()
322 322 >>> config = cl.load_config()
323 323 >>> config
324 324 {}
325 325 """
326 326 self.clear()
327 if log is None :
327 if log is None:
328 328 self.log = self._log_default()
329 329 self.log.debug('Using default logger')
330 else :
330 else:
331 331 self.log = log
332 332
333 333 def clear(self):
334 334 self.config = Config()
335 335
336 336 def load_config(self):
337 337 """Load a config from somewhere, return a :class:`Config` instance.
338 338
339 339 Usually, this will cause self.config to be set and then returned.
340 340 However, in most cases, :meth:`ConfigLoader.clear` should be called
341 341 to erase any previous state.
342 342 """
343 343 self.clear()
344 344 return self.config
345 345
346 346
347 347 class FileConfigLoader(ConfigLoader):
348 348 """A base class for file based configurations.
349 349
350 350 As we add more file based config loaders, the common logic should go
351 351 here.
352 352 """
353 353
354 354 def __init__(self, filename, path=None, **kw):
355 355 """Build a config loader for a filename and path.
356 356
357 357 Parameters
358 358 ----------
359 359 filename : str
360 360 The file name of the config file.
361 361 path : str, list, tuple
362 362 The path to search for the config file on, or a sequence of
363 363 paths to try in order.
364 364 """
365 365 super(FileConfigLoader, self).__init__(**kw)
366 366 self.filename = filename
367 367 self.path = path
368 368 self.full_filename = ''
369 369
370 370 def _find_file(self):
371 371 """Try to find the file by searching the paths."""
372 372 self.full_filename = filefind(self.filename, self.path)
373 373
374 374 class JSONFileConfigLoader(FileConfigLoader):
375 375 """A Json file loader for config"""
376 376
377 377 def load_config(self):
378 """Load the config from a file and return it as a Struct."""
378 """Load the config from a file and return it as a Config object."""
379 379 self.clear()
380 380 try:
381 381 self._find_file()
382 382 except IOError as e:
383 383 raise ConfigFileNotFound(str(e))
384 384 dct = self._read_file_as_dict()
385 385 self.config = self._convert_to_config(dct)
386 386 return self.config
387 387
388 388 def _read_file_as_dict(self):
389 with open(self.full_filename) as f :
389 with open(self.full_filename) as f:
390 390 return json.load(f)
391 391
392 392 def _convert_to_config(self, dictionary):
393 393 if 'version' in dictionary:
394 394 version = dictionary.pop('version')
395 else :
395 else:
396 396 version = 1
397 self.log.warn("Unrecognized JSON config file version, assuming version : {}".format(version))
397 self.log.warn("Unrecognized JSON config file version, assuming version {}".format(version))
398 398
399 399 if version == 1:
400 400 return Config(dictionary)
401 else :
402 raise ValueError('Unknown version of JSON config file : version number {version}'.format(version=version))
401 else:
402 raise ValueError('Unknown version of JSON config file: {version}'.format(version=version))
403 403
404 404
405 405 class PyFileConfigLoader(FileConfigLoader):
406 406 """A config loader for pure python files.
407 407
408 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.
409 path, then executing it to construct a Config object.
411 410 """
412 411
413 412 def load_config(self):
414 """Load the config from a file and return it as a Struct."""
413 """Load the config from a file and return it as a Config object."""
415 414 self.clear()
416 415 try:
417 416 self._find_file()
418 417 except IOError as e:
419 418 raise ConfigFileNotFound(str(e))
420 419 self._read_file_as_dict()
421 420 return self.config
422 421
423 422
424 423 def _read_file_as_dict(self):
425 424 """Load the config file into self.config, with recursive loading."""
426 425 # This closure is made available in the namespace that is used
427 426 # to exec the config file. It allows users to call
428 427 # load_subconfig('myconfig.py') to load config files recursively.
429 428 # It needs to be a closure because it has references to self.path
430 429 # and self.config. The sub-config is loaded with the same path
431 430 # as the parent, but it uses an empty config which is then merged
432 431 # with the parents.
433 432
434 433 # If a profile is specified, the config file will be loaded
435 434 # from that profile
436 435
437 436 def load_subconfig(fname, profile=None):
438 437 # import here to prevent circular imports
439 438 from IPython.core.profiledir import ProfileDir, ProfileDirError
440 439 if profile is not None:
441 440 try:
442 441 profile_dir = ProfileDir.find_profile_dir_by_name(
443 442 get_ipython_dir(),
444 443 profile,
445 444 )
446 445 except ProfileDirError:
447 446 return
448 447 path = profile_dir.location
449 448 else:
450 449 path = self.path
451 450 loader = PyFileConfigLoader(fname, path)
452 451 try:
453 452 sub_config = loader.load_config()
454 453 except ConfigFileNotFound:
455 454 # Pass silently if the sub config is not there. This happens
456 455 # when a user s using a profile, but not the default config.
457 456 pass
458 457 else:
459 458 self.config.merge(sub_config)
460 459
461 460 # Again, this needs to be a closure and should be used in config
462 461 # files to get the config being loaded.
463 462 def get_config():
464 463 return self.config
465 464
466 465 namespace = dict(
467 466 load_subconfig=load_subconfig,
468 467 get_config=get_config,
469 468 __file__=self.full_filename,
470 469 )
471 470 fs_encoding = sys.getfilesystemencoding() or 'ascii'
472 471 conf_filename = self.full_filename.encode(fs_encoding)
473 472 py3compat.execfile(conf_filename, namespace)
474 473
475 474
476 475 class CommandLineConfigLoader(ConfigLoader):
477 476 """A config loader for command line arguments.
478 477
479 478 As we add more command line based loaders, the common logic should go
480 479 here.
481 480 """
482 481
483 482 def _exec_config_str(self, lhs, rhs):
484 483 """execute self.config.<lhs> = <rhs>
485 484
486 485 * expands ~ with expanduser
487 486 * tries to assign with raw eval, otherwise assigns with just the string,
488 487 allowing `--C.a=foobar` and `--C.a="foobar"` to be equivalent. *Not*
489 488 equivalent are `--C.a=4` and `--C.a='4'`.
490 489 """
491 490 rhs = os.path.expanduser(rhs)
492 491 try:
493 492 # Try to see if regular Python syntax will work. This
494 493 # won't handle strings as the quote marks are removed
495 494 # by the system shell.
496 495 value = eval(rhs)
497 496 except (NameError, SyntaxError):
498 497 # This case happens if the rhs is a string.
499 498 value = rhs
500 499
501 500 exec(u'self.config.%s = value' % lhs)
502 501
503 502 def _load_flag(self, cfg):
504 503 """update self.config from a flag, which can be a dict or Config"""
505 504 if isinstance(cfg, (dict, Config)):
506 505 # don't clobber whole config sections, update
507 506 # each section from config:
508 507 for sec,c in iteritems(cfg):
509 508 self.config[sec].update(c)
510 509 else:
511 510 raise TypeError("Invalid flag: %r" % cfg)
512 511
513 512 # raw --identifier=value pattern
514 513 # but *also* accept '-' as wordsep, for aliases
515 514 # accepts: --foo=a
516 515 # --Class.trait=value
517 516 # --alias-name=value
518 517 # rejects: -foo=value
519 518 # --foo
520 519 # --Class.trait
521 520 kv_pattern = re.compile(r'\-\-[A-Za-z][\w\-]*(\.[\w\-]+)*\=.*')
522 521
523 522 # just flags, no assignments, with two *or one* leading '-'
524 523 # accepts: --foo
525 524 # -foo-bar-again
526 525 # rejects: --anything=anything
527 526 # --two.word
528 527
529 528 flag_pattern = re.compile(r'\-\-?\w+[\-\w]*$')
530 529
531 530 class KeyValueConfigLoader(CommandLineConfigLoader):
532 531 """A config loader that loads key value pairs from the command line.
533 532
534 533 This allows command line options to be gives in the following form::
535 534
536 535 ipython --profile="foo" --InteractiveShell.autocall=False
537 536 """
538 537
539 538 def __init__(self, argv=None, aliases=None, flags=None, **kw):
540 539 """Create a key value pair config loader.
541 540
542 541 Parameters
543 542 ----------
544 543 argv : list
545 544 A list that has the form of sys.argv[1:] which has unicode
546 545 elements of the form u"key=value". If this is None (default),
547 546 then sys.argv[1:] will be used.
548 547 aliases : dict
549 548 A dict of aliases for configurable traits.
550 549 Keys are the short aliases, Values are the resolved trait.
551 550 Of the form: `{'alias' : 'Configurable.trait'}`
552 551 flags : dict
553 552 A dict of flags, keyed by str name. Vaues can be Config objects,
554 553 dicts, or "key=value" strings. If Config or dict, when the flag
555 554 is triggered, The flag is loaded as `self.config.update(m)`.
556 555
557 556 Returns
558 557 -------
559 558 config : Config
560 559 The resulting Config object.
561 560
562 561 Examples
563 562 --------
564 563
565 564 >>> from IPython.config.loader import KeyValueConfigLoader
566 565 >>> cl = KeyValueConfigLoader()
567 566 >>> d = cl.load_config(["--A.name='brian'","--B.number=0"])
568 567 >>> sorted(d.items())
569 568 [('A', {'name': 'brian'}), ('B', {'number': 0})]
570 569 """
571 570 super(KeyValueConfigLoader, self).__init__(**kw)
572 571 if argv is None:
573 572 argv = sys.argv[1:]
574 573 self.argv = argv
575 574 self.aliases = aliases or {}
576 575 self.flags = flags or {}
577 576
578 577
579 578 def clear(self):
580 579 super(KeyValueConfigLoader, self).clear()
581 580 self.extra_args = []
582 581
583 582
584 583 def _decode_argv(self, argv, enc=None):
585 584 """decode argv if bytes, using stin.encoding, falling back on default enc"""
586 585 uargv = []
587 586 if enc is None:
588 587 enc = DEFAULT_ENCODING
589 588 for arg in argv:
590 589 if not isinstance(arg, unicode_type):
591 590 # only decode if not already decoded
592 591 arg = arg.decode(enc)
593 592 uargv.append(arg)
594 593 return uargv
595 594
596 595
597 596 def load_config(self, argv=None, aliases=None, flags=None):
598 597 """Parse the configuration and generate the Config object.
599 598
600 599 After loading, any arguments that are not key-value or
601 600 flags will be stored in self.extra_args - a list of
602 601 unparsed command-line arguments. This is used for
603 602 arguments such as input files or subcommands.
604 603
605 604 Parameters
606 605 ----------
607 606 argv : list, optional
608 607 A list that has the form of sys.argv[1:] which has unicode
609 608 elements of the form u"key=value". If this is None (default),
610 609 then self.argv will be used.
611 610 aliases : dict
612 611 A dict of aliases for configurable traits.
613 612 Keys are the short aliases, Values are the resolved trait.
614 613 Of the form: `{'alias' : 'Configurable.trait'}`
615 614 flags : dict
616 615 A dict of flags, keyed by str name. Values can be Config objects
617 616 or dicts. When the flag is triggered, The config is loaded as
618 617 `self.config.update(cfg)`.
619 618 """
620 619 self.clear()
621 620 if argv is None:
622 621 argv = self.argv
623 622 if aliases is None:
624 623 aliases = self.aliases
625 624 if flags is None:
626 625 flags = self.flags
627 626
628 627 # ensure argv is a list of unicode strings:
629 628 uargv = self._decode_argv(argv)
630 629 for idx,raw in enumerate(uargv):
631 630 # strip leading '-'
632 631 item = raw.lstrip('-')
633 632
634 633 if raw == '--':
635 634 # don't parse arguments after '--'
636 635 # this is useful for relaying arguments to scripts, e.g.
637 636 # ipython -i foo.py --matplotlib=qt -- args after '--' go-to-foo.py
638 637 self.extra_args.extend(uargv[idx+1:])
639 638 break
640 639
641 640 if kv_pattern.match(raw):
642 641 lhs,rhs = item.split('=',1)
643 642 # Substitute longnames for aliases.
644 643 if lhs in aliases:
645 644 lhs = aliases[lhs]
646 645 if '.' not in lhs:
647 646 # probably a mistyped alias, but not technically illegal
648 self.log.warn("Unrecognized alias: '%s', it will probably have no effect. %s,-- %s"%(lhs,raw, aliases))
647 self.log.warn("Unrecognized alias: '%s', it will probably have no effect.", raw)
649 648 try:
650 649 self._exec_config_str(lhs, rhs)
651 650 except Exception:
652 651 raise ArgumentError("Invalid argument: '%s'" % raw)
653 652
654 653 elif flag_pattern.match(raw):
655 654 if item in flags:
656 655 cfg,help = flags[item]
657 656 self._load_flag(cfg)
658 657 else:
659 658 raise ArgumentError("Unrecognized flag: '%s'"%raw)
660 659 elif raw.startswith('-'):
661 660 kv = '--'+item
662 661 if kv_pattern.match(kv):
663 662 raise ArgumentError("Invalid argument: '%s', did you mean '%s'?"%(raw, kv))
664 663 else:
665 664 raise ArgumentError("Invalid argument: '%s'"%raw)
666 665 else:
667 666 # keep all args that aren't valid in a list,
668 667 # in case our parent knows what to do with them.
669 668 self.extra_args.append(item)
670 669 return self.config
671 670
672 671 class ArgParseConfigLoader(CommandLineConfigLoader):
673 672 """A loader that uses the argparse module to load from the command line."""
674 673
675 674 def __init__(self, argv=None, aliases=None, flags=None, log=None, *parser_args, **parser_kw):
676 675 """Create a config loader for use with argparse.
677 676
678 677 Parameters
679 678 ----------
680 679
681 680 argv : optional, list
682 681 If given, used to read command-line arguments from, otherwise
683 682 sys.argv[1:] is used.
684 683
685 684 parser_args : tuple
686 685 A tuple of positional arguments that will be passed to the
687 686 constructor of :class:`argparse.ArgumentParser`.
688 687
689 688 parser_kw : dict
690 689 A tuple of keyword arguments that will be passed to the
691 690 constructor of :class:`argparse.ArgumentParser`.
692 691
693 692 Returns
694 693 -------
695 694 config : Config
696 695 The resulting Config object.
697 696 """
698 697 super(CommandLineConfigLoader, self).__init__(log=log)
699 698 self.clear()
700 699 if argv is None:
701 700 argv = sys.argv[1:]
702 701 self.argv = argv
703 702 self.aliases = aliases or {}
704 703 self.flags = flags or {}
705 704
706 705 self.parser_args = parser_args
707 706 self.version = parser_kw.pop("version", None)
708 707 kwargs = dict(argument_default=argparse.SUPPRESS)
709 708 kwargs.update(parser_kw)
710 709 self.parser_kw = kwargs
711 710
712 711 def load_config(self, argv=None, aliases=None, flags=None):
713 712 """Parse command line arguments and return as a Config object.
714 713
715 714 Parameters
716 715 ----------
717 716
718 717 args : optional, list
719 718 If given, a list with the structure of sys.argv[1:] to parse
720 719 arguments from. If not given, the instance's self.argv attribute
721 720 (given at construction time) is used."""
722 721 self.clear()
723 722 if argv is None:
724 723 argv = self.argv
725 724 if aliases is None:
726 725 aliases = self.aliases
727 726 if flags is None:
728 727 flags = self.flags
729 728 self._create_parser(aliases, flags)
730 729 self._parse_args(argv)
731 730 self._convert_to_config()
732 731 return self.config
733 732
734 733 def get_extra_args(self):
735 734 if hasattr(self, 'extra_args'):
736 735 return self.extra_args
737 736 else:
738 737 return []
739 738
740 739 def _create_parser(self, aliases=None, flags=None):
741 740 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
742 741 self._add_arguments(aliases, flags)
743 742
744 743 def _add_arguments(self, aliases=None, flags=None):
745 744 raise NotImplementedError("subclasses must implement _add_arguments")
746 745
747 746 def _parse_args(self, args):
748 747 """self.parser->self.parsed_data"""
749 748 # decode sys.argv to support unicode command-line options
750 749 enc = DEFAULT_ENCODING
751 750 uargs = [py3compat.cast_unicode(a, enc) for a in args]
752 751 self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
753 752
754 753 def _convert_to_config(self):
755 754 """self.parsed_data->self.config"""
756 755 for k, v in iteritems(vars(self.parsed_data)):
757 756 exec("self.config.%s = v"%k, locals(), globals())
758 757
759 758 class KVArgParseConfigLoader(ArgParseConfigLoader):
760 759 """A config loader that loads aliases and flags with argparse,
761 760 but will use KVLoader for the rest. This allows better parsing
762 761 of common args, such as `ipython -c 'print 5'`, but still gets
763 762 arbitrary config with `ipython --InteractiveShell.use_readline=False`"""
764 763
765 764 def _add_arguments(self, aliases=None, flags=None):
766 765 self.alias_flags = {}
767 766 # print aliases, flags
768 767 if aliases is None:
769 768 aliases = self.aliases
770 769 if flags is None:
771 770 flags = self.flags
772 771 paa = self.parser.add_argument
773 772 for key,value in iteritems(aliases):
774 773 if key in flags:
775 774 # flags
776 775 nargs = '?'
777 776 else:
778 777 nargs = None
779 778 if len(key) is 1:
780 779 paa('-'+key, '--'+key, type=unicode_type, dest=value, nargs=nargs)
781 780 else:
782 781 paa('--'+key, type=unicode_type, dest=value, nargs=nargs)
783 782 for key, (value, help) in iteritems(flags):
784 783 if key in self.aliases:
785 784 #
786 785 self.alias_flags[self.aliases[key]] = value
787 786 continue
788 787 if len(key) is 1:
789 788 paa('-'+key, '--'+key, action='append_const', dest='_flags', const=value)
790 789 else:
791 790 paa('--'+key, action='append_const', dest='_flags', const=value)
792 791
793 792 def _convert_to_config(self):
794 793 """self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
795 794 # remove subconfigs list from namespace before transforming the Namespace
796 795 if '_flags' in self.parsed_data:
797 796 subcs = self.parsed_data._flags
798 797 del self.parsed_data._flags
799 798 else:
800 799 subcs = []
801 800
802 801 for k, v in iteritems(vars(self.parsed_data)):
803 802 if v is None:
804 803 # it was a flag that shares the name of an alias
805 804 subcs.append(self.alias_flags[k])
806 805 else:
807 806 # eval the KV assignment
808 807 self._exec_config_str(k, v)
809 808
810 809 for subc in subcs:
811 810 self._load_flag(subc)
812 811
813 812 if self.extra_args:
814 813 sub_parser = KeyValueConfigLoader(log=self.log)
815 814 sub_parser.load_config(self.extra_args)
816 815 self.config.merge(sub_parser.config)
817 816 self.extra_args = sub_parser.extra_args
818 817
819 818
820 819 def load_pyconfig_files(config_files, path):
821 820 """Load multiple Python config files, merging each of them in turn.
822 821
823 822 Parameters
824 823 ==========
825 824 config_files : list of str
826 825 List of config files names to load and merge into the config.
827 826 path : unicode
828 827 The full path to the location of the config files.
829 828 """
830 829 config = Config()
831 830 for cf in config_files:
832 831 loader = PyFileConfigLoader(cf, path=path)
833 832 try:
834 833 next_config = loader.load_config()
835 834 except ConfigFileNotFound:
836 835 pass
837 836 except:
838 837 raise
839 838 else:
840 839 config.merge(next_config)
841 840 return config
@@ -1,566 +1,566 b''
1 1 .. _config_overview:
2 2
3 3 ============================================
4 4 Overview of the IPython configuration system
5 5 ============================================
6 6
7 7 This section describes the IPython configuration system.
8 8
9 9 The main concepts
10 10 =================
11 11
12 12 There are a number of abstractions that the IPython configuration system uses.
13 13 Each of these abstractions is represented by a Python class.
14 14
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 18 support dotted attribute style access (``cfg.Foo.bar``) in addition to the
19 19 regular dictionary style access (``cfg['Foo']['bar']``).
20 20 The Config object is a wrapper around a simple dictionary with some convenience methods,
21 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
25 25 application is the :command:`ipython` command line program. Each
26 26 application reads *one or more* configuration files and a single set of
27 27 command line options
28 28 and then produces a master configuration object for the application. This
29 29 configuration object is then passed to the configurable objects that the
30 30 application creates. These configurable objects implement the actual logic
31 31 of the application and know how to configure themselves given the
32 32 configuration object.
33 33
34 34 Applications always have a `log` attribute that is a configured Logger.
35 35 This allows centralized logging configuration per-application.
36 36
37 37 Configurable: :class:`~IPython.config.configurable.Configurable`
38 38 A configurable is a regular Python class that serves as a base class for
39 39 all main classes in an application. The
40 40 :class:`~IPython.config.configurable.Configurable` base class is
41 41 lightweight and only does one things.
42 42
43 43 This :class:`~IPython.config.configurable.Configurable` is a subclass
44 44 of :class:`~IPython.utils.traitlets.HasTraits` that knows how to configure
45 45 itself. Class level traits with the metadata ``config=True`` become
46 46 values that can be configured from the command line and configuration
47 47 files.
48 48
49 49 Developers create :class:`~IPython.config.configurable.Configurable`
50 50 subclasses that implement all of the logic in the application. Each of
51 51 these subclasses has its own configuration information that controls how
52 52 instances are created.
53 53
54 54 Singletons: :class:`~IPython.config.configurable.SingletonConfigurable`
55 55 Any object for which there is a single canonical instance. These are
56 56 just like Configurables, except they have a class method
57 57 :meth:`~IPython.config.configurable.SingletonConfigurable.instance`,
58 58 that returns the current active instance (or creates one if it
59 59 does not exist). Examples of singletons include
60 60 :class:`~IPython.config.application.Application`s and
61 61 :class:`~IPython.core.interactiveshell.InteractiveShell`. This lets
62 62 objects easily connect to the current running Application without passing
63 63 objects around everywhere. For instance, to get the current running
64 64 Application instance, simply do: ``app = Application.instance()``.
65 65
66 66
67 67 .. note::
68 68
69 69 Singletons are not strictly enforced - you can have many instances
70 70 of a given singleton class, but the :meth:`instance` method will always
71 71 return the same one.
72 72
73 73 Having described these main concepts, we can now state the main idea in our
74 74 configuration system: *"configuration" allows the default values of class
75 75 attributes to be controlled on a class by class basis*. Thus all instances of
76 76 a given class are configured in the same way. Furthermore, if two instances
77 77 need to be configured differently, they need to be instances of two different
78 78 classes. While this model may seem a bit restrictive, we have found that it
79 79 expresses most things that need to be configured extremely well. However, it
80 80 is possible to create two instances of the same class that have different
81 81 trait values. This is done by overriding the configuration.
82 82
83 83 Now, we show what our configuration objects and files look like.
84 84
85 85 Configuration objects and files
86 86 ===============================
87 87
88 88 A configuration object is little more than a wrapper around a dictionary.
89 89 A configuration *file* is simply a mechanism for producing that object.
90 90 The main IPython configuration file is a plain Python script,
91 91 which can perform extensive logic to populate the config object.
92 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.
93 which is just a direct JSON serialization of the config dictionary,
94 which is easily processed by external software.
95 95
96 96 When both Python and JSON configuration file are present, both will be loaded,
97 97 with JSON configuration having higher priority.
98 98
99 99 Python configuration Files
100 100 ~~~~~~~~~~~~~~~~~~~~~~~~~~
101 101
102 102 A Python configuration file is a pure Python file that populates a configuration object.
103 103 This configuration object is a :class:`~IPython.config.loader.Config` instance.
104 104 While in a configuration file, to get a reference to this object, simply call the :func:`get_config`
105 105 function, which is available in the global namespace of the script.
106 106
107 107 Here is an example of a super simple configuration file that does nothing::
108 108
109 109 c = get_config()
110 110
111 111 Once you get a reference to the configuration object, you simply set
112 112 attributes on it. All you have to know is:
113 113
114 114 * The name of the class to configure.
115 115 * The name of the attribute.
116 116 * The type of each attribute.
117 117
118 118 The answers to these questions are provided by the various
119 119 :class:`~IPython.config.configurable.Configurable` subclasses that an
120 120 application uses. Let's look at how this would work for a simple configurable
121 121 subclass::
122 122
123 123 # Sample configurable:
124 124 from IPython.config.configurable import Configurable
125 125 from IPython.utils.traitlets import Int, Float, Unicode, Bool
126 126
127 127 class MyClass(Configurable):
128 128 name = Unicode(u'defaultname', config=True)
129 129 ranking = Int(0, config=True)
130 130 value = Float(99.0)
131 131 # The rest of the class implementation would go here..
132 132
133 133 In this example, we see that :class:`MyClass` has three attributes, two
134 of (``name``, ``ranking``) can be configured. All of the attributes
134 of which (``name``, ``ranking``) can be configured. All of the attributes
135 135 are given types and default values. If a :class:`MyClass` is instantiated,
136 136 but not configured, these default values will be used. But let's see how
137 137 to configure this class in a configuration file::
138 138
139 139 # Sample config file
140 140 c = get_config()
141 141
142 142 c.MyClass.name = 'coolname'
143 143 c.MyClass.ranking = 10
144 144
145 145 After this configuration file is loaded, the values set in it will override
146 146 the class defaults anytime a :class:`MyClass` is created. Furthermore,
147 147 these attributes will be type checked and validated anytime they are set.
148 148 This type checking is handled by the :mod:`IPython.utils.traitlets` module,
149 149 which provides the :class:`Unicode`, :class:`Int` and :class:`Float` types.
150 150 In addition to these traitlets, the :mod:`IPython.utils.traitlets` provides
151 151 traitlets for a number of other types.
152 152
153 153 .. note::
154 154
155 155 Underneath the hood, the :class:`Configurable` base class is a subclass of
156 156 :class:`IPython.utils.traitlets.HasTraits`. The
157 157 :mod:`IPython.utils.traitlets` module is a lightweight version of
158 158 :mod:`enthought.traits`. Our implementation is a pure Python subset
159 159 (mostly API compatible) of :mod:`enthought.traits` that does not have any
160 160 of the automatic GUI generation capabilities. Our plan is to achieve 100%
161 161 API compatibility to enable the actual :mod:`enthought.traits` to
162 162 eventually be used instead. Currently, we cannot use
163 163 :mod:`enthought.traits` as we are committed to the core of IPython being
164 164 pure Python.
165 165
166 166 It should be very clear at this point what the naming convention is for
167 167 configuration attributes::
168 168
169 169 c.ClassName.attribute_name = attribute_value
170 170
171 171 Here, ``ClassName`` is the name of the class whose configuration attribute you
172 172 want to set, ``attribute_name`` is the name of the attribute you want to set
173 173 and ``attribute_value`` the the value you want it to have. The ``ClassName``
174 174 attribute of ``c`` is not the actual class, but instead is another
175 175 :class:`~IPython.config.loader.Config` instance.
176 176
177 177 .. note::
178 178
179 179 The careful reader may wonder how the ``ClassName`` (``MyClass`` in
180 180 the above example) attribute of the configuration object ``c`` gets
181 181 created. These attributes are created on the fly by the
182 182 :class:`~IPython.config.loader.Config` instance, using a simple naming
183 183 convention. Any attribute of a :class:`~IPython.config.loader.Config`
184 184 instance whose name begins with an uppercase character is assumed to be a
185 185 sub-configuration and a new empty :class:`~IPython.config.loader.Config`
186 186 instance is dynamically created for that attribute. This allows deeply
187 187 hierarchical information created easily (``c.Foo.Bar.value``) on the fly.
188 188
189 189 JSON configuration Files
190 190 ~~~~~~~~~~~~~~~~~~~~~~~~
191 191
192 A JSON configuration file is simply a file that contain a
192 A JSON configuration file is simply a file that contains a
193 193 :class:`~IPython.config.loader.Config` dictionary serialized to JSON.
194 194 A JSON configuration file has the same base name as a Python configuration file,
195 just with a .json extension.
195 but with a .json extension.
196 196
197 Configuration described in previous section could be written as follow in a
197 Configuration described in previous section could be written as follows in a
198 198 JSON configuration file:
199 199
200 200 .. sourcecode:: json
201 201
202 202 {
203 203 "version": "1.0",
204 204 "MyClass": {
205 205 "name": "coolname",
206 206 "ranking": 10
207 207 }
208 208 }
209 209
210 210 JSON configuration files can be more easily generated or processed by programs
211 211 or other languages.
212 212
213 213
214 214 Configuration files inheritance
215 215 ===============================
216 216
217 217 .. note::
218 218
219 219 This section only apply to Python configuration files.
220 220
221 221 Let's say you want to have different configuration files for various purposes.
222 222 Our configuration system makes it easy for one configuration file to inherit
223 223 the information in another configuration file. The :func:`load_subconfig`
224 224 command can be used in a configuration file for this purpose. Here is a simple
225 225 example that loads all of the values from the file :file:`base_config.py`::
226 226
227 227 # base_config.py
228 228 c = get_config()
229 229 c.MyClass.name = 'coolname'
230 230 c.MyClass.ranking = 100
231 231
232 232 into the configuration file :file:`main_config.py`::
233 233
234 234 # main_config.py
235 235 c = get_config()
236 236
237 237 # Load everything from base_config.py
238 238 load_subconfig('base_config.py')
239 239
240 240 # Now override one of the values
241 241 c.MyClass.name = 'bettername'
242 242
243 243 In a situation like this the :func:`load_subconfig` makes sure that the
244 244 search path for sub-configuration files is inherited from that of the parent.
245 245 Thus, you can typically put the two in the same directory and everything will
246 246 just work.
247 247
248 248 You can also load configuration files by profile, for instance:
249 249
250 250 .. sourcecode:: python
251 251
252 252 load_subconfig('ipython_config.py', profile='default')
253 253
254 254 to inherit your default configuration as a starting point.
255 255
256 256
257 257 Class based configuration inheritance
258 258 =====================================
259 259
260 260 There is another aspect of configuration where inheritance comes into play.
261 261 Sometimes, your classes will have an inheritance hierarchy that you want
262 262 to be reflected in the configuration system. Here is a simple example::
263 263
264 264 from IPython.config.configurable import Configurable
265 265 from IPython.utils.traitlets import Int, Float, Unicode, Bool
266 266
267 267 class Foo(Configurable):
268 268 name = Unicode(u'fooname', config=True)
269 269 value = Float(100.0, config=True)
270 270
271 271 class Bar(Foo):
272 272 name = Unicode(u'barname', config=True)
273 273 othervalue = Int(0, config=True)
274 274
275 275 Now, we can create a configuration file to configure instances of :class:`Foo`
276 276 and :class:`Bar`::
277 277
278 278 # config file
279 279 c = get_config()
280 280
281 281 c.Foo.name = u'bestname'
282 282 c.Bar.othervalue = 10
283 283
284 284 This class hierarchy and configuration file accomplishes the following:
285 285
286 286 * The default value for :attr:`Foo.name` and :attr:`Bar.name` will be
287 287 'bestname'. Because :class:`Bar` is a :class:`Foo` subclass it also
288 288 picks up the configuration information for :class:`Foo`.
289 289 * The default value for :attr:`Foo.value` and :attr:`Bar.value` will be
290 290 ``100.0``, which is the value specified as the class default.
291 291 * The default value for :attr:`Bar.othervalue` will be 10 as set in the
292 292 configuration file. Because :class:`Foo` is the parent of :class:`Bar`
293 293 it doesn't know anything about the :attr:`othervalue` attribute.
294 294
295 295
296 296 .. _ipython_dir:
297 297
298 298 Configuration file location
299 299 ===========================
300 300
301 301 So where should you put your configuration files? IPython uses "profiles" for
302 302 configuration, and by default, all profiles will be stored in the so called
303 303 "IPython directory". The location of this directory is determined by the
304 304 following algorithm:
305 305
306 306 * If the ``ipython-dir`` command line flag is given, its value is used.
307 307
308 308 * If not, the value returned by :func:`IPython.utils.path.get_ipython_dir`
309 309 is used. This function will first look at the :envvar:`IPYTHONDIR`
310 310 environment variable and then default to :file:`~/.ipython`.
311 311 Historical support for the :envvar:`IPYTHON_DIR` environment variable will
312 312 be removed in a future release.
313 313
314 314 For most users, the configuration directory will be :file:`~/.ipython`.
315 315
316 316 Previous versions of IPython on Linux would use the XDG config directory,
317 317 creating :file:`~/.config/ipython` by default. We have decided to go
318 318 back to :file:`~/.ipython` for consistency among systems. IPython will
319 319 issue a warning if it finds the XDG location, and will move it to the new
320 320 location if there isn't already a directory there.
321 321
322 322 Once the location of the IPython directory has been determined, you need to know
323 323 which profile you are using. For users with a single configuration, this will
324 324 simply be 'default', and will be located in
325 325 :file:`<IPYTHONDIR>/profile_default`.
326 326
327 327 The next thing you need to know is what to call your configuration file. The
328 328 basic idea is that each application has its own default configuration filename.
329 329 The default named used by the :command:`ipython` command line program is
330 330 :file:`ipython_config.py`, and *all* IPython applications will use this file.
331 331 Other applications, such as the parallel :command:`ipcluster` scripts or the
332 332 QtConsole will load their own config files *after* :file:`ipython_config.py`. To
333 333 load a particular configuration file instead of the default, the name can be
334 334 overridden by the ``config_file`` command line flag.
335 335
336 336 To generate the default configuration files, do::
337 337
338 338 $ ipython profile create
339 339
340 340 and you will have a default :file:`ipython_config.py` in your IPython directory
341 341 under :file:`profile_default`. If you want the default config files for the
342 342 :mod:`IPython.parallel` applications, add ``--parallel`` to the end of the
343 343 command-line args.
344 344
345 345
346 346 Locating these files
347 347 --------------------
348 348
349 349 From the command-line, you can quickly locate the IPYTHONDIR or a specific
350 350 profile with:
351 351
352 352 .. sourcecode:: bash
353 353
354 354 $ ipython locate
355 355 /home/you/.ipython
356 356
357 357 $ ipython locate profile foo
358 358 /home/you/.ipython/profile_foo
359 359
360 360 These map to the utility functions: :func:`IPython.utils.path.get_ipython_dir`
361 361 and :func:`IPython.utils.path.locate_profile` respectively.
362 362
363 363
364 364 .. _profiles_dev:
365 365
366 366 Profiles
367 367 ========
368 368
369 369 A profile is a directory containing configuration and runtime files, such as
370 370 logs, connection info for the parallel apps, and your IPython command history.
371 371
372 372 The idea is that users often want to maintain a set of configuration files for
373 373 different purposes: one for doing numerical computing with NumPy and SciPy and
374 374 another for doing symbolic computing with SymPy. Profiles make it easy to keep a
375 375 separate configuration files, logs, and histories for each of these purposes.
376 376
377 377 Let's start by showing how a profile is used:
378 378
379 379 .. code-block:: bash
380 380
381 381 $ ipython --profile=sympy
382 382
383 383 This tells the :command:`ipython` command line program to get its configuration
384 384 from the "sympy" profile. The file names for various profiles do not change. The
385 385 only difference is that profiles are named in a special way. In the case above,
386 386 the "sympy" profile means looking for :file:`ipython_config.py` in :file:`<IPYTHONDIR>/profile_sympy`.
387 387
388 388 The general pattern is this: simply create a new profile with:
389 389
390 390 .. code-block:: bash
391 391
392 392 $ ipython profile create <name>
393 393
394 394 which adds a directory called ``profile_<name>`` to your IPython directory. Then
395 395 you can load this profile by adding ``--profile=<name>`` to your command line
396 396 options. Profiles are supported by all IPython applications.
397 397
398 398 IPython ships with some sample profiles in :file:`IPython/config/profile`. If
399 399 you create profiles with the name of one of our shipped profiles, these config
400 400 files will be copied over instead of starting with the automatically generated
401 401 config files.
402 402
403 403 Security Files
404 404 --------------
405 405
406 406 If you are using the notebook, qtconsole, or parallel code, IPython stores
407 407 connection information in small JSON files in the active profile's security
408 408 directory. This directory is made private, so only you can see the files inside. If
409 409 you need to move connection files around to other computers, this is where they will
410 410 be. If you want your code to be able to open security files by name, we have a
411 411 convenience function :func:`IPython.utils.path.get_security_file`, which will return
412 412 the absolute path to a security file from its filename and [optionally] profile
413 413 name.
414 414
415 415 .. _startup_files:
416 416
417 417 Startup Files
418 418 -------------
419 419
420 420 If you want some code to be run at the beginning of every IPython session with
421 421 a particular profile, the easiest way is to add Python (``.py``) or
422 422 IPython (``.ipy``) scripts to your :file:`<profile>/startup` directory. Files
423 423 in this directory will always be executed as soon as the IPython shell is
424 424 constructed, and before any other code or scripts you have specified. If you
425 425 have multiple files in the startup directory, they will be run in
426 426 lexicographical order, so you can control the ordering by adding a '00-'
427 427 prefix.
428 428
429 429
430 430 .. _commandline:
431 431
432 432 Command-line arguments
433 433 ======================
434 434
435 435 IPython exposes *all* configurable options on the command-line. The command-line
436 436 arguments are generated from the Configurable traits of the classes associated
437 437 with a given Application. Configuring IPython from the command-line may look
438 438 very similar to an IPython config file
439 439
440 440 IPython applications use a parser called
441 441 :class:`~IPython.config.loader.KeyValueLoader` to load values into a Config
442 442 object. Values are assigned in much the same way as in a config file:
443 443
444 444 .. code-block:: bash
445 445
446 446 $ ipython --InteractiveShell.use_readline=False --BaseIPythonApplication.profile='myprofile'
447 447
448 448 Is the same as adding:
449 449
450 450 .. sourcecode:: python
451 451
452 452 c.InteractiveShell.use_readline=False
453 453 c.BaseIPythonApplication.profile='myprofile'
454 454
455 455 to your config file. Key/Value arguments *always* take a value, separated by '='
456 456 and no spaces.
457 457
458 458 Common Arguments
459 459 ----------------
460 460
461 461 Since the strictness and verbosity of the KVLoader above are not ideal for everyday
462 462 use, common arguments can be specified as flags_ or aliases_.
463 463
464 464 Flags and Aliases are handled by :mod:`argparse` instead, allowing for more flexible
465 465 parsing. In general, flags and aliases are prefixed by ``--``, except for those
466 466 that are single characters, in which case they can be specified with a single ``-``, e.g.:
467 467
468 468 .. code-block:: bash
469 469
470 470 $ ipython -i -c "import numpy; x=numpy.linspace(0,1)" --profile testing --colors=lightbg
471 471
472 472 Aliases
473 473 *******
474 474
475 475 For convenience, applications have a mapping of commonly used traits, so you don't have
476 476 to specify the whole class name:
477 477
478 478 .. code-block:: bash
479 479
480 480 $ ipython --profile myprofile
481 481 # and
482 482 $ ipython --profile='myprofile'
483 483 # are equivalent to
484 484 $ ipython --BaseIPythonApplication.profile='myprofile'
485 485
486 486 Flags
487 487 *****
488 488
489 489 Applications can also be passed **flags**. Flags are options that take no
490 490 arguments. They are simply wrappers for
491 491 setting one or more configurables with predefined values, often True/False.
492 492
493 493 For instance:
494 494
495 495 .. code-block:: bash
496 496
497 497 $ ipcontroller --debug
498 498 # is equivalent to
499 499 $ ipcontroller --Application.log_level=DEBUG
500 500 # and
501 501 $ ipython --matploitlib
502 502 # is equivalent to
503 503 $ ipython --matplotlib auto
504 504 # or
505 505 $ ipython --no-banner
506 506 # is equivalent to
507 507 $ ipython --TerminalIPythonApp.display_banner=False
508 508
509 509 Subcommands
510 510 -----------
511 511
512 512
513 513 Some IPython applications have **subcommands**. Subcommands are modeled after
514 514 :command:`git`, and are called with the form :command:`command subcommand
515 515 [...args]`. Currently, the QtConsole is a subcommand of terminal IPython:
516 516
517 517 .. code-block:: bash
518 518
519 519 $ ipython qtconsole --profile myprofile
520 520
521 521 and :command:`ipcluster` is simply a wrapper for its various subcommands (start,
522 522 stop, engines).
523 523
524 524 .. code-block:: bash
525 525
526 526 $ ipcluster start --profile=myprofile -n 4
527 527
528 528
529 529 To see a list of the available aliases, flags, and subcommands for an IPython application, simply pass ``-h`` or ``--help``. And to see the full list of configurable options (*very* long), pass ``--help-all``.
530 530
531 531
532 532 Design requirements
533 533 ===================
534 534
535 535 Here are the main requirements we wanted our configuration system to have:
536 536
537 537 * Support for hierarchical configuration information.
538 538
539 539 * Full integration with command line option parsers. Often, you want to read
540 540 a configuration file, but then override some of the values with command line
541 541 options. Our configuration system automates this process and allows each
542 542 command line option to be linked to a particular attribute in the
543 543 configuration hierarchy that it will override.
544 544
545 545 * Configuration files that are themselves valid Python code. This accomplishes
546 546 many things. First, it becomes possible to put logic in your configuration
547 547 files that sets attributes based on your operating system, network setup,
548 548 Python version, etc. Second, Python has a super simple syntax for accessing
549 549 hierarchical data structures, namely regular attribute access
550 550 (``Foo.Bar.Bam.name``). Third, using Python makes it easy for users to
551 551 import configuration attributes from one configuration file to another.
552 552 Fourth, even though Python is dynamically typed, it does have types that can
553 553 be checked at runtime. Thus, a ``1`` in a config file is the integer '1',
554 554 while a ``'1'`` is a string.
555 555
556 556 * A fully automated method for getting the configuration information to the
557 557 classes that need it at runtime. Writing code that walks a configuration
558 558 hierarchy to extract a particular attribute is painful. When you have
559 559 complex configuration information with hundreds of attributes, this makes
560 560 you want to cry.
561 561
562 562 * Type checking and validation that doesn't require the entire configuration
563 563 hierarchy to be specified statically before runtime. Python is a very
564 564 dynamic language and you don't always know everything that needs to be
565 565 configured when a program starts.
566 566
General Comments 0
You need to be logged in to leave comments. Login now