• 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

34.21
/src/python/pants/backend/python/lint/pylint/rules_integration_test.py
1
# Copyright 2020 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
from textwrap import dedent
3✔
7

8
import pytest
3✔
9

10
from pants.backend.python import target_types_rules
3✔
11
from pants.backend.python.lint.pylint import subsystem
3✔
12
from pants.backend.python.lint.pylint.rules import PartitionMetadata, PylintRequest
3✔
13
from pants.backend.python.lint.pylint.rules import rules as pylint_rules
3✔
14
from pants.backend.python.lint.pylint.subsystem import PylintFieldSet
3✔
15
from pants.backend.python.target_types import (
3✔
16
    PythonRequirementTarget,
17
    PythonSourcesGeneratorTarget,
18
    PythonSourceTarget,
19
)
20
from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints
3✔
21
from pants.core.goals.lint import LintResult, Partitions
3✔
22
from pants.core.util_rules import config_files
3✔
23
from pants.core.util_rules.partitions import Partition
3✔
24
from pants.engine.addresses import Address
3✔
25
from pants.engine.fs import DigestContents
3✔
26
from pants.engine.internals.native_engine import EMPTY_DIGEST
3✔
27
from pants.engine.target import Target
3✔
28
from pants.testutil.python_interpreter_selection import (
3✔
29
    all_major_minor_python_versions,
30
    skip_unless_all_pythons_present,
31
    skip_unless_python310_and_python311_present,
32
)
33
from pants.testutil.python_rule_runner import PythonRuleRunner
3✔
34
from pants.testutil.rule_runner import QueryRule
3✔
35
from pants.util.resources import read_resource, read_sibling_resource
3✔
36

37

38
@pytest.fixture
3✔
39
def rule_runner() -> PythonRuleRunner:
3✔
40
    return PythonRuleRunner(
3✔
41
        rules=[
42
            *pylint_rules(),
43
            *subsystem.rules(),
44
            *config_files.rules(),
45
            *target_types_rules.rules(),
46
            QueryRule(Partitions, [PylintRequest.PartitionRequest]),
47
            QueryRule(LintResult, [PylintRequest.Batch]),
48
        ],
49
        target_types=[PythonSourceTarget, PythonSourcesGeneratorTarget, PythonRequirementTarget],
50
    )
51

52

53
# See http://pylint.pycqa.org/en/latest/user_guide/run.html#exit-codes for exit codes.
54
PYLINT_ERROR_FAILURE_RETURN_CODE = 2
3✔
55
PYLINT_CONVENTION_FAILURE_RETURN_CODE = 16
3✔
56

57
PACKAGE = "src/python/project"
3✔
58
GOOD_FILE = "'''docstring'''\nUPPERCASE_CONSTANT = ''\n"
3✔
59
BAD_FILE = "'''docstring'''\nlowercase_constant = ''\n"
3✔
60

61

62
def run_pylint(
3✔
63
    rule_runner: PythonRuleRunner,
64
    targets: list[Target],
65
    *,
66
    extra_args: list[str] | None = None,
67
) -> tuple[LintResult, ...]:
68
    rule_runner.set_options(
3✔
69
        [
70
            "--backend-packages=pants.backend.python.lint.pylint",
71
            "--source-root-patterns=['src/python', 'tests/python']",
72
            *(extra_args or ()),
73
        ],
74
        env_inherit={"PATH", "PYENV_ROOT", "HOME"},
75
    )
76
    partitions = rule_runner.request(
3✔
77
        Partitions[PylintFieldSet, PartitionMetadata],
78
        [PylintRequest.PartitionRequest(tuple(PylintFieldSet.create(tgt) for tgt in targets))],
79
    )
80
    results = []
3✔
81
    for partition in partitions:
3✔
82
        result = rule_runner.request(
3✔
83
            LintResult,
84
            [PylintRequest.Batch("", partition.elements, partition.metadata)],
85
        )
86
        results.append(result)
3✔
87
    return tuple(results)
3✔
88

89

90
def assert_success(
3✔
91
    rule_runner: PythonRuleRunner, target: Target, *, extra_args: list[str] | None = None
92
) -> None:
93
    result = run_pylint(rule_runner, [target], extra_args=extra_args)
3✔
94
    assert len(result) == 1
3✔
95
    assert "Your code has been rated at 10.00/10" in result[0].stdout
3✔
96
    assert result[0].exit_code == 0
