def get_option_scope_help_info(self, description: str, parser: Parser, is_goal: bool): """Returns an OptionScopeHelpInfo for the options parsed by the given parser.""" basic_options = [] advanced_options = [] deprecated_options = [] for args, kwargs in parser.option_registrations_iter(): history = parser.history(kwargs["dest"]) ohi = self.get_option_help_info(args, kwargs) ohi = dataclasses.replace(ohi, value_history=history) if ohi.deprecation_active: deprecated_options.append(ohi) elif kwargs.get("advanced") or ( kwargs.get("recursive") and not kwargs.get("recursive_root") ): # In order to keep the regular help output uncluttered, we treat recursive # options as advanced. The concept of recursive options is not widely used # and not clear to the end user, so it's best not to expose it as a concept. advanced_options.append(ohi) else: basic_options.append(ohi) return OptionScopeHelpInfo( scope=self._scope, description=description, is_goal=is_goal, basic=tuple(basic_options), advanced=tuple(advanced_options), deprecated=tuple(deprecated_options), )
def get_option_scope_help_info( self, description: str, parser: Parser, is_goal: bool, provider: str = "", deprecated_scope: Optional[str] = None, ) -> OptionScopeHelpInfo: """Returns an OptionScopeHelpInfo for the options parsed by the given parser.""" basic_options = [] advanced_options = [] deprecated_options = [] for args, kwargs in parser.option_registrations_iter(): history = parser.history(kwargs["dest"]) ohi = self.get_option_help_info(args, kwargs) ohi = dataclasses.replace(ohi, value_history=history) if ohi.deprecation_active: deprecated_options.append(ohi) elif kwargs.get("advanced"): advanced_options.append(ohi) else: basic_options.append(ohi) return OptionScopeHelpInfo( scope=self._scope, description=description, provider=provider, is_goal=is_goal, deprecated_scope=deprecated_scope, basic=tuple(basic_options), advanced=tuple(advanced_options), deprecated=tuple(deprecated_options), )
def do_test(args, kwargs, expected_default): # Defaults are computed in the parser and added into the kwargs, so we # must jump through this hoop in this test. parser = Parser(env={}, config=Config.load([]), scope_info=GlobalOptionsRegistrar.get_scope_info(), parent_parser=None, option_tracker=OptionTracker()) parser.register(*args, **kwargs) oshi = HelpInfoExtracter.get_option_scope_help_info_from_parser(parser).basic self.assertEquals(1, len(oshi)) ohi = oshi[0] self.assertEqual(expected_default, ohi.default)
def from_dict(cls, alias: str, d: dict[str, Any]) -> DockerRegistryOptions: return cls( alias=alias, address=d["address"], default=Parser.ensure_bool(d.get("default", alias == "default")), skip_push=Parser.ensure_bool( d.get("skip_push", DockerRegistryOptions.skip_push)), extra_image_tags=tuple( d.get("extra_image_tags", DockerRegistryOptions.extra_image_tags)), )
def do_test(kwargs, expected_basic=False, expected_advanced=False): def exp_to_len(exp): return int(exp) # True -> 1, False -> 0. parser = Parser( env={}, config=Config.load([]), scope_info=GlobalOptions.get_scope_info(), ) parser.register("--foo", **kwargs) oshi = HelpInfoExtracter("").get_option_scope_help_info( "", parser, False) assert exp_to_len(expected_basic) == len(oshi.basic) assert exp_to_len(expected_advanced) == len(oshi.advanced)
def do_test(args, kwargs, expected_default_str): # Defaults are computed in the parser and added into the kwargs, so we # must jump through this hoop in this test. parser = Parser( env={}, config=Config.load([]), scope_info=GlobalOptions.get_scope_info(), ) parser.register(*args, **kwargs) oshi = HelpInfoExtracter(parser.scope).get_option_scope_help_info( "description", parser, False) assert oshi.description == "description" assert len(oshi.basic) == 1 ohi = oshi.basic[0] assert to_help_str(ohi.default) == expected_default_str
def gen_tasks_options_reference_data(): """Generate the template data for the options reference rst doc.""" goal_dict = {} goal_names = [] for goal in Goal.all(): tasks = [] for task_name in goal.ordered_task_names(): task_type = goal.task_type_by_name(task_name) doc_rst = indent_docstring_by_n(task_type.__doc__ or '', 2) doc_html = rst_to_html(dedent_docstring(task_type.__doc__)) option_parser = Parser(env={}, config={}, scope='', help_request=None, parent_parser=None) def register(*args, **kwargs): option_parser.register(*args, **kwargs) register.bootstrap = bootstrap_option_values() task_type.register_options(register) argparser = option_parser._help_argparser scope = Goal.scope(goal.name, task_name) # task_type may actually be a synthetic subclass of the authored class from the source code. # We want to display the authored class's name in the docs (but note that we must use the # subclass for registering options above) for authored_task_type in task_type.mro(): if authored_task_type.__module__ != 'abc': break impl = '{0}.{1}'.format(authored_task_type.__module__, authored_task_type.__name__) tasks.append(TemplateData( impl=impl, doc_html=doc_html, doc_rst=doc_rst, ogroup=oref_template_data_from_options(scope, argparser))) goal_dict[goal.name] = TemplateData(goal=goal, tasks=tasks) goal_names.append(goal.name) goals = [goal_dict[name] for name in sorted(goal_names, key=lambda x: x.lower())] return goals
def capture_the_flags(*args, **kwargs): for flag in Parser.expand_flags(*args, **kwargs): flags.add(flag.name) if len(flag.name) == 2: short_flags.add(flag.name) if flag.inverse_name: flags.add(flag.inverse_name)
def create( cls, env: Mapping[str, str], config: Config, known_scope_infos: Iterable[ScopeInfo], args: Sequence[str], bootstrap_option_values: OptionValueContainer | None = None, allow_unknown_options: bool = False, ) -> Options: """Create an Options instance. :param env: a dict of environment variables. :param config: data from a config file. :param known_scope_infos: ScopeInfos for all scopes that may be encountered. :param args: a list of cmd-line args; defaults to `sys.argv` if None is supplied. :param bootstrap_option_values: An optional namespace containing the values of bootstrap options. We can use these values when registering other options. :param allow_unknown_options: Whether to ignore or error on unknown cmd-line flags. """ # We need parsers for all the intermediate scopes, so inherited option values # can propagate through them. complete_known_scope_infos = cls.complete_scopes(known_scope_infos) splitter = ArgSplitter(complete_known_scope_infos, get_buildroot()) split_args = splitter.split_args(args) if split_args.passthru and len(split_args.goals) > 1: raise cls.AmbiguousPassthroughError( f"Specifying multiple goals (in this case: {split_args.goals}) " "along with passthrough args (args after `--`) is ambiguous.\n" "Try either specifying only a single goal, or passing the passthrough args " "directly to the relevant consumer via its associated flags.") if bootstrap_option_values: spec_files = bootstrap_option_values.spec_files if spec_files: for spec_file in spec_files: with open(spec_file) as f: split_args.specs.extend([ line for line in [line.strip() for line in f] if line ]) help_request = splitter.help_request parser_by_scope = { si.scope: Parser(env, config, si) for si in complete_known_scope_infos } known_scope_to_info = {s.scope: s for s in complete_known_scope_infos} return cls( goals=split_args.goals, scope_to_flags=split_args.scope_to_flags, specs=split_args.specs, passthru=split_args.passthru, help_request=help_request, parser_by_scope=parser_by_scope, bootstrap_option_values=bootstrap_option_values, known_scope_to_info=known_scope_to_info, allow_unknown_options=allow_unknown_options, )
def _make_parse_args_request( self, flags_in_scope, namespace: OptionValueContainerBuilder) -> Parser.ParseArgsRequest: return Parser.ParseArgsRequest( flags_in_scope=flags_in_scope, namespace=namespace, passthrough_args=self._passthru, )
def gen_glopts_reference_data(): option_parser = Parser(env={}, config={}, scope='', help_request=None, parent_parser=None) def register(*args, **kwargs): option_parser.register(*args, **kwargs) register.bootstrap = bootstrap_option_values() register_bootstrap_options(register, buildroot='<buildroot>') register_global_options(register) argparser = option_parser._help_argparser return oref_template_data_from_options(Options.GLOBAL_SCOPE, argparser)
def __init__(self, env, config, all_scopes): # Sorting ensures that ancestors precede descendants. all_scopes = sorted(set(list(all_scopes) + [GLOBAL_SCOPE])) self._parser_by_scope = {} for scope in all_scopes: parent_parser = (None if scope == GLOBAL_SCOPE else self._parser_by_scope[scope.rpartition('.')[0]]) self._parser_by_scope[scope] = Parser(env, config, scope, parent_parser)
def __init__(self, env, config, scope_infos): # Sorting ensures that ancestors precede descendants. scope_infos = sorted(set(list(scope_infos)), key=lambda si: si.scope) self._parser_by_scope = {} for scope_info in scope_infos: scope = scope_info.scope parent_parser = (None if scope == GLOBAL_SCOPE else self._parser_by_scope[scope.rpartition('.')[0]]) self._parser_by_scope[scope] = Parser(env, config, scope_info, parent_parser)
def _make_parse_args_request( self, flags_in_scope, namespace: OptionValueContainer) -> Parser.ParseArgsRequest: return Parser.ParseArgsRequest( flags_in_scope=flags_in_scope, namespace=namespace, get_all_scoped_flag_names=lambda: self. _all_scoped_flag_names_for_fuzzy_matching, passthrough_args=self._passthru, )
def _make_parse_args_request(self, flags_in_scope, namespace): levenshtein_max_distance = ( self._bootstrap_option_values.option_name_check_distance if self._bootstrap_option_values else 0) return Parser.ParseArgsRequest( flags_in_scope=flags_in_scope, namespace=namespace, get_all_scoped_flag_names=lambda: self. _all_scoped_flag_names_for_fuzzy_matching, levenshtein_max_distance=levenshtein_max_distance, )
def __init__( self, env: Mapping[str, str], config: Config, scope_infos: Iterable[ScopeInfo], ) -> None: # Sorting ensures that ancestors precede descendants. scope_infos = sorted(set(list(scope_infos)), key=lambda si: si.scope) self._parser_by_scope: Dict[str, Parser] = {} for scope_info in scope_infos: scope = scope_info.scope parent_parser = (None if scope == GLOBAL_SCOPE else self._parser_by_scope[enclosing_scope(scope)]) self._parser_by_scope[scope] = Parser(env, config, scope_info, parent_parser)
def parse(cls, value: bool | str) -> bool: try: return Parser.ensure_bool(value) except BooleanConversionError: enum_value = cls(value) bool_value = enum_value is not cls.NEVER deprecated_conditional( lambda: True, removal_version="2.6.0.dev0", entity_description= "python-setup resolve_all_constraints non boolean values", hint_message= f"Instead of {enum_value.value!r} use {bool_value!r}.", ) return bool_value
def _format_for_global_scope(show_advanced, show_deprecated, args, kwargs): parser = Parser( env={}, config=Config.load([]), scope_info=GlobalOptions.get_scope_info(), parent_parser=None, ) parser.register(*args, **kwargs) # Force a parse to generate the derivation history. parser.parse_args(Parser.ParseArgsRequest((), OptionValueContainerBuilder(), [], False)) oshi = HelpInfoExtracter("").get_option_scope_help_info("", parser, False) return HelpFormatter( show_advanced=show_advanced, show_deprecated=show_deprecated, color=False ).format_options(oshi)
def register(*args, **kwargs): _, option_dest = Parser.parse_name_and_dest(*args, **kwargs) default = kwargs.get("default") if default is None: if kwargs.get("type") == bool: default = False if kwargs.get("type") == list: default = [] defaults[option_dest] = RankedValue(Rank.HARDCODED, default) fingerprint = kwargs.get("fingerprint", False) if fingerprint: if is_list_option(kwargs): val_type = kwargs.get("member_type", str) else: val_type = kwargs.get("type", str) fingerprintables[option_dest] = val_type
def register(*args, **kwargs): option_name = Parser.parse_dest(*args, **kwargs) default = kwargs.get(b'default') if default is None: if kwargs.get(b'type') == bool: default = False if kwargs.get(b'type') == list: default = [] defaults[option_name] = RankedValue(RankedValue.HARDCODED, default) fingerprint = kwargs.get(b'fingerprint', False) if fingerprint: if is_list_option(kwargs): val_type = kwargs.get(b'member_type', str) else: val_type = kwargs.get(b'type', str) fingerprintables[option_name] = val_type
def _make_parse_args_request( self, flags_in_scope, namespace: OptionValueContainer, include_passive_options: bool = False, ) -> Parser.ParseArgsRequest: levenshtein_max_distance = ( self._bootstrap_option_values.option_name_check_distance if self._bootstrap_option_values else 0) return Parser.ParseArgsRequest( flags_in_scope=flags_in_scope, namespace=namespace, get_all_scoped_flag_names=lambda: self. _all_scoped_flag_names_for_fuzzy_matching, levenshtein_max_distance=levenshtein_max_distance, passthrough_args=self._passthru, include_passive_options=include_passive_options, )
def parser() -> Parser: return Parser( env={}, config=Config.load([]), scope_info=GlobalOptions.get_scope_info(), )
def get_option_help_info(self, args, kwargs): """Returns an OptionHelpInfo for the option registered with the given (args, kwargs).""" display_args = [] scoped_cmd_line_args = [] unscoped_cmd_line_args = [] for arg in args: is_short_arg = len(arg) == 2 unscoped_cmd_line_args.append(arg) if self._scope_prefix: scoped_arg = f"--{self._scope_prefix}-{arg.lstrip('-')}" else: scoped_arg = arg scoped_cmd_line_args.append(scoped_arg) if Parser.is_bool(kwargs): if is_short_arg: display_args.append(scoped_arg) else: unscoped_cmd_line_args.append(f"--no-{arg[2:]}") sa_2 = scoped_arg[2:] scoped_cmd_line_args.append(f"--no-{sa_2}") display_args.append(f"--[no-]{sa_2}") else: metavar = self.compute_metavar(kwargs) display_args.append(f"{scoped_arg}={metavar}") if kwargs.get("passthrough"): type_str = self.stringify_type(kwargs.get("member_type", str)) display_args.append(f"... -- [{type_str} [{type_str} [...]]]") typ = kwargs.get("type", str) default = self.compute_default(**kwargs) help_msg = kwargs.get("help", "No help available.") deprecation_start_version = kwargs.get("deprecation_start_version") removal_version = kwargs.get("removal_version") deprecation_active = removal_version is not None and deprecated.is_deprecation_active( deprecation_start_version ) deprecated_message = None if removal_version: deprecated_tense = deprecated.get_deprecated_tense(removal_version) message_start = ( "Deprecated" if deprecation_active else f"Upcoming deprecation in version: {deprecation_start_version}" ) deprecated_message = ( f"{message_start}, {deprecated_tense} removed in version: {removal_version}." ) removal_hint = kwargs.get("removal_hint") choices = self.compute_choices(kwargs) dest = Parser.parse_dest(*args, **kwargs) # Global options have three env var variants. The last one is the most human-friendly. env_var = Parser.get_env_var_names(self._scope, dest)[-1] ret = OptionHelpInfo( display_args=tuple(display_args), comma_separated_display_args=", ".join(display_args), scoped_cmd_line_args=tuple(scoped_cmd_line_args), unscoped_cmd_line_args=tuple(unscoped_cmd_line_args), env_var=env_var, config_key=dest, typ=typ, default=default, help=help_msg, deprecation_active=deprecation_active, deprecated_message=deprecated_message, removal_version=removal_version, removal_hint=removal_hint, choices=choices, comma_separated_choices=None if choices is None else ", ".join(choices), value_history=None, ) return ret
def recording_register(*args, **kwargs): _, dest = Parser.parse_name_and_dest(*args, **kwargs) option_names.append(dest) register(*args, **kwargs)
def from_dict(cls, alias: str, d: dict[str, Any]) -> DockerRegistryOptions: return cls( alias=alias, address=d["address"], default=Parser.ensure_bool(d.get("default", alias == "default")), )
def from_dict(cls, alias: str, d: dict[str, Any]) -> HelmRegistry: return cls( alias=alias, address=cast(str, d["address"]).rstrip("/"), default=Parser.ensure_bool(d.get("default", alias == "default")), )
def recording_register(*args, **kwargs): option_names.append(Parser.parse_dest(*args, **kwargs)) register(*args, **kwargs)