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

pantsbuild / pants / 25405422172

05 May 2026 10:18PM UTC coverage: 92.879% (-0.07%) from 92.944%
25405422172

Pull #23319

github

web-flow
Merge c82d0f333 into e8b784f89
Pull Request #23319: [pants_ng] Scaffolding for a pants_ng mode.

25 of 76 new or added lines in 9 files covered. (32.89%)

209 existing lines in 15 files now uncovered.

92234 of 99306 relevant lines covered (92.88%)

4.05 hits per line

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

0.0
/src/python/pants/bin/pants_runner.py
1
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
import logging
×
5
import os
×
6
import platform
×
7
import sys
×
8
import warnings
×
9
from collections.abc import Mapping
×
10
from dataclasses import dataclass
×
11

12
from packaging.version import Version
×
13

14
from pants.base.deprecated import warn_or_error
×
15
from pants.base.exception_sink import ExceptionSink
×
16
from pants.base.exiter import ExitCode
×
17
from pants.engine.env_vars import CompleteEnvironmentVars
×
NEW
18
from pants.engine.internals.native_engine import PyNgInvocation
×
19
from pants.init.logging import initialize_stdio, stdio_destination
×
20
from pants.init.util import init_workdir
×
21
from pants.option.bootstrap_options import BootstrapOptions
×
22
from pants.option.option_value_container import OptionValueContainer
×
23
from pants.option.options_bootstrapper import OptionsBootstrapper
×
24
from pants.util.docutil import doc_url
×
25
from pants.util.osutil import get_normalized_arch_name, macos_major_version
×
26
from pants.util.strutil import softwrap
×
27

28
logger = logging.getLogger(__name__)
×
29

30
# First version with working Python 3.11 support:
31
# https://github.com/pantsbuild/scie-pants/releases/tag/v0.12.2
32
# TODO: Likely still need to update scie-pants
33
MINIMUM_SCIE_PANTS_VERSION = Version("0.12.2")
×
34

35

36
@dataclass(frozen=True)
×
37
class PantsRunner:
×
38
    """A higher-level runner that delegates runs to either a LocalPantsRunner or
39
    RemotePantsRunner."""
40

41
    args: list[str]
42
    env: Mapping[str, str]
43

44
    # This could be a bootstrap option, but it's preferable to keep these very limited to make it
45
    # easier to make the daemon the default use case. Once the daemon lifecycle is stable enough we
46
    # should be able to avoid needing to kill it at all.
47
    def will_terminate_pantsd(self) -> bool:
×
48
        _DAEMON_KILLING_GOALS = frozenset(["kill-pantsd", "clean-all"])
×
49
        return not frozenset(self.args).isdisjoint(_DAEMON_KILLING_GOALS)
×
50

51
    def _should_run_with_pantsd(self, global_bootstrap_options: OptionValueContainer) -> bool:
×
52
        terminate_pantsd = self.will_terminate_pantsd()
×
53

54
        if terminate_pantsd:
×
55
            logger.debug(f"Pantsd terminating goal detected: {self.args}")
×
56

57
        # If we want concurrent pants runs, we can't have pantsd enabled.
58
        return (
×
59
            global_bootstrap_options.pantsd
60
            and not terminate_pantsd
61
            and not global_bootstrap_options.concurrent
62
        )
63

64
    @staticmethod
×
65
    def scrub_pythonpath() -> None:
×
66
        # Do not propagate any PYTHONPATH that happens to have been set in our environment
67
        # to our subprocesses.
68
        # Note that don't warn (but still scrub) if RUNNING_PANTS_FROM_SOURCES is set. This allows
69
        # scripts that run pants directly from sources, and therefore must set PYTHONPATH, to mute
70
        # this warning.
71
        pythonpath = os.environ.pop("PYTHONPATH", None)
×
72
        if pythonpath and not os.environ.pop("RUNNING_PANTS_FROM_SOURCES", None):
×
73
            logger.debug(f"Scrubbed PYTHONPATH={pythonpath} from the environment.")
