From 3e80b7b07775e87af334e909f7f687366cdd27e0 Mon Sep 17 00:00:00 2001 From: Joshua Cannon Date: Sat, 26 Feb 2022 19:42:31 -0600 Subject: [PATCH] [internal] Rollout Options v2 to (other dirs) (#14643) [ci skip-rust] --- .../backend/codegen/avro/java/subsystem.py | 29 +++--- .../pants/backend/codegen/protobuf/protoc.py | 29 ++---- .../codegen/protobuf/scala/subsystem.py | 61 ++++++------- .../backend/docker/lint/hadolint/subsystem.py | 89 +++++++----------- .../java/lint/google_java_format/subsystem.py | 41 +++------ .../pants/backend/java/subsystems/junit.py | 17 +--- .../pants/backend/project_info/count_loc.py | 27 ++---- .../pants/backend/project_info/dependees.py | 36 +++----- .../backend/project_info/dependencies.py | 37 +++----- .../pants/backend/project_info/filedeps.py | 69 ++++++-------- .../backend/project_info/filter_targets.py | 54 +++++------ .../backend/project_info/list_targets.py | 19 ++-- src/python/pants/backend/project_info/peek.py | 21 ++--- .../backend/scala/lint/scalafmt/subsystem.py | 20 ++-- .../backend/scala/subsystems/scalatest.py | 23 ++--- .../shell/lint/shellcheck/subsystem.py | 62 +++++-------- .../backend/shell/lint/shfmt/subsystem.py | 56 ++++-------- src/python/pants/engine/goal.py | 30 +++--- .../pants/jvm/resolve/coursier_setup.py | 34 +++---- src/python/pants/jvm/resolve/jvm_tool.py | 91 ++++++++----------- .../src/python/test_pants_plugin/register.py | 7 +- .../src/python/workunit_logger/register.py | 5 +- 22 files changed, 317 insertions(+), 540 deletions(-) diff --git a/src/python/pants/backend/codegen/avro/java/subsystem.py b/src/python/pants/backend/codegen/avro/java/subsystem.py index 5cd6f8603f1..8b773bbaf3f 100644 --- a/src/python/pants/backend/codegen/avro/java/subsystem.py +++ b/src/python/pants/backend/codegen/avro/java/subsystem.py @@ -1,7 +1,7 @@ # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). from pants.jvm.resolve.jvm_tool import JvmToolBase -from pants.option.custom_types import target_option +from pants.option.option_types import TargetListOption from pants.util.docutil import git_url @@ -20,19 +20,14 @@ class AvroSubsystem(JvmToolBase): ) default_lockfile_url = git_url(default_lockfile_path) - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--runtime-dependencies", - type=list, - member_type=target_option, - help=( - "A list of addresses to `jvm_artifact` targets for the runtime " - "dependencies needed for generated Java code to work. For example, " - "`['3rdparty/jvm:avro-runtime']`. These dependencies will " - "be automatically added to every `avro_sources` target. At the very least, " - "this option must be set to a `jvm_artifact` for the " - f"`org.apache.avro:avro:{cls.default_version}` runtime library." - ), - ) + runtime_dependencies = TargetListOption( + "--runtime-dependencies", + help=lambda cls: ( + "A list of addresses to `jvm_artifact` targets for the runtime " + "dependencies needed for generated Java code to work. For example, " + "`['3rdparty/jvm:avro-runtime']`. These dependencies will " + "be automatically added to every `avro_sources` target. At the very least, " + "this option must be set to a `jvm_artifact` for the " + f"`org.apache.avro:avro:{cls.default_version}` runtime library." + ), + ) diff --git a/src/python/pants/backend/codegen/protobuf/protoc.py b/src/python/pants/backend/codegen/protobuf/protoc.py index bc7fec0792c..dfbbe2bc877 100644 --- a/src/python/pants/backend/codegen/protobuf/protoc.py +++ b/src/python/pants/backend/codegen/protobuf/protoc.py @@ -1,10 +1,10 @@ # Copyright 2018 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -from typing import List, cast from pants.core.util_rules.external_tool import TemplatedExternalTool from pants.engine.platform import Platform +from pants.option.option_types import BoolOption class Protoc(TemplatedExternalTool): @@ -29,26 +29,13 @@ class Protoc(TemplatedExternalTool): "macos_x86_64": "osx-x86_64", } - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--dependency-inference", - default=True, - type=bool, - help=( - "Infer Protobuf dependencies on other Protobuf files by analyzing import " - "statements." - ), - ) + dependency_inference = BoolOption( + "--dependency-inference", + default=True, + help=( + "Infer Protobuf dependencies on other Protobuf files by analyzing import statements." + ), + ) def generate_exe(self, plat: Platform) -> str: return "./bin/protoc" - - @property - def runtime_targets(self) -> List[str]: - return cast(List[str], self.options.runtime_targets) - - @property - def dependency_inference(self) -> bool: - return cast(bool, self.options.dependency_inference) diff --git a/src/python/pants/backend/codegen/protobuf/scala/subsystem.py b/src/python/pants/backend/codegen/protobuf/scala/subsystem.py index 2ff37ae6863..923d6f43382 100644 --- a/src/python/pants/backend/codegen/protobuf/scala/subsystem.py +++ b/src/python/pants/backend/codegen/protobuf/scala/subsystem.py @@ -6,7 +6,7 @@ from pants.engine.addresses import UnparsedAddressInputs from pants.jvm.resolve.jvm_tool import JvmToolBase -from pants.option.custom_types import target_option +from pants.option.option_types import StrListOption, TargetListOption from pants.util.docutil import git_url @@ -42,42 +42,35 @@ class ScalaPBSubsystem(JvmToolBase): ) default_lockfile_url = git_url(default_lockfile_path) - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--runtime-dependencies", - type=list, - member_type=target_option, - help=( - "A list of addresses to `jvm_artifact` targets for the runtime " - "dependencies needed for generated Scala code to work. For example, " - "`['3rdparty/jvm:scalapb-runtime']`. These dependencies will " - "be automatically added to every `protobuf_sources` target. At the very least, " - "this option must be set to a `jvm_artifact` for the " - f"`com.thesamet.scalapb:scalapb-runtime_SCALAVER:{cls.default_version}` runtime library." - ), - ) - register( - "--jvm-plugins", - type=list, - member_type=str, - help=( - "A list of JVM-based `protoc` plugins to invoke when generating Scala code from protobuf files. " - "The format for each plugin specifier is `NAME=ARTIFACT` where NAME is the name of the " - "plugin and ARTIFACT is either the address of a `jvm_artifact` target or the colon-separated " - "Maven coordinate for the plugin's jar artifact.\n\n" - "For example, to invoke the fs2-grpc protoc plugin, the following option would work: " - "`--scalapb-jvm-plugins=fs2=org.typelevel:fs2-grpc-codegen_2.12:2.3.1`. " - "(Note: you would also need to set --scalapb-runtime-dependencies appropriately " - "to include the applicable runtime libraries for your chosen protoc plugins.)" - ), - ) + _runtime_dependencies = TargetListOption( + "--runtime-dependencies", + help=lambda cls: ( + "A list of addresses to `jvm_artifact` targets for the runtime " + "dependencies needed for generated Scala code to work. For example, " + "`['3rdparty/jvm:scalapb-runtime']`. These dependencies will " + "be automatically added to every `protobuf_sources` target. At the very least, " + "this option must be set to a `jvm_artifact` for the " + f"`com.thesamet.scalapb:scalapb-runtime_SCALAVER:{cls.default_version}` runtime library." + ), + ) + _jvm_plugins = StrListOption( + "--jvm-plugins", + help=( + "A list of JVM-based `protoc` plugins to invoke when generating Scala code from protobuf files. " + "The format for each plugin specifier is `NAME=ARTIFACT` where NAME is the name of the " + "plugin and ARTIFACT is either the address of a `jvm_artifact` target or the colon-separated " + "Maven coordinate for the plugin's jar artifact.\n\n" + "For example, to invoke the fs2-grpc protoc plugin, the following option would work: " + "`--scalapb-jvm-plugins=fs2=org.typelevel:fs2-grpc-codegen_2.12:2.3.1`. " + "(Note: you would also need to set --scalapb-runtime-dependencies appropriately " + "to include the applicable runtime libraries for your chosen protoc plugins.)" + ), + ) @property def runtime_dependencies(self) -> UnparsedAddressInputs: - return UnparsedAddressInputs(self.options.runtime_dependencies, owning_address=None) + return UnparsedAddressInputs(self._runtime_dependencies, owning_address=None) @property def jvm_plugins(self) -> tuple[PluginArtifactSpec, ...]: - return tuple(PluginArtifactSpec.from_str(pa_str) for pa_str in self.options.jvm_plugins) + return tuple(PluginArtifactSpec.from_str(pa_str) for pa_str in self._jvm_plugins) diff --git a/src/python/pants/backend/docker/lint/hadolint/subsystem.py b/src/python/pants/backend/docker/lint/hadolint/subsystem.py index 29aaf117e5b..7ee7f1550bd 100644 --- a/src/python/pants/backend/docker/lint/hadolint/subsystem.py +++ b/src/python/pants/backend/docker/lint/hadolint/subsystem.py @@ -3,11 +3,9 @@ from __future__ import annotations -from typing import cast - from pants.core.util_rules.config_files import ConfigFilesRequest from pants.core.util_rules.external_tool import TemplatedExternalTool -from pants.option.custom_types import file_option, shell_str +from pants.option.option_types import ArgsListOption, BoolOption, FileOption from pants.util.docutil import bin_name @@ -33,59 +31,36 @@ class Hadolint(TemplatedExternalTool): "linux_x86_64": "Linux-x86_64", } - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--skip", - type=bool, - default=False, - help=f"Don't use Hadolint when running `{bin_name()} lint`.", - ) - register( - "--args", - type=list, - member_type=shell_str, - help=( - "Arguments to pass directly to Hadolint, e.g. `--hadolint-args='--format json'`.'" - ), - ) - register( - "--config", - type=file_option, - default=None, - advanced=True, - help=( - "Path to an YAML config file understood by Hadolint " - "(https://github.com/hadolint/hadolint#configure).\n\n" - f"Setting this option will disable `[{cls.options_scope}].config_discovery`. Use " - "this option if the config is located in a non-standard location." - ), - ) - register( - "--config-discovery", - type=bool, - default=True, - advanced=True, - help=( - "If true, Pants will include all relevant config files during runs " - "(`.hadolint.yaml` and `.hadolint.yml`).\n\n" - f"Use `[{cls.options_scope}].config` instead if your config is in a " - "non-standard location." - ), - ) - - @property - def skip(self) -> bool: - return cast(bool, self.options.skip) - - @property - def args(self) -> tuple[str, ...]: - return tuple(self.options.args) - - @property - def config(self) -> str | None: - return cast("str | None", self.options.config) + skip = BoolOption( + "--skip", + default=False, + help=f"Don't use Hadolint when running `{bin_name()} lint`.", + ) + args = ArgsListOption( + help=("Arguments to pass directly to Hadolint, e.g. `--hadolint-args='--format json'`.'"), + ) + config = FileOption( + "--config", + default=None, + advanced=True, + help=lambda cls: ( + "Path to an YAML config file understood by Hadolint " + "(https://github.com/hadolint/hadolint#configure).\n\n" + f"Setting this option will disable `[{cls.options_scope}].config_discovery`. Use " + "this option if the config is located in a non-standard location." + ), + ) + config_discovery = BoolOption( + "--config-discovery", + default=True, + advanced=True, + help=lambda cls: ( + "If true, Pants will include all relevant config files during runs " + "(`.hadolint.yaml` and `.hadolint.yml`).\n\n" + f"Use `[{cls.options_scope}].config` instead if your config is in a " + "non-standard location." + ), + ) def config_request(self) -> ConfigFilesRequest: # Refer to https://github.com/hadolint/hadolint#configure for how config files are @@ -93,6 +68,6 @@ def config_request(self) -> ConfigFilesRequest: return ConfigFilesRequest( specified=self.config, specified_option_name=f"[{self.options_scope}].config", - discovery=cast(bool, self.options.config_discovery), + discovery=self.config_discovery, check_existence=[".hadolint.yaml", ".hadolint.yml"], ) diff --git a/src/python/pants/backend/java/lint/google_java_format/subsystem.py b/src/python/pants/backend/java/lint/google_java_format/subsystem.py index 41477930998..a0a87d07f55 100644 --- a/src/python/pants/backend/java/lint/google_java_format/subsystem.py +++ b/src/python/pants/backend/java/lint/google_java_format/subsystem.py @@ -1,8 +1,8 @@ # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -from typing import cast from pants.jvm.resolve.jvm_tool import JvmToolBase +from pants.option.option_types import BoolOption from pants.util.docutil import bin_name, git_url @@ -19,29 +19,16 @@ class GoogleJavaFormatSubsystem(JvmToolBase): default_lockfile_path = "src/python/pants/backend/java/lint/google_java_format/google_java_format.default.lockfile.txt" default_lockfile_url = git_url(default_lockfile_path) - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--skip", - type=bool, - default=False, - help=f"Don't use Google Java Format when running `{bin_name()} fmt` and `{bin_name()} lint`", - ) - register( - "--aosp", - type=bool, - default=False, - help=( - "Use AOSP style instead of Google Style (4-space indentation). " - '("AOSP" is the Android Open Source Project.)' - ), - ) - - @property - def skip(self) -> bool: - return cast(bool, self.options.skip) - - @property - def aosp(self) -> bool: - return cast(bool, self.options.aosp) + skip = BoolOption( + "--skip", + default=False, + help=f"Don't use Google Java Format when running `{bin_name()} fmt` and `{bin_name()} lint`", + ) + aosp = BoolOption( + "--aosp", + default=False, + help=( + "Use AOSP style instead of Google Style (4-space indentation). " + '("AOSP" is the Android Open Source Project.)' + ), + ) diff --git a/src/python/pants/backend/java/subsystems/junit.py b/src/python/pants/backend/java/subsystems/junit.py index 71030863e9a..ce5c476061e 100644 --- a/src/python/pants/backend/java/subsystems/junit.py +++ b/src/python/pants/backend/java/subsystems/junit.py @@ -2,7 +2,7 @@ # Licensed under the Apache License, Version 2.0 (see LICENSE). from pants.jvm.resolve.jvm_tool import JvmToolBase -from pants.option.custom_types import shell_str +from pants.option.option_types import ArgsListOption from pants.util.docutil import git_url @@ -20,14 +20,7 @@ class JUnit(JvmToolBase): default_lockfile_path = "src/python/pants/jvm/test/junit.default.lockfile.txt" default_lockfile_url = git_url(default_lockfile_path) - @classmethod - def register_options(cls, register): - super().register_options(register) - - register( - "--args", - type=list, - member_type=shell_str, - passthrough=True, - help="Arguments to pass directly to JUnit, e.g. `--disable-ansi-colors`", - ) + args = ArgsListOption( + passthrough=True, + help="Arguments to pass directly to JUnit, e.g. `--disable-ansi-colors`", + ) diff --git a/src/python/pants/backend/project_info/count_loc.py b/src/python/pants/backend/project_info/count_loc.py index 2cd3941eaa0..df7831f77c1 100644 --- a/src/python/pants/backend/project_info/count_loc.py +++ b/src/python/pants/backend/project_info/count_loc.py @@ -1,7 +1,6 @@ # Copyright 2019 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -from typing import Tuple from pants.core.util_rules.external_tool import ( DownloadedExternalTool, @@ -14,7 +13,7 @@ from pants.engine.platform import Platform from pants.engine.process import Process, ProcessResult from pants.engine.rules import Get, collect_rules, goal_rule -from pants.option.custom_types import shell_str +from pants.option.option_types import ArgsListOption from pants.util.logging import LogLevel from pants.util.strutil import pluralize @@ -40,23 +39,13 @@ class SuccinctCodeCounter(TemplatedExternalTool): "linux_x86_64": "x86_64-unknown-linux", } - @classmethod - def register_options(cls, register) -> None: - super().register_options(register) - register( - "--args", - type=list, - member_type=shell_str, - passthrough=True, - help=( - 'Arguments to pass directly to SCC, e.g. `--count-loc-args="--no-cocomo"`. Refer ' - "to https://github.com/boyter/scc." - ), - ) - - @property - def args(self) -> Tuple[str, ...]: - return tuple(self.options.args) + args = ArgsListOption( + passthrough=True, + help=( + 'Arguments to pass directly to SCC, e.g. `--count-loc-args="--no-cocomo"`. Refer ' + "to https://github.com/boyter/scc." + ), + ) def generate_exe(self, _: Platform) -> str: return "./scc" diff --git a/src/python/pants/backend/project_info/dependees.py b/src/python/pants/backend/project_info/dependees.py index 9467b8720d5..90d811dfe94 100644 --- a/src/python/pants/backend/project_info/dependees.py +++ b/src/python/pants/backend/project_info/dependees.py @@ -3,7 +3,7 @@ from collections import defaultdict from dataclasses import dataclass -from typing import Iterable, Set, cast +from typing import Iterable, Set from pants.engine.addresses import Address, Addresses from pants.engine.collection import DeduplicatedCollection @@ -11,6 +11,7 @@ from pants.engine.goal import Goal, GoalSubsystem, LineOriented from pants.engine.rules import Get, MultiGet, collect_rules, goal_rule, rule from pants.engine.target import AllUnexpandedTargets, Dependencies, DependenciesRequest +from pants.option.option_types import BoolOption from pants.util.frozendict import FrozenDict from pants.util.logging import LogLevel from pants.util.meta import frozen_after_init @@ -85,29 +86,16 @@ class DependeesSubsystem(LineOriented, GoalSubsystem): name = "dependees" help = "List all targets that depend on any of the input files/targets." - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--transitive", - default=False, - type=bool, - help="List all transitive dependees. If unspecified, list direct dependees only.", - ) - register( - "--closed", - type=bool, - default=False, - help="Include the input targets in the output, along with the dependees.", - ) - - @property - def transitive(self) -> bool: - return cast(bool, self.options.transitive) - - @property - def closed(self) -> bool: - return cast(bool, self.options.closed) + transitive = BoolOption( + "--transitive", + default=False, + help="List all transitive dependees. If unspecified, list direct dependees only.", + ) + closed = BoolOption( + "--closed", + default=False, + help="Include the input targets in the output, along with the dependees.", + ) class DependeesGoal(Goal): diff --git a/src/python/pants/backend/project_info/dependencies.py b/src/python/pants/backend/project_info/dependencies.py index d11cef29523..d39ba46d637 100644 --- a/src/python/pants/backend/project_info/dependencies.py +++ b/src/python/pants/backend/project_info/dependencies.py @@ -2,7 +2,6 @@ # Licensed under the Apache License, Version 2.0 (see LICENSE). import itertools -from typing import cast from pants.engine.addresses import Addresses from pants.engine.console import Console @@ -16,37 +15,23 @@ TransitiveTargetsRequest, UnexpandedTargets, ) +from pants.option.option_types import BoolOption class DependenciesSubsystem(LineOriented, GoalSubsystem): name = "dependencies" help = "List the dependencies of the input files/targets." - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--transitive", - default=False, - type=bool, - help=( - "List all transitive dependencies. If unspecified, list direct dependencies only." - ), - ) - register( - "--closed", - type=bool, - default=False, - help="Include the input targets in the output, along with the dependencies.", - ) - - @property - def transitive(self) -> bool: - return cast(bool, self.options.transitive) - - @property - def closed(self) -> bool: - return cast(bool, self.options.closed) + transitive = BoolOption( + "--transitive", + default=False, + help=("List all transitive dependencies. If unspecified, list direct dependencies only."), + ) + closed = BoolOption( + "--closed", + default=False, + help="Include the input targets in the output, along with the dependencies.", + ) class Dependencies(Goal): diff --git a/src/python/pants/backend/project_info/filedeps.py b/src/python/pants/backend/project_info/filedeps.py index 0355d67b253..c718535adc9 100644 --- a/src/python/pants/backend/project_info/filedeps.py +++ b/src/python/pants/backend/project_info/filedeps.py @@ -3,7 +3,7 @@ import itertools from pathlib import PurePath -from typing import Iterable, cast +from typing import Iterable from pants.base.build_root import BuildRoot from pants.engine.addresses import Address, Addresses, BuildFileAddress @@ -19,54 +19,37 @@ TransitiveTargetsRequest, UnexpandedTargets, ) +from pants.option.option_types import BoolOption class FiledepsSubsystem(LineOriented, GoalSubsystem): name = "filedeps" help = "List all source and BUILD files a target depends on." - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--absolute", - type=bool, - default=False, - help=( - "If True, output with absolute path. If unspecified, output with path relative to " - "the build root." - ), - ) - register( - "--globs", - type=bool, - default=False, - help=( - "Instead of outputting filenames, output the original globs used in the BUILD " - "file. This will not include exclude globs (i.e. globs that start with `!`)." - ), - ) - register( - "--transitive", - type=bool, - default=False, - help=( - "If True, list files from all dependencies, including transitive dependencies. If " - "unspecified, only list files from the target." - ), - ) - - @property - def absolute(self) -> bool: - return cast(bool, self.options.absolute) - - @property - def globs(self) -> bool: - return cast(bool, self.options.globs) - - @property - def transitive(self) -> bool: - return cast(bool, self.options.transitive) + absolute = BoolOption( + "--absolute", + default=False, + help=( + "If True, output with absolute path. If unspecified, output with path relative to " + "the build root." + ), + ) + globs = BoolOption( + "--globs", + default=False, + help=( + "Instead of outputting filenames, output the original globs used in the BUILD " + "file. This will not include exclude globs (i.e. globs that start with `!`)." + ), + ) + transitive = BoolOption( + "--transitive", + default=False, + help=( + "If True, list files from all dependencies, including transitive dependencies. If " + "unspecified, only list files from the target." + ), + ) class Filedeps(Goal): diff --git a/src/python/pants/backend/project_info/filter_targets.py b/src/python/pants/backend/project_info/filter_targets.py index 1593ad729b7..ce442f4b2fe 100644 --- a/src/python/pants/backend/project_info/filter_targets.py +++ b/src/python/pants/backend/project_info/filter_targets.py @@ -18,6 +18,7 @@ UnexpandedTargets, UnrecognizedTargetTypeException, ) +from pants.option.option_types import EnumOption, StrListOption from pants.util.enums import match from pants.util.filtering import and_filters, create_filters from pants.util.memo import memoized @@ -41,36 +42,29 @@ class FilterSubsystem(LineOriented, GoalSubsystem): "them." ) - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--target-type", - type=list, - metavar="[+-]type1,type2,...", - help="Filter on these target types, e.g. `resources` or `python_sources`.", - ) - register( - "--granularity", - type=TargetGranularity, - default=TargetGranularity.all_targets, - help=( - "Filter to rendering only targets declared in BUILD files, only file-level " - "targets, or all targets." - ), - ) - register( - "--address-regex", - type=list, - metavar="[+-]regex1,regex2,...", - help="Filter on target addresses matching these regexes.", - ) - register( - "--tag-regex", - type=list, - metavar="[+-]regex1,regex2,...", - help="Filter on targets with tags matching these regexes.", - ) + target_type = StrListOption( + "--target-type", + metavar="[+-]type1,type2,...", + help="Filter on these target types, e.g. `resources` or `python_sources`.", + ) + granularity = EnumOption( + "--granularity", + default=TargetGranularity.all_targets, + help=( + "Filter to rendering only targets declared in BUILD files, only file-level " + "targets, or all targets." + ), + ) + address_regex = StrListOption( + "--address-regex", + metavar="[+-]regex1,regex2,...", + help="Filter on target addresses matching these regexes.", + ) + tag_regex = StrListOption( + "--tag-regex", + metavar="[+-]regex1,regex2,...", + help="Filter on targets with tags matching these regexes.", + ) class FilterGoal(Goal): diff --git a/src/python/pants/backend/project_info/list_targets.py b/src/python/pants/backend/project_info/list_targets.py index 614617e530e..2acd5a1a270 100644 --- a/src/python/pants/backend/project_info/list_targets.py +++ b/src/python/pants/backend/project_info/list_targets.py @@ -9,6 +9,7 @@ from pants.engine.goal import Goal, GoalSubsystem, LineOriented from pants.engine.rules import Get, collect_rules, goal_rule from pants.engine.target import DescriptionField, UnexpandedTargets +from pants.option.option_types import BoolOption logger = logging.getLogger(__name__) @@ -17,19 +18,11 @@ class ListSubsystem(LineOriented, GoalSubsystem): name = "list" help = "Lists all targets matching the file or target arguments." - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--documented", - type=bool, - default=False, - help="Print only targets that are documented with a description.", - ) - - @property - def documented(self) -> bool: - return cast(bool, self.options.documented) + documented = BoolOption( + "--documented", + default=False, + help="Print only targets that are documented with a description.", + ) class List(Goal): diff --git a/src/python/pants/backend/project_info/peek.py b/src/python/pants/backend/project_info/peek.py index 2cfdb7b1a7d..a20ff1dab08 100644 --- a/src/python/pants/backend/project_info/peek.py +++ b/src/python/pants/backend/project_info/peek.py @@ -6,7 +6,7 @@ import collections import json from dataclasses import asdict, dataclass, is_dataclass -from typing import Any, Iterable, cast +from typing import Any, Iterable from pants.engine.collection import Collection from pants.engine.console import Console @@ -22,6 +22,7 @@ Targets, UnexpandedTargets, ) +from pants.option.option_types import BoolOption class PeekSubsystem(Outputting, GoalSubsystem): @@ -30,19 +31,11 @@ class PeekSubsystem(Outputting, GoalSubsystem): name = "peek" help = "Display BUILD target info" - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--exclude-defaults", - type=bool, - default=False, - help="Whether to leave off values that match the target-defined default values.", - ) - - @property - def exclude_defaults(self) -> bool: - return cast(bool, self.options.exclude_defaults) + exclude_defaults = BoolOption( + "--exclude-defaults", + default=False, + help="Whether to leave off values that match the target-defined default values.", + ) class Peek(Goal): diff --git a/src/python/pants/backend/scala/lint/scalafmt/subsystem.py b/src/python/pants/backend/scala/lint/scalafmt/subsystem.py index 069e753a5e2..49168d0243b 100644 --- a/src/python/pants/backend/scala/lint/scalafmt/subsystem.py +++ b/src/python/pants/backend/scala/lint/scalafmt/subsystem.py @@ -1,8 +1,8 @@ # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md). # Licensed under the Apache License, Version 2.0 (see LICENSE). -from typing import cast from pants.jvm.resolve.jvm_tool import JvmToolBase +from pants.option.option_types import BoolOption from pants.util.docutil import bin_name, git_url @@ -21,16 +21,8 @@ class ScalafmtSubsystem(JvmToolBase): ) default_lockfile_url = git_url(default_lockfile_path) - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--skip", - type=bool, - default=False, - help=f"Don't use `scalafmt` when running `{bin_name()} fmt` and `{bin_name()} lint`", - ) - - @property - def skip(self) -> bool: - return cast(bool, self.options.skip) + skip = BoolOption( + "--skip", + default=False, + help=f"Don't use `scalafmt` when running `{bin_name()} fmt` and `{bin_name()} lint`", + ) diff --git a/src/python/pants/backend/scala/subsystems/scalatest.py b/src/python/pants/backend/scala/subsystems/scalatest.py index 354366ba989..d6396836350 100644 --- a/src/python/pants/backend/scala/subsystems/scalatest.py +++ b/src/python/pants/backend/scala/subsystems/scalatest.py @@ -2,7 +2,7 @@ # Licensed under the Apache License, Version 2.0 (see LICENSE). from pants.jvm.resolve.jvm_tool import JvmToolBase -from pants.option.custom_types import shell_str +from pants.option.option_types import ArgsListOption from pants.util.docutil import git_url @@ -18,17 +18,10 @@ class Scalatest(JvmToolBase): ) default_lockfile_url = git_url(default_lockfile_path) - @classmethod - def register_options(cls, register): - super().register_options(register) - - register( - "--args", - type=list, - member_type=shell_str, - passthrough=True, - help=( - "Arguments to pass directly to Scalatest, e.g. `-t $testname`. See " - "https://www.scalatest.org/user_guide/using_the_runner for supported arguments." - ), - ) + args = ArgsListOption( + passthrough=True, + help=( + "Arguments to pass directly to Scalatest, e.g. `-t $testname`. See " + "https://www.scalatest.org/user_guide/using_the_runner for supported arguments." + ), + ) diff --git a/src/python/pants/backend/shell/lint/shellcheck/subsystem.py b/src/python/pants/backend/shell/lint/shellcheck/subsystem.py index 8963c5da652..add0626eaef 100644 --- a/src/python/pants/backend/shell/lint/shellcheck/subsystem.py +++ b/src/python/pants/backend/shell/lint/shellcheck/subsystem.py @@ -4,12 +4,12 @@ from __future__ import annotations import os.path -from typing import Iterable, cast +from typing import Iterable from pants.core.util_rules.config_files import ConfigFilesRequest from pants.core.util_rules.external_tool import TemplatedExternalTool from pants.engine.platform import Platform -from pants.option.custom_types import shell_str +from pants.option.option_types import ArgsListOption, BoolOption from pants.util.docutil import bin_name @@ -36,46 +36,28 @@ class Shellcheck(TemplatedExternalTool): "linux_x86_64": "linux.x86_64", } - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--skip", - type=bool, - default=False, - help=f"Don't use Shellcheck when running `{bin_name()} lint`.", - ) - register( - "--args", - type=list, - member_type=shell_str, - help=( - "Arguments to pass directly to Shellcheck, e.g. `--shellcheck-args='-e SC20529'`.'" - ), - ) - register( - "--config-discovery", - type=bool, - default=True, - advanced=True, - help=( - "If true, Pants will include all relevant `.shellcheckrc` and `shellcheckrc` files " - "during runs. See https://www.mankier.com/1/shellcheck#RC_Files for where these " - "can be located." - ), - ) + skip = BoolOption( + "--skip", + default=False, + help=f"Don't use Shellcheck when running `{bin_name()} lint`.", + ) + args = ArgsListOption( + help=("Arguments to pass directly to Shellcheck, e.g. `--shellcheck-args='-e SC20529'`.'"), + ) + config_discovery = BoolOption( + "--config-discovery", + default=True, + advanced=True, + help=( + "If true, Pants will include all relevant `.shellcheckrc` and `shellcheckrc` files " + "during runs. See https://www.mankier.com/1/shellcheck#RC_Files for where these " + "can be located." + ), + ) def generate_exe(self, _: Platform) -> str: return f"./shellcheck-{self.version}/shellcheck" - @property - def skip(self) -> bool: - return cast(bool, self.options.skip) - - @property - def args(self) -> tuple[str, ...]: - return tuple(self.options.args) - def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: # Refer to https://www.mankier.com/1/shellcheck#RC_Files for how config files are # discovered. @@ -83,6 +65,4 @@ def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: for d in ("", *dirs): candidates.append(os.path.join(d, ".shellcheckrc")) candidates.append(os.path.join(d, "shellcheckrc")) - return ConfigFilesRequest( - discovery=cast(bool, self.options.config_discovery), check_existence=candidates - ) + return ConfigFilesRequest(discovery=self.config_discovery, check_existence=candidates) diff --git a/src/python/pants/backend/shell/lint/shfmt/subsystem.py b/src/python/pants/backend/shell/lint/shfmt/subsystem.py index d7d1eaeb9b5..a4fc31a15b0 100644 --- a/src/python/pants/backend/shell/lint/shfmt/subsystem.py +++ b/src/python/pants/backend/shell/lint/shfmt/subsystem.py @@ -4,12 +4,12 @@ from __future__ import annotations import os.path -from typing import Iterable, cast +from typing import Iterable from pants.core.util_rules.config_files import ConfigFilesRequest from pants.core.util_rules.external_tool import TemplatedExternalTool from pants.engine.platform import Platform -from pants.option.custom_types import shell_str +from pants.option.option_types import ArgsListOption, BoolOption from pants.util.docutil import bin_name @@ -35,48 +35,32 @@ class Shfmt(TemplatedExternalTool): "linux_x86_64": "linux_amd64", } - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--skip", - type=bool, - default=False, - help=f"Don't use shfmt when running `{bin_name()} fmt` and `{bin_name()} lint`.", - ) - register( - "--args", - type=list, - member_type=shell_str, - help="Arguments to pass directly to shfmt, e.g. `--shfmt-args='-i 2'`.'", - ) - register( - "--config-discovery", - type=bool, - default=True, - advanced=True, - help=( - "If true, Pants will include all relevant `.editorconfig` files during runs. " - "See https://editorconfig.org." - ), - ) + skip = BoolOption( + "--skip", + default=False, + help=f"Don't use shfmt when running `{bin_name()} fmt` and `{bin_name()} lint`.", + ) + args = ArgsListOption( + help="Arguments to pass directly to shfmt, e.g. `--shfmt-args='-i 2'`.'", + ) + config_discovery = BoolOption( + "--config-discovery", + default=True, + advanced=True, + help=( + "If true, Pants will include all relevant `.editorconfig` files during runs. " + "See https://editorconfig.org." + ), + ) def generate_exe(self, plat: Platform) -> str: plat_str = self.default_url_platform_mapping[plat.value] return f"./shfmt_{self.version}_{plat_str}" - @property - def skip(self) -> bool: - return cast(bool, self.options.skip) - - @property - def args(self) -> tuple[str, ...]: - return tuple(self.options.args) - def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest: # Refer to https://editorconfig.org/#file-location for how config files are discovered. candidates = (os.path.join(d, ".editorconfig") for d in ("", *dirs)) return ConfigFilesRequest( - discovery=cast(bool, self.options.config_discovery), + discovery=self.config_discovery, check_content={fp: b"[*.sh]" for fp in candidates}, ) diff --git a/src/python/pants/engine/goal.py b/src/python/pants/engine/goal.py index 8d7d4eca4e0..90c9c1832da 100644 --- a/src/python/pants/engine/goal.py +++ b/src/python/pants/engine/goal.py @@ -9,6 +9,7 @@ from typing_extensions import final from pants.engine.unions import UnionMembership +from pants.option.option_types import StrOption from pants.option.scope import ScopeInfo from pants.option.subsystem import Subsystem from pants.util.meta import classproperty @@ -95,14 +96,12 @@ class Outputting: Useful for goals whose purpose is to emit output to the end user (as distinct from incidental logging to stderr). """ - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--output-file", - metavar="", - help="Output the goal's stdout to this file. If unspecified, outputs to stdout.", - ) + output_file = StrOption( + "--output-file", + default=None, + metavar="", + help="Output the goal's stdout to this file. If unspecified, outputs to stdout.", + ) @final @contextmanager @@ -132,15 +131,12 @@ def output_sink(self, console: "Console") -> Iterator: class LineOriented(Outputting): - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--sep", - default="\\n", - metavar="", - help="String to use to separate lines in line-oriented output.", - ) + sep = StrOption( + "--sep", + default="\\n", + metavar="", + help="String to use to separate lines in line-oriented output.", + ) @final @contextmanager diff --git a/src/python/pants/jvm/resolve/coursier_setup.py b/src/python/pants/jvm/resolve/coursier_setup.py index 18c287f1dcf..1847ca03934 100644 --- a/src/python/pants/jvm/resolve/coursier_setup.py +++ b/src/python/pants/jvm/resolve/coursier_setup.py @@ -21,6 +21,7 @@ from pants.engine.platform import Platform from pants.engine.process import BashBinary, Process from pants.engine.rules import Get, MultiGet, collect_rules, rule +from pants.option.option_types import StrListOption from pants.util.logging import LogLevel from pants.util.memo import memoized_property from pants.util.ordered_set import FrozenOrderedSet @@ -122,25 +123,20 @@ class CoursierSubsystem(TemplatedExternalTool): "linux_x86_64": "x86_64-pc-linux", } - @classmethod - def register_options(cls, register) -> None: - super().register_options(register) - register( - "--repos", - type=list, - member_type=str, - default=[ - "https://maven-central.storage-download.googleapis.com/maven2", - "https://repo1.maven.org/maven2", - ], - help=( - "Maven style repositories to resolve artifacts from." - "\n\n" - "Coursier will resolve these repositories in the order in which they are " - "specifed, and re-ordering repositories will cause artifacts to be " - "re-downloaded. This can result in artifacts in lockfiles becoming invalid." - ), - ) + repos = StrListOption( + "--repos", + default=[ + "https://maven-central.storage-download.googleapis.com/maven2", + "https://repo1.maven.org/maven2", + ], + help=( + "Maven style repositories to resolve artifacts from." + "\n\n" + "Coursier will resolve these repositories in the order in which they are " + "specifed, and re-ordering repositories will cause artifacts to be " + "re-downloaded. This can result in artifacts in lockfiles becoming invalid." + ), + ) def generate_exe(self, plat: Platform) -> str: archive_filename = os.path.basename(self.generate_url(plat)) diff --git a/src/python/pants/jvm/resolve/jvm_tool.py b/src/python/pants/jvm/resolve/jvm_tool.py index d7ad257ddde..ce83406bed2 100644 --- a/src/python/pants/jvm/resolve/jvm_tool.py +++ b/src/python/pants/jvm/resolve/jvm_tool.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import ClassVar, cast +from typing import ClassVar from pants.build_graph.address import Address, AddressInput from pants.core.goals.generate_lockfiles import DEFAULT_TOOL_LOCKFILE @@ -20,6 +20,7 @@ GatherJvmCoordinatesRequest, ) from pants.jvm.target_types import JvmArtifactFieldSet +from pants.option.option_types import StrListOption, StrOption from pants.option.subsystem import Subsystem from pants.util.docutil import bin_name from pants.util.ordered_set import FrozenOrderedSet @@ -40,61 +41,45 @@ class JvmToolBase(Subsystem): default_lockfile_url: ClassVar[str | None] = None - @classmethod - def register_options(cls, register): - super().register_options(register) - register( - "--version", - type=str, - advanced=True, - default=cls.default_version, - help=( - "Version string for the tool. This is available for substitution in the " - f"`[{cls.options_scope}].artifacts` option by including the string " - "`{version}`." - ), - ) - register( - "--artifacts", - type=list, - member_type=str, - advanced=True, - default=list(cls.default_artifacts), - help=( - "Artifact requirements for this tool using specified as either the address of a `jvm_artifact` " - "target or, alternatively, as a colon-separated Maven coordinates (e.g., group:name:version). " - "For Maven coordinates, the string `{version}` version will be substituted with the value of the " - f"`[{cls.options_scope}].version` option." - ), - ) - register( - "--lockfile", - type=str, - default=DEFAULT_TOOL_LOCKFILE, - advanced=True, - help=( - "Path to a lockfile used for installing the tool.\n\n" - f"Set to the string `{DEFAULT_TOOL_LOCKFILE}` to use a lockfile provided by " - "Pants, so long as you have not changed the `--version` option. " - f"See {cls.default_lockfile_url} for the default lockfile contents.\n\n" - "To use a custom lockfile, set this option to a file path relative to the " - f"build root, then run `{bin_name()} jvm-generate-lockfiles " - f"--resolve={cls.options_scope}`.\n\n" - ), - ) - - @property - def version(self) -> str: - return cast(str, self.options.version) + version = StrOption( + "--version", + advanced=True, + default=lambda cls: cls.default_version, + help=lambda cls: ( + "Version string for the tool. This is available for substitution in the " + f"`[{cls.options_scope}].artifacts` option by including the string " + "`{version}`." + ), + ) + artifacts = StrListOption( + "--artifacts", + advanced=True, + default=lambda cls: list(cls.default_artifacts), + help=lambda cls: ( + "Artifact requirements for this tool using specified as either the address of a `jvm_artifact` " + "target or, alternatively, as a colon-separated Maven coordinates (e.g., group:name:version). " + "For Maven coordinates, the string `{version}` version will be substituted with the value of the " + f"`[{cls.options_scope}].version` option." + ), + ) + lockfile = StrOption( + "--lockfile", + default=DEFAULT_TOOL_LOCKFILE, + advanced=True, + help=lambda cls: ( + "Path to a lockfile used for installing the tool.\n\n" + f"Set to the string `{DEFAULT_TOOL_LOCKFILE}` to use a lockfile provided by " + "Pants, so long as you have not changed the `--version` option. " + f"See {cls.default_lockfile_url} for the default lockfile contents.\n\n" + "To use a custom lockfile, set this option to a file path relative to the " + f"build root, then run `{bin_name()} jvm-generate-lockfiles " + f"--resolve={cls.options_scope}`.\n\n" + ), + ) @property def artifact_inputs(self) -> tuple[str, ...]: - return tuple(s.format(version=self.version) for s in self.options.artifacts) - - @property - def lockfile(self) -> str: - f"""The path to a lockfile or special string '{DEFAULT_TOOL_LOCKFILE}'.""" - return cast(str, self.options.lockfile) + return tuple(s.format(version=self.version) for s in self.artifacts) @rule diff --git a/testprojects/pants-plugins/src/python/test_pants_plugin/register.py b/testprojects/pants-plugins/src/python/test_pants_plugin/register.py index dd6fc068a17..275d2f3c373 100644 --- a/testprojects/pants-plugins/src/python/test_pants_plugin/register.py +++ b/testprojects/pants-plugins/src/python/test_pants_plugin/register.py @@ -6,6 +6,7 @@ from pants.engine.goal import Goal, GoalSubsystem from pants.engine.rules import collect_rules, goal_rule from pants.option.custom_types import file_option +from pants.option.option_types import FileOption class LifecycleStubsSubsystem(GoalSubsystem): @@ -13,12 +14,8 @@ class LifecycleStubsSubsystem(GoalSubsystem): name = "lifecycle-stub-goal" help = """Configure workflows for lifecycle tests (Pants stopping and starting).""" - @classmethod - def register_options(cls, register): - super().register_options(register) - register( + new_interactive_stream_output_file = FileOption( "--new-interactive-stream-output-file", - type=file_option, default=None, help="Redirect interactive output into a separate file.", ) diff --git a/testprojects/pants-plugins/src/python/workunit_logger/register.py b/testprojects/pants-plugins/src/python/workunit_logger/register.py index f51460ee661..a691ddcd1b4 100644 --- a/testprojects/pants-plugins/src/python/workunit_logger/register.py +++ b/testprojects/pants-plugins/src/python/workunit_logger/register.py @@ -15,6 +15,7 @@ WorkunitsCallbackFactoryRequest, ) from pants.engine.unions import UnionRule +from pants.option.option_types import StrOption from pants.option.subsystem import Subsystem logger = logging.getLogger(__name__) @@ -27,9 +28,7 @@ class WorkunitsLoggerOptions(Subsystem): options_scope = "workunit-logger" help = """Example plugin that logs workunits to a file.""" - @classmethod - def register_options(cls, register): - register("--dest", type=str, help="A filename to log workunits to.") + dest = StrOption("--dest", default=None, help="A filename to log workunits to.") class WorkunitsLoggerRequest: