1
1
import logging
2
2
import sys
3
3
4
+ from pip ._vendor .contextlib2 import suppress
4
5
from pip ._vendor .packaging .specifiers import InvalidSpecifier , SpecifierSet
5
6
from pip ._vendor .packaging .utils import canonicalize_name
6
7
from pip ._vendor .packaging .version import Version
7
8
8
9
from pip ._internal .exceptions import HashError , MetadataInconsistent
10
+ from pip ._internal .network .lazy_wheel import dist_from_wheel_url
9
11
from pip ._internal .req .constructors import (
10
12
install_req_from_editable ,
11
13
install_req_from_line ,
12
14
)
13
15
from pip ._internal .req .req_install import InstallRequirement
16
+ from pip ._internal .utils .logging import indent_log
14
17
from pip ._internal .utils .misc import normalize_version_info
15
18
from pip ._internal .utils .packaging import get_requires_python
16
19
from pip ._internal .utils .typing import MYPY_CHECK_RUNNING
@@ -197,6 +200,18 @@ def _prepare_abstract_distribution(self):
197
200
# type: () -> AbstractDistribution
198
201
raise NotImplementedError ("Override in subclass" )
199
202
203
+ def _check_metadata_consistency (self , dist ):
204
+ # type: (Distribution) -> None
205
+ """Check for consistency of project name and version of dist."""
206
+ # TODO: (Longer term) Rather than abort, reject this candidate
207
+ # and backtrack. This would need resolvelib support.
208
+ name = canonicalize_name (dist .project_name )
209
+ if self ._name is not None and self ._name != name :
210
+ raise MetadataInconsistent (self ._ireq , "name" , dist .project_name )
211
+ version = dist .parsed_version
212
+ if self ._version is not None and self ._version != version :
213
+ raise MetadataInconsistent (self ._ireq , "version" , dist .version )
214
+
200
215
def _prepare (self ):
201
216
# type: () -> None
202
217
if self ._dist is not None :
@@ -210,50 +225,43 @@ def _prepare(self):
210
225
211
226
self ._dist = abstract_dist .get_pkg_resources_distribution ()
212
227
assert self ._dist is not None , "Distribution already installed"
213
-
214
- # TODO: (Longer term) Rather than abort, reject this candidate
215
- # and backtrack. This would need resolvelib support.
216
- name = canonicalize_name (self ._dist .project_name )
217
- if self ._name is not None and self ._name != name :
218
- raise MetadataInconsistent (
219
- self ._ireq , "name" , self ._dist .project_name ,
220
- )
221
- version = self ._dist .parsed_version
222
- if self ._version is not None and self ._version != version :
223
- raise MetadataInconsistent (
224
- self ._ireq , "version" , self ._dist .version ,
225
- )
228
+ self ._check_metadata_consistency (self ._dist )
226
229
227
230
@property
228
231
def dist (self ):
229
232
# type: () -> Distribution
230
233
self ._prepare ()
231
234
return self ._dist
232
235
233
- def _get_requires_python_specifier (self ):
234
- # type: () -> Optional[SpecifierSet]
235
- requires_python = get_requires_python (self . dist )
236
+ def _get_requires_python_specifier (self , dist ):
237
+ # type: (Distribution ) -> Optional[SpecifierSet]
238
+ requires_python = get_requires_python (dist )
236
239
if requires_python is None :
237
240
return None
238
241
try :
239
242
spec = SpecifierSet (requires_python )
240
243
except InvalidSpecifier as e :
244
+ name = canonicalize_name (dist .project_name )
241
245
logger .warning (
242
- "Package %r has an invalid Requires-Python: %s" , self . name , e ,
246
+ "Package %r has an invalid Requires-Python: %s" , name , e ,
243
247
)
244
248
return None
245
249
return spec
246
250
247
- def iter_dependencies (self ):
248
- # type: () -> Iterable[Optional[Requirement]]
249
- for r in self . dist .requires ():
251
+ def _iter_dependencies (self , dist ):
252
+ # type: (Distribution ) -> Iterable[Optional[Requirement]]
253
+ for r in dist .requires ():
250
254
yield self ._factory .make_requirement_from_spec (str (r ), self ._ireq )
251
255
python_dep = self ._factory .make_requires_python_requirement (
252
- self ._get_requires_python_specifier (),
256
+ self ._get_requires_python_specifier (dist ),
253
257
)
254
258
if python_dep :
255
259
yield python_dep
256
260
261
+ def iter_dependencies (self ):
262
+ # type: () -> Iterable[Optional[Requirement]]
263
+ return self ._iter_dependencies (self .dist )
264
+
257
265
def get_install_requirement (self ):
258
266
# type: () -> Optional[InstallRequirement]
259
267
self ._prepare ()
@@ -291,6 +299,27 @@ def __init__(
291
299
version = version ,
292
300
)
293
301
302
+ def iter_dependencies (self ):
303
+ # type: () -> Iterable[Optional[Requirement]]
304
+ dist = None # type: Optional[Distribution]
305
+ preparer , req = self ._factory .preparer , self ._ireq
306
+ remote_wheel = self ._link .is_wheel and not self ._link .is_file
307
+ # TODO: Opt-in as unstable feature.
308
+ if remote_wheel and not preparer .require_hashes :
309
+ assert self ._name is not None
310
+ logger .info ('Collecting %s' , req .req or req )
311
+ # If RuntimeError is raised, fallback to self.dist.
312
+ with indent_log (), suppress (RuntimeError ):
313
+ logger .info (
314
+ 'Obtaining dependency information from %s %s' ,
315
+ self ._name , self ._version ,
316
+ )
317
+ url = self ._link .url .split ('#' , 1 )[0 ]
318
+ session = preparer .downloader ._session
319
+ dist = dist_from_wheel_url (self ._name , url , session )
320
+ self ._check_metadata_consistency (dist )
321
+ return self ._iter_dependencies (dist or self .dist )
322
+
294
323
def _prepare_abstract_distribution (self ):
295
324
# type: () -> AbstractDistribution
296
325
return self ._factory .preparer .prepare_linked_requirement (
0 commit comments