Skip to content

Commit 320a07f

Browse files
committed
Upgrade distlib to 0.2.0
1 parent 32e729f commit 320a07f

File tree

17 files changed

+268
-90
lines changed

17 files changed

+268
-90
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@
120120
* Reduce the verbosity of the pip command by default. (:pull:`2175`,
121121
:pull:`2177`, :pull:`2178`)
122122

123+
* Fixed :issue:`2031` - Respect sys.executable on OSX when installing from
124+
Wheels.
125+
123126

124127
**1.5.6 (2014-05-16)**
125128

pip/_vendor/distlib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#
77
import logging
88

9-
__version__ = '0.1.9'
9+
__version__ = '0.2.0'
1010

1111
class DistlibException(Exception):
1212
pass

pip/_vendor/distlib/compat.py

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
#
3-
# Copyright (C) 2013 Vinay Sajip.
3+
# Copyright (C) 2013-2014 Vinay Sajip.
44
# Licensed to the Python Software Foundation under a contributor agreement.
55
# See LICENSE.txt and CONTRIBUTORS.txt.
66
#
@@ -86,35 +86,73 @@ class CertificateError(ValueError):
8686
pass
8787

8888

89-
def _dnsname_to_pat(dn):
89+
def _dnsname_match(dn, hostname, max_wildcards=1):
90+
"""Matching according to RFC 6125, section 6.4.3
91+
92+
http://tools.ietf.org/html/rfc6125#section-6.4.3
93+
"""
9094
pats = []
91-
for frag in dn.split(r'.'):
92-
if frag == '*':
93-
# When '*' is a fragment by itself, it matches a non-empty
94-
# dotless fragment.
95-
pats.append('[^.]+')
96-
else:
97-
# Otherwise, '*' matches any dotless fragment.
98-
frag = re.escape(frag)
99-
pats.append(frag.replace(r'\*', '[^.]*'))
100-
return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
95+
if not dn:
96+
return False
97+
98+
parts = dn.split('.')
99+
leftmost, remainder = parts[0], parts[1:]
100+
101+
wildcards = leftmost.count('*')
102+
if wildcards > max_wildcards:
103+
# Issue #17980: avoid denials of service by refusing more
104+
# than one wildcard per fragment. A survery of established
105+
# policy among SSL implementations showed it to be a
106+
# reasonable choice.
107+
raise CertificateError(
108+
"too many wildcards in certificate DNS name: " + repr(dn))
109+
110+
# speed up common case w/o wildcards
111+
if not wildcards:
112+
return dn.lower() == hostname.lower()
113+
114+
# RFC 6125, section 6.4.3, subitem 1.
115+
# The client SHOULD NOT attempt to match a presented identifier in which
116+
# the wildcard character comprises a label other than the left-most label.
117+
if leftmost == '*':
118+
# When '*' is a fragment by itself, it matches a non-empty dotless
119+
# fragment.
120+
pats.append('[^.]+')
121+
elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
122+
# RFC 6125, section 6.4.3, subitem 3.
123+
# The client SHOULD NOT attempt to match a presented identifier
124+
# where the wildcard character is embedded within an A-label or
125+
# U-label of an internationalized domain name.
126+
pats.append(re.escape(leftmost))
127+
else:
128+
# Otherwise, '*' matches any dotless string, e.g. www*
129+
pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
130+
131+
# add the remaining fragments, ignore any wildcards
132+
for frag in remainder:
133+
pats.append(re.escape(frag))
134+
135+
pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
136+
return pat.match(hostname)
101137

102138

103139
def match_hostname(cert, hostname):
104140
"""Verify that *cert* (in decoded format as returned by
105-
SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules
106-
are mostly followed, but IP addresses are not accepted for *hostname*.
141+
SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
142+
rules are followed, but IP addresses are not accepted for *hostname*.
107143
108144
CertificateError is raised on failure. On success, the function
109145
returns nothing.
110146
"""
111147
if not cert:
112-
raise ValueError("empty or no certificate")
148+
raise ValueError("empty or no certificate, match_hostname needs a "
149+
"SSL socket or SSL context with either "
150+
"CERT_OPTIONAL or CERT_REQUIRED")
113151
dnsnames = []
114152
san = cert.get('subjectAltName', ())
115153
for key, value in san:
116154
if key == 'DNS':
117-
if _dnsname_to_pat(value).match(hostname):
155+
if _dnsname_match(value, hostname):
118156
return
119157
dnsnames.append(value)
120158
if not dnsnames:
@@ -125,7 +163,7 @@ def match_hostname(cert, hostname):
125163
# XXX according to RFC 2818, the most specific Common Name
126164
# must be used.
127165
if key == 'commonName':
128-
if _dnsname_to_pat(value).match(hostname):
166+
if _dnsname_match(value, hostname):
129167
return
130168
dnsnames.append(value)
131169
if len(dnsnames) > 1:

