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

pantsbuild / pants / 22285099215

22 Feb 2026 08:52PM UTC coverage: 75.854% (-17.1%) from 92.936%
22285099215

Pull #23121

github

web-flow
Merge c7299df9c into ba8359840
Pull Request #23121: fix issue with optional fields in dependency validator

28 of 29 new or added lines in 2 files covered. (96.55%)

11174 existing lines in 400 files now uncovered.

53694 of 70786 relevant lines covered (75.85%)

1.88 hits per line

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

97.17
/src/python/pants/core/environments/target_types.py
1
# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
4✔
5

6
from dataclasses import dataclass
4✔
7

8
from pants.engine.environment import LOCAL_ENVIRONMENT_MATCHER, LOCAL_WORKSPACE_ENVIRONMENT_MATCHER
4✔
9
from pants.engine.platform import Platform
4✔
10
from pants.engine.process import ProcessCacheScope
4✔
11
from pants.engine.target import (
4✔
12
    COMMON_TARGET_FIELDS,
13
    BoolField,
14
    StringField,
15
    StringSequenceField,
16
    Target,
17
)
18
from pants.util.enums import match
4✔
19
from pants.util.strutil import help_text
4✔
20

21

22
class EnvironmentField(StringField):
4✔
23
    alias = "environment"
4✔
24
    default = LOCAL_ENVIRONMENT_MATCHER
4✔
25
    value: str
4✔
26
    help = help_text(
4✔
27
        f"""
28
        Specify which environment target to consume environment-sensitive options from.
29

30
        Once environments are defined in `[environments-preview].names`, you can specify the environment
31
        for this target by its name. Any fields that are defined in that environment will override
32
        the values from options set by `pants.toml`, command line values, or environment variables.
33

34
        You can specify multiple valid environments by using `parametrize`. If
35
        `{LOCAL_ENVIRONMENT_MATCHER}` is specified, Pants will fall back to the `local_environment`
36
        defined for the current platform, or no environment if no such environment exists.
37
        """
38
    )
39

40

41
class FallbackEnvironmentField(StringField):
4✔
42
    alias = "fallback_environment"
4✔
43
    default = None
4✔
44

45

46
class CompatiblePlatformsField(StringSequenceField):
4✔
47
    alias = "compatible_platforms"
4✔
48
    default = tuple(plat.value for plat in Platform)
4✔
49
    valid_choices = Platform
4✔
50
    value: tuple[str, ...]
4✔
51
    help = help_text(
4✔
52
        f"""
53
        Which platforms this environment can be used with.
54

55
        This is used for Pants to automatically determine which environment target to use for
56
        the user's machine when the environment is set to the special value
57
        `{LOCAL_ENVIRONMENT_MATCHER}`. Currently, there cannot be more than one environment target
58
        registered in `[environments-preview].names` for a particular platform. If there is no
59
        environment target for a certain platform, Pants will use the options system instead to
60
        determine environment variables and executable search paths.
61
        """
62
    )
63

64

65
class LocalCompatiblePlatformsField(CompatiblePlatformsField):
4✔
66
    pass
4✔
67

68

69
class LocalFallbackEnvironmentField(FallbackEnvironmentField):
4✔
70
    help = help_text(
4✔
71
        f"""
72
        The environment to fallback to when this local environment cannot be used because the
73
        field `{CompatiblePlatformsField.alias}` is not compatible with the local host.
74

75
        Must be an environment name from the option `[environments-preview].names`, the
76
        special string `{LOCAL_ENVIRONMENT_MATCHER}` to use the relevant local environment, or the
77
        Python value `None` to error when this specific local environment cannot be used.
78

79
        Tip: when targeting Linux, it can be particularly helpful to fallback to a
80
        `docker_environment` or `remote_environment` target. That allows you to prefer using the
81
        local host when possible, which often has less overhead (particularly compared to Docker).
82
        If the local host is not compatible, then Pants will use Docker or remote execution to
83
        still run in a similar environment.
84
        """
85
    )
86

87

88
class LocalEnvironmentTarget(Target):
4✔
89
    alias = "local_environment"
4✔
90
    core_fields = (
4✔
91
        *COMMON_TARGET_FIELDS,
92
        LocalCompatiblePlatformsField,
93
        LocalFallbackEnvironmentField,
94
    )
