aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2013-07-15 12:06:54 -0400
committerJason R. Coombs <jaraco@jaraco.com>2013-07-15 12:06:54 -0400
commit95bf90f3dae23f3d672450d94adfbcc55f9e252a (patch)
tree85ebf94a4deff650ded8026487773ca5b67f9a91
parentbbd4994b81eefd1a652f3fa9dca7d6e93107b392 (diff)
downloadexternal_python_setuptools-95bf90f3dae23f3d672450d94adfbcc55f9e252a.tar.gz
external_python_setuptools-95bf90f3dae23f3d672450d94adfbcc55f9e252a.tar.bz2
external_python_setuptools-95bf90f3dae23f3d672450d94adfbcc55f9e252a.zip
Extracted hash-checking functionality into its own classes. Hashes are no longer checked when the proper pattern isn't detected. Fixes #42.
-rwxr-xr-xsetuptools/package_index.py99
1 files changed, 73 insertions, 26 deletions
diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index e29a142c..4c4a647d 100755
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -25,7 +25,6 @@ PYPI_MD5 = re.compile(
)
URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match
EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split()
-_HASH_RE = re.compile(r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)')
__all__ = [
'PackageIndex', 'distros_for_url', 'parse_bdist_wininst',
@@ -193,6 +192,61 @@ user_agent = "Python-urllib/%s setuptools/%s" % (
sys.version[:3], require('setuptools')[0].version
)
+class ContentChecker(object):
+ """
+ A null content checker that defines the interface for checking content
+ """
+ def feed(self, block):
+ """
+ Feed a block of data to the hash.
+ """
+ return
+
+ def check(self):
+ """
+ Check the hash. Return False if validation fails.
+ """
+ return True
+
+ def report(self, reporter, template):
+ """
+ Call reporter with information about the checker (hash name)
+ substituted into the template.
+ """
+ return
+
+class HashChecker(ContentChecker):
+ pattern = re.compile(
+ r'(?P<hash_name>sha1|sha224|sha384|sha256|sha512|md5)='
+ r'(?P<expected>[a-f0-9]+)'
+ )
+
+ def __init__(self, hash_name, expected):
+ self.hash = hashlib.new(hash_name)
+ self.expected = expected
+
+ @classmethod
+ def from_url(cls, url):
+ "Construct a (possibly null) ContentChecker from a URL"
+ fragment = urlparse(url)[-1]
+ if not fragment:
+ return ContentChecker()
+ match = cls.pattern.search(fragment)
+ if not match:
+ return ContentChecker()
+ return cls(**match.groupdict())
+
+ def feed(self, block):
+ self.hash.update(block)
+
+ def check(self):
+ return self.hash.hexdigest() == self.expected
+
+ def report(self, reporter, template):
+ msg = template % self.hash.name
+ return reporter(msg)
+
+
class PackageIndex(Environment):
"""A distribution index that scans web pages for download URLs"""
@@ -385,20 +439,20 @@ class PackageIndex(Environment):
- def check_hash(self, cs, info, filename, tfp):
- match = _HASH_RE.search(info)
- if match:
- hash_name = match.group(1)
- hash_data = match.group(2)
- self.debug("Validating %s checksum for %s", hash_name, filename)
- if cs.hexdigest() != hash_data:
- tfp.close()
- os.unlink(filename)
- raise DistutilsError(
- "%s validation failed for %s; "
- "possible download problem?" % (
- hash_name, os.path.basename(filename))
- )
+ def check_hash(self, checker, filename, tfp):
+ """
+ checker is a ContentChecker
+ """
+ checker.report(self.debug,
+ "Validating %%s checksum for %s" % filename)
+ if not checker.valid():
+ tfp.close()
+ os.unlink(filename)
+ raise DistutilsError(
+ "%s validation failed for %s; "
+ "possible download problem?" % (
+ checker.hash.name, os.path.basename(filename))
+ )
def add_find_links(self, urls):
"""Add `urls` to the list that will be prescanned for searches"""
@@ -600,14 +654,9 @@ class PackageIndex(Environment):
def _download_to(self, url, filename):
self.info("Downloading %s", url)
# Download the file
- fp, tfp, cs, info = None, None, None, None
+ fp, tfp, info = None, None, None
try:
- if '#' in url:
- url, info = url.split('#', 1)
- hmatch = _HASH_RE.search(info)
- hash_name = hmatch.group(1)
- hash_data = hmatch.group(2)
- cs = hashlib.new(hash_name)
+ checker = HashChecker.from_url(url)
fp = self.open_url(url)
if isinstance(fp, HTTPError):
raise DistutilsError(
@@ -626,14 +675,13 @@ class PackageIndex(Environment):
while True:
block = fp.read(bs)
if block:
- if cs is not None:
- cs.update(block)
+ checker.feed(block)
tfp.write(block)
blocknum += 1
self.reporthook(url, filename, blocknum, bs, size)
else:
break
- if info: self.check_hash(cs, info, filename, tfp)
+ self.check_hash(checker, filename, tfp)
return headers
finally:
if fp: fp.close()
@@ -642,7 +690,6 @@ class PackageIndex(Environment):
def reporthook(self, url, filename, blocknum, blksize, size):
pass # no-op
-
def open_url(self, url, warning=None):
if url.startswith('file:'):
return local_open(url)