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

pantsbuild / pants / 18252174847

05 Oct 2025 01:36AM UTC coverage: 43.382% (-36.9%) from 80.261%
18252174847

push

github

web-flow
run tests on mac arm (#22717)

Just doing the minimal to pull forward the x86_64 pattern.

ref #20993

25776 of 59416 relevant lines covered (43.38%)

1.3 hits per line

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

43.59
/src/python/pants/init/options_initializer.py
1
# Copyright 2016 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
3✔
5

6
import dataclasses
3✔
7
import importlib
3✔
8
import logging
3✔
9
import sys
3✔
10
from collections.abc import Iterator
3✔
11
from contextlib import contextmanager
3✔
12
from pathlib import Path
3✔
13

14
import pkg_resources
3✔
15

16
from pants.build_graph.build_configuration import BuildConfiguration
3✔
17
from pants.engine.env_vars import CompleteEnvironmentVars
3✔
18
from pants.engine.internals.native_engine import PyExecutor
3✔
19
from pants.engine.unions import UnionMembership
3✔
20
from pants.help.flag_error_help_printer import FlagErrorHelpPrinter
3✔
21
from pants.init.bootstrap_scheduler import BootstrapScheduler
3✔
22
from pants.init.engine_initializer import EngineInitializer
3✔
23
from pants.init.extension_loader import (
3✔
24
    load_backends_and_plugins,
25
    load_build_configuration_from_source,
26
)
27
from pants.init.plugin_resolver import PluginResolver
3✔
28
from pants.init.plugin_resolver import rules as plugin_resolver_rules
3✔
29
from pants.option.bootstrap_options import DynamicRemoteOptions
3✔
30
from pants.option.errors import UnknownFlagsError
3✔
31
from pants.option.options import Options
3✔
32
from pants.option.options_bootstrapper import OptionsBootstrapper
3✔
33
from pants.util.requirements import parse_requirements_file
3✔
34

35
logger = logging.getLogger(__name__)
3✔
36

37

38
def _initialize_build_configuration(
3✔
39
    plugin_resolver: PluginResolver,
40
    options_bootstrapper: OptionsBootstrapper,
41
    env: CompleteEnvironmentVars,
42
) -> BuildConfiguration:
43
    """Initialize a BuildConfiguration for the given OptionsBootstrapper.
44

45
    NB: This method:
46
      1. has the side effect of (idempotently) adding PYTHONPATH entries for this process
47
      2. is expensive to call, because it might resolve plugins from the network
48
    """
49

50
    bootstrap_options = options_bootstrapper.bootstrap_options.for_global_scope()
×
51

52
    # Add any extra paths to python path (e.g., for loading extra source backends).
53
    for path in bootstrap_options.pythonpath:
×
54
        if path not in sys.path:
×
55
            sys.path.append(path)
×
56
            pkg_resources.fixup_namespace_packages(path)
×
57

58
    # Resolve the actual Python code for any plugins. `sys.path` is modified as a side effect if
59
    # plugins were configured.
60
    backends_requirements = _collect_backends_requirements(bootstrap_options.backend_packages)
×
61
    plugin_resolver.resolve(options_bootstrapper, env, backends_requirements)
×
62

63
    # Load plugins and backends.
64
    return load_backends_and_plugins(
×
65
        bootstrap_options.plugins,
66
        bootstrap_options.backend_packages,
67
    )
68

69

70
def _collect_backends_requirements(backends: list[str]) -> list[str]:
3✔
71
    """Collects backend package dependencies, in case those are declared in an adjacent
72
    requirements.txt. Ignores any loading errors, assuming those will be later on handled by the
73
    backends loader.
74

75
    :param backends: An list of packages to load v2 backends requirements from.
76
    """
77
    requirements = []
×
78

79
    for backend_package in backends:
×
80
        try:
×
81
            backend_package_spec = importlib.util.find_spec(backend_package)
×
82
        except ModuleNotFoundError:
×
83
            continue
×
84

85
        if backend_package_spec is None:
×
86
            continue
×
87

88
        if backend_package_spec.origin is None:
×
89
            logger.warning(
×
90
                f"Can not check requirements for backend: '{backend_package}'. A __init__.py file is probably missing."
91
            )
92
            continue
×
93

94
        requirements_txt_file_path = Path(backend_package_spec.origin).parent.joinpath(
×
95
            "requirements.txt"
96
        )
97
        if requirements_txt_file_path.exists():
×
98
            content = requirements_txt_file_path.read_text()
×
99
            backend_package_requirements = [
×
100
                str(r)
101
                for r in parse_requirements_file(content, rel_path=str(requirements_txt_file_path))
102
            ]
103
            requirements.extend(backend_package_requirements)
×
104

105
    return requirements
×
106

107

108
def create_bootstrap_scheduler(
3✔
109
    options_bootstrapper: OptionsBootstrapper, executor: PyExecutor
110
) -> BootstrapScheduler:
111
    bc_builder = BuildConfiguration.Builder()
×
112
    # To load plugins, we only need access to the Python/PEX rules.
113
    load_build_configuration_from_source(bc_builder, ["pants.backend.python"])
×
114
    # And to plugin-loading-specific rules.
115
    bc_builder.register_rules("_dummy_for_bootstrapping_", plugin_resolver_rules())
×
116
    # We allow unrecognized options to defer any option error handling until post-bootstrap.
117
    bc_builder.allow_unknown_options()
×
118
    return BootstrapScheduler(
×
119
        EngineInitializer.setup_graph(
120
            options_bootstrapper.bootstrap_options.for_global_scope(),
121
            bc_builder.create(),
122
            DynamicRemoteOptions.disabled(),
123
            executor,
124
            is_bootstrap=True,
125
        ).scheduler
126
    )
127

128

129
class OptionsInitializer:
3✔
130
    """Initializes BuildConfiguration and Options instances given an OptionsBootstrapper.
131

132
    NB: Although this constructor takes an instance of the OptionsBootstrapper, it is
133
    used only to construct a "bootstrap" Scheduler: actual calls to resolve plugins use a
134
    per-request instance of the OptionsBootstrapper, which might request different plugins.
135

136
    TODO: We would eventually like to use the bootstrap Scheduler to construct the
137
    OptionsBootstrapper as well, but for now we do the opposite thing, and the Scheduler is
138
    used only to resolve plugins.
139
      see https://github.com/pantsbuild/pants/pull/11568
140
    """
141

142
    def __init__(
3✔
143
        self,
144
        options_bootstrapper: OptionsBootstrapper,
145
        executor: PyExecutor,
146
    ) -> None:
147
        self._bootstrap_scheduler = create_bootstrap_scheduler(options_bootstrapper, executor)
×
148
        self._plugin_resolver = PluginResolver(self._bootstrap_scheduler)
×
149

150
    def build_config(
3✔
151
        self,
152
        options_bootstrapper: OptionsBootstrapper,
153
        env: CompleteEnvironmentVars,
154
    ) -> BuildConfiguration:
155
        return _initialize_build_configuration(self._plugin_resolver, options_bootstrapper, env)
×
156

157
    def options(
3✔
158
        self,
159
        options_bootstrapper: OptionsBootstrapper,
160
        env: CompleteEnvironmentVars,
161
        build_config: BuildConfiguration,
162
        union_membership: UnionMembership,
163
        *,
164
        raise_: bool,
165
    ) -> Options:
166
        with self.handle_unknown_flags(options_bootstrapper, env, raise_=raise_):
×
167
            return options_bootstrapper.full_options(
×
168
                build_config.known_scope_infos, union_membership, build_config.allow_unknown_options
169
            )
170

171
    @contextmanager
3✔
172
    def handle_unknown_flags(
3✔
173
        self,
174
        options_bootstrapper: OptionsBootstrapper,
175
        env: CompleteEnvironmentVars,
176
        *,
177
        raise_: bool,
178
    ) -> Iterator[None]:
179
        """If there are any unknown flags, print "Did you mean?" and possibly error."""
180
        try:
×
181
            yield
×
182
        except UnknownFlagsError as err:
×
183
            build_config = _initialize_build_configuration(
×
184
                self._plugin_resolver, options_bootstrapper, env
185
            )
186
            # We need an options instance in order to get "did you mean" suggestions, but we know
187
            # there are bad flags in the args, so we generate options with no flags.
188
            no_arg_bootstrapper = dataclasses.replace(
×
189
                options_bootstrapper, args=("dummy_first_arg",)
190
            )
191
            options = no_arg_bootstrapper.full_options(
×
192
                build_config.known_scope_infos,
193
                union_membership=UnionMembership.empty(),
194
            )
195
            FlagErrorHelpPrinter(options).handle_unknown_flags(err)
×
196
            if raise_:
×
197
                raise err
×
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