3✔
97
    assert result[0].report == EMPTY_DIGEST
3✔
98

99

100
@pytest.mark.platform_specific_behavior
3✔
101
@pytest.mark.parametrize(
3✔
102
    "major_minor_interpreter",
103
    all_major_minor_python_versions(["CPython>=3.9,<4"]),
104
)
105
def test_passing(rule_runner: PythonRuleRunner, major_minor_interpreter: str) -> None:
3✔
106
    rule_runner.write_files({f"{PACKAGE}/f.py": GOOD_FILE, f"{PACKAGE}/BUILD": "python_sources()"})
3✔
107
    tgt = rule_runner.get_target(Address(PACKAGE, relative_file_path="f.py"))
3✔
108
    assert_success(
3✔
109
        rule_runner,
110
        tgt,
111
        extra_args=[f"--python-interpreter-constraints=['=={major_minor_interpreter}.*']"],
112
    )
113

114

115
def test_failing(rule_runner: PythonRuleRunner) -> None:
3✔
116
    rule_runner.write_files({f"{PACKAGE}/f.py": BAD_FILE, f"{PACKAGE}/BUILD": "python_sources()"})
×
117
    tgt = rule_runner.get_target(Address(PACKAGE, relative_file_path="f.py"))
×
118
    result = run_pylint(rule_runner, [tgt])
×
119
    assert len(result) == 1
×
120
    assert result[0].exit_code == PYLINT_CONVENTION_FAILURE_RETURN_CODE
×
121
    assert f"{PACKAGE}/f.py:2:0: C0103" in result[0].stdout
×
122
    assert result[0].report == EMPTY_DIGEST
×
123

124

125
def test_report_file(rule_runner: PythonRuleRunner) -> None:
3✔
126
    rule_runner.write_files({f"{PACKAGE}/f.py": BAD_FILE, f"{PACKAGE}/BUILD": "python_sources()"})
×
127
    tgt = rule_runner.get_target(Address(PACKAGE, relative_file_path="f.py"))
×
128
    result = run_pylint(
×
129
        rule_runner, [tgt], extra_args=["--pylint-args='--output=reports/output.txt'"]
130
    )
131
    assert len(result) == 1
×
132
    assert result[0].exit_code == PYLINT_CONVENTION_FAILURE_RETURN_CODE
×
133
    assert result[0].stdout.strip() == ""
×
134
    report_files = rule_runner.request(DigestContents, [result[0].report])
×
135
    assert len(report_files) == 1
×
136
    assert f"{PACKAGE}/f.py:2:0: C0103" in report_files[0].content.decode()
×
137

138

139
def test_multiple_targets(rule_runner: PythonRuleRunner) -> None:
3✔
140
    rule_runner.write_files(
×
141
        {
142
            f"{PACKAGE}/good.py": GOOD_FILE,
143
            f"{PACKAGE}/bad.py": BAD_FILE,
144
            f"{PACKAGE}/BUILD": "python_sources()",
145
        }
146
    )
147
    tgts = [
×
148
        rule_runner.get_target(Address(PACKAGE, relative_file_path="good.py")),
149
        rule_runner.get_target(Address(PACKAGE, relative_file_path="bad.py")),
150
    ]
151
    result = run_pylint(rule_runner, tgts)
×
152
    assert len(result) == 1
×
153
    assert result[0].exit_code == PYLINT_CONVENTION_FAILURE_RETURN_CODE
×
154
    assert f"{PACKAGE}/good.py" not in result[0].stdout
×
155
    assert f"{PACKAGE}/bad.py:2:0: C0103" in result[0].stdout
×
156
    assert result[0].report == EMPTY_DIGEST
×
157

158

159
@skip_unless_python310_and_python311_present
3✔
160
def test_uses_correct_python_version(rule_runner: PythonRuleRunner) -> None:
3✔
161
    rule_runner.write_files(
×
162
        {
163
            # ExceptionGroup was introduced in 3.11.
164
            f"{PACKAGE}/f.py": "'''docstring'''\neg = ExceptionGroup('', [Exception()])\n",
165
            f"{PACKAGE}/BUILD": dedent(
166
                """\
167
                python_sources(name='py310', interpreter_constraints=['CPython==3.10.*'])
168
                python_sources(name='py311', interpreter_constraints=['CPython==3.11.*'])
169
                """
170
            ),
171
        }
172
    )
