4 Setuptools bootstrapping installer.
6 Maintained at https://github.com/pypa/setuptools/tree/bootstrap.
8 Run this script to install or upgrade setuptools.
24 from distutils import log
27 from urllib.request import urlopen
28 from urllib.parse import urljoin
30 from urllib2 import urlopen
31 from urlparse import urljoin
34 from site import USER_SITE
39 DEFAULT_VERSION = LATEST
40 DEFAULT_URL = "https://pypi.io/packages/source/s/setuptools/"
41 DEFAULT_SAVE_DIR = os.curdir
43 MEANINGFUL_INVALID_ZIP_ERR_MSG = 'Maybe {0} is corrupted, delete it and try again.'
46 def _python_cmd(*args):
50 Return True if the command succeeded.
52 args = (sys.executable,) + args
53 return subprocess.call(args) == 0
56 def _install(archive_filename, install_args=()):
57 """Install Setuptools."""
58 with archive_context(archive_filename):
60 log.warn('Installing Setuptools')
61 if not _python_cmd('setup.py', 'install', *install_args):
62 log.warn('Something went wrong during the installation.')
63 log.warn('See the error message above.')
68 def _build_egg(egg, archive_filename, to_dir):
69 """Build Setuptools egg."""
70 with archive_context(archive_filename):
72 log.warn('Building a Setuptools egg in %s', to_dir)
73 _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
74 # returning the result
76 if not os.path.exists(egg):
77 raise IOError('Could not build the egg.')
80 class ContextualZipFile(zipfile.ZipFile):
82 """Supplement ZipFile class to support context manager for Python 2.6."""
87 def __exit__(self, type, value, traceback):
90 def __new__(cls, *args, **kwargs):
91 """Construct a ZipFile or ContextualZipFile as appropriate."""
92 if hasattr(zipfile.ZipFile, '__exit__'):
93 return zipfile.ZipFile(*args, **kwargs)
94 return super(ContextualZipFile, cls).__new__(cls)
97 @contextlib.contextmanager
98 def archive_context(filename):
100 Unzip filename to a temporary directory, set to the cwd.
102 The unzipped target is cleaned up after.
104 tmpdir = tempfile.mkdtemp()
105 log.warn('Extracting in %s', tmpdir)
110 with ContextualZipFile(filename) as archive:
112 except zipfile.BadZipfile as err:
115 err.args = err.args + (
116 MEANINGFUL_INVALID_ZIP_ERR_MSG.format(filename),
120 # going in the directory
121 subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
123 log.warn('Now working in %s', subdir)
128 shutil.rmtree(tmpdir)
131 def _do_download(version, download_base, to_dir, download_delay):
132 """Download Setuptools."""
133 py_desig = 'py{sys.version_info[0]}.{sys.version_info[1]}'.format(sys=sys)
134 tp = 'setuptools-{version}-{py_desig}.egg'
135 egg = os.path.join(to_dir, tp.format(**locals()))
136 if not os.path.exists(egg):
137 archive = download_setuptools(version, download_base,
138 to_dir, download_delay)
139 _build_egg(egg, archive, to_dir)
140 sys.path.insert(0, egg)
142 # Remove previously-imported pkg_resources if present (see
143 # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
144 if 'pkg_resources' in sys.modules:
145 _unload_pkg_resources()
148 setuptools.bootstrap_install_from = egg
152 version=DEFAULT_VERSION, download_base=DEFAULT_URL,
153 to_dir=DEFAULT_SAVE_DIR, download_delay=15):
155 Ensure that a setuptools version is installed.
157 Return None. Raise SystemExit if the requested version
158 or later cannot be installed.
160 version = _resolve_version(version)
161 to_dir = os.path.abspath(to_dir)
163 # prior to importing, capture the module state for
164 # representative modules.
165 rep_modules = 'pkg_resources', 'setuptools'
166 imported = set(sys.modules).intersection(rep_modules)
170 pkg_resources.require("setuptools>=" + version)
171 # a suitable version is already installed
174 # pkg_resources not available; setuptools is not installed; download
176 except pkg_resources.DistributionNotFound:
177 # no version of setuptools was found; allow download
179 except pkg_resources.VersionConflict as VC_err:
181 _conflict_bail(VC_err, version)
183 # otherwise, unload pkg_resources to allow the downloaded version to
186 _unload_pkg_resources()
188 return _do_download(version, download_base, to_dir, download_delay)
191 def _conflict_bail(VC_err, version):
193 Setuptools was imported prior to invocation, so it is
194 unsafe to unload it. Bail out.
196 conflict_tmpl = textwrap.dedent("""
197 The required version of setuptools (>={version}) is not available,
198 and can't be installed while this script is running. Please
199 install a more recent version first, using
200 'easy_install -U setuptools'.
202 (Currently using {VC_err.args[0]!r})
204 msg = conflict_tmpl.format(**locals())
205 sys.stderr.write(msg)
209 def _unload_pkg_resources():
212 for importer in sys.meta_path
213 if importer.__class__.__module__ != 'pkg_resources.extern'
216 name for name in sys.modules
217 if name.startswith('pkg_resources')
219 for mod_name in del_modules:
220 del sys.modules[mod_name]
223 def _clean_check(cmd, target):
225 Run the command to download target.
227 If the command fails, clean up before re-raising the error.
230 subprocess.check_call(cmd)
231 except subprocess.CalledProcessError:
232 if os.access(target, os.F_OK):
237 def download_file_powershell(url, target):
239 Download the file at url to target using Powershell.
241 Powershell will validate trust.
242 Raise an exception if the command cannot complete.
244 target = os.path.abspath(target)
246 "[System.Net.WebRequest]::DefaultWebProxy.Credentials = "
247 "[System.Net.CredentialCache]::DefaultCredentials; "
248 '(new-object System.Net.WebClient).DownloadFile("%(url)s", "%(target)s")'
256 _clean_check(cmd, target)
259 def has_powershell():
260 """Determine if Powershell is available."""
261 if platform.system() != 'Windows':
263 cmd = ['powershell', '-Command', 'echo test']
264 with open(os.path.devnull, 'wb') as devnull:
266 subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
270 download_file_powershell.viable = has_powershell
273 def download_file_curl(url, target):
274 cmd = ['curl', url, '--location', '--silent', '--output', target]
275 _clean_check(cmd, target)
279 cmd = ['curl', '--version']
280 with open(os.path.devnull, 'wb') as devnull:
282 subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
286 download_file_curl.viable = has_curl
289 def download_file_wget(url, target):
290 cmd = ['wget', url, '--quiet', '--output-document', target]
291 _clean_check(cmd, target)
295 cmd = ['wget', '--version']
296 with open(os.path.devnull, 'wb') as devnull:
298 subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
302 download_file_wget.viable = has_wget
305 def download_file_insecure(url, target):
306 """Use Python to download the file, without connection authentication."""
309 # Read all the data in one block.
314 # Write all the data in one block to avoid creating a partial file.
315 with open(target, "wb") as dst:
317 download_file_insecure.viable = lambda: True
320 def get_best_downloader():
322 download_file_powershell,
325 download_file_insecure,
327 viable_downloaders = (dl for dl in downloaders if dl.viable())
328 return next(viable_downloaders, None)
331 def download_setuptools(
332 version=DEFAULT_VERSION, download_base=DEFAULT_URL,
333 to_dir=DEFAULT_SAVE_DIR, delay=15,
334 downloader_factory=get_best_downloader):
336 Download setuptools from a specified location and return its filename.
338 `version` should be a valid setuptools version number that is available
339 as an sdist for download under the `download_base` URL (which should end
340 with a '/'). `to_dir` is the directory where the egg will be downloaded.
341 `delay` is the number of seconds to pause before an actual download
344 ``downloader_factory`` should be a function taking no arguments and
345 returning a function for downloading a URL to a target.
347 version = _resolve_version(version)
348 # making sure we use the absolute path
349 to_dir = os.path.abspath(to_dir)
350 zip_name = "setuptools-%s.zip" % version
351 url = download_base + zip_name
352 saveto = os.path.join(to_dir, zip_name)
353 if not os.path.exists(saveto): # Avoid repeated downloads
354 log.warn("Downloading %s", url)
355 downloader = downloader_factory()
356 downloader(url, saveto)
357 return os.path.realpath(saveto)
360 def _resolve_version(version):
362 Resolve LATEST version
364 if version is not LATEST:
367 meta_url = urljoin(DEFAULT_URL, '/pypi/setuptools/json')
368 resp = urlopen(meta_url)
369 with contextlib.closing(resp):
371 charset = resp.info().get_content_charset()
373 # Python 2 compat; assume UTF-8
375 reader = codecs.getreader(charset)
376 doc = json.load(reader(resp))
378 return str(doc['info']['version'])
381 def _build_install_args(options):
383 Build the arguments to 'python setup.py install' on the setuptools package.
385 Returns list of command line arguments.
387 return ['--user'] if options.user_install else []
391 """Parse the command line for options."""
392 parser = optparse.OptionParser()
394 '--user', dest='user_install', action='store_true', default=False,
395 help='install in user site package')
397 '--download-base', dest='download_base', metavar="URL",
399 help='alternative URL from where to download the setuptools package')
401 '--insecure', dest='downloader_factory', action='store_const',
402 const=lambda: download_file_insecure, default=get_best_downloader,
403 help='Use internal, non-validating downloader'
406 '--version', help="Specify which version to download",
407 default=DEFAULT_VERSION,
411 help="Directory to save (and re-use) package",
412 default=DEFAULT_SAVE_DIR,
414 options, args = parser.parse_args()
415 # positional arguments are ignored
419 def _download_args(options):
420 """Return args for download_setuptools function from cmdline args."""
422 version=options.version,
423 download_base=options.download_base,
424 downloader_factory=options.downloader_factory,
425 to_dir=options.to_dir,
430 """Install or upgrade setuptools and EasyInstall."""
431 options = _parse_args()
432 archive = download_setuptools(**_download_args(options))
433 return _install(archive, _build_install_args(options))
435 if __name__ == '__main__':