#!python """Bootstrap merlin installation If you want to use merlin in your package's setup.py, just include this directory as a subdirectory alongside setup.py (using svn:externals), and add this to the top of your setup.py:: from archimedes import use_merlin use_merlin() If you want to require a specific version of merlin, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_merlin()``. This file can also be run as a script to install or upgrade merlin. """ import sys, os # Create a new branch of the Merlin project when introducing # backwards-incompatible changes. Adjust the following numbers when # creating a branch. Branch numbers follow the CVS convention (but # with no even/odd rules). branch = "1" endOfBranch = "2a" default_url = "http://cheeseshop.python.org/packages/any/m/merlin/" reqSpec = "merlin >= %s, < %s" % (branch, endOfBranch) def use_merlin(package_index=default_url, to_dir=os.curdir, download_delay=15): """Automatically find/download merlin and make it available on sys.path `package_index` is a URL (ending with a '/') which contains merlin eggs. `to_dir` is the directory where merlin will be downloaded, if it is not already available. If `download_delay` is specified, it should be the number of seconds that will be paused before initiating a download, should one be required. If an older version of merlin is installed, this routine will print a message to ``sys.stderr`` and raise SystemExit in an attempt to abort the calling script. """ try: import merlin except ImportError: egg = download_merlin(package_index, to_dir, download_delay) sys.path.insert(0, egg) import merlin; merlin.bootstrap_install_from = egg try: merlin.require(reqSpec) except merlin.VersionConflict, e: # XXX could we install in a subprocess here? print >>sys.stderr, ( "The required version of merlin (v%s) is not available, and\n" "can't be installed while this script is running. Please install\n" " a more recent version first.\n\n(Currently using %r)" ) % (branch, e.args[0]) sys.exit(2) def download_merlin(package_index=default_url, to_dir=os.curdir, delay=15): """Download merlin from a specified location and return its filename `package_index` is a URL (ending with a '/') which contains merlin eggs. `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ import urllib2, urlparse, shutil, re from glob import glob # Avoid repeated downloads candidates = [] candidateMap = {} for egg in glob(os.path.join(to_dir, "*.egg")): basename = os.path.basename(egg) name, version = basename[:-4].split('-') if name == "merlin" and version.startswith(branch + "."): candidates.append(basename) candidateMap[basename] = egg if candidates: candidates.sort() winner = candidates[-1] saveto = candidateMap[winner] else: src = dst = None try: from distutils import log if delay: log.warn(""" --------------------------------------------------------------------------- This script requires merlin version %s to run (even to display help). I will attempt to download it for you (from %s), but you may need to enable firewall access for this script first. I will start the download in %d seconds. (Note: if this machine does not have network access, please obtain the most recent version of merlin v%s and place it in this directory before rerunning this script.) ---------------------------------------------------------------------------""", branch, package_index, delay, branch ); from time import sleep; sleep(delay) # scan the package index candidates = [] candidateMap = {} log.warn("Scanning %s", package_index) src = urllib2.urlopen(package_index) base = src.url # handle redirects data = src.read() HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) # this is here to fix emacs' cruddy broken syntax highlighting for match in HREF.finditer(data): link = urlparse.urljoin(base, match.group(1)) scheme, server, path, parameters, query, fragment = urlparse.urlparse(link) basename = urllib2.unquote(path.split('/')[-1]) if '#' in basename: basename, fragment = basename.split('#',1) if basename.endswith('.egg') and '-' in basename: name, version = basename[:-4].split('-') if name == "merlin" and version.startswith(branch + "."): candidates.append(basename) candidateMap[basename] = link if not candidates: print >>sys.stderr, ( "The required version of merlin (v%s) cannot be found.\n" ) % branch sys.exit(3) candidates.sort() winner = candidates[-1] url = candidateMap[winner] saveto = os.path.join(to_dir, winner) log.warn("Downloading %s", url) src = urllib2.urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = src.read() dst = open(saveto,"wb"); dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def main(argv): """Install or upgrade merlin""" try: import merlin except ImportError: egg = None try: egg = download_merlin(delay=0) sys.path.insert(0,egg) from merlin import main return main(['install'] + list(argv) + [egg]) # we're done here finally: if egg and os.path.exists(egg): os.unlink(egg) import merlin try: merlin.require(reqSpec) except merlin.VersionConflict: from merlin import main main(list(argv)+[download_merlin(delay=0)]) sys.exit(0) # try to force an exit else: if argv: from merlin import main main(['install'] + argv) else: print "Merlin version",branch,"has been installed." if __name__=='__main__': main(sys.argv[1:])