34
34
from pip ._vendor .requests .exceptions import SSLError
35
35
36
36
37
- __all__ = ['PackageFinder' ]
37
+ __all__ = ['FormatControl' , 'fmt_ctl_handle_mutual_exclude' , ' PackageFinder' ]
38
38
39
39
40
40
# Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC)
@@ -97,14 +97,20 @@ class PackageFinder(object):
97
97
"""This finds packages.
98
98
99
99
This is meant to match easy_install's technique for looking for
100
- packages, by reading pages and looking for appropriate links
100
+ packages, by reading pages and looking for appropriate links.
101
101
"""
102
102
103
103
def __init__ (self , find_links , index_urls ,
104
- use_wheel = True , allow_external = (), allow_unverified = (),
104
+ allow_external = (), allow_unverified = (),
105
105
allow_all_external = False , allow_all_prereleases = False ,
106
106
trusted_hosts = None , process_dependency_links = False ,
107
- session = None ):
107
+ session = None , format_control = None ):
108
+ """Create a PackageFinder.
109
+
110
+ :param format_control: A FormatControl object or None. Used to control
111
+ the selection of source packages / binary packages when consulting
112
+ the index and links.
113
+ """
108
114
if session is None :
109
115
raise TypeError (
110
116
"PackageFinder() missing 1 required keyword argument: "
@@ -130,7 +136,7 @@ def __init__(self, find_links, index_urls,
130
136
# These are boring links that have already been logged somehow:
131
137
self .logged_links = set ()
132
138
133
- self .use_wheel = use_wheel
139
+ self .format_control = format_control or FormatControl ( set (), set ())
134
140
135
141
# Do we allow (safe and verifiable) externally hosted files?
136
142
self .allow_external = set (normalize_name (n ) for n in allow_external )
@@ -413,13 +419,9 @@ def _find_all_versions(self, project_name):
413
419
for location in url_locations :
414
420
logger .debug ('* %s' , location )
415
421
416
- formats = set (["source" ])
417
- if self .use_wheel :
418
- formats .add ("binary" )
419
- search = Search (
420
- project_name .lower (),
421
- pkg_resources .safe_name (project_name ).lower (),
422
- frozenset (formats ))
422
+ canonical_name = pkg_resources .safe_name (project_name ).lower ()
423
+ formats = fmt_ctl_formats (self .format_control , canonical_name )
424
+ search = Search (project_name .lower (), canonical_name , formats )
423
425
find_links_versions = self ._package_versions (
424
426
# We trust every directly linked archive in find_links
425
427
(Link (url , '-f' , trusted = True ) for url in self .find_links ),
@@ -686,6 +688,7 @@ def _link_package_versions(self, link, search):
686
688
version = None
687
689
if link .egg_fragment :
688
690
egg_info = link .egg_fragment
691
+ ext = link .ext
689
692
else :
690
693
egg_info , ext = link .splitext ()
691
694
if not ext :
@@ -743,6 +746,12 @@ def _link_package_versions(self, link, search):
743
746
return
744
747
version = wheel .version
745
748
749
+ # This should be up by the search.ok_binary check, but see issue 2700.
750
+ if "source" not in search .formats and ext != wheel_ext :
751
+ self ._log_skipped_link (
752
+ link , 'No sources permitted for %s' % search .supplied )
753
+ return
754
+
746
755
if not version :
747
756
version = egg_info_matches (egg_info , search .supplied , link )
748
757
if version is None :
@@ -1192,6 +1201,56 @@ def is_wheel(self):
1192
1201
INSTALLED_VERSION = Link (Inf )
1193
1202
1194
1203
1204
+ FormatControl = namedtuple ('FormatControl' , 'no_binary only_binary' )
1205
+ """This object has two fields, no_binary and only_binary.
1206
+
1207
+ If a field is falsy, it isn't set. If it is {':all:'}, it should match all
1208
+ packages except those listed in the other field. Only one field can be set
1209
+ to {':all:'} at a time. The rest of the time exact package name matches
1210
+ are listed, with any given package only showing up in one field at a time.
1211
+ """
1212
+
1213
+
1214
+ def fmt_ctl_handle_mutual_exclude (value , target , other ):
1215
+ new = value .split (',' )
1216
+ while ':all:' in new :
1217
+ other .clear ()
1218
+ target .clear ()
1219
+ target .add (':all:' )
1220
+ del new [:new .index (':all:' ) + 1 ]
1221
+ if ':none:' not in new :
1222
+ # Without a none, we want to discard everything as :all: covers it
1223
+ return
1224
+ for name in new :
1225
+ if name == ':none:' :
1226
+ target .clear ()
1227
+ continue
1228
+ name = pkg_resources .safe_name (name ).lower ()
1229
+ other .discard (name )
1230
+ target .add (name )
1231
+
1232
+
1233
+ def fmt_ctl_formats (fmt_ctl , canonical_name ):
1234
+ result = set (["binary" , "source" ])
1235
+ if canonical_name in fmt_ctl .only_binary :
1236
+ result .discard ('source' )
1237
+ elif canonical_name in fmt_ctl .no_binary :
1238
+ result .discard ('binary' )
1239
+ elif ':all:' in fmt_ctl .only_binary :
1240
+ result .discard ('source' )
1241
+ elif ':all:' in fmt_ctl .no_binary :
1242
+ result .discard ('binary' )
1243
+ return frozenset (result )
1244
+
1245
+
1246
+ def fmt_ctl_no_use_wheel (fmt_ctl ):
1247
+ fmt_ctl_handle_mutual_exclude (
1248
+ ':all:' , fmt_ctl .no_binary , fmt_ctl .only_binary )
1249
+ warnings .warn (
1250
+ '--no-use-wheel is deprecated and will be removed in the future. '
1251
+ ' Please use --no-binary :all: instead.' )
1252
+
1253
+
1195
1254
Search = namedtuple ('Search' , 'supplied canonical formats' )
1196
1255
"""Capture key aspects of a search.
1197
1256
0 commit comments