95
    help = help_text(
4✔
96
        f"""
97
        Configuration of a local execution environment for specific platforms.
98

99
        Environment configuration includes the platforms the environment is compatible with, and
100
        optionally a fallback environment, along with environment-aware options (such as
101
        environment variables and search paths) used by Pants to execute processes in this
102
        environment.
103

104
        To use this environment, map this target's address with a memorable name in
105
        `[environments-preview].names`. You can then consume this environment by specifying the name in
106
        the `environment` field defined on other targets.
107

108
        Only one `local_environment` may be defined in `[environments-preview].names` per platform, and
109
        when `{LOCAL_ENVIRONMENT_MATCHER}` is specified as the environment, the
110
        `local_environment` that matches the current platform (if defined) will be selected.
111

112
        See https://www.pantsbuild.org/stable/docs/using-pants/environments for more information.
113
        """
114
    )
115

116

117
class LocalWorkspaceCompatiblePlatformsField(CompatiblePlatformsField):
4✔
118
    pass
4✔
119

120

121
class LocalWorkspaceEnvironmentTarget(Target):
4✔
122
    alias = "experimental_workspace_environment"
4✔
123
    core_fields = (
4✔
124
        *COMMON_TARGET_FIELDS,
125
        LocalWorkspaceCompatiblePlatformsField,
126
    )
127
    help = help_text(
4✔
128
        f"""
129
        Configuration of a "workspace" execution environment for specific platforms.
130

131
        A "workspace" environment is a local environment which executes build processes within
132
        the repository and not in the usual execution sandbox. This is useful when interacting with
133
        third-party build orchestration tools which may not run correctly when run from within the Pants
134
        execution sandbox.
135

136
        Environment configuration includes the platforms the environment is compatible with along with
137
        environment-aware options (such as environment variables and search paths) used by Pants to execute
138
        processes in this environment.
139

140
        To use this environment, map this target's address with a memorable name in
141
        `[environments-preview].names`. You can then consume this environment by specifying the name in
142
        the `environment` field defined on other targets.
143

144
        Only one `experimental_workspace_environment` may be defined in `[environments-preview].names` per platform, and
145
        when `{LOCAL_WORKSPACE_ENVIRONMENT_MATCHER}` is specified as the environment, the
146
        `experimental_workspace_environment` that matches the current platform (if defined) will be selected.
147

148
        Caching and reproducibility:
149

150
        Pants' caching relies on all process being reproducible based solely on inputs in the repository.
151
        Processes executed in a workspace environment can easily accidentally read unexpected files,
152
        that aren't specified as a dependency.  Thus, Pants puts that burden on you, the Pants user, to ensure
153
        a process output only depends on its specified input files, and doesn't read anything else.
154

155
        If a process isn't reproducible, re-running a build from the same source code could fail unexpectedly,
156
        or give different output to an earlier build.
157

158
        NOTE: This target type is EXPERIMENTAL and may change its semantics in subsequent Pants versions
159
        without a deprecation cycle.
160
        """
161
    )
162

163

164
class DockerImageField(StringField):
4✔
165
    alias = "image"
4✔
166
    required = True
4✔
167
    value: str
4✔
168
    help = help_text(
4✔
169
        """
170
        The docker image ID to use when this environment is loaded.
171

172
        This value may be any image identifier that the local Docker installation can accept.
173
        This includes image names with or without tags (e.g. `centos6` or `centos6:latest`), or
174
        image names with an immutable digest (e.g. `centos@sha256:<some_sha256_value>`).
175

176
        The choice of image ID can affect the reproducibility of builds. Consider using an
177
        immutable digest if reproducibility is needed, but regularly ensure that the image
178
        is free of relevant bugs or security vulnerabilities.
179

180
        Note that in order to use an image as a `docker_environment` it must have a few tools:
181
        - `/bin/sh`
182
        - `/usr/bin/env`
183
        - `bash`
184
        - `tar`
185

186
        While most images will have these preinstalled, users of base images such as Distroless or scratch will need to bake these tools into the image themselves. All of these except `bash` are available via busybox.
187
        """
188
    )
189

190

191
class DockerPlatformField(StringField):
4✔
192
    alias = "platform"
4✔
193
    default = None
4✔
194
    valid_choices = Platform
4✔
195
    help = help_text(
4✔
196
        """
197
        If set, Docker will always use the specified platform when pulling and running the image.
198

199
        If unset, Pants will default to the CPU architecture of your local host machine. For
200
        example, if you are running on Apple Silicon, it will use `linux_arm64`, whereas running on
201
        Intel macOS will use `linux_x86_64`. This mirrors Docker's behavior when `--platform` is
202
        left off.
203
        """
204
    )
205

206
    @property
4✔
207
    def normalized_value(self) -> Platform:
4✔
208
        if self.value is not None:
2✔
209
            return Platform(self.value)
