[Python-checkins] distutils2: Add an unpack method to distributions.

tarek.ziade python-checkins at python.org
Sun Aug 8 11:50:46 CEST 2010


tarek.ziade pushed ec647abcb17d to distutils2:

http://hg.python.org/distutils2/rev/ec647abcb17d
changeset:   459:ec647abcb17d
user:        Alexis Metaireau <ametaireau at gmail.com>
date:        Mon Jul 26 17:58:36 2010 +0200
summary:     Add an unpack method to distributions.
files:       src/distutils2/index/dist.py, src/distutils2/util.py

diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py
--- a/src/distutils2/index/dist.py
+++ b/src/distutils2/index/dist.py
@@ -10,10 +10,13 @@
 distributions contains download related informations.
 
 """
+import mimetypes
 import re
+import tarfile
 import tempfile
 import urllib
 import urlparse
+import zipfile
 
 try:
     import hashlib
@@ -25,6 +28,7 @@
                                      CantParseArchiveName)
 from distutils2.version import suggest_normalized_version, NormalizedVersion
 from distutils2.metadata import DistributionMetadata
+from distutils2.util import untar_file, unzip_file, splitext
 
 __all__ = ['ReleaseInfo', 'DistInfo', 'ReleasesList', 'get_infos_from_url']
 
@@ -247,6 +251,7 @@
         """Download the distribution to a path, and return it.
 
         If the path is given in path, use this, otherwise, generates a new one
+        Return the download location.
         """
         if path is None:
             path = tempfile.mkdtemp()
@@ -261,6 +266,30 @@
             self._check_md5(filename)
         return self.downloaded_location
 
+    def unpack(self, path=None):
+        """Unpack the distribution to the given path.
+        
+        If not destination is given, creates a temporary location.
+
+        Returns the location of the extracted files (root).
+        """
+        if path is None:
+            path = tempfile.mkdtemp()
+        
+        filename = self.download()
+        content_type = mimetypes.guess_type(filename)[0]
+ 
+        if (content_type == 'application/zip'
+            or filename.endswith('.zip')
+            or filename.endswith('.pybundle')
+            or zipfile.is_zipfile(filename)):
+            unzip_file(filename, path, flatten=not filename.endswith('.pybundle'))
+        elif (content_type == 'application/x-gzip'
+              or tarfile.is_tarfile(filename)
+              or splitext(filename)[1].lower() in ('.tar', '.tar.gz', '.tar.bz2', '.tgz', '.tbz')):
+            untar_file(filename, path)
+        return path
+
     def _check_md5(self, filename):
         """Check that the md5 checksum of the given file matches the one in
         url param"""
@@ -275,7 +304,7 @@
                     % (hashval.hexdigest(), expected_hashval))
 
     def __repr__(self):
-        return "%s %s %s" % (
+        return "<%s %s %s>" % (
             self.release.name, self.release.version, self.dist_type or "")
 
 
diff --git a/src/distutils2/util.py b/src/distutils2/util.py
--- a/src/distutils2/util.py
+++ b/src/distutils2/util.py
@@ -5,10 +5,14 @@
 
 __revision__ = "$Id: util.py 77761 2010-01-26 22:46:15Z tarek.ziade $"
 
+import os
+import posixpath
+import re
+import string
 import sys
-import os
-import string
-import re
+import shutil
+import tarfile
+import zipfile
 from copy import copy
 from fnmatch import fnmatchcase
 
@@ -688,3 +692,116 @@
         """ Issues a call to util.run_2to3. """
         return run_2to3(files, doctests_only, self.fixer_names,
                         self.options, self.explicit)
+
+
+def splitext(path):
+    """Like os.path.splitext, but take off .tar too"""
+    base, ext = posixpath.splitext(path)
+    if base.lower().endswith('.tar'):
+        ext = base[-4:] + ext
+        base = base[:-4]
+    return base, ext
+
+
+def unzip_file(filename, location, flatten=True):
+    """Unzip the file (zip file located at filename) to the destination
+    location"""
+    if not os.path.exists(location):
+        os.makedirs(location)
+    zipfp = open(filename, 'rb')
+    try:
+        zip = zipfile.ZipFile(zipfp)
+        leading = has_leading_dir(zip.namelist()) and flatten
+        for name in zip.namelist():
+            data = zip.read(name)
+            fn = name
+            if leading:
+                fn = split_leading_dir(name)[1]
+            fn = os.path.join(location, fn)
+            dir = os.path.dirname(fn)
+            if not os.path.exists(dir):
+                os.makedirs(dir)
+            if fn.endswith('/') or fn.endswith('\\'):
+                # A directory
+                if not os.path.exists(fn):
+                    os.makedirs(fn)
+            else:
+                fp = open(fn, 'wb')
+                try:
+                    fp.write(data)
+                finally:
+                    fp.close()
+    finally:
+        zipfp.close()
+
+
+def untar_file(filename, location):
+    """Untar the file (tar file located at filename) to the destination
+    location
+    """
+    if not os.path.exists(location):
+        os.makedirs(location)
+    if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'):
+        mode = 'r:gz'
+    elif (filename.lower().endswith('.bz2') 
+          or filename.lower().endswith('.tbz')):
+        mode = 'r:bz2'
+    elif filename.lower().endswith('.tar'):
+        mode = 'r'
+    else:
+        mode = 'r:*'
+    tar = tarfile.open(filename, mode)
+    try:
+        leading = has_leading_dir([member.name for member in tar.getmembers()])
+        for member in tar.getmembers():
+            fn = member.name
+            if leading:
+                fn = split_leading_dir(fn)[1]
+            path = os.path.join(location, fn)
+            if member.isdir():
+                if not os.path.exists(path):
+                    os.makedirs(path)
+            else:
+                try:
+                    fp = tar.extractfile(member)
+                except (KeyError, AttributeError), e:
+                    # Some corrupt tar files seem to produce this
+                    # (specifically bad symlinks)
+                    continue
+                if not os.path.exists(os.path.dirname(path)):
+                    os.makedirs(os.path.dirname(path))
+                destfp = open(path, 'wb')
+                try:
+                    shutil.copyfileobj(fp, destfp)
+                finally:
+                    destfp.close()
+                fp.close()
+    finally:
+        tar.close()
+
+
+def has_leading_dir(paths):
+    """Returns true if all the paths have the same leading path name
+    (i.e., everything is in one subdirectory in an archive)"""
+    common_prefix = None
+    for path in paths:
+        prefix, rest = split_leading_dir(path)
+        if not prefix:
+            return False
+        elif common_prefix is None:
+            common_prefix = prefix
+        elif prefix != common_prefix:
+            return False
+    return True
+
+
+def split_leading_dir(path):
+    path = str(path)
+    path = path.lstrip('/').lstrip('\\')
+    if '/' in path and (('\\' in path and path.find('/') < path.find('\\'))
+                        or '\\' not in path):
+        return path.split('/', 1)
+    elif '\\' in path:
+        return path.split('\\', 1)
+    else:
+        return path, ''

--
Repository URL: http://hg.python.org/distutils2


More information about the Python-checkins mailing list