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

pantsbuild / pants / 20826906489

08 Jan 2026 06:09PM UTC coverage: 80.192% (-0.08%) from 80.274%
20826906489

Pull #22987

github

web-flow
Merge aa52dd80f into 0d471f924
Pull Request #22987: WIP DRAFT: Pex 2.77.1 tests

3 of 3 new or added lines in 1 file covered. (100.0%)

90 existing lines in 15 files now uncovered.

78714 of 98157 relevant lines covered (80.19%)

3.36 hits per line

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

99.03
/src/python/pants/backend/python/util_rules/local_dists_pep660_test.py
1
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
1✔
5

6
import io
1✔
7
import json
1✔
8
import zipfile
1✔
9
from pathlib import PurePath
1✔
10
from textwrap import dedent
1✔
11

12
import pytest
1✔
13

14
from pants.backend.python import target_types_rules
1✔
15
from pants.backend.python.macros.python_artifact import PythonArtifact
1✔
16
from pants.backend.python.subsystems.setuptools import rules as setuptools_rules
1✔
17
from pants.backend.python.target_types import PythonDistribution, PythonSourcesGeneratorTarget
1✔
18
from pants.backend.python.util_rules import local_dists_pep660, pex_from_targets
1✔
19
from pants.backend.python.util_rules.dists import BuildSystem, DistBuildRequest
1✔
20
from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints
1✔
21
from pants.backend.python.util_rules.local_dists_pep660 import (
1✔
22
    EditableLocalDists,
23
    EditableLocalDistsRequest,
24
    PEP660BuildResult,
25
    ResolveSortedPythonDistributionTargets,
26
)
27
from pants.backend.python.util_rules.pex_from_targets import InterpreterConstraintsRequest
1✔
28
from pants.backend.python.util_rules.pex_requirements import PexRequirements
1✔
29
from pants.build_graph.address import Address
1✔
30
from pants.engine.fs import CreateDigest, Digest, DigestContents, FileContent
1✔
31
from pants.engine.internals.parametrize import Parametrize
1✔
32
from pants.testutil.python_interpreter_selection import skip_unless_python39_present
1✔
33
from pants.testutil.python_rule_runner import PythonRuleRunner
1✔
34
from pants.testutil.rule_runner import QueryRule
1✔
35
from pants.util.frozendict import FrozenDict
1✔
36

37

38
@pytest.fixture
1✔
39
def rule_runner() -> PythonRuleRunner:
1✔
40
    ret = PythonRuleRunner(
1✔
41
        rules=[
42
            *local_dists_pep660.rules(),
43
            *setuptools_rules(),
44
            *target_types_rules.rules(),
45
            *pex_from_targets.rules(),
46
            QueryRule(InterpreterConstraints, (InterpreterConstraintsRequest,)),
47
            QueryRule(ResolveSortedPythonDistributionTargets, ()),
48
            QueryRule(EditableLocalDists, (EditableLocalDistsRequest,)),
49
            QueryRule(PEP660BuildResult, (DistBuildRequest,)),
50
        ],
51
        target_types=[PythonSourcesGeneratorTarget, PythonDistribution],
52
        objects={"python_artifact": PythonArtifact, "parametrize": Parametrize},
53
    )
54
    ret.set_options(
1✔
55
        [],
56
        env_inherit={"PATH", "PYENV_ROOT", "HOME"},
57
    )
58
    return ret
1✔
59

60

61
def do_test_backend_wrapper(rule_runner: PythonRuleRunner, constraints: str) -> None:
1✔
62
    setup_py = "from setuptools import setup; setup(name='foobar', version='1.2.3')"
1✔
63
    input_digest = rule_runner.request(
1✔
64
        Digest, [CreateDigest([FileContent("setup.py", setup_py.encode())])]
65
    )
66
    req = DistBuildRequest(
1✔
67
        build_system=BuildSystem(
68
            PexRequirements(
69
                # NB: These are the last versions compatible with Python 2.7.
70
                ["setuptools==44.1.1", "wheel==0.37.1"]
71
            ),
72
            "setuptools.build_meta",
73
        ),
74
        interpreter_constraints=InterpreterConstraints([constraints]),
75
        build_wheel=True,
76
        build_sdist=True,
77
        input=input_digest,
78
        working_directory="",
79
        dist_source_root=".",
80
        build_time_source_roots=tuple(),
81
        output_path="dist",
82
        wheel_config_settings=FrozenDict({"setting1": ("value1",), "setting2": ("value2",)}),
83
    )