2✔
210
        return match(
1✔
211
            Platform.create_for_localhost(),
212
            {
213
                Platform.linux_x86_64: Platform.linux_x86_64,
214
                Platform.macos_x86_64: Platform.linux_x86_64,
215
                Platform.linux_arm64: Platform.linux_arm64,
216
                Platform.macos_arm64: Platform.linux_arm64,
217
            },
218
        )
219

220

221
class DockerFallbackEnvironmentField(FallbackEnvironmentField):
4✔
222
    help = help_text(
4✔
223
        f"""
224
        The environment to fallback to when this Docker environment cannot be used because either
225
        the global option `--docker-execution` is false, or the
226
        field `{DockerPlatformField.alias}` is not compatible with the local host's CPU
227
        architecture (this is only an issue when the local host is Linux; macOS is fine).
228

229
        Must be an environment name from the option `[environments-preview].names`, the
230
        special string `{LOCAL_ENVIRONMENT_MATCHER}` to use the relevant local environment, or the
231
        Python value `None` to error when this specific Docker environment cannot be used.
232
        """
233
    )
234

235

236
class DockerEnvironmentTarget(Target):
4✔
237
    alias = "docker_environment"
4✔
238
    core_fields = (
4✔
239
        *COMMON_TARGET_FIELDS,
240
        DockerImageField,
241
        DockerPlatformField,
242
        DockerFallbackEnvironmentField,
243
    )
244
    help = help_text(
4✔
245
        """
246
        Configuration of a Docker environment used for building your code.
247

248
        Environment configuration includes both Docker-specific information (including the image
249
        and platform choice), as well as environment-aware options (such as environment variables
250
        and search paths) used by Pants to execute processes in this Docker environment.
251

252
        To use this environment, map this target's address with a memorable name in
253
        `[environments-preview].names`. You can then consume this environment by specifying the name in
254
        the `environment` field defined on other targets.
255

256
        Before running Pants using this environment, if you are using Docker Desktop, make sure the option
257
        **Enable default Docker socket** is enabled, you can find it in **Docker Desktop Settings > Advanced**
258
        panel. That option tells Docker to create a socket at `/var/run/docker.sock` which Pants can use to
259
        communicate with Docker.
260

261
        See https://www.pantsbuild.org/stable/docs/using-pants/environments for more information.
262
        """
263
    )
264

265

266
class RemotePlatformField(StringField):
4✔
267
    alias = "platform"
4✔
268
    default = Platform.linux_x86_64.value
4✔
269
    valid_choices = Platform
4✔
270
    help = "The platform used by the remote execution environment."
4✔
271

272

273
class RemoteExtraPlatformPropertiesField(StringSequenceField):
4✔
274
    alias = "extra_platform_properties"
4✔
275
    default = ()
4✔
276
    value: tuple[str, ...]
4✔
277
    help = help_text(
4✔
278
        """
279
        Platform properties to set on remote execution requests.
280

281
        Format: `property=value`. Multiple values should be specified as multiple
282
        occurrences of this flag.
283

284
        Pants itself may add additional platform properties.
285
        """
286
    )
287

288

289
class RemoteFallbackEnvironmentField(FallbackEnvironmentField):
4✔
290
    help = help_text(
4✔
291
        f"""
292
        The environment to fallback to when remote execution is disabled via the global option
293
        `--remote-execution`.
294

295
        Must be an environment name from the option `[environments-preview].names`, the
296
        special string `{LOCAL_ENVIRONMENT_MATCHER}` to use the relevant local environment, or the
297
        Python value `None` to error when remote execution is disabled.
298

299
        Tip: if you are using a Docker image with your remote execution environment (usually
300
        enabled by setting the field `{RemoteExtraPlatformPropertiesField.alias}`), then it can be
301
        useful to fallback to an equivalent `docker_image` target so that you have a consistent
302
        execution environment.
303
        """
304
    )
305

306

307
class RemoteEnvironmentCacheBinaryDiscovery(BoolField):
4✔
308
    alias = "cache_binary_discovery"
4✔
309
    default = False
4✔
310
    help = help_text(
4✔
311
        f"""
312
        If true, will cache system binary discovery, e.g. finding Python interpreters.
313

314
        When safe to do, it is preferable to set this option to `True` for faster performance by
315
        avoiding wasted work. Otherwise, Pants will search for system binaries whenever the
316
        Pants daemon is restarted.
317

318
        However, it is only safe to set this to `True` if the remote execution environment has a
319
        stable environment, e.g. the server will not change versions of installed system binaries.
320
        Otherwise, you risk caching results that become stale when the server changes its
321
        environment, which may break your builds. With some remote execution servers, you can
322
        specify a Docker image to run with via the field
323
        `{RemoteExtraPlatformPropertiesField.alias}`; if you are able to specify what Docker image
324
        to use, and also use a pinned tag of the image, it is likely safe to set this field to true.
325
        """
326
    )
