|
1 | 1 | from __future__ import annotations
|
2 | 2 |
|
3 |
| -import importlib.util |
| 3 | +import os |
| 4 | +import shutil |
4 | 5 | import subprocess
|
5 | 6 | import sys
|
| 7 | +import types |
| 8 | +import warnings |
| 9 | +from collections.abc import Generator |
6 | 10 | from pathlib import Path
|
| 11 | +from venv import EnvBuilder |
7 | 12 |
|
8 | 13 | if sys.version_info < (3, 8):
|
9 | 14 | import importlib_metadata as metadata
|
@@ -63,13 +68,55 @@ def isolated(pep518_wheelhouse: str, monkeypatch: pytest.MonkeyPatch) -> None:
|
63 | 68 | monkeypatch.setenv("PIP_NO_INDEX", "true")
|
64 | 69 |
|
65 | 70 |
|
66 |
| -has_pyvenv = importlib.util.find_spec("pytest_virtualenv") is not None |
| 71 | +class VEnv(EnvBuilder): |
| 72 | + executable: Path |
| 73 | + env_dir: Path |
| 74 | + |
| 75 | + def __init__(self, env_dir: str) -> None: |
| 76 | + super().__init__(with_pip=True) |
| 77 | + # This warning is mistakenly generated by CPython 3.11.0 |
| 78 | + # https://github.com/python/cpython/pull/98743 |
| 79 | + with warnings.catch_warnings(): |
| 80 | + if sys.version_info[:3] == (3, 11, 0): |
| 81 | + warnings.filterwarnings( |
| 82 | + "ignore", |
| 83 | + "check_home argument is deprecated and ignored.", |
| 84 | + DeprecationWarning, |
| 85 | + ) |
| 86 | + self.create(env_dir) |
| 87 | + |
| 88 | + def ensure_directories( |
| 89 | + self, env_dir: str | bytes | os.PathLike[str] | os.PathLike[bytes] |
| 90 | + ) -> types.SimpleNamespace: |
| 91 | + context = super().ensure_directories(env_dir) |
| 92 | + # Store the path to the venv Python interpreter. |
| 93 | + # See https://github.com/mesonbuild/meson-python/blob/8a180be7b4abd7e1939a63d5d59f63197ee27cc7/tests/conftest.py#LL79 |
| 94 | + self.executable = Path(context.env_exe) |
| 95 | + self.env_dir = Path(context.env_dir) |
| 96 | + return context |
| 97 | + |
| 98 | + def run(self, expression: str, *, capture: bool = True) -> str: |
| 99 | + assert capture, "Always capture for now" |
| 100 | + env = os.environ.copy() |
| 101 | + env["PATH"] = f"{self.executable.parent}{os.pathsep}{env['PATH']}" |
| 102 | + env["VIRTUAL_ENV"] = str(self.env_dir) |
| 103 | + return subprocess.run( |
| 104 | + expression, |
| 105 | + check=True, |
| 106 | + capture_output=capture, |
| 107 | + text=True, |
| 108 | + shell=True, |
| 109 | + env=env, |
| 110 | + ).stdout.strip() |
67 | 111 |
|
68 |
| -if not has_pyvenv: |
69 | 112 |
|
70 |
| - @pytest.fixture |
71 |
| - def virtualenv() -> None: |
72 |
| - pytest.skip("pytest-virtualenv not available") |
| 113 | +@pytest.fixture |
| 114 | +def virtualenv(tmp_path: Path) -> Generator[VEnv, None, None]: |
| 115 | + path = tmp_path / "venv" |
| 116 | + try: |
| 117 | + yield VEnv(str(path)) |
| 118 | + finally: |
| 119 | + shutil.rmtree(path) |
73 | 120 |
|
74 | 121 |
|
75 | 122 | def pytest_collection_modifyitems(items: list[pytest.Item]) -> None:
|
|
0 commit comments