import argparse import shlex import sys from copy import deepcopy from ..config import Configuration from ..setting import BooleanSetting, ChoiceSetting, ListSetting from .base import Source __all__ = ( 'CommandLineSource', ) # pylint: disable=R0201 class CommandLineSource(Source): # pylint: disable=R0913 def __init__( self, arguments=None, options=None, version=None, parser_options=None, positional=None): super(CommandLineSource, self).__init__() if arguments is None: self.arguments = sys.argv[1:] elif isinstance(arguments, str): self.arguments = shlex.split(arguments) elif isinstance(arguments, (list, tuple)): self.arguments = arguments else: raise TypeError('arguments must be a string or list of strings') self.version = version self.options = options or {} self.parser_options = parser_options or {} self.positional = positional or () def get_flags(self, setting): if setting.name in self.options: if 'flags' in self.options[setting.name]: return self.options[setting.name]['flags'] flags = [] flag = '--%s' % setting.name.lower().replace('_', '-') flags.append(flag) return flags def get_action(self, setting): if isinstance(setting, BooleanSetting): return 'store_false' if setting.default else 'store_true' elif isinstance(setting, ListSetting): return 'append' else: return 'store' # pylint: disable=W0613 def get_default(self, setting): # Caveat: Returning something other than SUPPRESS probably won't # work the way you'd think. return argparse.SUPPRESS def get_type(self, setting): if isinstance(setting, (ListSetting, BooleanSetting)): return None elif isinstance(setting, ChoiceSetting): return setting.subtype.sanitize else: return setting.sanitize def get_dest(self, setting): return setting.name def get_choices(self, setting): if isinstance(setting, ChoiceSetting): return setting.choices else: return None def get_help(self, setting): if setting.name in self.options: if 'help' in self.options[setting.name]: return self.options[setting.name]['help'] return None def get_metavar(self, setting): if setting.name in self.options: if 'metavar' in self.options[setting.name]: return self.options[setting.name]['metavar'] return None def build_argument(self, setting): flags = self.get_flags(setting) action = self.get_action(setting) default = self.get_default(setting) argtype = self.get_type(setting) dest = self.get_dest(setting) choices = self.get_choices(setting) arghelp = self.get_help(setting) metavar = self.get_metavar(setting) argument_kwargs = { 'action': action, 'default': default, 'dest': dest, 'help': arghelp, } if argtype: argument_kwargs['type'] = argtype if choices: argument_kwargs['choices'] = choices if metavar: argument_kwargs['metavar'] = metavar return flags, argument_kwargs def build_parser(self, settings, manager): parser_options = deepcopy(self.parser_options) if not parser_options.get('prog') and manager: parser_options['prog'] = manager.name parser = argparse.ArgumentParser(**parser_options) add_version = (self.version is not None) for setting in settings: flags, argument_kwargs = self.build_argument(setting) parser.add_argument(*flags, **argument_kwargs) if add_version and setting.name == 'version': # Don't want to conflict with the desired setting add_version = False if add_version: parser.add_argument( '--version', action='version', version='%(prog)s ' + self.version, ) if self.positional: for name, options in self.positional: parser.add_argument(name, **options) return parser def get_config(self, settings, manager=None, parent=None): parser = self.build_parser(settings, manager) parsed = parser.parse_args(self.arguments) for setting in settings: if hasattr(parsed, setting.name): setting.value = getattr(parsed, setting.name) if self.positional and manager: arguments = {} for name, _ in self.positional: if hasattr(parsed, name): arguments[name] = getattr(parsed, name) setattr(manager, 'arguments', arguments) return Configuration(settings=settings, parent=parent)