relenv 0.21.1__py3-none-any.whl → 0.22.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- relenv/__init__.py +14 -2
- relenv/__main__.py +12 -6
- relenv/_resources/xz/config.h +148 -0
- relenv/_resources/xz/readme.md +4 -0
- relenv/build/__init__.py +28 -30
- relenv/build/common/__init__.py +50 -0
- relenv/build/common/_sysconfigdata_template.py +72 -0
- relenv/build/common/builder.py +907 -0
- relenv/build/common/builders.py +163 -0
- relenv/build/common/download.py +324 -0
- relenv/build/common/install.py +609 -0
- relenv/build/common/ui.py +432 -0
- relenv/build/darwin.py +128 -14
- relenv/build/linux.py +296 -78
- relenv/build/windows.py +259 -44
- relenv/buildenv.py +48 -17
- relenv/check.py +10 -5
- relenv/common.py +499 -163
- relenv/create.py +147 -7
- relenv/fetch.py +16 -4
- relenv/manifest.py +15 -7
- relenv/python-versions.json +329 -0
- relenv/pyversions.py +817 -30
- relenv/relocate.py +101 -55
- relenv/runtime.py +452 -253
- relenv/toolchain.py +9 -3
- {relenv-0.21.1.dist-info → relenv-0.22.0.dist-info}/METADATA +1 -1
- relenv-0.22.0.dist-info/RECORD +48 -0
- tests/__init__.py +2 -0
- tests/_pytest_typing.py +45 -0
- tests/conftest.py +42 -36
- tests/test_build.py +426 -9
- tests/test_common.py +311 -48
- tests/test_create.py +149 -6
- tests/test_downloads.py +19 -15
- tests/test_fips_photon.py +6 -3
- tests/test_module_imports.py +44 -0
- tests/test_pyversions_runtime.py +177 -0
- tests/test_relocate.py +45 -39
- tests/test_relocate_module.py +257 -0
- tests/test_runtime.py +1802 -6
- tests/test_verify_build.py +500 -34
- relenv/build/common.py +0 -1609
- relenv-0.21.1.dist-info/RECORD +0 -35
- {relenv-0.21.1.dist-info → relenv-0.22.0.dist-info}/WHEEL +0 -0
- {relenv-0.21.1.dist-info → relenv-0.22.0.dist-info}/entry_points.txt +0 -0
- {relenv-0.21.1.dist-info → relenv-0.22.0.dist-info}/licenses/LICENSE.md +0 -0
- {relenv-0.21.1.dist-info → relenv-0.22.0.dist-info}/licenses/NOTICE +0 -0
- {relenv-0.21.1.dist-info → relenv-0.22.0.dist-info}/top_level.txt +0 -0
tests/test_runtime.py
CHANGED
|
@@ -1,27 +1,1823 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2022-2025 Broadcom.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
4
6
|
import importlib
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import pathlib
|
|
5
10
|
import sys
|
|
11
|
+
from types import ModuleType, SimpleNamespace
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
6
15
|
|
|
7
16
|
import relenv.runtime
|
|
8
17
|
|
|
18
|
+
# mypy: ignore-errors
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _raise(exc: Exception):
|
|
22
|
+
raise exc
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_path_import_failure(
|
|
26
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
|
|
27
|
+
) -> None:
|
|
28
|
+
monkeypatch.setattr(
|
|
29
|
+
importlib.util, "spec_from_file_location", lambda *args, **kwargs: None
|
|
30
|
+
)
|
|
31
|
+
with pytest.raises(ImportError):
|
|
32
|
+
relenv.runtime.path_import("demo", tmp_path / "demo.py")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_path_import_success(tmp_path: pathlib.Path) -> None:
|
|
36
|
+
module_file = tmp_path / "mod.py"
|
|
37
|
+
module_file.write_text("value = 42\n")
|
|
38
|
+
mod = relenv.runtime.path_import("temp_mod", module_file)
|
|
39
|
+
assert mod.value == 42
|
|
40
|
+
assert sys.modules["temp_mod"] is mod
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_debug_print(
|
|
44
|
+
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
45
|
+
) -> None:
|
|
46
|
+
monkeypatch.setenv("RELENV_DEBUG", "1")
|
|
47
|
+
relenv.runtime.debug("hello")
|
|
48
|
+
out = capsys.readouterr().out
|
|
49
|
+
assert "hello" in out
|
|
50
|
+
monkeypatch.delenv("RELENV_DEBUG", raising=False)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_pushd_changes_directory(tmp_path: pathlib.Path) -> None:
|
|
54
|
+
original = os.getcwd()
|
|
55
|
+
with relenv.runtime.pushd(tmp_path):
|
|
56
|
+
assert os.getcwd() == str(tmp_path)
|
|
57
|
+
assert os.getcwd() == original
|
|
9
58
|
|
|
10
|
-
|
|
11
|
-
|
|
59
|
+
|
|
60
|
+
def test_relenv_root_windows(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
61
|
+
module_dir = pathlib.Path(relenv.runtime.__file__).resolve().parent
|
|
62
|
+
fake_sys = SimpleNamespace(platform="win32")
|
|
63
|
+
monkeypatch.setattr(relenv.runtime, "sys", fake_sys)
|
|
64
|
+
expected = module_dir.parent.parent.parent
|
|
65
|
+
assert relenv.runtime.relenv_root() == expected
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_get_major_version() -> None:
|
|
69
|
+
result = relenv.runtime.get_major_version()
|
|
70
|
+
major, minor = result.split(".")
|
|
71
|
+
assert major.isdigit() and minor.isdigit()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_importer() -> None:
|
|
75
|
+
def mywrapper(name: str) -> ModuleType:
|
|
12
76
|
mod = importlib.import_module(name)
|
|
13
|
-
mod.__test_case__ = True
|
|
77
|
+
mod.__test_case__ = True # type: ignore[attr-defined]
|
|
14
78
|
return mod
|
|
15
79
|
|
|
16
80
|
importer = relenv.runtime.RelenvImporter(
|
|
17
81
|
wrappers=[
|
|
18
|
-
relenv.runtime.Wrapper(
|
|
82
|
+
relenv.runtime.Wrapper(
|
|
83
|
+
"pip._internal.locations",
|
|
84
|
+
mywrapper,
|
|
85
|
+
),
|
|
19
86
|
]
|
|
20
87
|
)
|
|
21
88
|
|
|
22
89
|
sys.meta_path = [importer] + sys.meta_path
|
|
23
90
|
|
|
24
|
-
import pip._internal.locations
|
|
91
|
+
import pip._internal.locations # type: ignore[import]
|
|
25
92
|
|
|
26
93
|
assert hasattr(pip._internal.locations, "__test_case__")
|
|
27
94
|
assert pip._internal.locations.__test_case__ is True
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_set_env_if_not_set(
|
|
98
|
+
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
99
|
+
) -> None:
|
|
100
|
+
env_name = "RELENV_TEST_ENV"
|
|
101
|
+
monkeypatch.delenv(env_name, raising=False)
|
|
102
|
+
relenv.runtime.set_env_if_not_set(env_name, "value")
|
|
103
|
+
assert os.environ[env_name] == "value"
|
|
104
|
+
|
|
105
|
+
monkeypatch.setenv(env_name, "other")
|
|
106
|
+
relenv.runtime.set_env_if_not_set(env_name, "value")
|
|
107
|
+
captured = capsys.readouterr()
|
|
108
|
+
assert "Warning:" in captured.out
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_get_config_var_wrapper_with_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
112
|
+
monkeypatch.setattr(relenv.runtime, "relenv_root", lambda: pathlib.Path("/root"))
|
|
113
|
+
monkeypatch.setenv("RELENV_PIP_DIR", "1")
|
|
114
|
+
wrapped = relenv.runtime.get_config_var_wrapper(lambda name: "/orig")
|
|
115
|
+
assert wrapped("BINDIR") == pathlib.Path("/root")
|
|
116
|
+
monkeypatch.delenv("RELENV_PIP_DIR", raising=False)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_system_sysconfig_uses_system_python(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
120
|
+
monkeypatch.setattr(relenv.runtime, "_SYSTEM_CONFIG_VARS", None, raising=False)
|
|
121
|
+
|
|
122
|
+
original_exists = pathlib.Path.exists
|
|
123
|
+
|
|
124
|
+
def fake_exists(path: pathlib.Path) -> bool:
|
|
125
|
+
return str(path) == "/usr/bin/python3"
|
|
126
|
+
|
|
127
|
+
monkeypatch.setattr(pathlib.Path, "exists", fake_exists)
|
|
128
|
+
expected = {"AR": "ar"}
|
|
129
|
+
completed = SimpleNamespace(stdout=json.dumps(expected).encode(), returncode=0)
|
|
130
|
+
monkeypatch.setattr(
|
|
131
|
+
relenv.runtime.subprocess, "run", lambda *args, **kwargs: completed
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
result = relenv.runtime.system_sysconfig()
|
|
135
|
+
assert result["AR"] == "ar"
|
|
136
|
+
|
|
137
|
+
monkeypatch.setattr(pathlib.Path, "exists", original_exists)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_system_sysconfig_cached(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
141
|
+
cache = {"AR": "cached"}
|
|
142
|
+
monkeypatch.setattr(relenv.runtime, "_SYSTEM_CONFIG_VARS", cache, raising=False)
|
|
143
|
+
result = relenv.runtime.system_sysconfig()
|
|
144
|
+
assert result is cache
|
|
145
|
+
monkeypatch.setattr(relenv.runtime, "_SYSTEM_CONFIG_VARS", None, raising=False)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_system_sysconfig_fallback(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
149
|
+
monkeypatch.setattr(relenv.runtime, "_SYSTEM_CONFIG_VARS", None, raising=False)
|
|
150
|
+
monkeypatch.setattr(pathlib.Path, "exists", lambda _path: False)
|
|
151
|
+
result = relenv.runtime.system_sysconfig()
|
|
152
|
+
assert result == relenv.runtime.CONFIG_VARS_DEFAULTS
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_install_cargo_config_creates_file(
|
|
156
|
+
tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
|
|
157
|
+
) -> None:
|
|
158
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux")
|
|
159
|
+
data_dir = tmp_path / "data"
|
|
160
|
+
data_dir.mkdir()
|
|
161
|
+
toolchain_dir = tmp_path / "toolchain" / "x86_64-linux-gnu"
|
|
162
|
+
(toolchain_dir / "sysroot" / "lib").mkdir(parents=True)
|
|
163
|
+
(toolchain_dir / "bin").mkdir(parents=True)
|
|
164
|
+
(toolchain_dir / "bin" / "x86_64-linux-gnu-gcc").touch()
|
|
165
|
+
|
|
166
|
+
class StubDirs:
|
|
167
|
+
def __init__(self, data: pathlib.Path) -> None:
|
|
168
|
+
self.data = data
|
|
169
|
+
|
|
170
|
+
stub_dirs = StubDirs(data_dir)
|
|
171
|
+
stub_common = SimpleNamespace(
|
|
172
|
+
DATA_DIR=tmp_path,
|
|
173
|
+
work_dirs=lambda: stub_dirs,
|
|
174
|
+
get_triplet=lambda: "x86_64-linux-gnu",
|
|
175
|
+
get_toolchain=lambda: toolchain_dir,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
monkeypatch.setattr(relenv.runtime, "common", lambda: stub_common)
|
|
179
|
+
relenv.runtime.install_cargo_config()
|
|
180
|
+
config_path = data_dir / "cargo" / "config.toml"
|
|
181
|
+
assert config_path.exists()
|
|
182
|
+
assert "x86_64-unknown-linux-gnu" in config_path.read_text()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_build_shebang_value_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
186
|
+
monkeypatch.setattr(
|
|
187
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
188
|
+
)
|
|
189
|
+
monkeypatch.setattr(
|
|
190
|
+
relenv.runtime,
|
|
191
|
+
"common",
|
|
192
|
+
lambda: SimpleNamespace(
|
|
193
|
+
relative_interpreter=lambda *args, **kwargs: _raise(ValueError("boom"))
|
|
194
|
+
),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
called = {"count": 0}
|
|
198
|
+
|
|
199
|
+
def original(self: object, *args: object, **kwargs: object) -> bytes: # type: ignore[override]
|
|
200
|
+
called["count"] += 1
|
|
201
|
+
return b"orig"
|
|
202
|
+
|
|
203
|
+
wrapped = relenv.runtime._build_shebang(original)
|
|
204
|
+
result = wrapped(SimpleNamespace(target_dir="/tmp/dir"))
|
|
205
|
+
assert result == b"orig"
|
|
206
|
+
assert called["count"] == 1
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def test_build_shebang_windows(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
210
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "win32", raising=False)
|
|
211
|
+
monkeypatch.setattr(
|
|
212
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
213
|
+
)
|
|
214
|
+
monkeypatch.setattr(
|
|
215
|
+
relenv.runtime,
|
|
216
|
+
"common",
|
|
217
|
+
lambda: SimpleNamespace(
|
|
218
|
+
relative_interpreter=lambda *args: pathlib.Path("python.exe")
|
|
219
|
+
),
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def original(self: object) -> bytes: # type: ignore[override]
|
|
223
|
+
return b""
|
|
224
|
+
|
|
225
|
+
wrapped = relenv.runtime._build_shebang(original)
|
|
226
|
+
result = wrapped(SimpleNamespace(target_dir="/tmp/dir"))
|
|
227
|
+
assert result.startswith(b"#!") and result.endswith(b"\r\n")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_get_config_var_wrapper_bindir(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
231
|
+
monkeypatch.setattr(relenv.runtime, "relenv_root", lambda: pathlib.Path("/root"))
|
|
232
|
+
wrapped = relenv.runtime.get_config_var_wrapper(lambda name: "/orig")
|
|
233
|
+
result = wrapped("BINDIR")
|
|
234
|
+
assert result == pathlib.Path("/root/Scripts")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def test_get_config_var_wrapper_other(
|
|
238
|
+
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
239
|
+
) -> None:
|
|
240
|
+
monkeypatch.setattr(relenv.runtime, "relenv_root", lambda: pathlib.Path("/root"))
|
|
241
|
+
result = relenv.runtime.get_config_var_wrapper(lambda name: "value")("OTHER")
|
|
242
|
+
assert result == "value"
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_system_sysconfig_json_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
246
|
+
monkeypatch.setattr(relenv.runtime, "_SYSTEM_CONFIG_VARS", None, raising=False)
|
|
247
|
+
monkeypatch.setattr(
|
|
248
|
+
pathlib.Path, "exists", lambda self: str(self) == "/usr/bin/python3"
|
|
249
|
+
)
|
|
250
|
+
monkeypatch.setattr(
|
|
251
|
+
relenv.runtime.subprocess,
|
|
252
|
+
"run",
|
|
253
|
+
lambda *args, **kwargs: SimpleNamespace(stdout=b"invalid", returncode=0),
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
def fake_loads(_data: bytes) -> dict:
|
|
257
|
+
raise json.JSONDecodeError("msg", "doc", 0)
|
|
258
|
+
|
|
259
|
+
monkeypatch.setattr(relenv.runtime.json, "loads", fake_loads)
|
|
260
|
+
result = relenv.runtime.system_sysconfig()
|
|
261
|
+
assert result == relenv.runtime.CONFIG_VARS_DEFAULTS
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def test_get_paths_wrapper_updates_scripts(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
265
|
+
def original_get_paths(
|
|
266
|
+
*, scheme: Optional[str], vars: Optional[dict[str, str]], expand: bool
|
|
267
|
+
) -> dict[str, str]:
|
|
268
|
+
return {"scripts": "/original/scripts"}
|
|
269
|
+
|
|
270
|
+
wrapped = relenv.runtime.get_paths_wrapper(original_get_paths, "default")
|
|
271
|
+
monkeypatch.setenv("RELENV_PIP_DIR", "/tmp/scripts")
|
|
272
|
+
monkeypatch.setattr(relenv.runtime, "relenv_root", lambda: pathlib.Path("/relroot"))
|
|
273
|
+
|
|
274
|
+
result = wrapped()
|
|
275
|
+
expected_root = os.fspath(pathlib.Path("/relroot"))
|
|
276
|
+
assert result["scripts"] == expected_root
|
|
277
|
+
assert relenv.runtime.sys.exec_prefix == expected_root
|
|
278
|
+
|
|
279
|
+
monkeypatch.delenv("RELENV_PIP_DIR", raising=False)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def test_get_config_vars_wrapper_updates(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
283
|
+
module = ModuleType("sysconfig")
|
|
284
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
285
|
+
|
|
286
|
+
def original() -> dict[str, str]:
|
|
287
|
+
return {
|
|
288
|
+
key: "orig"
|
|
289
|
+
for key in (
|
|
290
|
+
"AR",
|
|
291
|
+
"CC",
|
|
292
|
+
"CFLAGS",
|
|
293
|
+
"CPPFLAGS",
|
|
294
|
+
"CXX",
|
|
295
|
+
"LIBDEST",
|
|
296
|
+
"SCRIPTDIR",
|
|
297
|
+
"BLDSHARED",
|
|
298
|
+
"LDFLAGS",
|
|
299
|
+
"LDCXXSHARED",
|
|
300
|
+
"LDSHARED",
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
monkeypatch.setattr(
|
|
305
|
+
relenv.runtime,
|
|
306
|
+
"system_sysconfig",
|
|
307
|
+
lambda: {
|
|
308
|
+
key: "sys"
|
|
309
|
+
for key in (
|
|
310
|
+
"AR",
|
|
311
|
+
"CC",
|
|
312
|
+
"CFLAGS",
|
|
313
|
+
"CPPFLAGS",
|
|
314
|
+
"CXX",
|
|
315
|
+
"LIBDEST",
|
|
316
|
+
"SCRIPTDIR",
|
|
317
|
+
"BLDSHARED",
|
|
318
|
+
"LDFLAGS",
|
|
319
|
+
"LDCXXSHARED",
|
|
320
|
+
"LDSHARED",
|
|
321
|
+
)
|
|
322
|
+
},
|
|
323
|
+
)
|
|
324
|
+
wrapped = relenv.runtime.get_config_vars_wrapper(original, module)
|
|
325
|
+
result = wrapped()
|
|
326
|
+
assert module._CONFIG_VARS["AR"] == "sys"
|
|
327
|
+
assert result == {
|
|
328
|
+
key: "orig"
|
|
329
|
+
for key in (
|
|
330
|
+
"AR",
|
|
331
|
+
"CC",
|
|
332
|
+
"CFLAGS",
|
|
333
|
+
"CPPFLAGS",
|
|
334
|
+
"CXX",
|
|
335
|
+
"LIBDEST",
|
|
336
|
+
"SCRIPTDIR",
|
|
337
|
+
"BLDSHARED",
|
|
338
|
+
"LDFLAGS",
|
|
339
|
+
"LDCXXSHARED",
|
|
340
|
+
"LDSHARED",
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def test_get_config_vars_wrapper_buildenv_skip(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
346
|
+
module = ModuleType("sysconfig")
|
|
347
|
+
monkeypatch.setenv("RELENV_BUILDENV", "1")
|
|
348
|
+
marker = object()
|
|
349
|
+
wrapped = relenv.runtime.get_config_vars_wrapper(lambda: marker, module)
|
|
350
|
+
assert wrapped() is marker
|
|
351
|
+
monkeypatch.delenv("RELENV_BUILDENV", raising=False)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def test_finalize_options_wrapper_appends_include(
|
|
355
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
356
|
+
) -> None:
|
|
357
|
+
class Dummy:
|
|
358
|
+
def __init__(self) -> None:
|
|
359
|
+
self.include_dirs: list[str] = []
|
|
360
|
+
|
|
361
|
+
def original(self: Dummy, *args: object, **kwargs: object) -> None:
|
|
362
|
+
self.include_dirs.append("existing")
|
|
363
|
+
|
|
364
|
+
wrapped = relenv.runtime.finalize_options_wrapper(original)
|
|
365
|
+
dummy = Dummy()
|
|
366
|
+
monkeypatch.setenv("RELENV_BUILDENV", "1")
|
|
367
|
+
monkeypatch.setattr(relenv.runtime, "relenv_root", lambda: pathlib.Path("/relroot"))
|
|
368
|
+
wrapped(dummy)
|
|
369
|
+
expected_include = os.fspath(pathlib.Path("/relroot") / "include")
|
|
370
|
+
assert dummy.include_dirs == ["existing", expected_include]
|
|
371
|
+
monkeypatch.delenv("RELENV_BUILDENV", raising=False)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def test_install_wheel_wrapper_processes_record(
|
|
375
|
+
tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
|
|
376
|
+
) -> None:
|
|
377
|
+
plat_dir = tmp_path / "plat"
|
|
378
|
+
info_dir = plat_dir / "demo.dist-info"
|
|
379
|
+
info_dir.mkdir(parents=True)
|
|
380
|
+
record = info_dir / "RECORD"
|
|
381
|
+
record.write_text("libdemo.so,,\n")
|
|
382
|
+
binary = plat_dir / "libdemo.so"
|
|
383
|
+
binary.touch()
|
|
384
|
+
|
|
385
|
+
handled: list[tuple[pathlib.Path, pathlib.Path]] = []
|
|
386
|
+
monkeypatch.setattr(
|
|
387
|
+
relenv.runtime,
|
|
388
|
+
"relocate",
|
|
389
|
+
lambda: SimpleNamespace(
|
|
390
|
+
is_elf=lambda path: path.name.endswith(".so"),
|
|
391
|
+
is_macho=lambda path: False,
|
|
392
|
+
handle_elf=lambda file, lib_dir, fix, root: handled.append((file, lib_dir)),
|
|
393
|
+
handle_macho=lambda *args, **kwargs: None,
|
|
394
|
+
),
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
wheel_utils = ModuleType("pip._internal.utils.wheel")
|
|
398
|
+
wheel_utils.parse_wheel = lambda _zf, _name: ("demo.dist-info", {})
|
|
399
|
+
monkeypatch.setitem(sys.modules, wheel_utils.__name__, wheel_utils)
|
|
400
|
+
|
|
401
|
+
class DummyZip:
|
|
402
|
+
def __init__(self, path: pathlib.Path) -> None:
|
|
403
|
+
self.path = path
|
|
404
|
+
|
|
405
|
+
def __enter__(self) -> DummyZip:
|
|
406
|
+
return self
|
|
407
|
+
|
|
408
|
+
def __exit__(self, *exc: object) -> bool:
|
|
409
|
+
return False
|
|
410
|
+
|
|
411
|
+
monkeypatch.setattr("zipfile.ZipFile", DummyZip)
|
|
412
|
+
|
|
413
|
+
install_module = ModuleType("pip._internal.operations.install.wheel")
|
|
414
|
+
|
|
415
|
+
def original_install(*_args: object, **_kwargs: object) -> str:
|
|
416
|
+
return "original"
|
|
417
|
+
|
|
418
|
+
install_module.install_wheel = original_install # type: ignore[attr-defined]
|
|
419
|
+
monkeypatch.setitem(sys.modules, install_module.__name__, install_module)
|
|
420
|
+
|
|
421
|
+
wrapped_module = relenv.runtime.wrap_pip_install_wheel(install_module.__name__)
|
|
422
|
+
|
|
423
|
+
scheme = SimpleNamespace(
|
|
424
|
+
platlib=str(plat_dir),
|
|
425
|
+
)
|
|
426
|
+
wrapped_module.install_wheel(
|
|
427
|
+
"demo",
|
|
428
|
+
tmp_path / "wheel.whl",
|
|
429
|
+
scheme,
|
|
430
|
+
"desc",
|
|
431
|
+
None,
|
|
432
|
+
None,
|
|
433
|
+
None,
|
|
434
|
+
None,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
assert handled and handled[0][0].name == "libdemo.so"
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def test_install_wheel_wrapper_missing_file(
|
|
441
|
+
tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
|
|
442
|
+
) -> None:
|
|
443
|
+
plat_dir = tmp_path / "plat"
|
|
444
|
+
info_dir = plat_dir / "demo.dist-info"
|
|
445
|
+
info_dir.mkdir(parents=True)
|
|
446
|
+
(info_dir / "RECORD").write_text("missing.so,,\n")
|
|
447
|
+
(info_dir / "WHEEL").write_text("Wheel-Version: 1.0\n")
|
|
448
|
+
import zipfile
|
|
449
|
+
|
|
450
|
+
wheel_path = tmp_path / "demo_missing.whl"
|
|
451
|
+
with zipfile.ZipFile(wheel_path, "w") as zf:
|
|
452
|
+
zf.writestr("demo.dist-info/RECORD", "missing.so,,\n")
|
|
453
|
+
zf.writestr("demo.dist-info/WHEEL", "Wheel-Version: 1.0\n")
|
|
454
|
+
|
|
455
|
+
monkeypatch.setattr(
|
|
456
|
+
relenv.runtime,
|
|
457
|
+
"relocate",
|
|
458
|
+
lambda: SimpleNamespace(is_elf=lambda path: False, is_macho=lambda path: False),
|
|
459
|
+
)
|
|
460
|
+
module_utils = ModuleType("pip._internal.utils.wheel.missing")
|
|
461
|
+
module_utils.parse_wheel = lambda zf, name: ("demo.dist-info", {})
|
|
462
|
+
wheel_module = ModuleType("pip._internal.operations.install.wheel.missing")
|
|
463
|
+
wheel_module.install_wheel = lambda *args, **kwargs: None # type: ignore[attr-defined]
|
|
464
|
+
monkeypatch.setitem(sys.modules, module_utils.__name__, module_utils)
|
|
465
|
+
monkeypatch.setitem(sys.modules, wheel_module.__name__, wheel_module)
|
|
466
|
+
monkeypatch.setattr(
|
|
467
|
+
relenv.runtime.importlib,
|
|
468
|
+
"import_module",
|
|
469
|
+
lambda name: wheel_module if name == wheel_module.__name__ else module_utils,
|
|
470
|
+
)
|
|
471
|
+
scheme = SimpleNamespace(platlib=str(plat_dir))
|
|
472
|
+
relenv.runtime.wrap_pip_install_wheel(wheel_module.__name__).install_wheel(
|
|
473
|
+
"demo", wheel_path, scheme, "desc", None, None, None, None
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def test_install_wheel_wrapper_macho_with_otool(
|
|
478
|
+
tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
|
|
479
|
+
) -> None:
|
|
480
|
+
plat_dir = tmp_path / "plat"
|
|
481
|
+
info_dir = plat_dir / "demo.dist-info"
|
|
482
|
+
info_dir.mkdir(parents=True)
|
|
483
|
+
(plat_dir / "libmach.dylib").touch()
|
|
484
|
+
(info_dir / "RECORD").write_text("libmach.dylib,,\n")
|
|
485
|
+
(info_dir / "WHEEL").write_text("Wheel-Version: 1.0\n")
|
|
486
|
+
import zipfile
|
|
487
|
+
|
|
488
|
+
wheel_path = tmp_path / "demo_otool.whl"
|
|
489
|
+
with zipfile.ZipFile(wheel_path, "w") as zf:
|
|
490
|
+
zf.writestr("demo.dist-info/RECORD", "libmach.dylib,,\n")
|
|
491
|
+
zf.writestr("demo.dist-info/WHEEL", "Wheel-Version: 1.0\n")
|
|
492
|
+
|
|
493
|
+
monkeypatch.setattr(
|
|
494
|
+
relenv.runtime,
|
|
495
|
+
"relocate",
|
|
496
|
+
lambda: SimpleNamespace(
|
|
497
|
+
is_elf=lambda path: False,
|
|
498
|
+
is_macho=lambda path: True,
|
|
499
|
+
handle_macho=lambda *args, **kwargs: None,
|
|
500
|
+
),
|
|
501
|
+
)
|
|
502
|
+
monkeypatch.setattr(relenv.runtime.shutil, "which", lambda cmd: "/usr/bin/otool")
|
|
503
|
+
module_utils = ModuleType("pip._internal.utils.wheel.otool")
|
|
504
|
+
module_utils.parse_wheel = lambda zf, name: ("demo.dist-info", {})
|
|
505
|
+
wheel_module = ModuleType("pip._internal.operations.install.wheel.otool")
|
|
506
|
+
wheel_module.install_wheel = lambda *args, **kwargs: None # type: ignore[attr-defined]
|
|
507
|
+
monkeypatch.setitem(sys.modules, module_utils.__name__, module_utils)
|
|
508
|
+
monkeypatch.setitem(sys.modules, wheel_module.__name__, wheel_module)
|
|
509
|
+
monkeypatch.setattr(
|
|
510
|
+
relenv.runtime.importlib,
|
|
511
|
+
"import_module",
|
|
512
|
+
lambda name: wheel_module if name == wheel_module.__name__ else module_utils,
|
|
513
|
+
)
|
|
514
|
+
scheme = SimpleNamespace(platlib=str(plat_dir))
|
|
515
|
+
relenv.runtime.wrap_pip_install_wheel(wheel_module.__name__).install_wheel(
|
|
516
|
+
"demo", wheel_path, scheme, "desc", None, None, None, None
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def test_install_wheel_wrapper_macho_without_otool(
|
|
521
|
+
tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
|
|
522
|
+
) -> None:
|
|
523
|
+
plat_dir = tmp_path / "plat"
|
|
524
|
+
info_dir = plat_dir / "demo.dist-info"
|
|
525
|
+
info_dir.mkdir(parents=True)
|
|
526
|
+
(plat_dir / "libmach.dylib").touch()
|
|
527
|
+
(info_dir / "RECORD").write_text("libmach.dylib,,\n")
|
|
528
|
+
(info_dir / "WHEEL").write_text("Wheel-Version: 1.0\n")
|
|
529
|
+
import zipfile
|
|
530
|
+
|
|
531
|
+
wheel_path = tmp_path / "demo_no_otool.whl"
|
|
532
|
+
with zipfile.ZipFile(wheel_path, "w") as zf:
|
|
533
|
+
zf.writestr("demo.dist-info/RECORD", "libmach.dylib,,\n")
|
|
534
|
+
zf.writestr("demo.dist-info/WHEEL", "Wheel-Version: 1.0\n")
|
|
535
|
+
|
|
536
|
+
monkeypatch.setattr(
|
|
537
|
+
relenv.runtime,
|
|
538
|
+
"relocate",
|
|
539
|
+
lambda: SimpleNamespace(
|
|
540
|
+
is_elf=lambda path: False,
|
|
541
|
+
is_macho=lambda path: True,
|
|
542
|
+
handle_macho=lambda *args, **kwargs: _raise(
|
|
543
|
+
AssertionError("unexpected macho")
|
|
544
|
+
),
|
|
545
|
+
),
|
|
546
|
+
)
|
|
547
|
+
monkeypatch.setattr(relenv.runtime.shutil, "which", lambda cmd: None)
|
|
548
|
+
messages: list[str] = []
|
|
549
|
+
monkeypatch.setattr(relenv.runtime, "debug", lambda msg: messages.append(str(msg)))
|
|
550
|
+
module_utils = ModuleType("pip._internal.utils.wheel.no_otool")
|
|
551
|
+
module_utils.parse_wheel = lambda zf, name: ("demo.dist-info", {})
|
|
552
|
+
wheel_module = ModuleType("pip._internal.operations.install.wheel.no_otool")
|
|
553
|
+
wheel_module.install_wheel = lambda *args, **kwargs: None # type: ignore[attr-defined]
|
|
554
|
+
monkeypatch.setitem(sys.modules, module_utils.__name__, module_utils)
|
|
555
|
+
monkeypatch.setitem(sys.modules, wheel_module.__name__, wheel_module)
|
|
556
|
+
monkeypatch.setattr(
|
|
557
|
+
relenv.runtime.importlib,
|
|
558
|
+
"import_module",
|
|
559
|
+
lambda name: wheel_module if name == wheel_module.__name__ else module_utils,
|
|
560
|
+
)
|
|
561
|
+
scheme = SimpleNamespace(platlib=str(plat_dir))
|
|
562
|
+
relenv.runtime.wrap_pip_install_wheel(wheel_module.__name__).install_wheel(
|
|
563
|
+
"demo", wheel_path, scheme, "desc", None, None, None, None
|
|
564
|
+
)
|
|
565
|
+
assert any("otool command is not available" in msg for msg in messages)
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def test_install_legacy_wrapper_prefix(
|
|
569
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
|
|
570
|
+
) -> None:
|
|
571
|
+
pkg_dir = tmp_path / "pkg"
|
|
572
|
+
pkg_dir.mkdir()
|
|
573
|
+
(pkg_dir / "PKG-INFO").write_text("Version: 1.0\nName: demo\n")
|
|
574
|
+
sitepack = (
|
|
575
|
+
tmp_path
|
|
576
|
+
/ "prefix"
|
|
577
|
+
/ "lib"
|
|
578
|
+
/ f"python{relenv.runtime.get_major_version()}"
|
|
579
|
+
/ "site-packages"
|
|
580
|
+
)
|
|
581
|
+
egg_dir = sitepack / "demo-1.0.egg-info"
|
|
582
|
+
egg_dir.mkdir(parents=True)
|
|
583
|
+
(egg_dir / "installed-files.txt").write_text("missing.so\n")
|
|
584
|
+
scheme = SimpleNamespace(
|
|
585
|
+
purelib=str(tmp_path / "pure"), platlib=str(tmp_path / "pure")
|
|
586
|
+
)
|
|
587
|
+
module = ModuleType("pip._internal.operations.install.legacy.prefix")
|
|
588
|
+
module.install = lambda *args, **kwargs: None # type: ignore[attr-defined]
|
|
589
|
+
monkeypatch.setitem(sys.modules, module.__name__, module)
|
|
590
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", lambda name: module)
|
|
591
|
+
monkeypatch.setattr(
|
|
592
|
+
relenv.runtime,
|
|
593
|
+
"relocate",
|
|
594
|
+
lambda: SimpleNamespace(is_elf=lambda path: False, is_macho=lambda path: False),
|
|
595
|
+
)
|
|
596
|
+
wrapper = relenv.runtime.wrap_pip_install_legacy(module.__name__)
|
|
597
|
+
wrapper.install(
|
|
598
|
+
None,
|
|
599
|
+
None,
|
|
600
|
+
str(sitepack.parent.parent.parent),
|
|
601
|
+
None,
|
|
602
|
+
str(sitepack.parent.parent.parent),
|
|
603
|
+
False,
|
|
604
|
+
False,
|
|
605
|
+
scheme,
|
|
606
|
+
str(pkg_dir / "setup.py"),
|
|
607
|
+
False,
|
|
608
|
+
"demo",
|
|
609
|
+
None,
|
|
610
|
+
pkg_dir,
|
|
611
|
+
"demo",
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def test_install_legacy_wrapper_no_egginfo(
|
|
616
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
|
|
617
|
+
) -> None:
|
|
618
|
+
pkg_dir = tmp_path / "pkg"
|
|
619
|
+
pkg_dir.mkdir()
|
|
620
|
+
(pkg_dir / "PKG-INFO").write_text("Name: demo\nVersion: 1.0\n")
|
|
621
|
+
scheme = SimpleNamespace(purelib=str(tmp_path / "pure"))
|
|
622
|
+
module = ModuleType("pip._internal.operations.install.legacy.none")
|
|
623
|
+
module.install = lambda *args, **kwargs: None # type: ignore[attr-defined]
|
|
624
|
+
monkeypatch.setitem(sys.modules, module.__name__, module)
|
|
625
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", lambda name: module)
|
|
626
|
+
wrapper = relenv.runtime.wrap_pip_install_legacy(module.__name__)
|
|
627
|
+
wrapper.install(
|
|
628
|
+
None,
|
|
629
|
+
None,
|
|
630
|
+
None,
|
|
631
|
+
None,
|
|
632
|
+
None,
|
|
633
|
+
False,
|
|
634
|
+
False,
|
|
635
|
+
scheme,
|
|
636
|
+
str(pkg_dir / "setup.py"),
|
|
637
|
+
False,
|
|
638
|
+
"demo",
|
|
639
|
+
None,
|
|
640
|
+
pkg_dir,
|
|
641
|
+
"demo",
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def test_install_legacy_wrapper_file_missing(
|
|
646
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
|
|
647
|
+
) -> None:
|
|
648
|
+
pkg_dir = tmp_path / "pkg"
|
|
649
|
+
pkg_dir.mkdir()
|
|
650
|
+
(pkg_dir / "PKG-INFO").write_text("Name: demo\nVersion: 1.0\n")
|
|
651
|
+
egg_dir = tmp_path / "pure" / "demo-1.0.egg-info"
|
|
652
|
+
egg_dir.mkdir(parents=True)
|
|
653
|
+
(egg_dir / "installed-files.txt").write_text("missing.so\n")
|
|
654
|
+
scheme = SimpleNamespace(
|
|
655
|
+
purelib=str(tmp_path / "pure"), platlib=str(tmp_path / "pure")
|
|
656
|
+
)
|
|
657
|
+
module = ModuleType("pip._internal.operations.install.legacy.missing")
|
|
658
|
+
module.install = lambda *args, **kwargs: None # type: ignore[attr-defined]
|
|
659
|
+
monkeypatch.setitem(sys.modules, module.__name__, module)
|
|
660
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", lambda name: module)
|
|
661
|
+
monkeypatch.setattr(
|
|
662
|
+
relenv.runtime,
|
|
663
|
+
"relocate",
|
|
664
|
+
lambda: SimpleNamespace(is_elf=lambda path: False, is_macho=lambda path: False),
|
|
665
|
+
)
|
|
666
|
+
wrapper = relenv.runtime.wrap_pip_install_legacy(module.__name__)
|
|
667
|
+
wrapper.install(
|
|
668
|
+
None,
|
|
669
|
+
None,
|
|
670
|
+
None,
|
|
671
|
+
None,
|
|
672
|
+
None,
|
|
673
|
+
False,
|
|
674
|
+
False,
|
|
675
|
+
scheme,
|
|
676
|
+
str(pkg_dir / "setup.py"),
|
|
677
|
+
False,
|
|
678
|
+
"demo",
|
|
679
|
+
None,
|
|
680
|
+
pkg_dir,
|
|
681
|
+
"demo",
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def test_install_legacy_wrapper_handles_elf(
|
|
686
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
|
|
687
|
+
) -> None:
|
|
688
|
+
pkg_dir = tmp_path / "pkg"
|
|
689
|
+
pkg_dir.mkdir()
|
|
690
|
+
(pkg_dir / "PKG-INFO").write_text("Name: demo\nVersion: 1.0\n")
|
|
691
|
+
egg_dir = tmp_path / "pure" / "demo-1.0.egg-info"
|
|
692
|
+
egg_dir.mkdir(parents=True)
|
|
693
|
+
binary = tmp_path / "pure" / "libdemo.so"
|
|
694
|
+
binary.parent.mkdir(parents=True, exist_ok=True)
|
|
695
|
+
binary.write_bytes(b"")
|
|
696
|
+
(egg_dir / "installed-files.txt").write_text(f"{binary}\n")
|
|
697
|
+
scheme = SimpleNamespace(
|
|
698
|
+
purelib=str(tmp_path / "pure"), platlib=str(tmp_path / "pure")
|
|
699
|
+
)
|
|
700
|
+
module = ModuleType("pip._internal.operations.install.legacy.elf")
|
|
701
|
+
module.install = lambda *args, **kwargs: None # type: ignore[attr-defined]
|
|
702
|
+
monkeypatch.setitem(sys.modules, module.__name__, module)
|
|
703
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", lambda name: module)
|
|
704
|
+
handled: list[tuple[pathlib.Path, pathlib.Path, bool, pathlib.Path]] = []
|
|
705
|
+
|
|
706
|
+
def fake_relocate() -> SimpleNamespace:
|
|
707
|
+
return SimpleNamespace(
|
|
708
|
+
is_elf=lambda path: path == binary,
|
|
709
|
+
is_macho=lambda path: False,
|
|
710
|
+
handle_elf=lambda *args: handled.append(args),
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
monkeypatch.setattr(relenv.runtime, "relocate", fake_relocate)
|
|
714
|
+
monkeypatch.setattr(relenv.runtime, "relenv_root", lambda: tmp_path)
|
|
715
|
+
wrapper = relenv.runtime.wrap_pip_install_legacy(module.__name__)
|
|
716
|
+
wrapper.install(
|
|
717
|
+
None,
|
|
718
|
+
None,
|
|
719
|
+
None,
|
|
720
|
+
None,
|
|
721
|
+
None,
|
|
722
|
+
False,
|
|
723
|
+
False,
|
|
724
|
+
scheme,
|
|
725
|
+
str(pkg_dir / "setup.py"),
|
|
726
|
+
False,
|
|
727
|
+
"demo",
|
|
728
|
+
None,
|
|
729
|
+
pkg_dir,
|
|
730
|
+
"demo",
|
|
731
|
+
)
|
|
732
|
+
assert handled and handled[0][0] == binary
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
def test_wrap_sysconfig_legacy(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
736
|
+
module = ModuleType("sysconfig")
|
|
737
|
+
|
|
738
|
+
def get_config_var(name: str) -> str:
|
|
739
|
+
return name
|
|
740
|
+
|
|
741
|
+
def get_config_vars() -> dict[str, str]:
|
|
742
|
+
return relenv.runtime.CONFIG_VARS_DEFAULTS.copy()
|
|
743
|
+
|
|
744
|
+
def get_paths(**kwargs: object) -> dict[str, str]:
|
|
745
|
+
return {"scripts": "/tmp"}
|
|
746
|
+
|
|
747
|
+
def default_scheme() -> str:
|
|
748
|
+
return "legacy"
|
|
749
|
+
|
|
750
|
+
module.get_config_var = get_config_var
|
|
751
|
+
module.get_config_vars = get_config_vars
|
|
752
|
+
module.get_paths = get_paths
|
|
753
|
+
module._get_default_scheme = default_scheme # type: ignore[attr-defined]
|
|
754
|
+
monkeypatch.setitem(sys.modules, "sysconfig.legacy", module)
|
|
755
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", lambda name: module)
|
|
756
|
+
wrapped = relenv.runtime.wrap_sysconfig("sysconfig.legacy")
|
|
757
|
+
assert wrapped is module
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
def test_wrap_pip_distlib_scripts(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
761
|
+
class ScriptMaker:
|
|
762
|
+
def __init__(self) -> None:
|
|
763
|
+
self.target_dir = "/tmp/dir"
|
|
764
|
+
|
|
765
|
+
def _build_shebang(self, target: str) -> bytes:
|
|
766
|
+
return b"orig"
|
|
767
|
+
|
|
768
|
+
module = ModuleType("pip._vendor.distlib.scripts")
|
|
769
|
+
module.ScriptMaker = ScriptMaker
|
|
770
|
+
monkeypatch.setitem(sys.modules, module.__name__, module)
|
|
771
|
+
wrapped = relenv.runtime.wrap_pip_distlib_scripts(module.__name__)
|
|
772
|
+
monkeypatch.setattr(
|
|
773
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
774
|
+
)
|
|
775
|
+
monkeypatch.setattr(
|
|
776
|
+
relenv.runtime,
|
|
777
|
+
"common",
|
|
778
|
+
lambda: SimpleNamespace(
|
|
779
|
+
relative_interpreter=lambda *args, **kwargs: _raise(ValueError("boom"))
|
|
780
|
+
),
|
|
781
|
+
)
|
|
782
|
+
result = wrapped.ScriptMaker()._build_shebang("target")
|
|
783
|
+
assert result == b"orig"
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
def test_wrap_distutils_command(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
787
|
+
class BuildExt:
|
|
788
|
+
def finalize_options(self) -> None:
|
|
789
|
+
return None
|
|
790
|
+
|
|
791
|
+
module = ModuleType("distutils.command.build_ext")
|
|
792
|
+
module.build_ext = BuildExt
|
|
793
|
+
monkeypatch.setitem(sys.modules, module.__name__, module)
|
|
794
|
+
wrapped = relenv.runtime.wrap_distutils_command(module.__name__)
|
|
795
|
+
dummy = wrapped.build_ext()
|
|
796
|
+
monkeypatch.setenv("RELENV_BUILDENV", "1")
|
|
797
|
+
monkeypatch.setattr(relenv.runtime, "relenv_root", lambda: pathlib.Path("/rel"))
|
|
798
|
+
dummy.include_dirs = []
|
|
799
|
+
wrapped.build_ext.finalize_options(dummy)
|
|
800
|
+
expected_include = os.fspath(pathlib.Path("/rel") / "include")
|
|
801
|
+
assert expected_include in dummy.include_dirs
|
|
802
|
+
monkeypatch.delenv("RELENV_BUILDENV", raising=False)
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
def test_wrap_pip_build_wheel_sets_env(
|
|
806
|
+
tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
|
|
807
|
+
) -> None:
|
|
808
|
+
relenv.runtime.TARGET.TARGET = False
|
|
809
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
810
|
+
toolchain = tmp_path / "toolchain" / "trip"
|
|
811
|
+
(toolchain / "sysroot" / "lib").mkdir(parents=True)
|
|
812
|
+
toolchain.mkdir(parents=True, exist_ok=True)
|
|
813
|
+
base_dir = tmp_path
|
|
814
|
+
set_calls: list[tuple[str, str]] = []
|
|
815
|
+
monkeypatch.setattr(
|
|
816
|
+
relenv.runtime,
|
|
817
|
+
"set_env_if_not_set",
|
|
818
|
+
lambda name, value: set_calls.append((name, value)),
|
|
819
|
+
)
|
|
820
|
+
stub_common = SimpleNamespace(
|
|
821
|
+
DATA_DIR=base_dir,
|
|
822
|
+
get_triplet=lambda: "trip",
|
|
823
|
+
get_toolchain=lambda: toolchain,
|
|
824
|
+
)
|
|
825
|
+
monkeypatch.setattr(relenv.runtime, "common", lambda: stub_common)
|
|
826
|
+
|
|
827
|
+
class DummyModule(ModuleType):
|
|
828
|
+
def build_wheel_pep517(self, *args: object, **kwargs: object) -> str: # type: ignore[override]
|
|
829
|
+
return "built"
|
|
830
|
+
|
|
831
|
+
module_name = "pip._internal.operations.build.wheel"
|
|
832
|
+
dummy = DummyModule(module_name)
|
|
833
|
+
monkeypatch.setitem(sys.modules, module_name, dummy)
|
|
834
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", lambda name: dummy)
|
|
835
|
+
|
|
836
|
+
monkeypatch.setattr(
|
|
837
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
838
|
+
)
|
|
839
|
+
wrapped = relenv.runtime.wrap_pip_build_wheel(module_name)
|
|
840
|
+
result = wrapped.build_wheel_pep517("backend", {}, {})
|
|
841
|
+
assert result == "built"
|
|
842
|
+
assert any(name == "CARGO_HOME" for name, _ in set_calls)
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
def test_wrap_pip_build_wheel_toolchain_missing(
|
|
846
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
847
|
+
) -> None:
|
|
848
|
+
relenv.runtime.TARGET.TARGET = False
|
|
849
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
850
|
+
stub_common = SimpleNamespace(
|
|
851
|
+
DATA_DIR=pathlib.Path("/data"),
|
|
852
|
+
get_triplet=lambda: "trip",
|
|
853
|
+
get_toolchain=lambda: None,
|
|
854
|
+
)
|
|
855
|
+
monkeypatch.setattr(relenv.runtime, "common", lambda: stub_common)
|
|
856
|
+
module_name = "pip._internal.operations.build.none"
|
|
857
|
+
module = ModuleType(module_name)
|
|
858
|
+
module.build_wheel_pep517 = lambda *args, **kwargs: "built" # type: ignore[attr-defined]
|
|
859
|
+
monkeypatch.setitem(sys.modules, module_name, module)
|
|
860
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", lambda name: module)
|
|
861
|
+
|
|
862
|
+
wrapped = relenv.runtime.wrap_pip_build_wheel(module_name)
|
|
863
|
+
assert wrapped.build_wheel_pep517("backend", {}, {}) == "built"
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
def test_wrap_pip_build_wheel_non_linux(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
867
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "darwin", raising=False)
|
|
868
|
+
module_name = "pip._internal.operations.build.nonlinux"
|
|
869
|
+
module = ModuleType(module_name)
|
|
870
|
+
module.build_wheel_pep517 = lambda *args, **kwargs: "built" # type: ignore[attr-defined]
|
|
871
|
+
monkeypatch.setitem(sys.modules, module_name, module)
|
|
872
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", lambda name: module)
|
|
873
|
+
wrapped = relenv.runtime.wrap_pip_build_wheel(module_name)
|
|
874
|
+
assert wrapped.build_wheel_pep517("backend", {}, {}) == "built"
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
def test_wrap_cmd_install_updates_target(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
878
|
+
relenv.runtime.TARGET.TARGET = False
|
|
879
|
+
relenv.runtime.TARGET.PATH = None
|
|
880
|
+
relenv.runtime.TARGET.IGNORE = False
|
|
881
|
+
|
|
882
|
+
fake_module = ModuleType("pip._internal.commands.install")
|
|
883
|
+
|
|
884
|
+
class FakeInstallCommand:
|
|
885
|
+
def run(self, options: SimpleNamespace, args: list[str]) -> str:
|
|
886
|
+
options.ran = True
|
|
887
|
+
return "ran"
|
|
888
|
+
|
|
889
|
+
def _handle_target_dir(
|
|
890
|
+
self, target_dir: str, target_temp_dir: str, upgrade: bool
|
|
891
|
+
) -> str:
|
|
892
|
+
return "handled"
|
|
893
|
+
|
|
894
|
+
fake_module.InstallCommand = FakeInstallCommand
|
|
895
|
+
monkeypatch.setitem(sys.modules, fake_module.__name__, fake_module)
|
|
896
|
+
|
|
897
|
+
status_module = ModuleType("pip._internal.cli.status_codes")
|
|
898
|
+
status_module.SUCCESS = 0
|
|
899
|
+
monkeypatch.setitem(sys.modules, status_module.__name__, status_module)
|
|
900
|
+
|
|
901
|
+
original_import = relenv.runtime.importlib.import_module
|
|
902
|
+
|
|
903
|
+
def fake_import_module(name: str) -> ModuleType:
|
|
904
|
+
if name == fake_module.__name__:
|
|
905
|
+
return fake_module
|
|
906
|
+
return original_import(name)
|
|
907
|
+
|
|
908
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", fake_import_module)
|
|
909
|
+
|
|
910
|
+
wrapped = relenv.runtime.wrap_cmd_install(fake_module.__name__)
|
|
911
|
+
options = SimpleNamespace(
|
|
912
|
+
use_user_site=False, target_dir="/tmp/target", ignore_installed=True
|
|
913
|
+
)
|
|
914
|
+
command = wrapped.InstallCommand()
|
|
915
|
+
result = command.run(options, [])
|
|
916
|
+
|
|
917
|
+
assert result == "ran"
|
|
918
|
+
assert relenv.runtime.TARGET.TARGET is True
|
|
919
|
+
assert relenv.runtime.TARGET.PATH == "/tmp/target"
|
|
920
|
+
assert relenv.runtime.TARGET.IGNORE is True
|
|
921
|
+
assert command._handle_target_dir("a", "b", True) == 0
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
def test_wrap_cmd_install_no_user_site(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
925
|
+
relenv.runtime.TARGET.TARGET = False
|
|
926
|
+
fake_module = ModuleType("pip._internal.commands.install.skip")
|
|
927
|
+
|
|
928
|
+
class InstallCommand:
|
|
929
|
+
def run(self, options: SimpleNamespace, args: list[str]) -> str:
|
|
930
|
+
return "ran"
|
|
931
|
+
|
|
932
|
+
fake_module.InstallCommand = InstallCommand
|
|
933
|
+
monkeypatch.setitem(sys.modules, fake_module.__name__, fake_module)
|
|
934
|
+
|
|
935
|
+
module_status = ModuleType("pip._internal.cli.status_codes")
|
|
936
|
+
module_status.SUCCESS = 0
|
|
937
|
+
monkeypatch.setitem(sys.modules, module_status.__name__, module_status)
|
|
938
|
+
|
|
939
|
+
monkeypatch.setattr(
|
|
940
|
+
relenv.runtime.importlib,
|
|
941
|
+
"import_module",
|
|
942
|
+
lambda name: fake_module if name == fake_module.__name__ else module_status,
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
wrapped = relenv.runtime.wrap_cmd_install(fake_module.__name__)
|
|
946
|
+
options = SimpleNamespace(
|
|
947
|
+
use_user_site=True, target_dir=None, ignore_installed=False
|
|
948
|
+
)
|
|
949
|
+
result = wrapped.InstallCommand().run(options, [])
|
|
950
|
+
assert result == "ran"
|
|
951
|
+
assert relenv.runtime.TARGET.TARGET is False
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
def test_wrap_locations_applies_target(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
955
|
+
relenv.runtime.TARGET.TARGET = True
|
|
956
|
+
relenv.runtime.TARGET.INSTALL = True
|
|
957
|
+
relenv.runtime.TARGET.PATH = "/target/path"
|
|
958
|
+
|
|
959
|
+
scheme_module = ModuleType("pip._internal.models.scheme")
|
|
960
|
+
|
|
961
|
+
class Scheme:
|
|
962
|
+
def __init__(
|
|
963
|
+
self,
|
|
964
|
+
platlib: str,
|
|
965
|
+
purelib: str,
|
|
966
|
+
headers: str,
|
|
967
|
+
scripts: str,
|
|
968
|
+
data: str,
|
|
969
|
+
) -> None:
|
|
970
|
+
self.platlib = platlib
|
|
971
|
+
self.purelib = purelib
|
|
972
|
+
self.headers = headers
|
|
973
|
+
self.scripts = scripts
|
|
974
|
+
self.data = data
|
|
975
|
+
|
|
976
|
+
scheme_module.Scheme = Scheme
|
|
977
|
+
monkeypatch.setitem(sys.modules, scheme_module.__name__, scheme_module)
|
|
978
|
+
|
|
979
|
+
fake_locations = ModuleType("pip._internal.locations")
|
|
980
|
+
|
|
981
|
+
class OriginalScheme:
|
|
982
|
+
platlib = "/original/plat"
|
|
983
|
+
purelib = "/original/pure"
|
|
984
|
+
headers = "headers"
|
|
985
|
+
scripts = "scripts"
|
|
986
|
+
data = "data"
|
|
987
|
+
|
|
988
|
+
def get_scheme(
|
|
989
|
+
dist_name: str,
|
|
990
|
+
user: bool = False,
|
|
991
|
+
home: str | None = None,
|
|
992
|
+
root: str | None = None,
|
|
993
|
+
isolated: bool = False,
|
|
994
|
+
prefix: str | None = None,
|
|
995
|
+
) -> OriginalScheme:
|
|
996
|
+
return OriginalScheme()
|
|
997
|
+
|
|
998
|
+
fake_locations.get_scheme = get_scheme # type: ignore[attr-defined]
|
|
999
|
+
monkeypatch.setitem(sys.modules, fake_locations.__name__, fake_locations)
|
|
1000
|
+
|
|
1001
|
+
original_import = relenv.runtime.importlib.import_module
|
|
1002
|
+
|
|
1003
|
+
def fake_import_module(name: str) -> ModuleType:
|
|
1004
|
+
if name == fake_locations.__name__:
|
|
1005
|
+
return fake_locations
|
|
1006
|
+
if name == scheme_module.__name__:
|
|
1007
|
+
return scheme_module
|
|
1008
|
+
return original_import(name)
|
|
1009
|
+
|
|
1010
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", fake_import_module)
|
|
1011
|
+
|
|
1012
|
+
wrapped = relenv.runtime.wrap_locations(fake_locations.__name__)
|
|
1013
|
+
scheme = wrapped.get_scheme("dist")
|
|
1014
|
+
assert scheme.platlib == "/target/path"
|
|
1015
|
+
assert scheme.purelib == "/target/path"
|
|
1016
|
+
assert scheme.headers == "headers"
|
|
1017
|
+
assert scheme.scripts == "scripts"
|
|
1018
|
+
assert scheme.data == "data"
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
def test_wrap_locations_without_target(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1022
|
+
relenv.runtime.TARGET.TARGET = False
|
|
1023
|
+
fake_module = ModuleType("pip._internal.locations.plain")
|
|
1024
|
+
|
|
1025
|
+
class OriginalScheme:
|
|
1026
|
+
def __init__(self) -> None:
|
|
1027
|
+
self.platlib = "/plat"
|
|
1028
|
+
|
|
1029
|
+
fake_module.get_scheme = lambda *args, **kwargs: OriginalScheme()
|
|
1030
|
+
monkeypatch.setitem(sys.modules, fake_module.__name__, fake_module)
|
|
1031
|
+
monkeypatch.setattr(
|
|
1032
|
+
relenv.runtime.importlib, "import_module", lambda name: fake_module
|
|
1033
|
+
)
|
|
1034
|
+
|
|
1035
|
+
wrapped = relenv.runtime.wrap_locations(fake_module.__name__)
|
|
1036
|
+
scheme = wrapped.get_scheme("dist")
|
|
1037
|
+
assert scheme.platlib == "/plat"
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
def test_wrap_req_command_honors_ignore(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1041
|
+
relenv.runtime.TARGET.TARGET = True
|
|
1042
|
+
relenv.runtime.TARGET.IGNORE = True
|
|
1043
|
+
|
|
1044
|
+
fake_module = ModuleType("pip._internal.cli.req_command")
|
|
1045
|
+
|
|
1046
|
+
class RequirementCommand:
|
|
1047
|
+
def _build_package_finder(
|
|
1048
|
+
self,
|
|
1049
|
+
options: SimpleNamespace,
|
|
1050
|
+
session: object,
|
|
1051
|
+
target_python: object | None = None,
|
|
1052
|
+
ignore_requires_python: object | None = None,
|
|
1053
|
+
) -> bool:
|
|
1054
|
+
return options.ignore_installed
|
|
1055
|
+
|
|
1056
|
+
fake_module.RequirementCommand = RequirementCommand
|
|
1057
|
+
monkeypatch.setitem(sys.modules, fake_module.__name__, fake_module)
|
|
1058
|
+
|
|
1059
|
+
original_import = relenv.runtime.importlib.import_module
|
|
1060
|
+
|
|
1061
|
+
def fake_import_module(name: str) -> ModuleType:
|
|
1062
|
+
if name == fake_module.__name__:
|
|
1063
|
+
return fake_module
|
|
1064
|
+
return original_import(name)
|
|
1065
|
+
|
|
1066
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", fake_import_module)
|
|
1067
|
+
|
|
1068
|
+
wrapped = relenv.runtime.wrap_req_command(fake_module.__name__)
|
|
1069
|
+
command = wrapped.RequirementCommand()
|
|
1070
|
+
options = SimpleNamespace(ignore_installed=False)
|
|
1071
|
+
result = command._build_package_finder(options, object())
|
|
1072
|
+
assert options.ignore_installed is True
|
|
1073
|
+
assert result is True
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
def test_wrap_req_command_without_target(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1077
|
+
relenv.runtime.TARGET.TARGET = False
|
|
1078
|
+
fake_module = ModuleType("pip._internal.cli.req_command.plain")
|
|
1079
|
+
|
|
1080
|
+
class RequirementCommand:
|
|
1081
|
+
def _build_package_finder(
|
|
1082
|
+
self,
|
|
1083
|
+
options: SimpleNamespace,
|
|
1084
|
+
session: object,
|
|
1085
|
+
target_python: object | None = None,
|
|
1086
|
+
ignore_requires_python: object | None = None,
|
|
1087
|
+
) -> bool:
|
|
1088
|
+
return options.ignore_installed
|
|
1089
|
+
|
|
1090
|
+
fake_module.RequirementCommand = RequirementCommand
|
|
1091
|
+
monkeypatch.setitem(sys.modules, fake_module.__name__, fake_module)
|
|
1092
|
+
monkeypatch.setattr(
|
|
1093
|
+
relenv.runtime.importlib, "import_module", lambda name: fake_module
|
|
1094
|
+
)
|
|
1095
|
+
|
|
1096
|
+
wrapped = relenv.runtime.wrap_req_command(fake_module.__name__)
|
|
1097
|
+
options = SimpleNamespace(ignore_installed=False)
|
|
1098
|
+
result = wrapped.RequirementCommand()._build_package_finder(options, object())
|
|
1099
|
+
assert result is False
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
def test_wrap_req_install_sets_target_home(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1103
|
+
relenv.runtime.TARGET.TARGET = True
|
|
1104
|
+
relenv.runtime.TARGET.PATH = "/target/path"
|
|
1105
|
+
|
|
1106
|
+
fake_module = ModuleType("pip._internal.req.req_install")
|
|
1107
|
+
|
|
1108
|
+
class InstallRequirement:
|
|
1109
|
+
def install(
|
|
1110
|
+
self,
|
|
1111
|
+
install_options: object,
|
|
1112
|
+
global_options: object,
|
|
1113
|
+
root: object,
|
|
1114
|
+
home: object,
|
|
1115
|
+
prefix: object,
|
|
1116
|
+
warn_script_location: bool,
|
|
1117
|
+
use_user_site: bool,
|
|
1118
|
+
pycompile: bool,
|
|
1119
|
+
) -> tuple[object, object]:
|
|
1120
|
+
return install_options, home
|
|
1121
|
+
|
|
1122
|
+
fake_module.InstallRequirement = InstallRequirement
|
|
1123
|
+
monkeypatch.setitem(sys.modules, fake_module.__name__, fake_module)
|
|
1124
|
+
|
|
1125
|
+
original_import = relenv.runtime.importlib.import_module
|
|
1126
|
+
|
|
1127
|
+
def fake_import_module(name: str) -> ModuleType:
|
|
1128
|
+
if name == fake_module.__name__:
|
|
1129
|
+
return fake_module
|
|
1130
|
+
return original_import(name)
|
|
1131
|
+
|
|
1132
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", fake_import_module)
|
|
1133
|
+
|
|
1134
|
+
wrapped = relenv.runtime.wrap_req_install(fake_module.__name__)
|
|
1135
|
+
installer = wrapped.InstallRequirement()
|
|
1136
|
+
_, home = installer.install(None, None, None, None, None, True, False, True)
|
|
1137
|
+
assert home == relenv.runtime.TARGET.PATH
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
def test_wrap_req_install_short_signature(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1141
|
+
relenv.runtime.TARGET.TARGET = True
|
|
1142
|
+
relenv.runtime.TARGET.PATH = "/another/path"
|
|
1143
|
+
|
|
1144
|
+
module_name = "pip._internal.req.req_install.short"
|
|
1145
|
+
short_module = ModuleType(module_name)
|
|
1146
|
+
|
|
1147
|
+
class InstallRequirement:
|
|
1148
|
+
def install(
|
|
1149
|
+
self,
|
|
1150
|
+
global_options: object = None,
|
|
1151
|
+
root: object = None,
|
|
1152
|
+
home: object = None,
|
|
1153
|
+
prefix: object = None,
|
|
1154
|
+
warn_script_location: bool = True,
|
|
1155
|
+
use_user_site: bool = False,
|
|
1156
|
+
pycompile: bool = True,
|
|
1157
|
+
) -> tuple[object, object]:
|
|
1158
|
+
return global_options, home
|
|
1159
|
+
|
|
1160
|
+
short_module.InstallRequirement = InstallRequirement
|
|
1161
|
+
monkeypatch.setitem(sys.modules, module_name, short_module)
|
|
1162
|
+
|
|
1163
|
+
original_import = relenv.runtime.importlib.import_module
|
|
1164
|
+
|
|
1165
|
+
def fake_import_module(name: str) -> ModuleType:
|
|
1166
|
+
if name == module_name:
|
|
1167
|
+
return short_module
|
|
1168
|
+
return original_import(name)
|
|
1169
|
+
|
|
1170
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", fake_import_module)
|
|
1171
|
+
|
|
1172
|
+
wrapped = relenv.runtime.wrap_req_install(module_name)
|
|
1173
|
+
installer = wrapped.InstallRequirement()
|
|
1174
|
+
_, home = installer.install()
|
|
1175
|
+
assert home == relenv.runtime.TARGET.PATH
|
|
1176
|
+
|
|
1177
|
+
|
|
1178
|
+
def test_wrap_req_install_no_target(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1179
|
+
relenv.runtime.TARGET.TARGET = False
|
|
1180
|
+
module_name = "pip._internal.req.req_install.none"
|
|
1181
|
+
module = ModuleType(module_name)
|
|
1182
|
+
|
|
1183
|
+
class InstallRequirement:
|
|
1184
|
+
def install(
|
|
1185
|
+
self,
|
|
1186
|
+
install_options: object,
|
|
1187
|
+
global_options: object,
|
|
1188
|
+
root: object,
|
|
1189
|
+
home: object,
|
|
1190
|
+
prefix: object,
|
|
1191
|
+
warn_script_location: bool,
|
|
1192
|
+
use_user_site: bool,
|
|
1193
|
+
pycompile: bool,
|
|
1194
|
+
) -> str:
|
|
1195
|
+
return "installed"
|
|
1196
|
+
|
|
1197
|
+
module.InstallRequirement = InstallRequirement
|
|
1198
|
+
monkeypatch.setitem(sys.modules, module_name, module)
|
|
1199
|
+
monkeypatch.setattr(relenv.runtime.importlib, "import_module", lambda name: module)
|
|
1200
|
+
|
|
1201
|
+
wrapped = relenv.runtime.wrap_req_install(module_name)
|
|
1202
|
+
installer = wrapped.InstallRequirement()
|
|
1203
|
+
result = installer.install(None, None, None, None, None, True, False, True)
|
|
1204
|
+
assert result == "installed"
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
def test_wrapsitecustomize_sanitizes_sys_path(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1208
|
+
sanitized = ["sanitized/path"]
|
|
1209
|
+
monkeypatch.setattr(
|
|
1210
|
+
relenv.runtime,
|
|
1211
|
+
"common",
|
|
1212
|
+
lambda: SimpleNamespace(sanitize_sys_path=lambda _: sanitized),
|
|
1213
|
+
)
|
|
1214
|
+
monkeypatch.setattr(
|
|
1215
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1216
|
+
)
|
|
1217
|
+
|
|
1218
|
+
def original() -> None:
|
|
1219
|
+
pass
|
|
1220
|
+
|
|
1221
|
+
wrapped = relenv.runtime.wrapsitecustomize(original)
|
|
1222
|
+
wrapped()
|
|
1223
|
+
assert relenv.runtime.site.ENABLE_USER_SITE is False
|
|
1224
|
+
assert relenv.runtime.sys.path == sanitized
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
def test_install_cargo_config_toolchain_none(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1228
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1229
|
+
stub_common = SimpleNamespace(
|
|
1230
|
+
DATA_DIR=pathlib.Path("/data"),
|
|
1231
|
+
work_dirs=lambda: SimpleNamespace(data=pathlib.Path("/data")),
|
|
1232
|
+
get_triplet=lambda: "trip",
|
|
1233
|
+
get_toolchain=lambda: None,
|
|
1234
|
+
)
|
|
1235
|
+
monkeypatch.setattr(relenv.runtime, "common", lambda: stub_common)
|
|
1236
|
+
relenv.runtime.install_cargo_config()
|
|
1237
|
+
|
|
1238
|
+
|
|
1239
|
+
def test_install_cargo_config_toolchain_missing_dir(
|
|
1240
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
1241
|
+
) -> None:
|
|
1242
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1243
|
+
toolchain = SimpleNamespace(exists=lambda: False)
|
|
1244
|
+
stub_common = SimpleNamespace(
|
|
1245
|
+
DATA_DIR=pathlib.Path("/data"),
|
|
1246
|
+
work_dirs=lambda: SimpleNamespace(data=pathlib.Path("/data")),
|
|
1247
|
+
get_triplet=lambda: "trip",
|
|
1248
|
+
get_toolchain=lambda: toolchain,
|
|
1249
|
+
)
|
|
1250
|
+
monkeypatch.setattr(relenv.runtime, "common", lambda: stub_common)
|
|
1251
|
+
relenv.runtime.install_cargo_config()
|
|
1252
|
+
|
|
1253
|
+
|
|
1254
|
+
def test_install_cargo_config_non_linux(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1255
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "darwin", raising=False)
|
|
1256
|
+
relenv.runtime.install_cargo_config()
|
|
1257
|
+
|
|
1258
|
+
|
|
1259
|
+
def test_install_cargo_config_alt_triplet(
|
|
1260
|
+
tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
|
|
1261
|
+
) -> None:
|
|
1262
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1263
|
+
data_dir = tmp_path / "data"
|
|
1264
|
+
data_dir.mkdir()
|
|
1265
|
+
toolchain_dir = tmp_path / "toolchain" / "aarch"
|
|
1266
|
+
(toolchain_dir / "sysroot" / "lib").mkdir(parents=True)
|
|
1267
|
+
(toolchain_dir / "bin").mkdir(parents=True)
|
|
1268
|
+
(toolchain_dir / "bin" / "aarch-gcc").touch()
|
|
1269
|
+
stub_common = SimpleNamespace(
|
|
1270
|
+
DATA_DIR=tmp_path,
|
|
1271
|
+
work_dirs=lambda: SimpleNamespace(data=data_dir),
|
|
1272
|
+
get_triplet=lambda: "aarch",
|
|
1273
|
+
get_toolchain=lambda: toolchain_dir,
|
|
1274
|
+
)
|
|
1275
|
+
monkeypatch.setattr(relenv.runtime, "common", lambda: stub_common)
|
|
1276
|
+
relenv.runtime.install_cargo_config()
|
|
1277
|
+
assert (data_dir / "cargo" / "config.toml").exists()
|
|
1278
|
+
|
|
1279
|
+
|
|
1280
|
+
def test_setup_openssl_windows(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1281
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "win32", raising=False)
|
|
1282
|
+
relenv.runtime.setup_openssl()
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
def test_setup_openssl_without_binary(
|
|
1286
|
+
tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
|
|
1287
|
+
) -> None:
|
|
1288
|
+
monkeypatch.setattr(relenv.runtime.sys, "RELENV", tmp_path, raising=False)
|
|
1289
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux")
|
|
1290
|
+
monkeypatch.setattr(relenv.runtime.shutil, "which", lambda _: None)
|
|
1291
|
+
|
|
1292
|
+
modules_dirs: list[str] = []
|
|
1293
|
+
monkeypatch.setattr(relenv.runtime, "set_openssl_modules_dir", modules_dirs.append)
|
|
1294
|
+
providers: list[str] = []
|
|
1295
|
+
|
|
1296
|
+
def fail_provider(name: str) -> int:
|
|
1297
|
+
providers.append(name)
|
|
1298
|
+
return 0
|
|
1299
|
+
|
|
1300
|
+
monkeypatch.setattr(relenv.runtime, "load_openssl_provider", fail_provider)
|
|
1301
|
+
|
|
1302
|
+
monkeypatch.delenv("OPENSSL_MODULES", raising=False)
|
|
1303
|
+
monkeypatch.delenv("SSL_CERT_DIR", raising=False)
|
|
1304
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
1305
|
+
|
|
1306
|
+
relenv.runtime.setup_openssl()
|
|
1307
|
+
assert modules_dirs[-1].endswith("ossl-modules")
|
|
1308
|
+
assert providers == ["default", "legacy"]
|
|
1309
|
+
|
|
1310
|
+
|
|
1311
|
+
def test_setup_openssl_with_system_binary(
|
|
1312
|
+
tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch
|
|
1313
|
+
) -> None:
|
|
1314
|
+
monkeypatch.setattr(relenv.runtime.sys, "RELENV", tmp_path, raising=False)
|
|
1315
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux")
|
|
1316
|
+
monkeypatch.setattr(relenv.runtime.shutil, "which", lambda _: "/usr/bin/openssl")
|
|
1317
|
+
|
|
1318
|
+
module_calls: list[str] = []
|
|
1319
|
+
monkeypatch.setattr(relenv.runtime, "set_openssl_modules_dir", module_calls.append)
|
|
1320
|
+
|
|
1321
|
+
providers: list[str] = []
|
|
1322
|
+
monkeypatch.setattr(
|
|
1323
|
+
relenv.runtime,
|
|
1324
|
+
"load_openssl_provider",
|
|
1325
|
+
lambda name: providers.append(name) or 1,
|
|
1326
|
+
)
|
|
1327
|
+
|
|
1328
|
+
def fake_run(args: list[str], **kwargs: object) -> SimpleNamespace:
|
|
1329
|
+
if args[:2] == ["/usr/bin/openssl", "version"]:
|
|
1330
|
+
if "-m" in args:
|
|
1331
|
+
return SimpleNamespace(
|
|
1332
|
+
returncode=0, stdout='MODULESDIR: "/usr/lib/ssl"'
|
|
1333
|
+
)
|
|
1334
|
+
if "-d" in args:
|
|
1335
|
+
return SimpleNamespace(returncode=0, stdout='OPENSSLDIR: "/etc/ssl"')
|
|
1336
|
+
return SimpleNamespace(returncode=1, stdout="", stderr="error")
|
|
1337
|
+
|
|
1338
|
+
monkeypatch.setattr(relenv.runtime.subprocess, "run", fake_run)
|
|
1339
|
+
|
|
1340
|
+
certs_dir = pathlib.Path("/etc/ssl/certs")
|
|
1341
|
+
monkeypatch.setattr(
|
|
1342
|
+
pathlib.Path,
|
|
1343
|
+
"exists",
|
|
1344
|
+
lambda self: str(self)
|
|
1345
|
+
in (str(certs_dir), str(tmp_path / "lib" / "libcrypto.so")),
|
|
1346
|
+
)
|
|
1347
|
+
|
|
1348
|
+
monkeypatch.delenv("OPENSSL_MODULES", raising=False)
|
|
1349
|
+
monkeypatch.delenv("SSL_CERT_DIR", raising=False)
|
|
1350
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
1351
|
+
|
|
1352
|
+
relenv.runtime.setup_openssl()
|
|
1353
|
+
|
|
1354
|
+
assert module_calls[0] == "/usr/lib/ssl"
|
|
1355
|
+
assert module_calls[-1].endswith("ossl-modules")
|
|
1356
|
+
assert {"default", "legacy"} <= set(providers)
|
|
1357
|
+
assert os.environ["SSL_CERT_DIR"] == str(certs_dir)
|
|
1358
|
+
monkeypatch.delenv("SSL_CERT_DIR", raising=False)
|
|
1359
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
1360
|
+
|
|
1361
|
+
|
|
1362
|
+
def test_relenv_importer_loading(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1363
|
+
loaded: list[str] = []
|
|
1364
|
+
|
|
1365
|
+
def wrapper(name: str) -> ModuleType:
|
|
1366
|
+
mod = ModuleType(name)
|
|
1367
|
+
mod.loaded = True # type: ignore[attr-defined]
|
|
1368
|
+
loaded.append(name)
|
|
1369
|
+
return mod
|
|
1370
|
+
|
|
1371
|
+
wrapper_obj = relenv.runtime.Wrapper("pkg.sub", wrapper, matcher="startswith")
|
|
1372
|
+
importer = relenv.runtime.RelenvImporter([wrapper_obj])
|
|
1373
|
+
assert importer.find_module("pkg.sub.module") is importer
|
|
1374
|
+
wrapper_obj.loading = False
|
|
1375
|
+
spec = importer.find_spec("pkg.sub.module")
|
|
1376
|
+
assert spec is not None
|
|
1377
|
+
module = importer.load_module("pkg.sub.module")
|
|
1378
|
+
assert getattr(module, "loaded", False)
|
|
1379
|
+
assert loaded == ["pkg.sub.module"]
|
|
1380
|
+
importer.create_module(spec)
|
|
1381
|
+
importer.exec_module(module)
|
|
1382
|
+
|
|
1383
|
+
|
|
1384
|
+
def test_relenv_importer_defaults() -> None:
|
|
1385
|
+
importer = relenv.runtime.RelenvImporter()
|
|
1386
|
+
assert importer.wrappers == set()
|
|
1387
|
+
assert importer._loads == {}
|
|
1388
|
+
|
|
1389
|
+
|
|
1390
|
+
def test_install_cargo_config_toolchain_missing(
|
|
1391
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
1392
|
+
) -> None:
|
|
1393
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1394
|
+
stub_common = SimpleNamespace(
|
|
1395
|
+
DATA_DIR=pathlib.Path("/data"),
|
|
1396
|
+
work_dirs=lambda: SimpleNamespace(data=pathlib.Path("/data")),
|
|
1397
|
+
get_triplet=lambda: "trip",
|
|
1398
|
+
get_toolchain=lambda: None,
|
|
1399
|
+
)
|
|
1400
|
+
monkeypatch.setattr(relenv.runtime, "common", lambda: stub_common)
|
|
1401
|
+
relenv.runtime.install_cargo_config()
|
|
1402
|
+
|
|
1403
|
+
|
|
1404
|
+
def test_bootstrap(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1405
|
+
calls: list[str] = []
|
|
1406
|
+
monkeypatch.setattr(
|
|
1407
|
+
relenv.runtime, "relenv_root", lambda: pathlib.Path("/relbootstrap")
|
|
1408
|
+
)
|
|
1409
|
+
monkeypatch.setattr(relenv.runtime, "setup_openssl", lambda: calls.append("ssl"))
|
|
1410
|
+
monkeypatch.setattr(
|
|
1411
|
+
relenv.runtime.site, "execsitecustomize", lambda: None, raising=False
|
|
1412
|
+
)
|
|
1413
|
+
monkeypatch.setattr(
|
|
1414
|
+
relenv.runtime, "setup_crossroot", lambda: calls.append("cross")
|
|
1415
|
+
)
|
|
1416
|
+
monkeypatch.setattr(
|
|
1417
|
+
relenv.runtime, "install_cargo_config", lambda: calls.append("cargo")
|
|
1418
|
+
)
|
|
1419
|
+
monkeypatch.setattr(
|
|
1420
|
+
relenv.runtime.warnings, "filterwarnings", lambda *args, **kwargs: None
|
|
1421
|
+
)
|
|
1422
|
+
original_meta = list(relenv.runtime.sys.meta_path)
|
|
1423
|
+
relenv.runtime.bootstrap()
|
|
1424
|
+
assert relenv.runtime.sys.RELENV == pathlib.Path("/relbootstrap")
|
|
1425
|
+
assert calls == ["ssl", "cross", "cargo"]
|
|
1426
|
+
assert relenv.runtime.sys.meta_path[0] is relenv.runtime.importer
|
|
1427
|
+
relenv.runtime.sys.meta_path = original_meta
|
|
1428
|
+
|
|
1429
|
+
|
|
1430
|
+
def test_common_path_import_invoked(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1431
|
+
monkeypatch.delattr(relenv.runtime.common, "common", raising=False)
|
|
1432
|
+
sentinel = ModuleType("cached.common")
|
|
1433
|
+
monkeypatch.setattr(relenv.runtime, "path_import", lambda name, path: sentinel)
|
|
1434
|
+
result = relenv.runtime.common()
|
|
1435
|
+
assert result is sentinel
|
|
1436
|
+
|
|
1437
|
+
|
|
1438
|
+
def test_relocate_path_import_invoked(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1439
|
+
monkeypatch.delattr(relenv.runtime.relocate, "relocate", raising=False)
|
|
1440
|
+
sentinel = ModuleType("cached.relocate")
|
|
1441
|
+
monkeypatch.setattr(relenv.runtime, "path_import", lambda name, path: sentinel)
|
|
1442
|
+
result = relenv.runtime.relocate()
|
|
1443
|
+
assert result is sentinel
|
|
1444
|
+
|
|
1445
|
+
|
|
1446
|
+
def test_buildenv_path_import_invoked(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1447
|
+
monkeypatch.delattr(relenv.runtime.buildenv, "builenv", raising=False)
|
|
1448
|
+
monkeypatch.delattr(relenv.runtime.buildenv, "buildenv", raising=False)
|
|
1449
|
+
sentinel = ModuleType("cached.buildenv")
|
|
1450
|
+
monkeypatch.setattr(relenv.runtime, "path_import", lambda name, path: sentinel)
|
|
1451
|
+
result = relenv.runtime.buildenv()
|
|
1452
|
+
assert result is sentinel
|
|
1453
|
+
|
|
1454
|
+
|
|
1455
|
+
def test_common_cached(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1456
|
+
count = {"calls": 0}
|
|
1457
|
+
|
|
1458
|
+
def loader(name: str, path: str) -> ModuleType:
|
|
1459
|
+
count["calls"] += 1
|
|
1460
|
+
return ModuleType(name)
|
|
1461
|
+
|
|
1462
|
+
monkeypatch.setattr(relenv.runtime, "path_import", loader)
|
|
1463
|
+
module1 = relenv.runtime.common()
|
|
1464
|
+
module2 = relenv.runtime.common()
|
|
1465
|
+
assert module1 is module2
|
|
1466
|
+
assert count["calls"] == 0
|
|
1467
|
+
|
|
1468
|
+
|
|
1469
|
+
def test_relocate_cached(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1470
|
+
module = ModuleType("relenv.relocate.cached")
|
|
1471
|
+
monkeypatch.setattr(relenv.runtime, "_RELOCATE", module, raising=False)
|
|
1472
|
+
result = relenv.runtime.relocate()
|
|
1473
|
+
assert result is module
|
|
1474
|
+
|
|
1475
|
+
|
|
1476
|
+
def test_buildenv_cached(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1477
|
+
module = ModuleType("relenv.buildenv.cached")
|
|
1478
|
+
monkeypatch.setattr(relenv.runtime, "_BUILDENV", module, raising=False)
|
|
1479
|
+
result = relenv.runtime.buildenv()
|
|
1480
|
+
assert result is module
|
|
1481
|
+
|
|
1482
|
+
|
|
1483
|
+
def test_build_shebang_target(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1484
|
+
relenv.runtime.TARGET.TARGET = True
|
|
1485
|
+
relenv.runtime.TARGET.PATH = "/target"
|
|
1486
|
+
monkeypatch.setattr(
|
|
1487
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1488
|
+
)
|
|
1489
|
+
monkeypatch.setattr(
|
|
1490
|
+
relenv.runtime,
|
|
1491
|
+
"common",
|
|
1492
|
+
lambda: SimpleNamespace(
|
|
1493
|
+
relative_interpreter=lambda *args: pathlib.Path("bin/python"),
|
|
1494
|
+
format_shebang=lambda path: f"#!{path}",
|
|
1495
|
+
),
|
|
1496
|
+
)
|
|
1497
|
+
|
|
1498
|
+
def original(self: object) -> bytes: # type: ignore[override]
|
|
1499
|
+
return b""
|
|
1500
|
+
|
|
1501
|
+
result = relenv.runtime._build_shebang(original)(
|
|
1502
|
+
SimpleNamespace(target_dir="/tmp/scripts")
|
|
1503
|
+
)
|
|
1504
|
+
shebang = result.decode().strip()
|
|
1505
|
+
assert shebang.startswith("#!")
|
|
1506
|
+
path_part = shebang[2:]
|
|
1507
|
+
expected_suffix = os.fspath(pathlib.Path("bin") / "python")
|
|
1508
|
+
normalized = path_part.replace("\\", "/")
|
|
1509
|
+
assert normalized.endswith(expected_suffix.replace("\\", "/"))
|
|
1510
|
+
relenv.runtime.TARGET.TARGET = False
|
|
1511
|
+
relenv.runtime.TARGET.PATH = None
|
|
1512
|
+
|
|
1513
|
+
|
|
1514
|
+
def test_build_shebang_linux(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1515
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1516
|
+
monkeypatch.setattr(
|
|
1517
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1518
|
+
)
|
|
1519
|
+
|
|
1520
|
+
class StubCommon:
|
|
1521
|
+
@staticmethod
|
|
1522
|
+
def relative_interpreter(_relenv, _scripts, _exec):
|
|
1523
|
+
return pathlib.Path("bin/python")
|
|
1524
|
+
|
|
1525
|
+
@staticmethod
|
|
1526
|
+
def format_shebang(path: pathlib.Path) -> str:
|
|
1527
|
+
return f"#!{path}"
|
|
1528
|
+
|
|
1529
|
+
monkeypatch.setattr(relenv.runtime, "common", lambda: StubCommon())
|
|
1530
|
+
|
|
1531
|
+
def original(self: object) -> bytes: # type: ignore[override]
|
|
1532
|
+
return b""
|
|
1533
|
+
|
|
1534
|
+
result = relenv.runtime._build_shebang(original)(
|
|
1535
|
+
SimpleNamespace(target_dir="/tmp/dir")
|
|
1536
|
+
)
|
|
1537
|
+
shebang = result.decode().strip()
|
|
1538
|
+
assert shebang.startswith("#!")
|
|
1539
|
+
path_part = shebang[2:]
|
|
1540
|
+
# Use PurePosixPath since we're testing Linux behavior
|
|
1541
|
+
expected = os.fspath(pathlib.PurePosixPath("/") / "bin" / "python")
|
|
1542
|
+
assert path_part == expected
|
|
1543
|
+
|
|
1544
|
+
|
|
1545
|
+
def test_setup_openssl_version_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1546
|
+
monkeypatch.setattr(
|
|
1547
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1548
|
+
)
|
|
1549
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1550
|
+
monkeypatch.setattr(relenv.runtime.shutil, "which", lambda _: "/usr/bin/openssl")
|
|
1551
|
+
monkeypatch.setattr(relenv.runtime, "set_openssl_modules_dir", lambda path: None)
|
|
1552
|
+
monkeypatch.setattr(relenv.runtime, "load_openssl_provider", lambda name: 1)
|
|
1553
|
+
|
|
1554
|
+
def fake_run(args: list[str], **kwargs: object) -> SimpleNamespace:
|
|
1555
|
+
return SimpleNamespace(returncode=1, stdout="", stderr="err")
|
|
1556
|
+
|
|
1557
|
+
monkeypatch.setattr(relenv.runtime.subprocess, "run", fake_run)
|
|
1558
|
+
relenv.runtime.setup_openssl()
|
|
1559
|
+
|
|
1560
|
+
|
|
1561
|
+
def test_setup_openssl_parse_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1562
|
+
monkeypatch.setattr(
|
|
1563
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1564
|
+
)
|
|
1565
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1566
|
+
monkeypatch.setattr(relenv.runtime.shutil, "which", lambda _: "/usr/bin/openssl")
|
|
1567
|
+
monkeypatch.setattr(relenv.runtime, "load_openssl_provider", lambda name: 1)
|
|
1568
|
+
monkeypatch.setattr(relenv.runtime, "set_openssl_modules_dir", lambda path: None)
|
|
1569
|
+
monkeypatch.delenv("OPENSSL_MODULES", raising=False)
|
|
1570
|
+
monkeypatch.delenv("SSL_CERT_DIR", raising=False)
|
|
1571
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
1572
|
+
|
|
1573
|
+
def fake_run(args: list[str], **kwargs: object) -> SimpleNamespace:
|
|
1574
|
+
if "-m" in args:
|
|
1575
|
+
return SimpleNamespace(returncode=0, stdout="invalid", stderr="")
|
|
1576
|
+
return SimpleNamespace(returncode=0, stdout='OPENSSLDIR: "/etc/ssl"', stderr="")
|
|
1577
|
+
|
|
1578
|
+
monkeypatch.setattr(relenv.runtime.subprocess, "run", fake_run)
|
|
1579
|
+
relenv.runtime.setup_openssl()
|
|
1580
|
+
|
|
1581
|
+
|
|
1582
|
+
def test_setup_openssl_cert_dir_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1583
|
+
monkeypatch.setattr(
|
|
1584
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1585
|
+
)
|
|
1586
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1587
|
+
monkeypatch.setattr(relenv.runtime.shutil, "which", lambda _: "/usr/bin/openssl")
|
|
1588
|
+
monkeypatch.setattr(relenv.runtime, "load_openssl_provider", lambda name: 1)
|
|
1589
|
+
monkeypatch.setattr(relenv.runtime, "set_openssl_modules_dir", lambda path: None)
|
|
1590
|
+
|
|
1591
|
+
def fake_run(args: list[str], **kwargs: object) -> SimpleNamespace:
|
|
1592
|
+
if "-m" in args:
|
|
1593
|
+
return SimpleNamespace(
|
|
1594
|
+
returncode=0, stdout='MODULESDIR: "/usr/lib"', stderr=""
|
|
1595
|
+
)
|
|
1596
|
+
return SimpleNamespace(returncode=1, stdout="", stderr="error")
|
|
1597
|
+
|
|
1598
|
+
monkeypatch.setattr(relenv.runtime.subprocess, "run", fake_run)
|
|
1599
|
+
monkeypatch.delenv("SSL_CERT_DIR", raising=False)
|
|
1600
|
+
relenv.runtime.setup_openssl()
|
|
1601
|
+
|
|
1602
|
+
|
|
1603
|
+
def test_setup_openssl_cert_dir_parse_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1604
|
+
monkeypatch.setattr(
|
|
1605
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1606
|
+
)
|
|
1607
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1608
|
+
monkeypatch.setattr(relenv.runtime.shutil, "which", lambda _: "/usr/bin/openssl")
|
|
1609
|
+
monkeypatch.setattr(relenv.runtime, "load_openssl_provider", lambda name: 1)
|
|
1610
|
+
monkeypatch.setattr(relenv.runtime, "set_openssl_modules_dir", lambda path: None)
|
|
1611
|
+
monkeypatch.delenv("OPENSSL_MODULES", raising=False)
|
|
1612
|
+
monkeypatch.delenv("SSL_CERT_DIR", raising=False)
|
|
1613
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
1614
|
+
|
|
1615
|
+
def fake_run(args: list[str], **kwargs: object) -> SimpleNamespace:
|
|
1616
|
+
if "-m" in args:
|
|
1617
|
+
return SimpleNamespace(
|
|
1618
|
+
returncode=0, stdout='MODULESDIR: "/usr/lib"', stderr=""
|
|
1619
|
+
)
|
|
1620
|
+
return SimpleNamespace(returncode=0, stdout="invalid", stderr="")
|
|
1621
|
+
|
|
1622
|
+
monkeypatch.setattr(relenv.runtime.subprocess, "run", fake_run)
|
|
1623
|
+
relenv.runtime.setup_openssl()
|
|
1624
|
+
|
|
1625
|
+
|
|
1626
|
+
def test_setup_openssl_cert_file(
|
|
1627
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
|
|
1628
|
+
) -> None:
|
|
1629
|
+
monkeypatch.setattr(
|
|
1630
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1631
|
+
)
|
|
1632
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1633
|
+
monkeypatch.setattr(relenv.runtime.shutil, "which", lambda _: "/usr/bin/openssl")
|
|
1634
|
+
cert_dir = tmp_path / "etc" / "ssl"
|
|
1635
|
+
cert_dir.mkdir(parents=True)
|
|
1636
|
+
cert_file = cert_dir / "cert.pem"
|
|
1637
|
+
cert_file.write_text("cert")
|
|
1638
|
+
|
|
1639
|
+
def fake_run(args: list[str], **kwargs: object) -> SimpleNamespace:
|
|
1640
|
+
if "-m" in args:
|
|
1641
|
+
return SimpleNamespace(
|
|
1642
|
+
returncode=0, stdout='MODULESDIR: "{}"'.format(cert_dir), stderr=""
|
|
1643
|
+
)
|
|
1644
|
+
return SimpleNamespace(
|
|
1645
|
+
returncode=0, stdout='OPENSSLDIR: "{}"'.format(cert_dir), stderr=""
|
|
1646
|
+
)
|
|
1647
|
+
|
|
1648
|
+
monkeypatch.setattr(relenv.runtime.subprocess, "run", fake_run)
|
|
1649
|
+
monkeypatch.setenv("OPENSSL_MODULES", "")
|
|
1650
|
+
monkeypatch.delenv("SSL_CERT_DIR", raising=False)
|
|
1651
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
1652
|
+
monkeypatch.setattr(relenv.runtime, "set_openssl_modules_dir", lambda path: None)
|
|
1653
|
+
monkeypatch.setattr(relenv.runtime, "load_openssl_provider", lambda name: 1)
|
|
1654
|
+
relenv.runtime.setup_openssl()
|
|
1655
|
+
assert os.environ["SSL_CERT_FILE"] == os.fspath(cert_file)
|
|
1656
|
+
monkeypatch.delenv("OPENSSL_MODULES", raising=False)
|
|
1657
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
1658
|
+
|
|
1659
|
+
|
|
1660
|
+
def test_set_openssl_modules_dir(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1661
|
+
called = {}
|
|
1662
|
+
|
|
1663
|
+
class FakeLib:
|
|
1664
|
+
def __init__(self) -> None:
|
|
1665
|
+
self.OSSL_PROVIDER_set_default_search_path = (
|
|
1666
|
+
lambda ctx, path: called.update({"path": path}) or 1
|
|
1667
|
+
)
|
|
1668
|
+
|
|
1669
|
+
monkeypatch.setattr(relenv.runtime.ctypes, "CDLL", lambda path: FakeLib())
|
|
1670
|
+
monkeypatch.setattr(
|
|
1671
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1672
|
+
)
|
|
1673
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "darwin", raising=False)
|
|
1674
|
+
relenv.runtime.set_openssl_modules_dir("/mods")
|
|
1675
|
+
assert called["path"] == b"/mods"
|
|
1676
|
+
|
|
1677
|
+
|
|
1678
|
+
def test_load_openssl_provider(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1679
|
+
class FakeLib:
|
|
1680
|
+
def __init__(self) -> None:
|
|
1681
|
+
self.OSSL_PROVIDER_load = lambda ctx, name: 123
|
|
1682
|
+
|
|
1683
|
+
monkeypatch.setattr(relenv.runtime.ctypes, "CDLL", lambda path: FakeLib())
|
|
1684
|
+
monkeypatch.setattr(
|
|
1685
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1686
|
+
)
|
|
1687
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "darwin", raising=False)
|
|
1688
|
+
assert relenv.runtime.load_openssl_provider("default") == 123
|
|
1689
|
+
|
|
1690
|
+
|
|
1691
|
+
def test_setup_crossroot(
|
|
1692
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
|
|
1693
|
+
) -> None:
|
|
1694
|
+
monkeypatch.setenv("RELENV_CROSS", str(tmp_path))
|
|
1695
|
+
original_path = sys.path[:]
|
|
1696
|
+
try:
|
|
1697
|
+
relenv.runtime.setup_crossroot()
|
|
1698
|
+
assert sys.prefix == str(tmp_path.resolve())
|
|
1699
|
+
assert str(tmp_path / "lib") in sys.path[0]
|
|
1700
|
+
finally:
|
|
1701
|
+
sys.path = original_path
|
|
1702
|
+
monkeypatch.delenv("RELENV_CROSS", raising=False)
|
|
1703
|
+
|
|
1704
|
+
|
|
1705
|
+
def test_setup_openssl_provider_failure(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1706
|
+
monkeypatch.setattr(
|
|
1707
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1708
|
+
)
|
|
1709
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1710
|
+
monkeypatch.setattr(relenv.runtime.shutil, "which", lambda _: "/usr/bin/openssl")
|
|
1711
|
+
order: list[str] = []
|
|
1712
|
+
monkeypatch.setattr(
|
|
1713
|
+
relenv.runtime, "set_openssl_modules_dir", lambda path: order.append(path)
|
|
1714
|
+
)
|
|
1715
|
+
providers: list[str] = []
|
|
1716
|
+
monkeypatch.setattr(
|
|
1717
|
+
relenv.runtime,
|
|
1718
|
+
"load_openssl_provider",
|
|
1719
|
+
lambda name: providers.append(name) or 0,
|
|
1720
|
+
)
|
|
1721
|
+
monkeypatch.setattr(
|
|
1722
|
+
relenv.runtime.subprocess,
|
|
1723
|
+
"run",
|
|
1724
|
+
lambda args, **kwargs: SimpleNamespace(
|
|
1725
|
+
returncode=0,
|
|
1726
|
+
stdout='MODULESDIR: "/usr/lib"'
|
|
1727
|
+
if "-m" in args
|
|
1728
|
+
else 'OPENSSLDIR: "/etc/ssl"',
|
|
1729
|
+
stderr="",
|
|
1730
|
+
),
|
|
1731
|
+
)
|
|
1732
|
+
monkeypatch.delenv("OPENSSL_MODULES", raising=False)
|
|
1733
|
+
monkeypatch.delenv("SSL_CERT_DIR", raising=False)
|
|
1734
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
1735
|
+
relenv.runtime.setup_openssl()
|
|
1736
|
+
assert order[0] == "/usr/lib"
|
|
1737
|
+
assert order[-1].endswith("ossl-modules")
|
|
1738
|
+
assert providers == ["fips", "default", "legacy"]
|
|
1739
|
+
monkeypatch.delenv("SSL_CERT_DIR", raising=False)
|
|
1740
|
+
monkeypatch.delenv("SSL_CERT_FILE", raising=False)
|
|
1741
|
+
|
|
1742
|
+
|
|
1743
|
+
def test_wrapsitecustomize_import_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1744
|
+
def original() -> None:
|
|
1745
|
+
pass
|
|
1746
|
+
|
|
1747
|
+
class CustomError(ImportError):
|
|
1748
|
+
def __init__(self) -> None:
|
|
1749
|
+
super().__init__("other")
|
|
1750
|
+
self.name = "other"
|
|
1751
|
+
|
|
1752
|
+
import builtins
|
|
1753
|
+
|
|
1754
|
+
orig_import = builtins.__import__
|
|
1755
|
+
|
|
1756
|
+
def fake_import(
|
|
1757
|
+
name: str,
|
|
1758
|
+
globals: Optional[dict] = None,
|
|
1759
|
+
locals: Optional[dict] = None,
|
|
1760
|
+
fromlist=(),
|
|
1761
|
+
level: int = 0,
|
|
1762
|
+
):
|
|
1763
|
+
if name == "sitecustomize":
|
|
1764
|
+
raise CustomError()
|
|
1765
|
+
return orig_import(name, globals, locals, fromlist, level)
|
|
1766
|
+
|
|
1767
|
+
monkeypatch.setattr(builtins, "__import__", fake_import)
|
|
1768
|
+
monkeypatch.setattr(
|
|
1769
|
+
relenv.runtime,
|
|
1770
|
+
"common",
|
|
1771
|
+
lambda: SimpleNamespace(sanitize_sys_path=lambda paths: paths),
|
|
1772
|
+
)
|
|
1773
|
+
wrapped = relenv.runtime.wrapsitecustomize(original)
|
|
1774
|
+
with pytest.raises(ImportError):
|
|
1775
|
+
wrapped()
|
|
1776
|
+
|
|
1777
|
+
|
|
1778
|
+
def test_wrapsitecustomize_skip(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1779
|
+
def original() -> None:
|
|
1780
|
+
pass
|
|
1781
|
+
|
|
1782
|
+
fake_module = ModuleType("sitecustomize")
|
|
1783
|
+
fake_module.__file__ = "/tmp/pip-build-env/sitecustomize.py"
|
|
1784
|
+
monkeypatch.setitem(sys.modules, "sitecustomize", fake_module)
|
|
1785
|
+
monkeypatch.setattr(
|
|
1786
|
+
relenv.runtime,
|
|
1787
|
+
"common",
|
|
1788
|
+
lambda: SimpleNamespace(sanitize_sys_path=lambda paths: paths),
|
|
1789
|
+
)
|
|
1790
|
+
wrapped = relenv.runtime.wrapsitecustomize(original)
|
|
1791
|
+
monkeypatch.setattr(relenv.runtime, "debug", lambda msg: None)
|
|
1792
|
+
wrapped()
|
|
1793
|
+
|
|
1794
|
+
|
|
1795
|
+
def test_set_openssl_modules_dir_linux(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1796
|
+
called = {}
|
|
1797
|
+
|
|
1798
|
+
class FakeLib:
|
|
1799
|
+
def __init__(self) -> None:
|
|
1800
|
+
self.OSSL_PROVIDER_set_default_search_path = (
|
|
1801
|
+
lambda ctx, path: called.update({"path": path}) or 1
|
|
1802
|
+
)
|
|
1803
|
+
|
|
1804
|
+
monkeypatch.setattr(relenv.runtime.ctypes, "CDLL", lambda path: FakeLib())
|
|
1805
|
+
monkeypatch.setattr(
|
|
1806
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1807
|
+
)
|
|
1808
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1809
|
+
relenv.runtime.set_openssl_modules_dir("/mods")
|
|
1810
|
+
assert called["path"] == b"/mods"
|
|
1811
|
+
|
|
1812
|
+
|
|
1813
|
+
def test_load_openssl_provider_linux(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1814
|
+
class FakeLib:
|
|
1815
|
+
def __init__(self) -> None:
|
|
1816
|
+
self.OSSL_PROVIDER_load = lambda ctx, name: 456
|
|
1817
|
+
|
|
1818
|
+
monkeypatch.setattr(relenv.runtime.ctypes, "CDLL", lambda path: FakeLib())
|
|
1819
|
+
monkeypatch.setattr(
|
|
1820
|
+
relenv.runtime.sys, "RELENV", pathlib.Path("/rel"), raising=False
|
|
1821
|
+
)
|
|
1822
|
+
monkeypatch.setattr(relenv.runtime.sys, "platform", "linux", raising=False)
|
|
1823
|
+
assert relenv.runtime.load_openssl_provider("default") == 456
|