173

174
    py310_tgt = rule_runner.get_target(
×
175
        Address(PACKAGE, target_name="py310", relative_file_path="f.py")
176
    )
177
    py310_result = run_pylint(rule_runner, [py310_tgt])
×
178
    assert len(py310_result) == 1
×
179
    assert py310_result[0].exit_code == 2
×
180
    assert (
×
181
        "E0602: Undefined variable 'ExceptionGroup' (undefined-variable)" in py310_result[0].stdout
182
    )
183

184
    py311_tgt = rule_runner.get_target(
×
185
        Address(PACKAGE, target_name="py311", relative_file_path="f.py")
186
    )
187
    py311_result = run_pylint(rule_runner, [py311_tgt])
×
188
    assert len(py311_result) == 1
×
189
    assert py311_result[0].exit_code == 0
×
190
    assert "Your code has been rated at 10.00/10" in py311_result[0].stdout.strip()
×
191

192
    combined_result = run_pylint(rule_runner, [py310_tgt, py311_tgt])
×
193
    assert len(combined_result) == 2
×
194
    batched_py311_result, batched_py310_result = sorted(
×
195
        combined_result, key=lambda result: result.exit_code
196
    )
197

198
    assert batched_py310_result.exit_code == 2
×
199
    assert batched_py310_result.partition_description == "['CPython==3.10.*']"
×
200
    assert (
×
201
        "E0602: Undefined variable 'ExceptionGroup' (undefined-variable)"
202
        in batched_py310_result.stdout
203
    )
204

205
    assert batched_py311_result.exit_code == 0
×
206
    assert batched_py311_result.partition_description == "['CPython==3.11.*']"
×
207
    assert "Your code has been rated at 10.00/10" in batched_py311_result.stdout.strip()
×
208

209

210
@pytest.mark.parametrize(
3✔
211
    "config_path,extra_args",
212
    (["pylintrc", []], ["custom_config.ini", ["--pylint-config=custom_config.ini"]]),
213
)
214
def test_config_file(
3✔
215
    rule_runner: PythonRuleRunner, config_path: str, extra_args: list[str]
216
) -> None:
217
    rule_runner.write_files(
×
218
        {
219
            f"{PACKAGE}/f.py": BAD_FILE,
220
            f"{PACKAGE}/BUILD": "python_sources()",
221
            config_path: "[pylint]\ndisable = C0103",
222
        }
223
    )
224
    tgt = rule_runner.get_target(Address(PACKAGE, relative_file_path="f.py"))
×
225
    assert_success(rule_runner, tgt, extra_args=extra_args)
×
226

227

228
def test_passthrough_args(rule_runner: PythonRuleRunner) -> None:
3✔
229
    rule_runner.write_files({f"{PACKAGE}/f.py": BAD_FILE, f"{PACKAGE}/BUILD": "python_sources()"})
×
230
    tgt = rule_runner.get_target(Address(PACKAGE, relative_file_path="f.py"))
×
231
    assert_success(rule_runner, tgt, extra_args=["--pylint-args='--disable=C0103'"])
×
232

233

234
def test_skip(rule_runner: PythonRuleRunner) -> None:
3✔
235
    rule_runner.write_files({f"{PACKAGE}/f.py": BAD_FILE, f"{PACKAGE}/BUILD": "python_sources()"})
×
236
    tgt = rule_runner.get_target(Address(PACKAGE, relative_file_path="f.py"))
×
237
    result = run_pylint(rule_runner, [tgt], extra_args=["--pylint-skip"])
×
238
    assert not result
×
239

240

