1#!python 2"""Bootstrap setuptools installation 3 4If you want to use setuptools in your package's setup.py, just include this 5file in the same directory with it, and add this to the top of your setup.py:: 6 7 from ez_setup import use_setuptools 8 use_setuptools() 9 10If you want to require a specific version of setuptools, set a download 11mirror, or use an alternate download directory, you can do so by supplying 12the appropriate options to ``use_setuptools()``. 13 14This file can also be run as a script to install or upgrade setuptools. 15""" 16import os 17import shutil 18import sys 19import tempfile 20import tarfile 21import optparse 22import subprocess 23 24from distutils import log 25 26try: 27 from site import USER_SITE 28except ImportError: 29 USER_SITE = None 30 31DEFAULT_VERSION = "0.9.6" 32DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" 33 34def _python_cmd(*args): 35 args = (sys.executable,) + args 36 return subprocess.call(args) == 0 37 38def _install(tarball, install_args=()): 39 # extracting the tarball 40 tmpdir = tempfile.mkdtemp() 41 log.warn('Extracting in %s', tmpdir) 42 old_wd = os.getcwd() 43 try: 44 os.chdir(tmpdir) 45 tar = tarfile.open(tarball) 46 _extractall(tar) 47 tar.close() 48 49 # going in the directory 50 subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 51 os.chdir(subdir) 52 log.warn('Now working in %s', subdir) 53 54 # installing 55 log.warn('Installing Setuptools') 56 if not _python_cmd('setup.py', 'install', *install_args): 57 log.warn('Something went wrong during the installation.') 58 log.warn('See the error message above.') 59 # exitcode will be 2 60 return 2 61 finally: 62 os.chdir(old_wd) 63 shutil.rmtree(tmpdir) 64 65 66def _build_egg(egg, tarball, to_dir): 67 # extracting the tarball 68 tmpdir = tempfile.mkdtemp() 69 log.warn('Extracting in %s', tmpdir) 70 old_wd = os.getcwd() 71 try: 72 os.chdir(tmpdir) 73 tar = tarfile.open(tarball) 74 _extractall(tar) 75 tar.close() 76 77 # going in the directory 78 subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 79 os.chdir(subdir) 80 log.warn('Now working in %s', subdir) 81 82 # building an egg 83 log.warn('Building a Setuptools egg in %s', to_dir) 84 _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) 85 86 finally: 87 os.chdir(old_wd) 88 shutil.rmtree(tmpdir) 89 # returning the result 90 log.warn(egg) 91 if not os.path.exists(egg): 92 raise IOError('Could not build the egg.') 93 94 95def _do_download(version, download_base, to_dir, download_delay): 96 egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg' 97 % (version, sys.version_info[0], sys.version_info[1])) 98 if not os.path.exists(egg): 99 tarball = download_setuptools(version, download_base, 100 to_dir, download_delay) 101 _build_egg(egg, tarball, to_dir) 102 sys.path.insert(0, egg) 103 import setuptools 104 setuptools.bootstrap_install_from = egg 105 106 107def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 108 to_dir=os.curdir, download_delay=15): 109 # making sure we use the absolute path 110 to_dir = os.path.abspath(to_dir) 111 was_imported = 'pkg_resources' in sys.modules or \ 112 'setuptools' in sys.modules 113 try: 114 import pkg_resources 115 except ImportError: 116 return _do_download(version, download_base, to_dir, download_delay) 117 try: 118 pkg_resources.require("setuptools>=" + version) 119 return 120 except pkg_resources.VersionConflict: 121 e = sys.exc_info()[1] 122 if was_imported: 123 sys.stderr.write( 124 "The required version of setuptools (>=%s) is not available,\n" 125 "and can't be installed while this script is running. Please\n" 126 "install a more recent version first, using\n" 127 "'easy_install -U setuptools'." 128 "\n\n(Currently using %r)\n" % (version, e.args[0])) 129 sys.exit(2) 130 else: 131 del pkg_resources, sys.modules['pkg_resources'] # reload ok 132 return _do_download(version, download_base, to_dir, 133 download_delay) 134 except pkg_resources.DistributionNotFound: 135 return _do_download(version, download_base, to_dir, 136 download_delay) 137 138 139def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 140 to_dir=os.curdir, delay=15): 141 """Download setuptools from a specified location and return its filename 142 143 `version` should be a valid setuptools version number that is available 144 as an egg for download under the `download_base` URL (which should end 145 with a '/'). `to_dir` is the directory where the egg will be downloaded. 146 `delay` is the number of seconds to pause before an actual download 147 attempt. 148 """ 149 # making sure we use the absolute path 150 to_dir = os.path.abspath(to_dir) 151 try: 152 from urllib.request import urlopen 153 except ImportError: 154 from urllib2 import urlopen 155 tgz_name = "setuptools-%s.tar.gz" % version 156 url = download_base + tgz_name 157 saveto = os.path.join(to_dir, tgz_name) 158 src = dst = None 159 if not os.path.exists(saveto): # Avoid repeated downloads 160 try: 161 log.warn("Downloading %s", url) 162 src = urlopen(url) 163 # Read/write all in one block, so we don't create a corrupt file 164 # if the download is interrupted. 165 data = src.read() 166 dst = open(saveto, "wb") 167 dst.write(data) 168 finally: 169 if src: 170 src.close() 171 if dst: 172 dst.close() 173 return os.path.realpath(saveto) 174 175 176def _extractall(self, path=".", members=None): 177 """Extract all members from the archive to the current working 178 directory and set owner, modification time and permissions on 179 directories afterwards. `path' specifies a different directory 180 to extract to. `members' is optional and must be a subset of the 181 list returned by getmembers(). 182 """ 183 import copy 184 import operator 185 from tarfile import ExtractError 186 directories = [] 187 188 if members is None: 189 members = self 190 191 for tarinfo in members: 192 if tarinfo.isdir(): 193 # Extract directories with a safe mode. 194 directories.append(tarinfo) 195 tarinfo = copy.copy(tarinfo) 196 tarinfo.mode = 448 # decimal for oct 0700 197 self.extract(tarinfo, path) 198 199 # Reverse sort directories. 200 if sys.version_info < (2, 4): 201 def sorter(dir1, dir2): 202 return cmp(dir1.name, dir2.name) 203 directories.sort(sorter) 204 directories.reverse() 205 else: 206 directories.sort(key=operator.attrgetter('name'), reverse=True) 207 208 # Set correct owner, mtime and filemode on directories. 209 for tarinfo in directories: 210 dirpath = os.path.join(path, tarinfo.name) 211 try: 212 self.chown(tarinfo, dirpath) 213 self.utime(tarinfo, dirpath) 214 self.chmod(tarinfo, dirpath) 215 except ExtractError: 216 e = sys.exc_info()[1] 217 if self.errorlevel > 1: 218 raise 219 else: 220 self._dbg(1, "tarfile: %s" % e) 221 222 223def _build_install_args(options): 224 """ 225 Build the arguments to 'python setup.py install' on the setuptools package 226 """ 227 install_args = [] 228 if options.user_install: 229 if sys.version_info < (2, 6): 230 log.warn("--user requires Python 2.6 or later") 231 raise SystemExit(1) 232 install_args.append('--user') 233 return install_args 234 235def _parse_args(): 236 """ 237 Parse the command line for options 238 """ 239 parser = optparse.OptionParser() 240 parser.add_option( 241 '--user', dest='user_install', action='store_true', default=False, 242 help='install in user site package (requires Python 2.6 or later)') 243 parser.add_option( 244 '--download-base', dest='download_base', metavar="URL", 245 default=DEFAULT_URL, 246 help='alternative URL from where to download the setuptools package') 247 options, args = parser.parse_args() 248 # positional arguments are ignored 249 return options 250 251def main(version=DEFAULT_VERSION): 252 """Install or upgrade setuptools and EasyInstall""" 253 options = _parse_args() 254 tarball = download_setuptools(download_base=options.download_base) 255 return _install(tarball, _build_install_args(options)) 256 257if __name__ == '__main__': 258 sys.exit(main()) 259