|
1 | 1 | """Build Environment used for isolation during sdist building
|
2 | 2 | """
|
3 | 3 |
|
| 4 | +import contextlib |
4 | 5 | import logging
|
5 | 6 | import os
|
| 7 | +import pathlib |
6 | 8 | import sys
|
7 | 9 | import textwrap
|
| 10 | +import zipfile |
8 | 11 | from collections import OrderedDict
|
9 | 12 | from sysconfig import get_paths
|
10 | 13 | from types import TracebackType
|
11 |
| -from typing import TYPE_CHECKING, Iterable, List, Optional, Set, Tuple, Type |
| 14 | +from typing import TYPE_CHECKING, Iterable, Iterator, List, Optional, Set, Tuple, Type |
12 | 15 |
|
13 | 16 | from pip._vendor.pkg_resources import Requirement, VersionConflict, WorkingSet
|
14 | 17 |
|
@@ -37,6 +40,61 @@ def __init__(self, path):
|
37 | 40 | self.lib_dirs = get_prefixed_libs(path)
|
38 | 41 |
|
39 | 42 |
|
| 43 | +@contextlib.contextmanager |
| 44 | +def _create_standalone_pip() -> Iterator[str]: |
| 45 | + """Create a zip file containing specified pip installation.""" |
| 46 | + source = pathlib.Path(pip_location).resolve().parent |
| 47 | + with TempDirectory() as tmp_dir: |
| 48 | + pip_zip = os.path.join(tmp_dir.path, "pip.zip") |
| 49 | + with zipfile.ZipFile(pip_zip, "w") as zf: |
| 50 | + for child in source.rglob("*"): |
| 51 | + arcname = child.relative_to(source.parent) |
| 52 | + zf.write(child, arcname.as_posix()) |
| 53 | + yield os.path.join(pip_zip, "pip") |
| 54 | + |
| 55 | + |
| 56 | +def _install_requirements( |
| 57 | + standalone_pip: str, |
| 58 | + finder: "PackageFinder", |
| 59 | + requirements: Iterable[str], |
| 60 | + prefix: _Prefix, |
| 61 | + message: str, |
| 62 | +) -> None: |
| 63 | + args = [ |
| 64 | + sys.executable, standalone_pip, 'install', |
| 65 | + '--ignore-installed', '--no-user', '--prefix', prefix.path, |
| 66 | + '--no-warn-script-location', |
| 67 | + ] # type: List[str] |
| 68 | + if logger.getEffectiveLevel() <= logging.DEBUG: |
| 69 | + args.append('-v') |
| 70 | + for format_control in ('no_binary', 'only_binary'): |
| 71 | + formats = getattr(finder.format_control, format_control) |
| 72 | + args += [ |
| 73 | + '--' + format_control.replace('_', '-'), |
| 74 | + ','.join(sorted(formats or {':none:'})) |
| 75 | + ] |
| 76 | + index_urls = finder.index_urls |
| 77 | + if index_urls: |
| 78 | + args.extend(['-i', index_urls[0]]) |
| 79 | + for extra_index in index_urls[1:]: |
| 80 | + args.extend(['--extra-index-url', extra_index]) |
| 81 | + else: |
| 82 | + args.append('--no-index') |
| 83 | + for link in finder.find_links: |
| 84 | + args.extend(['--find-links', link]) |
| 85 | + |
| 86 | + for host in finder.trusted_hosts: |
| 87 | + args.extend(['--trusted-host', host]) |
| 88 | + if finder.allow_all_prereleases: |
| 89 | + args.append('--pre') |
| 90 | + if finder.prefer_binary: |
| 91 | + args.append('--prefer-binary') |
| 92 | + args.append('--') |
| 93 | + args.extend(requirements) |
| 94 | + with open_spinner(message) as spinner: |
| 95 | + call_subprocess(args, spinner=spinner) |
| 96 | + |
| 97 | + |
40 | 98 | class BuildEnvironment:
|
41 | 99 | """Creates and manages an isolated environment to install build deps
|
42 | 100 | """
|
@@ -160,38 +218,14 @@ def install_requirements(
|
160 | 218 | prefix.setup = True
|
161 | 219 | if not requirements:
|
162 | 220 | return
|
163 |
| - args = [ |
164 |
| - sys.executable, os.path.dirname(pip_location), 'install', |
165 |
| - '--ignore-installed', '--no-user', '--prefix', prefix.path, |
166 |
| - '--no-warn-script-location', |
167 |
| - ] # type: List[str] |
168 |
| - if logger.getEffectiveLevel() <= logging.DEBUG: |
169 |
| - args.append('-v') |
170 |
| - for format_control in ('no_binary', 'only_binary'): |
171 |
| - formats = getattr(finder.format_control, format_control) |
172 |
| - args.extend(('--' + format_control.replace('_', '-'), |
173 |
| - ','.join(sorted(formats or {':none:'})))) |
174 |
| - |
175 |
| - index_urls = finder.index_urls |
176 |
| - if index_urls: |
177 |
| - args.extend(['-i', index_urls[0]]) |
178 |
| - for extra_index in index_urls[1:]: |
179 |
| - args.extend(['--extra-index-url', extra_index]) |
180 |
| - else: |
181 |
| - args.append('--no-index') |
182 |
| - for link in finder.find_links: |
183 |
| - args.extend(['--find-links', link]) |
184 |
| - |
185 |
| - for host in finder.trusted_hosts: |
186 |
| - args.extend(['--trusted-host', host]) |
187 |
| - if finder.allow_all_prereleases: |
188 |
| - args.append('--pre') |
189 |
| - if finder.prefer_binary: |
190 |
| - args.append('--prefer-binary') |
191 |
| - args.append('--') |
192 |
| - args.extend(requirements) |
193 |
| - with open_spinner(message) as spinner: |
194 |
| - call_subprocess(args, spinner=spinner) |
| 221 | + with _create_standalone_pip() as standalone_pip: |
| 222 | + _install_requirements( |
| 223 | + standalone_pip, |
| 224 | + finder, |
| 225 | + requirements, |
| 226 | + prefix, |
| 227 | + message, |
| 228 | + ) |
195 | 229 |
|
196 | 230 |
|
197 | 231 | class NoOpBuildEnvironment(BuildEnvironment):
|
|
0 commit comments