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.
25 from distutils
import log
28 from site
import USER_SITE
35 def _python_cmd(*args
):
36 args
= (sys
.executable
,) + args
37 return subprocess
.call(args
) == 0
40 # will be used for python 2.3
41 def _python_cmd(*args
):
42 args
= (sys
.executable
,) + args
43 # quoting arguments if windows
44 if sys
.platform
== 'win32':
49 args
= [quote(arg
) for arg
in args
]
50 return os
.spawnl(os
.P_WAIT
, sys
.executable
, *args
) == 0
52 DEFAULT_VERSION
= "0.6.36"
53 DEFAULT_URL
= "http://pypi.python.org/packages/source/d/distribute/"
54 SETUPTOOLS_FAKED_VERSION
= "0.6c11"
56 SETUPTOOLS_PKG_INFO
= """\
66 """ % SETUPTOOLS_FAKED_VERSION
69 def _install(tarball
, install_args
=()):
70 # extracting the tarball
71 tmpdir
= tempfile
.mkdtemp()
72 log
.warn('Extracting in %s', tmpdir
)
76 tar
= tarfile
.open(tarball
)
80 # going in the directory
81 subdir
= os
.path
.join(tmpdir
, os
.listdir(tmpdir
)[0])
83 log
.warn('Now working in %s', subdir
)
86 log
.warn('Installing Distribute')
87 if not _python_cmd('setup.py', 'install', *install_args
):
88 log
.warn('Something went wrong during the installation.')
89 log
.warn('See the error message above.')
97 def _build_egg(egg
, tarball
, to_dir
):
98 # extracting the tarball
99 tmpdir
= tempfile
.mkdtemp()
100 log
.warn('Extracting in %s', tmpdir
)
104 tar
= tarfile
.open(tarball
)
108 # going in the directory
109 subdir
= os
.path
.join(tmpdir
, os
.listdir(tmpdir
)[0])
111 log
.warn('Now working in %s', subdir
)
114 log
.warn('Building a Distribute egg in %s', to_dir
)
115 _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir
)
119 shutil
.rmtree(tmpdir
)
120 # returning the result
122 if not os
.path
.exists(egg
):
123 raise IOError('Could not build the egg.')
126 def _do_download(version
, download_base
, to_dir
, download_delay
):
127 egg
= os
.path
.join(to_dir
, 'distribute-%s-py%d.%d.egg'
128 % (version
, sys
.version_info
[0], sys
.version_info
[1]))
129 if not os
.path
.exists(egg
):
130 tarball
= download_setuptools(version
, download_base
,
131 to_dir
, download_delay
)
132 _build_egg(egg
, tarball
, to_dir
)
133 sys
.path
.insert(0, egg
)
135 setuptools
.bootstrap_install_from
= egg
138 def use_setuptools(version
=DEFAULT_VERSION
, download_base
=DEFAULT_URL
,
139 to_dir
=os
.curdir
, download_delay
=15, no_fake
=True):
140 # making sure we use the absolute path
141 to_dir
= os
.path
.abspath(to_dir
)
142 was_imported
= 'pkg_resources' in sys
.modules
or \
143 'setuptools' in sys
.modules
147 if not hasattr(pkg_resources
, '_distribute'):
152 return _do_download(version
, download_base
, to_dir
, download_delay
)
154 pkg_resources
.require("distribute>=" + version
)
156 except pkg_resources
.VersionConflict
:
157 e
= sys
.exc_info()[1]
160 "The required version of distribute (>=%s) is not available,\n"
161 "and can't be installed while this script is running. Please\n"
162 "install a more recent version first, using\n"
163 "'easy_install -U distribute'."
164 "\n\n(Currently using %r)\n" % (version
, e
.args
[0]))
167 del pkg_resources
, sys
.modules
['pkg_resources'] # reload ok
168 return _do_download(version
, download_base
, to_dir
,
170 except pkg_resources
.DistributionNotFound
:
171 return _do_download(version
, download_base
, to_dir
,
175 _create_fake_setuptools_pkg_info(to_dir
)
178 def download_setuptools(version
=DEFAULT_VERSION
, download_base
=DEFAULT_URL
,
179 to_dir
=os
.curdir
, delay
=15):
180 """Download distribute from a specified location and return its filename
182 `version` should be a valid distribute version number that is available
183 as an egg for download under the `download_base` URL (which should end
184 with a '/'). `to_dir` is the directory where the egg will be downloaded.
185 `delay` is the number of seconds to pause before an actual download
188 # making sure we use the absolute path
189 to_dir
= os
.path
.abspath(to_dir
)
191 from urllib
.request
import urlopen
193 from urllib2
import urlopen
194 tgz_name
= "distribute-%s.tar.gz" % version
195 url
= download_base
+ tgz_name
196 saveto
= os
.path
.join(to_dir
, tgz_name
)
198 if not os
.path
.exists(saveto
): # Avoid repeated downloads
200 log
.warn("Downloading %s", url
)
202 # Read/write all in one block, so we don't create a corrupt file
203 # if the download is interrupted.
205 dst
= open(saveto
, "wb")
212 return os
.path
.realpath(saveto
)
215 def _no_sandbox(function
):
216 def __no_sandbox(*args
, **kw
):
218 from setuptools
.sandbox
import DirectorySandbox
219 if not hasattr(DirectorySandbox
, '_old'):
220 def violation(*args
):
222 DirectorySandbox
._old
= DirectorySandbox
._violation
223 DirectorySandbox
._violation
= violation
231 return function(*args
, **kw
)
234 DirectorySandbox
._violation
= DirectorySandbox
._old
235 del DirectorySandbox
._old
240 def _patch_file(path
, content
):
241 """Will backup the file then patch it"""
243 existing_content
= f
.read()
245 if existing_content
== content
:
247 log
.warn('Already patched.')
249 log
.warn('Patching...')
258 _patch_file
= _no_sandbox(_patch_file
)
261 def _same_content(path
, content
):
263 existing_content
= f
.read()
265 return existing_content
== content
268 def _rename_path(path
):
269 new_name
= path
+ '.OLD.%s' % time
.time()
270 log
.warn('Renaming %s to %s', path
, new_name
)
271 os
.rename(path
, new_name
)
275 def _remove_flat_installation(placeholder
):
276 if not os
.path
.isdir(placeholder
):
277 log
.warn('Unkown installation at %s', placeholder
)
280 for file in os
.listdir(placeholder
):
281 if fnmatch
.fnmatch(file, 'setuptools*.egg-info'):
285 log
.warn('Could not locate setuptools*.egg-info')
288 log
.warn('Moving elements out of the way...')
289 pkg_info
= os
.path
.join(placeholder
, file)
290 if os
.path
.isdir(pkg_info
):
291 patched
= _patch_egg_dir(pkg_info
)
293 patched
= _patch_file(pkg_info
, SETUPTOOLS_PKG_INFO
)
296 log
.warn('%s already patched.', pkg_info
)
298 # now let's move the files out of the way
299 for element
in ('setuptools', 'pkg_resources.py', 'site.py'):
300 element
= os
.path
.join(placeholder
, element
)
301 if os
.path
.exists(element
):
302 _rename_path(element
)
304 log
.warn('Could not find the %s element of the '
305 'Setuptools distribution', element
)
308 _remove_flat_installation
= _no_sandbox(_remove_flat_installation
)
311 def _after_install(dist
):
312 log
.warn('After install bootstrap.')
313 placeholder
= dist
.get_command_obj('install').install_purelib
314 _create_fake_setuptools_pkg_info(placeholder
)
317 def _create_fake_setuptools_pkg_info(placeholder
):
318 if not placeholder
or not os
.path
.exists(placeholder
):
319 log
.warn('Could not find the install location')
321 pyver
= '%s.%s' % (sys
.version_info
[0], sys
.version_info
[1])
322 setuptools_file
= 'setuptools-%s-py%s.egg-info' % \
323 (SETUPTOOLS_FAKED_VERSION
, pyver
)
324 pkg_info
= os
.path
.join(placeholder
, setuptools_file
)
325 if os
.path
.exists(pkg_info
):
326 log
.warn('%s already exists', pkg_info
)
329 log
.warn('Creating %s', pkg_info
)
331 f
= open(pkg_info
, 'w')
332 except EnvironmentError:
333 log
.warn("Don't have permissions to write %s, skipping", pkg_info
)
336 f
.write(SETUPTOOLS_PKG_INFO
)
340 pth_file
= os
.path
.join(placeholder
, 'setuptools.pth')
341 log
.warn('Creating %s', pth_file
)
342 f
= open(pth_file
, 'w')
344 f
.write(os
.path
.join(os
.curdir
, setuptools_file
))
348 _create_fake_setuptools_pkg_info
= _no_sandbox(
349 _create_fake_setuptools_pkg_info
353 def _patch_egg_dir(path
):
354 # let's check if it's already patched
355 pkg_info
= os
.path
.join(path
, 'EGG-INFO', 'PKG-INFO')
356 if os
.path
.exists(pkg_info
):
357 if _same_content(pkg_info
, SETUPTOOLS_PKG_INFO
):
358 log
.warn('%s already patched.', pkg_info
)
362 os
.mkdir(os
.path
.join(path
, 'EGG-INFO'))
363 pkg_info
= os
.path
.join(path
, 'EGG-INFO', 'PKG-INFO')
364 f
= open(pkg_info
, 'w')
366 f
.write(SETUPTOOLS_PKG_INFO
)
371 _patch_egg_dir
= _no_sandbox(_patch_egg_dir
)
374 def _before_install():
375 log
.warn('Before install bootstrap.')
379 def _under_prefix(location
):
380 if 'install' not in sys
.argv
:
382 args
= sys
.argv
[sys
.argv
.index('install') + 1:]
383 for index
, arg
in enumerate(args
):
384 for option
in ('--root', '--prefix'):
385 if arg
.startswith('%s=' % option
):
386 top_dir
= arg
.split('root=')[-1]
387 return location
.startswith(top_dir
)
389 if len(args
) > index
:
390 top_dir
= args
[index
+ 1]
391 return location
.startswith(top_dir
)
392 if arg
== '--user' and USER_SITE
is not None:
393 return location
.startswith(USER_SITE
)
397 def _fake_setuptools():
398 log
.warn('Scanning installed packages')
403 log
.warn('Setuptools or Distribute does not seem to be installed.')
405 ws
= pkg_resources
.working_set
407 setuptools_dist
= ws
.find(
408 pkg_resources
.Requirement
.parse('setuptools', replacement
=False)
412 setuptools_dist
= ws
.find(
413 pkg_resources
.Requirement
.parse('setuptools')
416 if setuptools_dist
is None:
417 log
.warn('No setuptools distribution found')
419 # detecting if it was already faked
420 setuptools_location
= setuptools_dist
.location
421 log
.warn('Setuptools installation detected at %s', setuptools_location
)
423 # if --root or --preix was provided, and if
424 # setuptools is not located in them, we don't patch it
425 if not _under_prefix(setuptools_location
):
426 log
.warn('Not patching, --root or --prefix is installing Distribute'
427 ' in another location')
430 # let's see if its an egg
431 if not setuptools_location
.endswith('.egg'):
432 log
.warn('Non-egg installation')
433 res
= _remove_flat_installation(setuptools_location
)
437 log
.warn('Egg installation')
438 pkg_info
= os
.path
.join(setuptools_location
, 'EGG-INFO', 'PKG-INFO')
439 if (os
.path
.exists(pkg_info
) and
440 _same_content(pkg_info
, SETUPTOOLS_PKG_INFO
)):
441 log
.warn('Already patched.')
443 log
.warn('Patching...')
444 # let's create a fake egg replacing setuptools one
445 res
= _patch_egg_dir(setuptools_location
)
448 log
.warn('Patching complete.')
453 log
.warn('Relaunching...')
454 # we have to relaunch the process
455 # pip marker to avoid a relaunch bug
456 _cmd1
= ['-c', 'install', '--single-version-externally-managed']
457 _cmd2
= ['-c', 'install', '--record']
458 if sys
.argv
[:3] == _cmd1
or sys
.argv
[:3] == _cmd2
:
459 sys
.argv
[0] = 'setup.py'
460 args
= [sys
.executable
] + sys
.argv
461 sys
.exit(subprocess
.call(args
))
464 def _extractall(self
, path
=".", members
=None):
465 """Extract all members from the archive to the current working
466 directory and set owner, modification time and permissions on
467 directories afterwards. `path' specifies a different directory
468 to extract to. `members' is optional and must be a subset of the
469 list returned by getmembers().
473 from tarfile
import ExtractError
479 for tarinfo
in members
:
481 # Extract directories with a safe mode.
482 directories
.append(tarinfo
)
483 tarinfo
= copy
.copy(tarinfo
)
484 tarinfo
.mode
= 448 # decimal for oct 0700
485 self
.extract(tarinfo
, path
)
487 # Reverse sort directories.
488 if sys
.version_info
< (2, 4):
489 def sorter(dir1
, dir2
):
490 return cmp(dir1
.name
, dir2
.name
)
491 directories
.sort(sorter
)
492 directories
.reverse()
494 directories
.sort(key
=operator
.attrgetter('name'), reverse
=True)
496 # Set correct owner, mtime and filemode on directories.
497 for tarinfo
in directories
:
498 dirpath
= os
.path
.join(path
, tarinfo
.name
)
500 self
.chown(tarinfo
, dirpath
)
501 self
.utime(tarinfo
, dirpath
)
502 self
.chmod(tarinfo
, dirpath
)
504 e
= sys
.exc_info()[1]
505 if self
.errorlevel
> 1:
508 self
._dbg
(1, "tarfile: %s" % e
)
511 def _build_install_args(options
):
513 Build the arguments to 'python setup.py install' on the distribute package
516 if options
.user_install
:
517 if sys
.version_info
< (2, 6):
518 log
.warn("--user requires Python 2.6 or later")
520 install_args
.append('--user')
525 Parse the command line for options
527 parser
= optparse
.OptionParser()
529 '--user', dest
='user_install', action
='store_true', default
=False,
530 help='install in user site package (requires Python 2.6 or later)')
532 '--download-base', dest
='download_base', metavar
="URL",
534 help='alternative URL from where to download the distribute package')
535 options
, args
= parser
.parse_args()
536 # positional arguments are ignored
539 def main(version
=DEFAULT_VERSION
):
540 """Install or upgrade setuptools and EasyInstall"""
541 options
= _parse_args()
542 tarball
= download_setuptools(download_base
=options
.download_base
)
543 return _install(tarball
, _build_install_args(options
))
545 if __name__
== '__main__':