×
74

75
    def run(self, start_time: float) -> ExitCode:
×
76
        self.scrub_pythonpath()
×
77

78
        # Create a transient OptionsBootstrapper with no args, to read the value of pants_ng from
79
        # config and env. We can't parse args until we know if we're ng or og.
NEW
80
        options_bootstrapper = OptionsBootstrapper.create(args=[], env=self.env, allow_pantsrc=True)
×
NEW
81
        pants_ng = options_bootstrapper.bootstrap_options.for_global_scope().pants_ng
×
82

NEW
83
        if pants_ng:
×
NEW
84
            logger.info("PantsRunner running as pants_ng")
×
NEW
85
            ng_invocation = PyNgInvocation.from_args(tuple(self.args[1:]))
×
86
            # Allow the existing logic to read flags in global position (note, these can be
87
            # prefixed with a scope so they are not necessarily just global options), so
88
            # that the engine can configure itself.
NEW
89
            global_flags = ng_invocation.global_flag_strings()
×
NEW
90
            options_bootstrapper = OptionsBootstrapper.create(
×
91
                args=[self.args[0], *global_flags], env=self.env, allow_pantsrc=True
92
            )
93
        else:
NEW
94
            ng_invocation = None
×
NEW
95
            options_bootstrapper = OptionsBootstrapper.create(
×
96
                args=self.args, env=self.env, allow_pantsrc=True
97
            )
98

99
        with warnings.catch_warnings(record=True):
×
100
            bootstrap_options = options_bootstrapper.bootstrap_options
×
101
            global_bootstrap_options = bootstrap_options.for_global_scope()
×
102

103
        BootstrapOptions.maybe_enable_stack_trampoline(global_bootstrap_options)
×
104

105
        # We enable logging here, and everything before it will be routed through regular
106
        # Python logging.
107
        stdin_fileno = sys.stdin.fileno()
×
108
        stdout_fileno = sys.stdout.fileno()
×
109
        stderr_fileno = sys.stderr.fileno()
×
110
        with (
×
111
            initialize_stdio(global_bootstrap_options),
112
            stdio_destination(
113
                stdin_fileno=stdin_fileno,
114
                stdout_fileno=stdout_fileno,
115
                stderr_fileno=stderr_fileno,
116
            ),
117
        ):
118
            run_via_scie = "SCIE" in os.environ
×
119
            enable_scie_warnings = "NO_SCIE_WARNING" not in os.environ
×
120
            scie_pants_version = os.environ.get("SCIE_PANTS_VERSION")
×
121

122
            if enable_scie_warnings:
×
123
                if not run_via_scie:
×
124
                    raise RuntimeError(
×
125
                        softwrap(
126
                            f"""
127
                            The `pants` launcher binary is now the only supported way of running Pants.
128
                            See {doc_url("docs/getting-started/installing-pants")} for details.
129
                            """
130
                        ),
131
                    )
132

133
                if run_via_scie and (
×
134
                    # either scie-pants is too old to communicate its version:
135
                    scie_pants_version is None
136
                    # or the version itself is too old:
137
                    or Version(scie_pants_version) < MINIMUM_SCIE_PANTS_VERSION
138
                ):
139
                    current_version_text = (
×
140
                        f"The current version of the `pants` launcher binary is {scie_pants_version}"
141
                        if scie_pants_version
142
                        else "Run `PANTS_BOOTSTRAP_VERSION=report pants` to see the current version of the `pants` launcher binary"
143
                    )
144
                    warn_or_error(
×
145
                        "2.25.0.dev0",
146
                        f"using a `pants` launcher binary older than {MINIMUM_SCIE_PANTS_VERSION}",
147
                        softwrap(
148
                            f"""
149
                            {current_version_text}, and see {doc_url("docs/getting-started/installing-pants#upgrading-pants")} for how to upgrade.
150
                            """
151
                        ),
152
                    )
153

154
            _validate_macos_version(global_bootstrap_options)
