23
23
from pip ._vendor .distlib .scripts import ScriptMaker
24
24
from pip ._vendor .distlib .util import get_export_entry
25
25
from pip ._vendor .packaging .utils import canonicalize_name
26
- from pip ._vendor .six import StringIO
26
+ from pip ._vendor .six import StringIO , ensure_str
27
27
28
28
from pip ._internal .exceptions import InstallationError , UnsupportedWheel
29
29
from pip ._internal .locations import get_major_minor_version
33
33
from pip ._internal .utils .unpacking import unpack_file
34
34
35
35
if MYPY_CHECK_RUNNING :
36
+ from email .message import Message
36
37
from typing import (
37
38
Dict , List , Optional , Sequence , Tuple , IO , Text , Any ,
38
39
Iterable , Callable , Set ,
@@ -97,23 +98,9 @@ def fix_script(path):
97
98
return None
98
99
99
100
100
- dist_info_re = re .compile (r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>.+?))?)
101
- \.dist-info$""" , re .VERBOSE )
102
-
103
-
104
- def root_is_purelib (name , wheeldir ):
105
- # type: (str, str) -> bool
106
- """True if the extracted wheel in wheeldir should go into purelib."""
107
- name_folded = name .replace ("-" , "_" )
108
- for item in os .listdir (wheeldir ):
109
- match = dist_info_re .match (item )
110
- if match and match .group ('name' ) == name_folded :
111
- with open (os .path .join (wheeldir , item , 'WHEEL' )) as wheel :
112
- for line in wheel :
113
- line = line .lower ().rstrip ()
114
- if line == "root-is-purelib: true" :
115
- return True
116
- return False
101
+ def wheel_root_is_purelib (metadata ):
102
+ # type: (Message) -> bool
103
+ return metadata .get ("Root-Is-Purelib" , "" ).lower () == "true"
117
104
118
105
119
106
def get_entrypoints (filename ):
@@ -324,23 +311,25 @@ def install_unpacked_wheel(
324
311
# TODO: Look into moving this into a dedicated class for representing an
325
312
# installation.
326
313
314
+ source = wheeldir .rstrip (os .path .sep ) + os .path .sep
315
+
327
316
try :
328
- version = wheel_version (wheeldir )
317
+ info_dir = wheel_dist_info_dir (source , name )
318
+ metadata = wheel_metadata (source , info_dir )
319
+ version = wheel_version (metadata )
329
320
except UnsupportedWheel as e :
330
321
raise UnsupportedWheel (
331
322
"{} has an invalid wheel, {}" .format (name , str (e ))
332
323
)
333
324
334
325
check_compatibility (version , name )
335
326
336
- if root_is_purelib ( name , wheeldir ):
327
+ if wheel_root_is_purelib ( metadata ):
337
328
lib_dir = scheme .purelib
338
329
else :
339
330
lib_dir = scheme .platlib
340
331
341
- source = wheeldir .rstrip (os .path .sep ) + os .path .sep
342
332
subdirs = os .listdir (source )
343
- info_dirs = [s for s in subdirs if s .endswith ('.dist-info' )]
344
333
data_dirs = [s for s in subdirs if s .endswith ('.data' )]
345
334
346
335
# Record details of the files moved
@@ -434,27 +423,6 @@ def clobber(
434
423
435
424
clobber (source , lib_dir , True )
436
425
437
- assert info_dirs , "{} .dist-info directory not found" .format (
438
- req_description
439
- )
440
-
441
- assert len (info_dirs ) == 1 , (
442
- '{} multiple .dist-info directories found: {}' .format (
443
- req_description , ', ' .join (info_dirs )
444
- )
445
- )
446
-
447
- info_dir = info_dirs [0 ]
448
-
449
- info_dir_name = canonicalize_name (info_dir )
450
- canonical_name = canonicalize_name (name )
451
- if not info_dir_name .startswith (canonical_name ):
452
- raise UnsupportedWheel (
453
- "{} .dist-info directory {!r} does not start with {!r}" .format (
454
- req_description , info_dir , canonical_name
455
- )
456
- )
457
-
458
426
dest_info_dir = os .path .join (lib_dir , info_dir )
459
427
460
428
# Get the defined entry points
@@ -656,25 +624,48 @@ def install_wheel(
656
624
)
657
625
658
626
659
- def wheel_version (source_dir ):
660
- # type: (Optional[str]) -> Tuple[int, ...]
661
- """Return the Wheel-Version of an extracted wheel, if possible.
662
- Otherwise, raise UnsupportedWheel if we couldn't parse / extract it.
627
+ def wheel_dist_info_dir (source , name ):
628
+ # type: (str, str) -> str
629
+ """Returns the name of the contained .dist-info directory.
630
+
631
+ Raises AssertionError or UnsupportedWheel if not found, >1 found, or
632
+ it doesn't match the provided name.
663
633
"""
664
- try :
665
- dists = [d for d in pkg_resources .find_on_path (None , source_dir )]
666
- except Exception as e :
634
+ subdirs = os .listdir (source )
635
+ info_dirs = [s for s in subdirs if s .endswith ('.dist-info' )]
636
+
637
+ if not info_dirs :
638
+ raise UnsupportedWheel (".dist-info directory not found" )
639
+
640
+ if len (info_dirs ) > 1 :
641
+ raise UnsupportedWheel (
642
+ "multiple .dist-info directories found: {}" .format (
643
+ ", " .join (info_dirs )
644
+ )
645
+ )
646
+
647
+ info_dir = info_dirs [0 ]
648
+
649
+ info_dir_name = canonicalize_name (info_dir )
650
+ canonical_name = canonicalize_name (name )
651
+ if not info_dir_name .startswith (canonical_name ):
667
652
raise UnsupportedWheel (
668
- "could not find a contained distribution due to: {!r}" .format (e )
653
+ ".dist-info directory {!r} does not start with {!r}" .format (
654
+ info_dir , canonical_name
655
+ )
669
656
)
670
657
671
- if not dists :
672
- raise UnsupportedWheel ("no contained distribution found" )
658
+ return info_dir
673
659
674
- dist = dists [0 ]
675
660
661
+ def wheel_metadata (source , dist_info_dir ):
662
+ # type: (str, str) -> Message
663
+ """Return the WHEEL metadata of an extracted wheel, if possible.
664
+ Otherwise, raise UnsupportedWheel.
665
+ """
676
666
try :
677
- wheel_text = dist .get_metadata ('WHEEL' )
667
+ with open (os .path .join (source , dist_info_dir , "WHEEL" ), "rb" ) as f :
668
+ wheel_text = ensure_str (f .read ())
678
669
except (IOError , OSError ) as e :
679
670
raise UnsupportedWheel ("could not read WHEEL file: {!r}" .format (e ))
680
671
except UnicodeDecodeError as e :
@@ -683,8 +674,14 @@ def wheel_version(source_dir):
683
674
# FeedParser (used by Parser) does not raise any exceptions. The returned
684
675
# message may have .defects populated, but for backwards-compatibility we
685
676
# currently ignore them.
686
- wheel_data = Parser ().parsestr (wheel_text )
677
+ return Parser ().parsestr (wheel_text )
687
678
679
+
680
+ def wheel_version (wheel_data ):
681
+ # type: (Message) -> Tuple[int, ...]
682
+ """Given WHEEL metadata, return the parsed Wheel-Version.
683
+ Otherwise, raise UnsupportedWheel.
684
+ """
688
685
version_text = wheel_data ["Wheel-Version" ]
689
686
if version_text is None :
690
687
raise UnsupportedWheel ("WHEEL is missing Wheel-Version" )
0 commit comments