Skip to content

Commit f446856

Browse files
committed
Bump pip's src from 20.1.1 to 21.2.4
WARNING: fetchcode's vcs will not work on this commit. This is result of separating pip's code from changes made in scope of this repository. This should make it easier to track potentially replicated issues from pip when taking their vcs pkg. It also made cleaning up easier, due to some maintenance activities done in pip: - dropping Python 2 & 3.5 support pypa/pip#9189 - modernized code after above - partially done, tracked in: pypa/pip#8802 - added py3.9 support - updated vendored libraries (e.g. fixing CVE-2021-28363) multiple PRs pip._internal.vcs (and related code) changes: - Fetch resources that are missing locally: pypa/pip#8817 - Improve SVN version parser (Windows) pypa/pip#8665 - Always close stderr after subprocess completion: pypa/pip#9156 - Remove vcs export feature: pypa/pip#9713 - Remove support for git+ssh@ scheme in favour of git+ssh:// pypa/pip#9436 - Security fix in git tags parsing (CVE-2021-3572): pypa/pip#9827 - Reimplement Git version parsing: pypa/pip#10117 In next commits, most of pip's internals will be removed from fetchcode, leaving only vcs module with supporting code (like utils functions, tests (which will be added & submitted with this change)) This will allow for changes such as ability to add return codes (probably via futures) from long running downloads and other features. Switching to having own vcs module might also be a good call due to pip._internal.vcs close integration with pip's cli in vcs module (some pip code has been commented out in commit mentioned below) While generally copy-pasting code without history is bad idea, this commit follows precedence set in this repo by: 8046215 with exception that all changes to pip's code will be submitted as separate commits. It has been agreed with @pombredanne & @TG1999 that history from pip will be rebased on fetchcode by @pombredanne (thanks!). It will be done only for the files that are of concern for fetchcode to limit noise in git history. I'm leaving this commit without SoB intentionally, as this is not my work, but that of the many pip's authors: https://github.com/pypa/pip/blob/21.2.4/AUTHORS.txt License of pip: MIT (https://pypi.org/project/pip/)
1 parent ab65b2e commit f446856

File tree

312 files changed

+58271
-18370
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

312 files changed

+58271
-18370
lines changed

src/fetchcode/vcs/pip/__init__.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
1-
from fetchcode.vcs.pip._internal.utils.typing import MYPY_CHECK_RUNNING
1+
from typing import List, Optional
22

3-
if MYPY_CHECK_RUNNING:
4-
from typing import List, Optional
3+
__version__ = "21.2.4"
54

65

7-
__version__ = "20.1.1"
8-
9-
10-
def main(args=None):
11-
# type: (Optional[List[str]]) -> int
6+
def main(args: Optional[List[str]] = None) -> int:
127
"""This is an internal API only meant for use by pip's own console scripts.
138
149
For additional details, see https://github.com/pypa/pip/issues/7498.
1510
"""
16-
from fetchcode.vcs.pip._internal.utils.entrypoints import _wrapper
11+
from pip._internal.utils.entrypoints import _wrapper
1712

1813
return _wrapper(args)

src/fetchcode/vcs/pip/__main__.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
1-
from __future__ import absolute_import
2-
31
import os
42
import sys
3+
import warnings
54

65
# Remove '' and current working directory from the first entry
76
# of sys.path, if present to avoid using current directory
87
# in pip commands check, freeze, install, list and show,
98
# when invoked as python -m pip <command>
10-
if sys.path[0] in ('', os.getcwd()):
9+
if sys.path[0] in ("", os.getcwd()):
1110
sys.path.pop(0)
1211

1312
# If we are running from a wheel, add the wheel to sys.path
1413
# This allows the usage python pip-*.whl/pip install pip-*.whl
15-
if __package__ == '':
14+
if __package__ == "":
1615
# __file__ is pip-*.whl/pip/__main__.py
1716
# first dirname call strips of '/__main__.py', second strips off '/pip'
1817
# Resulting path is the name of the wheel itself
1918
# Add that to sys.path so we can import pip
2019
path = os.path.dirname(os.path.dirname(__file__))
2120
sys.path.insert(0, path)
2221

