diff --git a/docs/markdown/Writing Plugins/common-plugin-tasks/plugin-upgrade-guide.md b/docs/markdown/Writing Plugins/common-plugin-tasks/plugin-upgrade-guide.md index 1ab1beba94f..9f48edc5919 100644 --- a/docs/markdown/Writing Plugins/common-plugin-tasks/plugin-upgrade-guide.md +++ b/docs/markdown/Writing Plugins/common-plugin-tasks/plugin-upgrade-guide.md @@ -6,6 +6,25 @@ hidden: false createdAt: "2020-10-12T16:19:01.543Z" updatedAt: "2022-07-25T20:02:17.695Z" --- + +2.16 +---- + +### `RunFieldSet` and `TestRequest` now have a `.rules()` method + +These methods should be used to register your run/test plugins: + +```python + +def rules(): + return [ + *MyRunFieldSetSubclass.rules(), + *MyTestRequestSubclass.rules(), + ] +``` + +Additionally, these types now by-default register the implementations for the rules used for `--debug`/`--debug-adapter`. If your plugin doesn't support these flags, simply remove the rules you've declared and let the default ones handle erroring. If your plugin does support these, set the class property(s) `supports_debug = True`/`supports_debug_adapter = True`, respectively. + 2.15 ---- diff --git a/docs/markdown/Writing Plugins/common-plugin-tasks/plugins-run-goal.md b/docs/markdown/Writing Plugins/common-plugin-tasks/plugins-run-goal.md index 384ba73cbf2..b5b00080979 100644 --- a/docs/markdown/Writing Plugins/common-plugin-tasks/plugins-run-goal.md +++ b/docs/markdown/Writing Plugins/common-plugin-tasks/plugins-run-goal.md @@ -72,7 +72,7 @@ from pants.engine.unions import UnionRule def rules(): return [ *collect_rules(), - UnionRule(RunFieldSet, BashRunFieldSet), + *BashRunFieldSet.rules(), ] ``` @@ -156,7 +156,32 @@ def rules(): Now, when you run `./pants run path/to/binary.sh`, Pants should run the program. -4. Add tests (optional) +4. Define `@rule`s for debugging +------------------------------------ + +`./pants run` exposes `--debug-adapter` options for debugging code. To hook into this behavior, opt-in in your `RunRequest` subclass and define an additional rule: + +```python +from pants.core.goals.run import RunDebugAdapterRequest +from pants.core.subsystems.debug_adapter import DebugAdapterSubsystem + +@dataclass(frozen=True) +class BashRunFieldSet(RunFieldSet): + ... # Fields from earlier + supports_debug_adapter = True # Supports --debug-adapter + + +@rule +async def run_bash_binary_debug_adapter( + field_set: BashRunFieldSet, + debug_adapter: DebugAdapterSubsystem, +) -> RunDebugAdapterRequest: + ... +``` + +Your rule should be configured to wait for client connection before continuing. + +5. Add tests (optional) ----------------------- Refer to [Testing rules](doc:rules-api-testing). TODO diff --git a/docs/markdown/Writing Plugins/common-plugin-tasks/plugins-test-goal.md b/docs/markdown/Writing Plugins/common-plugin-tasks/plugins-test-goal.md index b74eadfec62..88b30c850aa 100644 --- a/docs/markdown/Writing Plugins/common-plugin-tasks/plugins-test-goal.md +++ b/docs/markdown/Writing Plugins/common-plugin-tasks/plugins-test-goal.md @@ -76,18 +76,6 @@ class ExampleTestFieldSet(TestFieldSet): return tgt.get(SkipExampleTestsField).value ``` -Register your new subclass as a valid `TestFieldSet` using a `UnionRule`: - -```python -from pants.engine.unions import UnionRule - -def rules(): - return [ - # Add to any other existing rules here: - UnionRule(TestFieldSet, ExampleTestFieldSet), - ] -``` - 3. Set up a `Subsystem` for your test runner -------------------------------------------- @@ -231,10 +219,17 @@ If you didn't override the `partitioner_type` in your `TestRequest` subclass, `e 7. Define `@rule`s for debug testing ------------------------------------ -`./pants test` exposes `--debug` and `--debug-adapter` options for interactive execution of tests. To hook into these execution modes, define two additional rules: +`./pants test` exposes `--debug` and `--debug-adapter` options for interactive execution of tests. To hook into these execution modes, opt-in in your `TestRequest` subclass and define one/both additional rules: ```python from pants.core.goals.test import TestDebugAdapterRequest, TestDebugRequest +from pants.core.subsystems.debug_adapter import DebugAdapterSubsystem + +@dataclass(frozen=True) +class ExampleTestRequest(TestRequest): + ... # Fields from earlier + supports_debug = True # Supports --debug + supports_debug_adapter = True # Supports --debug-adapter @rule async def setup_example_debug_test( @@ -245,8 +240,7 @@ async def setup_example_debug_test( @rule async def setup_example_debug_adapter_test( batch: ExampleTestRequest.Batch[ExampleTestFieldSet, ExampleTestMetadata], + debug_adapter: DebugAdapterSubsystem, ) -> TestDebugAdapterRequest: ... ``` - -You _must_ define these rules to avoid rule-graph errors. If your test runner is not compatible with the Debug Adapter, or if it doesn't benefit from running in `--debug` mode, you can simply `raise` a `NotImplementedError` saying so in one/both of these rules. diff --git a/src/python/pants/backend/docker/goals/run_image.py b/src/python/pants/backend/docker/goals/run_image.py index f50d7366a01..e8ef00d7e8a 100644 --- a/src/python/pants/backend/docker/goals/run_image.py +++ b/src/python/pants/backend/docker/goals/run_image.py @@ -11,11 +11,10 @@ from pants.backend.docker.target_types import DockerImageRegistriesField, DockerImageSourceField from pants.backend.docker.util_rules.docker_binary import DockerBinary from pants.core.goals.package import BuiltPackage, PackageFieldSet -from pants.core.goals.run import RunDebugAdapterRequest, RunFieldSet, RunRequest +from pants.core.goals.run import RunFieldSet, RunRequest from pants.engine.env_vars import EnvironmentVars, EnvironmentVarsRequest from pants.engine.rules import Get, MultiGet, collect_rules, rule from pants.engine.target import WrappedTarget, WrappedTargetRequest -from pants.engine.unions import UnionRule @dataclass(frozen=True) @@ -60,17 +59,8 @@ async def docker_image_run_request( ) -@rule -async def docker_image_run_debug_adapter_request( - field_set: DockerRunFieldSet, -) -> RunDebugAdapterRequest: - raise NotImplementedError( - "Debugging a Docker image using a debug adapter has not yet been implemented." - ) - - def rules(): return [ *collect_rules(), - UnionRule(RunFieldSet, DockerRunFieldSet), + *DockerRunFieldSet.rules(), ] diff --git a/src/python/pants/backend/go/goals/run_binary.py b/src/python/pants/backend/go/goals/run_binary.py index e0552b44b4f..52db28f4aa7 100644 --- a/src/python/pants/backend/go/goals/run_binary.py +++ b/src/python/pants/backend/go/goals/run_binary.py @@ -5,10 +5,9 @@ from pants.backend.go.goals.package_binary import GoBinaryFieldSet from pants.core.goals.package import BuiltPackage, PackageFieldSet -from pants.core.goals.run import RunDebugAdapterRequest, RunFieldSet, RunRequest +from pants.core.goals.run import RunRequest from pants.engine.internals.selectors import Get from pants.engine.rules import collect_rules, rule -from pants.engine.unions import UnionRule @rule @@ -19,14 +18,8 @@ async def create_go_binary_run_request(field_set: GoBinaryFieldSet) -> RunReques return RunRequest(digest=binary.digest, args=(os.path.join("{chroot}", artifact_relpath),)) -@rule -async def go_binary_run_debug_adapter_request( - field_set: GoBinaryFieldSet, -) -> RunDebugAdapterRequest: - raise NotImplementedError( - "Debugging a Go binary using a debug adapter has not yet been implemented." - ) - - def rules(): - return [*collect_rules(), UnionRule(RunFieldSet, GoBinaryFieldSet)] + return [ + *collect_rules(), + *GoBinaryFieldSet.rules(), + ] diff --git a/src/python/pants/backend/go/goals/test.py b/src/python/pants/backend/go/goals/test.py index 922d411d1fc..8b9fb73432e 100644 --- a/src/python/pants/backend/go/goals/test.py +++ b/src/python/pants/backend/go/goals/test.py @@ -40,15 +40,7 @@ from pants.backend.go.util_rules.import_analysis import ImportConfig, ImportConfigRequest from pants.backend.go.util_rules.link import LinkedGoBinary, LinkGoBinaryRequest from pants.backend.go.util_rules.tests_analysis import GeneratedTestMain, GenerateTestMainRequest -from pants.core.goals.test import ( - TestDebugAdapterRequest, - TestDebugRequest, - TestExtraEnv, - TestFieldSet, - TestRequest, - TestResult, - TestSubsystem, -) +from pants.core.goals.test import TestExtraEnv, TestFieldSet, TestRequest, TestResult, TestSubsystem from pants.core.target_types import FileSourceField from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest from pants.engine.env_vars import EnvironmentVars, EnvironmentVarsRequest @@ -57,7 +49,6 @@ from pants.engine.process import FallibleProcessResult, Process, ProcessCacheScope from pants.engine.rules import Get, MultiGet, collect_rules, rule from pants.engine.target import Dependencies, DependenciesRequest, SourcesField, Target, Targets -from pants.engine.unions import UnionRule from pants.util.logging import LogLevel from pants.util.ordered_set import FrozenOrderedSet @@ -476,21 +467,8 @@ def compilation_failure(exit_code: int, stdout: str | None, stderr: str | None) ) -@rule -async def generate_go_tests_debug_request(_: GoTestRequest.Batch) -> TestDebugRequest: - raise NotImplementedError("This is a stub.") - - -@rule -async def generate_go_tests_debug_adapter_request( - _: GoTestRequest.Batch, -) -> TestDebugAdapterRequest: - raise NotImplementedError("This is a stub.") - - def rules(): return [ *collect_rules(), - UnionRule(TestFieldSet, GoTestFieldSet), *GoTestRequest.rules(), ] diff --git a/src/python/pants/backend/helm/test/unittest.py b/src/python/pants/backend/helm/test/unittest.py index 9c94c4e8d26..1db60cd0b76 100644 --- a/src/python/pants/backend/helm/test/unittest.py +++ b/src/python/pants/backend/helm/test/unittest.py @@ -24,14 +24,7 @@ from pants.backend.helm.util_rules.chart import HelmChart, HelmChartRequest from pants.backend.helm.util_rules.sources import HelmChartRoot, HelmChartRootRequest from pants.backend.helm.util_rules.tool import HelmProcess -from pants.core.goals.test import ( - TestDebugAdapterRequest, - TestDebugRequest, - TestFieldSet, - TestRequest, - TestResult, - TestSubsystem, -) +from pants.core.goals.test import TestFieldSet, TestRequest, TestResult, TestSubsystem from pants.core.target_types import ResourceSourceField from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest from pants.engine.addresses import Address @@ -45,7 +38,6 @@ TransitiveTargets, TransitiveTargetsRequest, ) -from pants.engine.unions import UnionRule from pants.util.logging import LogLevel logger = logging.getLogger(__name__) @@ -166,24 +158,11 @@ async def run_helm_unittest( ) -@rule -async def generate_helm_unittest_debug_request(_: HelmUnitTestRequest.Batch) -> TestDebugRequest: - raise NotImplementedError("Can not debug Helm unit tests") - - -@rule -async def generate_helm_unittest_debug_adapter_request( - _: HelmUnitTestRequest.Batch, -) -> TestDebugAdapterRequest: - raise NotImplementedError("Can not debug Helm unit tests") - - def rules(): return [ *collect_rules(), *subsystem_rules(), *dependency_rules(), *tool.rules(), - UnionRule(TestFieldSet, HelmUnitTestFieldSet), *HelmUnitTestRequest.rules(), ] diff --git a/src/python/pants/backend/python/goals/pytest_runner.py b/src/python/pants/backend/python/goals/pytest_runner.py index e1da8f50612..a791cc277a3 100644 --- a/src/python/pants/backend/python/goals/pytest_runner.py +++ b/src/python/pants/backend/python/goals/pytest_runner.py @@ -34,7 +34,6 @@ TestDebugAdapterRequest, TestDebugRequest, TestExtraEnv, - TestFieldSet, TestRequest, TestResult, TestSubsystem, @@ -430,6 +429,8 @@ class PyTestRequest(TestRequest): tool_subsystem = PyTest field_set_type = PythonTestFieldSet partitioner_type = PartitionerType.CUSTOM + supports_debug = True + supports_debug_adapter = True @rule(desc="Partition Pytest", level=LogLevel.DEBUG) @@ -595,7 +596,6 @@ def rules(): return [ *collect_rules(), *pytest.rules(), - UnionRule(TestFieldSet, PythonTestFieldSet), UnionRule(PytestPluginSetupRequest, RuntimePackagesPluginRequest), *PyTestRequest.rules(), ] diff --git a/src/python/pants/backend/python/goals/run_pex_binary.py b/src/python/pants/backend/python/goals/run_pex_binary.py index 4f6cd0fd44e..a881d9e347b 100644 --- a/src/python/pants/backend/python/goals/run_pex_binary.py +++ b/src/python/pants/backend/python/goals/run_pex_binary.py @@ -4,23 +4,15 @@ import os from pants.backend.python.goals.package_pex_binary import PexBinaryFieldSet -from pants.backend.python.subsystems.debugpy import DebugPy -from pants.backend.python.target_types import PexBinaryDefaults, PexLayout -from pants.backend.python.util_rules.pex_environment import PexEnvironment +from pants.backend.python.target_types import PexLayout from pants.core.goals.package import BuiltPackage -from pants.core.goals.run import RunDebugAdapterRequest, RunFieldSet, RunRequest -from pants.core.subsystems.debug_adapter import DebugAdapterSubsystem +from pants.core.goals.run import RunRequest from pants.engine.rules import Get, collect_rules, rule -from pants.engine.unions import UnionRule from pants.util.logging import LogLevel @rule(level=LogLevel.DEBUG) -async def create_pex_binary_run_request( - field_set: PexBinaryFieldSet, - pex_binary_defaults: PexBinaryDefaults, - pex_env: PexEnvironment, -) -> RunRequest: +async def create_pex_binary_run_request(field_set: PexBinaryFieldSet) -> RunRequest: built_pex = await Get(BuiltPackage, PexBinaryFieldSet, field_set) relpath = built_pex.artifacts[0].relpath assert relpath is not None @@ -33,18 +25,13 @@ async def create_pex_binary_run_request( ) -@rule -async def run_pex_debug_adapter_binary( - field_set: PexBinaryFieldSet, - debugpy: DebugPy, - debug_adapter: DebugAdapterSubsystem, -) -> RunDebugAdapterRequest: - # NB: Technically we could run this using `debugpy`, however it is unclear how the user - # would be able to debug the code, as the client and server will disagree on the code's path. - raise NotImplementedError( - "Debugging a `pex_binary` using a debug adapter has not yet been implemented." - ) +# NB: Technically we could implement RunDebugAdapterRequest by using `debugpy`. +# However it is unclear how the user would be able to debug the code, +# as the client and server will disagree on the code's path. def rules(): - return [*collect_rules(), UnionRule(RunFieldSet, PexBinaryFieldSet)] + return [ + *collect_rules(), + *PexBinaryFieldSet.rules(), + ] diff --git a/src/python/pants/backend/python/goals/run_python_source.py b/src/python/pants/backend/python/goals/run_python_source.py index 7520cc34edf..f45d8b2c6c3 100644 --- a/src/python/pants/backend/python/goals/run_python_source.py +++ b/src/python/pants/backend/python/goals/run_python_source.py @@ -22,12 +22,12 @@ from pants.core.subsystems.debug_adapter import DebugAdapterSubsystem from pants.engine.internals.selectors import Get from pants.engine.rules import collect_rules, rule -from pants.engine.unions import UnionRule from pants.util.logging import LogLevel @dataclass(frozen=True) class PythonSourceFieldSet(RunFieldSet): + supports_debug_adapter = True required_fields = (PythonSourceField, PythonRunGoalUseSandboxField) source: PythonSourceField @@ -91,5 +91,5 @@ async def create_python_source_debug_adapter_request( def rules(): return [ *collect_rules(), - UnionRule(RunFieldSet, PythonSourceFieldSet), + *PythonSourceFieldSet.rules(), ] diff --git a/src/python/pants/backend/python/packaging/pyoxidizer/rules.py b/src/python/pants/backend/python/packaging/pyoxidizer/rules.py index 4876dc7b3f7..73b57702961 100644 --- a/src/python/pants/backend/python/packaging/pyoxidizer/rules.py +++ b/src/python/pants/backend/python/packaging/pyoxidizer/rules.py @@ -24,7 +24,7 @@ from pants.backend.python.target_types import GenerateSetupField, WheelField from pants.backend.python.util_rules.pex import Pex, PexProcess, PexRequest from pants.core.goals.package import BuiltPackage, BuiltPackageArtifact, PackageFieldSet -from pants.core.goals.run import RunDebugAdapterRequest, RunFieldSet, RunRequest +from pants.core.goals.run import RunFieldSet, RunRequest from pants.core.util_rules.system_binaries import BashBinary from pants.engine.fs import ( AddPrefix, @@ -57,7 +57,7 @@ @dataclass(frozen=True) -class PyOxidizerFieldSet(PackageFieldSet): +class PyOxidizerFieldSet(PackageFieldSet, RunFieldSet): required_fields = (PyOxidizerDependenciesField,) binary_name: PyOxidizerBinaryNameField @@ -238,18 +238,9 @@ def is_executable_binary(artifact_relpath: str | None) -> bool: return RunRequest(digest=binary.digest, args=(os.path.join("{chroot}", artifact.relpath),)) -@rule -async def run_pyoxidizer_debug_adapter_binary( - field_set: PyOxidizerFieldSet, -) -> RunDebugAdapterRequest: - raise NotImplementedError( - "Debugging a PyOxidizer binary using a debug adapter has not yet been implemented." - ) - - def rules(): return ( *collect_rules(), UnionRule(PackageFieldSet, PyOxidizerFieldSet), - UnionRule(RunFieldSet, PyOxidizerFieldSet), + *PyOxidizerFieldSet.rules(), ) diff --git a/src/python/pants/backend/scala/test/scalatest.py b/src/python/pants/backend/scala/test/scalatest.py index b267ccb1f9e..4fd497262d2 100644 --- a/src/python/pants/backend/scala/test/scalatest.py +++ b/src/python/pants/backend/scala/test/scalatest.py @@ -14,7 +14,6 @@ ) from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel from pants.core.goals.test import ( - TestDebugAdapterRequest, TestDebugRequest, TestExtraEnv, TestFieldSet, @@ -65,6 +64,7 @@ class ScalatestTestFieldSet(TestFieldSet): class ScalatestTestRequest(TestRequest): tool_subsystem = Scalatest field_set_type = ScalatestTestFieldSet + supports_debug = True class ScalatestToolLockfileSentinel(GenerateJvmToolLockfileSentinel): @@ -204,13 +204,6 @@ async def setup_scalatest_debug_request( ) -@rule -async def setup_scalatest_debug_adapter_request( - _: ScalatestTestRequest.Batch, -) -> TestDebugAdapterRequest: - raise NotImplementedError("Debugging Scala using a debug adapter has not yet been implemented.") - - @rule def generate_scalatest_lockfile_request( _: ScalatestToolLockfileSentinel, scalatest: Scalatest @@ -222,7 +215,6 @@ def rules(): return [ *collect_rules(), *lockfile.rules(), - UnionRule(TestFieldSet, ScalatestTestFieldSet), UnionRule(GenerateToolLockfileSentinel, ScalatestToolLockfileSentinel), *ScalatestTestRequest.rules(), ] diff --git a/src/python/pants/backend/shell/goals/test.py b/src/python/pants/backend/shell/goals/test.py index 3e346320546..7eff03b33fb 100644 --- a/src/python/pants/backend/shell/goals/test.py +++ b/src/python/pants/backend/shell/goals/test.py @@ -14,20 +14,11 @@ ) from pants.backend.shell.util_rules import shell_command from pants.backend.shell.util_rules.shell_command import ShellCommandProcessFromTargetRequest -from pants.core.goals.test import ( - TestDebugAdapterRequest, - TestDebugRequest, - TestExtraEnv, - TestFieldSet, - TestRequest, - TestResult, - TestSubsystem, -) +from pants.core.goals.test import TestExtraEnv, TestFieldSet, TestRequest, TestResult, TestSubsystem from pants.engine.internals.selectors import Get from pants.engine.process import FallibleProcessResult, Process, ProcessCacheScope from pants.engine.rules import collect_rules, rule from pants.engine.target import Target, WrappedTarget, WrappedTargetRequest -from pants.engine.unions import UnionRule from pants.util.frozendict import FrozenDict from pants.util.logging import LogLevel @@ -86,22 +77,9 @@ async def test_shell_command( ) -@rule -async def generate_shell_tests_debug_request(_: ShellTestRequest.Batch) -> TestDebugRequest: - raise NotImplementedError("This is a stub.") - - -@rule -async def generate_shell_tests_debug_adapter_request( - _: ShellTestRequest.Batch, -) -> TestDebugAdapterRequest: - raise NotImplementedError("This is a stub.") - - def rules(): return ( *collect_rules(), *shell_command.rules(), *ShellTestRequest.rules(), - UnionRule(TestFieldSet, TestShellCommandFieldSet), ) diff --git a/src/python/pants/backend/shell/shunit2_test_runner.py b/src/python/pants/backend/shell/shunit2_test_runner.py index d542e74690f..5b12e3a0f4e 100644 --- a/src/python/pants/backend/shell/shunit2_test_runner.py +++ b/src/python/pants/backend/shell/shunit2_test_runner.py @@ -20,7 +20,6 @@ BuildPackageDependenciesRequest, BuiltPackageDependencies, RuntimePackageDependenciesField, - TestDebugAdapterRequest, TestDebugRequest, TestExtraEnv, TestFieldSet, @@ -48,7 +47,6 @@ ) from pants.engine.rules import Get, MultiGet, collect_rules, rule from pants.engine.target import SourcesField, Target, TransitiveTargets, TransitiveTargetsRequest -from pants.engine.unions import UnionRule from pants.option.global_options import GlobalOptions from pants.util.docutil import bin_name from pants.util.logging import LogLevel @@ -72,6 +70,7 @@ def opt_out(cls, tgt: Target) -> bool: class Shunit2TestRequest(TestRequest): tool_subsystem = Shunit2 field_set_type = Shunit2FieldSet + supports_debug = True @dataclass(frozen=True) @@ -268,16 +267,8 @@ async def setup_shunit2_debug_test( ) -@rule -async def setup_shunit2_debug_adapter_test( - _: Shunit2TestRequest.Batch[Shunit2FieldSet, Any] -) -> TestDebugAdapterRequest: - raise NotImplementedError("Debugging Shell using a debug adapter has not yet been implemented.") - - def rules(): return [ *collect_rules(), - UnionRule(TestFieldSet, Shunit2FieldSet), *Shunit2TestRequest.rules(), ] diff --git a/src/python/pants/backend/shell/util_rules/shell_command.py b/src/python/pants/backend/shell/util_rules/shell_command.py index 62388f6426c..343f80a5f9b 100644 --- a/src/python/pants/backend/shell/util_rules/shell_command.py +++ b/src/python/pants/backend/shell/util_rules/shell_command.py @@ -28,7 +28,7 @@ from pants.backend.shell.util_rules.builtin import BASH_BUILTIN_COMMANDS from pants.base.deprecated import warn_or_error from pants.core.goals.package import BuiltPackage, PackageFieldSet -from pants.core.goals.run import RunDebugAdapterRequest, RunFieldSet, RunRequest +from pants.core.goals.run import RunFieldSet, RunRequest from pants.core.target_types import FileSourceField from pants.core.util_rules.environments import EnvironmentNameRequest from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest @@ -401,18 +401,9 @@ async def run_shell_command_request(shell_command: RunShellCommand) -> RunReques ) -@rule -async def run_shell_debug_adapter_binary( - field_set: RunShellCommand, -) -> RunDebugAdapterRequest: - raise NotImplementedError( - "Debugging a shell command using a debug adapter has not yet been implemented." - ) - - def rules(): return [ *collect_rules(), UnionRule(GenerateSourcesRequest, GenerateFilesFromShellCommandRequest), - UnionRule(RunFieldSet, RunShellCommand), + *RunShellCommand.rules(), ] diff --git a/src/python/pants/core/goals/run.py b/src/python/pants/core/goals/run.py index 1a359cf0dd1..c2a3a145223 100644 --- a/src/python/pants/core/goals/run.py +++ b/src/python/pants/core/goals/run.py @@ -8,7 +8,9 @@ from abc import ABCMeta from dataclasses import dataclass from itertools import filterfalse, tee -from typing import Callable, Iterable, Mapping, NamedTuple, Optional, Tuple, TypeVar +from typing import Callable, ClassVar, Iterable, Mapping, NamedTuple, Optional, Tuple, TypeVar + +from typing_extensions import final from pants.core.subsystems.debug_adapter import DebugAdapterSubsystem from pants.core.util_rules.environments import _warn_on_non_local_environments @@ -21,7 +23,7 @@ TooManyTargetsException, ) from pants.engine.process import InteractiveProcess, InteractiveProcessResult -from pants.engine.rules import Effect, Get, collect_rules, goal_rule, rule_helper +from pants.engine.rules import Effect, Get, collect_rules, goal_rule, rule, rule_helper from pants.engine.target import ( BoolField, FieldSet, @@ -31,10 +33,11 @@ TargetRootsToFieldSets, TargetRootsToFieldSetsRequest, ) -from pants.engine.unions import UnionMembership, union +from pants.engine.unions import UnionMembership, UnionRule, union from pants.option.global_options import GlobalOptions from pants.option.option_types import ArgsListOption, BoolOption from pants.util.frozendict import FrozenDict +from pants.util.memo import memoized from pants.util.meta import frozen_after_init from pants.util.strutil import softwrap @@ -47,6 +50,15 @@ class RunFieldSet(FieldSet, metaclass=ABCMeta): """The fields necessary from a target to run a program/script.""" + supports_debug_adapter: ClassVar[bool] = False + + @final + @classmethod + def rules(cls) -> Iterable: + yield UnionRule(RunFieldSet, cls) + if not cls.supports_debug_adapter: + yield from _unsupported_debug_adapter_rules(cls) + class RestartableField(BoolField): alias = "restartable" @@ -248,5 +260,18 @@ async def run( return Run(result.exit_code) +@memoized +def _unsupported_debug_adapter_rules(cls: type[RunFieldSet]) -> Iterable: + """Returns a rule that implements DebugAdapterRequest by raising an error.""" + + @rule(_param_type_overrides={"request": cls}) + async def get_run_debug_adapter_request(request: RunFieldSet) -> RunDebugAdapterRequest: + raise NotImplementedError( + "Running this target type with a debug adapter is not yet supported." + ) + + return collect_rules(locals()) + + def rules(): return collect_rules() diff --git a/src/python/pants/core/goals/test.py b/src/python/pants/core/goals/test.py index 2cfb0858ca4..b2526370fa1 100644 --- a/src/python/pants/core/goals/test.py +++ b/src/python/pants/core/goals/test.py @@ -63,6 +63,7 @@ from pants.util.collections import partition_sequentially from pants.util.docutil import bin_name from pants.util.logging import LogLevel +from pants.util.memo import memoized from pants.util.meta import classproperty from pants.util.strutil import softwrap @@ -267,7 +268,7 @@ class TestDebugAdapterRequest(TestDebugRequest): """ -@union(in_scope_types=[EnvironmentName]) +@union @dataclass(frozen=True) class TestFieldSet(FieldSet, metaclass=ABCMeta): """The fields necessary to run tests on a target.""" @@ -304,6 +305,9 @@ def rules(): field_set_type: ClassVar[type[TestFieldSet]] partitioner_type: ClassVar[PartitionerType] = PartitionerType.DEFAULT_ONE_PARTITION_PER_INPUT + supports_debug: ClassVar[bool] = False + supports_debug_adapter: ClassVar[bool] = False + __test__ = False @classproperty @@ -358,10 +362,17 @@ def metadata(self) -> dict[str, Any]: def rules(cls) -> Iterable: yield from cls.partitioner_type.default_rules(cls, by_file=False) + yield UnionRule(TestFieldSet, cls.field_set_type) yield UnionRule(TestRequest, cls) yield UnionRule(TestRequest.PartitionRequest, cls.PartitionRequest) yield UnionRule(TestRequest.Batch, cls.Batch) + if not cls.supports_debug: + yield from _unsupported_debug_rules(cls) + + if not cls.supports_debug_adapter: + yield from _unsupported_debug_adapter_rules(cls) + class CoverageData(ABC): """Base class for inputs to a coverage report. @@ -470,7 +481,7 @@ class TestSubsystem(GoalSubsystem): @classmethod def activated(cls, union_membership: UnionMembership) -> bool: - return TestFieldSet in union_membership + return TestRequest in union_membership class EnvironmentAware: extra_env_vars = StrListOption( @@ -957,6 +968,30 @@ async def get_filtered_environment(test_env_aware: TestSubsystem.EnvironmentAwar ) +@memoized +def _unsupported_debug_rules(cls: type[TestRequest]) -> Iterable: + """Returns a rule that implements TestDebugRequest by raising an error.""" + + @rule(_param_type_overrides={"request": cls.Batch}) + async def get_test_debug_request(request: TestRequest.Batch) -> TestDebugRequest: + raise NotImplementedError("Testing this target with --debug is not yet supported.") + + return collect_rules(locals()) + + +@memoized +def _unsupported_debug_adapter_rules(cls: type[TestRequest]) -> Iterable: + """Returns a rule that implements TestDebugAdapterRequest by raising an error.""" + + @rule(_param_type_overrides={"request": cls.Batch}) + async def get_test_debug_adapter_request(request: TestRequest.Batch) -> TestDebugAdapterRequest: + raise NotImplementedError( + "Testing this target type with a debug adapter is not yet supported." + ) + + return collect_rules(locals()) + + # ------------------------------------------------------------------------------------------- # `runtime_package_dependencies` field # ------------------------------------------------------------------------------------------- diff --git a/src/python/pants/jvm/run_deploy_jar.py b/src/python/pants/jvm/run_deploy_jar.py index 1a5208137dc..37713e24f8e 100644 --- a/src/python/pants/jvm/run_deploy_jar.py +++ b/src/python/pants/jvm/run_deploy_jar.py @@ -5,10 +5,9 @@ from typing import Iterable from pants.core.goals.package import BuiltPackage -from pants.core.goals.run import RunDebugAdapterRequest, RunFieldSet, RunRequest +from pants.core.goals.run import RunRequest from pants.engine.process import Process from pants.engine.rules import Get, collect_rules, rule -from pants.engine.unions import UnionRule from pants.jvm.jdk_rules import JdkEnvironment, JdkRequest, JvmProcess from pants.jvm.package.deploy_jar import DeployJarFieldSet from pants.util.logging import LogLevel @@ -73,14 +72,8 @@ def prefixed(arg: str, prefixes: Iterable[str]) -> str: ) -@rule -async def run_deploy_jar_debug_adapter_binary( - field_set: DeployJarFieldSet, -) -> RunDebugAdapterRequest: - raise NotImplementedError( - "Debugging a deploy JAR using a debug adapter has not yet been implemented." - ) - - def rules(): - return [*collect_rules(), UnionRule(RunFieldSet, DeployJarFieldSet)] + return [ + *collect_rules(), + *DeployJarFieldSet.rules(), + ] diff --git a/src/python/pants/jvm/test/junit.py b/src/python/pants/jvm/test/junit.py index b34b4a74b38..11044c633ba 100644 --- a/src/python/pants/jvm/test/junit.py +++ b/src/python/pants/jvm/test/junit.py @@ -10,7 +10,6 @@ from pants.backend.java.subsystems.junit import JUnit from pants.core.goals.generate_lockfiles import GenerateToolLockfileSentinel from pants.core.goals.test import ( - TestDebugAdapterRequest, TestDebugRequest, TestExtraEnv, TestExtraEnvVarsField, @@ -67,6 +66,7 @@ class JunitTestFieldSet(TestFieldSet): class JunitTestRequest(TestRequest): tool_subsystem = JUnit field_set_type = JunitTestFieldSet + supports_debug = True class JunitToolLockfileSentinel(GenerateJvmToolLockfileSentinel): @@ -203,15 +203,6 @@ async def setup_junit_debug_request( ) -@rule -async def setup_junit_debug_adapter_request( - _: JunitTestRequest.Batch[JunitTestFieldSet, Any], -) -> TestDebugAdapterRequest: - raise NotImplementedError( - "Debugging JUnit tests using a debug adapter has not yet been implemented." - ) - - @rule def generate_junit_lockfile_request( _: JunitToolLockfileSentinel, junit: JUnit @@ -223,7 +214,6 @@ def rules(): return [ *collect_rules(), *lockfile.rules(), - UnionRule(TestFieldSet, JunitTestFieldSet), UnionRule(GenerateToolLockfileSentinel, JunitToolLockfileSentinel), *JunitTestRequest.rules(), ]