-
Notifications
You must be signed in to change notification settings - Fork 309
Expand file tree
/
Copy pathconftest.py
More file actions
222 lines (192 loc) · 7.79 KB
/
conftest.py
File metadata and controls
222 lines (192 loc) · 7.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
import json
import os
import subprocess
from collections.abc import Generator
import pytest
from filelock import FileLock
from cibuildwheel.architecture import Architecture
from cibuildwheel.ci import detect_ci_provider
from cibuildwheel.options import CommandLineArguments, Options
from cibuildwheel.selector import EnableGroup
from cibuildwheel.typing import PLATFORMS
from cibuildwheel.venv import find_uv
from . import utils
from .utils import DEFAULT_CIBW_ENABLE, EMULATED_ARCHS, get_platform
def pytest_addoption(parser: pytest.Parser) -> None:
parser.addoption(
"--run-emulation",
action="store",
default=None,
help="run emulation tests",
choices=("all", *EMULATED_ARCHS),
)
parser.addoption("--run-podman", action="store_true", default=False, help="run podman tests")
parser.addoption(
"--run-cp38-universal2",
action="store_true",
default=False,
help="macOS cp38 uses the universal2 installer",
)
parser.addoption(
"--enable",
action="store",
default=None,
help="Set the CIBW_ENABLE environment variable for all tests.",
)
parser.addoption(
"--platform",
action="store",
default=None,
help="Set the CIBW_PLATFORM environment variable for all tests.",
)
def pytest_configure(config):
flag_enable = config.getoption("--enable")
flag_platform = config.getoption("--platform")
if flag_enable is not None and "CIBW_ENABLE" in os.environ:
msg = (
"Both --enable pytest option and CIBW_ENABLE environment variable are set. "
"Please specify only one."
)
raise pytest.UsageError(msg)
if flag_platform is not None and "CIBW_PLATFORM" in os.environ:
msg = (
"Both --platform pytest option and CIBW_PLATFORM environment variable are set. "
"Please specify only one."
)
raise pytest.UsageError(msg)
if flag_enable is not None:
EnableGroup.parse_option_value(flag_enable)
os.environ["CIBW_ENABLE"] = flag_enable
if flag_enable is None and "CIBW_ENABLE" not in os.environ:
# Set default value for CIBW_ENABLE
os.environ["CIBW_ENABLE"] = DEFAULT_CIBW_ENABLE
if flag_platform is not None:
assert flag_platform in PLATFORMS, f"Invalid platform: {flag_platform}"
os.environ["CIBW_PLATFORM"] = flag_platform
def docker_warmup(request: pytest.FixtureRequest) -> None:
machine = request.config.getoption("--run-emulation", default=None)
if machine is None:
archs = {arch.value for arch in Architecture.auto_archs("linux")}
elif machine == "all":
archs = set(EMULATED_ARCHS)
else:
archs = {machine}
# Only include architectures where there are missing pre-installed interpreters
archs &= {"x86_64", "i686", "aarch64"}
if not archs:
return
options = Options(
platform="linux",
command_line_arguments=CommandLineArguments.defaults(),
env={},
defaults=True,
)
build_options = options.build_options(None)
assert build_options.manylinux_images is not None
assert build_options.musllinux_images is not None
images = [build_options.manylinux_images[arch] for arch in archs] + [
build_options.musllinux_images[arch] for arch in archs
]
command = (
"manylinux-interpreters ensure-all &&"
"cpython3.13 -m pip download -d /tmp setuptools wheel pytest"
)
for image in images:
container_id = subprocess.run(
["docker", "create", image, "bash", "-c", command],
text=True,
check=True,
stdout=subprocess.PIPE,
).stdout.strip()
try:
subprocess.run(["docker", "start", container_id], check=True, stdout=subprocess.DEVNULL)
exit_code = subprocess.run(
["docker", "wait", container_id], text=True, check=True, stdout=subprocess.PIPE
).stdout.strip()
assert exit_code == "0"
subprocess.run(
["docker", "commit", container_id, image], check=True, stdout=subprocess.DEVNULL
)
finally:
subprocess.run(["docker", "rm", container_id], check=True, stdout=subprocess.DEVNULL)
@pytest.fixture(scope="session", autouse=True)
def docker_warmup_fixture(
request: pytest.FixtureRequest, tmp_path_factory: pytest.TempPathFactory, worker_id: str
) -> None:
# if we're in CI testing linux, let's warm-up docker images
if detect_ci_provider() is None or get_platform() != "linux":
return None
if request.config.getoption("--run-emulation", default=None) is not None:
# emulation tests only run one test in CI, caching the image only slows down the test
return None
if worker_id == "master":
# not executing with multiple workers
# it might be unsafe to write to tmp_path_factory.getbasetemp().parent
return docker_warmup(request)
# get the temp directory shared by all workers
root_tmp_dir = tmp_path_factory.getbasetemp().parent
fn = root_tmp_dir / "warmup.done"
with FileLock(str(fn) + ".lock"):
if not fn.is_file():
docker_warmup(request)
fn.write_text("done")
return None
@pytest.fixture(params=["pip", "build"])
def build_frontend_env_nouv(request: pytest.FixtureRequest) -> dict[str, str]:
frontend = request.param
marks = {m.name for m in request.node.iter_markers()}
platform = "pyodide" if "pyodide" in marks else get_platform()
if platform == "pyodide" and frontend == "pip":
pytest.skip("Can't use pip as build frontend for pyodide platform")
return {"CIBW_BUILD_FRONTEND": frontend}
@pytest.fixture(params=["pip", "build", "build[uv]", "uv"])
def build_frontend_env(request: pytest.FixtureRequest) -> Generator[dict[str, str], None, None]:
frontend = request.param
marks = {m.name for m in request.node.iter_markers()}
if "android" in marks:
platform = "android"
elif "ios" in marks:
platform = "ios"
elif "pyodide" in marks:
platform = "pyodide"
else:
platform = get_platform()
if platform in {"pyodide", "ios", "android"} and frontend == "pip":
pytest.skip(f"Can't use pip as build frontend for {platform}")
if platform == "pyodide" and frontend in {"build[uv]", "uv"}:
pytest.skip("Can't use uv with pyodide yet")
uv_path = find_uv()
if uv_path is None and frontend in {"build[uv]", "uv"}:
pytest.skip("Can't find uv, so skipping uv tests")
if uv_path is not None and frontend == "build" and platform not in {"android", "ios"}:
pytest.skip("No need to check build when uv is present")
# temporary workaround: uv doesn't work with graalpy yet
uses_uv = "uv" in frontend
env: dict[str, str] = {"CIBW_BUILD_FRONTEND": frontend}
if uses_uv:
utils.include_graalpy_in_expected_wheels = False
env["CIBW_SKIP"] = "gp*" # skip graalpy when using uv, until uv supports it
try:
yield env
finally:
if uses_uv:
utils.include_graalpy_in_expected_wheels = True
@pytest.fixture
def docker_cleanup() -> Generator[None, None, None]:
def get_images() -> set[str]:
if detect_ci_provider() is None or get_platform() != "linux":
return set()
images = subprocess.run(
["docker", "image", "ls", "--format", "{{json .ID}}"],
text=True,
check=True,
stdout=subprocess.PIPE,
).stdout
return {json.loads(image.strip()) for image in images.splitlines() if image.strip()}
images_before = get_images()
try:
yield
finally:
images_after = get_images()
for image in images_after - images_before:
subprocess.run(["docker", "rmi", image], check=False)