241
def test_includes_transitive_dependencies(rule_runner: PythonRuleRunner) -> None:
3✔
242
    rule_runner.write_files(
×
243
        {
244
            "BUILD": dedent(
245
                """\
246
                python_requirement(name='transitive_req', requirements=['freezegun'])
247
                python_requirement(name='direct_req', requirements=['ansicolors'])
248
                """
249
            ),
250
            f"{PACKAGE}/transitive_dep.py": dedent(
251
                """\
252
                import freezegun
253

254
                A = NotImplemented
255
                """
256
            ),
257
            f"{PACKAGE}/direct_dep.py": dedent(
258
                """\
259
                # No docstring - Pylint doesn't lint dependencies.
260

261
                from project.transitive_dep import A
262

263
                B = A
264
                """
265
            ),
266
            f"{PACKAGE}/f.py": dedent(
267
                """\
268
                '''Pylint should be upset about raising NotImplemented.'''
269
                from colors import green
270
                from project.direct_dep import B
271

272
                def i_just_raise():
273
                    '''A docstring.'''
274
                    print(green("hello"))
275
                    raise B  # pylint should error here
276
                """
277
            ),
278
            f"{PACKAGE}/BUILD": dedent(
279
                """\
280
                python_source(
281
                    name='transitive_dep',
282
                    source='transitive_dep.py',
283
                    dependencies=['//:transitive_req'],
284
                )
285
                python_source(
286
                    name='direct_dep',
287
                    source='direct_dep.py',
288
                    dependencies=[':transitive_dep']
289
                )
290
                python_source(
291
                    name="f",
292
                    source='f.py',
293
                    dependencies=['//:direct_req', ':direct_dep'],
294
                )
295
                """
296
            ),
297
        }
298
    )
299
    tgt = rule_runner.get_target(Address(PACKAGE, target_name="f"))
×
300
    result = run_pylint(rule_runner, [tgt])
×
301
    assert len(result) == 1
×
302
    assert result[0].exit_code == PYLINT_ERROR_FAILURE_RETURN_CODE
×
303
    assert f"{PACKAGE}/f.py:8:4: E0702" in result[0].stdout
×
304
    assert result[0].report == EMPTY_DIGEST
×
305

306

307
def test_pep420_namespace_packages(rule_runner: PythonRuleRunner) -> None:
3✔
308
    rule_runner.write_files(
×
309
        {
310
            f"{PACKAGE}/f.py": GOOD_FILE,
311
            f"{PACKAGE}/BUILD": "python_sources()",
312
            "tests/python/project/f2.py": dedent(
313
                """\
314
                '''Docstring.'''
315

316
                from project.f import UPPERCASE_CONSTANT
317

318
                CONSTANT2 = UPPERCASE_CONSTANT
319
                """
320
            ),
321
            "tests/python/project/BUILD": f"python_sources(dependencies=['{PACKAGE}'])",
322
        }
323
    )
324
    tgts = [
×
325
        rule_runner.get_target(Address(PACKAGE, relative_file_path="f.py")),
326
        rule_runner.get_target(Address("tests/python/project", relative_file_path="f2.py")),
327
    ]
328
    result = run_pylint(rule_runner, tgts)
×
329
    assert len(result) == 1
×
330
    assert result[0].exit_code == 0
×
331
    assert "Your code has been rated at 10.00/10" in result[0].stdout.strip()
×
332
    assert result[0].report == EMPTY_DIGEST
×
333

334

335
def test_type_stubs(rule_runner: PythonRuleRunner) -> None:
3✔
336
    # If an implementation file shares the same name as a type stub, Pylint will only check the
337
    # implementation file. So, here, we only check running directly on a type stub.
338
    rule_runner.write_files({f"{PACKAGE}/f.pyi": BAD_FILE, f"{PACKAGE}/BUILD": "python_sources()"})
×
339
    tgt = rule_runner.get_target(Address(PACKAGE, relative_file_path="f.pyi"))
×
340
    result = run_pylint(rule_runner, [tgt])
×
341
    assert len(result) == 1
×
342
    assert result[0].exit_code == PYLINT_CONVENTION_FAILURE_RETURN_CODE
×
343
    assert f"{PACKAGE}/f.pyi:2:0: C0103" in result[0].stdout
×
344
    assert result[0].report == EMPTY_DIGEST
×
345

346

347
def test_3rdparty_plugin(rule_runner: PythonRuleRunner) -> None:
3✔
348
    rule_runner.write_files(
×
349
        {
350
            f"{PACKAGE}/f.py": dedent(
351
                """\
352
                '''Docstring.'''
353

354
                import unittest
355

356
                class PluginTest(unittest.TestCase):
357
                    '''Docstring.'''
358

359
                    def test_plugin(self):
360
                        '''Docstring.'''
361
                        self.assertEqual(True, True)
362
                """
363
            ),
364
            f"{PACKAGE}/BUILD": "python_sources()",
365
            "pylint.lock": read_resource(
366
                "pants.backend.python.lint.pylint", "pylint_3rdparty_plugin_test.lock"
367
            ),
368
        }
369
    )
370
    tgt = rule_runner.get_target(Address(PACKAGE, relative_file_path="f.py"))
