2 """Bootstrap distribute installation
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::
7 from distribute_setup import use_setuptools
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
12 the appropriate options to ``use_setuptools()``.
14 This file can also be run as a script to install or upgrade setuptools.
22 from distutils
import log
25 from site
import USER_SITE
32 def _python_cmd(*args
):
33 args
= (sys
.executable
,) + args
34 return subprocess
.call(args
) == 0
37 # will be used for python 2.3
38 def _python_cmd(*args
):
39 args
= (sys
.executable
,) + args
40 # quoting arguments if windows
41 if sys
.platform
== 'win32':
46 args
= [quote(arg
) for arg
in args
]
47 return os
.spawnl(os
.P_WAIT
, sys
.executable
, *args
) == 0
49 DEFAULT_VERSION
= "0.6.8"
50 DEFAULT_URL
= "http://pypi.python.org/packages/source/d/distribute/"
51 SETUPTOOLS_PKG_INFO
= """\
64 def _install(tarball
):
65 # extracting the tarball
66 tmpdir
= tempfile
.mkdtemp()
67 log
.warn('Extracting in %s', tmpdir
)
71 tar
= tarfile
.open(tarball
)
75 # going in the directory
76 subdir
= os
.path
.join(tmpdir
, os
.listdir(tmpdir
)[0])
78 log
.warn('Now working in %s', subdir
)
81 log
.warn('Installing Distribute')
82 assert _python_cmd('setup.py', 'install')
87 def _build_egg(egg
, tarball
, to_dir
):
88 # extracting the tarball
89 tmpdir
= tempfile
.mkdtemp()
90 log
.warn('Extracting in %s', tmpdir
)
94 tar
= tarfile
.open(tarball
)
98 # going in the directory
99 subdir
= os
.path
.join(tmpdir
, os
.listdir(tmpdir
)[0])
101 log
.warn('Now working in %s', subdir
)
104 log
.warn('Building a Distribute egg in %s', to_dir
)
105 _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir
)
109 # returning the result
111 if not os
.path
.exists(egg
):
112 raise IOError('Could not build the egg.')
115 def _do_download(version
, download_base
, to_dir
, download_delay
):
116 egg
= os
.path
.join(to_dir
, 'distribute-%s-py%d.%d.egg'
117 % (version
, sys
.version_info
[0], sys
.version_info
[1]))
118 if not os
.path
.exists(egg
):
119 tarball
= download_setuptools(version
, download_base
,
120 to_dir
, download_delay
)
121 _build_egg(egg
, tarball
, to_dir
)
122 sys
.path
.insert(0, egg
)
124 setuptools
.bootstrap_install_from
= egg
127 def use_setuptools(version
=DEFAULT_VERSION
, download_base
=DEFAULT_URL
,
128 to_dir
=os
.curdir
, download_delay
=15, no_fake
=True):
129 # making sure we use the absolute path
130 to_dir
= os
.path
.abspath(to_dir
)
131 was_imported
= 'pkg_resources' in sys
.modules
or \
132 'setuptools' in sys
.modules
136 if not hasattr(pkg_resources
, '_distribute'):
141 return _do_download(version
, download_base
, to_dir
, download_delay
)
143 pkg_resources
.require("distribute>="+version
)
145 except pkg_resources
.VersionConflict
:
146 e
= sys
.exc_info()[1]
149 "The required version of distribute (>=%s) is not available,\n"
150 "and can't be installed while this script is running. Please\n"
151 "install a more recent version first, using\n"
152 "'easy_install -U distribute'."
153 "\n\n(Currently using %r)\n" % (version
, e
.args
[0]))
156 del pkg_resources
, sys
.modules
['pkg_resources'] # reload ok
157 return _do_download(version
, download_base
, to_dir
,
159 except pkg_resources
.DistributionNotFound
:
160 return _do_download(version
, download_base
, to_dir
,
164 _create_fake_setuptools_pkg_info(to_dir
)
166 def download_setuptools(version
=DEFAULT_VERSION
, download_base
=DEFAULT_URL
,
167 to_dir
=os
.curdir
, delay
=15):
168 """Download distribute from a specified location and return its filename
170 `version` should be a valid distribute version number that is available
171 as an egg for download under the `download_base` URL (which should end
172 with a '/'). `to_dir` is the directory where the egg will be downloaded.
173 `delay` is the number of seconds to pause before an actual download
176 # making sure we use the absolute path
177 to_dir
= os
.path
.abspath(to_dir
)
179 from urllib
.request
import urlopen
181 from urllib2
import urlopen
182 tgz_name
= "distribute-%s.tar.gz" % version
183 url
= download_base
+ tgz_name
184 saveto
= os
.path
.join(to_dir
, tgz_name
)
186 if not os
.path
.exists(saveto
): # Avoid repeated downloads
188 log
.warn("Downloading %s", url
)
190 # Read/write all in one block, so we don't create a corrupt file
191 # if the download is interrupted.
193 dst
= open(saveto
, "wb")
200 return os
.path
.realpath(saveto
)
203 def _patch_file(path
, content
):
204 """Will backup the file then patch it"""
205 existing_content
= open(path
).read()
206 if existing_content
== content
:
208 log
.warn('Already patched.')
210 log
.warn('Patching...')
220 def _same_content(path
, content
):
221 return open(path
).read() == content
224 def _rename_path(path
):
225 new_name
= path
+ '.OLD.%s' % time
.time()
226 log
.warn('Renaming %s into %s', path
, new_name
)
228 from setuptools
.sandbox
import DirectorySandbox
229 def _violation(*args
):
231 DirectorySandbox
._violation
= _violation
235 os
.rename(path
, new_name
)
239 def _remove_flat_installation(placeholder
):
240 if not os
.path
.isdir(placeholder
):
241 log
.warn('Unkown installation at %s', placeholder
)
244 for file in os
.listdir(placeholder
):
245 if fnmatch
.fnmatch(file, 'setuptools*.egg-info'):
249 log
.warn('Could not locate setuptools*.egg-info')
252 log
.warn('Removing elements out of the way...')
253 pkg_info
= os
.path
.join(placeholder
, file)
254 if os
.path
.isdir(pkg_info
):
255 patched
= _patch_egg_dir(pkg_info
)
257 patched
= _patch_file(pkg_info
, SETUPTOOLS_PKG_INFO
)
260 log
.warn('%s already patched.', pkg_info
)
262 # now let's move the files out of the way
263 for element
in ('setuptools', 'pkg_resources.py', 'site.py'):
264 element
= os
.path
.join(placeholder
, element
)
265 if os
.path
.exists(element
):
266 _rename_path(element
)
268 log
.warn('Could not find the %s element of the '
269 'Setuptools distribution', element
)
273 def _after_install(dist
):
274 log
.warn('After install bootstrap.')
275 placeholder
= dist
.get_command_obj('install').install_purelib
276 _create_fake_setuptools_pkg_info(placeholder
)
278 def _create_fake_setuptools_pkg_info(placeholder
):
279 if not placeholder
or not os
.path
.exists(placeholder
):
280 log
.warn('Could not find the install location')
282 pyver
= '%s.%s' % (sys
.version_info
[0], sys
.version_info
[1])
283 setuptools_file
= 'setuptools-0.6c9-py%s.egg-info' % pyver
284 pkg_info
= os
.path
.join(placeholder
, setuptools_file
)
285 if os
.path
.exists(pkg_info
):
286 log
.warn('%s already exists', pkg_info
)
288 log
.warn('Creating %s', pkg_info
)
289 f
= open(pkg_info
, 'w')
291 f
.write(SETUPTOOLS_PKG_INFO
)
294 pth_file
= os
.path
.join(placeholder
, 'setuptools.pth')
295 log
.warn('Creating %s', pth_file
)
296 f
= open(pth_file
, 'w')
298 f
.write(os
.path
.join(os
.curdir
, setuptools_file
))
303 def _patch_egg_dir(path
):
304 # let's check if it's already patched
305 pkg_info
= os
.path
.join(path
, 'EGG-INFO', 'PKG-INFO')
306 if os
.path
.exists(pkg_info
):
307 if _same_content(pkg_info
, SETUPTOOLS_PKG_INFO
):
308 log
.warn('%s already patched.', pkg_info
)
312 os
.mkdir(os
.path
.join(path
, 'EGG-INFO'))
313 pkg_info
= os
.path
.join(path
, 'EGG-INFO', 'PKG-INFO')
314 f
= open(pkg_info
, 'w')
316 f
.write(SETUPTOOLS_PKG_INFO
)
322 def _before_install():
323 log
.warn('Before install bootstrap.')
327 def _under_prefix(location
):
328 if 'install' not in sys
.argv
:
330 args
= sys
.argv
[sys
.argv
.index('install')+1:]
331 for index
, arg
in enumerate(args
):
332 for option
in ('--root', '--prefix'):
333 if arg
.startswith('%s=' % option
):
334 top_dir
= arg
.split('root=')[-1]
335 return location
.startswith(top_dir
)
337 if len(args
) > index
:
338 top_dir
= args
[index
+1]
339 return location
.startswith(top_dir
)
340 elif option
== '--user' and USER_SITE
is not None:
341 return location
.startswith(USER_SITE
)
345 def _fake_setuptools():
346 log
.warn('Scanning installed packages')
351 log
.warn('Setuptools or Distribute does not seem to be installed.')
353 ws
= pkg_resources
.working_set
355 setuptools_dist
= ws
.find(pkg_resources
.Requirement
.parse('setuptools',
359 setuptools_dist
= ws
.find(pkg_resources
.Requirement
.parse('setuptools'))
361 if setuptools_dist
is None:
362 log
.warn('No setuptools distribution found')
364 # detecting if it was already faked
365 setuptools_location
= setuptools_dist
.location
366 log
.warn('Setuptools installation detected at %s', setuptools_location
)
368 # if --root or --preix was provided, and if
369 # setuptools is not located in them, we don't patch it
370 if not _under_prefix(setuptools_location
):
371 log
.warn('Not patching, --root or --prefix is installing Distribute'
372 ' in another location')
375 # let's see if its an egg
376 if not setuptools_location
.endswith('.egg'):
377 log
.warn('Non-egg installation')
378 res
= _remove_flat_installation(setuptools_location
)
382 log
.warn('Egg installation')
383 pkg_info
= os
.path
.join(setuptools_location
, 'EGG-INFO', 'PKG-INFO')
384 if (os
.path
.exists(pkg_info
) and
385 _same_content(pkg_info
, SETUPTOOLS_PKG_INFO
)):
386 log
.warn('Already patched.')
388 log
.warn('Patching...')
389 # let's create a fake egg replacing setuptools one
390 res
= _patch_egg_dir(setuptools_location
)
393 log
.warn('Patched done.')
398 log
.warn('Relaunching...')
399 # we have to relaunch the process
400 args
= [sys
.executable
] + sys
.argv
401 sys
.exit(subprocess
.call(args
))
404 def _extractall(self
, path
=".", members
=None):
405 """Extract all members from the archive to the current working
406 directory and set owner, modification time and permissions on
407 directories afterwards. `path' specifies a different directory
408 to extract to. `members' is optional and must be a subset of the
409 list returned by getmembers().
413 from tarfile
import ExtractError
419 for tarinfo
in members
:
421 # Extract directories with a safe mode.
422 directories
.append(tarinfo
)
423 tarinfo
= copy
.copy(tarinfo
)
424 tarinfo
.mode
= 448 # decimal for oct 0700
425 self
.extract(tarinfo
, path
)
427 # Reverse sort directories.
428 if sys
.version_info
< (2, 4):
429 def sorter(dir1
, dir2
):
430 return cmp(dir1
.name
, dir2
.name
)
431 directories
.sort(sorter
)
432 directories
.reverse()
434 directories
.sort(key
=operator
.attrgetter('name'), reverse
=True)
436 # Set correct owner, mtime and filemode on directories.
437 for tarinfo
in directories
:
438 dirpath
= os
.path
.join(path
, tarinfo
.name
)
440 self
.chown(tarinfo
, dirpath
)
441 self
.utime(tarinfo
, dirpath
)
442 self
.chmod(tarinfo
, dirpath
)
444 e
= sys
.exc_info()[1]
445 if self
.errorlevel
> 1:
448 self
._dbg
(1, "tarfile: %s" % e
)
451 def main(argv
, version
=DEFAULT_VERSION
):
452 """Install or upgrade setuptools and EasyInstall"""
453 tarball
= download_setuptools()
457 if __name__
== '__main__':