23-
from fetchcode.vcs.pip._internal.cli.main import main as _main # isort:skip # noqa
22+
if __name__ == "__main__":
23+
# Work around the error reported in #9540, pending a proper fix.
24+
# Note: It is essential the warning filter is set *before* importing
25+
# pip, as the deprecation happens at import time, not runtime.
26+
warnings.filterwarnings(
27+
"ignore", category=DeprecationWarning, module=".*packaging\\.version"
28+
)
29+
from pip._internal.cli.main import main as _main
2430

25-
if __name__ == '__main__':
2631
sys.exit(_main())
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
import fetchcode.vcs.pip._internal.utils.inject_securetransport # noqa
2-
from fetchcode.vcs.pip._internal.utils.typing import MYPY_CHECK_RUNNING
1+
from typing import List, Optional
32

4-
if MYPY_CHECK_RUNNING:
5-
from typing import Optional, List
3+
import pip._internal.utils.inject_securetransport # noqa
4+
from pip._internal.utils import _log
65

6+
# init_logging() must be called before any call to logging.getLogger()
7+
# which happens at import of most modules.
8+
_log.init_logging()
79

8-
def main(args=None):
9-
# type: (Optional[List[str]]) -> int
10+
11+
def main(args: (Optional[List[str]]) = None) -> int:
1012
"""This is preserved for old console scripts that may still be referencing
1113
it.
1214
1315
For additional details, see https://github.com/pypa/pip/issues/7498.
1416
"""
15-
from fetchcode.vcs.pip._internal.utils.entrypoints import _wrapper
17+
from pip._internal.utils.entrypoints import _wrapper
1618

1719
return _wrapper(args)

src/fetchcode/vcs/pip/_internal/build_env.py

Lines changed: 117 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
11
"""Build Environment used for isolation during sdist building
22
"""
33

4-
# The following comment should be removed at some point in the future.
5-
# mypy: strict-optional=False
6-
# mypy: disallow-untyped-defs=False
7-
4+
import contextlib
85
import logging
96
import os
7+
import pathlib
108
import sys
119
import textwrap
10+
import zipfile
1211
from collections import OrderedDict
13-
from distutils.sysconfig import get_python_lib
1412
from sysconfig import get_paths
13+
from types import TracebackType
14+
from typing import TYPE_CHECKING, Iterable, Iterator, List, Optional, Set, Tuple, Type
1515

16-
from fetchcode.vcs.pip._vendor.pkg_resources import Requirement, VersionConflict, WorkingSet
16+
from pip._vendor.certifi import where
17+
from pip._vendor.packaging.requirements import Requirement
18+
from pip._vendor.packaging.version import Version
1719

1820
from pip import __file__ as pip_location
19-
from fetchcode.vcs.pip._internal.cli.spinners import open_spinner
20-
from fetchcode.vcs.pip._internal.utils.subprocess import call_subprocess
21-
from fetchcode.vcs.pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
22-
from fetchcode.vcs.pip._internal.utils.typing import MYPY_CHECK_RUNNING
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_environment
24+
from pip._internal.utils.subprocess import call_subprocess
25+
from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
2326

24-
if MYPY_CHECK_RUNNING:
25-
from typing import Tuple, Set, Iterable, Optional, List
26-
from fetchcode.vcs.pip._internal.index.package_finder import PackageFinder
27+
if TYPE_CHECKING:
28+
from pip._internal.index.package_finder import PackageFinder
2729

2830
logger = logging.getLogger(__name__)
2931

@@ -38,17 +40,36 @@ def __init__(self, path):
3840
'nt' if os.name == 'nt' else 'posix_prefix',
3941
vars={'base': path, 'platbase': path}
4042
)['scripts']
41-
# Note: prefer distutils' sysconfig to get the
42-
# library paths so PyPy is correctly supported.
43-
purelib = get_python_lib(plat_specific=False, prefix=path)
44-
platlib = get_python_lib(plat_specific=True, prefix=path)
45-
if purelib == platlib:
46-
self.lib_dirs = [purelib]
47-
else:
48-
self.lib_dirs = [purelib, platlib]
43+
self.lib_dirs = get_prefixed_libs(path)
4944