×
371
    result = run_pylint(
×
372
        rule_runner,
373
        [tgt],
374
        extra_args=[
375
            "--python-resolves={'pylint':'pylint.lock'}",
376
            "--pylint-install-from-resolve=pylint",
377
            "--pylint-args='--load-plugins=pylint_unittest'",
378
        ],
379
    )
380
    assert len(result) == 1
×
381
    assert result[0].exit_code == 4
×
382
    assert f"{PACKAGE}/f.py:10:8: W5301" in result[0].stdout
×
383
    assert result[0].report == EMPTY_DIGEST
×
384

385

386
def test_source_plugin(rule_runner: PythonRuleRunner) -> None:
3✔
387
    # NB: We make this source plugin fairly complex by having it use transitive dependencies.
388
    # This is to ensure that we can correctly support plugins with dependencies.
389
    # The plugin bans `print()`.
390
    rule_runner.write_files(
×
391
        {
392
            "BUILD": dedent(
393
                """\
394
                python_requirement(name='pylint', requirements=['pylint>=2.13.0,<2.15'])
395
                python_requirement(name='colors', requirements=['ansicolors'])
396
                """
397
            ),
398
            "pylint.lock": read_sibling_resource(__name__, "pylint_source_plugin_test.lock"),
399
            "pants-plugins/plugins/subdir/dep.py": dedent(
400
                """\
401
                from colors import red
402

403
                def is_print(node):
404
                    _ = red("Test that transitive deps are loaded.")
405
                    return hasattr(node.func, "name") and node.func.name == "print"
406
                """
407
            ),
408
            "pants-plugins/plugins/subdir/BUILD": "python_sources(dependencies=['//:colors'])",
409
            "pants-plugins/plugins/print_plugin.py": dedent(
410
                """\
411
                '''Docstring.'''
412

413
                from pylint.checkers import BaseChecker
414
                from pylint.interfaces import IAstroidChecker
415

416
                from subdir.dep import is_print
417

418
                class PrintChecker(BaseChecker):
419
                    '''Docstring.'''
420

421
                    __implements__ = IAstroidChecker
422
                    name = "print_plugin"
423
                    msgs = {
424
                        "C9871": ("`print` statements are banned", "print-statement-used", ""),
425
                    }
426

427
                    def visit_call(self, node):
428
                        '''Docstring.'''
429
                        if is_print(node):
430
                            self.add_message("print-statement-used", node=node)
431

432

433
                def register(linter):
434
                    '''Docstring.'''
435
                    linter.register_checker(PrintChecker(linter))
436
                """
437
            ),
438
            "pants-plugins/plugins/BUILD": (
439
                "python_sources(dependencies=['//:pylint', 'pants-plugins/plugins/subdir'])"
440
            ),
441
            "pylintrc": dedent(
442
                """\
443
                [MASTER]
444
                load-plugins=print_plugin
445
                """
446
            ),
447
            f"{PACKAGE}/f.py": "'''Docstring.'''\nprint()\n",
448
            f"{PACKAGE}/BUILD": "python_sources()",
449
        }
450
    )
451

452
    def run_pylint_with_plugin(tgt: Target) -> LintResult:
×
453
        res = run_pylint(
×
454
            rule_runner,
455
            [tgt],
456
            extra_args=[
457
                "--pylint-source-plugins=['pants-plugins/plugins']",
458
                f"--source-root-patterns=['pants-plugins/plugins', '{PACKAGE}']",
459
                "--python-resolves={'pylint':'pylint.lock'}",
460
                "--pylint-install-from-resolve=pylint",
461
            ],
462
        )
463
        assert len(res) == 1
×
464
        return res[0]
×
465

466
    tgt = rule_runner.get_target(Address(PACKAGE, relative_file_path="f.py"))
×
467
    result = run_pylint_with_plugin(tgt)
×
468
    assert result.exit_code == PYLINT_CONVENTION_FAILURE_RETURN_CODE
×
469
    assert f"{PACKAGE}/f.py:2:0: C9871" in result.stdout
×
470
    assert result.report == EMPTY_DIGEST
×
471

472
    # Ensure that running Pylint on the plugin itself still works.
473
    plugin_tgt = rule_runner.get_target(
×
474
        Address("pants-plugins/plugins", relative_file_path="print_plugin.py")
475
    )
476
    result = run_pylint_with_plugin(plugin_tgt)
