fix(macos): make matching python3-config available in build/test venvs (#2922)
This commit is contained in:
+13
-1
@@ -152,6 +152,10 @@ def virtualenv(
|
|||||||
venv. Otherwise, pip is installed.
|
venv. Otherwise, pip is installed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# the unresolved path, so e.g. the python.org framework bin directory is
|
||||||
|
# used rather than the internal location of the real binary
|
||||||
|
base_python_bin_dir = None if _IS_WIN else python.parent
|
||||||
|
|
||||||
# virtualenv may fail if this is a symlink.
|
# virtualenv may fail if this is a symlink.
|
||||||
python = python.resolve()
|
python = python.resolve()
|
||||||
|
|
||||||
@@ -189,7 +193,7 @@ def virtualenv(
|
|||||||
python,
|
python,
|
||||||
venv_path,
|
venv_path,
|
||||||
)
|
)
|
||||||
venv_env = activate_virtualenv(venv_path, env=env)
|
venv_env = activate_virtualenv(venv_path, env=env, base_python_bin_dir=base_python_bin_dir)
|
||||||
if not use_uv and pip_version == "embed":
|
if not use_uv and pip_version == "embed":
|
||||||
call(
|
call(
|
||||||
"python",
|
"python",
|
||||||
@@ -208,11 +212,19 @@ def virtualenv(
|
|||||||
def activate_virtualenv(
|
def activate_virtualenv(
|
||||||
venv_path: Path,
|
venv_path: Path,
|
||||||
env: dict[str, str] | None = None,
|
env: dict[str, str] | None = None,
|
||||||
|
base_python_bin_dir: Path | None = None,
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
Return a copy of the environment with the virtualenv at `venv_path` activated.
|
Return a copy of the environment with the virtualenv at `venv_path` activated.
|
||||||
|
|
||||||
|
If given, `base_python_bin_dir` is placed on PATH right after the venv, so
|
||||||
|
that scripts installed alongside the base interpreter that aren't copied
|
||||||
|
into the venv (such as python3-config, see #2021) resolve to the matching
|
||||||
|
interpreter.
|
||||||
"""
|
"""
|
||||||
paths = [str(venv_path), str(venv_path / "Scripts")] if _IS_WIN else [str(venv_path / "bin")]
|
paths = [str(venv_path), str(venv_path / "Scripts")] if _IS_WIN else [str(venv_path / "bin")]
|
||||||
|
if base_python_bin_dir is not None:
|
||||||
|
paths.append(str(base_python_bin_dir))
|
||||||
venv_env = os.environ.copy() if env is None else env.copy()
|
venv_env = os.environ.copy() if env is None else env.copy()
|
||||||
venv_env["PATH"] = os.pathsep.join([*paths, venv_env["PATH"]])
|
venv_env["PATH"] = os.pathsep.join([*paths, venv_env["PATH"]])
|
||||||
venv_env["VIRTUAL_ENV"] = str(venv_path)
|
venv_env["VIRTUAL_ENV"] = str(venv_path)
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import cibuildwheel.venv
|
||||||
|
from cibuildwheel.venv import activate_virtualenv, find_uv, virtualenv
|
||||||
|
|
||||||
|
|
||||||
|
def test_activate_virtualenv(tmp_path: Path) -> None:
|
||||||
|
venv_path = tmp_path / "venv"
|
||||||
|
env = activate_virtualenv(venv_path, env={"PATH": "/usr/bin"})
|
||||||
|
paths = env["PATH"].split(os.pathsep)
|
||||||
|
if sys.platform == "win32":
|
||||||
|
assert paths[:2] == [str(venv_path), str(venv_path / "Scripts")]
|
||||||
|
else:
|
||||||
|
assert paths[0] == str(venv_path / "bin")
|
||||||
|
assert paths[-1] == "/usr/bin"
|
||||||
|
assert env["VIRTUAL_ENV"] == str(venv_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_activate_virtualenv_base_python_bin_dir(tmp_path: Path) -> None:
|
||||||
|
venv_path = tmp_path / "venv"
|
||||||
|
base_bin = tmp_path / "base" / "bin"
|
||||||
|
env = activate_virtualenv(venv_path, env={"PATH": "/usr/bin"}, base_python_bin_dir=base_bin)
|
||||||
|
paths = env["PATH"].split(os.pathsep)
|
||||||
|
# the base interpreter's bin dir comes right after the venv, so scripts
|
||||||
|
# like python3-config resolve to the matching interpreter (see #2021)
|
||||||
|
if sys.platform == "win32":
|
||||||
|
assert paths[:3] == [str(venv_path), str(venv_path / "Scripts"), str(base_bin)]
|
||||||
|
else:
|
||||||
|
assert paths[:2] == [str(venv_path / "bin"), str(base_bin)]
|
||||||
|
assert paths[-1] == "/usr/bin"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.platform == "win32", reason="POSIX-only behavior")
|
||||||
|
@pytest.mark.skipif(find_uv() is None, reason="requires uv")
|
||||||
|
def test_virtualenv_puts_base_python_bin_dir_on_path(tmp_path: Path) -> None:
|
||||||
|
version = "{}.{}".format(*sys.version_info[:2])
|
||||||
|
venv_path = tmp_path / "venv"
|
||||||
|
env = virtualenv(
|
||||||
|
version, Path(sys.executable), venv_path, None, use_uv=True, env={"PATH": "/usr/bin"}
|
||||||
|
)
|
||||||
|
paths = env["PATH"].split(os.pathsep)
|
||||||
|
assert paths == [str(venv_path / "bin"), str(Path(sys.executable).parent), "/usr/bin"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(find_uv() is None, reason="requires uv")
|
||||||
|
def test_virtualenv_no_base_python_bin_dir_on_windows(
|
||||||
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
|
) -> None:
|
||||||
|
monkeypatch.setattr(cibuildwheel.venv, "_IS_WIN", True)
|
||||||
|
version = "{}.{}".format(*sys.version_info[:2])
|
||||||
|
venv_path = tmp_path / "venv"
|
||||||
|
env = virtualenv(
|
||||||
|
version, Path(sys.executable), venv_path, None, use_uv=True, env={"PATH": "/usr/bin"}
|
||||||
|
)
|
||||||
|
paths = env["PATH"].split(os.pathsep)
|
||||||
|
assert str(Path(sys.executable).parent) not in paths
|
||||||
Reference in New Issue
Block a user