Skip to content

Commit 5a55c88

Browse files
authored
Merge pull request #2643 from pypa/fix-find-links
Fix resolution using `dependency_links` with ssh
2 parents a853814 + 75eaafc commit 5a55c88

File tree

6 files changed

+96
-38
lines changed

6 files changed

+96
-38
lines changed

news/2434.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed the ability of pipenv to parse ``dependency_links`` from ``setup.py`` when ``PIP_PROCESS_DEPENDENCY_LINKS`` is enabled.

news/2643.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Dependency links to private repositories defined via ``ssh://`` schemes will now install correctly and skip hashing as long as ``PIP_PROCESS_DEPENDENCY_LINKS=1``.

news/2643.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Enhanced resolution of editable and VCS dependencies.

pipenv/patched/piptools/repositories/pypi.py

+27-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# coding: utf-8
22
from __future__ import (absolute_import, division, print_function,
33
unicode_literals)
4-
4+
import copy
55
import hashlib
66
import os
77
import sys
@@ -21,18 +21,16 @@
2121
SafeFileCache,
2222
)
2323

24-
from pipenv.patched.notpip._vendor.packaging.requirements import InvalidRequirement, Requirement
25-
from pipenv.patched.notpip._vendor.packaging.version import Version, InvalidVersion, parse as parse_version
26-
from pipenv.patched.notpip._vendor.packaging.specifiers import SpecifierSet, InvalidSpecifier, Specifier
27-
from pipenv.patched.notpip._vendor.packaging.markers import Marker, Op, Value, Variable
28-
from pipenv.patched.notpip._vendor.pyparsing import ParseException
24+
from pipenv.patched.notpip._vendor.packaging.requirements import Requirement
25+
from pipenv.patched.notpip._vendor.packaging.specifiers import SpecifierSet, Specifier
26+
from pipenv.patched.notpip._vendor.packaging.markers import Op, Value, Variable
2927
from pipenv.patched.notpip._internal.exceptions import InstallationError
28+
from pipenv.patched.notpip._internal.vcs import VcsSupport
3029

31-
from ..cache import CACHE_DIR
3230
from pipenv.environments import PIPENV_CACHE_DIR
3331
from ..exceptions import NoCandidateFound
34-
from ..utils import (fs_str, is_pinned_requirement, lookup_table, as_tuple, key_from_req,
35-
make_install_requirement, format_requirement, dedup, clean_requires_python)
32+
from ..utils import (fs_str, is_pinned_requirement, lookup_table,
33+
make_install_requirement, clean_requires_python)
3634

3735
from .base import BaseRepository
3836

@@ -64,15 +62,20 @@ def __init__(self, *args, **kwargs):
6462
def get_hash(self, location):
6563
# if there is no location hash (i.e., md5 / sha256 / etc) we on't want to store it
6664
hash_value = None
67-
can_hash = location.hash
65+
vcs = VcsSupport()
66+
orig_scheme = location.scheme
67+
new_location = copy.deepcopy(location)
68+
if orig_scheme in vcs.all_schemes:
69+
new_location.url = new_location.url.split("+", 1)[-1]
70+
can_hash = new_location.hash
6871
if can_hash:
6972
# hash url WITH fragment
70-
hash_value = self.get(location.url)
73+
hash_value = self.get(new_location.url)
7174
if not hash_value:
72-
hash_value = self._get_file_hash(location)
75+
hash_value = self._get_file_hash(new_location)
7376
hash_value = hash_value.encode('utf8')
7477
if can_hash:
75-
self.set(location.url, hash_value)
78+
self.set(new_location.url, hash_value)
7679
return hash_value.decode('utf8')
7780

7881
def _get_file_hash(self, location):
@@ -276,6 +279,13 @@ def get_legacy_dependencies(self, ireq):
276279
setup_requires = {}
277280
dist = None
278281
if ireq.editable:
282+
try:
283+
from pipenv.utils import chdir
284+
with chdir(ireq.setup_py_dir):
285+
from setuptools.dist import distutils
286+
distutils.core.run_setup(ireq.setup_py)
287+
except (ImportError, InstallationError, TypeError, AttributeError):
288+
pass
279289
try:
280290
dist = ireq.get_dist()
281291
except InstallationError:
@@ -425,6 +435,10 @@ def get_hashes(self, ireq):
425435
if ireq.editable:
426436
return set()
427437

438+
vcs = VcsSupport()
439+
if ireq.link and ireq.link.scheme in vcs.all_schemes and 'ssh' in ireq.link.scheme:
440+
return set()
441+
428442
if not is_pinned_requirement(ireq):
429443
raise TypeError(
430444
"Expected pinned requirement, got {}".format(ireq))

pipenv/utils.py

