Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Plugin API] Add type CurrentGoals to allow rules to find out the current running goals. #17674

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/python/pants/bin/local_pants_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from pants.build_graph.build_configuration import BuildConfiguration
from pants.core.util_rules.environments import determine_bootstrap_environment
from pants.engine.env_vars import CompleteEnvironmentVars
from pants.engine.goal import CurrentExecutingGoal
from pants.engine.internals import native_engine
from pants.engine.internals.native_engine import PySessionCancellationLatch
from pants.engine.internals.scheduler import ExecutionError
Expand Down Expand Up @@ -100,6 +101,7 @@ def _init_graph_session(
{
OptionsBootstrapper: options_bootstrapper,
CompleteEnvironmentVars: env,
CurrentExecutingGoal: CurrentExecutingGoal(),
}
),
cancellation_latch=cancellation_latch,
Expand Down
23 changes: 23 additions & 0 deletions src/python/pants/engine/goal.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
from __future__ import annotations

from abc import abstractmethod
from contextlib import contextmanager
Expand All @@ -10,6 +11,7 @@
from typing_extensions import final

from pants.base.deprecated import deprecated_conditional
from pants.engine.engine_aware import EngineAwareReturnType
from pants.engine.unions import UnionMembership
from pants.option.option_types import StrOption
from pants.option.scope import ScopeInfo
Expand Down Expand Up @@ -133,6 +135,27 @@ def name(cls) -> str:
return cast(str, cls.subsystem_cls.name)


@dataclass(frozen=True)
class CurrentExecutingGoal(EngineAwareReturnType):
kaos marked this conversation as resolved.
Show resolved Hide resolved
goal: type[Goal] | None = None

@property
def name(self) -> str | None:
return None if self.goal is None else self.goal.name

@contextmanager
def _executing(self, goal: type[Goal]) -> Iterator[None]:
# Mutate current goal; we're only frozen to avoid inadvertent tampering with `self.goal`.
object.__setattr__(self, "goal", goal)
try:
yield
finally:
object.__setattr__(self, "goal", None)

def cacheable(self) -> bool:
return False


