1- #!python
1+ #!/usr/bin/env python
22"""Bootstrap setuptools installation
33
4- If you want to use setuptools in your package's setup.py, just include this
5- file in the same directorywith it, and add this to the top of your setup.py::
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::
66
77 from ez_setup import use_setuptools
88 use_setuptools()
99
10- If you want to require a specific version of setuptools, set a download
11- mirror, or use an alternate download directory,you can do so by supplying
10+ To require a specific version of setuptools, set a download
11+ mirror, or use an alternate download directory,simply supply
1212the appropriate options to ``use_setuptools()``.
1313
1414This file can also be run as a script to install or upgrade setuptools.
2020import tarfile
2121import optparse
2222import subprocess
23+ import platform
24+ import textwrap
2325
2426from distutils import log
2527
2830except ImportError :
2931USER_SITE = None
3032
31- DEFAULT_VERSION = "0.8 "
33+ DEFAULT_VERSION = "2.1 "
3234DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
3335
3436def _python_cmd (* args ):
@@ -100,76 +102,184 @@ def _do_download(version, download_base, to_dir, download_delay):
100102to_dir ,download_delay )
101103_build_egg (egg ,tarball ,to_dir )
102104sys .path .insert (0 ,egg )
105+
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' ]
110+
103111import setuptools
104112setuptools .bootstrap_install_from = egg
105113
106114
107115def use_setuptools (version = DEFAULT_VERSION ,download_base = DEFAULT_URL ,
108116to_dir = os .curdir ,download_delay = 15 ):
109- # making sure we use the absolute path
110117to_dir = os .path .abspath (to_dir )
111- was_imported = 'pkg_resources' in sys . modules or \
112- 'setuptools' in sys .modules
118+ rep_modules = 'pkg_resources' , 'setuptools'
119+ imported = set ( sys .modules ). intersection ( rep_modules )
113120try :
114121import pkg_resources
115122except ImportError :
116123return _do_download (version ,download_base ,to_dir ,download_delay )
117124try :
118125pkg_resources .require ("setuptools>=" + version )
119126return
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 )
134127except pkg_resources .DistributionNotFound :
135- return _do_download (version ,download_base ,to_dir ,
136- download_delay )
128+ return _do_download (version ,download_base ,to_dir ,download_delay )
129+ except pkg_resources .VersionConflict as VC_err :
130+ if imported :
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'.
136+
137+ (Currently using {VC_err.args[0]!r})
138+ """ ).format (VC_err = VC_err ,version = version )
139+ sys .stderr .write (msg )
140+ sys .exit (2 )
141+
142+ # otherwise, reload ok
143+ del pkg_resources ,sys .modules ['pkg_resources' ]
144+ return _do_download (version ,download_base ,to_dir ,download_delay )
145+
146+ def _clean_check (cmd ,target ):
147+ """
148+ Run the command to download target. If the command fails, clean up before
149+ re-raising the error.
150+ """
151+ try :
152+ subprocess .check_call (cmd )
153+ except subprocess .CalledProcessError :
154+ if os .access (target ,os .F_OK ):
155+ os .unlink (target )
156+ raise
157+
158+ def download_file_powershell (url ,target ):
159+ """
160+ Download the file at url to target using Powershell (which will validate
161+ trust). Raise an exception if the command cannot complete.
162+ """
163+ target = os .path .abspath (target )
164+ cmd = [
165+ 'powershell' ,
166+ '-Command' ,
167+ "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars (),
168+ ]
169+ _clean_check (cmd ,target )
170+
171+ def has_powershell ():
172+ if platform .system ()!= 'Windows' :
173+ return False
174+ cmd = ['powershell' ,'-Command' ,'echo test' ]
175+ devnull = open (os .path .devnull ,'wb' )
176+ try :
177+ try :
178+ subprocess .check_call (cmd ,stdout = devnull ,stderr = devnull )
179+ except :
180+ return False
181+ finally :
182+ devnull .close ()
183+ return True
184+
185+ download_file_powershell .viable = has_powershell
137186
187+ def download_file_curl (url ,target ):
188+ cmd = ['curl' ,url ,'--silent' ,'--output' ,target ]
189+ _clean_check (cmd ,target )
190+
191+ def has_curl ():
192+ cmd = ['curl' ,'--version' ]
193+ devnull = open (os .path .devnull ,'wb' )
194+ try :
195+ try :
196+ subprocess .check_call (cmd ,stdout = devnull ,stderr = devnull )
197+ except :
198+ return False
199+ finally :
200+ devnull .close ()
201+ return True
202+
203+ download_file_curl .viable = has_curl
204+
205+ def download_file_wget (url ,target ):
206+ cmd = ['wget' ,url ,'--quiet' ,'--output-document' ,target ]
207+ _clean_check (cmd ,target )
208+
209+ def has_wget ():
210+ cmd = ['wget' ,'--version' ]
211+ devnull = open (os .path .devnull ,'wb' )
212+ try :
213+ try :
214+ subprocess .check_call (cmd ,stdout = devnull ,stderr = devnull )
215+ except :
216+ return False
217+ finally :
218+ devnull .close ()
219+ return True
220+
221+ download_file_wget .viable = has_wget
222+
223+ def download_file_insecure (url ,target ):
224+ """
225+ Use Python to download the file, even though it cannot authenticate the
226+ connection.
227+ """
228+ try :
229+ from urllib .request import urlopen
230+ except ImportError :
231+ from urllib2 import urlopen
232+ src = dst = None
233+ try :
234+ src = urlopen (url )
235+ # Read/write all in one block, so we don't create a corrupt file
236+ # if the download is interrupted.
237+ data = src .read ()
238+ dst = open (target ,"wb" )
239+ dst .write (data )
240+ finally :
241+ if src :
242+ src .close ()
243+ if dst :
244+ dst .close ()
245+
246+ download_file_insecure .viable = lambda :True
247+
248+ def get_best_downloader ():
249+ downloaders = [
250+ download_file_powershell ,
251+ download_file_curl ,
252+ download_file_wget ,
253+ download_file_insecure ,
254+ ]
255+
256+ for dl in downloaders :
257+ if dl .viable ():
258+ return dl
138259
139260def download_setuptools (version = DEFAULT_VERSION ,download_base = DEFAULT_URL ,
140- to_dir = os .curdir ,delay = 15 ):
261+ to_dir = os .curdir ,delay = 15 ,
262+ downloader_factory = get_best_downloader ):
141263"""Download setuptools from a specified location and return its filename
142264
143265 `version` should be a valid setuptools version number that is available
144266 as an egg for download under the `download_base` URL (which should end
145267 with a '/'). `to_dir` is the directory where the egg will be downloaded.
146268 `delay` is the number of seconds to pause before an actual download
147269 attempt.
270+
271+ ``downloader_factory`` should be a function taking no arguments and
272+ returning a function for downloading a URL to a target.
148273 """
149274# making sure we use the absolute path
150275to_dir = os .path .abspath (to_dir )
151- try :
152- from urllib .request import urlopen
153- except ImportError :
154- from urllib2 import urlopen
155276tgz_name = "setuptools-%s.tar.gz" % version
156277url = download_base + tgz_name
157278saveto = os .path .join (to_dir ,tgz_name )
158- src = dst = None
159279if 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 ()
280+ log .warn ("Downloading %s" ,url )
281+ downloader = downloader_factory ()
282+ downloader (url ,saveto )
173283return os .path .realpath (saveto )
174284
175285
@@ -197,13 +307,7 @@ def _extractall(self, path=".", members=None):
197307self .extract (tarinfo ,path )
198308
199309# 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 )
310+ directories .sort (key = operator .attrgetter ('name' ),reverse = True )
207311
208312# Set correct owner, mtime and filemode on directories.
209313for tarinfo in directories :
@@ -212,8 +316,7 @@ def sorter(dir1, dir2):
212316self .chown (tarinfo ,dirpath )
213317self .utime (tarinfo ,dirpath )
214318self .chmod (tarinfo ,dirpath )
215- except ExtractError :
216- e = sys .exc_info ()[1 ]
319+ except ExtractError as e :
217320if self .errorlevel > 1 :
218321raise
219322else :
@@ -224,13 +327,7 @@ def _build_install_args(options):
224327"""
225328 Build the arguments to 'python setup.py install' on the setuptools package
226329 """
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
330+ return ['--user' ]if options .user_install else []
234331
235332def _parse_args ():
236333"""
@@ -244,14 +341,20 @@ def _parse_args():
244341'--download-base' ,dest = 'download_base' ,metavar = "URL" ,
245342default = DEFAULT_URL ,
246343help = 'alternative URL from where to download the setuptools package' )
344+ parser .add_option (
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'
348+ )
247349options ,args = parser .parse_args ()
248350# positional arguments are ignored
249351return options
250352
251353def main (version = DEFAULT_VERSION ):
252354"""Install or upgrade setuptools and EasyInstall"""
253355options = _parse_args ()
254- tarball = download_setuptools (download_base = options .download_base )
356+ tarball = download_setuptools (download_base = options .download_base ,
357+ downloader_factory = options .downloader_factory )
255358return _install (tarball ,_build_install_args (options ))
256359
257360if __name__ == '__main__' :