From 3f455e161b10d8c40bb15066fe95915bb729a0af Mon Sep 17 00:00:00 2001 From: utahta Date: Sat, 13 Nov 2010 03:25:56 +0900 Subject: [PATCH] update 0.5 --- ChangeLog | 8 + README.rst | 48 +-- pythonbrew | 696 ------------------------------- pythonbrew-install | 124 ++++++ pythonbrew.py | 696 ------------------------------- pythonbrew/__init__.py | 19 + pythonbrew/basecommand.py | 38 ++ pythonbrew/baseparser.py | 13 + pythonbrew/commands/__init__.py | 0 pythonbrew/commands/clean.py | 19 + pythonbrew/commands/help.py | 27 ++ pythonbrew/commands/install.py | 184 ++++++++ pythonbrew/commands/installed.py | 26 ++ pythonbrew/commands/list.py | 34 ++ pythonbrew/commands/off.py | 12 + pythonbrew/commands/switch.py | 41 ++ pythonbrew/commands/uninstall.py | 29 ++ pythonbrew/commands/update.py | 64 +++ pythonbrew/commands/version.py | 13 + pythonbrew/define.py | 56 +++ pythonbrew/downloader.py | 39 ++ pythonbrew/exceptions.py | 3 + pythonbrew/installer.py | 46 ++ pythonbrew/log.py | 43 ++ pythonbrew/util.py | 111 +++++ pythonbrew_install.py | 3 + 26 files changed, 969 insertions(+), 1423 deletions(-) create mode 100644 ChangeLog delete mode 100755 pythonbrew create mode 100755 pythonbrew-install delete mode 100755 pythonbrew.py create mode 100644 pythonbrew/__init__.py create mode 100644 pythonbrew/basecommand.py create mode 100644 pythonbrew/baseparser.py create mode 100644 pythonbrew/commands/__init__.py create mode 100644 pythonbrew/commands/clean.py create mode 100644 pythonbrew/commands/help.py create mode 100644 pythonbrew/commands/install.py create mode 100644 pythonbrew/commands/installed.py create mode 100644 pythonbrew/commands/list.py create mode 100644 pythonbrew/commands/off.py create mode 100644 pythonbrew/commands/switch.py create mode 100644 pythonbrew/commands/uninstall.py create mode 100644 pythonbrew/commands/update.py create mode 100644 pythonbrew/commands/version.py create mode 100644 pythonbrew/define.py create mode 100644 pythonbrew/downloader.py create mode 100644 pythonbrew/exceptions.py create mode 100644 pythonbrew/installer.py create mode 100644 pythonbrew/log.py create mode 100644 pythonbrew/util.py create mode 100644 pythonbrew_install.py diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..2255893 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,8 @@ +* 0.5 + - This version is incompatible with the 0.4 or less. + - Split the file. + - Added pythonbrew-install script. + - Added pybrew script. This is a symbolic pythonbrew. + - Rename `search` to `list`. + - Removed `init`. + - Added syntax that `pythonbrew command ` diff --git a/README.rst b/README.rst index 8b91d9b..86a097b 100644 --- a/README.rst +++ b/README.rst @@ -13,48 +13,42 @@ Following python version is required to use pythonbrew: The recommended way to download and install pythonbrew is to run these statements in your shell.:: - curl -LO http://github.com/utahta/pythonbrew/raw/master/pythonbrew - chmod +x pythonbrew - ./pythonbrew install + curl -LO http://github.com/utahta/pythonbrew/raw/master/pythonbrew-install + chmod +x pythonbrew-install + ./pythonbrew-install -After that, pythonbrew installs itself to ~/python/pythonbrew/bin, and you should follow the instruction on screen to setup your .bashrc or .cshrc to put it in your PATH. +After that, pythonbrew installs itself to ~/.pythonbrew/bin, and you should follow the instruction on screen to setup your .bashrc or .cshrc to put it in your PATH. If you need to install pythonbrew into somewhere else, you can do that by setting a PYTHONBREW_ROOT environment variable.:: export PYTHONBREW_ROOT=/path/to/pythonbrew - ./pythonbrew install + ./pythonbrew-install Usage ===== pythonbrew command [options] - -Initialize:: - - pythonbrew init Install some Pythons:: - pythonbrew install Python-3.1.2 - pythonbrew install Python-2.6.6 + pythonbrew install 2.6.6 + pythonbrew install Python-2.5.5 pythonbrew install --configure="CC=gcc_4.1" Python-2.6.6 pythonbrew install --no-setuptools Python-2.6.6 - pythonbrew install http://www.python.org/ftp/python/2.6.6/Python-2.6.6.tgz - pythonbrew install /path/to/Python-2.6.6.tgz Switch python in the $PATH:: - pythonbrew switch Python-2.6.6 - pythonbrew switch /path/to/python + pythonbrew switch 2.6.6 + pythonbrew switch Python-2.5.5 -Search python packages:: +List the available install versions of Python:: - pythonbrew search Python-2.6 - pythonbrew search Python-3 + pythonbrew list 2.6 + pythonbrew list 3.0 Uninstall some Pythons:: - pythonbrew uninstall Python-2.6.6 + pythonbrew uninstall 2.6.6 Remove stale source folders and archives:: @@ -75,12 +69,7 @@ Show version:: COMMANDS ======== -init - Run this once to setup the pythonbrew directory ready for installing. - - pythons into. Run it again if you decide to change PYTHONBREW_ROOT. - -install Python- +install Build and install the given version of Python. Setuptools and pip is automatically installed. @@ -90,14 +79,11 @@ install Python- installed List the installed versions of python. -switch Python- +switch Switch to the given version. -switch /path/to/python - Switch to the given version of python. - -search Python- - Search Python packages. +list + List the available install version of python. uninstall Python- Uninstall the given version of python. diff --git a/pythonbrew b/pythonbrew deleted file mode 100755 index 70525cf..0000000 --- a/pythonbrew +++ /dev/null @@ -1,696 +0,0 @@ -#!/usr/bin/env python -# vim:fileencoding=utf-8 - -import os -import sys -import urllib -import errno -import re -import shutil -import filecmp -import subprocess -import tempfile -from HTMLParser import HTMLParser -from optparse import OptionParser - -VERSION = "0.4" -if os.environ.has_key("PYTHONBREW_ROOT"): - ROOT = os.environ["PYTHONBREW_ROOT"] -else: - ROOT = "%s/python/pythonbrew" % os.environ["HOME"] -PYTHONDLSITE = "http://www.python.org/ftp/python/%s/%s" -DISTRIBUTE_SETUP_DLSITE = "http://python-distribute.org/distribute_setup.py" -PYTHONBREW_DLSITE = "http://github.com/utahta/pythonbrew/raw/master/pythonbrew" - -PATH_PYTHONS = "%s/pythons" % ROOT -PATH_BUILD = "%s/build" % ROOT -PATH_DISTS = "%s/dists" % ROOT -PATH_ETC = "%s/etc" % ROOT -PATH_BIN = "%s/bin" % ROOT - -parser = OptionParser(usage="%prog COMMAND [OPTIONS]", - version=VERSION, - add_help_option=False) -parser.add_option( - '-h', '--help', - dest='help', - action='store_true', - help='Show help') -parser.disable_interspersed_args() - -command_dict = {} -def add_command(command): - command_dict[command.name] = command - -#---------------------------------------------------- -# exception -#---------------------------------------------------- -class BuildingException(Exception): - """General exception during building""" - -#---------------------------------------------------- -# util -#---------------------------------------------------- -def size_format(b): - kb = 1000 - mb = kb*kb - b = float(b) - if b >= mb: - return "%.1fMb" % (b/mb) - if b >= kb: - return "%.1fKb" % (b/kb) - return "%.0fbytes" % (b) - -def is_url(name): - if ':' not in name: - return False - scheme = name.split(':', 1)[0].lower() - return scheme in ['http', 'https', 'file', 'ftp'] - -def splitext(name): - base, ext = os.path.splitext(name) - if base.lower().endswith('.tar'): - ext = base[-4:] + ext - base = base[:-4] - return base, ext - -def is_archive_file(name): - ext = splitext(name)[1].lower() - archives = ('.zip', '.tar.gz', '.tar.bz2', '.tgz', '.tar') - if ext in archives: - return True - return False - -def makedirs(path): - try: - os.makedirs(path) - except OSError, (e, es): - if errno.EEXIST != e: - raise - -def symlink(src, dst): - try: - os.symlink(src, dst) - except: - pass - -def unlink(path): - try: - os.unlink(path) - except OSError, (e, es): - if errno.ENOENT != e: - raise - -def rm_r(path): - """like rm -r command.""" - if os.path.isdir(path): - shutil.rmtree(path) - else: - unlink(path) - -def off(): - for root, dirs, files in os.walk(PATH_BIN): - for f in files: - if f == "pythonbrew": - continue - unlink("%s/%s" % (root, f)) - unlink("%s/current" % PATH_PYTHONS) - -def copy_myself(path): - (fd, src) = tempfile.mkstemp() - fp = file(path, "r") - line = fp.readline() - if line.startswith("#!"): - os.write(fd, "#!%s\n" % os.path.realpath(sys.executable)) - else: - os.write(fd, line) - os.write(fd, fp.read()) - os.close(fd) - fp.close() - - dist = "%s/pythonbrew" % PATH_BIN - if os.path.isfile(src) and os.path.isfile(dist): - if filecmp.cmp(src, dist): - unlink(src) - return (False, dist) - makedirs(PATH_BIN) - shutil.copy(src, dist) - os.chmod(dist, 0755) - unlink(src) - return (True, dist) - -#---------------------------------------------------- -# classes -#---------------------------------------------------- -class Downloader(object): - def __init__(self): - self._msg = "" - self._last_msg = "" - self._bytes = 0.0 - - def download(self, msg, url, path): - self._msg = msg - self._bytes = 0 - urllib.urlretrieve(url, path, self._download_progress) - print " downloaded." - - def _download_progress(self, block, blockbytes, maxbytes): - self._bytes += float(blockbytes) - if self._bytes >= maxbytes: - self._bytes = maxbytes - percent = (self._bytes / maxbytes) * 100 - max_size = size_format(maxbytes) - now_size = size_format(self._bytes) - now_msg = "\rDownloading %s (%s): %3i%% %s" % (self._msg, max_size, percent, now_size) - padding = " " * (len(self._last_msg) - len(now_msg)) - sys.stdout.write("%s%s" % (now_msg, padding)) - sys.stdout.flush() - self._last_msg = now_msg - -class Subprocess(object): - def __init__(self, log=None, shell=False, cwd=None, print_cmd=True): - self._log = log - self._shell = shell - self._cwd = cwd - self._print_cmd = print_cmd - - def chdir(self, cwd): - self._cwd = cwd - - def check_call(self, cmd, shell=None, cwd=None): - if shell: - self._shell = shell - if cwd: - self._cwd = cwd - if self._print_cmd: - print cmd - if self._log: - cmd = "(%s) >> '%s' 2>&1" % (cmd, self._log) - retcode = subprocess.call(cmd, shell=self._shell, cwd=self._cwd) - if retcode != 0: - raise BuildingException() - -class PythonVersionParser(HTMLParser): - def __init__(self): - HTMLParser.__init__(self) - self._versions = [] - self._re = re.compile("^(\d+\.\d+(\..*)?)/$") - - def handle_starttag(self, tag, attrs): - if tag == "a": - attrs = dict(attrs) - if "href" in attrs: - m = self._re.search(attrs["href"]) - if m: - self._versions.append(m.group(1)) - - def get_sorted_versions(self): - return sorted(self._versions) - -class PythonPackages(object): - def __init__(self): - parser = PythonVersionParser() - fp = urllib.urlopen("http://www.python.org/ftp/python/") - parser.feed(fp.read()) - fp.close() - parser.close() - self._versions = parser.get_sorted_versions() - - def has_version(self, version): - return version in self._versions - - def get_packages(self): - return ["Python-%s" % v for v in self._versions] - -#---------------------------------------------------- -# commands -#---------------------------------------------------- -class Command(object): - name = None - usage = None - summary = "" - - def __init__(self): - self.parser = OptionParser(usage=self.usage, - prog='%s %s' % (os.path.basename(sys.argv[0]), self.name)) - - def run(self, args): - options, args = self.parser.parse_args(args) - self.run_command(options, args[1:]) - -class HelpCommand(Command): - name = "help" - usage = "%prog [COMMAND]" - summary = "Show available commands" - - def run_command(self, options, args): - if args: - command = args[0] - if command not in command_dict: - parser.error("Unknown command: `%s`" % command) - return - command = command_dict[command] - command.parser.print_help() - return - parser.print_help() - print - print "Commands available:" - commands = [command_dict[key] for key in sorted(command_dict.keys())] - for command in commands: - print " %s: %s" % (command.name, command.summary) - print - print "Further Instructions:" - print " http://github.com/utahta/pythonbrew" - -class InitCommand(Command): - name = "init" - usage = "%prog" - summary = "Run this once to setup the pythonbrew directory ready for installing pythons into" - - def run_command(self, options, args): - makedirs(PATH_PYTHONS) - makedirs(PATH_BUILD) - makedirs(PATH_DISTS) - makedirs(PATH_ETC) - - os.system("echo 'export PATH=%s/bin:%s/current/bin:${PATH}' > %s/bashrc" % (ROOT, PATH_PYTHONS, PATH_ETC)) - os.system("echo 'setenv PATH %s/bin:%s/current/bin:$PATH' > %s/cshrc" % (ROOT, PATH_PYTHONS, PATH_ETC)) - m = re.search("(t?csh)", os.environ.get("SHELL")) - if m: - shrc = "cshrc" - yourshrc = m.group(1)+"rc" - else: - shrc = yourshrc = "bashrc" - print """ -Pythonbrew environment initiated, required directories are created under - - """+ROOT+""" - -Well-done! Congratulations! Please add the following line to the end -of your ~/."""+yourshrc+""" - - source """+PATH_ETC+"""/"""+shrc+""" - -After that, exit this shell, start a new one, and install some fresh -pythons: - - pythonbrew install Python-2.6.6 - pythonbrew install Python-2.5.5 - -For further instructions, simply run: - - pythonbrew - -The default help messages will popup and tell you what to do! - -Enjoy pythonbrew at $HOME!! -INSTRUCTION""" - -class InstallCommand(Command): - name = "install" - usage = "%prog [OPTIONS] PACKAGE_NAMES" - summary = "Build and install the given version of python" - - def __init__(self): - super(InstallCommand, self).__init__() - self.parser.add_option( - "-f", "--force", - dest="force", - action="store_true", - default=False, - help="Force installation of a Python." - ) - self.parser.add_option( - "-C", "--configure", - dest="configure", - default="", - metavar="CONFIGURE_OPTIONS", - help="Custom configure options." - ) - self.parser.add_option( - "-n", "--no-setuptools", - dest="no_setuptools", - action="store_true", - default=False, - help="Skip installation of setuptools." - ) - self._logfile = "%s/build.log" % ROOT - - def run_command(self, options, args): - if args: - # Install Python - self._install_python(args[0], options) - else: - # Install pythonbrew - self._install_myself() - - def _install_myself(self): - executable = os.path.abspath(sys.argv[0]) - (retval, dist) = copy_myself(executable) - if not retval: - print "You are already running the installed latest version of pythonbrew." - print "\n%s" % dist - sys.exit() - print """The pythonbrew is installed as: - - """+dist+""" - -You may trash the downloaded """+executable+""" from now on. - -Next, if this is the first time you've run pythonbrew installation, run: - - """+dist+""" init - -And follow the instruction on screen.""" - - def _get_package(self, name): - if not os.path.isfile(name) and not os.path.isdir(name): - if is_url(name): - basename = os.path.basename(name) - download_url = name - download_path = "%s/%s" % (PATH_DISTS, basename) - else: - m = re.search("^Python-(\d+\.\d+(\..*)?)$", name) - if not m: - print "Unknown package: `%s`" % name - sys.exit(1) - if os.path.isdir("%s/%s" % (PATH_PYTHONS, name)): - print "You are already installed `%s`." % name - sys.exit() - dist_version = m.group(1) - pkgs = PythonPackages() - if not pkgs.has_version(dist_version): - print "Package not found: `%s`" % name - sys.exit(1) - basename = "%s.tgz" % name - download_url = PYTHONDLSITE % (dist_version, basename) - download_path = "%s/%s" % (PATH_DISTS, basename) - - if os.path.isfile(download_path): - print "Use the previously fetched %s" % (download_path) - else: - try: - dl = Downloader() - dl.download( - basename, - download_url, - download_path - ) - except: - unlink(download_path) - print "\nInterrupt to abort. `%s`" % (download_url) - sys.exit(1) - # iffy - if os.path.getsize(download_path) < 1000000: - print "Failed to download. (maybe not stable version of python) `%s`" % (download_url) - unlink(download_path) - sys.exit(1) - else: - if is_archive_file(name): - basename = os.path.basename(name) - print "Copy the archive file %s to %s/%s" % (name, PATH_DISTS, basename) - shutil.copy(name, "%s/%s" % (PATH_DISTS, basename)) - else: - print "Invalid file. `%s`" % name - sys.exit(1) - return basename - - def _get_uncompress_command(self, basename): - distpath = "%s/%s" % (PATH_DISTS, basename) - if os.path.isfile(distpath): - ext = splitext(basename)[1] - if ext == ".tar.gz" or ext == ".tgz": - return "tar zxf %s" % (distpath) - elif ext == ".tar.bz2": - return "tar jxf %s" % (distpath) - elif ext == ".tar": - return "tar xf %s" % (distpath) - elif ext == ".zip": - return "unzip %s" % (distpath) - elif os.path.isdir(distpath): - return "mv %s %s/%s" % (distpath, PATH_BUILD, basename) - else: - print "File not found. `%s`" % (basename) - return "" - - def _install_python(self, dist, options): - basename = self._get_package(dist) - pkgname = splitext(basename)[0] - - install_dir = "%s/%s" % (PATH_PYTHONS, pkgname) - configure = "--prefix=%s %s" % (install_dir, options.configure) - print "Installing %s into %s" % (pkgname, install_dir); - print """This could take a while. You can run the following command on another shell to track the status: - - tail -f %s -""" % (self._logfile) - try: - s = Subprocess(log=self._logfile, shell=True, cwd=PATH_BUILD) - s.check_call(self._get_uncompress_command(basename)) - - s.chdir("%s/%s" % (PATH_BUILD, pkgname)) - s.check_call("./configure %s" % (configure)) - if options.force: - s.check_call("make") - s.check_call("make install") - else: - s.check_call("make") - s.check_call("make test") - s.check_call("make install") - except: - print """Installing %(pkgname)s failed. See %(ROOT)s/build.log to see why. - - pythonbrew install --force %(pkgname)s""" % {"pkgname":pkgname, "ROOT":ROOT} - sys.exit(1) - - # install setuptools - self._install_setuptools(pkgname, options.no_setuptools) - print """ -Installed %(pkgname)s successfully. Run the following command to switch to it. - - pythonbrew switch %(pkgname)s""" % {"pkgname":pkgname} - - def _install_setuptools(self, pkgname, no_setuptools): - if no_setuptools: - print "Skip installation setuptools." - return - if re.match("^Python-3.*", pkgname): - is_python3 = True - else: - is_python3 = False - download_url = DISTRIBUTE_SETUP_DLSITE - basename = os.path.basename(download_url) - - dl = Downloader() - dl.download(basename, download_url, "%s/%s" % (PATH_DISTS, basename)) - - if is_python3: - if os.path.isfile("%s/%s/bin/python3" % (PATH_PYTHONS, pkgname)): - pyexec = "%s/%s/bin/python3" % (PATH_PYTHONS, pkgname) - elif os.path.isfile("%s/%s/bin/python3.0" % (PATH_PYTHONS, pkgname)): - pyexec = "%s/%s/bin/python3.0" % (PATH_PYTHONS, pkgname) - else: - print "Python3 binary not found. `%s/%s`" % (PATH_PYTHONS, pkgname) - return - else: - pyexec = "%s/%s/bin/python" % (PATH_PYTHONS, pkgname) - - try: - s = Subprocess(log=self._logfile, shell=True, cwd=PATH_DISTS) - s.check_call("%s %s" % (pyexec, basename)) - if os.path.isfile("%s/%s/bin/easy_install" % (PATH_PYTHONS, pkgname)) and not is_python3: - s.check_call("%s/%s/bin/easy_install pip" % (PATH_PYTHONS, pkgname), cwd=None) - except: - print "Installing setuptools failed. See %s/build.log to see why." % (ROOT) - sys.exit(1) - -class InstalledCommand(Command): - name = "installed" - usage = "%prog" - summary = "List the installed versions of python" - - def run_command(self, options, args): - if not os.path.islink("%s/current" % PATH_PYTHONS): - cur = os.path.realpath("%s/bin/python" % ROOT) - print "%s (*)" % cur - elif os.path.islink("%s/current" % PATH_PYTHONS): - cur = os.path.basename(os.path.realpath("%s/current" % PATH_PYTHONS)) - else: - cur = "" - for d in sorted(os.listdir("%s/" % PATH_PYTHONS)): - if d == "current": - continue - if cur == d: - print "%s (*)" % cur - else: - print "%s" % (d) - -class SwitchCommand(Command): - name = "switch" - usage = "%prog PACKAGE" - summary = "Switch to the given version" - - def run_command(self, options, args): - if args: - dist = args[0] - distdir = dist - if os.path.isfile( dist ) and os.access( dist, os.X_OK ): - if re.search( ".*python(\d(\.\d)?)?$", dist ): - self._switch_file(dist) - else: - print "`%s` is not python binary." % dist - return - elif not os.path.isdir( "%s/%s" % (PATH_PYTHONS, dist) ): - print "`%s` is not installed." % dist - return - self._switch_dir( distdir ) - - def _switch_file(self, dist): - off() - symlink(dist, "%s/python" % PATH_BIN) - print "Switched to "+dist - - def _switch_dir(self, dist): - off() - symlink(dist, "%s/current" % PATH_PYTHONS) - for root, dirs, files in os.walk("%s/current/bin/" % PATH_PYTHONS): - for f in files: - symlink("%s%s" % (root, f), "%s/%s" % (PATH_BIN, f)) - break - # I want better code - if not os.path.isfile("%s/python" % PATH_BIN): - if os.path.isfile("%s/python3" % PATH_BIN): - symlink(os.path.realpath("%s/python3" % PATH_BIN), "%s/python" % PATH_BIN) - elif os.path.isfile("%s/python3.0" % PATH_BIN): - symlink(os.path.realpath("%s/python3.0" % PATH_BIN), "%s/python" % PATH_BIN) - print "Switched to "+dist - -class OffCommand(Command): - name = "off" - usage = "%prog" - summary = "Disable pythonbrew" - - def run_command(self, options, args): - off() - -class VersionCommand(Command): - name = "version" - usage = "%prog" - summary = "Show version" - - def run_command(self, options, args): - print VERSION - -class SearchCommand(Command): - name = "search" - usage = "%prog Python-" - summary = "Search Python packages" - - def run_command(self, options, args): - pkgs = PythonPackages() - if args: - pattern = args[0] - _re = re.compile(r"%s" % pattern) - pkgnames = [] - for pkgname in pkgs.get_packages(): - if _re.match(pkgname): - pkgnames.append(pkgname) - if pkgnames: - for pkgname in pkgnames: - print pkgname - else: - print "Package not found. `%s`" % pattern - else: - for pkgname in pkgs.get_packages(): - print pkgname - -class UninstallCommand(Command): - name = "uninstall" - usage = "%prog Python-" - summary = "Uninstall the given version of python" - - def run_command(self, options, args): - if args: - pkgname = args[0] - pkgpath = "%s/%s" % (PATH_PYTHONS, pkgname) - if not os.path.isdir(pkgpath): - print "%s is not installed." % pkgname - sys.exit(1) - if os.path.islink("%s/current" % PATH_PYTHONS): - curpath = os.path.realpath("%s/current" % PATH_PYTHONS) - if pkgpath == curpath: - off() - rm_r(pkgpath) - else: - self.parser.print_help() - -class CleanCommand(Command): - name = "clean" - usage = "%prog" - summary = "Remove stale source folders and archives" - - def run_command(self, options, args): - self._clean(PATH_BUILD) - self._clean(PATH_DISTS) - - def _clean(self, root): - for dir in os.listdir(root): - rm_r("%s/%s" % (root, dir)) - -class UpdateCommand(Command): - name = "update" - usage = "%prog" - summary = "Upgrades pythonbrew to the latest version" - - def run_command(self, options, args): - download_url = PYTHONBREW_DLSITE - basename = os.path.basename(download_url) - download_path = "%s/%s" % (PATH_DISTS, basename) - - try: - d = Downloader() - d.download(basename, download_url, download_path) - except: - print "Failed to download. `%s`" % download_url - sys.exit(1) - - (retval, dist) = copy_myself(download_path) - if not retval: - print "You are already running the installed latest version of pythonbrew." - print "\n%s" % dist - sys.exit() - print "The pythonbrew has been updated." - -class Pythonbrew(object): - def run(self): - options, args = parser.parse_args(sys.argv[1:]) - if options.help and not args: - args = ["help"] - if not args: - parser.error('You must give a command (use "pythonbrew help" to see a list of commands)') - return - add_command(HelpCommand()) - add_command(InitCommand()) - add_command(InstallCommand()) - add_command(InstalledCommand()) - add_command(SwitchCommand()) - add_command(OffCommand()) - add_command(VersionCommand()) - add_command(SearchCommand()) - add_command(UninstallCommand()) - add_command(CleanCommand()) - add_command(UpdateCommand()) - - command = args[0].lower() - if command not in command_dict: - parser.error("Unknown command: `%s`" % command) - return - command = command_dict[command] - command.run(args) - -def main(): - p = Pythonbrew() - p.run() - -if __name__ == "__main__": - main() diff --git a/pythonbrew-install b/pythonbrew-install new file mode 100755 index 0000000..c931952 --- /dev/null +++ b/pythonbrew-install @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +PYTHON=`command -v python` + +usage() +{ + printf " + Usage: + + ${0} [options] + + options: + + --python : Python interpreter. + +" +} + +parse_arguments() +{ + for arg do + val=`echo "$arg" | sed -e "s;--[^=]*=;;"` + case "$arg" in + --python=*) PYTHON="$val" ;; + --help) usage ;; + *) echo "Can't find the option. :$arg";; + esac + done +} + +parse_arguments $@ + +if [[ ! -x $PYTHON ]] ; then + echo "I think $PYTHON is not Python interpreter." + exit +fi + +PYTHON_VERSION=`$PYTHON -V 2>&1` +PYTHON_VERSION=${PYTHON_VERSION/"Python "/""} +PYTHON_VERSION_S=`echo $PYTHON_VERSION | sed -e "s/\(^[[:digit:]]\{1,\}.[[:digit:]]\{1,\}\).*/\1/"` + +if [[ $PYTHON_VERSION_S != "2.4" ]] && [[ $PYTHON_VERSION_S != "2.5" ]] && [[ $PYTHON_VERSION_S != "2.6" ]] ; then + printf "Python's version is $PYTHON_VERSION_S: + 2.4 <= python < 3.0 is required. +" + exit +fi + +ROOT="$HOME/.pythonbrew" +if [[ -n $PYTHONBREW_ROOT ]] ; then + ROOT=$PYTHONBREW_ROOT +fi +PATH_DISTS="$ROOT/dists" +PATH_ETC="$ROOT/etc" + +mkdir -p $PATH_DISTS +rm -rf $PATH_DISTS/pythonbrew.tar.gz +rm -rf $PATH_DISTS/utahta-pythonbrew* + +TEMP_TARBALL="pythonbrew.tar.gz" +DOWNLOAD_URL="http://github.com/utahta/pythonbrew/tarball/master" + +echo "Downloading $DOWNLOAD_URL" +builtin cd $PATH_DISTS ; curl -L $DOWNLOAD_URL -o "$TEMP_TARBALL" > /dev/null 2>&1 + +echo "Extracting $PATH_DISTS/$TEMP_TARBALL" +builtin cd $PATH_DISTS ; tar zxf $TEMP_TARBALL + +TEMP_FILE=`ls $PATH_DISTS | grep utahta-pythonbrew` + +echo "Installing pythonbrew into $ROOT" +$PYTHON $PATH_DISTS/$TEMP_FILE/pythonbrew_install.py +if [[ $? == 1 ]] ; then + echo "Failed to install pythonbrew." + exit +fi + +case $SHELL in + *tcsh) + shrc="cshrc" + yourshrc="tcshrc" + ;; + + *csh) + shrc="cshrc" + yourshrc=$shrc + ;; + + *) + shrc="bashrc" + yourshrc=$shrc + ;; +esac + +printf " +The pythonbrew is installed as: + + $ROOT/bin/pythonbrew + +You may trash the downloaded $0 from now on. + +Pythonbrew environment initiated, required directories are created under + + $ROOT + +Well-done! Congratulations! Please add the following line to the end +of your ~/.$yourshrc + + source $PATH_ETC/$shrc + +After that, exit this shell, start a new one, and install some fresh +pythons: + + pythonbrew install 2.6.6 + pythonbrew install 2.5.5 + +For further instructions, run: + + pythonbrew help + +The default help messages will popup and tell you what to do! + +Enjoy pythonbrew at $ROOT!! +" diff --git a/pythonbrew.py b/pythonbrew.py deleted file mode 100755 index 70525cf..0000000 --- a/pythonbrew.py +++ /dev/null @@ -1,696 +0,0 @@ -#!/usr/bin/env python -# vim:fileencoding=utf-8 - -import os -import sys -import urllib -import errno -import re -import shutil -import filecmp -import subprocess -import tempfile -from HTMLParser import HTMLParser -from optparse import OptionParser - -VERSION = "0.4" -if os.environ.has_key("PYTHONBREW_ROOT"): - ROOT = os.environ["PYTHONBREW_ROOT"] -else: - ROOT = "%s/python/pythonbrew" % os.environ["HOME"] -PYTHONDLSITE = "http://www.python.org/ftp/python/%s/%s" -DISTRIBUTE_SETUP_DLSITE = "http://python-distribute.org/distribute_setup.py" -PYTHONBREW_DLSITE = "http://github.com/utahta/pythonbrew/raw/master/pythonbrew" - -PATH_PYTHONS = "%s/pythons" % ROOT -PATH_BUILD = "%s/build" % ROOT -PATH_DISTS = "%s/dists" % ROOT -PATH_ETC = "%s/etc" % ROOT -PATH_BIN = "%s/bin" % ROOT - -parser = OptionParser(usage="%prog COMMAND [OPTIONS]", - version=VERSION, - add_help_option=False) -parser.add_option( - '-h', '--help', - dest='help', - action='store_true', - help='Show help') -parser.disable_interspersed_args() - -command_dict = {} -def add_command(command): - command_dict[command.name] = command - -#---------------------------------------------------- -# exception -#---------------------------------------------------- -class BuildingException(Exception): - """General exception during building""" - -#---------------------------------------------------- -# util -#---------------------------------------------------- -def size_format(b): - kb = 1000 - mb = kb*kb - b = float(b) - if b >= mb: - return "%.1fMb" % (b/mb) - if b >= kb: - return "%.1fKb" % (b/kb) - return "%.0fbytes" % (b) - -def is_url(name): - if ':' not in name: - return False - scheme = name.split(':', 1)[0].lower() - return scheme in ['http', 'https', 'file', 'ftp'] - -def splitext(name): - base, ext = os.path.splitext(name) - if base.lower().endswith('.tar'): - ext = base[-4:] + ext - base = base[:-4] - return base, ext - -def is_archive_file(name): - ext = splitext(name)[1].lower() - archives = ('.zip', '.tar.gz', '.tar.bz2', '.tgz', '.tar') - if ext in archives: - return True - return False - -def makedirs(path): - try: - os.makedirs(path) - except OSError, (e, es): - if errno.EEXIST != e: - raise - -def symlink(src, dst): - try: - os.symlink(src, dst) - except: - pass - -def unlink(path): - try: - os.unlink(path) - except OSError, (e, es): - if errno.ENOENT != e: - raise - -def rm_r(path): - """like rm -r command.""" - if os.path.isdir(path): - shutil.rmtree(path) - else: - unlink(path) - -def off(): - for root, dirs, files in os.walk(PATH_BIN): - for f in files: - if f == "pythonbrew": - continue - unlink("%s/%s" % (root, f)) - unlink("%s/current" % PATH_PYTHONS) - -def copy_myself(path): - (fd, src) = tempfile.mkstemp() - fp = file(path, "r") - line = fp.readline() - if line.startswith("#!"): - os.write(fd, "#!%s\n" % os.path.realpath(sys.executable)) - else: - os.write(fd, line) - os.write(fd, fp.read()) - os.close(fd) - fp.close() - - dist = "%s/pythonbrew" % PATH_BIN - if os.path.isfile(src) and os.path.isfile(dist): - if filecmp.cmp(src, dist): - unlink(src) - return (False, dist) - makedirs(PATH_BIN) - shutil.copy(src, dist) - os.chmod(dist, 0755) - unlink(src) - return (True, dist) - -#---------------------------------------------------- -# classes -#---------------------------------------------------- -class Downloader(object): - def __init__(self): - self._msg = "" - self._last_msg = "" - self._bytes = 0.0 - - def download(self, msg, url, path): - self._msg = msg - self._bytes = 0 - urllib.urlretrieve(url, path, self._download_progress) - print " downloaded." - - def _download_progress(self, block, blockbytes, maxbytes): - self._bytes += float(blockbytes) - if self._bytes >= maxbytes: - self._bytes = maxbytes - percent = (self._bytes / maxbytes) * 100 - max_size = size_format(maxbytes) - now_size = size_format(self._bytes) - now_msg = "\rDownloading %s (%s): %3i%% %s" % (self._msg, max_size, percent, now_size) - padding = " " * (len(self._last_msg) - len(now_msg)) - sys.stdout.write("%s%s" % (now_msg, padding)) - sys.stdout.flush() - self._last_msg = now_msg - -class Subprocess(object): - def __init__(self, log=None, shell=False, cwd=None, print_cmd=True): - self._log = log - self._shell = shell - self._cwd = cwd - self._print_cmd = print_cmd - - def chdir(self, cwd): - self._cwd = cwd - - def check_call(self, cmd, shell=None, cwd=None): - if shell: - self._shell = shell - if cwd: - self._cwd = cwd - if self._print_cmd: - print cmd - if self._log: - cmd = "(%s) >> '%s' 2>&1" % (cmd, self._log) - retcode = subprocess.call(cmd, shell=self._shell, cwd=self._cwd) - if retcode != 0: - raise BuildingException() - -class PythonVersionParser(HTMLParser): - def __init__(self): - HTMLParser.__init__(self) - self._versions = [] - self._re = re.compile("^(\d+\.\d+(\..*)?)/$") - - def handle_starttag(self, tag, attrs): - if tag == "a": - attrs = dict(attrs) - if "href" in attrs: - m = self._re.search(attrs["href"]) - if m: - self._versions.append(m.group(1)) - - def get_sorted_versions(self): - return sorted(self._versions) - -class PythonPackages(object): - def __init__(self): - parser = PythonVersionParser() - fp = urllib.urlopen("http://www.python.org/ftp/python/") - parser.feed(fp.read()) - fp.close() - parser.close() - self._versions = parser.get_sorted_versions() - - def has_version(self, version): - return version in self._versions - - def get_packages(self): - return ["Python-%s" % v for v in self._versions] - -#---------------------------------------------------- -# commands -#---------------------------------------------------- -class Command(object): - name = None - usage = None - summary = "" - - def __init__(self): - self.parser = OptionParser(usage=self.usage, - prog='%s %s' % (os.path.basename(sys.argv[0]), self.name)) - - def run(self, args): - options, args = self.parser.parse_args(args) - self.run_command(options, args[1:]) - -class HelpCommand(Command): - name = "help" - usage = "%prog [COMMAND]" - summary = "Show available commands" - - def run_command(self, options, args): - if args: - command = args[0] - if command not in command_dict: - parser.error("Unknown command: `%s`" % command) - return - command = command_dict[command] - command.parser.print_help() - return - parser.print_help() - print - print "Commands available:" - commands = [command_dict[key] for key in sorted(command_dict.keys())] - for command in commands: - print " %s: %s" % (command.name, command.summary) - print - print "Further Instructions:" - print " http://github.com/utahta/pythonbrew" - -class InitCommand(Command): - name = "init" - usage = "%prog" - summary = "Run this once to setup the pythonbrew directory ready for installing pythons into" - - def run_command(self, options, args): - makedirs(PATH_PYTHONS) - makedirs(PATH_BUILD) - makedirs(PATH_DISTS) - makedirs(PATH_ETC) - - os.system("echo 'export PATH=%s/bin:%s/current/bin:${PATH}' > %s/bashrc" % (ROOT, PATH_PYTHONS, PATH_ETC)) - os.system("echo 'setenv PATH %s/bin:%s/current/bin:$PATH' > %s/cshrc" % (ROOT, PATH_PYTHONS, PATH_ETC)) - m = re.search("(t?csh)", os.environ.get("SHELL")) - if m: - shrc = "cshrc" - yourshrc = m.group(1)+"rc" - else: - shrc = yourshrc = "bashrc" - print """ -Pythonbrew environment initiated, required directories are created under - - """+ROOT+""" - -Well-done! Congratulations! Please add the following line to the end -of your ~/."""+yourshrc+""" - - source """+PATH_ETC+"""/"""+shrc+""" - -After that, exit this shell, start a new one, and install some fresh -pythons: - - pythonbrew install Python-2.6.6 - pythonbrew install Python-2.5.5 - -For further instructions, simply run: - - pythonbrew - -The default help messages will popup and tell you what to do! - -Enjoy pythonbrew at $HOME!! -INSTRUCTION""" - -class InstallCommand(Command): - name = "install" - usage = "%prog [OPTIONS] PACKAGE_NAMES" - summary = "Build and install the given version of python" - - def __init__(self): - super(InstallCommand, self).__init__() - self.parser.add_option( - "-f", "--force", - dest="force", - action="store_true", - default=False, - help="Force installation of a Python." - ) - self.parser.add_option( - "-C", "--configure", - dest="configure", - default="", - metavar="CONFIGURE_OPTIONS", - help="Custom configure options." - ) - self.parser.add_option( - "-n", "--no-setuptools", - dest="no_setuptools", - action="store_true", - default=False, - help="Skip installation of setuptools." - ) - self._logfile = "%s/build.log" % ROOT - - def run_command(self, options, args): - if args: - # Install Python - self._install_python(args[0], options) - else: - # Install pythonbrew - self._install_myself() - - def _install_myself(self): - executable = os.path.abspath(sys.argv[0]) - (retval, dist) = copy_myself(executable) - if not retval: - print "You are already running the installed latest version of pythonbrew." - print "\n%s" % dist - sys.exit() - print """The pythonbrew is installed as: - - """+dist+""" - -You may trash the downloaded """+executable+""" from now on. - -Next, if this is the first time you've run pythonbrew installation, run: - - """+dist+""" init - -And follow the instruction on screen.""" - - def _get_package(self, name): - if not os.path.isfile(name) and not os.path.isdir(name): - if is_url(name): - basename = os.path.basename(name) - download_url = name - download_path = "%s/%s" % (PATH_DISTS, basename) - else: - m = re.search("^Python-(\d+\.\d+(\..*)?)$", name) - if not m: - print "Unknown package: `%s`" % name - sys.exit(1) - if os.path.isdir("%s/%s" % (PATH_PYTHONS, name)): - print "You are already installed `%s`." % name - sys.exit() - dist_version = m.group(1) - pkgs = PythonPackages() - if not pkgs.has_version(dist_version): - print "Package not found: `%s`" % name - sys.exit(1) - basename = "%s.tgz" % name - download_url = PYTHONDLSITE % (dist_version, basename) - download_path = "%s/%s" % (PATH_DISTS, basename) - - if os.path.isfile(download_path): - print "Use the previously fetched %s" % (download_path) - else: - try: - dl = Downloader() - dl.download( - basename, - download_url, - download_path - ) - except: - unlink(download_path) - print "\nInterrupt to abort. `%s`" % (download_url) - sys.exit(1) - # iffy - if os.path.getsize(download_path) < 1000000: - print "Failed to download. (maybe not stable version of python) `%s`" % (download_url) - unlink(download_path) - sys.exit(1) - else: - if is_archive_file(name): - basename = os.path.basename(name) - print "Copy the archive file %s to %s/%s" % (name, PATH_DISTS, basename) - shutil.copy(name, "%s/%s" % (PATH_DISTS, basename)) - else: - print "Invalid file. `%s`" % name - sys.exit(1) - return basename - - def _get_uncompress_command(self, basename): - distpath = "%s/%s" % (PATH_DISTS, basename) - if os.path.isfile(distpath): - ext = splitext(basename)[1] - if ext == ".tar.gz" or ext == ".tgz": - return "tar zxf %s" % (distpath) - elif ext == ".tar.bz2": - return "tar jxf %s" % (distpath) - elif ext == ".tar": - return "tar xf %s" % (distpath) - elif ext == ".zip": - return "unzip %s" % (distpath) - elif os.path.isdir(distpath): - return "mv %s %s/%s" % (distpath, PATH_BUILD, basename) - else: - print "File not found. `%s`" % (basename) - return "" - - def _install_python(self, dist, options): - basename = self._get_package(dist) - pkgname = splitext(basename)[0] - - install_dir = "%s/%s" % (PATH_PYTHONS, pkgname) - configure = "--prefix=%s %s" % (install_dir, options.configure) - print "Installing %s into %s" % (pkgname, install_dir); - print """This could take a while. You can run the following command on another shell to track the status: - - tail -f %s -""" % (self._logfile) - try: - s = Subprocess(log=self._logfile, shell=True, cwd=PATH_BUILD) - s.check_call(self._get_uncompress_command(basename)) - - s.chdir("%s/%s" % (PATH_BUILD, pkgname)) - s.check_call("./configure %s" % (configure)) - if options.force: - s.check_call("make") - s.check_call("make install") - else: - s.check_call("make") - s.check_call("make test") - s.check_call("make install") - except: - print """Installing %(pkgname)s failed. See %(ROOT)s/build.log to see why. - - pythonbrew install --force %(pkgname)s""" % {"pkgname":pkgname, "ROOT":ROOT} - sys.exit(1) - - # install setuptools - self._install_setuptools(pkgname, options.no_setuptools) - print """ -Installed %(pkgname)s successfully. Run the following command to switch to it. - - pythonbrew switch %(pkgname)s""" % {"pkgname":pkgname} - - def _install_setuptools(self, pkgname, no_setuptools): - if no_setuptools: - print "Skip installation setuptools." - return - if re.match("^Python-3.*", pkgname): - is_python3 = True - else: - is_python3 = False - download_url = DISTRIBUTE_SETUP_DLSITE - basename = os.path.basename(download_url) - - dl = Downloader() - dl.download(basename, download_url, "%s/%s" % (PATH_DISTS, basename)) - - if is_python3: - if os.path.isfile("%s/%s/bin/python3" % (PATH_PYTHONS, pkgname)): - pyexec = "%s/%s/bin/python3" % (PATH_PYTHONS, pkgname) - elif os.path.isfile("%s/%s/bin/python3.0" % (PATH_PYTHONS, pkgname)): - pyexec = "%s/%s/bin/python3.0" % (PATH_PYTHONS, pkgname) - else: - print "Python3 binary not found. `%s/%s`" % (PATH_PYTHONS, pkgname) - return - else: - pyexec = "%s/%s/bin/python" % (PATH_PYTHONS, pkgname) - - try: - s = Subprocess(log=self._logfile, shell=True, cwd=PATH_DISTS) - s.check_call("%s %s" % (pyexec, basename)) - if os.path.isfile("%s/%s/bin/easy_install" % (PATH_PYTHONS, pkgname)) and not is_python3: - s.check_call("%s/%s/bin/easy_install pip" % (PATH_PYTHONS, pkgname), cwd=None) - except: - print "Installing setuptools failed. See %s/build.log to see why." % (ROOT) - sys.exit(1) - -class InstalledCommand(Command): - name = "installed" - usage = "%prog" - summary = "List the installed versions of python" - - def run_command(self, options, args): - if not os.path.islink("%s/current" % PATH_PYTHONS): - cur = os.path.realpath("%s/bin/python" % ROOT) - print "%s (*)" % cur - elif os.path.islink("%s/current" % PATH_PYTHONS): - cur = os.path.basename(os.path.realpath("%s/current" % PATH_PYTHONS)) - else: - cur = "" - for d in sorted(os.listdir("%s/" % PATH_PYTHONS)): - if d == "current": - continue - if cur == d: - print "%s (*)" % cur - else: - print "%s" % (d) - -class SwitchCommand(Command): - name = "switch" - usage = "%prog PACKAGE" - summary = "Switch to the given version" - - def run_command(self, options, args): - if args: - dist = args[0] - distdir = dist - if os.path.isfile( dist ) and os.access( dist, os.X_OK ): - if re.search( ".*python(\d(\.\d)?)?$", dist ): - self._switch_file(dist) - else: - print "`%s` is not python binary." % dist - return - elif not os.path.isdir( "%s/%s" % (PATH_PYTHONS, dist) ): - print "`%s` is not installed." % dist - return - self._switch_dir( distdir ) - - def _switch_file(self, dist): - off() - symlink(dist, "%s/python" % PATH_BIN) - print "Switched to "+dist - - def _switch_dir(self, dist): - off() - symlink(dist, "%s/current" % PATH_PYTHONS) - for root, dirs, files in os.walk("%s/current/bin/" % PATH_PYTHONS): - for f in files: - symlink("%s%s" % (root, f), "%s/%s" % (PATH_BIN, f)) - break - # I want better code - if not os.path.isfile("%s/python" % PATH_BIN): - if os.path.isfile("%s/python3" % PATH_BIN): - symlink(os.path.realpath("%s/python3" % PATH_BIN), "%s/python" % PATH_BIN) - elif os.path.isfile("%s/python3.0" % PATH_BIN): - symlink(os.path.realpath("%s/python3.0" % PATH_BIN), "%s/python" % PATH_BIN) - print "Switched to "+dist - -class OffCommand(Command): - name = "off" - usage = "%prog" - summary = "Disable pythonbrew" - - def run_command(self, options, args): - off() - -class VersionCommand(Command): - name = "version" - usage = "%prog" - summary = "Show version" - - def run_command(self, options, args): - print VERSION - -class SearchCommand(Command): - name = "search" - usage = "%prog Python-" - summary = "Search Python packages" - - def run_command(self, options, args): - pkgs = PythonPackages() - if args: - pattern = args[0] - _re = re.compile(r"%s" % pattern) - pkgnames = [] - for pkgname in pkgs.get_packages(): - if _re.match(pkgname): - pkgnames.append(pkgname) - if pkgnames: - for pkgname in pkgnames: - print pkgname - else: - print "Package not found. `%s`" % pattern - else: - for pkgname in pkgs.get_packages(): - print pkgname - -class UninstallCommand(Command): - name = "uninstall" - usage = "%prog Python-" - summary = "Uninstall the given version of python" - - def run_command(self, options, args): - if args: - pkgname = args[0] - pkgpath = "%s/%s" % (PATH_PYTHONS, pkgname) - if not os.path.isdir(pkgpath): - print "%s is not installed." % pkgname - sys.exit(1) - if os.path.islink("%s/current" % PATH_PYTHONS): - curpath = os.path.realpath("%s/current" % PATH_PYTHONS) - if pkgpath == curpath: - off() - rm_r(pkgpath) - else: - self.parser.print_help() - -class CleanCommand(Command): - name = "clean" - usage = "%prog" - summary = "Remove stale source folders and archives" - - def run_command(self, options, args): - self._clean(PATH_BUILD) - self._clean(PATH_DISTS) - - def _clean(self, root): - for dir in os.listdir(root): - rm_r("%s/%s" % (root, dir)) - -class UpdateCommand(Command): - name = "update" - usage = "%prog" - summary = "Upgrades pythonbrew to the latest version" - - def run_command(self, options, args): - download_url = PYTHONBREW_DLSITE - basename = os.path.basename(download_url) - download_path = "%s/%s" % (PATH_DISTS, basename) - - try: - d = Downloader() - d.download(basename, download_url, download_path) - except: - print "Failed to download. `%s`" % download_url - sys.exit(1) - - (retval, dist) = copy_myself(download_path) - if not retval: - print "You are already running the installed latest version of pythonbrew." - print "\n%s" % dist - sys.exit() - print "The pythonbrew has been updated." - -class Pythonbrew(object): - def run(self): - options, args = parser.parse_args(sys.argv[1:]) - if options.help and not args: - args = ["help"] - if not args: - parser.error('You must give a command (use "pythonbrew help" to see a list of commands)') - return - add_command(HelpCommand()) - add_command(InitCommand()) - add_command(InstallCommand()) - add_command(InstalledCommand()) - add_command(SwitchCommand()) - add_command(OffCommand()) - add_command(VersionCommand()) - add_command(SearchCommand()) - add_command(UninstallCommand()) - add_command(CleanCommand()) - add_command(UpdateCommand()) - - command = args[0].lower() - if command not in command_dict: - parser.error("Unknown command: `%s`" % command) - return - command = command_dict[command] - command.run(args) - -def main(): - p = Pythonbrew() - p.run() - -if __name__ == "__main__": - main() diff --git a/pythonbrew/__init__.py b/pythonbrew/__init__.py new file mode 100644 index 0000000..1335db3 --- /dev/null +++ b/pythonbrew/__init__.py @@ -0,0 +1,19 @@ +import sys +from pythonbrew.basecommand import command_dict, load_all_commands +from pythonbrew.baseparser import parser + +def main(): + options, args = parser.parse_args(sys.argv[1:]) + if options.help and not args: + args = ["help"] + if not args: + parser.error('You must give a command (use "pythonbrew help" to see a list of commands)') + return + + load_all_commands() + command = args[0].lower() + if command not in command_dict: + parser.error("Unknown command: `%s`" % command) + return + command = command_dict[command] + command.run(args) diff --git a/pythonbrew/basecommand.py b/pythonbrew/basecommand.py new file mode 100644 index 0000000..56f3388 --- /dev/null +++ b/pythonbrew/basecommand.py @@ -0,0 +1,38 @@ +import os +import sys +import re +from optparse import OptionParser +from pythonbrew import commands +from pythonbrew.define import PATH_BIN_PYTHONBREW + +command_dict = {} + +class Command(object): + name = None + usage = None + summary = "" + + def __init__(self): + self.parser = OptionParser(usage=self.usage, + prog='%s %s' % (PATH_BIN_PYTHONBREW, self.name)) + command_dict[self.name] = self + + def run(self, args): + options, args = self.parser.parse_args(args) + self.run_command(options, args[1:]) + +def load_command(name): + full_name = 'pythonbrew.commands.%s' % name + if full_name in sys.modules: + return + try: + __import__(full_name) + except ImportError: + pass + +def load_all_commands(): + for name in command_names(): + load_command(name) + +def command_names(): + return [path[:-3] for path in os.listdir(commands.__path__[0]) if not re.match("(__init__\.py$|.*\.pyc$)", path)] diff --git a/pythonbrew/baseparser.py b/pythonbrew/baseparser.py new file mode 100644 index 0000000..073ce31 --- /dev/null +++ b/pythonbrew/baseparser.py @@ -0,0 +1,13 @@ +from optparse import OptionParser +from pythonbrew.define import VERSION, PATH_BIN_PYTHONBREW + +parser = OptionParser(usage="%prog COMMAND [OPTIONS]", + prog=PATH_BIN_PYTHONBREW, + version=VERSION, + add_help_option=False) +parser.add_option( + '-h', '--help', + dest='help', + action='store_true', + help='Show help') +parser.disable_interspersed_args() diff --git a/pythonbrew/commands/__init__.py b/pythonbrew/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pythonbrew/commands/clean.py b/pythonbrew/commands/clean.py new file mode 100644 index 0000000..76c3ce7 --- /dev/null +++ b/pythonbrew/commands/clean.py @@ -0,0 +1,19 @@ +import os +from pythonbrew.basecommand import Command +from pythonbrew.define import PATH_BUILD, PATH_DISTS +from pythonbrew.util import rm_r + +class CleanCommand(Command): + name = "clean" + usage = "%prog" + summary = "Remove stale source folders and archives" + + def run_command(self, options, args): + self._clean(PATH_BUILD) + self._clean(PATH_DISTS) + + def _clean(self, root): + for dir in os.listdir(root): + rm_r("%s/%s" % (root, dir)) + +CleanCommand() diff --git a/pythonbrew/commands/help.py b/pythonbrew/commands/help.py new file mode 100644 index 0000000..a3dda2a --- /dev/null +++ b/pythonbrew/commands/help.py @@ -0,0 +1,27 @@ +from pythonbrew.basecommand import Command, command_dict +from pythonbrew.baseparser import parser +from pythonbrew.log import logger + +class HelpCommand(Command): + name = "help" + usage = "%prog [COMMAND]" + summary = "Show available commands" + + def run_command(self, options, args): + if args: + command = args[0] + if command not in command_dict: + parser.error("Unknown command: `%s`" % command) + return + command = command_dict[command] + command.parser.print_help() + return + parser.print_help() + logger.info("\nCommands available:") + commands = [command_dict[key] for key in sorted(command_dict.keys())] + for command in commands: + logger.info(" %s: %s" % (command.name, command.summary)) + logger.info("\nFurther Instructions:") + logger.info(" http://github.com/utahta/pythonbrew") + +HelpCommand() diff --git a/pythonbrew/commands/install.py b/pythonbrew/commands/install.py new file mode 100644 index 0000000..a4b8cb0 --- /dev/null +++ b/pythonbrew/commands/install.py @@ -0,0 +1,184 @@ +import os +import sys +import re +import shutil +from pythonbrew.basecommand import Command +from pythonbrew.util import unlink, splitext, Subprocess, Package, makedirs,\ + rm_r +from pythonbrew.define import ROOT, PATH_PYTHONS, PATH_DISTS, PATH_BUILD, PATH_LOG, DISTRIBUTE_SETUP_DLSITE +from pythonbrew.log import logger +from pythonbrew.downloader import get_python_package_url, Downloader + +class InstallCommand(Command): + name = "install" + usage = "%prog [OPTIONS] VERSION" + summary = "Build and install the given version of python" + + def __init__(self): + super(InstallCommand, self).__init__() + self.parser.add_option( + "-f", "--force", + dest="force", + action="store_true", + default=False, + help="Force installation of a Python." + ) + self.parser.add_option( + "-C", "--configure", + dest="configure", + default="", + metavar="CONFIGURE_OPTIONS", + help="Custom configure options." + ) + self.parser.add_option( + "-n", "--no-setuptools", + dest="no_setuptools", + action="store_true", + default=False, + help="Skip installation of setuptools." + ) + self._logfile = "%s/build.log" % PATH_LOG + + def run_command(self, options, args): + if args: + # Install Python + self._install_python(args[0], options) + else: + logger.error("Package not found.") + + def _install_python(self, dist, options): + pkg = Package(dist) + distname = self._download_package(pkg) + pkgname = pkg.name + version = pkg.version + + install_dir = "%s/%s" % (PATH_PYTHONS, pkgname) + configure = "--prefix=%s %s" % (install_dir, options.configure) + logger.info("") + logger.info("This could take a while. You can run the following command on another shell to track the status:") + logger.info(" tail -f %s" % self._logfile) + logger.info("") + + try: + s = Subprocess(log=self._logfile, shell=True, cwd=PATH_BUILD, print_cmd=False) + + logger.info("Extracting %s" % distname) + s.check_call(self._get_uncompress_command(distname)) + + logger.info("Installing %s into %s" % (pkgname, install_dir)) + s.chdir("%s/%s" % (PATH_BUILD, pkgname)) + s.check_call("./configure %s" % (configure)) + if options.force: + s.check_call("make") + else: + s.check_call("make") + s.check_call("make test") + if version == "1.5.2" or version == "1.6.1": + makedirs(install_dir) + s.check_call("make install") + except: + rm_r(install_dir) + logger.error("""Failed to install %(pkgname)s. See %(ROOT)s/build.log to see why. + + pythonbrew install --force %(version)s""" % {"pkgname":pkgname, "ROOT":ROOT, "version":version}) + sys.exit(1) + + # install setuptools + self._install_setuptools(pkgname, options.no_setuptools) + logger.info(""" +Installed %(pkgname)s successfully. Run the following command to switch to %(pkgname)s. + + pythonbrew switch %(version)s""" % {"pkgname":pkgname, "version":version}) + + def _download_package(self, pkg): + pkgname = pkg.name + version = pkg.version + + if os.path.isdir("%s/%s" % (PATH_PYTHONS, pkgname)): + logger.error("You are already installed `%s`." % pkgname) + sys.exit(1) + + download_url = get_python_package_url(version) + if not download_url: + logger.error("Unknown package: `%s`" % pkgname) + sys.exit(1) + distname = os.path.basename(download_url) + download_path = "%s/%s" % (PATH_DISTS, distname) + + if os.path.isfile(download_path): + logger.info("Use the previously fetched %s" % (download_path)) + else: + try: + dl = Downloader() + dl.download( + distname, + download_url, + download_path + ) + except: + unlink(download_path) + logger.info("\nInterrupt to abort. `%s`" % (download_url)) + sys.exit(1) + # iffy + if os.path.getsize(download_path) < 1000000: + logger.error("Failed to download. `%s`" % (download_url)) + unlink(download_path) + sys.exit(1) + return distname + + def _get_uncompress_command(self, basename): + distpath = "%s/%s" % (PATH_DISTS, basename) + if os.path.isfile(distpath): + ext = splitext(basename)[1] + if ext == ".tar.gz" or ext == ".tgz": + return "tar zxf %s" % (distpath) + elif ext == ".tar.bz2": + return "tar jxf %s" % (distpath) + elif ext == ".tar": + return "tar xf %s" % (distpath) + elif ext == ".zip": + return "unzip %s" % (distpath) + elif os.path.isdir(distpath): + return "mv %s %s/%s" % (distpath, PATH_BUILD, basename) + else: + logger.error("File not found. `%s`" % (basename)) + return "" + + def _install_setuptools(self, pkgname, no_setuptools): + if no_setuptools: + logger.info("Skip installation setuptools.") + return + if re.match("^Python-3.*", pkgname): + is_python3 = True + else: + is_python3 = False + download_url = DISTRIBUTE_SETUP_DLSITE + basename = os.path.basename(download_url) + + dl = Downloader() + dl.download(basename, download_url, "%s/%s" % (PATH_DISTS, basename)) + + install_dir = "%s/%s" % (PATH_PYTHONS, pkgname) + if is_python3: + if os.path.isfile("%s/bin/python3" % (install_dir)): + pyexec = "%s/bin/python3" % (install_dir) + elif os.path.isfile("%s/bin/python3.0" % (install_dir)): + pyexec = "%s/bin/python3.0" % (install_dir) + else: + logger.error("Python3 binary not found. `%s`" % (install_dir)) + return + else: + pyexec = "%s/bin/python" % (install_dir) + + try: + s = Subprocess(log=self._logfile, shell=True, cwd=PATH_DISTS, print_cmd=False) + logger.info("Installing distribute into %s" % install_dir) + s.check_call("%s %s" % (pyexec, basename)) + if os.path.isfile("%s/bin/easy_install" % (install_dir)) and not is_python3: + logger.info("Installing pip into %s" % install_dir) + s.check_call("%s/bin/easy_install pip" % (install_dir), cwd=None) + except: + logger.error("Failed to install setuptools. See %s/build.log to see why." % (ROOT)) + logger.info("Skip install setuptools.") + +InstallCommand() diff --git a/pythonbrew/commands/installed.py b/pythonbrew/commands/installed.py new file mode 100644 index 0000000..4bf423f --- /dev/null +++ b/pythonbrew/commands/installed.py @@ -0,0 +1,26 @@ +import os +from pythonbrew.basecommand import Command +from pythonbrew.define import PATH_PYTHONS +from pythonbrew.log import logger +import sys + +class InstalledCommand(Command): + name = "installed" + usage = "%prog" + summary = "List the installed versions of python" + + def run_command(self, options, args): + cur = "" + if not os.path.islink("%s/current" % PATH_PYTHONS): + logger.info("%s (*)" % sys.executable) + elif os.path.islink("%s/current" % PATH_PYTHONS): + cur = os.path.basename(os.path.realpath("%s/current" % PATH_PYTHONS)) + for d in sorted(os.listdir("%s/" % PATH_PYTHONS)): + if d == "current": + continue + if cur == d: + logger.info("%s (*)" % cur) + else: + logger.info("%s" % (d)) + +InstalledCommand() diff --git a/pythonbrew/commands/list.py b/pythonbrew/commands/list.py new file mode 100644 index 0000000..8608cfc --- /dev/null +++ b/pythonbrew/commands/list.py @@ -0,0 +1,34 @@ +import re +from pythonbrew.basecommand import Command +from pythonbrew.define import PYTHON_PACKAGE_URL +from pythonbrew.util import Package +from pythonbrew.log import logger + +class ListCommand(Command): + name = "list" + usage = "%prog [VERSION]" + summary = "List the available install version of python" + + def run_command(self, options, args): + if args: + pkg = Package(args[0]) + _re = re.compile(r"%s" % pkg.name) + pkgs = [] + for pkgname in self._get_packages_name(): + if _re.match(pkgname): + pkgs.append(pkgname) + if pkgs: + logger.info("Pythons:") + for pkgname in pkgs: + logger.info(" %s" % pkgname) + else: + print "Package not found. `%s`" % pkg.name + else: + logger.info("Pythons:") + for pkgname in self._get_packages_name(): + logger.info(" %s" % pkgname) + + def _get_packages_name(self): + return ["Python-%s" % version for version in sorted(PYTHON_PACKAGE_URL.keys())] + +ListCommand() diff --git a/pythonbrew/commands/off.py b/pythonbrew/commands/off.py new file mode 100644 index 0000000..ba16963 --- /dev/null +++ b/pythonbrew/commands/off.py @@ -0,0 +1,12 @@ +from pythonbrew.basecommand import Command +from pythonbrew.util import off + +class OffCommand(Command): + name = "off" + usage = "%prog" + summary = "Disable pythonbrew" + + def run_command(self, options, args): + off() + +OffCommand() diff --git a/pythonbrew/commands/switch.py b/pythonbrew/commands/switch.py new file mode 100644 index 0000000..7273ca3 --- /dev/null +++ b/pythonbrew/commands/switch.py @@ -0,0 +1,41 @@ +import os +import sys +import re +from pythonbrew.basecommand import Command +from pythonbrew.define import PATH_PYTHONS, PATH_BIN +from pythonbrew.util import off, symlink, Package +from pythonbrew.log import logger + +class SwitchCommand(Command): + name = "switch" + usage = "%prog VERSION" + summary = "Switch to the given version" + + def run_command(self, options, args): + if not args: + logger.error("Unrecognized command line argument: argument not found.") + sys.exit(1) + pkg = Package(args[0]) + pkgname = pkg.name + pkgdir = "%s/%s" % (PATH_PYTHONS, pkgname) + if not os.path.isdir(pkgdir): + logger.error("`%s` is not installed." % pkgname) + sys.exit(1) + self._switch_dir(pkgdir) + logger.info("Switched to %s" % pkgname) + + def _switch_dir(self, pkgdir): + off() + symlink(pkgdir, "%s/current" % PATH_PYTHONS) + for root, dirs, files in os.walk("%s/current/bin/" % PATH_PYTHONS): + for f in files: + symlink("%s%s" % (root, f), "%s/%s" % (PATH_BIN, f)) + break + # I want better code + if not os.path.isfile("%s/python" % PATH_BIN): + if os.path.isfile("%s/python3" % PATH_BIN): + symlink(os.path.realpath("%s/python3" % PATH_BIN), "%s/python" % PATH_BIN) + elif os.path.isfile("%s/python3.0" % PATH_BIN): + symlink(os.path.realpath("%s/python3.0" % PATH_BIN), "%s/python" % PATH_BIN) + +SwitchCommand() diff --git a/pythonbrew/commands/uninstall.py b/pythonbrew/commands/uninstall.py new file mode 100644 index 0000000..1c5e12f --- /dev/null +++ b/pythonbrew/commands/uninstall.py @@ -0,0 +1,29 @@ +import os +import sys +from pythonbrew.basecommand import Command +from pythonbrew.define import PATH_PYTHONS +from pythonbrew.util import off, rm_r, Package +from pythonbrew.log import logger + +class UninstallCommand(Command): + name = "uninstall" + usage = "%prog VERSION" + summary = "Uninstall the given version of python" + + def run_command(self, options, args): + if args: + pkg = Package(args[0]) + pkgname = pkg.name + pkgpath = "%s/%s" % (PATH_PYTHONS, pkgname) + if not os.path.isdir(pkgpath): + logger.error("`%s` is not installed." % pkgname) + sys.exit(1) + if os.path.islink("%s/current" % PATH_PYTHONS): + curpath = os.path.realpath("%s/current" % PATH_PYTHONS) + if pkgpath == curpath: + off() + rm_r(pkgpath) + else: + self.parser.print_help() + +UninstallCommand() diff --git a/pythonbrew/commands/update.py b/pythonbrew/commands/update.py new file mode 100644 index 0000000..a08e680 --- /dev/null +++ b/pythonbrew/commands/update.py @@ -0,0 +1,64 @@ +import os +import sys +import re +from pythonbrew.basecommand import Command +from pythonbrew.define import PATH_DISTS, VERSION, PYTHONBREW_DIRNAME, ROOT +from pythonbrew.log import logger +from pythonbrew.downloader import Downloader, get_pythonbrew_update_url +from pythonbrew.util import Subprocess, rm_r + +class UpdateCommand(Command): + name = "update" + usage = "%prog" + summary = "Upgrades pythonbrew to the latest version" + + def run_command(self, options, args): + version = "head" + if args: + version = args[0] + + # check for latest version + if version == VERSION: + logger.info("You are already running the installed latest version of pythonbrew.") + sys.exit() + + download_url = get_pythonbrew_update_url(version) + if not download_url: + logger.error("`%s` of pythonbrew not found." % version) + sys.exit(1) + + distname = "pythonbrew.tgz" + download_path = "%s/%s" % (PATH_DISTS, distname) + try: + d = Downloader() + d.download(distname, download_url, download_path) + except: + logger.error("Failed to download. `%s`" % download_url) + sys.exit(1) + + _re = re.compile("^%s.*" % PYTHONBREW_DIRNAME) + for name in os.listdir(PATH_DISTS): + if _re.match(name): + rm_r("%s/%s" % (PATH_DISTS, name)) + try: + s = Subprocess(shell=True, cwd=PATH_DISTS, print_cmd=False) + logger.info("Extracting %s" % download_path) + s.check_call("tar zxf %s" % download_path) + except: + logger.error("Failed to update pythonbrew.") + sys.exit(1) + + for name in os.listdir(PATH_DISTS): + if _re.match(name): + try: + installer_path = "%s/%s" % (PATH_DISTS, name) + s = Subprocess(shell=True, cwd=PATH_DISTS, print_cmd=False) + logger.info("Installing %s into %s" % (installer_path, ROOT)) + s.check_call("%s %s/pythonbrew_install.py" % (sys.executable, installer_path)) + except: + logger.error("Failed to update pythonbrew.") + sys.exit(1) + break + logger.info("The pythonbrew has been updated.") + +UpdateCommand() diff --git a/pythonbrew/commands/version.py b/pythonbrew/commands/version.py new file mode 100644 index 0000000..2144123 --- /dev/null +++ b/pythonbrew/commands/version.py @@ -0,0 +1,13 @@ +from pythonbrew.basecommand import Command +from pythonbrew.define import VERSION +from pythonbrew.log import logger + +class VersionCommand(Command): + name = "version" + usage = "%prog" + summary = "Show version" + + def run_command(self, options, args): + logger.info(VERSION) + +VersionCommand() diff --git a/pythonbrew/define.py b/pythonbrew/define.py new file mode 100644 index 0000000..1273a48 --- /dev/null +++ b/pythonbrew/define.py @@ -0,0 +1,56 @@ +import os + +VERSION = "0.5" + +if os.environ.has_key("PYTHONBREW_ROOT"): + ROOT = os.environ["PYTHONBREW_ROOT"] +else: + ROOT = "%s/.pythonbrew" % os.environ["HOME"] + +INSTALLER_ROOT = os.path.dirname(os.path.abspath(__file__)) + +PATH_PYTHONS = "%s/pythons" % ROOT +PATH_BUILD = "%s/build" % ROOT +PATH_DISTS = "%s/dists" % ROOT +PATH_ETC = "%s/etc" % ROOT +PATH_BIN = "%s/bin" % ROOT +PATH_LOG = "%s/log" % ROOT +PATH_SCRIPTS = "%s/scripts" % ROOT +PATH_SCRIPTS_PYTHONBREW = "%s/pythonbrew" % PATH_SCRIPTS +PATH_SCRIPTS_PYTHONBREW_COMMANDS = "%s/commands" % PATH_SCRIPTS_PYTHONBREW + +# file path +PATH_BIN_PYTHONBREW = "%s/pythonbrew" % PATH_BIN +PATH_BIN_PYBREW = "%s/pybrew" % PATH_BIN # pybrew is symlink as pythonbrew + +# download setuptools url +DISTRIBUTE_SETUP_DLSITE = "http://python-distribute.org/distribute_setup.py" + +# download pythonbrew url +PYTHONBREW_UPDATE_URL = { + "head": "http://github.com/utahta/pythonbrew/tarball/master" +} +PYTHONBREW_DIRNAME = "utahta-pythonbrew" + +# download Python package url +PYTHON_PACKAGE_URL = {} +PYTHON_PACKAGE_URL["1.5.2"] = "http://www.python.org/ftp/python/src/py152.tgz" +PYTHON_PACKAGE_URL["1.6.1"] = "http://www.python.org/download/releases/1.6.1/Python-1.6.1.tar.gz" +_PYTHON_PACKAGE_VERSIONS = [ + "2.0", "2.0.1", + "2.1", "2.1.1", "2.1.2", "2.1.3", + "2.2", "2.2.1", "2.2.2", "2.2.3", + "2.3", "2.3.1", "2.3.2", "2.3.4", "2.3.5", "2.3.6", "2.3.7", + "2.4", "2.4.1", "2.4.2", "2.4.3", "2.4.4", "2.4.5", "2.4.6", + "2.5", "2.5.1", "2.5.2", "2.5.3", "2.5.4", "2.5.5", + "2.6", "2.6.1", "2.6.2", "2.6.3", "2.6.4", "2.6.5", "2.6.6", + "2.7", + "3.0", "3.0.1", + "3.1", "3.1.1", "3.1.2", +] +for version in _PYTHON_PACKAGE_VERSIONS: + PYTHON_PACKAGE_URL[version] = "http://www.python.org/ftp/python/%s/Python-%s.tgz" % (version, version) +del _PYTHON_PACKAGE_VERSIONS +PYTHON_PACKAGE_URL["3.2a1"] = "http://www.python.org/ftp/python/3.2/Python-3.2a1.tgz" +PYTHON_PACKAGE_URL["3.2a2"] = "http://www.python.org/ftp/python/3.2/Python-3.2a2.tgz" + diff --git a/pythonbrew/downloader.py b/pythonbrew/downloader.py new file mode 100644 index 0000000..6c88b48 --- /dev/null +++ b/pythonbrew/downloader.py @@ -0,0 +1,39 @@ +import sys +import urllib +from pythonbrew.util import size_format +from pythonbrew.define import PYTHON_PACKAGE_URL, PYTHONBREW_UPDATE_URL + +class Downloader(object): + def __init__(self): + self._msg = "" + self._last_msg = "" + self._bytes = 0.0 + + def download(self, msg, url, path): + self._msg = msg + self._bytes = 0 + urllib.urlretrieve(url, path, self._download_progress) + print " downloaded." + + def _download_progress(self, block, blockbytes, maxbytes): + self._bytes += float(blockbytes) + if self._bytes >= maxbytes: + self._bytes = maxbytes + percent = (self._bytes / maxbytes) * 100 + max_size = size_format(maxbytes) + now_size = size_format(self._bytes) + now_msg = "\rDownloading %s (%s): %3i%% %s" % (self._msg, max_size, percent, now_size) + padding = " " * (len(self._last_msg) - len(now_msg)) + sys.stdout.write("%s%s" % (now_msg, padding)) + sys.stdout.flush() + self._last_msg = now_msg + +def get_pythonbrew_update_url(version): + if PYTHONBREW_UPDATE_URL.has_key(version): + return PYTHONBREW_UPDATE_URL[version] + return None + +def get_python_package_url(version): + if PYTHON_PACKAGE_URL.has_key(version): + return PYTHON_PACKAGE_URL[version] + return None diff --git a/pythonbrew/exceptions.py b/pythonbrew/exceptions.py new file mode 100644 index 0000000..89749ac --- /dev/null +++ b/pythonbrew/exceptions.py @@ -0,0 +1,3 @@ + +class BuildingException(Exception): + """General exception during building""" diff --git a/pythonbrew/installer.py b/pythonbrew/installer.py new file mode 100644 index 0000000..1c2ac7c --- /dev/null +++ b/pythonbrew/installer.py @@ -0,0 +1,46 @@ +import os +import sys +import glob +import shutil +from pythonbrew.util import makedirs, symlink +from pythonbrew.define import PATH_BUILD, PATH_BIN, PATH_DISTS, PATH_PYTHONS,\ + PATH_ETC, PATH_SCRIPTS, PATH_SCRIPTS_PYTHONBREW,\ + PATH_SCRIPTS_PYTHONBREW_COMMANDS, INSTALLER_ROOT, PATH_BIN_PYTHONBREW,\ + PATH_BIN_PYBREW, ROOT, PATH_LOG + +def install_pythonbrew(): + makedirs(PATH_PYTHONS) + makedirs(PATH_BUILD) + makedirs(PATH_DISTS) + makedirs(PATH_ETC) + makedirs(PATH_BIN) + makedirs(PATH_LOG) + makedirs(PATH_SCRIPTS) + makedirs(PATH_SCRIPTS_PYTHONBREW) + makedirs(PATH_SCRIPTS_PYTHONBREW_COMMANDS) + + for path in glob.glob("%s/*.py" % INSTALLER_ROOT): + shutil.copy(path, PATH_SCRIPTS_PYTHONBREW) + + for path in glob.glob("%s/commands/*.py" % INSTALLER_ROOT): + shutil.copy(path, PATH_SCRIPTS_PYTHONBREW_COMMANDS) + + fp = open("%s/pythonbrew.py" % PATH_SCRIPTS, "w") + fp.write("""import pythonbrew +if __name__ == "__main__": + pythonbrew.main() +""") + fp.close() + + fp = open(PATH_BIN_PYTHONBREW, "w") + fp.write("""#!/usr/bin/env bash +%s %s/pythonbrew.py $@ +""" % (sys.executable, PATH_SCRIPTS)) + fp.close() + os.chmod(PATH_BIN_PYTHONBREW, 0755) + symlink(PATH_BIN_PYTHONBREW, PATH_BIN_PYBREW) # pyb as pythonbrew + + os.system("echo 'export PATH=%s/bin:%s/current/bin:${PATH}' > %s/bashrc" % (ROOT, PATH_PYTHONS, PATH_ETC)) + os.system("echo 'setenv PATH %s/bin:%s/current/bin:$PATH' > %s/cshrc" % (ROOT, PATH_PYTHONS, PATH_ETC)) + + \ No newline at end of file diff --git a/pythonbrew/log.py b/pythonbrew/log.py new file mode 100644 index 0000000..f57a472 --- /dev/null +++ b/pythonbrew/log.py @@ -0,0 +1,43 @@ +import sys +import logging + +class Logger(object): + + DEBUG = logging.DEBUG + INFO = logging.INFO + ERROR = logging.ERROR + + def __init__(self): + self._consumers = [] + consumer = logging.getLogger('info') + consumer.setLevel(Logger.INFO) + hdlr = logging.StreamHandler(sys.stdout) + hdlr.setFormatter(logging.Formatter("%(message)s")) + consumer.addHandler(hdlr) + self.add_consumer(Logger.INFO, consumer) + + consumer = logging.getLogger('error') + consumer.setLevel(Logger.ERROR) + hdlr = logging.StreamHandler(sys.stderr) + hdlr.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) + consumer.addHandler(hdlr) + self.add_consumer(Logger.ERROR, consumer) + + def debug(self, msg, *args, **keys): + self._log(Logger.DEBUG, msg, *args, **keys) + + def info(self, msg, *args, **keys): + self._log(Logger.INFO, msg, *args, **keys) + + def error(self, msg, *args, **keys): + self._log(Logger.ERROR, msg, *args, **keys) + + def _log(self, level, msg, *args, **keys): + for (consumer_level, consumer) in self._consumers: + if level == consumer_level: + consumer.log(level, msg, *args, **keys) + + def add_consumer(self, level, consumer): + self._consumers.append((level, consumer)) + +logger = Logger() diff --git a/pythonbrew/util.py b/pythonbrew/util.py new file mode 100644 index 0000000..2c15242 --- /dev/null +++ b/pythonbrew/util.py @@ -0,0 +1,111 @@ +import os +import sys +import errno +import shutil +import urllib +import subprocess +import re +from pythonbrew.define import PATH_BIN, PATH_PYTHONS +from pythonbrew.exceptions import BuildingException +from pythonbrew.log import logger + +def size_format(b): + kb = 1000 + mb = kb*kb + b = float(b) + if b >= mb: + return "%.1fMb" % (b/mb) + if b >= kb: + return "%.1fKb" % (b/kb) + return "%.0fbytes" % (b) + +def is_url(name): + if ':' not in name: + return False + scheme = name.split(':', 1)[0].lower() + return scheme in ['http', 'https', 'file', 'ftp'] + +def splitext(name): + base, ext = os.path.splitext(name) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + +def is_archive_file(name): + ext = splitext(name)[1].lower() + archives = ('.zip', '.tar.gz', '.tar.bz2', '.tgz', '.tar') + if ext in archives: + return True + return False + +def makedirs(path): + try: + os.makedirs(path) + except OSError, (e, es): + if errno.EEXIST != e: + raise + +def symlink(src, dst): + try: + os.symlink(src, dst) + except: + pass + +def unlink(path): + try: + os.unlink(path) + except OSError, (e, es): + if errno.ENOENT != e: + raise + +def rm_r(path): + """like rm -r command.""" + if os.path.isdir(path): + shutil.rmtree(path) + else: + unlink(path) + +def off(): + for root, dirs, files in os.walk(PATH_BIN): + for f in files: + if f == "pythonbrew" or f == "pybrew": + continue + unlink("%s/%s" % (root, f)) + unlink("%s/current" % PATH_PYTHONS) + +class Subprocess(object): + def __init__(self, log=None, shell=False, cwd=None, print_cmd=True): + self._log = log + self._shell = shell + self._cwd = cwd + self._print_cmd = print_cmd + + def chdir(self, cwd): + self._cwd = cwd + + def check_call(self, cmd, shell=None, cwd=None): + if shell: + self._shell = shell + if cwd: + self._cwd = cwd + if self._print_cmd: + logger.info(cmd) + if self._log: + cmd = "(%s) >> '%s' 2>&1" % (cmd, self._log) + retcode = subprocess.call(cmd, shell=self._shell, cwd=self._cwd) + if retcode != 0: + raise BuildingException() + +class Package(object): + def __init__(self, name): + self.name = None + self.version = None + m = re.search("^Python-(.*)$", name) + if m: + self.name = name + self.version = m.group(1) + else: + self.name = "Python-%s" % name + self.version = name + \ No newline at end of file diff --git a/pythonbrew_install.py b/pythonbrew_install.py new file mode 100644 index 0000000..e8508ed --- /dev/null +++ b/pythonbrew_install.py @@ -0,0 +1,3 @@ +from pythonbrew.installer import install_pythonbrew +if __name__ == "__main__": + install_pythonbrew()