84
    res = rule_runner.request(PEP660BuildResult, [req])
1✔
85

UNCOV
86
    assert res.editable_wheel_path == "dist/foobar-1.2.3-0.editable-py3-none-any.whl"
×
87

88

89
@skip_unless_python39_present
1✔
90
def test_works_with_python39(rule_runner: PythonRuleRunner) -> None:
1✔
91
    do_test_backend_wrapper(rule_runner, constraints="CPython==3.9.*")
1✔
92

93

94
def test_sort_all_python_distributions_by_resolve(rule_runner: PythonRuleRunner) -> None:
1✔
95
    rule_runner.set_options(
1✔
96
        [
97
            "--python-enable-resolves=True",
98
            "--python-resolves={'a': 'lock.txt', 'b': 'lock.txt'}",
99
            # Turn off lockfile validation to make the test simpler.
100
            "--python-invalid-lockfile-behavior=ignore",
101
            # Turn off python synthetic lockfile targets to make the test simpler.
102
            "--no-python-enable-lockfile-targets",
103
        ],
104
        env_inherit={"PATH", "PYENV_ROOT", "HOME"},
105
    )
106
    rule_runner.write_files(
1✔
107
        {
108
            "foo/BUILD": dedent(
109
                """
110
                python_sources(resolve=parametrize("a", "b"))
111

112
                python_distribution(
113
                    name="dist",
114
                    dependencies=[":foo@resolve=a"],
115
                    provides=python_artifact(name="foo", version="9.8.7"),
116
                    sdist=False,
117
                    generate_setup=False,
118
                )
119
                """
120
            ),
121
            "foo/bar.py": "BAR = 42",
122
            "foo/setup.py": dedent(
123
                """
124
                from setuptools import setup
125

126
                setup(
127
                    name="foo",
128
                    version="9.8.7",
129
                    packages=["foo"],
130
                    package_dir={"foo": "."},
131
                    entry_points={"foo.plugins": ["bar = foo.bar.BAR"]},
132
                )
133
                """
134
            ),
135
            "lock.txt": "",
136
        }
137
    )
138
    dist = rule_runner.get_target(Address("foo", target_name="dist"))
1✔
139
    result = rule_runner.request(ResolveSortedPythonDistributionTargets, ())
1✔
140
    assert len(result.targets) == 1
1✔
141
    assert "b" not in result.targets
1✔
142
    assert "a" in result.targets
1✔
143
    assert len(result.targets["a"]) == 1
1✔
144
    assert dist == result.targets["a"][0]
1✔
145

146

147
def test_build_editable_local_dists(rule_runner: PythonRuleRunner) -> None:
1✔
148
    foo = PurePath("foo")
1✔
149
    rule_runner.write_files(
1✔
150
        {
151
            foo / "BUILD": dedent(
152
                """
153
            python_sources()
154

155
            python_distribution(
156
                name="dist",
157
                dependencies=[":foo"],
158
                provides=python_artifact(name="foo", version="9.8.7"),
159
                sdist=False,
160
                generate_setup=False,
161
            )
162
            """
163
            ),
164
            foo / "bar.py": "BAR = 42",
165
            foo / "setup.py": dedent(
166
                """
167
                from setuptools import setup
168

169
                setup(
170
                    name="foo",
171
                    version="9.8.7",
172
                    packages=["foo"],
173
                    package_dir={"foo": "."},
174
                    entry_points={"foo.plugins": ["bar = foo.bar.BAR"]},
175
                )
176
                """
177
            ),
178
        }
179
    )
180
    request = EditableLocalDistsRequest(
1✔
181
        resolve=None,  # resolves is disabled
182
    )
183
    result = rule_runner.request(EditableLocalDists, [request])
1✔
184

185
    assert result.optional_digest is not None
1✔
186
    contents = rule_runner.request(DigestContents, [result.optional_digest])
1✔
187
    assert len(contents) == 1
1✔
188
    whl_content = contents[0]
1✔
189
    assert whl_content
1✔
190
    assert whl_content.path == "foo-9.8.7-0.editable-py3-none-any.whl"
1✔
191
    with io.BytesIO(whl_content.content) as fp:
1✔
192
        with zipfile.ZipFile(fp, "r") as whl:
1✔
193
            whl_files = whl.namelist()
1✔
194
            # Check that sources are not present in editable wheel