×
477
    print(result.stdout)
×
478
    assert result.exit_code == 0
×
479
    assert "Your code has been rated at 10.00/10" in result.stdout
×
480
    assert result.report == EMPTY_DIGEST
×
481

482

483
@skip_unless_all_pythons_present("3.8", "3.9")
3✔
484
def test_partition_targets(rule_runner: PythonRuleRunner) -> None:
3✔
485
    def create_folder(folder: str, resolve: str, interpreter: str) -> dict[str, str]:
×
486
        return {
×
487
            f"{folder}/dep.py": "",
488
            f"{folder}/root.py": "",
489
            f"{folder}/BUILD": dedent(
490
                f"""\
491
                python_source(
492
                    name='dep',
493
                    source='dep.py',
494
                    resolve='{resolve}',
495
                    interpreter_constraints=['=={interpreter}.*'],
496
                )
497
                python_source(
498
                    name='root',
499
                    source='root.py',
500
                    resolve='{resolve}',
501
                    interpreter_constraints=['=={interpreter}.*'],
502
                    dependencies=[':dep'],
503
                )
504
                """
505
            ),
506
        }
507

508
    files = {
×
509
        **create_folder("resolveA_py38", "a", "3.8"),
510
        **create_folder("resolveA_py39", "a", "3.9"),
511
        **create_folder("resolveB_1", "b", "3.9"),
512
        **create_folder("resolveB_2", "b", "3.9"),
513
    }
514
    rule_runner.write_files(files)
×
515
    rule_runner.set_options(
×
516
        ["--python-resolves={'a': '', 'b': ''}", "--python-enable-resolves"],
517
        env_inherit={"PATH", "PYENV_ROOT", "HOME"},
518
    )
519

520
    resolve_a_py38_dep = rule_runner.get_target(Address("resolveA_py38", target_name="dep"))
×
521
    resolve_a_py38_root = rule_runner.get_target(Address("resolveA_py38", target_name="root"))
×
522
    resolve_a_py39_dep = rule_runner.get_target(Address("resolveA_py39", target_name="dep"))
×
523
    resolve_a_py39_root = rule_runner.get_target(Address("resolveA_py39", target_name="root"))
×
524
    resolve_b_dep1 = rule_runner.get_target(Address("resolveB_1", target_name="dep"))
×
525
    resolve_b_root1 = rule_runner.get_target(Address("resolveB_1", target_name="root"))
×
526
    resolve_b_dep2 = rule_runner.get_target(Address("resolveB_2", target_name="dep"))
×
527
    resolve_b_root2 = rule_runner.get_target(Address("resolveB_2", target_name="root"))
×
528
    request: PylintRequest.PartitionRequest[PylintFieldSet] = PylintRequest.PartitionRequest(
×
529
        tuple(
530
            PylintFieldSet.create(t)
531
            for t in (
532
                resolve_a_py38_root,
533
                resolve_a_py39_root,
534
                resolve_b_root1,
535
                resolve_b_root2,
536
            )
537
        )
538
    )
539

540
    partitions = list(rule_runner.request(Partitions[PylintFieldSet, PartitionMetadata], [request]))
×
541
    assert len(partitions) == 3
×
542

543
    def assert_partition(
×
544
        partition: Partition,
545
        roots: list[Target],
546
        deps: list[Target],
547
        interpreter: str,
548
        resolve: str,
549
    ) -> None:
550
        assert partition.metadata is not None
×
551
        key = partition.metadata
×
552

553
        root_addresses = {t.address for t in roots}
×
554
        assert {t.address for t in key.coarsened_targets.closure()} == {
×
555
            *root_addresses,
556
            *(t.address for t in deps),
557
        }
558
        ics = [f"CPython=={interpreter}.*"]
×
559
        assert key.interpreter_constraints == InterpreterConstraints(ics)
×
560
        assert key.description == f"{resolve}, {ics}"
×
561

562
    assert_partition(partitions[0], [resolve_a_py38_root], [resolve_a_py38_dep], "3.8", "a")
×
563
    assert_partition(partitions[1], [resolve_a_py39_root], [resolve_a_py39_dep], "3.9", "a")
×
564
    assert_partition(
×
565
        partitions[2],
566
        [resolve_b_root1, resolve_b_root2],
567
        [resolve_b_dep1, resolve_b_dep2],
568
        "3.9",
569
        "b",
570
    )
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