~sambuddhabasu1/mailman/fix_mailman_run_error

« back to all changes in this revision

Viewing changes to distribute_setup.py

  • Committer: Barry Warsaw
  • Date: 2009-11-24 03:20:48 UTC
  • Revision ID: barry@python.org-20091124032048-wi6uoch11nlyksb7
* Switch to distribute from setuptools.
* Remove the dependency on setuptools_bzr and use a MANIFEST.in instead

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
17
import sys
 
18
import time
 
19
import fnmatch
 
20
import tempfile
 
21
import tarfile
 
22
from distutils import log
 
23
 
 
24
try:
 
25
    from site import USER_SITE
 
26
except ImportError:
 
27
    USER_SITE = None
 
28
 
 
29
try:
 
30
    import subprocess
 
31
 
 
32
    def _python_cmd(*args):
 
33
        args = (sys.executable,) + args
 
34
        return subprocess.call(args) == 0
 
35
 
 
36
except ImportError:
 
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':
 
42
            def quote(arg):
 
43
                if ' ' in arg:
 
44
                    return '"%s"' % arg
 
45
                return arg
 
46
            args = [quote(arg) for arg in args]
 
47
        return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
 
48
 
 
49
DEFAULT_VERSION = "0.6.8"
 
50
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
 
51
SETUPTOOLS_PKG_INFO = """\
 
52
Metadata-Version: 1.0
 
53
Name: setuptools
 
54
Version: 0.6c9
 
55
Summary: xxxx
 
56
Home-page: xxx
 
57
Author: xxx
 
58
Author-email: xxx
 
59
License: xxx
 
60
Description: xxx
 
61
"""
 
62
 
 
63
 
 
64
def _install(tarball):
 
65
    # extracting the tarball
 
66
    tmpdir = tempfile.mkdtemp()
 
67
    log.warn('Extracting in %s', tmpdir)
 
68
    old_wd = os.getcwd()
 
69
    try:
 
70
        os.chdir(tmpdir)
 
71
        tar = tarfile.open(tarball)
 
72
        _extractall(tar)
 
73
        tar.close()
 
74
 
 
75
        # going in the directory
 
76
        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
 
77
        os.chdir(subdir)
 
78
        log.warn('Now working in %s', subdir)
 
79
 
 
80
        # installing
 
81
        log.warn('Installing Distribute')
 
82
        assert _python_cmd('setup.py', 'install')
 
83
    finally:
 
84
        os.chdir(old_wd)
 
85
 
 
86
 
 
87
def _build_egg(egg, tarball, to_dir):
 
88
    # extracting the tarball
 
89
    tmpdir = tempfile.mkdtemp()
 
90
    log.warn('Extracting in %s', tmpdir)
 
91
    old_wd = os.getcwd()
 
92
    try:
 
93
        os.chdir(tmpdir)
 
94
        tar = tarfile.open(tarball)
 
95
        _extractall(tar)
 
96
        tar.close()
 
97
 
 
98
        # going in the directory
 
99
        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
 
100
        os.chdir(subdir)
 
101
        log.warn('Now working in %s', subdir)
 
102
 
 
103
        # building an egg
 
104
        log.warn('Building a Distribute egg in %s', to_dir)
 
105
        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
 
106
 
 
107
    finally:
 
108
        os.chdir(old_wd)
 
109
    # returning the result
 
110
    log.warn(egg)
 
111
    if not os.path.exists(egg):
 
112
        raise IOError('Could not build the egg.')
 
113
 
 
114
 
 
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)
 
123
    import setuptools
 
124
    setuptools.bootstrap_install_from = egg
 
125
 
 
126
 
 
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
 
133
    try:
 
134
        try:
 
135
            import pkg_resources
 
136
            if not hasattr(pkg_resources, '_distribute'):
 
137
                if not no_fake:
 
138
                    _fake_setuptools()
 
139
                raise ImportError
 
140
        except ImportError:
 