5045

51-
class BuildEnvironment(object):
46+
@contextlib.contextmanager
47+
def _create_standalone_pip() -> Iterator[str]:
48+
"""Create a "standalone pip" zip file.
49+
50+
The zip file's content is identical to the currently-running pip.
51+
It will be used to install requirements into the build environment.
52+
"""
53+
source = pathlib.Path(pip_location).resolve().parent
54+
55+
# Return the current instance if `source` is not a directory. We can't build
56+
# a zip from this, and it likely means the instance is already standalone.
57+
if not source.is_dir():
58+
yield str(source)
59+
return
60+
61+
with TempDirectory(kind="standalone-pip") as tmp_dir:
62+
pip_zip = os.path.join(tmp_dir.path, "__env_pip__.zip")
63+
kwargs = {}
64+
if sys.version_info >= (3, 8):
65+
kwargs["strict_timestamps"] = False
66+
with zipfile.ZipFile(pip_zip, "w", **kwargs) as zf:
67+
for child in source.rglob("*"):
68+
zf.write(child, child.relative_to(source.parent).as_posix())
69+
yield os.path.join(pip_zip, "pip")
70+
71+
72+
class BuildEnvironment:
5273
"""Creates and manages an isolated environment to install build deps
5374
"""
5475

@@ -58,10 +79,10 @@ def __init__(self):
5879
kind=tempdir_kinds.BUILD_ENV, globally_managed=True
5980
)
6081

61-
self._prefixes = OrderedDict((
82+
self._prefixes = OrderedDict(
6283
(name, _Prefix(os.path.join(temp_dir.path, name)))
6384
for name in ('normal', 'overlay')
64-
))
85+
)
6586

6687
self._bin_dirs = [] # type: List[str]
6788
self._lib_dirs = [] # type: List[str]
@@ -73,10 +94,7 @@ def __init__(self):
7394
# - ensure .pth files are honored
7495
# - prevent access to system site packages
7596
system_sites = {
76-
os.path.normcase(site) for site in (
77-
get_python_lib(plat_specific=False),
78-
get_python_lib(plat_specific=True),
79-
)
97+
os.path.normcase(site) for site in (get_purelib(), get_platlib())
8098
}
8199
self._site_dir = os.path.join(temp_dir.path, 'site')
82100
if not os.path.exists(self._site_dir):
@@ -110,6 +128,7 @@ def __init__(self):
110128
).format(system_sites=system_sites, lib_dirs=self._lib_dirs))
111129

112130
def __enter__(self):
131+
# type: () -> None
113132
self._save_env = {
114133
name: os.environ.get(name, None)
115134
for name in ('PATH', 'PYTHONNOUSERSITE', 'PYTHONPATH')
@@ -128,7 +147,13 @@ def __enter__(self):
128147
'PYTHONPATH': os.pathsep.join(pythonpath),
129148
})
130149

131-
def __exit__(self, exc_type, exc_val, exc_tb):
150+
def __exit__(
151+
self,
152+
exc_type, # type: Optional[Type[BaseException]]
153+
exc_val, # type: Optional[BaseException]
154+
exc_tb # type: Optional[TracebackType]
155+
):
156+
# type: (...) -> None
132157
for varname, old_value in self._save_env.items():
133158
if old_value is None:
134159
os.environ.pop(varname, None)
@@ -144,31 +169,62 @@ def check_requirements(self, reqs):
144169
missing = set()
145170
conflicting = set()
146171
if reqs:
147-
ws = WorkingSet(self._lib_dirs)
148-
for req in reqs:
149-
try:
150-
if ws.find(Requirement.parse(req)) is None:
151-
missing.add(req)
152-
except VersionConflict as e:
153-
conflicting.add((str(e.args[0].as_requirement()),
154-
str(e.args[1])))
172+
env = get_environment(self._lib_dirs)
173+
for req_str in reqs:
174+
req = Requirement(req_str)
175+
dist = env.get_distribution(req.name)
176+
if not dist:
177+
missing.add(req_str)
178+
continue
179+
if isinstance(dist.version, Version):
180+
installed_req_str = f"{req.name}=={dist.version}"
181+
else:
182+
installed_req_str = f"{req.name}==={dist.version}"
183+
if dist.version not in req.specifier:
184+
conflicting.add((installed_req_str, req_str))
185+
# FIXME: Consider direct URL?
155186
return conflicting, missing
156187

