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
44 def _python_cmd(*args):
48 Return True if the command succeeded.
50 args = (sys.executable,) + args
51 return subprocess.call(args) == 0
54 def _install(archive_filename, install_args=()):
55 """Install Setuptools."""
56 with archive_context(archive_filename):
58 log.warn('Installing Setuptools')
59 if not _python_cmd('setup.py', 'install', *install_args):
60 log.warn('Something went wrong during the installation.')
61 log.warn('See the error message above.')
66 def _build_egg(egg, archive_filename, to_dir):
67 """Build Setuptools egg."""
68 with archive_context(archive_filename):
70 log.warn('Building a Setuptools egg in %s', to_dir)
71 _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
72 # returning the result
74 if not os.path.exists(egg):
75 raise IOError('Could not build the egg.')
78 class ContextualZipFile(zipfile.ZipFile):
80 """Supplement ZipFile class to support context manager for Python 2.6."""
85 def __exit__(self, type, value, traceback):
88 def __new__(cls, *args, **kwargs):
89 """Construct a ZipFile or ContextualZipFile as appropriate."""
90 if hasattr(zipfile.ZipFile, '__exit__'):
91 return zipfile.ZipFile(*args, **kwargs)
92 return super(ContextualZipFile, cls).__new__(cls)
95 @contextlib.contextmanager
96 def archive_context(filename):
98 Unzip filename to a temporary directory, set to the cwd.
100 The unzipped target is cleaned up after.
102 tmpdir = tempfile.mkdtemp()
103 log.warn('Extracting in %s', tmpdir)
107 with ContextualZipFile(filename) as archive:
110 # going in the directory
111 subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
113 log.warn('Now working in %s', subdir)
118 shutil.rmtree(tmpdir)
121 def _do_download(version, download_base, to_dir, download_delay):
122 """Download Setuptools."""
123 py_desig = 'py{sys.version_info[0]}.{sys.version_info[1]}'.format(sys=sys)
124 tp = 'setuptools-{version}-{py_desig}.egg'
125 egg = os.path.join(to_dir, tp.format(**locals()))
126 if not os.path.exists(egg):
127 archive = download_setuptools(version, download_base,
128 to_dir, download_delay)
129 _build_egg(egg, archive, to_dir)
130 sys.path.insert(0, egg)
132 # Remove previously-imported pkg_resources if present (see
133 # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
134 if 'pkg_resources' in sys.modules:
135 _unload_pkg_resources()
138 setuptools.bootstrap_install_from = egg
142 version=DEFAULT_VERSION, download_base=DEFAULT_URL,
143 to_dir=DEFAULT_SAVE_DIR, download_delay=15):
145 Ensure that a setuptools version is installed.
147 Return None. Raise SystemExit if the requested version
148 or later cannot be installed.
150 version = _resolve_version(version)
151 to_dir = os.path.abspath(to_dir)
153 # prior to importing, capture the module state for
154 # representative modules.
155 rep_modules = 'pkg_resources', 'setuptools'
156 imported = set(sys.modules).intersection(rep_modules)
160 pkg_resources.require("setuptools>=" + version)
161 # a suitable version is already installed
164 # pkg_resources not available; setuptools is not installed; download
166 except pkg_resources.DistributionNotFound:
167 # no version of setuptools was found; allow download
169 except pkg_resources.VersionConflict as VC_err:
171 _conflict_bail(VC_err, version)
173 # otherwise, unload pkg_resources to allow the downloaded version to
176 _unload_pkg_resources()
178 return _do_download(version, download_base, to_dir, download_delay)
181 def _conflict_bail(VC_err, version):
183 Setuptools was imported prior to invocation, so it is
184 unsafe to unload it. Bail out.
186 conflict_tmpl = textwrap.dedent("""
187 The required version of setuptools (>={version}) is not available,
188 and can't be installed while this script is running. Please
189 install a more recent version first, using
190 'easy_install -U setuptools'.
192 (Currently using {VC_err.args[0]!r})
194 msg = conflict_tmpl.format(**locals())
195 sys.stderr.write(msg)
199 def _unload_pkg_resources():
202 for importer in sys.meta_path
203 if importer.__class__.__module__ != 'pkg_resources.extern'
206 name for name in sys.modules
207 if name.startswith('pkg_resources')
209 for mod_name in del_modules:
210 del sys.modules[mod_name]
213 def _clean_check(cmd, target):
215 Run the command to download target.
217 If the command fails, clean up before re-raising the error.
220 subprocess.check_call(cmd)
221 except subprocess.CalledProcessError:
222 if os.access(target, os.F_OK):
227 def download_file_powershell(url, target):
229 Download the file at url to target using Powershell.
231 Powershell will validate trust.
232 Raise an exception if the command cannot complete.
234 target = os.path.abspath(target)
236 "[System.Net.WebRequest]::DefaultWebProxy.Credentials = "
237 "[System.Net.CredentialCache]::DefaultCredentials; "
238 '(new-object System.Net.WebClient).DownloadFile("%(url)s", "%(target)s")'
246 _clean_check(cmd, target)
249 def has_powershell():
250 """Determine if Powershell is available."""
251 if platform.system() != 'Windows':
253 cmd = ['powershell', '-Command', 'echo test']
254 with open(os.path.devnull, 'wb') as devnull:
256 subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
260 download_file_powershell.viable = has_powershell
263 def download_file_curl(url, target):
264 cmd = ['curl', url, '--location', '--silent', '--output', target]
265 _clean_check(cmd, target)
269 cmd = ['curl', '--version']
270 with open(os.path.devnull, 'wb') as devnull:
272 subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
276 download_file_curl.viable = has_curl
279 def download_file_wget(url, target):
280 cmd = ['wget', url, '--quiet', '--output-document', target]
281 _clean_check(cmd, target)
285 cmd = ['wget', '--version']
286 with open(os.path.devnull, 'wb') as devnull:
288 subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
292 download_file_wget.viable = has_wget
295 def download_file_insecure(url, target):
296 """Use Python to download the file, without connection authentication."""
299 # Read all the data in one block.
304 # Write all the data in one block to avoid creating a partial file.
305 with open(target, "wb") as dst:
307 download_file_insecure.viable = lambda: True
310 def get_best_downloader():
312 download_file_powershell,
315 download_file_insecure,
317 viable_downloaders = (dl for dl in downloaders if dl.viable())
318 return next(viable_downloaders, None)
321 def download_setuptools(
322 version=DEFAULT_VERSION, download_base=DEFAULT_URL,
323 to_dir=DEFAULT_SAVE_DIR, delay=15,
324 downloader_factory=get_best_downloader):
326 Download setuptools from a specified location and return its filename.
328 `version` should be a valid setuptools version number that is available
329 as an sdist for download under the `download_base` URL (which should end
330 with a '/'). `to_dir` is the directory where the egg will be downloaded.
331 `delay` is the number of seconds to pause before an actual download
334 ``downloader_factory`` should be a function taking no arguments and
335 returning a function for downloading a URL to a target.
337 version = _resolve_version(version)
338 # making sure we use the absolute path
339 to_dir = os.path.abspath(to_dir)
340 zip_name = "setuptools-%s.zip" % version
341 url = download_base + zip_name
342 saveto = os.path.join(to_dir, zip_name)
343 if not os.path.exists(saveto): # Avoid repeated downloads
344 log.warn("Downloading %s", url)
345 downloader = downloader_factory()
346 downloader(url, saveto)
347 return os.path.realpath(saveto)
350 def _resolve_version(version):
352 Resolve LATEST version
354 if version is not LATEST:
357 meta_url = urljoin(DEFAULT_URL, '/pypi/setuptools/json')
358 resp = urlopen(meta_url)
359 with contextlib.closing(resp):
361 charset = resp.info().get_content_charset()
363 # Python 2 compat; assume UTF-8
365 reader = codecs.getreader(charset)
366 doc = json.load(reader(resp))
368 return str(doc['info']['version'])
371 def _build_install_args(options):
373 Build the arguments to 'python setup.py install' on the setuptools package.
375 Returns list of command line arguments.
377 return ['--user'] if options.user_install else []
381 """Parse the command line for options."""
382 parser = optparse.OptionParser()
384 '--user', dest='user_install', action='store_true', default=False,
385 help='install in user site package')
387 '--download-base', dest='download_base', metavar="URL",
389 help='alternative URL from where to download the setuptools package')
391 '--insecure', dest='downloader_factory', action='store_const',
392 const=lambda: download_file_insecure, default=get_best_downloader,
393 help='Use internal, non-validating downloader'
396 '--version', help="Specify which version to download",
397 default=DEFAULT_VERSION,
401 help="Directory to save (and re-use) package",
402 default=DEFAULT_SAVE_DIR,
404 options, args = parser.parse_args()
405 # positional arguments are ignored
409 def _download_args(options):
410 """Return args for download_setuptools function from cmdline args."""
412 version=options.version,
413 download_base=options.download_base,
414 downloader_factory=options.downloader_factory,
415 to_dir=options.to_dir,
420 """Install or upgrade setuptools and EasyInstall."""
421 options = _parse_args()
422 archive = download_setuptools(**_download_args(options))
423 return _install(archive, _build_install_args(options))
425 if __name__ == '__main__':