141
            return _do_download(version, download_base, to_dir, download_delay)
 
142
        try:
 
143
            pkg_resources.require("distribute>="+version)
 
144
            return
 
145
        except pkg_resources.VersionConflict:
 
146
            e = sys.exc_info()[1]
 
147
            if was_imported:
 
148
                sys.stderr.write(
 
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]))
 
154
                sys.exit(2)
 
155
            else:
 
156
                del pkg_resources, sys.modules['pkg_resources']    # reload ok
 
157
                return _do_download(version, download_base, to_dir,
 
158
                                    download_delay)
 
159
        except pkg_resources.DistributionNotFound:
 
160
            return _do_download(version, download_base, to_dir,
 
161
                                download_delay)
 
162
    finally:
 
163
        if not no_fake:
 
164
            _create_fake_setuptools_pkg_info(to_dir)
 
165
 
 
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
 
169
 
 
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
 
174
    attempt.
 
175
    """
 
176
    # making sure we use the absolute path
 
177
    to_dir = os.path.abspath(to_dir)
 
178
    try:
 
179
        from urllib.request import urlopen
 
180
    except ImportError:
 
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)
 
185
    src = dst = None
 
186
    if not os.path.exists(saveto):  # Avoid repeated downloads
 
187
        try:
 
188
            log.warn("Downloading %s", url)
 
189
            src = urlopen(url)
 
190
            # Read/write all in one block, so we don't create a corrupt file
 
191
            # if the download is interrupted.
 
192
            data = src.read()
 
193
            dst = open(saveto, "wb")
 
194
            dst.write(data)
 
195
        finally:
 
196
            if src:
 
197
                src.close()
 
198
            if dst:
 
199
                dst.close()
 
200
    return os.path.realpath(saveto)
 
201
 
 
202
 
 
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:
 
207
        # already patched
 
208
        log.warn('Already patched.')
 
209
        return False
 
210
    log.warn('Patching...')
 
211
    _rename_path(path)
 
212
    f = open(path, 'w')
 
213
    try:
 
214
        f.write(content)
 
215
    finally:
 
216
        f.close()
 
217
    return True
 
218
 
 
219
 
 
220
def _same_content(path, content):
 
221
    return open(path).read() == content
 
222
 
 
223
 
 
224
def _rename_path(path):
 
225
    new_name = path + '.OLD.%s' % time.time()
 
226
    log.warn('Renaming %s into %s', path, new_name)
 
227
    try:
 
228
        from setuptools.sandbox import DirectorySandbox
 
229
        def _violation(*args):
 
230
            pass
 
231
        DirectorySandbox._violation = _violation
 
232
    except ImportError:
 
233
        pass
 
234
 
 
235
    os.rename(path, new_name)
 
236
    return new_name
 
237
 
 
238
 
 
239
def _remove_flat_installation(placeholder):
 
240
    if not os.path.isdir(placeholder):
 
241
        log.warn('Unkown installation at %s', placeholder)
 
242
        return False
 
243
    found = False
 
244
    for file in os.listdir(placeholder):
 
245
        if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
 
246
            found = True
 
247
            break
 
248
    if not found:
 
249
        log.warn('Could not locate setuptools*.egg-info')
 
250
        return
 
251
 
 
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)
 
256
    else:
 
257
        patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
 
258
 
 
259
    if not patched:
 
260
        log.warn('%s already patched.', pkg_info)
 
261
        return False
 
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)
 
267
        else:
 
268
            log.warn('Could not find the %s element of the '
 
269
                     'Setuptools distribution', element)
 
270
    return True
 
271
 
 
272
 
 
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)
 
277
 
 
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')
 
281
        return
 
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)
 
287
        return
 
288
    log.warn('Creating %s', pkg_info)
 
289
    f = open(pkg_info, 'w')
 
290
    try:
 
291
        f.write(SETUPTOOLS_PKG_INFO)
 
292
    finally:
 
293
        f.close()
 
294
    pth_file = os.path.join(placeholder, 'setuptools.pth')
 
295
    log.warn('Creating %s', pth_file)
 
296
    f = open(pth_file, 'w')
 
297
    try:
 
298
        f.write(os.path.join(os.curdir, setuptools_file))
 
299
    finally:
 
300
        f.close()
 
301
 
 
302
 
 
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)
 
309
            return False
 
310
    _rename_path(path)
 
311
    os.mkdir(path)
 
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')
 
315
    try:
 
316
        f.write(SETUPTOOLS_PKG_INFO)
 
317
    finally:
 
318
        f.close()
 
319
    return True
 
320
 
 
321
 
 
322
def _before_install():
 
323
    log.warn('Before install bootstrap.')
 
324
    _fake_setuptools()
 
325
 
 
326
 
 
327
def _under_prefix(location):
 
328
    if 'install' not in sys.argv:
 
329
        return True
 
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)
 
336
            elif arg == option:
 
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)
 
342
    return True
 
343
 
 
344
 
 
345
def _fake_setuptools():
 
346
    log.warn('Scanning installed packages')
 
347
    try:
 
348
        import pkg_resources
 
349
    except ImportError:
 
350
        # we're cool
 
351
        log.warn('Setuptools or Distribute does not seem to be installed.')
 
352
        return
 
353
    ws = pkg_resources.working_set
 
354
    try:
 
355
        setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
 
356
                                  replacement=False))
 
357
    except TypeError:
 
358
        # old distribute API
 
359
        setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
 
360
 
 
361
    if setuptools_dist is None:
 
362
        log.warn('No setuptools distribution found')
 
363
        return
 
364
    # detecting if it was already faked
 
365
    setuptools_location = setuptools_dist.location
 
366
    log.warn('Setuptools installation detected at %s', setuptools_location)
 
367
 
 
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')
 
373
        return
 
374
 
 
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)
 
379
        if not res:
 
380
            return
 
381
    else:
 
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.')
 
387
            return
 
388
        log.warn('Patching...')
 
389
        # let's create a fake egg replacing setuptools one
 
390
        res = _patch_egg_dir(setuptools_location)
 
391
        if not res:
 
392
            return
 
393
    log.warn('Patched done.')
 
394
    _relaunch()
 
395
 
 
396
 
 
397
def _relaunch():
 
398
    log.warn('Relaunching...')
 
399
    # we have to relaunch the process
 
400
    args = [sys.executable] + sys.argv
 
401
    sys.exit(subprocess.call(args))
 
402
 
 
403
 
 
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().
 
410
    """
 
