diff --git a/src/python/pants/core/util_rules/environments.py b/src/python/pants/core/util_rules/environments.py index 13322d210bc..fbad11812fe 100644 --- a/src/python/pants/core/util_rules/environments.py +++ b/src/python/pants/core/util_rules/environments.py @@ -41,7 +41,7 @@ from pants.engine.unions import UnionRule from pants.option import custom_types from pants.option.global_options import GlobalOptions -from pants.option.option_types import DictOption, OptionsInfo, collect_options_info +from pants.option.option_types import DictOption, OptionInfo, collect_options_info from pants.option.subsystem import Subsystem from pants.util.enums import match from pants.util.frozendict import FrozenDict @@ -1160,12 +1160,12 @@ def option_field_name_for(flag_names: Sequence[str]) -> str: def _add_option_field_for( env_aware_t: type[Subsystem.EnvironmentAware], - option: OptionsInfo, + option: OptionInfo, ) -> Iterable[UnionRule]: - option_type: type = option.flag_options["type"] + option_type: type = option.kwargs["type"] scope = env_aware_t.subsystem.options_scope - snake_name = option_field_name_for(option.flag_names) + snake_name = option_field_name_for(option.args) # Note that there is not presently good support for enum options. `str`-backed enums should # be easy enough to add though... @@ -1180,7 +1180,7 @@ def _add_option_field_for( "to a `Field` subtype that supports your option's value type." ) else: - member_type = option.flag_options["member_type"] + member_type = option.kwargs["member_type"] try: field_type = _LIST_OPTIONS[member_type] except KeyError: @@ -1203,7 +1203,7 @@ class OptionField(field_type, _EnvironmentSensitiveOptionFieldMixin): # type: i "environment target is active." ) subsystem = env_aware_t - option_name = option.flag_names[0] + option_name = option.args[0] setattr(OptionField, "__qualname__", f"{option_type.__qualname__}.{OptionField.__name__}") diff --git a/src/python/pants/help/help_info_extracter.py b/src/python/pants/help/help_info_extracter.py index d4c2b93e7ec..1cffb8a86ac 100644 --- a/src/python/pants/help/help_info_extracter.py +++ b/src/python/pants/help/help_info_extracter.py @@ -43,6 +43,7 @@ from pants.engine.target import Field, RegisteredTargetTypes, StringField, Target, TargetGenerator from pants.engine.unions import UnionMembership, UnionRule, is_union from pants.option.native_options import NativeOptionParser, parse_dest +from pants.option.option_types import OptionInfo from pants.option.option_util import is_dict_option, is_list_option from pants.option.options import Options from pants.option.ranked_value import Rank, RankedValue @@ -1014,10 +1015,8 @@ def get_option_scope_help_info( basic_options = [] advanced_options = [] deprecated_options = [] - for args, kwargs in registrar.option_registrations_iter(): - derivation = native_parser.get_derivation( - scope=registrar.scope, registration_args=args, registration_kwargs=kwargs - ) + for option_info in registrar.option_registrations_iter(): + derivation = native_parser.get_derivation(registrar.scope, option_info) # Massage the derivation structure returned by the NativeOptionParser into an # OptionValueHistory as returned by the legacy parser. # TODO: Once we get rid of the legacy parser we can probably simplify by @@ -1027,8 +1026,8 @@ def get_option_scope_help_info( # Adding this constant, empty history entry is silly, but it appears in the # legacy parser's results as an implementation artifact, and we want to be # consistent with its tests until we get rid of it. - is_list = kwargs.get("type") == list - is_dict = kwargs.get("type") == dict + is_list = option_info.kwargs.get("type") == list + is_dict = option_info.kwargs.get("type") == dict empty_val: list | dict | None = [] if is_list else {} if is_dict else None empty_details = "" if (is_list or is_dict) else None ranked_values.append(RankedValue(Rank.NONE, empty_val, empty_details)) @@ -1036,11 +1035,11 @@ def get_option_scope_help_info( for value, rank, details in derivation: ranked_values.append(RankedValue(rank, value, details or empty_details)) history = OptionValueHistory(tuple(ranked_values)) - ohi = self.get_option_help_info(args, kwargs) + ohi = self.get_option_help_info(option_info) ohi = dataclasses.replace(ohi, value_history=history) if ohi.deprecation_active: deprecated_options.append(ohi) - elif kwargs.get("advanced"): + elif option_info.kwargs.get("advanced"): advanced_options.append(ohi) else: basic_options.append(ohi) @@ -1056,13 +1055,13 @@ def get_option_scope_help_info( deprecated=tuple(deprecated_options), ) - def get_option_help_info(self, args, kwargs): + def get_option_help_info(self, option_info: OptionInfo) -> OptionHelpInfo: """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: + for arg in option_info.args: is_short_arg = len(arg) == 2 unscoped_cmd_line_args.append(arg) if self._scope_prefix: @@ -1071,7 +1070,7 @@ def get_option_help_info(self, args, kwargs): scoped_arg = arg scoped_cmd_line_args.append(scoped_arg) - if OptionRegistrar.is_bool(kwargs): + if OptionRegistrar.is_bool(option_info.kwargs): if is_short_arg: display_args.append(scoped_arg) else: @@ -1080,18 +1079,18 @@ def get_option_help_info(self, args, kwargs): scoped_cmd_line_args.append(f"--no-{sa_2}") display_args.append(f"--[no-]{sa_2}") else: - metavar = self.compute_metavar(kwargs) + metavar = self.compute_metavar(option_info.kwargs) separator = "" if is_short_arg else "=" display_args.append(f"{scoped_arg}{separator}{metavar}") - if kwargs.get("passthrough"): - type_str = self.stringify_type(kwargs.get("member_type", str)) + if option_info.kwargs.get("passthrough"): + type_str = self.stringify_type(option_info.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") + typ = option_info.kwargs.get("type", str) + default = self.compute_default(**option_info.kwargs) + help_msg = option_info.kwargs.get("help", "No help available.") + deprecation_start_version = option_info.kwargs.get("deprecation_start_version") + removal_version = option_info.kwargs.get("removal_version") deprecation_active = removal_version is not None and deprecated.is_deprecation_active( deprecation_start_version ) @@ -1106,10 +1105,10 @@ def get_option_help_info(self, args, kwargs): deprecated_message = ( f"{message_start}, {deprecated_tense} removed in version: {removal_version}." ) - removal_hint = kwargs.get("removal_hint") - choices = self.compute_choices(kwargs) + removal_hint = option_info.kwargs.get("removal_hint") + choices = self.compute_choices(option_info.kwargs) - dest = parse_dest(*args, **kwargs) + dest = parse_dest(option_info) udest = dest.upper() if self._scope == GLOBAL_SCOPE: # Global options have 2-3 env var variants, e.g., --pants-workdir can be @@ -1122,9 +1121,11 @@ def get_option_help_info(self, args, kwargs): else: env_var = f"PANTS_{_ENV_SANITIZER_RE.sub('_', self._scope.upper())}_{udest}" - target_field_name = f"{self._scope_prefix}_{option_field_name_for(args)}".replace("-", "_") - environment_aware = kwargs.get("environment_aware") is True - fromfile = kwargs.get("fromfile", False) + target_field_name = ( + f"{self._scope_prefix}_{option_field_name_for(option_info.args)}".replace("-", "_") + ) + environment_aware = option_info.kwargs.get("environment_aware") is True + fromfile = option_info.kwargs.get("fromfile", False) ret = OptionHelpInfo( display_args=tuple(display_args), diff --git a/src/python/pants/help/help_info_extracter_test.py b/src/python/pants/help/help_info_extracter_test.py index 2a95cd77853..7dbd1580dfe 100644 --- a/src/python/pants/help/help_info_extracter_test.py +++ b/src/python/pants/help/help_info_extracter_test.py @@ -16,7 +16,7 @@ from pants.option.config import Config from pants.option.global_options import GlobalOptions, LogLevelOption from pants.option.native_options import NativeOptionParser -from pants.option.option_types import BoolOption, IntListOption, StrListOption +from pants.option.option_types import BoolOption, IntListOption, OptionInfo, StrListOption from pants.option.options import Options from pants.option.ranked_value import Rank from pants.option.registrar import OptionRegistrar @@ -35,7 +35,7 @@ def test_global_scope(): def do_test(args, kwargs, expected_display_args, expected_scoped_cmd_line_args): # The scoped and unscoped args are the same in global scope. expected_unscoped_cmd_line_args = expected_scoped_cmd_line_args - ohi = HelpInfoExtracter("").get_option_help_info(args, kwargs) + ohi = HelpInfoExtracter("").get_option_help_info(OptionInfo(args, kwargs)) assert tuple(expected_display_args) == ohi.display_args assert tuple(expected_scoped_cmd_line_args) == ohi.scoped_cmd_line_args assert tuple(expected_unscoped_cmd_line_args) == ohi.unscoped_cmd_line_args @@ -81,7 +81,7 @@ def do_test( expected_scoped_cmd_line_args, expected_unscoped_cmd_line_args, ): - ohi = HelpInfoExtracter("bar.baz").get_option_help_info(args, kwargs) + ohi = HelpInfoExtracter("bar.baz").get_option_help_info(OptionInfo(args, kwargs)) assert tuple(expected_display_args) == ohi.display_args assert tuple(expected_scoped_cmd_line_args) == ohi.scoped_cmd_line_args assert tuple(expected_unscoped_cmd_line_args) == ohi.unscoped_cmd_line_args @@ -142,7 +142,7 @@ def do_test(expected_default: Optional[Any], **kwargs): def test_deprecated(): kwargs = {"removal_version": "999.99.9", "removal_hint": "do not use this"} - ohi = HelpInfoExtracter("").get_option_help_info(["--foo"], kwargs) + ohi = HelpInfoExtracter("").get_option_help_info(OptionInfo(("--foo",), kwargs)) assert "999.99.9" == ohi.removal_version assert "do not use this" == ohi.removal_hint assert ohi.deprecated_message is not None @@ -150,28 +150,28 @@ def test_deprecated(): def test_not_deprecated(): - ohi = HelpInfoExtracter("").get_option_help_info(["--foo"], {}) + ohi = HelpInfoExtracter("").get_option_help_info(OptionInfo(("--foo",), {})) assert ohi.removal_version is None assert not ohi.deprecation_active def test_deprecation_start_version_past(): kwargs = {"deprecation_start_version": "1.0.0", "removal_version": "999.99.9"} - ohi = HelpInfoExtracter("").get_option_help_info(["--foo"], kwargs) + ohi = HelpInfoExtracter("").get_option_help_info(OptionInfo(("--foo",), kwargs)) assert "999.99.9" == ohi.removal_version assert ohi.deprecation_active def test_deprecation_start_version_future(): kwargs = {"deprecation_start_version": "999.99.8", "removal_version": "999.99.9"} - ohi = HelpInfoExtracter("").get_option_help_info(["--foo"], kwargs) + ohi = HelpInfoExtracter("").get_option_help_info(OptionInfo(("--foo",), kwargs)) assert "999.99.9" == ohi.removal_version assert not ohi.deprecation_active def test_passthrough(): kwargs = {"passthrough": True, "type": list, "member_type": str} - ohi = HelpInfoExtracter("").get_option_help_info(["--thing"], kwargs) + ohi = HelpInfoExtracter("").get_option_help_info(OptionInfo(("--thing",), kwargs)) assert 2 == len(ohi.display_args) assert any(args.startswith("--thing") for args in ohi.display_args) assert any(args.startswith("... -- ") for args in ohi.display_args) @@ -179,19 +179,19 @@ def test_passthrough(): def test_choices() -> None: kwargs = {"choices": ["info", "debug"]} - ohi = HelpInfoExtracter("").get_option_help_info(["--foo"], kwargs) + ohi = HelpInfoExtracter("").get_option_help_info(OptionInfo(("--foo",), kwargs)) assert ohi.choices == ("info", "debug") def test_choices_enum() -> None: kwargs = {"type": LogLevelSimple} - ohi = HelpInfoExtracter("").get_option_help_info(["--foo"], kwargs) + ohi = HelpInfoExtracter("").get_option_help_info(OptionInfo(("--foo",), kwargs)) assert ohi.choices == ("info", "debug") def test_list_of_enum() -> None: kwargs = {"type": list, "member_type": LogLevelSimple} - ohi = HelpInfoExtracter("").get_option_help_info(["--foo"], kwargs) + ohi = HelpInfoExtracter("").get_option_help_info(OptionInfo(("--foo",), kwargs)) assert ohi.choices == ("info", "debug") diff --git a/src/python/pants/option/global_options.py b/src/python/pants/option/global_options.py index a0779f2ae68..7c46bacd612 100644 --- a/src/python/pants/option/global_options.py +++ b/src/python/pants/option/global_options.py @@ -2259,11 +2259,11 @@ def create(cls, GlobalOptionsType: type[GlobalOptions]) -> GlobalOptionsFlags: short_flags = set() for options_info in collect_options_info(BootstrapOptions): - for flag in options_info.flag_names: + for flag in options_info.args: flags.add(flag) if len(flag) == 2: short_flags.add(flag) - elif options_info.flag_options.get("type") == bool: + elif options_info.kwargs.get("type") == bool: flags.add(f"--no-{flag[2:]}") return cls(FrozenOrderedSet(flags), FrozenOrderedSet(short_flags)) diff --git a/src/python/pants/option/native_options.py b/src/python/pants/option/native_options.py index 6f87b4e555c..6438f9e6b1f 100644 --- a/src/python/pants/option/native_options.py +++ b/src/python/pants/option/native_options.py @@ -16,6 +16,7 @@ from pants.option.config import ConfigSource from pants.option.custom_types import _flatten_shlexed_list, dir_option, file_option, shell_str from pants.option.errors import BooleanOptionNameWithNo, OptionsError, ParseError +from pants.option.option_types import OptionInfo from pants.option.ranked_value import Rank from pants.option.scope import GLOBAL_SCOPE from pants.util.strutil import get_strict_env, softwrap @@ -23,7 +24,7 @@ logger = logging.getLogger() -def parse_dest(*args: str, **kwargs) -> str: +def parse_dest(option_info: OptionInfo) -> str: """Return the dest for an option registration. If an explicit `dest` is specified, returns that and otherwise derives a default from the @@ -34,12 +35,12 @@ def parse_dest(*args: str, **kwargs) -> str: - The key in the config file. - Computing the name of the env var used to set the option name. """ - dest = kwargs.get("dest") + dest = option_info.kwargs.get("dest") if dest: return str(dest) # No explicit dest, so compute one based on the first long arg, or the short arg # if that's all there is. - arg = next((a for a in args if a.startswith("--")), args[0]) + arg = next((a for a in option_info.args if a.startswith("--")), option_info.args[0]) return arg.lstrip("-").replace("-", "_") @@ -120,30 +121,32 @@ def with_derivation(self) -> NativeOptionParser: known_scopes_to_flags=self._known_scopes_to_flags, ) - def get_value(self, *, scope, registration_args, registration_kwargs) -> Tuple[Any, Rank]: - val, rank, _ = self._get_value_and_derivation(scope, registration_args, registration_kwargs) + def get_value(self, *, scope: str, option_info: OptionInfo) -> Tuple[Any, Rank]: + val, rank, _ = self._get_value_and_derivation(scope, option_info) return (val, rank) def get_derivation( - self, *, scope, registration_args, registration_kwargs + self, + scope: str, + option_info: OptionInfo, ) -> list[Tuple[Any, Rank, Optional[str]]]: - _, _, derivation = self._get_value_and_derivation( - scope, registration_args, registration_kwargs - ) + _, _, derivation = self._get_value_and_derivation(scope, option_info) return derivation def _get_value_and_derivation( - self, scope, registration_args, registration_kwargs + self, + scope: str, + option_info: OptionInfo, ) -> Tuple[Any, Rank, list[Tuple[Any, Rank, Optional[str]]]]: return self._get( scope=scope, - dest=parse_dest(*registration_args, **registration_kwargs), - flags=registration_args, - default=registration_kwargs.get("default"), - option_type=registration_kwargs.get("type"), - member_type=registration_kwargs.get("member_type"), - choices=registration_kwargs.get("choices"), - passthrough=registration_kwargs.get("passthrough"), + dest=parse_dest(option_info), + flags=option_info.args, + default=option_info.kwargs.get("default"), + option_type=option_info.kwargs.get("type"), + member_type=option_info.kwargs.get("member_type"), + choices=option_info.kwargs.get("choices"), + passthrough=option_info.kwargs.get("passthrough"), ) def _get( diff --git a/src/python/pants/option/option_types.py b/src/python/pants/option/option_types.py index 3cb5738bbd4..ee565d7f3bf 100644 --- a/src/python/pants/option/option_types.py +++ b/src/python/pants/option/option_types.py @@ -13,13 +13,15 @@ from pants.util.strutil import softwrap -@dataclass(frozen=True) -class OptionsInfo: - flag_names: tuple[str, ...] - flag_options: dict[str, Any] +@dataclass(frozen=True, order=True) +class OptionInfo: + """Registration info for a single option.""" + args: tuple[str, ...] # The *args in the registration. + kwargs: dict[str, Any] # The **kwargs in the registration. -def collect_options_info(cls: type) -> Iterator[OptionsInfo]: + +def collect_options_info(cls: type) -> Iterator[OptionInfo]: """Yields the ordered options info from the MRO of the provided class.""" # NB: Since registration ordering matters (it impacts `help` output), we register these in @@ -28,7 +30,7 @@ def collect_options_info(cls: type) -> Iterator[OptionsInfo]: for attrname in class_.__dict__.keys(): # NB: We use attrname and getattr to trigger descriptors attr = getattr(cls, attrname) - if isinstance(attr, OptionsInfo): + if isinstance(attr, OptionInfo): yield attr @@ -186,7 +188,7 @@ def get_flag_options(self, subsystem_cls) -> dict: ) @overload - def __get__(self, obj: None, objtype: Any) -> OptionsInfo | None: + def __get__(self, obj: None, objtype: Any) -> OptionInfo | None: ... @overload @@ -197,7 +199,7 @@ def __get__(self, obj, objtype): assert self._flag_names is not None if obj is None: if self._register_if(objtype): - return OptionsInfo(self._flag_names, self.get_flag_options(objtype)) + return OptionInfo(self._flag_names, self.get_flag_options(objtype)) return None long_name = self._flag_names[-1] option_value = getattr(obj.options, long_name[2:].replace("-", "_")) diff --git a/src/python/pants/option/option_types_test.py b/src/python/pants/option/option_types_test.py index 7852d79a84f..f07896194fc 100644 --- a/src/python/pants/option/option_types_test.py +++ b/src/python/pants/option/option_types_test.py @@ -28,7 +28,7 @@ IntOption, MemorySizeListOption, MemorySizeOption, - OptionsInfo, + OptionInfo, ShellStrListOption, ShellStrOption, SkipOption, @@ -50,9 +50,9 @@ class MyEnum(Enum): def opt_info(*names, **options): - return OptionsInfo( - flag_names=names, - flag_options=options, + return OptionInfo( + args=names, + kwargs=options, ) @@ -272,7 +272,7 @@ def __init__(self): removal_version="99.9.9", ) - flag_options = MySubsystem.prop.flag_options + flag_options = MySubsystem.prop.kwargs assert flag_options["advanced"] assert flag_options["metavar"] == "META" assert flag_options["fromfile"] @@ -351,7 +351,7 @@ class MySubsystem(MySubsystemBase): options_info = list(collect_options_info(MySubsystem)) assert len(options_info) == 1 - assert options_info[0].flag_names == ("--registered",) + assert options_info[0].args == ("--registered",) def test_property_types() -> None: diff --git a/src/python/pants/option/options.py b/src/python/pants/option/options.py index 588fb770559..b5747f32790 100644 --- a/src/python/pants/option/options.py +++ b/src/python/pants/option/options.py @@ -429,28 +429,26 @@ def for_scope( registrar = self.get_registrar(scope) scope_str = "global scope" if scope == GLOBAL_SCOPE else f"scope '{scope}'" - for args, kwargs in registrar.option_registrations_iter(): - dest = kwargs["dest"] - val, rank = self._native_parser.get_value( - scope=scope, registration_args=args, registration_kwargs=kwargs - ) + for option_info in registrar.option_registrations_iter(): + dest = option_info.kwargs["dest"] + val, rank = self._native_parser.get_value(scope=scope, option_info=option_info) explicitly_set = rank > Rank.HARDCODED # If we explicitly set a deprecated but not-yet-expired option, warn about it. # Otherwise, raise a CodeRemovedError if the deprecation has expired. - removal_version = kwargs.get("removal_version", None) + removal_version = option_info.kwargs.get("removal_version", None) if removal_version is not None: warn_or_error( removal_version=removal_version, entity=f"option '{dest}' in {scope_str}", - start_version=kwargs.get("deprecation_start_version", None), - hint=kwargs.get("removal_hint", None), + start_version=option_info.kwargs.get("deprecation_start_version", None), + hint=option_info.kwargs.get("removal_hint", None), print_warning=explicitly_set, ) # If we explicitly set the option, check for mutual exclusivity. if explicitly_set: - mutex_dest = kwargs.get("mutually_exclusive_group") + mutex_dest = option_info.kwargs.get("mutually_exclusive_group") mutex_map_key = mutex_dest or dest mutex_map[mutex_map_key].append(dest) if len(mutex_map[mutex_map_key]) > 1: @@ -493,18 +491,18 @@ def get_fingerprintable_for_scope( pairs = [] registrar = self.get_registrar(scope) # Sort the arguments, so that the fingerprint is consistent. - for _, kwargs in sorted(registrar.option_registrations_iter()): - if not kwargs.get("fingerprint", True): + for option_info in sorted(registrar.option_registrations_iter()): + if not option_info.kwargs.get("fingerprint", True): continue - if daemon_only and not kwargs.get("daemon", False): + if daemon_only and not option_info.kwargs.get("daemon", False): continue - dest = kwargs["dest"] + dest = option_info.kwargs["dest"] val = self.for_scope(scope)[dest] # If we have a list then we delegate to the fingerprinting implementation of the members. - if is_list_option(kwargs): - val_type = kwargs.get("member_type", str) + if is_list_option(option_info.kwargs): + val_type = option_info.kwargs.get("member_type", str) else: - val_type = kwargs.get("type", str) + val_type = option_info.kwargs.get("type", str) pairs.append((dest, val_type, val)) return pairs diff --git a/src/python/pants/option/options_bootstrapper.py b/src/python/pants/option/options_bootstrapper.py index 6b08b421e55..cfd3251f0c2 100644 --- a/src/python/pants/option/options_bootstrapper.py +++ b/src/python/pants/option/options_bootstrapper.py @@ -100,9 +100,7 @@ def parse_bootstrap_options( for options_info in collect_options_info(BootstrapOptions): # Only use of Options.register? - bootstrap_options.register( - GLOBAL_SCOPE, *options_info.flag_names, **options_info.flag_options - ) + bootstrap_options.register(GLOBAL_SCOPE, *options_info.args, **options_info.kwargs) return bootstrap_options diff --git a/src/python/pants/option/options_test.py b/src/python/pants/option/options_test.py index 32c1f817af0..f5ed413cf88 100644 --- a/src/python/pants/option/options_test.py +++ b/src/python/pants/option/options_test.py @@ -41,7 +41,7 @@ ) from pants.option.global_options import GlobalOptions from pants.option.native_options import parse_dest -from pants.option.option_types import StrOption +from pants.option.option_types import OptionInfo, StrOption from pants.option.options import Options from pants.option.options_bootstrapper import OptionsBootstrapper from pants.option.options_fingerprinter import OptionEncoder @@ -1011,8 +1011,8 @@ def test_choices() -> None: def test_parse_dest() -> None: - assert "thing" == parse_dest("--thing") - assert "other_thing" == parse_dest("--thing", dest="other_thing") + assert "thing" == parse_dest(OptionInfo(("--thing",), {})) + assert "other_thing" == parse_dest(OptionInfo(("--thing",), {"dest": "other_thing"})) def test_validation() -> None: diff --git a/src/python/pants/option/registrar.py b/src/python/pants/option/registrar.py index 56bb2600e09..8251fed992f 100644 --- a/src/python/pants/option/registrar.py +++ b/src/python/pants/option/registrar.py @@ -9,7 +9,7 @@ import typing from dataclasses import dataclass from enum import Enum -from typing import Any, Mapping +from typing import Any, Iterator, Mapping from pants.base.deprecated import validate_deprecation_semver from pants.option.custom_types import ( @@ -38,6 +38,7 @@ RegistrationError, ) from pants.option.native_options import parse_dest +from pants.option.option_types import OptionInfo from pants.option.ranked_value import RankedValue from pants.option.scope import GLOBAL_SCOPE @@ -113,19 +114,19 @@ def known_scoped_args(self) -> frozenset[str]: prefix = f"{self.scope}-" if self.scope != GLOBAL_SCOPE else "" return frozenset(f"--{prefix}{arg.lstrip('--')}" for arg in self._known_args) - def option_registrations_iter(self): + def option_registrations_iter(self) -> Iterator[OptionInfo]: """Returns an iterator over the normalized registration arguments of each option in this registrar. Useful for generating help and other documentation. - Each yielded item is an (args, kwargs) pair, as passed to register(), except that kwargs - will be normalized to always have 'dest' and 'default' explicitly set. + Each yielded item is an OptionInfo containing the args and kwargs as passed to register(), + except that kwargs will be normalized to always have 'dest' and 'default' explicitly set. """ def normalize_kwargs(orig_args, orig_kwargs): nkwargs = copy.copy(orig_kwargs) - dest = parse_dest(*orig_args, **nkwargs) + dest = parse_dest(OptionInfo(orig_args, nkwargs)) nkwargs["dest"] = dest if "default" not in nkwargs: type_arg = nkwargs.get("type", str) @@ -139,7 +140,7 @@ def normalize_kwargs(orig_args, orig_kwargs): # Yield our directly-registered options. for args, kwargs in self._option_registrations: normalized_kwargs = normalize_kwargs(args, kwargs) - yield args, normalized_kwargs + yield OptionInfo(args, normalized_kwargs) def register(self, *args, **kwargs) -> None: """Register an option.""" diff --git a/src/python/pants/option/subsystem.py b/src/python/pants/option/subsystem.py index 10237308e7d..2a200705973 100644 --- a/src/python/pants/option/subsystem.py +++ b/src/python/pants/option/subsystem.py @@ -13,7 +13,7 @@ from pants.engine.internals.selectors import AwaitableConstraints, Get from pants.engine.unions import UnionMembership, UnionRule, distinct_union_type_per_subclass from pants.option.errors import OptionsError -from pants.option.option_types import OptionsInfo, collect_options_info +from pants.option.option_types import OptionInfo, collect_options_info from pants.option.option_value_container import OptionValueContainer from pants.option.options import Options from pants.option.scope import Scope, ScopedOptions, ScopeInfo, normalize_scope @@ -118,12 +118,12 @@ def __getattribute__(self, __name: str) -> Any: return default # Resolving an attribute on the class object will return the underlying descriptor. - # If the descriptor is an `OptionsInfo`, we can resolve it against the environment + # If the descriptor is an `OptionInfo`, we can resolve it against the environment # target. - if isinstance(v, OptionsInfo): + if isinstance(v, OptionInfo): # If the value is not defined in the `EnvironmentTarget`, return the value # from the options system. - override = resolve_environment_sensitive_option(v.flag_names[0], self) + override = resolve_environment_sensitive_option(v.args[0], self) return override if override is not None else default # We should just return the default at this point. @@ -134,12 +134,12 @@ def _is_default(self, __name: str) -> bool: from pants.core.util_rules.environments import resolve_environment_sensitive_option v = getattr(type(self), __name) - assert isinstance(v, OptionsInfo) + assert isinstance(v, OptionInfo) return ( # vars beginning with `_` are exposed as option names with the leading `_` stripped self.options.is_default(__name.lstrip("_")) - and resolve_environment_sensitive_option(v.flag_names[0], self) is None + and resolve_environment_sensitive_option(v.args[0], self) is None ) @classmethod @@ -294,15 +294,15 @@ def register(*args, **kwargs): plugin_option_containers = union_membership.get(cls.PluginOption) for options_info in collect_options_info(cls): - register(*options_info.flag_names, **options_info.flag_options) + register(*options_info.args, **options_info.kwargs) for options_info in collect_options_info(cls.EnvironmentAware): - register(*options_info.flag_names, environment_aware=True, **options_info.flag_options) + register(*options_info.args, environment_aware=True, **options_info.kwargs) for options_info in ( option for container in plugin_option_containers for option in collect_options_info(container) ): - register(*options_info.flag_names, **options_info.flag_options) + register(*options_info.args, **options_info.kwargs) def __init__(self, options: OptionValueContainer) -> None: self.validate_scope()