327

328

329
class RemoteEnvironmentTarget(Target):
4✔
330
    alias = "remote_environment"
4✔
331
    core_fields = (
4✔
332
        *COMMON_TARGET_FIELDS,
333
        RemotePlatformField,
334
        RemoteExtraPlatformPropertiesField,
335
        RemoteFallbackEnvironmentField,
336
        RemoteEnvironmentCacheBinaryDiscovery,
337
    )
338
    help = help_text(
4✔
339
        """
340
        Configuration of a remote execution environment used for building your code.
341

342
        Environment configuration includes platform properties and a fallback environment, as well
343
        as environment-aware options (such as environment variables and search paths) used by Pants
344
        to execute processes in this remote environment.
345

346
        Note that you must also configure remote execution with the global options like
347
        `remote_execution` and `remote_execution_address`.
348

349
        To use this environment, map this target's address with a memorable name in
350
        `[environments-preview].names`. You can then consume this environment by specifying the name in
351
        the `environment` field defined on other targets.
352

353
        Often, it is only necessary to have a single `remote_environment` target for your
354
        repository, but it can be useful to have >1 so that you can set different
355
        `extra_platform_properties`. For example, with some servers, you could use this to
356
        configure a different Docker image per environment.
357

358
        See https://www.pantsbuild.org/stable/docs/using-pants/environments for more information.
359
        """
360
    )
361

362

363
@dataclass(frozen=True)
4✔
364
class EnvironmentTarget:
4✔
365
    name: str | None
4✔
366
    val: Target | None
4✔
367

368
    def executable_search_path_cache_scope(
4✔
369
        self, *, cache_failures: bool = False
370
    ) -> ProcessCacheScope:
371
        """Whether it's safe to cache executable search path discovery or not.
372

373
        If the environment may change on us, e.g. the user upgrades a binary, then it's not safe to
374
        cache the discovery to disk. Technically, in that case, we should recheck the binary every
375
        session (i.e. Pants run), but we instead settle for every Pantsd lifetime to have more
376
        acceptable performance.
377

378
        Meanwhile, when running with Docker, we already invalidate whenever the image changes
379
        thanks to https://github.com/pantsbuild/pants/pull/17101.
380

381
        Remote execution often is safe to cache, but depends on the remote execution server.
382
        So, we rely on the user telling us what is safe.
383
        """
384
        caching_allowed = self.val and (
4✔
385
            self.val.has_field(DockerImageField)
386
            or (
387
                self.val.has_field(RemoteEnvironmentCacheBinaryDiscovery)
388
                and self.val[RemoteEnvironmentCacheBinaryDiscovery].value
389
            )
390
        )
391
        if cache_failures:
4✔
392
            return (
4✔
393
                ProcessCacheScope.ALWAYS
394
                if caching_allowed
395
                else ProcessCacheScope.PER_RESTART_ALWAYS
396
            )
397
        return (
4✔
398
            ProcessCacheScope.SUCCESSFUL
399
            if caching_allowed
400
            else ProcessCacheScope.PER_RESTART_SUCCESSFUL
401
        )
402

403
    def sandbox_base_path(self) -> str:
4✔
UNCOV
404
        if self.val and self.val.has_field(LocalWorkspaceCompatiblePlatformsField):
×
405
            return "{chroot}"
×
406
        else:
UNCOV
407
            return ""
×
408

409
    @property
4✔
410
    def default_cache_scope(self) -> ProcessCacheScope:
4✔
411
        if self.val and self.val.has_field(LocalWorkspaceCompatiblePlatformsField):
2✔
412
            return ProcessCacheScope.PER_SESSION
1✔
413
        else:
414
            return ProcessCacheScope.SUCCESSFUL
2✔
415

416
    @property
4✔
417
    def use_working_directory_as_base_for_output_captures(self) -> bool:
4✔
418
        if self.val and self.val.has_field(LocalWorkspaceCompatiblePlatformsField):
2✔
419
            return False
1✔
420
        return True
2✔
421

422
    @property
4✔
423
    def can_access_local_system_paths(self) -> bool:
4✔
424
        tgt = self.val
4✔
425
        if not tgt:
4✔
426
            return True
4✔
427

428
        return tgt.has_field(LocalCompatiblePlatformsField) or tgt.has_field(
2✔
429
            LocalWorkspaceCompatiblePlatformsField
430
        )
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