##// END OF EJS Templates
use argparse to parse aliases & flags
MinRK -
Show More
@@ -27,7 +27,7 b' from copy import deepcopy'
27 27
28 28 from IPython.config.configurable import SingletonConfigurable
29 29 from IPython.config.loader import (
30 KeyValueConfigLoader, PyFileConfigLoader, Config, ArgumentError
30 KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError
31 31 )
32 32
33 33 from IPython.utils.traitlets import (
@@ -350,7 +350,7 b' class Application(SingletonConfigurable):'
350 350 self.print_version()
351 351 self.exit(0)
352 352
353 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
353 loader = KVArgParseConfigLoader(argv=argv, aliases=self.aliases,
354 354 flags=self.flags)
355 355 try:
356 356 config = loader.load_config()
@@ -326,6 +326,31 b' class CommandLineConfigLoader(ConfigLoader):'
326 326 here.
327 327 """
328 328
329 def _exec_config_str(self, lhs, rhs):
330 exec_str = 'self.config.' + lhs + '=' + rhs
331 try:
332 # Try to see if regular Python syntax will work. This
333 # won't handle strings as the quote marks are removed
334 # by the system shell.
335 exec exec_str in locals(), globals()
336 except (NameError, SyntaxError):
337 # This case happens if the rhs is a string but without
338 # the quote marks. Use repr, to get quote marks, and
339 # 'u' prefix and see if
340 # it succeeds. If it still fails, we let it raise.
341 exec_str = u'self.config.' + lhs + '=' + repr(rhs)
342 exec exec_str in locals(), globals()
343
344 def _load_flag(self, cfg):
345 """update self.config from a flag, which can be a dict or Config"""
346 if isinstance(cfg, (dict, Config)):
347 # don't clobber whole config sections, update
348 # each section from config:
349 for sec,c in cfg.iteritems():
350 self.config[sec].update(c)
351 else:
352 raise ValueError("Invalid flag: '%s'"%raw)
353
329 354 # raw --identifier=value pattern
330 355 # but *also* accept '-' as wordsep, for aliases
331 356 # accepts: --foo=a
@@ -463,29 +488,12 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
463 488 if '.' not in lhs:
464 489 # probably a mistyped alias, but not technically illegal
465 490 warn.warn("Unrecognized alias: '%s', it will probably have no effect."%lhs)
466 exec_str = 'self.config.' + lhs + '=' + rhs
467 try:
468 # Try to see if regular Python syntax will work. This
469 # won't handle strings as the quote marks are removed
470 # by the system shell.
471 exec exec_str in locals(), globals()
472 except (NameError, SyntaxError):
473 # This case happens if the rhs is a string but without
474 # the quote marks. Use repr, to get quote marks, and
475 # 'u' prefix and see if
476 # it succeeds. If it still fails, we let it raise.
477 exec_str = u'self.config.' + lhs + '=' + repr(rhs)
478 exec exec_str in locals(), globals()
491 self._exec_config_str(lhs, rhs)
492
479 493 elif flag_pattern.match(raw):
480 494 if item in flags:
481 495 cfg,help = flags[item]
482 if isinstance(cfg, (dict, Config)):
483 # don't clobber whole config sections, update
484 # each section from config:
485 for sec,c in cfg.iteritems():
486 self.config[sec].update(c)
487 else:
488 raise ValueError("Invalid flag: '%s'"%raw)
496 self._load_flag(cfg)
489 497 else:
490 498 raise ArgumentError("Unrecognized flag: '%s'"%raw)
491 499 elif raw.startswith('-'):
@@ -503,7 +511,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
503 511 class ArgParseConfigLoader(CommandLineConfigLoader):
504 512 """A loader that uses the argparse module to load from the command line."""
505 513
506 def __init__(self, argv=None, *parser_args, **parser_kw):
514 def __init__(self, argv=None, aliases=None, flags=None, *parser_args, **parser_kw):
507 515 """Create a config loader for use with argparse.
508 516
509 517 Parameters
@@ -527,16 +535,20 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
527 535 The resulting Config object.
528 536 """
529 537 super(CommandLineConfigLoader, self).__init__()
530 if argv == None:
538 self.clear()
539 if argv is None:
531 540 argv = sys.argv[1:]
532 541 self.argv = argv
542 self.aliases = aliases or {}
543 self.flags = flags or {}
544
533 545 self.parser_args = parser_args
534 546 self.version = parser_kw.pop("version", None)
535 547 kwargs = dict(argument_default=argparse.SUPPRESS)
536 548 kwargs.update(parser_kw)
537 549 self.parser_kw = kwargs
538 550
539 def load_config(self, argv=None):
551 def load_config(self, argv=None, aliases=None, flags=None):
540 552 """Parse command line arguments and return as a Config object.
541 553
542 554 Parameters
@@ -549,7 +561,11 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
549 561 self.clear()
550 562 if argv is None:
551 563 argv = self.argv
552 self._create_parser()
564 if aliases is None:
565 aliases = self.aliases
566 if flags is None:
567 flags = self.flags
568 self._create_parser(aliases, flags)
553 569 self._parse_args(argv)
554 570 self._convert_to_config()
555 571 return self.config
@@ -560,11 +576,11 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
560 576 else:
561 577 return []
562 578
563 def _create_parser(self):
579 def _create_parser(self, aliases=None, flags=None):
564 580 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
565 self._add_arguments()
581 self._add_arguments(aliases, flags)
566 582
567 def _add_arguments(self):
583 def _add_arguments(self, aliases=None, flags=None):
568 584 raise NotImplementedError("subclasses must implement _add_arguments")
569 585
570 586 def _parse_args(self, args):
@@ -582,7 +598,63 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
582 598 def _convert_to_config(self):
583 599 """self.parsed_data->self.config"""
584 600 for k, v in vars(self.parsed_data).iteritems():
585 exec_str = 'self.config.' + k + '= v'
586 exec exec_str in locals(), globals()
587
588
601 self._exec_config_str(k, v)
602
603 class KVArgParseConfigLoader(ArgParseConfigLoader):
604 """A config loader that loads aliases and flags with argparse,
605 but will use KVLoader for the rest. This allows better parsing
606 of common args, such as `ipython -c 'print 5'`, but still gets
607 arbitrary config with `ipython --InteractiveShell.use_readline=False`"""
608 def _add_arguments(self, aliases=None, flags=None):
609 self.alias_flags = {}
610 # print aliases, flags
611 if aliases is None:
612 aliases = self.aliases
613 if flags is None:
614 flags = self.flags
615 paa = self.parser.add_argument
616 for key,value in aliases.iteritems():
617 if key in flags:
618 # flags
619 nargs = '?'
620 else:
621 nargs = None
622 if len(key) is 1:
623 paa('-'+key, '--'+key, type=str, dest=value, nargs=nargs)
624 else:
625 paa('--'+key, type=str, dest=value, nargs=nargs)
626 for key, (value, help) in flags.iteritems():
627 if key in self.aliases:
628 #
629 self.alias_flags[self.aliases[key]] = value
630 continue
631 if len(key) is 1:
632 paa('-'+key, '--'+key, action='append_const', dest='_flags', const=value)
633 else:
634 paa('--'+key, action='append_const', dest='_flags', const=value)
635
636 def _convert_to_config(self):
637 """self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
638 # remove subconfigs list from namespace before transforming the Namespace
639 if '_flags' in self.parsed_data:
640 subcs = self.parsed_data._flags
641 del self.parsed_data._flags
642 else:
643 subcs = []
644
645 for k, v in vars(self.parsed_data).iteritems():
646 if v is None:
647 # it was a flag that shares the name of an alias
648 subcs.append(self.alias_flags[k])
649 else:
650 # eval the KV assignment
651 self._exec_config_str(k, v)
652
653 for subc in subcs:
654 self.config.update(subc)
655
656 if self.extra_args:
657 sub_parser = KeyValueConfigLoader()
658 sub_parser.load_config(self.extra_args)
659 self.config.update(sub_parser.config)
660 self.extra_args = sub_parser.extra_args
General Comments 0
You need to be logged in to leave comments. Login now