~nkarageuzian/mailman/usermanagement

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