Skip to content

Commit e9fae06

Browse files
authored
chore: rework virtual environments creation for tests (#2356)
As mentioned in #2347 (comment) Rework the way test venvs are created to reuse the same mechanisms as build venvs.
1 parent 1fcca39 commit e9fae06

File tree

3 files changed

+45
-104
lines changed

3 files changed

+45
-104
lines changed

cibuildwheel/platforms/macos.py

+15-53
Original file line numberDiff line numberDiff line change
@@ -241,25 +241,6 @@ def setup_python(
241241
# we version pip ourselves, so we don't care about pip version checking
242242
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
243243

244-
# upgrade pip to the version matching our constraints
245-
# if necessary, reinstall it to ensure that it's available on PATH as 'pip'
246-
if build_frontend == "build[uv]":
247-
assert uv_path is not None
248-
pip = [str(uv_path), "pip"]
249-
else:
250-
pip = ["python", "-m", "pip"]
251-
252-
if not use_uv:
253-
call(
254-
*pip,
255-
"install",
256-
"--upgrade",
257-
"pip",
258-
*constraint_flags(dependency_constraint),
259-
env=env,
260-
cwd=venv_path,
261-
)
262-
263244
# Apply our environment after pip is ready
264245
env = environment.as_dictionary(prev_environment=env)
265246

@@ -434,8 +415,7 @@ def build(options: Options, tmp_path: Path) -> None:
434415
build_options.environment,
435416
build_frontend.name,
436417
)
437-
if not use_uv:
438-
pip_version = get_pip_version(env)
418+
pip_version = None if use_uv else get_pip_version(env)
439419

440420
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
441421
if compatible_wheel:
@@ -460,7 +440,7 @@ def build(options: Options, tmp_path: Path) -> None:
460440
)
461441

462442
build_env = env.copy()
463-
if not use_uv:
443+
if pip_version is not None:
464444
build_env["VIRTUALENV_PIP"] = pip_version
465445
if constraints_path:
466446
combine_constraints(
@@ -614,19 +594,6 @@ def build(options: Options, tmp_path: Path) -> None:
614594
else f"Testing wheel on {testing_arch}..."
615595
)
616596

617-
# set up a virtual environment to install and test from, to make sure
618-
# there are no dependencies that were pulled in at build time.
619-
if not use_uv:
620-
call(
621-
"pip",
622-
"install",
623-
"virtualenv",
624-
*constraint_flags(constraints_path),
625-
env=env,
626-
)
627-
628-
venv_dir = identifier_tmp_dir / f"venv-test-{testing_arch}"
629-
630597
arch_prefix = []
631598
uv_arch_args = []
632599
if testing_arch != machine_arch:
@@ -642,29 +609,24 @@ def build(options: Options, tmp_path: Path) -> None:
642609
call_with_arch = functools.partial(call, *arch_prefix)
643610
shell_with_arch = functools.partial(call, *arch_prefix, "/bin/sh", "-c")
644611

612+
# set up a virtual environment to install and test from, to make sure
613+
# there are no dependencies that were pulled in at build time.
614+
venv_dir = identifier_tmp_dir / f"venv-test-{testing_arch}"
615+
virtualenv_env = virtualenv(
616+
config.version,
617+
base_python,
618+
venv_dir,
619+
None,
620+
use_uv=use_uv,
621+
env=env,
622+
pip_version=pip_version,
623+
)
645624
if use_uv:
646625
pip_install = functools.partial(call, *pip, "install", *uv_arch_args)
647-
call("uv", "venv", venv_dir, f"--python={base_python}", env=env)
648626
else:
649627
pip_install = functools.partial(call_with_arch, *pip, "install")
650-
# Use pip version from the initial env to ensure determinism
651-
venv_args = [
652-
"--no-periodic-update",
653-
f"--pip={pip_version}",
654-
"--no-setuptools",
655-
"--no-wheel",
656-
]
657-
call_with_arch("python", "-m", "virtualenv", *venv_args, venv_dir, env=env)
658-
659-
virtualenv_env = env.copy()
628+
660629
virtualenv_env["MACOSX_DEPLOYMENT_TARGET"] = get_test_macosx_deployment_target()
661-
virtualenv_env["PATH"] = os.pathsep.join(
662-
[
663-
str(venv_dir / "bin"),
664-
virtualenv_env["PATH"],
665-
]
666-
)
667-
virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
668630

669631
# check that we are using the Python from the virtual environment
670632
call_with_arch("which", "python", env=virtualenv_env)

cibuildwheel/platforms/windows.py

+10-43
Original file line numberDiff line numberDiff line change
@@ -265,21 +265,6 @@ def setup_python(
265265
env["PYTHON_ARCH"] = python_configuration.arch
266266
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
267267

268-
# upgrade pip to the version matching our constraints
269-
# if necessary, reinstall it to ensure that it's available on PATH as 'pip.exe'
270-
if not use_uv:
271-
call(
272-
"python",
273-
"-m",
274-
"pip",
275-
"install",
276-
"--upgrade",
277-
"pip",
278-
*constraint_flags(dependency_constraint),
279-
env=env,
280-
cwd=venv_path,
281-
)
282-
283268
# update env with results from CIBW_ENVIRONMENT
284269
env = environment.as_dictionary(prev_environment=env)
285270

@@ -378,8 +363,7 @@ def build(options: Options, tmp_path: Path) -> None:
378363
build_options.environment,
379364
build_frontend.name,
380365
)
381-
if not use_uv:
382-
pip_version = get_pip_version(env)
366+
pip_version = None if use_uv else get_pip_version(env)
383367

