Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
keewenaw
GitHub Repository: keewenaw/ethereum-wallet-cracker
Path: blob/main/test/lib/python3.9/site-packages/pip/_internal/build_env.py
4799 views
1
"""Build Environment used for isolation during sdist building
2
"""
3
4
import contextlib
5
import logging
6
import os
7
import pathlib
8
import sys
9
import textwrap
10
import zipfile
11
from collections import OrderedDict
12
from sysconfig import get_paths
13
from types import TracebackType
14
from typing import TYPE_CHECKING, Generator, Iterable, List, Optional, Set, Tuple, Type
15
16
from pip._vendor.certifi import where
17
from pip._vendor.packaging.requirements import Requirement
18
from pip._vendor.packaging.version import Version
19
20
from pip import __file__ as pip_location
21
from pip._internal.cli.spinners import open_spinner
22
from pip._internal.locations import get_platlib, get_prefixed_libs, get_purelib
23
from pip._internal.metadata import get_default_environment, get_environment
24
from pip._internal.utils.subprocess import call_subprocess
25
from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
26
27
if TYPE_CHECKING:
28
from pip._internal.index.package_finder import PackageFinder
29
30
logger = logging.getLogger(__name__)
31
32
33
class _Prefix:
34
def __init__(self, path: str) -> None:
35
self.path = path
36
self.setup = False
37
self.bin_dir = get_paths(
38
"nt" if os.name == "nt" else "posix_prefix",
39
vars={"base": path, "platbase": path},
40
)["scripts"]
41
self.lib_dirs = get_prefixed_libs(path)
42
43
44
@contextlib.contextmanager
45
def _create_standalone_pip() -> Generator[str, None, None]:
46
"""Create a "standalone pip" zip file.
47
48
The zip file's content is identical to the currently-running pip.
49
It will be used to install requirements into the build environment.
50
"""
51
source = pathlib.Path(pip_location).resolve().parent
52
53
# Return the current instance if `source` is not a directory. We can't build
54
# a zip from this, and it likely means the instance is already standalone.
55
if not source.is_dir():
56
yield str(source)
57
return
58
59
with TempDirectory(kind="standalone-pip") as tmp_dir:
60
pip_zip = os.path.join(tmp_dir.path, "__env_pip__.zip")
61
kwargs = {}
62
if sys.version_info >= (3, 8):
63
kwargs["strict_timestamps"] = False
64
with zipfile.ZipFile(pip_zip, "w", **kwargs) as zf:
65
for child in source.rglob("*"):
66
zf.write(child, child.relative_to(source.parent).as_posix())
67
yield os.path.join(pip_zip, "pip")
68
69
70
class BuildEnvironment:
71
"""Creates and manages an isolated environment to install build deps"""
72
73
def __init__(self) -> None:
74
temp_dir = TempDirectory(kind=tempdir_kinds.BUILD_ENV, globally_managed=True)
75
76
self._prefixes = OrderedDict(
77
(name, _Prefix(os.path.join(temp_dir.path, name)))
78
for name in ("normal", "overlay")
79
)
80
81
self._bin_dirs: List[str] = []
82
self._lib_dirs: List[str] = []
83
for prefix in reversed(list(self._prefixes.values())):
84
self._bin_dirs.append(prefix.bin_dir)
85
self._lib_dirs.extend(prefix.lib_dirs)
86
87
# Customize site to:
88
# - ensure .pth files are honored
89
# - prevent access to system site packages
90
system_sites = {
91
os.path.normcase(site) for site in (get_purelib(), get_platlib())
92
}
93
self._site_dir = os.path.join(temp_dir.path, "site")
94
if not os.path.exists(self._site_dir):
95
os.mkdir(self._site_dir)
96
with open(
97
os.path.join(self._site_dir, "sitecustomize.py"), "w", encoding="utf-8"
98
) as fp:
99
fp.write(
100
textwrap.dedent(
101
"""
102
import os, site, sys
103
104
# First, drop system-sites related paths.
105
original_sys_path = sys.path[:]
106
known_paths = set()
107
for path in {system_sites!r}:
108
site.addsitedir(path, known_paths=known_paths)
109
system_paths = set(
110
os.path.normcase(path)
111
for path in sys.path[len(original_sys_path):]
112
)
113
original_sys_path = [
114
path for path in original_sys_path
115
if os.path.normcase(path) not in system_paths
116
]
117
sys.path = original_sys_path
118
119
# Second, add lib directories.
120
# ensuring .pth file are processed.
121
for path in {lib_dirs!r}:
122
assert not path in sys.path
123
site.addsitedir(path)
124
"""
125
).format(system_sites=system_sites, lib_dirs=self._lib_dirs)
126
)
127
128
def __enter__(self) -> None:
129
self._save_env = {
130
name: os.environ.get(name, None)
131
for name in ("PATH", "PYTHONNOUSERSITE", "PYTHONPATH")
132
}
133
134
path = self._bin_dirs[:]
135
old_path = self._save_env["PATH"]
136
if old_path:
137
path.extend(old_path.split(os.pathsep))
138
139
pythonpath = [self._site_dir]
140
141
os.environ.update(
142
{
143
"PATH": os.pathsep.join(path),
144
"PYTHONNOUSERSITE": "1",
145
"PYTHONPATH": os.pathsep.join(pythonpath),
146
}
147
)
148
149
def __exit__(
150
self,
151
exc_type: Optional[Type[BaseException]],
152
exc_val: Optional[BaseException],
153
exc_tb: Optional[TracebackType],
154
) -> None:
155
for varname, old_value in self._save_env.items():
156
if old_value is None:
157
os.environ.pop(varname, None)
158
else:
159
os.environ[varname] = old_value
160
161
def check_requirements(
162
self, reqs: Iterable[str]
163
) -> Tuple[Set[Tuple[str, str]], Set[str]]:
164
"""Return 2 sets:
165
- conflicting requirements: set of (installed, wanted) reqs tuples
166
- missing requirements: set of reqs
167
"""
168
missing = set()
169
conflicting = set()
170
if reqs:
171
env = (
172
get_environment(self._lib_dirs)
173
if hasattr(self, "_lib_dirs")
174
else get_default_environment()
175
)
176
for req_str in reqs:
177
req = Requirement(req_str)
178
# We're explicitly evaluating with an empty extra value, since build
179
# environments are not provided any mechanism to select specific extras.
180
if req.marker is not None and not req.marker.evaluate({"extra": ""}):
181
continue
182
dist = env.get_distribution(req.name)
183
if not dist:
184
missing.add(req_str)
185
continue
186
if isinstance(dist.version, Version):
187
installed_req_str = f"{req.name}=={dist.version}"
188
else:
189
installed_req_str = f"{req.name}==={dist.version}"
190
if not req.specifier.contains(dist.version, prereleases=True):
191
conflicting.add((installed_req_str, req_str))
192
# FIXME: Consider direct URL?
193
return conflicting, missing
194
195
def install_requirements(
196
self,
197
finder: "PackageFinder",
198
requirements: Iterable[str],
199
prefix_as_string: str,
200
*,
201
kind: str,
202
) -> None:
203
prefix = self._prefixes[prefix_as_string]
204
assert not prefix.setup
205
prefix.setup = True
206
if not requirements:
207
return
208
with contextlib.ExitStack() as ctx:
209
pip_runnable = ctx.enter_context(_create_standalone_pip())
210
self._install_requirements(
211
pip_runnable,
212
finder,
213
requirements,
214
prefix,
215
kind=kind,
216
)
217
218
@staticmethod
219
def _install_requirements(
220
pip_runnable: str,
221
finder: "PackageFinder",
222
requirements: Iterable[str],
223
prefix: _Prefix,
224
*,
225
kind: str,
226
) -> None:
227
args: List[str] = [
228
sys.executable,
229
pip_runnable,
230
"install",
231
"--ignore-installed",
232
"--no-user",
233
"--prefix",
234
prefix.path,
235
"--no-warn-script-location",
236
]
237
if logger.getEffectiveLevel() <= logging.DEBUG:
238
args.append("-v")
239
for format_control in ("no_binary", "only_binary"):
240
formats = getattr(finder.format_control, format_control)
241
args.extend(
242
(
243
"--" + format_control.replace("_", "-"),
244
",".join(sorted(formats or {":none:"})),
245
)
246
)
247
248
index_urls = finder.index_urls
249
if index_urls:
250
args.extend(["-i", index_urls[0]])
251
for extra_index in index_urls[1:]:
252
args.extend(["--extra-index-url", extra_index])
253
else:
254
args.append("--no-index")
255
for link in finder.find_links:
256
args.extend(["--find-links", link])
257
258
for host in finder.trusted_hosts:
259
args.extend(["--trusted-host", host])
260
if finder.allow_all_prereleases:
261
args.append("--pre")
262
if finder.prefer_binary:
263
args.append("--prefer-binary")
264
args.append("--")
265
args.extend(requirements)
266
extra_environ = {"_PIP_STANDALONE_CERT": where()}
267
with open_spinner(f"Installing {kind}") as spinner:
268
call_subprocess(
269
args,
270
command_desc=f"pip subprocess to install {kind}",
271
spinner=spinner,
272
extra_environ=extra_environ,
273
)
274
275
276
class NoOpBuildEnvironment(BuildEnvironment):
277
"""A no-op drop-in replacement for BuildEnvironment"""
278
279
def __init__(self) -> None:
280
pass
281
282
def __enter__(self) -> None:
283
pass
284
285
def __exit__(
286
self,
287
exc_type: Optional[Type[BaseException]],
288
exc_val: Optional[BaseException],
289
exc_tb: Optional[TracebackType],
290
) -> None:
291
pass
292
293
def cleanup(self) -> None:
294
pass
295
296
def install_requirements(
297
self,
298
finder: "PackageFinder",
299
requirements: Iterable[str],
300
prefix_as_string: str,
301
*,
302
kind: str,
303
) -> None:
304
raise NotImplementedError()
305
306