source: orange/distribute_setup.py @ 11618:1e9af978cbf2

Revision 11618:1e9af978cbf2, 17.3 KB checked in by Ales Erjavec <ales.erjavec@…>, 10 months ago (diff)

Updated included distribute_setup.py to latest 0.6.48 version.

(for compatibility with existing installed setuptools 0.7)

Line 
1#!python
2"""Bootstrap distribute installation
3
4If you want to use setuptools in your package's setup.py, just include this
5file in the same directory with it, and add this to the top of your setup.py::
6
7    from distribute_setup import use_setuptools
8    use_setuptools()
9
10If you want to require a specific version of setuptools, set a download
11mirror, or use an alternate download directory, you can do so by supplying
12the appropriate options to ``use_setuptools()``.
13
14This file can also be run as a script to install or upgrade setuptools.
15"""
16import os
17import shutil
18import sys
19import time
20import fnmatch
21import tempfile
22import tarfile
23import optparse
24
25from distutils import log
26
27try:
28    from site import USER_SITE
29except ImportError:
30    USER_SITE = None
31
32try:
33    import subprocess
34
35    def _python_cmd(*args):
36        args = (sys.executable,) + args
37        return subprocess.call(args) == 0
38
39except ImportError:
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':
45            def quote(arg):
46                if ' ' in arg:
47                    return '"%s"' % arg
48                return arg
49            args = [quote(arg) for arg in args]
50        return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
51
52DEFAULT_VERSION = "0.6.48"
53DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
54SETUPTOOLS_FAKED_VERSION = "0.6c11"
55
56SETUPTOOLS_PKG_INFO = """\
57Metadata-Version: 1.0
58Name: setuptools
59Version: %s
60Summary: xxxx
61Home-page: xxx
62Author: xxx
63Author-email: xxx
64License: xxx
65Description: xxx
66""" % SETUPTOOLS_FAKED_VERSION
67
68
69def _install(tarball, install_args=()):
70    # extracting the tarball
71    tmpdir = tempfile.mkdtemp()
72    log.warn('Extracting in %s', tmpdir)
73    old_wd = os.getcwd()
74    try:
75        os.chdir(tmpdir)
76        tar = tarfile.open(tarball)
77        _extractall(tar)
78        tar.close()
79
80        # going in the directory
81        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
82        os.chdir(subdir)
83        log.warn('Now working in %s', subdir)
84
85        # installing
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.')
90            # exitcode will be 2
91            return 2
92    finally:
93        os.chdir(old_wd)
94        shutil.rmtree(tmpdir)
95
96
97def _build_egg(egg, tarball, to_dir):
98    # extracting the tarball
99    tmpdir = tempfile.mkdtemp()
100    log.warn('Extracting in %s', tmpdir)
101    old_wd = os.getcwd()
102    try:
103        os.chdir(tmpdir)
104        tar = tarfile.open(tarball)
105        _extractall(tar)
106        tar.close()
107
108        # going in the directory
109        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
110        os.chdir(subdir)
111        log.warn('Now working in %s', subdir)
112
113        # building an egg
114        log.warn('Building a Distribute egg in %s', to_dir)
115        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
116
117    finally:
118        os.chdir(old_wd)
119        shutil.rmtree(tmpdir)
120    # returning the result
121    log.warn(egg)
122    if not os.path.exists(egg):
123        raise IOError('Could not build the egg.')
124
125
126def _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)
134    import setuptools
135    setuptools.bootstrap_install_from = egg
136
137
138def 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
144    try:
145        try:
146            import pkg_resources
147
148            # Setuptools 0.7b and later is a suitable (and preferable)
149            # substitute for any Distribute version.
150            try:
151                pkg_resources.require("setuptools>=0.7b")
152                return
153            except (pkg_resources.DistributionNotFound,
154                    pkg_resources.VersionConflict):
155                pass
156
157            if not hasattr(pkg_resources, '_distribute'):
158                if not no_fake:
159                    _fake_setuptools()
160                raise ImportError
161        except ImportError:
162            return _do_download(version, download_base, to_dir, download_delay)
163        try:
164            pkg_resources.require("distribute>=" + version)
165            return
166        except pkg_resources.VersionConflict:
167            e = sys.exc_info()[1]
168            if was_imported:
169                sys.stderr.write(
170                "The required version of distribute (>=%s) is not available,\n"
171                "and can't be installed while this script is running. Please\n"
172                "install a more recent version first, using\n"
173                "'easy_install -U distribute'."
174                "\n\n(Currently using %r)\n" % (version, e.args[0]))
175                sys.exit(2)
176            else:
177                del pkg_resources, sys.modules['pkg_resources']    # reload ok
178                return _do_download(version, download_base, to_dir,
179                                    download_delay)
180        except pkg_resources.DistributionNotFound:
181            return _do_download(version, download_base, to_dir,
182                                download_delay)
183    finally:
184        if not no_fake:
185            _create_fake_setuptools_pkg_info(to_dir)
186
187
188def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
189                        to_dir=os.curdir, delay=15):
190    """Download distribute from a specified location and return its filename
191
192    `version` should be a valid distribute version number that is available
193    as an egg for download under the `download_base` URL (which should end
194    with a '/'). `to_dir` is the directory where the egg will be downloaded.
195    `delay` is the number of seconds to pause before an actual download
196    attempt.
197    """
198    # making sure we use the absolute path
199    to_dir = os.path.abspath(to_dir)
200    try:
201        from urllib.request import urlopen
202    except ImportError:
203        from urllib2 import urlopen
204    tgz_name = "distribute-%s.tar.gz" % version
205    url = download_base + tgz_name
206    saveto = os.path.join(to_dir, tgz_name)
207    src = dst = None
208    if not os.path.exists(saveto):  # Avoid repeated downloads
209        try:
210            log.warn("Downloading %s", url)
211            src = urlopen(url)
212            # Read/write all in one block, so we don't create a corrupt file
213            # if the download is interrupted.
214            data = src.read()
215            dst = open(saveto, "wb")
216            dst.write(data)
217        finally:
218            if src:
219                src.close()
220            if dst:
221                dst.close()
222    return os.path.realpath(saveto)
223
224
225def _no_sandbox(function):
226    def __no_sandbox(*args, **kw):
227        try:
228            from setuptools.sandbox import DirectorySandbox
229            if not hasattr(DirectorySandbox, '_old'):
230                def violation(*args):
231                    pass
232                DirectorySandbox._old = DirectorySandbox._violation
233                DirectorySandbox._violation = violation
234                patched = True
235            else:
236                patched = False
237        except ImportError:
238            patched = False
239
240        try:
241            return function(*args, **kw)
242        finally:
243            if patched:
244                DirectorySandbox._violation = DirectorySandbox._old
245                del DirectorySandbox._old
246
247    return __no_sandbox
248
249
250def _patch_file(path, content):
251    """Will backup the file then patch it"""
252    f = open(path)
253    existing_content = f.read()
254    f.close()
255    if existing_content == content:
256        # already patched
257        log.warn('Already patched.')
258        return False
259    log.warn('Patching...')
260    _rename_path(path)
261    f = open(path, 'w')
262    try:
263        f.write(content)
264    finally:
265        f.close()
266    return True
267
268_patch_file = _no_sandbox(_patch_file)
269
270
271def _same_content(path, content):
272    f = open(path)
273    existing_content = f.read()
274    f.close()
275    return existing_content == content
276
277
278def _rename_path(path):
279    new_name = path + '.OLD.%s' % time.time()
280    log.warn('Renaming %s to %s', path, new_name)
281    os.rename(path, new_name)
282    return new_name
283
284
285def _remove_flat_installation(placeholder):
286    if not os.path.isdir(placeholder):
287        log.warn('Unkown installation at %s', placeholder)
288        return False
289    found = False
290    for file in os.listdir(placeholder):
291        if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
292            found = True
293            break
294    if not found:
295        log.warn('Could not locate setuptools*.egg-info')
296        return
297
298    log.warn('Moving elements out of the way...')
299    pkg_info = os.path.join(placeholder, file)
300    if os.path.isdir(pkg_info):
301        patched = _patch_egg_dir(pkg_info)
302    else:
303        patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
304
305    if not patched:
306        log.warn('%s already patched.', pkg_info)
307        return False
308    # now let's move the files out of the way
309    for element in ('setuptools', 'pkg_resources.py', 'site.py'):
310        element = os.path.join(placeholder, element)
311        if os.path.exists(element):
312            _rename_path(element)
313        else:
314            log.warn('Could not find the %s element of the '
315                     'Setuptools distribution', element)
316    return True
317
318_remove_flat_installation = _no_sandbox(_remove_flat_installation)
319
320
321def _after_install(dist):
322    log.warn('After install bootstrap.')
323    placeholder = dist.get_command_obj('install').install_purelib
324    _create_fake_setuptools_pkg_info(placeholder)
325
326
327def _create_fake_setuptools_pkg_info(placeholder):
328    if not placeholder or not os.path.exists(placeholder):
329        log.warn('Could not find the install location')
330        return
331    pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
332    setuptools_file = 'setuptools-%s-py%s.egg-info' % \
333            (SETUPTOOLS_FAKED_VERSION, pyver)
334    pkg_info = os.path.join(placeholder, setuptools_file)
335    if os.path.exists(pkg_info):
336        log.warn('%s already exists', pkg_info)
337        return
338
339    log.warn('Creating %s', pkg_info)
340    try:
341        f = open(pkg_info, 'w')
342    except EnvironmentError:
343        log.warn("Don't have permissions to write %s, skipping", pkg_info)
344        return
345    try:
346        f.write(SETUPTOOLS_PKG_INFO)
347    finally:
348        f.close()
349
350    pth_file = os.path.join(placeholder, 'setuptools.pth')
351    log.warn('Creating %s', pth_file)
352    f = open(pth_file, 'w')
353    try:
354        f.write(os.path.join(os.curdir, setuptools_file))
355    finally:
356        f.close()
357
358_create_fake_setuptools_pkg_info = _no_sandbox(
359    _create_fake_setuptools_pkg_info
360)
361
362
363def _patch_egg_dir(path):
364    # let's check if it's already patched
365    pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
366    if os.path.exists(pkg_info):
367        if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
368            log.warn('%s already patched.', pkg_info)
369            return False
370    _rename_path(path)
371    os.mkdir(path)
372    os.mkdir(os.path.join(path, 'EGG-INFO'))
373    pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
374    f = open(pkg_info, 'w')
375    try:
376        f.write(SETUPTOOLS_PKG_INFO)
377    finally:
378        f.close()
379    return True
380
381_patch_egg_dir = _no_sandbox(_patch_egg_dir)
382
383
384def _before_install():
385    log.warn('Before install bootstrap.')
386    _fake_setuptools()
387
388
389def _under_prefix(location):
390    if 'install' not in sys.argv:
391        return True
392    args = sys.argv[sys.argv.index('install') + 1:]
393    for index, arg in enumerate(args):
394        for option in ('--root', '--prefix'):
395            if arg.startswith('%s=' % option):
396                top_dir = arg.split('root=')[-1]
397                return location.startswith(top_dir)
398            elif arg == option:
399                if len(args) > index:
400                    top_dir = args[index + 1]
401                    return location.startswith(top_dir)
402        if arg == '--user' and USER_SITE is not None:
403            return location.startswith(USER_SITE)
404    return True
405
406
407def _fake_setuptools():
408    log.warn('Scanning installed packages')
409    try:
410        import pkg_resources
411    except ImportError:
412        # we're cool
413        log.warn('Setuptools or Distribute does not seem to be installed.')
414        return
415    ws = pkg_resources.working_set
416    try:
417        setuptools_dist = ws.find(
418            pkg_resources.Requirement.parse('setuptools', replacement=False)
419            )
420    except TypeError:
421        # old distribute API
422        setuptools_dist = ws.find(
423            pkg_resources.Requirement.parse('setuptools')
424        )
425
426    if setuptools_dist is None:
427        log.warn('No setuptools distribution found')
428        return
429    # detecting if it was already faked
430    setuptools_location = setuptools_dist.location
431    log.warn('Setuptools installation detected at %s', setuptools_location)
432
433    # if --root or --preix was provided, and if
434    # setuptools is not located in them, we don't patch it
435    if not _under_prefix(setuptools_location):
436        log.warn('Not patching, --root or --prefix is installing Distribute'
437                 ' in another location')
438        return
439
440    # let's see if its an egg
441    if not setuptools_location.endswith('.egg'):
442        log.warn('Non-egg installation')
443        res = _remove_flat_installation(setuptools_location)
444        if not res:
445            return
446    else:
447        log.warn('Egg installation')
448        pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
449        if (os.path.exists(pkg_info) and
450            _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
451            log.warn('Already patched.')
452            return
453        log.warn('Patching...')
454        # let's create a fake egg replacing setuptools one
455        res = _patch_egg_dir(setuptools_location)
456        if not res:
457            return
458    log.warn('Patching complete.')
459    _relaunch()
460
461
462def _relaunch():
463    log.warn('Relaunching...')
464    # we have to relaunch the process
465    # pip marker to avoid a relaunch bug
466    _cmd1 = ['-c', 'install', '--single-version-externally-managed']
467    _cmd2 = ['-c', 'install', '--record']
468    if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
469        sys.argv[0] = 'setup.py'
470    args = [sys.executable] + sys.argv
471    sys.exit(subprocess.call(args))
472
473
474def _extractall(self, path=".", members=None):
475    """Extract all members from the archive to the current working
476       directory and set owner, modification time and permissions on
477       directories afterwards. `path' specifies a different directory
478       to extract to. `members' is optional and must be a subset of the
479       list returned by getmembers().
480    """
481    import copy
482    import operator
483    from tarfile import ExtractError
484    directories = []
485
486    if members is None:
487        members = self
488
489    for tarinfo in members:
490        if tarinfo.isdir():
491            # Extract directories with a safe mode.
492            directories.append(tarinfo)
493            tarinfo = copy.copy(tarinfo)
494            tarinfo.mode = 448  # decimal for oct 0700
495        self.extract(tarinfo, path)
496
497    # Reverse sort directories.
498    if sys.version_info < (2, 4):
499        def sorter(dir1, dir2):
500            return cmp(dir1.name, dir2.name)
501        directories.sort(sorter)
502        directories.reverse()
503    else:
504        directories.sort(key=operator.attrgetter('name'), reverse=True)
505
506    # Set correct owner, mtime and filemode on directories.
507    for tarinfo in directories:
508        dirpath = os.path.join(path, tarinfo.name)
509        try:
510            self.chown(tarinfo, dirpath)
511            self.utime(tarinfo, dirpath)
512            self.chmod(tarinfo, dirpath)
513        except ExtractError:
514            e = sys.exc_info()[1]
515            if self.errorlevel > 1:
516                raise
517            else:
518                self._dbg(1, "tarfile: %s" % e)
519
520
521def _build_install_args(options):
522    """
523    Build the arguments to 'python setup.py install' on the distribute package
524    """
525    install_args = []
526    if options.user_install:
527        if sys.version_info < (2, 6):
528            log.warn("--user requires Python 2.6 or later")
529            raise SystemExit(1)
530        install_args.append('--user')
531    return install_args
532
533def _parse_args():
534    """
535    Parse the command line for options
536    """
537    parser = optparse.OptionParser()
538    parser.add_option(
539        '--user', dest='user_install', action='store_true', default=False,
540        help='install in user site package (requires Python 2.6 or later)')
541    parser.add_option(
542        '--download-base', dest='download_base', metavar="URL",
543        default=DEFAULT_URL,
544        help='alternative URL from where to download the distribute package')
545    options, args = parser.parse_args()
546    # positional arguments are ignored
547    return options
548
549def main(version=DEFAULT_VERSION):
550    """Install or upgrade setuptools and EasyInstall"""
551    options = _parse_args()
552    tarball = download_setuptools(download_base=options.download_base)
553    return _install(tarball, _build_install_args(options))
554
555if __name__ == '__main__':
556    sys.exit(main())
Note: See TracBrowser for help on using the repository browser.