source: TI12-security/trunk/NDGSecurity/python/buildout/ndgsecurity/eggs/zc.buildout-1.2.1-py2.5.egg/zc/buildout/buildout.py @ 7081

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/NDGSecurity/python/buildout/ndgsecurity/eggs/zc.buildout-1.2.1-py2.5.egg/zc/buildout/buildout.py@7081
Revision 7081, 53.7 KB checked in by pjkersha, 10 years ago (diff)
  • Property svn:keywords set to Id
Line 
1##############################################################################
2#
3# Copyright (c) 2005 Zope Corporation and Contributors.
4# All Rights Reserved.
5#
6# This software is subject to the provisions of the Zope Public License,
7# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11# FOR A PARTICULAR PURPOSE.
12#
13##############################################################################
14"""Buildout main script
15
16$Id$
17"""
18
19import distutils.errors
20import logging
21import os
22import pprint
23import re
24import shutil
25import cStringIO
26import sys
27import tempfile
28import urllib2
29import ConfigParser
30import UserDict
31import glob
32
33import pkg_resources
34import zc.buildout
35import zc.buildout.easy_install
36
37from rmtree import rmtree
38
39try:
40    from hashlib import md5
41except ImportError:
42    # Python 2.4 and older
43    from md5 import md5
44
45realpath = zc.buildout.easy_install.realpath
46
47pkg_resources_loc = pkg_resources.working_set.find(
48    pkg_resources.Requirement.parse('setuptools')).location
49
50_isurl = re.compile('([a-zA-Z0-9+.-]+)://').match
51
52is_jython = sys.platform.startswith('java')
53
54if is_jython:
55    import subprocess
56
57class MissingOption(zc.buildout.UserError, KeyError):
58    """A required option was missing
59    """
60
61class MissingSection(zc.buildout.UserError, KeyError):
62    """A required section is missinh
63    """
64
65    def __str__(self):
66        return "The referenced section, %r, was not defined." % self[0]
67
68_buildout_default_options = {
69    'eggs-directory': 'eggs',
70    'develop-eggs-directory': 'develop-eggs',
71    'bin-directory': 'bin',
72    'parts-directory': 'parts',
73    'installed': '.installed.cfg',
74    'python': 'buildout',
75    'executable': sys.executable,
76    'log-level': 'INFO',
77    'log-format': '',
78    }
79
80class Buildout(UserDict.DictMixin):
81
82    def __init__(self, config_file, cloptions,
83                 user_defaults=True, windows_restart=False, command=None):
84
85        __doing__ = 'Initializing.'
86       
87        self.__windows_restart = windows_restart
88
89        # default options
90        data = dict(buildout=_buildout_default_options.copy())
91        self._buildout_dir = os.getcwd()
92
93        if not _isurl(config_file):
94            config_file = os.path.abspath(config_file)
95            base = os.path.dirname(config_file)
96            if not os.path.exists(config_file):
97                if command == 'init':
98                    print 'Creating %r.' % config_file
99                    open(config_file, 'w').write('[buildout]\nparts = \n')
100                elif command == 'setup':
101                    # Sigh. this model of a buildout nstance
102                    # with methods is breaking down :(
103                    config_file = None
104                    data['buildout']['directory'] = '.'
105                else:
106                    raise zc.buildout.UserError(
107                        "Couldn't open %s" % config_file)
108
109            if config_file:
110                data['buildout']['directory'] = os.path.dirname(config_file)
111        else:
112            base = None
113
114        # load user defaults, which override defaults
115        if user_defaults:
116            user_config = os.path.join(os.path.expanduser('~'),
117                                       '.buildout', 'default.cfg')
118            if os.path.exists(user_config):
119                _update(data, _open(os.path.dirname(user_config), user_config,
120                                    []))
121
122        # load configuration files
123        if config_file:
124            _update(data, _open(os.path.dirname(config_file), config_file, []))
125
126        # apply command-line options
127        for (section, option, value) in cloptions:
128            options = data.get(section)
129            if options is None:
130                options = data[section] = {}
131            options[option] = value
132                # The egg dire
133
134        self._raw = data
135        self._data = {}
136        self._parts = []
137        # provide some defaults before options are parsed
138        # because while parsing options those attributes might be
139        # used already (Gottfried Ganssauge)
140        buildout_section = data.get('buildout')
141           
142        # Try to make sure we have absolute paths for standard
143        # directories. We do this before doing substitutions, in case
144        # a one of these gets read by another section.  If any
145        # variable references are used though, we leave it as is in
146        # _buildout_path.
147        if 'directory' in buildout_section:
148            self._buildout_dir = buildout_section['directory']
149            for name in ('bin', 'parts', 'eggs', 'develop-eggs'):
150                d = self._buildout_path(buildout_section[name+'-directory'])
151                buildout_section[name+'-directory'] = d
152
153        links = buildout_section and buildout_section.get('find-links', '')
154        self._links = links and links.split() or ()
155
156        allow_hosts = buildout_section and buildout_section.get(
157             'allow-hosts', '*').split('\n')
158        self._allow_hosts = tuple([host.strip() for host in allow_hosts
159                                   if host.strip() != ''])
160
161        self._logger = logging.getLogger('zc.buildout')
162        self.offline = False
163        self.newest = True
164       
165        ##################################################################
166        ## WARNING!!!
167        ## ALL ATTRIBUTES MUST HAVE REASONABLE DEFAULTS AT THIS POINT
168        ## OTHERWISE ATTRIBUTEERRORS MIGHT HAPPEN ANY TIME
169        ##################################################################
170        # initialize some attrs and buildout directories.
171        options = self['buildout']
172
173        # now reinitialize
174        links = options.get('find-links', '')
175        self._links = links and links.split() or ()
176       
177        allow_hosts = options.get('allow-hosts', '*').split('\n')
178        self._allow_hosts = tuple([host.strip() for host in allow_hosts
179                                   if host.strip() != ''])
180
181        self._buildout_dir = options['directory']
182
183        # Make sure we have absolute paths for standard directories.  We do this
184        # a second time here in case someone overrode these in their configs.
185        for name in ('bin', 'parts', 'eggs', 'develop-eggs'):
186            d = self._buildout_path(options[name+'-directory'])
187            options[name+'-directory'] = d
188
189        if options['installed']:
190            options['installed'] = os.path.join(options['directory'],
191                                                options['installed'])
192
193        self._setup_logging()
194
195        offline = options.get('offline', 'false')
196        if offline not in ('true', 'false'):
197            self._error('Invalid value for offline option: %s', offline)
198        options['offline'] = offline
199        self.offline = offline == 'true'
200
201        if self.offline:
202            newest = options['newest'] = 'false'
203        else:
204            newest = options.get('newest', 'true')
205            if newest not in ('true', 'false'):
206                self._error('Invalid value for newest option: %s', newest)
207            options['newest'] = newest
208        self.newest = newest == 'true'
209
210        versions = options.get('versions')
211        if versions:
212            zc.buildout.easy_install.default_versions(dict(self[versions]))
213
214        prefer_final = options.get('prefer-final', 'false')
215        if prefer_final not in ('true', 'false'):
216            self._error('Invalid value for prefer-final option: %s',
217                        prefer_final)
218        zc.buildout.easy_install.prefer_final(prefer_final=='true')
219       
220        use_dependency_links = options.get('use-dependency-links', 'true')
221        if use_dependency_links not in ('true', 'false'):
222            self._error('Invalid value for use-dependency-links option: %s',
223                        use_dependency_links)
224        zc.buildout.easy_install.use_dependency_links(
225            use_dependency_links == 'true')
226
227        allow_picked_versions = options.get('allow-picked-versions', 'true')
228        if allow_picked_versions not in ('true', 'false'):
229            self._error('Invalid value for allow-picked-versions option: %s',
230                        allow_picked_versions)
231        zc.buildout.easy_install.allow_picked_versions(
232            allow_picked_versions=='true')
233
234        download_cache = options.get('download-cache')
235        if download_cache:
236            download_cache = os.path.join(options['directory'], download_cache)
237            if not os.path.isdir(download_cache):
238                raise zc.buildout.UserError(
239                    'The specified download cache:\n'
240                    '%r\n'
241                    "Doesn't exist.\n"
242                    % download_cache)
243            download_cache = os.path.join(download_cache, 'dist')
244            if not os.path.isdir(download_cache):
245                os.mkdir(download_cache)
246               
247            zc.buildout.easy_install.download_cache(download_cache)
248
249        install_from_cache = options.get('install-from-cache')
250        if install_from_cache:
251            if install_from_cache not in ('true', 'false'):
252                self._error('Invalid value for install-from-cache option: %s',
253                            install_from_cache)
254            if install_from_cache == 'true':
255                zc.buildout.easy_install.install_from_cache(True)
256
257
258        always_unzip = options.get('unzip')
259        if always_unzip:
260            if always_unzip not in ('true', 'false'):
261                self._error('Invalid value for unzip option: %s',
262                            always_unzip)
263            if always_unzip == 'true':
264                zc.buildout.easy_install.always_unzip(True)
265
266        # "Use" each of the defaults so they aren't reported as unused options.
267        for name in _buildout_default_options:
268            options[name]
269
270        os.chdir(options['directory'])
271
272    def _buildout_path(self, name):
273        if '${' in name:
274            return name
275        return os.path.join(self._buildout_dir, name)
276
277    def bootstrap(self, args):
278        __doing__ = 'Bootstraping.'
279
280        self._setup_directories()
281
282        # Now copy buildout and setuptools eggs, amd record destination eggs:
283        entries = []
284        for name in 'setuptools', 'zc.buildout':
285            r = pkg_resources.Requirement.parse(name)
286            dist = pkg_resources.working_set.find(r)
287            if dist.precedence == pkg_resources.DEVELOP_DIST:
288                dest = os.path.join(self['buildout']['develop-eggs-directory'],
289                                    name+'.egg-link')
290                open(dest, 'w').write(dist.location)
291                entries.append(dist.location)
292            else:
293                dest = os.path.join(self['buildout']['eggs-directory'],
294                                    os.path.basename(dist.location))
295                entries.append(dest)
296                if not os.path.exists(dest):
297                    if os.path.isdir(dist.location):
298                        shutil.copytree(dist.location, dest)
299                    else:
300                        shutil.copy2(dist.location, dest)
301
302        # Create buildout script
303        ws = pkg_resources.WorkingSet(entries)
304        ws.require('zc.buildout')
305        zc.buildout.easy_install.scripts(
306            ['zc.buildout'], ws, sys.executable,
307            self['buildout']['bin-directory'])
308
309    init = bootstrap
310
311    def install(self, install_args):
312        __doing__ = 'Installing.'
313
314        self._load_extensions()
315        self._setup_directories()
316
317        # Add develop-eggs directory to path so that it gets searched
318        # for eggs:
319        sys.path.insert(0, self['buildout']['develop-eggs-directory'])
320
321        # Check for updates. This could cause the process to be rstarted
322        self._maybe_upgrade()
323
324        # load installed data
325        (installed_part_options, installed_exists
326         )= self._read_installed_part_options()
327
328        # Remove old develop eggs
329        self._uninstall(
330            installed_part_options['buildout'].get(
331                'installed_develop_eggs', '')
332            )
333
334        # Build develop eggs
335        installed_develop_eggs = self._develop()
336        installed_part_options['buildout']['installed_develop_eggs'
337                                           ] = installed_develop_eggs
338       
339        if installed_exists:
340            self._update_installed(
341                installed_develop_eggs=installed_develop_eggs)
342
343        # get configured and installed part lists
344        conf_parts = self['buildout']['parts']
345        conf_parts = conf_parts and conf_parts.split() or []
346        installed_parts = installed_part_options['buildout']['parts']
347        installed_parts = installed_parts and installed_parts.split() or []
348       
349        if install_args:
350            install_parts = install_args
351            uninstall_missing = False
352        else:
353            install_parts = conf_parts
354            uninstall_missing = True
355
356        # load and initialize recipes
357        [self[part]['recipe'] for part in install_parts]
358        if not install_args:
359            install_parts = self._parts
360
361        if self._log_level < logging.DEBUG:
362            sections = list(self)
363            sections.sort()
364            print   
365            print 'Configuration data:'
366            for section in self._data:
367                _save_options(section, self[section], sys.stdout)
368            print   
369
370
371        # compute new part recipe signatures
372        self._compute_part_signatures(install_parts)
373
374        # uninstall parts that are no-longer used or who's configs
375        # have changed
376        for part in reversed(installed_parts):
377            if part in install_parts:
378                old_options = installed_part_options[part].copy()
379                installed_files = old_options.pop('__buildout_installed__')
380                new_options = self.get(part)
381                if old_options == new_options:
382                    # The options are the same, but are all of the
383                    # installed files still there?  If not, we should
384                    # reinstall.
385                    if not installed_files:
386                        continue
387                    for f in installed_files.split('\n'):
388                        if not os.path.exists(self._buildout_path(f)):
389                            break
390                    else:
391                        continue
392
393                # output debugging info
394                if self._logger.getEffectiveLevel() < logging.DEBUG:
395                    for k in old_options:
396                        if k not in new_options:
397                            self._logger.debug("Part %s, dropped option %s.",
398                                               part, k)
399                        elif old_options[k] != new_options[k]:
400                            self._logger.debug(
401                                "Part %s, option %s changed:\n%r != %r",
402                                part, k, new_options[k], old_options[k],
403                                )
404                    for k in new_options:
405                        if k not in old_options:
406                            self._logger.debug("Part %s, new option %s.",
407                                               part, k)
408
409            elif not uninstall_missing:
410                continue
411
412            self._uninstall_part(part, installed_part_options)
413            installed_parts = [p for p in installed_parts if p != part]
414
415            if installed_exists:
416                self._update_installed(parts=' '.join(installed_parts))
417
418        # Check for unused buildout options:
419        _check_for_unused_options_in_section(self, 'buildout')
420
421        # install new parts
422        for part in install_parts:
423            signature = self[part].pop('__buildout_signature__')
424            saved_options = self[part].copy()
425            recipe = self[part].recipe
426            if part in installed_parts: # update
427                need_to_save_installed = False
428                __doing__ = 'Updating %s.', part
429                self._logger.info(*__doing__)
430                old_options = installed_part_options[part]
431                old_installed_files = old_options['__buildout_installed__']
432
433                try:
434                    update = recipe.update
435                except AttributeError:
436                    update = recipe.install
437                    self._logger.warning(
438                        "The recipe for %s doesn't define an update "
439                        "method. Using its install method.",
440                        part)
441
442                try:
443                    installed_files = self[part]._call(update)
444                except:
445                    installed_parts.remove(part)
446                    self._uninstall(old_installed_files)
447                    if installed_exists:
448                        self._update_installed(
449                            parts=' '.join(installed_parts))
450                    raise
451
452                old_installed_files = old_installed_files.split('\n')
453                if installed_files is None:
454                    installed_files = old_installed_files
455                else:
456                    if isinstance(installed_files, str):
457                        installed_files = [installed_files]
458                    else:
459                        installed_files = list(installed_files)
460
461                    need_to_save_installed = [
462                        p for p in installed_files
463                        if p not in old_installed_files]
464
465                    if need_to_save_installed:
466                        installed_files = (old_installed_files
467                                           + need_to_save_installed)
468
469            else: # install
470                need_to_save_installed = True
471                __doing__ = 'Installing %s.', part
472                self._logger.info(*__doing__)
473                installed_files = self[part]._call(recipe.install)
474                if installed_files is None:
475                    self._logger.warning(
476                        "The %s install returned None.  A path or "
477                        "iterable os paths should be returned.",
478                        part)
479                    installed_files = ()
480                elif isinstance(installed_files, str):
481                    installed_files = [installed_files]
482                else:
483                    installed_files = list(installed_files)
484
485            installed_part_options[part] = saved_options
486            saved_options['__buildout_installed__'
487                          ] = '\n'.join(installed_files)
488            saved_options['__buildout_signature__'] = signature
489
490            installed_parts = [p for p in installed_parts if p != part]
491            installed_parts.append(part)
492            _check_for_unused_options_in_section(self, part)
493
494            if need_to_save_installed:
495                installed_part_options['buildout']['parts'] = (
496                    ' '.join(installed_parts))           
497                self._save_installed_options(installed_part_options)
498                installed_exists = True
499            else:
500                assert installed_exists
501                self._update_installed(parts=' '.join(installed_parts))
502
503        if installed_develop_eggs:
504            if not installed_exists:
505                self._save_installed_options(installed_part_options)
506        elif (not installed_parts) and installed_exists:
507            os.remove(self['buildout']['installed'])
508
509    def _update_installed(self, **buildout_options):
510        installed = self['buildout']['installed']
511        f = open(installed, 'a')
512        f.write('\n[buildout]\n')
513        for option, value in buildout_options.items():
514            _save_option(option, value, f)
515        f.close()
516
517    def _uninstall_part(self, part, installed_part_options):
518        # ununstall part
519        __doing__ = 'Uninstalling %s.', part
520        self._logger.info(*__doing__)
521
522        # run uinstall recipe
523        recipe, entry = _recipe(installed_part_options[part])
524        try:
525            uninstaller = _install_and_load(
526                recipe, 'zc.buildout.uninstall', entry, self)
527            self._logger.info('Running uninstall recipe.')
528            uninstaller(part, installed_part_options[part])
529        except (ImportError, pkg_resources.DistributionNotFound), v:
530            pass
531
532        # remove created files and directories
533        self._uninstall(
534            installed_part_options[part]['__buildout_installed__'])
535
536
537    def _setup_directories(self):
538        __doing__ = 'Setting up buildout directories'
539
540        # Create buildout directories
541        for name in ('bin', 'parts', 'eggs', 'develop-eggs'):
542            d = self['buildout'][name+'-directory']
543            if not os.path.exists(d):
544                self._logger.info('Creating directory %r.', d)
545                os.mkdir(d)
546
547    def _develop(self):
548        """Install sources by running setup.py develop on them
549        """
550        __doing__ = 'Processing directories listed in the develop option'
551
552        develop = self['buildout'].get('develop')
553        if not develop:
554            return ''
555
556        dest = self['buildout']['develop-eggs-directory']
557        old_files = os.listdir(dest)
558
559        env = dict(os.environ, PYTHONPATH=pkg_resources_loc)
560        here = os.getcwd()
561        try:
562            try:
563                for setup in develop.split():
564                    setup = self._buildout_path(setup)
565                    files = glob.glob(setup)
566                    if not files:
567                        self._logger.warn("Couldn't develop %r (not found)", setup)
568                    else:
569                        files.sort()
570                    for setup in files:
571                        self._logger.info("Develop: %r", setup)
572                        __doing__ = 'Processing develop directory %r.', setup
573                        zc.buildout.easy_install.develop(setup, dest)
574            except:
575                # if we had an error, we need to roll back changes, by
576                # removing any files we created.
577                self._sanity_check_develop_eggs_files(dest, old_files)
578                self._uninstall('\n'.join(
579                    [os.path.join(dest, f)
580                     for f in os.listdir(dest)
581                     if f not in old_files
582                     ]))
583                raise
584                     
585            else:
586                self._sanity_check_develop_eggs_files(dest, old_files)
587                return '\n'.join([os.path.join(dest, f)
588                                  for f in os.listdir(dest)
589                                  if f not in old_files
590                                  ])
591
592        finally:
593            os.chdir(here)
594
595
596    def _sanity_check_develop_eggs_files(self, dest, old_files):
597        for f in os.listdir(dest):
598            if f in old_files:
599                continue
600            if not (os.path.isfile(os.path.join(dest, f))
601                    and f.endswith('.egg-link')):
602                self._logger.warning(
603                    "Unexpected entry, %r, in develop-eggs directory.", f)
604
605    def _compute_part_signatures(self, parts):
606        # Compute recipe signature and add to options
607        for part in parts:
608            options = self.get(part)
609            if options is None:
610                options = self[part] = {}
611            recipe, entry = _recipe(options)
612            req = pkg_resources.Requirement.parse(recipe)
613            sig = _dists_sig(pkg_resources.working_set.resolve([req]))
614            options['__buildout_signature__'] = ' '.join(sig)
615
616    def _read_installed_part_options(self):
617        old = self['buildout']['installed']
618        if old and os.path.isfile(old):
619            parser = ConfigParser.RawConfigParser()
620            parser.optionxform = lambda s: s
621            parser.read(old)
622            result = {}
623            for section in parser.sections():
624                options = {}
625                for option, value in parser.items(section):
626                    if '%(' in value:
627                        for k, v in _spacey_defaults:
628                            value = value.replace(k, v)
629                    options[option] = value
630                result[section] = Options(self, section, options)
631                       
632            return result, True
633        else:
634            return ({'buildout': Options(self, 'buildout', {'parts': ''})},
635                    False,
636                    )
637
638    def _uninstall(self, installed):
639        for f in installed.split('\n'):
640            if not f:
641                continue
642            f = self._buildout_path(f)
643            if os.path.isdir(f):
644                rmtree(f)
645            elif os.path.isfile(f):
646                try:
647                    os.remove(f)
648                except OSError:
649                    if not (
650                        sys.platform == 'win32' and
651                        (realpath(os.path.join(os.path.dirname(sys.argv[0]),
652                                               'buildout.exe'))
653                         ==
654                         realpath(f)
655                         )
656                        # Sigh. This is the exectable used to run the buildout
657                        # and, of course, it's in use. Leave it.
658                        ):
659                        raise                   
660               
661    def _install(self, part):
662        options = self[part]
663        recipe, entry = _recipe(options)
664        recipe_class = pkg_resources.load_entry_point(
665            recipe, 'zc.buildout', entry)
666        installed = recipe_class(self, part, options).install()
667        if installed is None:
668            installed = []
669        elif isinstance(installed, basestring):
670            installed = [installed]
671        base = self._buildout_path('')
672        installed = [d.startswith(base) and d[len(base):] or d
673                     for d in installed]
674        return ' '.join(installed)
675
676
677    def _save_installed_options(self, installed_options):
678        installed = self['buildout']['installed']
679        if not installed:
680            return
681        f = open(installed, 'w')
682        _save_options('buildout', installed_options['buildout'], f)
683        for part in installed_options['buildout']['parts'].split():
684            print >>f
685            _save_options(part, installed_options[part], f)
686        f.close()
687
688    def _error(self, message, *args):
689        raise zc.buildout.UserError(message % args)
690
691    def _setup_logging(self):
692        root_logger = logging.getLogger()
693        self._logger = logging.getLogger('zc.buildout')
694        handler = logging.StreamHandler(sys.stdout)
695        log_format = self['buildout']['log-format']
696        if not log_format:
697            # No format specified. Use different formatter for buildout
698            # and other modules, showing logger name except for buildout
699            log_format = '%(name)s: %(message)s'
700            buildout_handler = logging.StreamHandler(sys.stdout)
701            buildout_handler.setFormatter(logging.Formatter('%(message)s'))
702            self._logger.propagate = False
703            self._logger.addHandler(buildout_handler)
704           
705        handler.setFormatter(logging.Formatter(log_format))
706        root_logger.addHandler(handler)
707
708        level = self['buildout']['log-level']
709        if level in ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'):
710            level = getattr(logging, level)
711        else:
712            try:
713                level = int(level)
714            except ValueError:
715                self._error("Invalid logging level %s", level)
716        verbosity = self['buildout'].get('verbosity', 0)
717        try:
718            verbosity = int(verbosity)
719        except ValueError:
720            self._error("Invalid verbosity %s", verbosity)
721
722        level -= verbosity
723        root_logger.setLevel(level)
724        self._log_level = level
725
726    def _maybe_upgrade(self):
727        # See if buildout or setuptools need to be upgraded.
728        # If they do, do the upgrade and restart the buildout process.
729        __doing__ = 'Checking for upgrades.'
730
731        if not self.newest:
732            return
733       
734        ws = zc.buildout.easy_install.install(
735            [
736            (spec + ' ' + self['buildout'].get(spec+'-version', '')).strip()
737            for spec in ('zc.buildout', 'setuptools')
738            ],
739            self['buildout']['eggs-directory'],
740            links = self['buildout'].get('find-links', '').split(),
741            index = self['buildout'].get('index'),
742            path = [self['buildout']['develop-eggs-directory']],
743            allow_hosts = self._allow_hosts
744            )
745
746        upgraded = []
747        for project in 'zc.buildout', 'setuptools':
748            req = pkg_resources.Requirement.parse(project)
749            if ws.find(req) != pkg_resources.working_set.find(req):
750                upgraded.append(ws.find(req))
751
752        if not upgraded:
753            return
754
755        __doing__ = 'Upgrading.'
756
757        should_run = realpath(
758            os.path.join(os.path.abspath(self['buildout']['bin-directory']),
759                         'buildout')
760            )
761        if sys.platform == 'win32':
762            should_run += '-script.py'
763
764        if (realpath(os.path.abspath(sys.argv[0])) != should_run):
765            self._logger.debug("Running %r.", realpath(sys.argv[0]))
766            self._logger.debug("Local buildout is %r.", should_run)
767            self._logger.warn("Not upgrading because not running a local "
768                              "buildout command.")
769            return
770
771        if sys.platform == 'win32' and not self.__windows_restart:
772            args = map(zc.buildout.easy_install._safe_arg, sys.argv)
773            args.insert(1, '-W')
774            if not __debug__:
775                args.insert(0, '-O')
776            args.insert(0, zc.buildout.easy_install._safe_arg (sys.executable))
777            os.execv(sys.executable, args)           
778       
779        self._logger.info("Upgraded:\n  %s;\nrestarting.",
780                          ",\n  ".join([("%s version %s"
781                                       % (dist.project_name, dist.version)
782                                       )
783                                      for dist in upgraded
784                                      ]
785                                     ),
786                          )
787               
788        # the new dist is different, so we've upgraded.
789        # Update the scripts and return True
790        zc.buildout.easy_install.scripts(
791            ['zc.buildout'], ws, sys.executable,
792            self['buildout']['bin-directory'],
793            )
794
795        # Restart
796        args = map(zc.buildout.easy_install._safe_arg, sys.argv)
797        if not __debug__:
798            args.insert(0, '-O')
799        args.insert(0, zc.buildout.easy_install._safe_arg (sys.executable))
800
801        if is_jython:
802            sys.exit(subprocess.Popen([sys.executable] + list(args)).wait())
803        else:
804            sys.exit(os.spawnv(os.P_WAIT, sys.executable, args))
805
806    def _load_extensions(self):
807        __doing__ = 'Loading extensions.'
808        specs = self['buildout'].get('extensions', '').split()
809        if specs:
810            path = [self['buildout']['develop-eggs-directory']]
811            if self.offline:
812                dest = None
813                path.append(self['buildout']['eggs-directory'])
814            else:
815                dest = self['buildout']['eggs-directory']
816                if not os.path.exists(dest):
817                    self._logger.info('Creating directory %r.', dest)
818                    os.mkdir(dest)
819
820            zc.buildout.easy_install.install(
821                specs, dest, path=path,
822                working_set=pkg_resources.working_set,
823                links = self['buildout'].get('find-links', '').split(),
824                index = self['buildout'].get('index'),
825                newest=self.newest, allow_hosts=self._allow_hosts)
826
827            # Clear cache because extensions might now let us read pages we
828            # couldn't read before.
829            zc.buildout.easy_install.clear_index_cache()
830
831            for ep in pkg_resources.iter_entry_points('zc.buildout.extension'):
832                ep.load()(self)
833
834    def setup(self, args):
835        if not args:
836            raise zc.buildout.UserError(
837                "The setup command requires the path to a setup script or \n"
838                "directory containing a setup script, and it's arguments."
839                )
840        setup = args.pop(0)
841        if os.path.isdir(setup):
842            setup = os.path.join(setup, 'setup.py')
843
844        self._logger.info("Running setup script %r.", setup)
845        setup = os.path.abspath(setup)
846
847        fd, tsetup = tempfile.mkstemp()
848        try:
849            os.write(fd, zc.buildout.easy_install.runsetup_template % dict(
850                setuptools=pkg_resources_loc,
851                setupdir=os.path.dirname(setup),
852                setup=setup,
853                __file__ = setup,
854                ))
855            if is_jython:
856                arg_list = list()
857               
858                for a in args:
859                    add_args.append(zc.buildout.easy_install._safe_arg(a))
860               
861                subprocess.Popen([zc.buildout.easy_install._safe_arg(sys.executable)] + list(tsetup) +
862                                arg_list).wait()
863           
864            else:
865                os.spawnl(os.P_WAIT, sys.executable, zc.buildout.easy_install._safe_arg (sys.executable), tsetup,
866                        *[zc.buildout.easy_install._safe_arg(a)
867                            for a in args])
868        finally:
869            os.close(fd)
870            os.remove(tsetup)
871
872    runsetup = setup # backward compat.
873
874    def __getitem__(self, section):
875        __doing__ = 'Getting section %s.', section
876        try:
877            return self._data[section]
878        except KeyError:
879            pass
880
881        try:
882            data = self._raw[section]
883        except KeyError:
884            raise MissingSection(section)
885
886        options = Options(self, section, data)
887        self._data[section] = options
888        options._initialize()
889        return options         
890
891    def __setitem__(self, key, value):
892        raise NotImplementedError('__setitem__')
893
894    def __delitem__(self, key):
895        raise NotImplementedError('__delitem__')
896
897    def keys(self):
898        return self._raw.keys()
899
900    def __iter__(self):
901        return iter(self._raw)
902
903
904def _install_and_load(spec, group, entry, buildout):
905    __doing__ = 'Loading recipe %r.', spec
906    try:
907        req = pkg_resources.Requirement.parse(spec)
908
909        buildout_options = buildout['buildout']
910        if pkg_resources.working_set.find(req) is None:
911            __doing__ = 'Installing recipe %s.', spec
912            if buildout.offline:
913                dest = None
914                path = [buildout_options['develop-eggs-directory'],
915                        buildout_options['eggs-directory'],
916                        ]
917            else:
918                dest = buildout_options['eggs-directory']
919                path = [buildout_options['develop-eggs-directory']]
920
921            zc.buildout.easy_install.install(
922                [spec], dest,
923                links=buildout._links,
924                index=buildout_options.get('index'),
925                path=path,
926                working_set=pkg_resources.working_set,
927                newest=buildout.newest,
928                allow_hosts=buildout._allow_hosts
929                )
930
931        __doing__ = 'Loading %s recipe entry %s:%s.', group, spec, entry
932        return pkg_resources.load_entry_point(
933            req.project_name, group, entry)
934
935    except Exception, v:
936        buildout._logger.log(
937            1,
938            "Could't load %s entry point %s\nfrom %s:\n%s.",
939            group, entry, spec, v)
940        raise
941
942class Options(UserDict.DictMixin):
943
944    def __init__(self, buildout, section, data):
945        self.buildout = buildout
946        self.name = section
947        self._raw = data
948        self._cooked = {}
949        self._data = {}
950
951    def _initialize(self):
952        name = self.name
953        __doing__ = 'Initializing section %s.', name
954       
955        # force substitutions
956        for k, v in self._raw.items():
957            if '${' in v:
958                self._dosub(k, v)
959
960        if self.name == 'buildout':
961            return # buildout section can never be a part
962       
963        recipe = self.get('recipe')
964        if not recipe:
965            return
966       
967        reqs, entry = _recipe(self._data)
968        buildout = self.buildout
969        recipe_class = _install_and_load(reqs, 'zc.buildout', entry, buildout)
970
971        __doing__ = 'Initializing part %s.', name
972        self.recipe = recipe_class(buildout, name, self)
973        buildout._parts.append(name)
974
975    def _dosub(self, option, v):
976        __doing__ = 'Getting option %s:%s.', self.name, option
977        seen = [(self.name, option)]
978        v = '$$'.join([self._sub(s, seen) for s in v.split('$$')])
979        self._cooked[option] = v
980
981    def get(self, option, default=None, seen=None):
982        try:
983            return self._data[option]
984        except KeyError:
985            pass
986
987        v = self._cooked.get(option)
988        if v is None:
989            v = self._raw.get(option)
990            if v is None:
991                return default
992
993        __doing__ = 'Getting option %s:%s.', self.name, option
994
995        if '${' in v:
996            key = self.name, option
997            if seen is None:
998                seen = [key]
999            elif key in seen:
1000                raise zc.buildout.UserError(
1001                    "Circular reference in substitutions.\n"
1002                    )
1003            else:
1004                seen.append(key)
1005            v = '$$'.join([self._sub(s, seen) for s in v.split('$$')])
1006            seen.pop()
1007
1008        self._data[option] = v
1009        return v
1010
1011    _template_split = re.compile('([$]{[^}]*})').split
1012    _simple = re.compile('[-a-zA-Z0-9 ._]+$').match
1013    _valid = re.compile('\${[-a-zA-Z0-9 ._]+:[-a-zA-Z0-9 ._]+}$').match
1014    def _sub(self, template, seen):
1015        value = self._template_split(template)
1016        subs = []
1017        for ref in value[1::2]:
1018            s = tuple(ref[2:-1].split(':'))
1019            if not self._valid(ref):
1020                if len(s) < 2:
1021                    raise zc.buildout.UserError("The substitution, %s,\n"
1022                                                "doesn't contain a colon."
1023                                                % ref)
1024                if len(s) > 2:
1025                    raise zc.buildout.UserError("The substitution, %s,\n"
1026                                                "has too many colons."
1027                                                % ref)
1028                if not self._simple(s[0]):
1029                    raise zc.buildout.UserError(
1030                        "The section name in substitution, %s,\n"
1031                        "has invalid characters."
1032                        % ref)
1033                if not self._simple(s[1]):
1034                    raise zc.buildout.UserError(
1035                        "The option name in substitution, %s,\n"
1036                        "has invalid characters."
1037                        % ref)
1038               
1039            v = self.buildout[s[0]].get(s[1], None, seen)
1040            if v is None:
1041                raise MissingOption("Referenced option does not exist:", *s)
1042            subs.append(v)
1043        subs.append('')
1044
1045        return ''.join([''.join(v) for v in zip(value[::2], subs)])
1046       
1047    def __getitem__(self, key):
1048        try:
1049            return self._data[key]
1050        except KeyError:
1051            pass
1052
1053        v = self.get(key)
1054        if v is None:
1055            raise MissingOption("Missing option: %s:%s" % (self.name, key))
1056        return v
1057
1058    def __setitem__(self, option, value):
1059        if not isinstance(value, str):
1060            raise TypeError('Option values must be strings', value)
1061        self._data[option] = value
1062
1063    def __delitem__(self, key):
1064        if key in self._raw:
1065            del self._raw[key]
1066            if key in self._data:
1067                del self._data[key]
1068            if key in self._cooked:
1069                del self._cooked[key]
1070        elif key in self._data:
1071            del self._data[key]
1072        else:
1073            raise KeyError, key
1074
1075    def keys(self):
1076        raw = self._raw
1077        return list(self._raw) + [k for k in self._data if k not in raw]
1078
1079    def copy(self):
1080        result = self._raw.copy()
1081        result.update(self._cooked)
1082        result.update(self._data)
1083        return result
1084
1085    def _call(self, f):
1086        buildout_directory = self.buildout['buildout']['directory']
1087        self._created = []
1088        try:
1089            try:
1090                os.chdir(buildout_directory)
1091                return f()
1092            except:
1093                for p in self._created:
1094                    if os.path.isdir(p):
1095                        rmtree(p)
1096                    elif os.path.isfile(p):
1097                        os.remove(p)
1098                    else:
1099                        self._buildout._logger.warn("Couldn't clean up %r.", p)
1100                raise
1101        finally:
1102            self._created = None
1103            os.chdir(buildout_directory)
1104
1105    def created(self, *paths):
1106        try:
1107            self._created.extend(paths)
1108        except AttributeError:
1109            raise TypeError(
1110                "Attempt to register a created path while not installing",
1111                self.name)
1112        return self._created
1113
1114_spacey_nl = re.compile('[ \t\r\f\v]*\n[ \t\r\f\v\n]*'
1115                        '|'
1116                        '^[ \t\r\f\v]+'
1117                        '|'
1118                        '[ \t\r\f\v]+$'
1119                        )
1120
1121_spacey_defaults = [
1122    ('%(__buildout_space__)s',   ' '),
1123    ('%(__buildout_space_n__)s', '\n'),
1124    ('%(__buildout_space_r__)s', '\r'),
1125    ('%(__buildout_space_f__)s', '\f'),
1126    ('%(__buildout_space_v__)s', '\v'),
1127    ]
1128
1129def _quote_spacey_nl(match):
1130    match = match.group(0).split('\n', 1)
1131    result = '\n\t'.join(
1132        [(s
1133          .replace(' ', '%(__buildout_space__)s')
1134          .replace('\r', '%(__buildout_space_r__)s')
1135          .replace('\f', '%(__buildout_space_f__)s')
1136          .replace('\v', '%(__buildout_space_v__)s')
1137          .replace('\n', '%(__buildout_space_n__)s')
1138          )
1139         for s in match]
1140        )
1141    return result
1142
1143def _save_option(option, value, f):
1144    value = _spacey_nl.sub(_quote_spacey_nl, value)
1145    if value.startswith('\n\t'):
1146        value = '%(__buildout_space_n__)s' + value[2:]
1147    if value.endswith('\n\t'):
1148        value = value[:-2] + '%(__buildout_space_n__)s'
1149    print >>f, option, '=', value
1150   
1151def _save_options(section, options, f):
1152    print >>f, '[%s]' % section
1153    items = options.items()
1154    items.sort()
1155    for option, value in items:
1156        _save_option(option, value, f)
1157
1158def _open(base, filename, seen):
1159    """Open a configuration file and return the result as a dictionary,
1160
1161    Recursively open other files based on buildout options found.
1162    """
1163
1164    if _isurl(filename):
1165        fp = urllib2.urlopen(filename)
1166        base = filename[:filename.rfind('/')]
1167    elif _isurl(base):
1168        if os.path.isabs(filename):
1169            fp = open(filename)
1170            base = os.path.dirname(filename)
1171        else:
1172            filename = base + '/' + filename
1173            fp = urllib2.urlopen(filename)
1174            base = filename[:filename.rfind('/')]
1175    else:
1176        filename = os.path.join(base, filename)
1177        fp = open(filename)
1178        base = os.path.dirname(filename)
1179
1180    if filename in seen:
1181        raise zc.buildout.UserError("Recursive file include", seen, filename)
1182
1183    seen.append(filename)
1184
1185    result = {}
1186
1187    parser = ConfigParser.RawConfigParser()
1188    parser.optionxform = lambda s: s
1189    parser.readfp(fp)
1190    extends = extended_by = None
1191    for section in parser.sections():
1192        options = dict(parser.items(section))
1193        if section == 'buildout':
1194            extends = options.pop('extends', extends)
1195            extended_by = options.pop('extended-by', extended_by)
1196        result[section] = options
1197
1198    if extends:
1199        extends = extends.split()
1200        extends.reverse()
1201        for fname in extends:
1202            result = _update(_open(base, fname, seen), result)
1203
1204    if extended_by:
1205        self._logger.warn(
1206            "The extendedBy option is deprecated.  Stop using it."
1207            )
1208        for fname in extended_by.split():
1209            result = _update(result, _open(base, fname, seen))
1210
1211    seen.pop()
1212    return result
1213   
1214
1215ignore_directories = '.svn', 'CVS'
1216def _dir_hash(dir):
1217    hash = md5()
1218    for (dirpath, dirnames, filenames) in os.walk(dir):
1219        dirnames[:] = [n for n in dirnames if n not in ignore_directories]
1220        filenames[:] = [f for f in filenames
1221                        if (not (f.endswith('pyc') or f.endswith('pyo'))
1222                            and os.path.exists(os.path.join(dirpath, f)))
1223                        ]
1224        hash.update(' '.join(dirnames))
1225        hash.update(' '.join(filenames))
1226        for name in filenames:
1227            hash.update(open(os.path.join(dirpath, name)).read())
1228    return hash.digest().encode('base64').strip()
1229   
1230def _dists_sig(dists):
1231    result = []
1232    for dist in dists:
1233        location = dist.location
1234        if dist.precedence == pkg_resources.DEVELOP_DIST:
1235            result.append(dist.project_name + '-' + _dir_hash(location))
1236        else:
1237            result.append(os.path.basename(location))
1238    return result
1239
1240def _update_section(s1, s2):
1241    for k, v in s2.items():
1242        if k.endswith('+'):
1243            key = k.rstrip(' +')
1244            s2[key] = "\n".join(s1.get(key, "").split('\n') + s2[k].split('\n'))
1245            del s2[k]
1246        elif k.endswith('-'):
1247            key = k.rstrip(' -')
1248            s2[key] = "\n".join([v for v in s1.get(key, "").split('\n')
1249                                 if v not in s2[k].split('\n')])
1250            del s2[k]
1251               
1252    s1.update(s2)
1253    return s1
1254
1255def _update(d1, d2):
1256    for section in d2:
1257        if section in d1:
1258            d1[section] = _update_section(d1[section], d2[section])
1259        else:
1260            d1[section] = d2[section]
1261    return d1
1262
1263def _recipe(options):
1264    recipe = options['recipe']
1265    if ':' in recipe:
1266        recipe, entry = recipe.split(':')
1267    else:
1268        entry = 'default'
1269
1270    return recipe, entry
1271
1272def _doing():
1273    _, v, tb = sys.exc_info()
1274    message = str(v)
1275    doing = []
1276    while tb is not None:
1277        d = tb.tb_frame.f_locals.get('__doing__')
1278        if d:
1279            doing.append(d)
1280        tb = tb.tb_next
1281       
1282    if doing:
1283        sys.stderr.write('While:\n')
1284        for d in doing:
1285            if not isinstance(d, str):
1286                d = d[0] % d[1:]
1287            sys.stderr.write(%s\n' % d)
1288
1289def _error(*message):
1290    sys.stderr.write('Error: ' + ' '.join(message) +'\n')
1291    sys.exit(1)
1292
1293_internal_error_template = """
1294An internal error occured due to a bug in either zc.buildout or in a
1295recipe being used:
1296"""
1297
1298def _check_for_unused_options_in_section(buildout, section):
1299    options = buildout[section]
1300    unused = [option for option in options._raw if option not in options._data]
1301    if unused:
1302        buildout._logger.warn("Unused options for %s: %s."
1303                              % (section, ' '.join(map(repr, unused)))
1304                              )
1305
1306_usage = """\
1307Usage: buildout [options] [assignments] [command [command arguments]]
1308
1309Options:
1310
1311  -h, --help
1312
1313     Print this message and exit.
1314
1315  -v
1316
1317     Increase the level of verbosity.  This option can be used multiple times.
1318
1319  -q
1320
1321     Decrease the level of verbosity.  This option can be used multiple times.
1322
1323  -c config_file
1324
1325     Specify the path to the buildout configuration file to be used.
1326     This defaults to the file named "buildout.cfg" in the current
1327     working directory.
1328
1329  -t socket_timeout
1330
1331     Specify the socket timeout in seconds.
1332
1333  -U
1334
1335     Don't read user defaults.
1336
1337  -o
1338 
1339    Run in off-line mode.  This is equivalent to the assignment
1340    buildout:offline=true.
1341
1342  -O
1343
1344    Run in non-off-line mode.  This is equivalent to the assignment
1345    buildout:offline=false.  This is the default buildout mode.  The
1346    -O option would normally be used to override a true offline
1347    setting in a configuration file.
1348
1349  -n
1350
1351    Run in newest mode.  This is equivalent to the assignment
1352    buildout:newest=true.  With this setting, which is the default,
1353    buildout will try to find the newest versions of distributions
1354    available that satisfy its requirements.
1355
1356  -N
1357
1358    Run in non-newest mode.  This is equivalent to the assignment
1359    buildout:newest=false.  With this setting, buildout will not seek
1360    new distributions if installed distributions satisfy it's
1361    requirements.
1362
1363  -D
1364
1365    Debug errors.  If an error occurs, then the post-mortem debugger
1366    will be started. This is especially useful for debuging recipe
1367    problems.
1368
1369Assignments are of the form: section:option=value and are used to
1370provide configuration options that override those given in the
1371configuration file.  For example, to run the buildout in offline mode,
1372use buildout:offline=true.
1373
1374Options and assignments can be interspersed.
1375
1376Commands:
1377
1378  install [parts]
1379
1380    Install parts.  If no command arguments are given, then the parts
1381    definition from the configuration file is used.  Otherwise, the
1382    arguments specify the parts to be installed.
1383
1384    Note that the semantics differ depending on whether any parts are
1385    specified.  If parts are specified, then only those parts will be
1386    installed. If no parts are specified, then the parts specified by
1387    the buildout parts option will be installed along with all of
1388    their dependencies.
1389
1390  bootstrap
1391
1392    Create a new buildout in the current working directory, copying
1393    the buildout and setuptools eggs and, creating a basic directory
1394    structure and a buildout-local buildout script.
1395
1396  init
1397
1398    Initialize a buildout, creating a buildout.cfg file if it doesn't
1399    exist and then performing the same actions as for the buildout
1400    command.
1401
1402  setup script [setup command and options]
1403
1404    Run a given setup script arranging that setuptools is in the
1405    script's path and and that it has been imported so that
1406    setuptools-provided commands (like bdist_egg) can be used even if
1407    the setup script doesn't import setuptools itself.
1408
1409    The script can be given either as a script path or a path to a
1410    directory containing a setup.py script.
1411   
1412"""
1413def _help():
1414    print _usage
1415    sys.exit(0)
1416
1417def main(args=None):
1418    if args is None:
1419        args = sys.argv[1:]
1420
1421    config_file = 'buildout.cfg'
1422    verbosity = 0
1423    options = []
1424    windows_restart = False
1425    user_defaults = True
1426    debug = False
1427    while args:
1428        if args[0][0] == '-':
1429            op = orig_op = args.pop(0)
1430            op = op[1:]
1431            while op and op[0] in 'vqhWUoOnND':
1432                if op[0] == 'v':
1433                    verbosity += 10
1434                elif op[0] == 'q':
1435                    verbosity -= 10
1436                elif op[0] == 'W':
1437                    windows_restart = True
1438                elif op[0] == 'U':
1439                    user_defaults = False
1440                elif op[0] == 'o':
1441                    options.append(('buildout', 'offline', 'true'))
1442                elif op[0] == 'O':
1443                    options.append(('buildout', 'offline', 'false'))
1444                elif op[0] == 'n':
1445                    options.append(('buildout', 'newest', 'true'))
1446                elif op[0] == 'N':
1447                    options.append(('buildout', 'newest', 'false'))
1448                elif op[0] == 'D':
1449                    debug = True
1450                else:
1451                    _help()
1452                op = op[1:]
1453               
1454            if op[:1] in  ('c', 't'):
1455                op_ = op[:1]
1456                op = op[1:]
1457
1458                if op_ == 'c':
1459                    if op:
1460                        config_file = op
1461                    else:
1462                        if args:
1463                            config_file = args.pop(0)
1464                        else:
1465                            _error("No file name specified for option", orig_op)
1466                elif op_ == 't':
1467                    try:
1468                        timeout = int(args.pop(0))
1469                    except IndexError:
1470                        _error("No timeout value specified for option", orig_op)
1471                    except ValueError:
1472                        _error("No timeout value must be numeric", orig_op)
1473
1474                    import socket
1475                    print 'Setting socket time out to %d seconds' % timeout
1476                    socket.setdefaulttimeout(timeout)
1477
1478            elif op:
1479                if orig_op == '--help':
1480                    _help()
1481                _error("Invalid option", '-'+op[0])
1482        elif '=' in args[0]:
1483            option, value = args.pop(0).split('=', 1)
1484            if len(option.split(':')) != 2:
1485                _error('Invalid option:', option)
1486            section, option = option.split(':')
1487            options.append((section.strip(), option.strip(), value.strip()))
1488        else:
1489            # We've run out of command-line options and option assignnemnts
1490            # The rest should be commands, so we'll stop here
1491            break
1492
1493    if verbosity:
1494        options.append(('buildout', 'verbosity', str(verbosity)))
1495
1496    if args:
1497        command = args.pop(0)
1498        if command not in (
1499            'install', 'bootstrap', 'runsetup', 'setup', 'init',
1500            ):
1501            _error('invalid command:', command)
1502    else:
1503        command = 'install'
1504
1505    try:
1506        try:
1507            buildout = Buildout(config_file, options,
1508                                user_defaults, windows_restart, command)
1509            getattr(buildout, command)(args)
1510        except SystemExit:
1511            pass
1512        except Exception, v:
1513            _doing()
1514            exc_info = sys.exc_info()
1515            import pdb, traceback
1516            if debug:
1517                traceback.print_exception(*exc_info)
1518                sys.stderr.write('\nStarting pdb:\n')
1519                pdb.post_mortem(exc_info[2])
1520            else:
1521                if isinstance(v, (zc.buildout.UserError,
1522                                  distutils.errors.DistutilsError,
1523                                  )
1524                              ):
1525                    _error(str(v))
1526                else:
1527                    sys.stderr.write(_internal_error_template)
1528                    traceback.print_exception(*exc_info)
1529                    sys.exit(1)
1530   
1531           
1532    finally:
1533            logging.shutdown()
1534
1535if sys.version_info[:2] < (2, 4):
1536    def reversed(iterable):
1537        result = list(iterable);
1538        result.reverse()
1539        return result
Note: See TracBrowser for help on using the repository browser.