[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