[Checkins] SVN: zc.buildout/branches/tlotze-download-api/src/zc/buildout/download.py first shot at a download API, still lacks tests, doesn't handle offline mode yet

Thomas Lotze tl at gocept.com
Mon May 18 15:24:11 EDT 2009


Log message for revision 100099:
  first shot at a download API, still lacks tests, doesn't handle offline mode yet
  

Changed:
  A   zc.buildout/branches/tlotze-download-api/src/zc/buildout/download.py

-=-
Added: zc.buildout/branches/tlotze-download-api/src/zc/buildout/download.py
===================================================================
--- zc.buildout/branches/tlotze-download-api/src/zc/buildout/download.py	                        (rev 0)
+++ zc.buildout/branches/tlotze-download-api/src/zc/buildout/download.py	2009-05-18 19:24:10 UTC (rev 100099)
@@ -0,0 +1,138 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Buildout download infrastructure"""
+
+try:
+    from hashlib import md5
+except ImportError:
+    from md5 import new as md5
+import os
+import os.path
+import shutil
+import urllib
+import urlparse
+
+
+class Download(object):
+    """Configurable download utility.
+
+    Handles the download cache and offline mode.
+
+    """
+
+    def __init__(self, buildout,
+                 use_cache=True, namespace=None, hash_name=False):
+        self.buildout = buildout
+        self.set_cache(use_cache, namespace, hash_name)
+
+    def set_cache(self, use_cache=True, namespace=None, hash_name=False):
+        """Configure the caching properties.
+
+        use_cache: whether to use the cache at all
+        namespace: namespace directory to use inside the cache
+        hash_name: whether to use a hash of the URL as cache file name
+
+        """
+        self.use_cache = use_cache
+        self.namespace = namespace
+        self.hash_name = hash_name
+        if use_cache and 'download-cache' in self.buildout:
+            self.cache = os.path.join(self.buildout['download-cache'],
+                                      namespace or '')
+        else:
+            self.cache = None
+
+    def __call__(self, url, md5sum=None, path=None):
+        """Download a file according to the utility's configuration.
+
+        url: URL to download
+        md5sum: MD5 checksum to match
+        path: where to place the downloaded file
+
+        Returns the path to the downloaded file.
+
+        """
+        if self.cache is None:
+            return self.download(url, md5sum, path)
+
+        cached_path = self.download_cached(url, md5sum)
+        if (path is None
+            or os.path.abspath(path) == os.path.abspath(cached_path)):
+            return cached_path
+
+        try:
+            os.link(cached_path, path)
+        except (AttributeError, OSError):
+            shutil.copyfile(cached_path, path)
+        return path
+
+    def download_cached(self, url, md5sum=None):
+        """Download a file to the cache, assuming the cache was configured.
+
+        See __call__.
+
+        """
+        cached_path = os.path.join(self.cache, self.filename(url))
+        if os.path.isfile(cached_path):
+            if not check_md5sum(cached_path, md5sum):
+                raise ValueError('MD5 checksum mismatch for cached download '
+                                 'from %r at %r' % (url, cached_path))
+        return self.download(url, md5sum, cached_path)
+
+    def download(self, url, md5sum=None, path=None):
+        """Download a file to a given path.
+
+        If path is None, download to a temporary file.
+
+        See __call__.
+
+        """
+        path, headers = urllib.urlretrieve(url, path)
+        if not check_md5sum(path, md5sum):
+            raise ValueError('MD5 checksum mismatch downloading %r' % url)
+        return path
+
+    def filename(self, url):
+        """Determine a file name from a URL according to the configuration.
+
+        """
+        if self.hash_name:
+            return md5(url).hexdigest()
+        else:
+            for name in reversed(urlparse.urlparse(url).path.split('/')):
+                if name:
+                    return name
+            else:
+                return 'default'
+
+
+def check_md5sum(path, md5sum):
+    """Tell whether the MD5 checksum of the file at path matches.
+
+    No checksum being given is considered a match.
+
+    """
+    if md5sum is None:
+        return True
+
+    f = open(path)
+    checksum = md5()
+    try:
+        chunk = f.read(2**16)
+        while chunk:
+            checksum.update(chunk)
+            chunk = f.read(2**16)
+        return checksum.hexdigest() == md5sum
+    finally:
+        f.close()


Property changes on: zc.buildout/branches/tlotze-download-api/src/zc/buildout/download.py
___________________________________________________________________
Added: svn:keywords
   + Id Rev Date
Added: svn:eol-style
   + native



More information about the Checkins mailing list