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

pantsbuild / pants / 26342152999

23 May 2026 07:59PM UTC coverage: 91.165% (-1.6%) from 92.792%
26342152999

push

github

web-flow
Run Linux ARM CI on Depot runners (#23363)

RunsOn is deprecating their v2 stack, and rather than migrate
to v3 we should use the resources kindly donated by Depot.

GitHub also now has Linux ARM runners, should we need them.

87305 of 95766 relevant lines covered (91.16%)

3.87 hits per line

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

98.21
/src/python/pants/backend/python/subsystems/pytest.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
8✔
5

6
import os.path
8✔
7
from collections.abc import Iterable
8✔
8
from dataclasses import dataclass
8✔
9

10
from pants.backend.python.subsystems.python_tool_base import PythonToolBase
8✔
11
from pants.backend.python.target_types import (
8✔
12
    ConsoleScript,
13
    InterpreterConstraintsField,
14
    PythonResolveField,
15
    PythonTestsBatchCompatibilityTagField,
16
    PythonTestsExtraEnvVarsField,
17
    PythonTestSourceField,
18
    PythonTestsTimeoutField,
19
    PythonTestsXdistConcurrencyField,
20
    SkipPythonTestsField,
21
)
22
from pants.core.environments.target_types import EnvironmentField
8✔
23
from pants.core.goals.resolves import ExportableTool
8✔
24
from pants.core.goals.test import RuntimePackageDependenciesField, TestFieldSet
8✔
25
from pants.core.util_rules.config_files import ConfigFilesRequest
8✔
26
from pants.engine.rules import collect_rules
8✔
27
from pants.engine.target import Target
8✔
28
from pants.engine.unions import UnionRule
8✔
29
from pants.option.option_types import ArgsListOption, BoolOption, FileOption, SkipOption, StrOption
8✔
30
from pants.util.strutil import softwrap
8✔
31

32
# pants: infer-dep(pytest.lock*)
33

34

35
@dataclass(frozen=True)
8✔
36
class PythonTestFieldSet(TestFieldSet):
8✔
37
    required_fields = (PythonTestSourceField,)
8✔
38

39
    source: PythonTestSourceField
8✔
40
    interpreter_constraints: InterpreterConstraintsField
8✔
41
    timeout: PythonTestsTimeoutField
8✔
42
    runtime_package_dependencies: RuntimePackageDependenciesField
8✔
43
    extra_env_vars: PythonTestsExtraEnvVarsField
8✔
44
    xdist_concurrency: PythonTestsXdistConcurrencyField
8✔
45
    batch_compatibility_tag: PythonTestsBatchCompatibilityTagField
8✔
46
    resolve: PythonResolveField
8✔
47
    environment: EnvironmentField
8✔
48

49
    @classmethod
8✔
50
    def opt_out(cls, tgt: Target) -> bool:
8✔
51
        return tgt.get(SkipPythonTestsField).value
×
52

53

54
class PyTest(PythonToolBase):
8✔
55
    options_scope = "pytest"
8✔
56
    name = "Pytest"
8✔
57
    help_short = "The pytest Python test framework (https://docs.pytest.org/)."
8✔
58

59
    default_requirements = [
8✔
60
        "pytest>=7,<9,!=7.1.0,!=7.1.1",
61
        "pytest-cov>=5,<7",
62
        "pytest-xdist>=3.6.1,<4",
63
    ]
64

65
    default_main = ConsoleScript("pytest")
8✔
66

67
    default_lockfile_resource = ("pants.backend.python.subsystems", "pytest.lock")
8✔
68

69
    args = ArgsListOption(example="-k test_foo --quiet", passthrough=True)
8✔
70
    junit_family = StrOption(
8✔
71
        default="xunit2",
72
        advanced=True,
73
        help=softwrap(
74
            """
75
            The format of generated junit XML files. See
76
            https://docs.pytest.org/en/latest/reference.html#confval-junit_family.
77
            """
78
        ),
79
    )
80
    execution_slot_var = StrOption(
8✔
81
        default=None,
82
        advanced=True,
83
        help=softwrap(
84
            """
85
            If a non-empty string, the process execution slot id (an integer) will be exposed
86
            to tests under this environment variable name.
87
            """
88
        ),
89
    )
90
    config = FileOption(
8✔
91
        default=None,
92
        advanced=True,
93
        help=lambda cls: softwrap(
94
            f"""
95
            Path to a config file understood by Pytest
96
            (https://docs.pytest.org/en/latest/reference/customize.html#configuration-file-formats).
97
            Setting this option will disable `[{cls.options_scope}].config_discovery`. Use
98
            this option if the config is located in a non-standard location.
99
            """
100
        ),
101
    )
102
    config_discovery = BoolOption(
8✔
103
        default=True,
104
        advanced=True,
105
        help=lambda cls: softwrap(
106
            f"""
107
            If true, Pants will include all relevant Pytest config files (e.g. `pytest.ini`)
108
            during runs. See
109
            https://docs.pytest.org/en/stable/customize.html#finding-the-rootdir for where
110
            config files should be located for Pytest to discover them.
111

112
            Use `[{cls.options_scope}].config` instead if your config is in a
113
            non-standard location.
114
            """
115
        ),
116
    )
117
    xdist_enabled = BoolOption(
8✔
118
        default=False,
119
        advanced=False,
120
        help=softwrap(
121
            """
122
            If true, Pants will use `pytest-xdist` (https://pytest-xdist.readthedocs.io/en/latest/)
123
            to parallelize tests within each `python_test` target.
124

125
            NOTE: Enabling `pytest-xdist` can cause high-level scoped fixtures (for example `session`)
126
            to execute more than once. See the `pytest-xdist` docs for more info:
127
            https://pypi.org/project/pytest-xdist/#making-session-scoped-fixtures-execute-only-once
128
            """
129
        ),
130
    )
131
    allow_empty_test_collection = BoolOption(
8✔
132
        default=False,
133
        help=softwrap(
134
            """
135
            If true, treat pytest exit code 5 ("No tests were collected") as success.
136

137
            Pytest returns exit code 5 when no tests are collected, e.g., when all tests
138
            in a file are skipped via markers or deselected via `-k`. By default, Pants
139
            treats this as a test failure. Enable this option to instead treat it as
140
            "no tests found", which will be skipped in the test summary.
141

142
            See https://docs.pytest.org/en/stable/reference/exit-codes.html
143
            """
144
        ),
145
    )
146

147
    skip = SkipOption("test")
8✔
148

149
    def config_request(self, dirs: Iterable[str]) -> ConfigFilesRequest:
8✔
150
        # Refer to https://docs.pytest.org/en/stable/customize.html#finding-the-rootdir for how
151
        # config files are discovered.
152
        check_existence = []
2✔
153
        check_content = {}
2✔
154
        for d in ("", *dirs):
2✔
155
            check_existence.append(os.path.join(d, "pytest.ini"))
2✔
156
            check_content[os.path.join(d, "pyproject.toml")] = b"[tool.pytest.ini_options]"
2✔
157
            check_content[os.path.join(d, "tox.ini")] = b"[pytest]"
2✔
158
            check_content[os.path.join(d, "setup.cfg")] = b"[tool:pytest]"
2✔
159

160
        return ConfigFilesRequest(
2✔
161
            specified=self.config,
162
            specified_option_name=f"[{self.options_scope}].config",
163
            discovery=self.config_discovery,
164
            check_existence=check_existence,
165
            check_content=check_content,
166
        )
167

168

169
def rules():
8✔
170
    return (
6✔
171
        *collect_rules(),
172
        UnionRule(ExportableTool, PyTest),
173
    )
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