×
155

156
            # N.B. We inline imports to speed up the python thin client run, and avoids importing
157
            # engine types until after the runner has had a chance to set __PANTS_BIN_NAME.
158
            if self._should_run_with_pantsd(global_bootstrap_options):
×
159
                from pants.bin.remote_pants_runner import RemotePantsRunner
×
160

161
                try:
×
162
                    remote_runner = RemotePantsRunner(self.args, self.env, options_bootstrapper)
×
163
                    return remote_runner.run(start_time)
×
164
                except RemotePantsRunner.Fallback as e:
×
165
                    logger.warning(f"Client exception: {e!r}, falling back to non-daemon mode")
×
166

167
            from pants.bin.local_pants_runner import LocalPantsRunner
×
168

169
            # We only install signal handling via ExceptionSink if the run will execute in this process.
170
            ExceptionSink.install(
×
171
                log_location=init_workdir(global_bootstrap_options), pantsd_instance=False
172
            )
173
            runner = LocalPantsRunner.create(
×
174
                env=CompleteEnvironmentVars(self.env),
175
                working_dir=os.getcwd(),
176
                options_bootstrapper=options_bootstrapper,
177
                ng_invocation=ng_invocation,
178
            )
179
            return runner.run(start_time)
×
180

181

182
# for each architecture, indicate the first Pants version that doesn't support the given version of
183
# macOS, if it is (soon to be) unsupported:
184
_MACOS_VERSION_BECOMES_UNSUPPORTED_IN = {
×
185
    # macos-14 is currently oldest github hosted runner for arm
186
    "arm64": {
187
        10: "2.24.0.dev0",
188
        11: "2.24.0.dev0",
189
        12: "2.25.0.dev0",
190
        13: "2.25.0.dev0",
191
        # adding new values here should update the phrasing of the message below
192
    },
193
    # macos-13 will soon be the oldest (and only) github hosted runner for x86-64 (see https://github.com/pantsbuild/pants/issues/21333)
194
    "x86_64": {
195
        10: "2.24.0.dev0",
196
        11: "2.24.0.dev0",
197
        12: "2.25.0.dev0",
198
        # adding new values here should update the phrasing of the message below
199
    },
200
}
201

202

203
def _validate_macos_version(global_bootstrap_options: OptionValueContainer) -> None:
×
204
    """Check for running on deprecated/unsupported versions of macOS, and similar."""
205

206
    macos_version = macos_major_version()
×
207
    if macos_version is None:
×
208
        # Not macOS, no validation/deprecations required!
209
        return
×
210

211
    arch_versions = _MACOS_VERSION_BECOMES_UNSUPPORTED_IN[get_normalized_arch_name()]
×
212
    unsupported_version = arch_versions.get(macos_version)
×
213

214
    is_permitted_deprecated_macos_version = (
×
215
        str(macos_version) in global_bootstrap_options.allow_deprecated_macos_versions
216
    )
217

218
    if unsupported_version is not None and not is_permitted_deprecated_macos_version:
×
219
        warn_or_error(
×
220
            unsupported_version,
221
            "using Pants on older macOS",
222
            softwrap(
223
                f"""
224
                Recent versions of Pants only support macOS 13 and newer (on x86-64) and macOS
225
                14 and newer (on arm64), but this machine appears older ({platform.platform()}
226
                implies macOS version {macos_version}). This version also isn't permitted by your
227
                `[GLOBAL].allow_deprecated_macos_versions` configuration
228
                ({global_bootstrap_options.allow_deprecated_macos_versions}).
229

230
                Either upgrade your operating system(s), or silence this message (and thus opt-in to
231
                potential breakage) by adding "{macos_version}" to the
232
                `[GLOBAL].allow_deprecated_macos_versions` list.
233

234
                If you have questions or concerns about this, please reach out to us at
235
                {doc_url("community/getting-help")}.
236
                """
237
            ),
238
        )
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

© 2026 Coveralls, Inc