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
|
"""Pytest fixtures."""
import importlib.metadata
import json
import pathlib
import subprocess
import sys
from collections.abc import Generator
from pathlib import Path
from typing import Callable
import pytest
from ansible_compat.runtime import Runtime
@pytest.fixture()
# pylint: disable=unused-argument
def runtime(scope: str = "session") -> Generator[Runtime, None, None]: # noqa: ARG001
"""Isolated runtime fixture."""
instance = Runtime(isolated=True)
yield instance
instance.clean()
@pytest.fixture()
# pylint: disable=unused-argument
def runtime_tmp(
tmp_path: pathlib.Path,
scope: str = "session", # noqa: ARG001
) -> Generator[Runtime, None, None]:
"""Isolated runtime fixture using a temp directory."""
instance = Runtime(project_dir=tmp_path, isolated=True)
yield instance
instance.clean()
def query_pkg_version(pkg: str) -> str:
"""Get the version of a current installed package.
:param pkg: Package name
:return: Package version
"""
return importlib.metadata.version(pkg)
@pytest.fixture()
def pkg_version() -> Callable[[str], str]:
"""Get the version of a current installed package.
:return: Callable function to get package version
"""
return query_pkg_version
class VirtualEnvironment:
"""Virtualenv wrapper."""
def __init__(self, path: Path) -> None:
"""Initialize.
:param path: Path to virtualenv
"""
self.project = path
self.venv_path = self.project / "venv"
self.venv_bin_path = self.venv_path / "bin"
self.venv_python_path = self.venv_bin_path / "python"
def create(self) -> None:
"""Create virtualenv."""
cmd = [str(sys.executable), "-m", "venv", str(self.venv_path)]
subprocess.check_call(args=cmd)
# Install this package into the virtual environment
self.install(f"{__file__}/../..")
def install(self, *packages: str) -> None:
"""Install packages in virtualenv.
:param packages: Packages to install
"""
cmd = [str(self.venv_python_path), "-m", "pip", "install", *packages]
subprocess.check_call(args=cmd)
def python_script_run(self, script: str) -> subprocess.CompletedProcess[str]:
"""Run command in project dir using venv.
:param args: Command to run
"""
proc = subprocess.run(
args=[self.venv_python_path, "-c", script],
capture_output=True,
cwd=self.project,
check=False,
text=True,
)
return proc
def site_package_dirs(self) -> list[Path]:
"""Get site packages.
:return: List of site packages dirs
"""
script = "import json, site; print(json.dumps(site.getsitepackages()))"
proc = subprocess.run(
args=[self.venv_python_path, "-c", script],
capture_output=True,
check=False,
text=True,
)
dirs = json.loads(proc.stdout)
if not isinstance(dirs, list):
msg = "Expected list of site packages"
raise TypeError(msg)
sanitized = list({Path(d).resolve() for d in dirs})
return sanitized
@pytest.fixture(scope="module")
def venv_module(tmp_path_factory: pytest.TempPathFactory) -> VirtualEnvironment:
"""Create a virtualenv in a temporary directory.
:param tmp_path: pytest fixture for temp path
:return: VirtualEnvironment instance
"""
test_project = tmp_path_factory.mktemp(basename="test_project-", numbered=True)
_venv = VirtualEnvironment(test_project)
_venv.create()
return _venv
|