• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 19015773527

02 Nov 2025 05:33PM UTC coverage: 17.872% (-62.4%) from 80.3%
19015773527

Pull #22816

github

web-flow
Merge a12d75757 into 6c024e162
Pull Request #22816: Update Pants internal Python to 3.14

4 of 5 new or added lines in 3 files covered. (80.0%)

28452 existing lines in 683 files now uncovered.

9831 of 55007 relevant lines covered (17.87%)

0.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/python/pants/bsp/goal.py
1
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
UNCOV
3
from __future__ import annotations
×
4

UNCOV
5
import json
×
UNCOV
6
import logging
×
UNCOV
7
import os
×
UNCOV
8
import shlex
×
UNCOV
9
import sys
×
UNCOV
10
import textwrap
×
UNCOV
11
from collections.abc import Mapping
×
12

UNCOV
13
from pants.base.build_root import BuildRoot
×
UNCOV
14
from pants.base.exiter import PANTS_FAILED_EXIT_CODE, PANTS_SUCCEEDED_EXIT_CODE, ExitCode
×
UNCOV
15
from pants.bsp.context import BSPContext
×
UNCOV
16
from pants.bsp.protocol import BSPConnection
×
UNCOV
17
from pants.bsp.util_rules.lifecycle import BSP_VERSION, BSPLanguageSupport
×
UNCOV
18
from pants.engine.env_vars import CompleteEnvironmentVars
×
UNCOV
19
from pants.engine.internals.session import SessionValues
×
UNCOV
20
from pants.engine.unions import UnionMembership
×
UNCOV
21
from pants.goal.auxiliary_goal import AuxiliaryGoal, AuxiliaryGoalContext
×
UNCOV
22
from pants.init.engine_initializer import GraphSession
×
UNCOV
23
from pants.option.option_types import BoolOption, FileListOption, StrListOption
×
UNCOV
24
from pants.option.option_value_container import OptionValueContainer
×
UNCOV
25
from pants.util.docutil import bin_name
×
UNCOV
26
from pants.util.strutil import softwrap
×
UNCOV
27
from pants.version import VERSION
×
28

UNCOV
29
_logger = logging.getLogger(__name__)
×
30

31

UNCOV
32
class BSPGoal(AuxiliaryGoal):
×
UNCOV
33
    name = "experimental-bsp"
×
UNCOV
34
    help = "Setup repository for Build Server Protocol (https://build-server-protocol.github.io/)."
×
35

UNCOV
36
    server = BoolOption(
×
37
        default=False,
38
        advanced=True,
39
        help=softwrap(
40
            """
41
            Run the Build Server Protocol server. Pants will receive BSP RPC requests via the console.
42
            This should only ever be invoked via the IDE.
43
            """
44
        ),
45
    )
UNCOV
46
    runner_env_vars = StrListOption(
×
47
        default=["PATH"],
48
        help=softwrap(
49
            f"""
50
            Environment variables to set in the BSP runner script when setting up BSP in a repository.
51
            Entries are either strings in the form `ENV_VAR=value` to set an explicit value;
52
            or just `ENV_VAR` to copy the value from Pants' own environment when the {name} goal was run.
53

54
            This option only takes effect when the BSP runner script is written. If the option changes, you
55
            must run `{bin_name()} {name}` again to write a new copy of the BSP runner script.
56

57
            Note: The environment variables passed to the Pants BSP server will be those set for your IDE
58
            and not your shell. For example, on macOS, the IDE is generally launched by `launchd` after
59
            clicking on a Dock icon, and not from the shell. Thus, any environment variables set for your
60
            shell will likely not be seen by the Pants BSP server. At the very least, on macOS consider
61
            writing an explicit PATH into the BSP runner script via this option.
62
            """
63
        ),
64
        advanced=True,
65
    )
66

UNCOV
67
    groups_config_files = FileListOption(
×
68
        help=softwrap(
69
            """
70
            A list of config files that define groups of Pants targets to expose to IDEs via Build Server Protocol.
71

72
            Pants generally uses fine-grained targets to define the components of a build (in many cases on a file-by-file
73
            basis). Many IDEs, however, favor coarse-grained targets that contain large numbers of source files.
74
            To accommodate this distinction, the Pants BSP server will compute a set of BSP build targets to use
75
            from the groups specified in the config files set for this option. Each group will become one or more
76
            BSP build targets.
77

78
            Each config file is a TOML file with a `groups` dictionary with the following format for an entry:
79

80
                # The dictionary key is used to identify the group. It must be unique.
81
                [groups.ID1]:
82
                # One or more Pants address specs defining what targets to include in the group.
83
                addresses = [
84
                  "src/jvm::",
85
                  "tests/jvm::",
86
                ]
87
                # Filter targets to a specific resolve. Targets in a group must be from a single resolve.
88
                # Format of filter is `TYPE:RESOLVE_NAME`. The only supported TYPE is `jvm`. RESOLVE_NAME must be
89
                # a valid resolve name.
90
                resolve = "jvm:jvm-default"
91
                display_name = "Display Name"  # (Optional) Name shown to the user in the IDE.
92
                base_directory = "path/from/build/root"  # (Optional) Hint to the IDE for where the build target should "live."
93

94
            Pants will merge the contents of the config files together. If the same ID is used for a group definition,
95
            in multiple config files, the definition in the latter config file will take effect.
96
            """
97
        ),
98
    )
99

UNCOV
100
    def run(
×
101
        self,
102
        context: AuxiliaryGoalContext,
103
    ) -> ExitCode:
104
        goal_options = context.options.for_scope(self.name)
