2 """Bootstrap setuptools installation
4 To use setuptools in your package's setup.py, include this
5 file in the same directory and add this to the top of your setup.py::
7 from ez_setup import use_setuptools
10 To require a specific version of setuptools, set a download
11 mirror, or use an alternate download directory, simply supply
12 the appropriate options to ``use_setuptools()``.
14 This file can also be run as a script to install or upgrade setuptools.
26 from distutils
import log
29 from site
import USER_SITE
33 DEFAULT_VERSION
= "2.0.1"
34 DEFAULT_URL
= "https://pypi.python.org/packages/source/s/setuptools/"
36 def _python_cmd(*args
):
37 args
= (sys
.executable
,) + args
38 return subprocess
.call(args
) == 0
40 def _install(tarball
, install_args
=()):
41 # extracting the tarball
42 tmpdir
= tempfile
.mkdtemp()
43 log
.warn('Extracting in %s', tmpdir
)
47 tar
= tarfile
.open(tarball
)
51 # going in the directory
52 subdir
= os
.path
.join(tmpdir
, os
.listdir(tmpdir
)[0])
54 log
.warn('Now working in %s', subdir
)
57 log
.warn('Installing Setuptools')
58 if not _python_cmd('setup.py', 'install', *install_args
):
59 log
.warn('Something went wrong during the installation.')
60 log
.warn('See the error message above.')
68 def _build_egg(egg
, tarball
, to_dir
):
69 # extracting the tarball
70 tmpdir
= tempfile
.mkdtemp()
71 log
.warn('Extracting in %s', tmpdir
)
75 tar
= tarfile
.open(tarball
)
79 # going in the directory
80 subdir
= os
.path
.join(tmpdir
, os
.listdir(tmpdir
)[0])
82 log
.warn('Now working in %s', subdir
)
85 log
.warn('Building a Setuptools egg in %s', to_dir
)
86 _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir
)
91 # returning the result
93 if not os
.path
.exists(egg
):
94 raise IOError('Could not build the egg.')
97 def _do_download(version
, download_base
, to_dir
, download_delay
):
98 egg
= os
.path
.join(to_dir
, 'setuptools-%s-py%d.%d.egg'
99 % (version
, sys
.version_info
[0], sys
.version_info
[1]))
100 if not os
.path
.exists(egg
):
101 tarball
= download_setuptools(version
, download_base
,
102 to_dir
, download_delay
)
103 _build_egg(egg
, tarball
, to_dir
)
104 sys
.path
.insert(0, egg
)
106 # Remove previously-imported pkg_resources if present (see
107 # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
108 if 'pkg_resources' in sys
.modules
:
109 del sys
.modules
['pkg_resources']
112 setuptools
.bootstrap_install_from
= egg
115 def use_setuptools(version
=DEFAULT_VERSION
, download_base
=DEFAULT_URL
,
116 to_dir
=os
.curdir
, download_delay
=15):
117 to_dir
= os
.path
.abspath(to_dir
)
118 rep_modules
= 'pkg_resources', 'setuptools'
119 imported
= set(sys
.modules
).intersection(rep_modules
)
123 return _do_download(version
, download_base
, to_dir
, download_delay
)
125 pkg_resources
.require("setuptools>=" + version
)
127 except pkg_resources
.DistributionNotFound
:
128 return _do_download(version
, download_base
, to_dir
, download_delay
)
129 except pkg_resources
.VersionConflict
as VC_err
:
131 msg
= textwrap
.dedent("""
132 The required version of setuptools (>={version}) is not available,
133 and can't be installed while this script is running. Please
134 install a more recent version first, using
135 'easy_install -U setuptools'.
137 (Currently using {VC_err.args[0]!r})
138 """).format(VC_err
=VC_err
, version
=version
)
139 sys
.stderr
.write(msg
)
142 # otherwise, reload ok
143 del pkg_resources
, sys
.modules
['pkg_resources']
144 return _do_download(version
, download_base
, to_dir
, download_delay
)
146 def _clean_check(cmd
, target
):
148 Run the command to download target. If the command fails, clean up before
149 re-raising the error.
152 subprocess
.check_call(cmd
)
153 except subprocess
.CalledProcessError
:
154 if os
.access(target
, os
.F_OK
):
158 def download_file_powershell(url
, target
):
160 Download the file at url to target using Powershell (which will validate
161 trust). Raise an exception if the command cannot complete.
163 target
= os
.path
.abspath(target
)
167 "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(),
169 _clean_check(cmd
, target
)
171 def has_powershell():
172 if platform
.system() != 'Windows':
174 cmd
= ['powershell', '-Command', 'echo test']
175 devnull
= open(os
.path
.devnull
, 'wb')
178 subprocess
.check_call(cmd
, stdout
=devnull
, stderr
=devnull
)
185 download_file_powershell
.viable
= has_powershell
187 def download_file_curl(url
, target
):
188 cmd
= ['curl', url
, '--silent', '--output', target
]
189 _clean_check(cmd
, target
)
192 cmd
= ['curl', '--version']
193 devnull
= open(os
.path
.devnull
, 'wb')
196 subprocess
.check_call(cmd
, stdout
=devnull
, stderr
=devnull
)
203 download_file_curl
.viable
= has_curl
205 def download_file_wget(url
, target
):
206 cmd
= ['wget', url
, '--quiet', '--output-document', target
]
207 _clean_check(cmd
, target
)
210 cmd
= ['wget', '--version']
211 devnull
= open(os
.path
.devnull
, 'wb')
214 subprocess
.check_call(cmd
, stdout
=devnull
, stderr
=devnull
)
221 download_file_wget
.viable
= has_wget
223 def download_file_insecure(url
, target
):
225 Use Python to download the file, even though it cannot authenticate the
229 from urllib
.request
import urlopen
231 from urllib2
import urlopen
235 # Read/write all in one block, so we don't create a corrupt file
236 # if the download is interrupted.
238 dst
= open(target
, "wb")
246 download_file_insecure
.viable
= lambda: True
248 def get_best_downloader():
250 download_file_powershell
,
253 download_file_insecure
,
256 for dl
in downloaders
:
260 def download_setuptools(version
=DEFAULT_VERSION
, download_base
=DEFAULT_URL
,
261 to_dir
=os
.curdir
, delay
=15,
262 downloader_factory
=get_best_downloader
):
263 """Download setuptools from a specified location and return its filename
265 `version` should be a valid setuptools version number that is available
266 as an egg for download under the `download_base` URL (which should end
267 with a '/'). `to_dir` is the directory where the egg will be downloaded.
268 `delay` is the number of seconds to pause before an actual download
271 ``downloader_factory`` should be a function taking no arguments and
272 returning a function for downloading a URL to a target.
274 # making sure we use the absolute path
275 to_dir
= os
.path
.abspath(to_dir
)
276 tgz_name
= "setuptools-%s.tar.gz" % version
277 url
= download_base
+ tgz_name
278 saveto
= os
.path
.join(to_dir
, tgz_name
)
279 if not os
.path
.exists(saveto
): # Avoid repeated downloads
280 log
.warn("Downloading %s", url
)
281 downloader
= downloader_factory()
282 downloader(url
, saveto
)
283 return os
.path
.realpath(saveto
)
286 def _extractall(self
, path
=".", members
=None):
287 """Extract all members from the archive to the current working
288 directory and set owner, modification time and permissions on
289 directories afterwards. `path' specifies a different directory
290 to extract to. `members' is optional and must be a subset of the
291 list returned by getmembers().
295 from tarfile
import ExtractError
301 for tarinfo
in members
:
303 # Extract directories with a safe mode.
304 directories
.append(tarinfo
)
305 tarinfo
= copy
.copy(tarinfo
)
306 tarinfo
.mode
= 448 # decimal for oct 0700
307 self
.extract(tarinfo
, path
)
309 # Reverse sort directories.
310 directories
.sort(key
=operator
.attrgetter('name'), reverse
=True)
312 # Set correct owner, mtime and filemode on directories.
313 for tarinfo
in directories
:
314 dirpath
= os
.path
.join(path
, tarinfo
.name
)
316 self
.chown(tarinfo
, dirpath
)
317 self
.utime(tarinfo
, dirpath
)
318 self
.chmod(tarinfo
, dirpath
)
319 except ExtractError
as e
:
320 if self
.errorlevel
> 1:
323 self
._dbg
(1, "tarfile: %s" % e
)
326 def _build_install_args(options
):
328 Build the arguments to 'python setup.py install' on the setuptools package
330 return ['--user'] if options
.user_install
else []
334 Parse the command line for options
336 parser
= optparse
.OptionParser()
338 '--user', dest
='user_install', action
='store_true', default
=False,
339 help='install in user site package (requires Python 2.6 or later)')
341 '--download-base', dest
='download_base', metavar
="URL",
343 help='alternative URL from where to download the setuptools package')
345 '--insecure', dest
='downloader_factory', action
='store_const',
346 const
=lambda: download_file_insecure
, default
=get_best_downloader
,
347 help='Use internal, non-validating downloader'
349 options
, args
= parser
.parse_args()
350 # positional arguments are ignored
353 def main(version
=DEFAULT_VERSION
):
354 """Install or upgrade setuptools and EasyInstall"""
355 options
= _parse_args()
356 tarball
= download_setuptools(download_base
=options
.download_base
,
357 downloader_factory
=options
.downloader_factory
)
358 return _install(tarball
, _build_install_args(options
))
360 if __name__
== '__main__':