[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