×
105
        if goal_options.server:
×
106
            return self._run_server(
×
107
                graph_session=context.graph_session,
108
                union_membership=context.union_membership,
109
            )
110
        current_session_values = context.graph_session.scheduler_session.py_session.session_values
×
111
        env = current_session_values[CompleteEnvironmentVars]
×
112
        return self._setup_bsp_connection(
×
113
            union_membership=context.union_membership, env=env, options=goal_options
114
        )
115

UNCOV
116
    def _setup_bsp_connection(
×
117
        self,
118
        union_membership: UnionMembership,
119
        env: Mapping[str, str],
120
        options: OptionValueContainer,
121
    ) -> ExitCode:
122
        """Setup the BSP connection file."""
123

124
        build_root = BuildRoot()
×
125
        bsp_conn_path = build_root.pathlib_path / ".bsp" / "pants.json"
×
126
        if bsp_conn_path.exists():
×
127
            print(
×
128
                f"ERROR: A BSP connection file already exists at path `{bsp_conn_path}`. "
129
                "Please delete that file if you intend to re-setup BSP in this repository.",
130
                file=sys.stderr,
131
            )
132
            return PANTS_FAILED_EXIT_CODE
×
133

134
        bsp_dir = build_root.pathlib_path / ".pants.d" / "bsp"
×
135

136
        bsp_scripts_dir = bsp_dir / "scripts"
×
137
        bsp_scripts_dir.mkdir(exist_ok=True, parents=True)
×
138

139
        bsp_logs_dir = bsp_dir / "logs"
×
140
        bsp_logs_dir.mkdir(exist_ok=True, parents=True)
×
141

142
        # Determine which environment variables to set in the BSP runner script.
143
        # TODO: Consider whether some of this logic could be shared with
144
        #  `pants.engine.environment.CompleteEnvironmentVars.get_subset`.
145
        run_script_env_lines: list[str] = []
×
146
        for env_var in options.runner_env_vars:
×
147
            if "=" in env_var:
×
148
                run_script_env_lines.append(env_var)
×
149
            else:
150
                if env_var not in env:
×
151
                    print(
×
152
                        f"ERROR: The `[{self.name}].runner_env_vars` option is configured to add the `{env_var}` "
153
                        "environment variable to the BSP runner script using its value in the current environment. "
154
                        "That environment variable, however, is not present in the current environment. "
155
                        "Please either set it in the current environment first or else configure a specific value "
156
                        "in `pants.toml`.",
157
                        file=sys.stderr,
158
                    )
159
                    return PANTS_FAILED_EXIT_CODE
×
160
                run_script_env_lines.append(f"{env_var}={env[env_var]}")
×
161

162
        run_script_env_lines_str = "\n".join(
×
163
            [f"export {shlex.quote(line)}" for line in run_script_env_lines]
164
        )
165

166
        run_script_path = bsp_scripts_dir / "run-bsp.sh"
×
167
        run_script_path.write_text(
×
168
            textwrap.dedent(  # noqa: PNT20
169
                f"""\
170
                #!/bin/sh
171
                {run_script_env_lines_str}
172
                exec 2>>{shlex.quote(str(bsp_logs_dir / "stderr.log"))}
173
                env 1>&2
174
                exec {shlex.quote(bin_name())} --no-pantsd {self.name} --server
175
                """
176
            )
177
        )
178
        run_script_path.chmod(0o755)
×
179
        _logger.info(f"Wrote BSP runner script to `{run_script_path}`.")
×
180

181
        bsp_conn_data = {
×
182
            "name": "Pants",
183
            "version": VERSION,
184
            "bspVersion": BSP_VERSION,
185
            "languages": sorted(
186
                [lang.language_id for lang in union_membership.get(BSPLanguageSupport)]
187
            ),
188
            "argv": ["./.pants.d/bsp/scripts/run-bsp.sh"],
189
        }
190

191
        bsp_conn_path.parent.mkdir(exist_ok=True, parents=True)
×
192
        bsp_conn_path.write_text(json.dumps(bsp_conn_data))
×
193
        _logger.info(f"Wrote BSP connection file to `{bsp_conn_path}`.")
×
194

195
        return PANTS_SUCCEEDED_EXIT_CODE
×
196

UNCOV
197
    def _run_server(
×
198
        self,
199
        *,
200
        graph_session: GraphSession,
201
        union_membership: UnionMembership,
202
    ) -> ExitCode:
203
        """Run the BSP server."""
204

205
        current_session_values = graph_session.scheduler_session.py_session.session_values
×
206
        context = BSPContext()
×
207
        session_values = SessionValues(
×
208
            {
209
                **current_session_values,
210
                BSPContext: context,
211
            }
212
        )
213
        scheduler_session = graph_session.scheduler_session.scheduler.new_session(
×
214
            build_id="bsp", dynamic_ui=False, session_values=session_values
215
        )
216

217
        saved_stdout = sys.stdout
×
218
        saved_stdin = sys.stdin
×
219
        try:
×
220
            sys.stdout = os.fdopen(sys.stdout.fileno(), "wb", buffering=0)
×
221
            sys.stdin = os.fdopen(sys.stdin.fileno(), "rb", buffering=0)
×
222
            conn = BSPConnection(
×
223
                scheduler_session,
224
                union_membership,
225
                context,
226
                sys.stdin,
227
                sys.stdout,
228
            )
229
            conn.run()
×
230
        finally:
231
            sys.stdout = saved_stdout
×
232
            sys.stdin = saved_stdin
×
233

234
        return ExitCode(0)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc