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 directory with 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 :
2931 USER_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):
100102 to_dir , download_delay )
101103 _build_egg (egg , tarball , to_dir )
102104 sys .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+
103111 import setuptools
104112 setuptools .bootstrap_install_from = egg
105113
106114
107115def use_setuptools (version = DEFAULT_VERSION , download_base = DEFAULT_URL ,
108116 to_dir = os .curdir , download_delay = 15 ):
109- # making sure we use the absolute path
110117 to_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 )
113120 try :
114121 import pkg_resources
115122 except ImportError :
116123 return _do_download (version , download_base , to_dir , download_delay )
117124 try :
118125 pkg_resources .require ("setuptools>=" + version )
119126 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 )
134127 except 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
150275 to_dir = os .path .abspath (to_dir )
151- try :
152- from urllib .request import urlopen
153- except ImportError :
154- from urllib2 import urlopen
155276 tgz_name = "setuptools-%s.tar.gz" % version
156277 url = download_base + tgz_name
157278 saveto = os .path .join (to_dir , tgz_name )
158- src = dst = None
159279 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 ()
280+ log .warn ("Downloading %s" , url )
281+ downloader = downloader_factory ()
282+ downloader (url , saveto )
173283 return os .path .realpath (saveto )
174284
175285
@@ -197,13 +307,7 @@ def _extractall(self, path=".", members=None):
197307 self .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.
209313 for tarinfo in directories :
@@ -212,8 +316,7 @@ def sorter(dir1, dir2):
212316 self .chown (tarinfo , dirpath )
213317 self .utime (tarinfo , dirpath )
214318 self .chmod (tarinfo , dirpath )
215- except ExtractError :
216- e = sys .exc_info ()[1 ]
319+ except ExtractError as e :
217320 if self .errorlevel > 1 :
218321 raise
219322 else :
@@ -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" ,
245342 default = DEFAULT_URL ,
246343 help = '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+ )
247349 options , args = parser .parse_args ()
248350 # positional arguments are ignored
249351 return options
250352
251353def main (version = DEFAULT_VERSION ):
252354 """Install or upgrade setuptools and EasyInstall"""
253355 options = _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 )
255358 return _install (tarball , _build_install_args (options ))
256359
257360if __name__ == '__main__' :
0 commit comments