pip/_vendor/distlib/database.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
#
3-
# Copyright (C) 2012-2013 The Python Software Foundation.
3+
# Copyright (C) 2012-2014 The Python Software Foundation.
44
# See LICENSE.txt and CONTRIBUTORS.txt.
55
#
66
"""PEP 376 implementation."""
@@ -334,6 +334,8 @@ def __init__(self, metadata):
334334
self.digest = None
335335
self.extras = None # additional features requested
336336
self.context = None # environment marker overrides
337+
self.download_urls = set()
338+
self.digests = {}
337339

338340
@property
339341
def source_url(self):
@@ -925,9 +927,9 @@ def parse_requires_path(req_path):
925927
requires = None
926928
elif path.endswith('.egg-info'):
927929
if os.path.isdir(path):
928-
path = os.path.join(path, 'PKG-INFO')
929930
req_path = os.path.join(path, 'requires.txt')
930931
requires = parse_requires_path(req_path)
932+
path = os.path.join(path, 'PKG-INFO')
931933
metadata = Metadata(path=path, scheme='legacy')
932934
else:
933935
raise DistlibException('path must end with .egg-info or .egg, '

pip/_vendor/distlib/locators.py

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
#
3-
# Copyright (C) 2012-2013 Vinay Sajip.
3+
# Copyright (C) 2012-2014 Vinay Sajip.
44
# Licensed to the Python Software Foundation under a contributor agreement.
55
# See LICENSE.txt and CONTRIBUTORS.txt.
66
#
@@ -290,9 +290,9 @@ def _get_digest(self, info):
290290

291291
def _update_version_data(self, result, info):
292292
"""
293-
Update a result dictionary (the final result from _get_project) with a dictionary for a
294-
specific version, whih typically holds information gleaned from a filename or URL for an
295-
archive for the distribution.
293+
Update a result dictionary (the final result from _get_project) with a
294+
dictionary for a specific version, which typically holds information
295+
gleaned from a filename or URL for an archive for the distribution.
296296
"""
297297
name = info.pop('name')
298298
version = info.pop('version')
@@ -302,9 +302,12 @@ def _update_version_data(self, result, info):
302302
else:
303303
dist = make_dist(name, version, scheme=self.scheme)
304304
md = dist.metadata
305-
dist.digest = self._get_digest(info)
305+
dist.digest = digest = self._get_digest(info)
306+
url = info['url']
307+
result['digests'][url] = digest
306308
if md.source_url != info['url']:
307-
md.source_url = self.prefer_url(md.source_url, info['url'])
309+
md.source_url = self.prefer_url(md.source_url, url)
310+
result['urls'].setdefault(version, set()).add(url)
308311
dist.locator = self
309312
result[version] = dist
310313

@@ -350,9 +353,18 @@ def locate(self, requirement, prereleases=False):
350353
slist = sorted(slist, key=scheme.key)
351354
if slist:
352355
logger.debug('sorted list: %s', slist)
353-
result = versions[slist[-1]]
354-
if result and r.extras:
355-
result.extras = r.extras
356+
version = slist[-1]
357+
result = versions[version]
358+
if result:
359+
if r.extras:
360+
result.extras = r.extras
361+
result.download_urls = versions.get('urls', {}).get(version, set())
362+
d = {}
363+
sd = versions.get('digests', {})
364+
for url in result.download_urls:
365+
if url in sd:
366+
d[url] = sd[url]
367+
result.digests = d
356368
self.matcher = None
357369
return result
358370

@@ -380,7 +392,7 @@ def get_distribution_names(self):
380392
return set(self.client.list_packages())
381393

382394
def _get_project(self, name):
383-
result = {}
395+
result = {'urls': {}, 'digests': {}}
384396
versions = self.client.package_releases(name, True)
385397
for v in versions:
386398
urls = self.client.release_urls(name, v)
@@ -398,12 +410,17 @@ def _get_project(self, name):
398410
dist.digest = self._get_digest(info)
399411
dist.locator = self
400412
result[v] = dist
413+
for info in urls:
414+
url = info['url']
415+
digest = self._get_digest(info)
416+
result['urls'].setdefault(v, set()).add(url)
417+
result['digests'][url] = digest
401418
return result
402419

403420
class PyPIJSONLocator(Locator):
404421
"""
405422
This locator uses PyPI's JSON interface. It's very limited in functionality
406-
nad probably not worth using.
423+
and probably not worth using.
407424
"""
408425
def __init__(self, url, **kwargs):
409426
super(PyPIJSONLocator, self).__init__(**kwargs)
@@ -416,7 +433,7 @@ def get_distribution_names(self):
416433
raise NotImplementedError('Not available from this locator')
417434

418435
def _get_project(self, name):
419-
result = {}
436+
result = {'urls': {}, 'digests': {}}
420437
url = urljoin(self.base_url, '%s/json' % quote(name))
421438
try:
422439
resp = self.opener.open(url)
@@ -437,6 +454,10 @@ def _get_project(self, name):
437454
dist.digest = self._get_digest(info)
438455
dist.locator = self
439456
result[md.version] = dist
457+
for info in urls:
458+
url = info['url']
459+
result['urls'].setdefault(md.version, set()).add(url)
460+
result['digests'][url] = digest
440461
except Exception as e:
441462
logger.exception('JSON fetch failed: %s', e)
442463
return result
@@ -567,7 +588,7 @@ def _wait_threads(self):
567588
self._threads = []
568589

569590
def _get_project(self, name):
570-
result = {}
591+
result = {'urls': {}, 'digests': {}}
571592
with self._gplock:
572593
self.result = result
573594
self.project_name = name
@@ -774,7 +795,7 @@ def should_include(self, filename, parent):
774795
return filename.endswith(self.downloadable_extensions)
775796

776797
def _get_project(self, name):
777-
result = {}
798+
result = {'urls': {}, 'digests': {}}
778799
for root, dirs, files in os.walk(self.base_dir):
779800
for fn in files:
780801
if self.should_include(fn, root):
@@ -822,7 +843,7 @@ def get_distribution_names(self):
822843
raise NotImplementedError('Not available from this locator')
823844

824845
def _get_project(self, name):
825-
result = {}
846+
result = {'urls': {}, 'digests': {}}
826847
data = get_project_data(name)
827848
if data:
828849
for info in data.get('files', []):
@@ -843,6 +864,7 @@ def _get_project(self, name):
843864
md.dependencies = info.get('requirements', {})
844865
dist.exports = info.get('exports', {})
845866
result[dist.version] = dist
867+
result['urls'].setdefault(dist.version, set()).add(info['url'])
846868
return result
847869

848870
class DistPathLocator(Locator):
@@ -865,7 +887,10 @@ def _get_project(self, name):
865887
if dist is None:
866888
result = {}
867889
else:
868-
result = { dist.version: dist }
890+
result = {
891+
dist.version: dist,
892+
'urls': {dist.version: set([dist.source_url])}
893+
}
869894
return result
870895

871896

@@ -907,7 +932,20 @@ def _get_project(self, name):
907932
d = locator.get_project(name)
908933
if d:
909934
if self.merge:
935+
files = result.get('urls', {})
936+
digests = result.get('digests', {})
937+
# next line could overwrite result['urls'], result['digests']
910938
result.update(d)
939+
df = result.get('urls')
940+
if files and df:
941+
for k, v in files.items():
942+
if k in df:
943+
df[k] |= v
944+
else:
945+
df[k] = v
946+
dd = result.get('digests')
947+
if digests and dd:
948+
dd.update(digests)
911949
else:
912950
# See issue #18. If any dists are found and we're looking
913951
# for specific constraints, we only return something if
@@ -1071,7 +1109,8 @@ def try_to_replace(self, provider, other, problems):
10711109
unmatched.add(s)
10721110
if unmatched:
10731111
# can't replace other with provider
1074-
problems.add(('cantreplace', provider, other, unmatched))
1112+
problems.add(('cantreplace', provider, other,
1113+
frozenset(unmatched)))
10751114
result = False
10761115
else:
10771116
# can replace other with provider

pip/_vendor/distlib/manifest.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,10 @@ def process_directive(self, directive):
147147

148148
elif action == 'exclude':
149149
for pattern in patterns:
150-
if not self._exclude_pattern(pattern, anchor=True):
151-
logger.warning('no previously-included files '
152-
'found matching %r', pattern)
150+
found = self._exclude_pattern(pattern, anchor=True)
151+
#if not found:
152+
# logger.warning('no previously-included files '
153+
# 'found matching %r', pattern)
153154

154155
elif action == 'global-include':
155156
for pattern in patterns:
@@ -159,10 +160,11 @@ def process_directive(self, directive):
159160

160161
elif action == 'global-exclude':
161162
for pattern in patterns:
162-
if not self._exclude_pattern(pattern, anchor=False):
163-
logger.warning('no previously-included files '
164-
'matching %r found anywhere in '
165-
'distribution', pattern)
163+
found = self._exclude_pattern(pattern, anchor=False)
164+
#if not found:
165+
# logger.warning('no previously-included files '
166+
# 'matching %r found anywhere in '
167+
# 'distribution', pattern)
166168

167169
elif action == 'recursive-include':
168170
for pattern in patterns:
@@ -172,10 +174,11 @@ def process_directive(self, directive):
172174

173175
elif action == 'recursive-exclude':
174176
for pattern in patterns:
175-
if not self._exclude_pattern(pattern, prefix=thedir):
176-
logger.warning('no previously-included files '
177-
'matching %r found under directory %r',
178-
pattern, thedir)
177+
found = self._exclude_pattern(pattern, prefix=thedir)
178+
#if not found:
179+
# logger.warning('no previously-included files '
180+
# 'matching %r found under directory %r',
181+
# pattern, thedir)
179182

180183
elif action == 'graft':
181184
if not self._include_pattern(None, prefix=dirpattern):

0 commit comments

Comments
 (0)