+16
Original file line numberDiff line numberDiff line change
@@ -1361,3 +1361,19 @@ def is_virtual_environment(path):
13611361
if python_like.is_file() and os.access(str(python_like), os.X_OK):
13621362
return True
13631363
return False
1364+
1365+
1366+
@contextmanager
1367+
def chdir(path):
1368+
"""Context manager to change working directories."""
1369+
from ._compat import Path
1370+
if not path:
1371+
return
1372+
prev_cwd = Path.cwd().as_posix()
1373+
if isinstance(path, Path):
1374+
path = path.as_posix()
1375+
os.chdir(str(path))
1376+
try:
1377+
yield
1378+
finally:
1379+
os.chdir(prev_cwd)

tasks/vendoring/patches/patched/piptools.patch

+50-25
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,22 @@ index 4e6174c..75f9b49 100644
1919
# NOTE
2020
# We used to store the cache dir under ~/.pip-tools, which is not the
2121
diff --git a/pipenv/patched/piptools/repositories/pypi.py b/pipenv/patched/piptools/repositories/pypi.py
22-
index 1c4b943..c922be1 100644
22+
index 1c4b943..91902dc 100644
2323
--- a/pipenv/patched/piptools/repositories/pypi.py
2424
+++ b/pipenv/patched/piptools/repositories/pypi.py
25-
@@ -4,6 +4,7 @@ from __future__ import (absolute_import, division, print_function,
26-
25+
@@ -1,9 +1,10 @@
26+
# coding: utf-8
27+
from __future__ import (absolute_import, division, print_function,
28+
unicode_literals)
29+
-
30+
+import copy
2731
import hashlib
2832
import os
2933
+import sys
3034
from contextlib import contextmanager
3135
from shutil import rmtree
3236

33-
@@ -15,13 +16,24 @@ from .._compat import (
37+
@@ -15,13 +16,22 @@ from .._compat import (
3438
Wheel,
3539
FAVORITE_HASH,
3640
TemporaryDirectory,
@@ -40,25 +44,23 @@ index 1c4b943..c922be1 100644
4044
+ SafeFileCache,
4145
)
4246

43-
+from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
44-
+from pip._vendor.packaging.version import Version, InvalidVersion, parse as parse_version
45-
+from pip._vendor.packaging.specifiers import SpecifierSet, InvalidSpecifier, Specifier
46-
+from pip._vendor.packaging.markers import Marker, Op, Value, Variable
47-
+from pip._vendor.pyparsing import ParseException
47+
-from ..cache import CACHE_DIR
48+
+from pip._vendor.packaging.requirements import Requirement
49+
+from pip._vendor.packaging.specifiers import SpecifierSet, Specifier
50+
+from pip._vendor.packaging.markers import Op, Value, Variable
4851
+from pip._internal.exceptions import InstallationError
52+
+from pip._internal.vcs import VcsSupport
4953
+
50-
from ..cache import CACHE_DIR
5154
+from pipenv.environments import PIPENV_CACHE_DIR
5255
from ..exceptions import NoCandidateFound
53-
-from ..utils import (fs_str, is_pinned_requirement, lookup_table,
56+
from ..utils import (fs_str, is_pinned_requirement, lookup_table,
5457
- make_install_requirement)
55-
+from ..utils import (fs_str, is_pinned_requirement, lookup_table, as_tuple, key_from_req,
56-
+ make_install_requirement, format_requirement, dedup, clean_requires_python)
58+
+ make_install_requirement, clean_requires_python)
5759
+
5860
from .base import BaseRepository
5961

6062

61-
@@ -37,6 +49,40 @@ except ImportError:
63+
@@ -37,6 +47,45 @@ except ImportError:
6264
from pip.wheel import WheelCache
6365

6466

@@ -77,15 +79,20 @@ index 1c4b943..c922be1 100644
7779
+ def get_hash(self, location):
7880
+ # if there is no location hash (i.e., md5 / sha256 / etc) we on't want to store it
7981
+ hash_value = None
80-
+ can_hash = location.hash
82+
+ vcs = VcsSupport()
83+
+ orig_scheme = location.scheme
84+
+ new_location = copy.deepcopy(location)
85+
+ if orig_scheme in vcs.all_schemes:
86+
+ new_location.url = new_location.url.split("+", 1)[-1]
87+
+ can_hash = new_location.hash
8188
+ if can_hash:
8289
+ # hash url WITH fragment
83-
+ hash_value = self.get(location.url)
90+
+ hash_value = self.get(new_location.url)
8491
+ if not hash_value:
85-
+ hash_value = self._get_file_hash(location)
92+
+ hash_value = self._get_file_hash(new_location)
8693
+ hash_value = hash_value.encode('utf8')
8794
+ if can_hash:
88-
+ self.set(location.url, hash_value)
95+
+ self.set(new_location.url, hash_value)
8996
+ return hash_value.decode('utf8')
9097
+
9198
+ def _get_file_hash(self, location):
@@ -99,7 +106,7 @@ index 1c4b943..c922be1 100644
99106
class PyPIRepository(BaseRepository):
100107
DEFAULT_INDEX_URL = PyPI.simple_url
101108

102-
@@ -46,10 +92,11 @@ class PyPIRepository(BaseRepository):
109+
@@ -46,10 +95,11 @@ class PyPIRepository(BaseRepository):
103110
config), but any other PyPI mirror can be used if index_urls is
104111
changed/configured on the Finder.
105112
"""
@@ -113,7 +120,7 @@ index 1c4b943..c922be1 100644
113120

114121
index_urls = [pip_options.index_url] + pip_options.extra_index_urls
115122
if pip_options.no_index:
116-
@@ -74,11 +121,15 @@ class PyPIRepository(BaseRepository):
123+
@@ -74,11 +124,15 @@ class PyPIRepository(BaseRepository):
117124
# of all secondary dependencies for the given requirement, so we
118125
# only have to go to disk once for each requirement
119126
self._dependencies_cache = {}
@@ -131,7 +138,7 @@ index 1c4b943..c922be1 100644
131138

132139
def freshen_build_caches(self):
133140
"""
134-
@@ -114,10 +165,14 @@ class PyPIRepository(BaseRepository):
141+
@@ -114,10 +168,14 @@ class PyPIRepository(BaseRepository):
135142
if ireq.editable:
136143
return ireq # return itself as the best match
137144

@@ -148,7 +155,7 @@ index 1c4b943..c922be1 100644
148155

149156
# Reuses pip's internal candidate sort key to sort
150157
matching_candidates = [candidates_by_version[ver] for ver in matching_versions]
151-
@@ -126,11 +181,71 @@ class PyPIRepository(BaseRepository):
158+
@@ -126,11 +184,71 @@ class PyPIRepository(BaseRepository):
152159
best_candidate = max(matching_candidates, key=self.finder._candidate_sort_key)
153160

154161
# Turn the candidate into a pinned InstallRequirement
@@ -223,7 +230,7 @@ index 1c4b943..c922be1 100644
223230
"""
224231
Given a pinned or an editable InstallRequirement, returns a set of
225232
dependencies (also InstallRequirements, but not necessarily pinned).
226-
@@ -155,20 +270,40 @@ class PyPIRepository(BaseRepository):
233+
@@ -155,20 +273,47 @@ class PyPIRepository(BaseRepository):
227234
os.makedirs(download_dir)
228235
if not os.path.isdir(self._wheel_download_dir):
229236
os.makedirs(self._wheel_download_dir)
@@ -235,6 +242,13 @@ index 1c4b943..c922be1 100644
235242
+ dist = None
236243
+ if ireq.editable:
237244
+ try:
245+
+ from pipenv.utils import chdir
246+
+ with chdir(ireq.setup_py_dir):
247+
+ from setuptools.dist import distutils
248+
+ distutils.core.run_setup(ireq.setup_py)
249+
+ except (ImportError, InstallationError, TypeError, AttributeError):
250+
+ pass
251+
+ try:
238252
+ dist = ireq.get_dist()
239253
+ except InstallationError:
240254
+ ireq.run_egg_info()
@@ -268,7 +282,7 @@ index 1c4b943..c922be1 100644
268282
)
269283
except TypeError:
270284
# Pip >= 10 (new resolver!)
271-
@@ -188,17 +323,97 @@ class PyPIRepository(BaseRepository):
285+
@@ -188,17 +333,97 @@ class PyPIRepository(BaseRepository):
272286
finder=self.finder,
273287
session=self.session,
274288
upgrade_strategy="to-satisfy-only",
@@ -369,7 +383,18 @@ index 1c4b943..c922be1 100644
369383
return set(self._dependencies_cache[ireq])
370384

371385
def get_hashes(self, ireq):
372-
@@ -217,24 +432,22 @@ class PyPIRepository(BaseRepository):
386+
@@ -210,6 +435,10 @@ class PyPIRepository(BaseRepository):
387+
if ireq.editable:
388+
return set()
389+
390+
+ vcs = VcsSupport()
391+
+ if ireq.link and ireq.link.scheme in vcs.all_schemes and 'ssh' in ireq.link.scheme:
392+
+ return set()
393+
+
394+
if not is_pinned_requirement(ireq):
395+
raise TypeError(
396+
"Expected pinned requirement, got {}".format(ireq))
397+
@@ -217,24 +446,22 @@ class PyPIRepository(BaseRepository):
373398
# We need to get all of the candidates that match our current version
374399
# pin, these will represent all of the files that could possibly
375400
# satisfy this constraint.

0 commit comments

Comments
 (0)