411
    import copy
 
412
    import operator
 
413
    from tarfile import ExtractError
 
414
    directories = []
 
415
 
 
416
    if members is None:
 
417
        members = self
 
418
 
 
419
    for tarinfo in members:
 
420
        if tarinfo.isdir():
 
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)
 
426
 
 
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()
 
433
    else:
 
434
        directories.sort(key=operator.attrgetter('name'), reverse=True)
 
435
 
 
436
    # Set correct owner, mtime and filemode on directories.
 
437
    for tarinfo in directories:
 
438
        dirpath = os.path.join(path, tarinfo.name)
 
439
        try:
 
440
            self.chown(tarinfo, dirpath)
 
441
            self.utime(tarinfo, dirpath)
 
442
            self.chmod(tarinfo, dirpath)
 
443
        except ExtractError:
 
444
            e = sys.exc_info()[1]
 
445
            if self.errorlevel > 1:
 
446
                raise
 
447
            else:
 
448
                self._dbg(1, "tarfile: %s" % e)
 
449
 
 
450
 
 
451
def main(argv, version=DEFAULT_VERSION):
 
452
    """Install or upgrade setuptools and EasyInstall"""
 
453
    tarball = download_setuptools()
 
454
    _install(tarball)
 
455
 
 
456
 
 
457
if __name__ == '__main__':
 
458
    main(sys.argv[1:])