class Outputting:
"""A mixin for Goal that adds options to support output-related context managers.

Expand Down
33 changes: 30 additions & 3 deletions src/python/pants/engine/goal_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from pants.engine.console import Console
from pants.engine.goal import Goal, GoalSubsystem, LineOriented
from pants.engine.rules import goal_rule
from pants.engine.goal import CurrentExecutingGoal, Goal, GoalSubsystem, LineOriented
from pants.engine.rules import collect_rules, goal_rule
from pants.option.scope import ScopeInfo
from pants.testutil.option_util import create_goal_subsystem, create_options_bootstrapper
from pants.testutil.rule_runner import mock_console, run_rule_with_mocks
from pants.testutil.rule_runner import RuleRunner, mock_console, run_rule_with_mocks


def test_line_oriented_goal() -> None:
Expand Down Expand Up @@ -43,3 +43,30 @@ class DummyGoal(GoalSubsystem):

dummy = create_goal_subsystem(DummyGoal)
assert dummy.get_scope_info() == ScopeInfo(scope="dummy", subsystem_cls=DummyGoal, is_goal=True)


def test_current_executing_goal() -> None:
class OutputtingGoalOptions(LineOriented, GoalSubsystem):
name = "dummy"
help = "dummy help"

class OutputtingGoal(Goal):
subsystem_cls = OutputtingGoalOptions
environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY

@goal_rule
def output_rule(
console: Console,
options: OutputtingGoalOptions,
current_executing_goal: CurrentExecutingGoal,
) -> OutputtingGoal:
with options.output(console) as write_stdout:
write_stdout(f"current goal is {current_executing_goal.name!r}")
return OutputtingGoal(0)

rule_runner = RuleRunner(
rules=collect_rules(locals()),
)
result = rule_runner.run_goal_rule(OutputtingGoal)
assert result.exit_code == 0
assert result.stdout == "current goal is 'dummy'"
26 changes: 16 additions & 10 deletions src/python/pants/engine/internals/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
Snapshot,
SymlinkEntry,
)
from pants.engine.goal import Goal
from pants.engine.goal import CurrentExecutingGoal, Goal
from pants.engine.internals import native_engine
from pants.engine.internals.docker import DockerResolveImageRequest, DockerResolveImageResult
from pants.engine.internals.native_engine import (
Expand Down Expand Up @@ -349,6 +349,9 @@ class SchedulerSession:
def __init__(self, scheduler: Scheduler, session: PySession) -> None:
self._scheduler = scheduler
self._py_session = session
self._current_goal = (
session.session_values.get(CurrentExecutingGoal) or CurrentExecutingGoal()
)

@property
def scheduler(self) -> Scheduler:
Expand Down Expand Up @@ -526,16 +529,19 @@ def run_goal_rule(
:param poll_delay: See self.execution_request.
:returns: An exit_code for the given Goal.
"""
if self._scheduler.visualize_to_dir is not None:
rule_graph_name = f"rule_graph.{product.name}.dot"
params = self._scheduler._to_params_list(subject)
self._scheduler.visualize_rule_subgraph_to_file(
os.path.join(self._scheduler.visualize_to_dir, rule_graph_name),
[type(p) for p in params],
product,
with self._current_goal._executing(product):
if self._scheduler.visualize_to_dir is not None:
rule_graph_name = f"rule_graph.{product.name}.dot"
params = self._scheduler._to_params_list(subject)
self._scheduler.visualize_rule_subgraph_to_file(
os.path.join(self._scheduler.visualize_to_dir, rule_graph_name),
[type(p) for p in params],
product,
)
(return_value,) = self.product_request(
product, [subject], poll=poll, poll_delay=poll_delay
)
(return_value,) = self.product_request(product, [subject], poll=poll, poll_delay=poll_delay)
return cast(int, return_value.exit_code)
return cast(int, return_value.exit_code)

def product_request(
self,
Expand Down
6 changes: 5 additions & 1 deletion src/python/pants/init/engine_initializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from pants.engine.console import Console
from pants.engine.environment import EnvironmentName
from pants.engine.fs import PathGlobs, Snapshot, Workspace
from pants.engine.goal import Goal
from pants.engine.goal import CurrentExecutingGoal, Goal
from pants.engine.internals import (
build_files,
dep_rules,
Expand Down Expand Up @@ -263,6 +263,10 @@ def union_membership_singleton() -> UnionMembership:
def build_root_singleton() -> BuildRoot:
return cast(BuildRoot, BuildRoot.instance)

@rule
def current_executing_goal(session_values: SessionValues) -> CurrentExecutingGoal:
return session_values.get(CurrentExecutingGoal) or CurrentExecutingGoal()

# Create a Scheduler containing graph and filesystem rules, with no installed goals.
rules = FrozenOrderedSet(
(
Expand Down
3 changes: 2 additions & 1 deletion src/python/pants/testutil/rule_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from pants.engine.env_vars import CompleteEnvironmentVars
from pants.engine.environment import EnvironmentName
from pants.engine.fs import Digest, PathGlobs, PathGlobsAndRoot, Snapshot, Workspace
from pants.engine.goal import Goal
from pants.engine.goal import CurrentExecutingGoal, Goal
from pants.engine.internals import native_engine
from pants.engine.internals.native_engine import ProcessConfigFromEnvironment, PyExecutor
from pants.engine.internals.scheduler import ExecutionError, Scheduler, SchedulerSession
Expand Down Expand Up @@ -314,6 +314,7 @@ def _set_new_session(self, scheduler: Scheduler) -> None:
{
OptionsBootstrapper: self.options_bootstrapper,
CompleteEnvironmentVars: self.environment,
CurrentExecutingGoal: CurrentExecutingGoal(),
**self.extra_session_values,
}
),
Expand Down