384368
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
385369
if compatible_wheel:
@@ -407,7 +391,7 @@ def build(options: Options, tmp_path: Path) -> None:
407391
)
408392

409393
build_env = env.copy()
410-
if not use_uv:
394+
if pip_version is not None:
411395
build_env["VIRTUALENV_PIP"] = pip_version
412396

413397
if constraints_path:
@@ -486,33 +470,16 @@ def build(options: Options, tmp_path: Path) -> None:
486470
log.step("Testing wheel...")
487471
# set up a virtual environment to install and test from, to make sure
488472
# there are no dependencies that were pulled in at build time.
489-
if not use_uv:
490-
call(
491-
"pip", "install", "virtualenv", *constraint_flags(constraints_path), env=env
492-
)
493-
494473
venv_dir = identifier_tmp_dir / "venv-test"
495-
496-
if use_uv:
497-
call("uv", "venv", venv_dir, f"--python={base_python}", env=env)
498-
else:
499-
# Use pip version from the initial env to ensure determinism
500-
venv_args = [
501-
"--no-periodic-update",
502-
f"--pip={pip_version}",
503-
"--no-setuptools",
504-
"--no-wheel",
505-
]
506-
call("python", "-m", "virtualenv", *venv_args, venv_dir, env=env)
507-
508-
virtualenv_env = env.copy()
509-
virtualenv_env["PATH"] = os.pathsep.join(
510-
[
511-
str(venv_dir / "Scripts"),
512-
virtualenv_env["PATH"],
513-
]
474+
virtualenv_env = virtualenv(
475+
config.version,
476+
base_python,
477+
venv_dir,
478+
None,
479+
use_uv=use_uv,
480+
env=env,
481+
pip_version=pip_version,
514482
)
515-
virtualenv_env["VIRTUAL_ENV"] = str(venv_dir)
516483

517484
# check that we are using the Python from the virtual environment
518485
call("where", "python", env=virtualenv_env)

cibuildwheel/venv.py

+20-8
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ def virtualenv(
9393
dependency_constraint: Path | None,
9494
*,
9595
use_uv: bool,
96+
env: dict[str, str] | None = None,
97+
pip_version: str | None = None,
9698
) -> dict[str, str]:
9799
"""
98100
Create a virtual environment. If `use_uv` is True,
@@ -109,16 +111,17 @@ def virtualenv(
109111
call("uv", "venv", venv_path, "--python", python)
110112
else:
111113
virtualenv_app = _ensure_virtualenv(version)
112-
pip_constraint = _parse_pip_constraint_for_virtualenv(dependency_constraint)
113-
additional_flags = [f"--pip={pip_constraint}", "--no-setuptools", "--no-wheel"]
114+
if pip_version is None:
115+
pip_version = _parse_pip_constraint_for_virtualenv(dependency_constraint)
116+
additional_flags = [f"--pip={pip_version}", "--no-setuptools", "--no-wheel"]
114117

115118
# Using symlinks to pre-installed seed packages is really the fastest way to get a virtual
116119
# environment. The initial cost is a bit higher but reusing is much faster.
117120
# Windows does not always allow symlinks so just disabling for now.
118121
# Requires pip>=19.3 so disabling for "embed" because this means we don't know what's the
119122
# version of pip that will end-up installed.
120123
# c.f. https://virtualenv.pypa.io/en/latest/cli_interface.html#section-seeder
121-
if not _IS_WIN and pip_constraint != "embed" and Version(pip_constraint) >= Version("19.3"):
124+
if not _IS_WIN and pip_version != "embed" and Version(pip_version) >= Version("19.3"):
122125
additional_flags.append("--symlink-app-data")
123126

124127
call(
@@ -132,12 +135,21 @@ def virtualenv(
132135
python,
133136
venv_path,
134137
)
135-
136138
paths = [str(venv_path), str(venv_path / "Scripts")] if _IS_WIN else [str(venv_path / "bin")]
137-
env = os.environ.copy()
138-
env["PATH"] = os.pathsep.join([*paths, env["PATH"]])
139-
env["VIRTUAL_ENV"] = str(venv_path)
140-
return env
139+
venv_env = os.environ.copy() if env is None else env.copy()
140+
venv_env["PATH"] = os.pathsep.join([*paths, venv_env["PATH"]])
141+
venv_env["VIRTUAL_ENV"] = str(venv_path)
142+
if not use_uv and pip_version == "embed":
143+
call(
144+
"pip",
145+
"install",
146+
"--upgrade",
147+
"pip",
148+
*constraint_flags(dependency_constraint),
149+
env=venv_env,
150+
cwd=venv_path,
151+
)
152+
return venv_env
141153

142154

143155
def find_uv() -> Path | None:

0 commit comments

Comments
 (0)