195
            assert "foo/bar.py" not in whl_files
1✔
196
            assert "foo/qux.py" not in whl_files
1✔
197
            # Check that pth and metadata files are present in editable wheel
198
            assert "foo__pants__.pth" in whl_files
1✔
199
            assert "foo-9.8.7.dist-info/METADATA" in whl_files
1✔
200
            assert "foo-9.8.7.dist-info/RECORD" in whl_files
1✔
201
            assert "foo-9.8.7.dist-info/WHEEL" in whl_files
1✔
202
            assert "foo-9.8.7.dist-info/direct_url__pants__.json" in whl_files
1✔
203
            assert "foo-9.8.7.dist-info/entry_points.txt" in whl_files
1✔
204

205

206
def test_build_editable_local_dists_special_files(rule_runner: PythonRuleRunner) -> None:
1✔
207
    # we need multiple source roots to make sure that any dependencies
208
    # from other source roots do not end up listed as the direct_url.
209
    pkgs = ("a", "b", "c")
1✔
210
    for pkg in pkgs:
1✔
211
        root = PurePath(f"root_{pkg}")
1✔
212
        rule_runner.write_files(
1✔
213
            {
214
                root / pkg / "BUILD": "python_sources()\n",
215
                root / pkg / "__init__.py": "",
216
                root / pkg / "bar.py": "BAR = 42" if pkg == "a" else "from a.bar import BAR",
217
                root / "BUILD": dedent(
218
                    f"""
219
                    python_sources()
220

221
                    python_distribution(
222
                        name="dist",
223
                        dependencies=["./{pkg}", ":root_{pkg}"],
224
                        provides=python_artifact(name="{pkg}", version="9.8.7"),
225
                        sdist=False,
226
                        generate_setup=False,
227
                    )
228
                    """
229
                ),
230
                root / "setup.py": dedent(
231
                    f"""
232
                    from setuptools import setup
233

234
                    setup(
235
                        name="{pkg}",
236
                        version="9.8.7",
237
                        packages=["{pkg}"],
238
                        entry_points={{"foo.plugins": ["{pkg} = {pkg}.bar.BAR"]}},
239
                    )
240
                    """
241
                ),
242
            }
243
        )
244

245
    args = [
1✔
246
        "--source-root-patterns=root_*",
247
    ]
248
    rule_runner.set_options(args, env_inherit={"PATH", "PYENV_ROOT", "HOME"})
1✔
249

250
    request = EditableLocalDistsRequest(
1✔
251
        resolve=None,  # resolves is disabled
252
    )
253
    result = rule_runner.request(EditableLocalDists, [request])
1✔
254

255
    assert result.optional_digest is not None
1✔
256
    contents = rule_runner.request(DigestContents, [result.optional_digest])
1✔
257
    assert len(pkgs) == len(contents)
1✔
258
    for pkg, whl_content in zip(pkgs, contents):
1✔
259
        assert whl_content
1✔
260
        assert whl_content.path == f"{pkg}-9.8.7-0.editable-py3-none-any.whl"
1✔
261
        with io.BytesIO(whl_content.content) as fp:
1✔
262
            with zipfile.ZipFile(fp, "r") as whl:
1✔
263
                whl_files = whl.namelist()
1✔
264

265
                pth_path = f"{pkg}__pants__.pth"
1✔
266
                assert pth_path in whl_files
1✔
267
                pth_contents = zipfile.Path(whl, pth_path).read_text()
1✔
268
                pth_lines = pth_contents.split("\n")
1✔
269

270
                direct_url_path = f"{pkg}-9.8.7.dist-info/direct_url__pants__.json"
1✔
271
                assert direct_url_path in whl_files
1✔
272
                with whl.open(direct_url_path) as direct_url_contents:
1✔
273
                    direct_url = json.loads(direct_url_contents.read())
1✔
274

275
        # make sure inferred dep on "a" is included as well
276
        assert f"{rule_runner.build_root}/root_a" in pth_lines
1✔
277
        assert f"{rule_runner.build_root}/root_{pkg}" in pth_lines
1✔
278

279
        assert len(direct_url) == 2
1✔
280
        assert "dir_info" in direct_url
1✔
281
        assert direct_url["dir_info"] == {"editable": True}
1✔
282
        assert "url" in direct_url
1✔
283
        assert direct_url["url"] == f"file://{rule_runner.build_root}/root_{pkg}"
1✔
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