def parse_args(self, *args, **kwargs): """Overwrite default values based on global configuration.""" # mapping of all verbs to parsers def collect_parsers_by_verb(root, parsers, parent_verbs=()): for sp in root._subparsers: for name, p in sp._parsers.items(): verbs = parent_verbs + (name, ) parsers[verbs] = p collect_parsers_by_verb(p, parsers, verbs) all_parsers = {} collect_parsers_by_verb(self, all_parsers) # collect passed verbs to determine relevant configuration options with SuppressUsageOutput([self._parser] + list(all_parsers.values())): known_args, _ = self._parser.parse_known_args(*args, **kwargs) data = self._get_defaults_values(self._config_path) # determine data keys and parsers for passed verbs (including the root) keys_and_parsers = [] nested_verbs = () parser = self while True: keys_and_parsers.append(('.'.join(nested_verbs), parser)) if len(parser._recursive_decorators) != 1: break if not hasattr(parser._recursive_decorators[0], 'dest'): break verb = getattr(known_args, parser._recursive_decorators[0].dest, None) if verb is None: break nested_verbs = nested_verbs + (verb, ) parser = all_parsers[nested_verbs] for key, parser in keys_and_parsers: parser._set_parser_defaults(data.get(key, {}), parser_name=key) return self._parser.parse_args(*args, **kwargs)
def parse_args(self, *args, **kwargs): """Add mixin argument for each parser.""" global VERB_BLOCKLIST # mapping of all "leaf" verbs to parsers def collect_parsers_by_verb(root, parsers, parent_verbs=()): found_any = False for sp in root._subparsers: for name, p in sp._parsers.items(): verbs = parent_verbs + (name, ) found_children = collect_parsers_by_verb(p, parsers, verbs) # only add verbs which don't have subverbs if not found_children: parsers[verbs] = p found_any = True return found_any parsers = {} collect_parsers_by_verb(self, parsers) mixins_by_verb = get_mixins() # add mixin arguments to these parsers # doing this here instead of in the add_parser() method makes sure # the arguments are documented at the very end of the help message groups = {} for k, p in parsers.items(): # match all slices starting from index 0 of k against the blocklist # e.g. k=(a,b,c) it checks against (a), (a,b), (a,b,c) k_prefixes = {k[0:index] for index in range(1, len(k) + 1)} if not k_prefixes & VERB_BLOCKLIST: groups[p] = self._add_mixin_argument_group(p) # add dummy --mixin argument to prevent parse_known_args to interpret # --mixin arguments as --mixin-files mixin_arguments = {} for verb, p in parsers.items(): if p in groups: mixin_arguments[verb] = self._add_mixin_argument( p, groups[p], verb) with SuppressUsageOutput([self._parser] + list(parsers.values())): known_args, _ = self._parser.parse_known_args(*args, **kwargs) for mixin_file in (getattr(known_args, 'mixin_files', None) or []): # add mixins from explicitly provided file add_mixins(Path(mixin_file), mixins_by_verb) # update the --mixin argument help and completer with available mixins for verb, argument in mixin_arguments.items(): self._update_mixin_argument(argument, mixins_by_verb.get(verb, {})) args = self._parser.parse_args(*args, **kwargs) # update args based on selected mixins if 'mixin_verb' in args: mixins = mixins_by_verb.get(args.mixin_verb, {}) for mixin in args.mixin or (): if mixin not in mixins: context = '.'.join(args.mixin_verb) self._parser.error( "Mixin '{mixin}' is not available for '{context}'". format_map(locals())) mixin_args = mixins[mixin] logger.debug("Using mixin '{mixin}': {mixin_args}".format_map( locals())) self._update_args(args, mixin_args, '.'.join(args.mixin_verb)) # undo default value wrapping injected in the add_argument() method for k, v in args.__dict__.items(): if is_default_value(v): setattr(args, k, _custom_unwrap_default_value(v)) return args
def main(*, command_name='colcon', argv=None): """ Execute the main logic of the command. The overview of the process: * Configure logging level based on an environment variable * Configure the configuration path * Create the argument parser * Document all environment variables * Decorate the parsers with additional functionality * Add the available verbs and their arguments * Configure logging level based on an arguments * Create an invocation specific log directory * Invoke the logic of the selected verb (if applicable) :param str command_name: The name of the command invoked :param list argv: The list of arguments :returns: The return code """ global colcon_logger # default log level colcon_logger.setLevel(logging.WARN) set_logger_level_from_env( colcon_logger, '{command_name}_LOG_LEVEL'.format_map(locals()).upper()) colcon_logger.debug('Command line arguments: {argv}'.format( argv=argv if argv is not None else sys.argv)) # set default locations for config files set_default_config_path( path=(Path('~') / '.{command_name}'.format_map(locals())).expanduser(), env_var='{command_name}_HOME'.format_map(locals()).upper()) parser = create_parser('colcon_core.environment_variable') verb_extensions = get_verb_extensions() # add subparsers for all verb extensions but without arguments for now subparser = create_subparser(parser, command_name, verb_extensions, attribute='verb_name') verb_parsers = add_parsers_without_arguments(parser, subparser, verb_extensions, attribute='verb_name') with SuppressUsageOutput([parser] + list(verb_parsers.values())): known_args, _ = parser.parse_known_args(args=argv) # add the arguments for the requested verb if known_args.verb_name: add_parser_arguments(known_args.verb_parser, known_args.verb_extension) args = parser.parse_args(args=argv) context = CommandContext(command_name=command_name, args=args) if args.log_level: colcon_logger.setLevel(args.log_level) colcon_logger.debug('Parsed command line arguments: {args}'.format_map( locals())) # error: no verb provided if args.verb_name is None: print(parser.format_usage()) return 'Error: No verb provided' # set default locations for log files now = datetime.datetime.now() now_str = str(now)[:-7].replace(' ', '_').replace(':', '-') set_default_log_path( base_path=args.log_base, env_var='{command_name}_LOG_PATH'.format_map(locals()).upper(), subdirectory='{args.verb_name}_{now_str}'.format_map(locals())) # add a file handler writing all levels create_log_path(args.verb_name) handler = add_file_handler(colcon_logger, get_log_path() / 'logger_all.log') # write previous log messages to the file handler log_record = colcon_logger.makeRecord( colcon_logger.name, logging.DEBUG, __file__, 0, 'Command line arguments: {argv}'.format( argv=argv if argv is not None else sys.argv), None, None) handler.handle(log_record) log_record = colcon_logger.makeRecord( colcon_logger.name, logging.DEBUG, __file__, 0, 'Parsed command line arguments: {args}'.format_map(locals()), None, None) handler.handle(log_record) # invoke verb return verb_main(context, colcon_logger)
def _main(*, command_name, argv): global colcon_logger # default log level colcon_logger.setLevel(logging.WARNING) set_logger_level_from_env( colcon_logger, '{command_name}_LOG_LEVEL'.format_map(locals()).upper()) colcon_logger.debug( 'Command line arguments: {argv}' .format(argv=argv if argv is not None else sys.argv)) # set default locations for config files set_default_config_path( path=( Path('~') / '.{command_name}'.format_map(locals())).expanduser(), env_var='{command_name}_HOME'.format_map(locals()).upper()) parser = create_parser('colcon_core.environment_variable') verb_extensions = get_verb_extensions() # add subparsers for all verb extensions but without arguments for now subparser = create_subparser( parser, command_name, verb_extensions, attribute='verb_name') verb_parsers = add_parsers_without_arguments( parser, subparser, verb_extensions, attribute='verb_name') with SuppressUsageOutput([parser] + list(verb_parsers.values())): known_args, _ = parser.parse_known_args(args=argv) # add the arguments for the requested verb if known_args.verb_name: add_parser_arguments(known_args.verb_parser, known_args.verb_extension) args = parser.parse_args(args=argv) context = CommandContext(command_name=command_name, args=args) if args.log_level: colcon_logger.setLevel(args.log_level) colcon_logger.debug( 'Parsed command line arguments: {args}'.format_map(locals())) # error: no verb provided if args.verb_name is None: print(parser.format_usage()) return 'Error: No verb provided' # set default locations for log files now = datetime.datetime.now() now_str = str(now)[:-7].replace(' ', '_').replace(':', '-') set_default_log_path( base_path=args.log_base, env_var='{command_name}_LOG_PATH'.format_map(locals()).upper(), subdirectory='{args.verb_name}_{now_str}'.format_map(locals())) # add a file handler writing all levels if logging isn't disabled log_path = get_log_path() if log_path is not None: create_log_path(args.verb_name) handler = add_file_handler( colcon_logger, log_path / 'logger_all.log') # write previous log messages to the file handler log_record = colcon_logger.makeRecord( colcon_logger.name, logging.DEBUG, __file__, 0, 'Command line arguments: {argv}' .format(argv=argv if argv is not None else sys.argv), None, None) handler.handle(log_record) log_record = colcon_logger.makeRecord( colcon_logger.name, logging.DEBUG, __file__, 0, 'Parsed command line arguments: {args}'.format_map(locals()), None, None) handler.handle(log_record) # invoke verb return verb_main(context, colcon_logger)
def parse_args(self, *args, **kwargs): """Add mixin argument for each parser.""" # mapping of all "leaf" verbs to parsers def collect_parsers_by_verb(root, parsers, parent_verbs=()): found_any = False for sp in root._subparsers: for name, p in sp._parsers.items(): verbs = parent_verbs + (name, ) found_children = collect_parsers_by_verb(p, parsers, verbs) # only add verbs which don't have subverbs if not found_children: parsers[verbs] = p found_any = True return found_any parsers = {} collect_parsers_by_verb(self, parsers) mixins_by_verb = get_mixins() # add mixin arguments to these parsers # doing this here instead of in the add_parser() method makes sure # the arguments are documented at the very end of the help message groups = {} for p in parsers.values(): groups[p] = self._add_mixin_argument_group(p) # add dummy --mixin argument to prevent parse_known_args to interpret # --mixin arguments as --mixin-files mixin_arguments = {} for verb, p in parsers.items(): mixin_arguments[verb] = self._add_mixin_argument( p, groups[p], verb) with SuppressUsageOutput([self._parser] + list(parsers.values())): known_args, _ = self._parser.parse_known_args(*args, **kwargs) for mixin_file in (getattr(known_args, 'mixin_files', None) or []): # add mixins from explicitly provided file add_mixins(Path(mixin_file), mixins_by_verb) # update the --mixin argument help and completer with available mixins for verb, argument in mixin_arguments.items(): self._update_mixin_argument(argument, mixins_by_verb.get(verb, {})) args = self._parser.parse_args(*args, **kwargs) # update args based on selected mixins if 'mixin_verb' in args: # unwrap the mixin_verb which is a default value args.mixin_verb = args.mixin_verb.value mixins = mixins_by_verb.get(args.mixin_verb, {}) for mixin in args.mixin or (): if mixin not in mixins: context = '.'.join(args.mixin_verb) logger.warning( "Mixin '{mixin}' is not available for '{context}'". format_map(locals())) continue mixin_args = mixins[mixin] logger.debug("Using mixin '{mixin}': {mixin_args}".format_map( locals())) self._update_args(args, mixin_args, '.'.join(args.mixin_verb)) # undo default value wrapping injected in the add_argument() method for k, v in args.__dict__.items(): if isinstance(v, DefaultValue): setattr(args, k, v.value) return args