[Python-checkins] r61630 - sandbox/trunk/bootstrap sandbox/trunk/bootstrap/bootstrap.py
guido.van.rossum
python-checkins at python.org
Wed Mar 19 18:12:08 CET 2008
Author: guido.van.rossum
Date: Wed Mar 19 18:12:08 2008
New Revision: 61630
Added:
sandbox/trunk/bootstrap/
sandbox/trunk/bootstrap/bootstrap.py
Log:
Prototype bootstrap module.
Added: sandbox/trunk/bootstrap/bootstrap.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/bootstrap/bootstrap.py Wed Mar 19 18:12:08 2008
@@ -0,0 +1,197 @@
+"""Bootstrap for package loaders.
+
+This allows automatic downloading and installation of pure-Python
+packages that have a setup.py file that depends on distutils only. It
+supports zip and tar files, the latter uncompressed or compressed with
+gzip or bz2.
+"""
+
+import os
+import sys
+import glob
+import getopt
+import shutil
+import urllib
+import tarfile
+import zipfile
+import tempfile
+
+def main(args=None):
+ """Main function invoked by python -m bootstrap ...
+
+ Command line flags:
+ (None)
+
+ Positional command line arguments:
+ url: the URL to download
+ """
+ if args is None:
+ args = sys.argv[1:]
+
+ try:
+ opts, args = getopt.gnu_getopt(args, "")
+ except getopt.GetoptError, err:
+ print >>sys.stderr, err
+ sys.exit(2)
+
+ if not args:
+ print >>sys.stderr, "Please specify a URL on the command line."
+ sys.exit(2)
+ url = args[0]
+ if args[1:]:
+ print >>sys.stderr, "Too many arguments. Only one URL is allowed."
+ sys.exit(2)
+
+ zip = False
+ tar = False
+ compression = None
+ if url.endswith(".zip"):
+ zip = True
+ elif url.endswith(".tgz") or url.endswith(".tar.gz"):
+ tar = True
+ compression = "gz"
+ elif url.endswith(".tar.bz2"):
+ tar = True
+ compression = "bz2"
+ elif url.endswith(".tar"):
+ tar = True
+ else:
+ print >>sys.stderr, "Can't figure out download type; expected .zip, .tar, .tar.gz, .tar.bz2, or .tgz"
+ sys.exit(2)
+
+ try:
+ tempstream = tempfile.TemporaryFile()
+ except Exception, err:
+ print >>sys.stderr, "Can't create temporary file; %s: %s" % (err.__class__.__name__, err)
+ sys.exit(1)
+
+ try:
+ try:
+ tempdir = tempfile.mkdtemp()
+ except Exception, err:
+ print >>sys.stderr, "Can't create temporary directory; %s: %s" % (err.__class__.__name__, err)
+ sys.exit(1)
+
+ try:
+ try:
+ netstream = urllib.urlopen(url)
+ code = 200
+ if hasattr(netstream, "getcode"):
+ code = netstream.getcode()
+ if not 200 <= code < 300:
+ raise ValueError("HTTP Error code %s" % code)
+ except Exception, err:
+ print >>sys.stderr, "Can't open URL; %s: %s" % (err.__class__.__name__, err)
+ sys.exit(1)
+
+ BUFSIZE = 2**13 # 8KB
+ size = 0
+ while True:
+ data = netstream.read(BUFSIZE)
+ if not data:
+ break
+ tempstream.write(data)
+ size += len(data)
+ netstream.close()
+ print "Downloaded %d bytes." % size
+
+ tempstream.seek(0)
+ if zip:
+ unpack_zip(tempstream, tempdir)
+ elif tar:
+ unpack_tar(tempstream, compression, tempdir)
+ else:
+ assert False, "Neither zip nor tar (should have been caught earlier)."
+
+ print "Extracted all into", tempdir
+ run_setup(tempdir)
+
+ finally:
+ shutil.rmtree(tempdir)
+
+ finally:
+ tempstream.close()
+
+def unpack_tar(stream, compression, destdir):
+ mode = "r|"
+ if compression:
+ mode += compression
+ tarstream = tarfile.open("", mode=mode, fileobj=stream)
+ try:
+ for tarinfo in tarstream:
+ name = tarinfo.name
+ if bad_name(name):
+ print "%s: skipping" % name
+ continue
+ print "%s: extracting" % name
+ filestream = tarstream.extractfile(tarinfo)
+ data = filestream.read()
+ filestream.close()
+ save_file(data, destdir, name)
+ finally:
+ tarstream.close()
+
+def unpack_zip(stream, destdir):
+ zipstream = zipfile.ZipFile(stream, mode="r")
+ try:
+ for zipinfo in zipstream.infolist():
+ name = zipinfo.filename
+ if bad_name(name):
+ print "%s: skipping" % name
+ continue
+ print "%s: extracting" % name
+ data = zipstream.read(name)
+ save_file(data, destdir, name)
+ finally:
+ zipstream.close()
+
+def bad_name(name):
+ return ("\\" in name or name.startswith("/") or name.endswith("/") or
+ ".." in name or name.endswith("/."))
+
+def save_file(data, destdir, name):
+ target = os.path.join(destdir, name)
+ dirname = os.path.dirname(target)
+ if not os.path.isdir(dirname):
+ try:
+ os.makedirs(dirname)
+ except os.error, err:
+ print >>sys.stderr, "Can't create extraction directory: %s" % str(err)
+ return
+ try:
+ wstream = open(target, "wb")
+ except IOError, err:
+ print >>sys.stderr, "Can't create extraction target file: %s" % str(err)
+ return
+ try:
+ wstream.write(data)
+ finally:
+ wstream.close()
+
+def run_setup(destdir):
+ setup_py = os.path.join(destdir, "setup.py")
+ if not os.path.exists(setup_py):
+ setups = glob.glob(os.path.join(destdir, "*/setup.py"))
+ if not setups:
+ print >>sys.stderr, "Can't find a setup.py file in there"
+ sys.exit(1)
+ if len(setups) > 1:
+ print >>sys.stderr, "Too many setup.py files found"
+ sys.exit(1)
+ setup_py = setups[0]
+ destdir = os.path.dirname(setup_py)
+ namespace = {"__name__": "__main__", "__file__": setup_py}
+ save_sys_argv = sys.argv
+ save_pwd = os.getcwd()
+ try:
+ sys.argv = [setup_py, "install"]
+ os.chdir(destdir)
+ print "Running setup_py install"
+ execfile(setup_py, namespace)
+ finally:
+ sys.argv = save_sys_argv
+ os.chdir(save_pwd)
+ print "Running setup.py completed"
+
+if __name__ == "__main__":
+ main()
More information about the Python-checkins
mailing list