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

pantsbuild / pants / 19047803412

03 Nov 2025 08:06PM UTC coverage: 80.295% (-0.004%) from 80.299%
19047803412

push

github

web-flow
rust: upgrade to v1.91.0 (#22762)

Upgrade to Rust v1.91.0.

77989 of 97128 relevant lines covered (80.3%)

3.08 hits per line

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

81.25
/src/python/pants/testutil/python_interpreter_selection.py
1
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
10✔
5

6
import os
10✔
7
import subprocess
10✔
8
from collections.abc import Iterable
10✔
9
from functools import lru_cache
10✔
10
from unittest import skipIf
10✔
11

12
import _pytest.mark.structures
10✔
13
import pytest
10✔
14

15
from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints
10✔
16

17
PY_2 = "2"
10✔
18
PY_3 = "3"
10✔
19

20
PY_27 = "2.7"
10✔
21
PY_36 = "3.6"
10✔
22
PY_37 = "3.7"
10✔
23
PY_38 = "3.8"
10✔
24
PY_39 = "3.9"
10✔
25
PY_310 = "3.10"
10✔
26
PY_311 = "3.11"
10✔
27

28

29
def has_python_version(version):
10✔
30
    """Returns `True` if the current system has the specified version of python.
31

32
    :param string version: A python version string, such as 2.7, 3.
33
    """
34
    # TODO: Tests that skip unless a python interpreter is present often need the path to that
35
    # interpreter, and so end up calling python_interpreter_path again. Find a way to streamline this.
36
    return python_interpreter_path(version) is not None
10✔
37

38

39
@lru_cache
10✔
40
def python_interpreter_path(version):
10✔
41
    """Returns the interpreter path if the current system has the specified version of python.
42

43
    :param string version: A python version string, such as 2.7, 3.
44
    :returns: the normalized path to the interpreter binary if found; otherwise `None`
45
    :rtype: string
46
    """
47
    try:
10✔
48
        command = [f"python{version}", "-c", "import sys; print(sys.executable)"]
10✔
49
        py_path = subprocess.check_output(command).decode().strip()
10✔
50
        return os.path.realpath(py_path)
10✔
51
    except (subprocess.CalledProcessError, FileNotFoundError):
×
52
        return None
×
53

54

55
def skip_unless_all_pythons_present(*versions):
10✔
56
    """A decorator that only runs the decorated test method if all of the specified pythons are
57
    present.
58

59
    :param string *versions: Python version strings, such as 2.7, 3.
60
    """
61
    missing_versions = [v for v in versions if not has_python_version(v)]
9✔
62
    if len(missing_versions) == 1:
9✔
63
        return skipIf(True, f"Could not find python {missing_versions[0]} on system. Skipping.")
×
64
    elif len(missing_versions) > 1:
9✔
65
        return skipIf(
×
66
            True,
67
            "Skipping due to the following missing required pythons: {}".format(
68
                ", ".join(missing_versions)
69
            ),
70
        )
71
    else:
72
        return skipIf(False, "All required pythons present, continuing with test!")
9✔
73

74

75
def skip_unless_python27_present(func):
10✔
76
    """A test skip decorator that only runs a test method if python2.7 is present."""
77
    return skip_unless_all_pythons_present(PY_27)(func)
×
78

79

80
def skip_unless_python3_present(func):
10✔
81
    """A test skip decorator that only runs a test method if python3 is present."""
82
    return skip_unless_all_pythons_present(PY_3)(func)
×
83

84

85
def skip_unless_python36_present(func):
10✔
86
    """A test skip decorator that only runs a test method if python3.6 is present."""
87
    return skip_unless_all_pythons_present(PY_36)(func)
×
88

89

90
def skip_unless_python37_present(func):
10✔
91
    """A test skip decorator that only runs a test method if python3.7 is present."""
92
    return skip_unless_all_pythons_present(PY_37)(func)
1✔
93

94

95
def skip_unless_python38_present(func):
10✔
96
    """A test skip decorator that only runs a test method if python3.8 is present."""
97
    return skip_unless_all_pythons_present(PY_38)(func)
7✔
98

99

100
def skip_unless_python39_present(func):
10✔
101
    """A test skip decorator that only runs a test method if python3.9 is present."""
102
    return skip_unless_all_pythons_present(PY_39)(func)
7✔
103

104

105
def skip_unless_python310_present(func):
10✔
106
    """A test skip decorator that only runs a test method if python3.10 is present."""
107
    return skip_unless_all_pythons_present(PY_310)(func)
×
108

109

110
def skip_unless_python311_present(func):
10✔
111
    """A test skip decorator that only runs a test method if python3.11 is present."""
112
    return skip_unless_all_pythons_present(PY_311)(func)
×
113

114

115
def skip_unless_python310_and_python311_present(func):
10✔
116
    """A test skip decorator that only runs a test method if python3.8 and python3.9 are present."""
117
    return skip_unless_all_pythons_present(PY_310, PY_311)(func)
3✔
118

119

120
def skip_unless_python27_and_python3_present(func):
10✔
121
    """A test skip decorator that only runs a test method if python2.7 and python3 are present."""
122
    return skip_unless_all_pythons_present(PY_27, PY_3)(func)
×
123

124

125
def skip_unless_python27_and_python36_present(func):
10✔
126
    """A test skip decorator that only runs a test method if python2.7 and python3.6 are present."""
127
    return skip_unless_all_pythons_present(PY_27, PY_36)(func)
×
128

129

130
def skip_unless_python38_and_python39_present(func):
10✔
131
    """A test skip decorator that only runs a test method if python3.8 and python3.9 are present."""
132
    return skip_unless_all_pythons_present(PY_38, PY_39)(func)
1✔
133

134

135
def skip_unless_python37_and_python39_present(func):
10✔
136
    """A test skip decorator that only runs a test method if python3.7 and python3.9 are present."""
137
    return skip_unless_all_pythons_present(PY_37, PY_39)(func)
×
138

139

140
def all_major_minor_python_versions(
10✔
141
    constraints: Iterable[str],
142
) -> tuple[_pytest.mark.structures.ParameterSet, ...]:
143
    """All major.minor Python versions used by the interpreter constraints.
144

145
    This is intended to be used with `@pytest.mark.parametrize()` to run a test with every relevant
146
    Python interpreter.
147
    """
148
    versions = InterpreterConstraints(constraints).partition_into_major_minor_versions(
10✔
149
        # Please update this when new stable Python versions are released to CI.
150
        interpreter_universe=["2.7", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
151
    )
152
    return tuple(
10✔
153
        pytest.param(
154
            version,
155
            marks=pytest.mark.skipif(
156
                not has_python_version(version),
157
                reason=f"Could not find python {version} on system. Skipping.",
158
            ),
159
        )
160
        for version in versions
161
    )
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