157188
def install_requirements(
158189
self,
159190
finder, # type: PackageFinder
160191
requirements, # type: Iterable[str]
161192
prefix_as_string, # type: str
162-
message # type: Optional[str]
193+
message # type: str
163194
):
164195
# type: (...) -> None
165196
prefix = self._prefixes[prefix_as_string]
166197
assert not prefix.setup
167198
prefix.setup = True
168199
if not requirements:
169200
return
201+
with contextlib.ExitStack() as ctx:
202+
# TODO: Remove this block when dropping 3.6 support. Python 3.6
203+
# lacks importlib.resources and pep517 has issues loading files in
204+
# a zip, so we fallback to the "old" method by adding the current
205+
# pip directory to the child process's sys.path.
206+
if sys.version_info < (3, 7):
207+
pip_runnable = os.path.dirname(pip_location)
208+
else:
209+
pip_runnable = ctx.enter_context(_create_standalone_pip())
210+
self._install_requirements(
211+
pip_runnable,
212+
finder,
213+
requirements,
214+
prefix,
215+
message,
216+
)
217+
218+
@staticmethod
219+
def _install_requirements(
220+
pip_runnable: str,
221+
finder: "PackageFinder",
222+
requirements: Iterable[str],
223+
prefix: _Prefix,
224+
message: str,
225+
) -> None:
170226
args = [
171-
sys.executable, os.path.dirname(pip_location), 'install',
227+
sys.executable, pip_runnable, 'install',
172228
'--ignore-installed', '--no-user', '--prefix', prefix.path,
173229
'--no-warn-script-location',
174230
] # type: List[str]
@@ -193,27 +249,46 @@ def install_requirements(
193249
args.extend(['--trusted-host', host])
194250
if finder.allow_all_prereleases:
195251
args.append('--pre')
252+
if finder.prefer_binary:
253+
args.append('--prefer-binary')
196254
args.append('--')
197255
args.extend(requirements)
256+
extra_environ = {"_PIP_STANDALONE_CERT": where()}
198257
with open_spinner(message) as spinner:
199-
call_subprocess(args, spinner=spinner)
258+
call_subprocess(args, spinner=spinner, extra_environ=extra_environ)
200259

201260

202261
class NoOpBuildEnvironment(BuildEnvironment):
203262
"""A no-op drop-in replacement for BuildEnvironment
204263
"""
205264

206265
def __init__(self):
266+
# type: () -> None
207267
pass
208268

209269
def __enter__(self):
270+
# type: () -> None
210271
pass
211272

212-
def __exit__(self, exc_type, exc_val, exc_tb):
273+
def __exit__(
274+
self,
275+
exc_type, # type: Optional[Type[BaseException]]
276+
exc_val, # type: Optional[BaseException]
277+
exc_tb # type: Optional[TracebackType]
278+
):
279+
# type: (...) -> None
213280
pass
214281

215282
def cleanup(self):
283+
# type: () -> None
216284
pass
217285

218-
def install_requirements(self, finder, requirements, prefix, message):
286+
def install_requirements(
287+
self,
288+
finder, # type: PackageFinder
289+
requirements, # type: Iterable[str]
290+
prefix_as_string, # type: str
291+
message # type: str
292+
):
293+
# type: (...) -> None
219294
raise NotImplementedError()

0 commit comments

Comments
 (0)