import sys, os, warnings, email
from types import ClassType, ListType
from distutils import command, filelist, version
from distutils.cmd import Command
from distutils.core import Distribution, gen_usage, DEBUG
from distutils.errors import *
from distutils.fancy_getopt import FancyGetopt, wrap_text
try:
    from distutils import log
except ImportError:
    # Python 2.2; create an instance that has the module interface but acts
    # like the announce() methods in 2.2.
    class Log:
        verbose = 1
        def log(self, level, msg):
            if self.verbose >= level:
                print msg
                sys.stdout.flush()
        def set_verbosity(self, verbose):
            self.verbose = verbose
    log = Log()
else:
    if sys.version < '2.5':
        def _log(self, level, msg, args):
            if level >= self.threshold:
                if args:
                    msg %= args
                print msg
                sys.stdout.flush()
            return
        log.Log._log = _log
        del _log

from Ft.Lib import Terminfo
from Ft.Lib.DistExt import Version

# Our new Distribution class
class Dist(Distribution):
    """
    An enhanced version of core Distutils' Distribution class.

    Currently supported features, for *all* Python (2.2+) versions:
    (from Python 2.3+)
    download_url, classifiers - PEP 314 metadata fields

    (from Python 2.5+)
    install_egg_info command - for setuptools
    requires, provides, obsoletes - PEP 314 metadata fields

    (only available in 4Suite)
    requires_python - [PEP 345] a list of version restrictions for Python
    requires_external - [PEP 345] a list of external requirements
    command_mapping - maps command names to a module/class name that differs
                      from the actual command name
    """
    # 'command_mapping' maps command names to the module/class names
    command_mapping = {
        'config' : 'Config',

        'build' : 'Build',
        'build_py' : 'BuildPy',
        'build_ext' : 'BuildExt',
        'build_clib' : None,
        'build_scripts' : 'BuildScripts',
        'build_docs' : 'BuildDocs',                     # only in 4Suite
        'build_l10n' : 'BuildL10n',                     # only in 4Suite

        'clean' : None,

        'install' : 'Install',
        'install_lib' : 'InstallLib',
        'install_headers' : None,
        'install_scripts' : 'InstallScripts',
        'install_data' : 'InstallData',
        'install_egg_info' : 'InstallEggInfo',          # new in 2.5+
        'install_sysconf' : 'InstallSysconf',           # only in 4Suite
        'install_localstate' : 'InstallLocalState',     # only in 4Suite
        'install_devel' : 'InstallDevel',               # only in 4Suite
        #'install_man' : 'InstallMan',                   # only in 4Suite
        'install_text' : 'InstallText',                 # only in 4Suite
        'install_html' : 'InstallHtml',                 # only in 4Suite
        #'install_info' : 'InstallInfo',                 # only in 4Suite
        'install_l10n' : 'InstallL10n',                 # only in 4Suite
        'install_config' : 'InstallConfig',             # only in 4Suite

        'sdist' : 'SDist',

        'register' : None,                              # new in 2.3+

        'bdist' : 'BDist',
        'bdist_dumb' : None,
        'bdist_rpm' : 'BDistRpm',
        'bdist_inno' : 'BDistInno',                     # only in 4Suite
        'bdist_msi' : None,                             # new in 2.5+
        'bdist_egg' : 'BDistEgg',

        'upload' : None,                                # new in 2.5+

        'generate' : 'Generate',                        # only in 4Suite
        'generate_bgen' : 'GenerateBisonGen',           # only in 4Suite
        'generate_l10n' : 'GenerateL10n',               # only in 4Suite

        }

    command_aliases = {
        'install_docs' : 'install_html',
        'bdist_wininst' : 'bdist_inno',
        }

    standard_commands = ['config', 'build', 'clean', 'install', 'sdist',
                         'register', 'bdist', 'upload', 'generate']
    if sys.version < '2.5':
        standard_commands.remove('upload')
    if sys.version < '2.3':
        standard_commands.remove('register')

    # 'toplevel_options' desribes the command-line options that may be
    # supplied to the setup script prior to any actual command.
    toplevel_options = []

    # PKG-INFO is created for source distributions, so allow "developer"
    # friendly features to be enabled/disabled (i.e., install_docs)
    source_package = os.path.exists('PKG-INFO')
    if not source_package:
        toplevel_options.extend([
            ('source-package', 's',
             'run as if from a source dist (developer testing)'),
            ])

    def __init__(self, attrs):
        # Add our placeholders for arguments from setup()
        self.l10n = []
        self.doc_files = []
        self.bgen_files = []
        self.sysconf_files = []
        self.localstate_files = []
        self.devel_files = []

        # The module where configuration variables are written.
        # Used by the 'install_config' command.
        self.config_module = None

        # File in source tree that represents the software copyright.
        # Currently, only used by the 'bdist_inno' command.
        self.license_file = None

        # 'package' is the name of the subpackage.
        self.package = None

        # File in source tree that contains the setup attributes for the
        # subpackage.
        self.package_file = None
        self.main_distribution = None

        # Used for gathering and validating the files included in a source
        # distribution. Used by the 'sdist' command.
        self.manifest_templates = []
        self.validate_templates = []

        # Add support for build_py's 'package_data'. New in Python 2.4+
        self.package_data = {}

        # 'namespace_packages' is a list of package names whose contents are
        # split across multiple distributions.
        self.namespace_packages = None
        Distribution.__init__(self, attrs)
        return

    def get_allfiles(self):
        if self._allfiles is None:
            # If a "main" distribution exists, use its files to prevent
            # unnecessary additional searches.
            if self.main_distribution:
                self._allfiles = self.main_distribution.get_allfiles()
            else:
                source_list = filelist.FileList()
                source_list.extend(filelist.findall())
                # Remove files that don't really belong in the file list.
                # Note the leading slash (\) before os.sep substitutions. It is
                # needed to prevent regex-escaping when os.sep is '\' (Windows).
                exclude_patterns = (
                    # revision control (CVS client) files
                    r'\%s?CVS(\.sandboxinfo)?\%s' % (os.sep, os.sep),
                    r'\.cvsignore$',
                    r'\.#[^\%s]+$' % os.sep,
                    # (X)Emacs temporary files
                    r'\.?#[^\%s]+#$' % os.sep,
                    # common editor backup files
                    r'[^\%s]+~$' % os.sep,
                    # python bytecode files
                    r'\.py[co]$',
                    )
                for pattern in exclude_patterns:
                    source_list.exclude_pattern(pattern, is_regex=True)
                self._allfiles = source_list.files
        return self._allfiles

    def get_source_files(self):
        source_list = filelist.FileList()
        source_list.set_allfiles(self.get_allfiles())

        # Add the files used to create the Distribution
        source_list.append(self.script_name)
        if os.path.exists('setup.cfg'):
            source_list.append('setup.cfg')
        if self.package_file:
            source_list.append(self.package_file)

        # Get the source files from the command groupds
        for cmd_name in ('generate', 'build', 'install'):
            cmd = self.get_command_obj(cmd_name)
            cmd.ensure_finalized()
            source_list.extend(cmd.get_source_files())
        # 'license_file' is used by bdist_inno
        if self.license_file:
            source_list.append(self.license_file)

        # Add the files not included by the commands
        for line in self.manifest_templates:
            try:
                source_list.process_template_line(line)
            except DistutilsTemplateError, msg:
                self.warn(str(msg))

        # File list now complete -- sort it so that higher-level files
        # come first
        source_list.sort()

        # Remove duplicates from the file list
        source_list.remove_duplicates()

        return source_list.files

    # -- Config file finding/parsing methods ---------------------------

    if sys.version < '2.4':
        def parse_config_files(self, filenames=None):
            Distribution.parse_config_files(self, filenames)
            if 'global' in self.command_options:
                global_options = self.command_options['global']
                boolean_options = {'verbose':1, 'dry_run':1}
                boolean_options.update(self.negative_opt)
                for opt in global_options:
                    if opt not in boolean_options:
                        setattr(self, opt, global_options[opt][1])
            return
    # -- Command-line parsing methods ----------------------------------

    if sys.version < '2.4':
        def parse_command_line(self):
            """Parse the setup script's command line, taken from the
            'script_args' instance attribute (which defaults to 'sys.argv[1:]'
            -- see 'setup()' in core.py).  This list is first processed for
            "global options" -- options that set attributes of the Distribution
            instance.  Then, it is alternately scanned for Distutils commands
            and options for that command.  Each new command terminates the
            options for the previous command.  The allowed options for a
            command are determined by the 'user_options' attribute of the
            command class -- thus, we have to be able to load command classes
            in order to parse the command line.  Any error in that 'options'
            attribute raises DistutilsGetoptError; any error on the
            command-line raises DistutilsArgError.  If no Distutils commands
            were found on the command line, raises DistutilsArgError.  Return
            true if command-line was successfully parsed and we should carry
            on with executing commands; false if no errors but we shouldn't
            execute commands (currently, this only happens if user asks for
            help).
            """
            #
            # We now have enough information to show the Macintosh dialog
            # that allows the user to interactively specify the "command line".
            #
            toplevel_options = self._get_toplevel_options()
            if sys.platform == 'mac':
                import EasyDialogs
                cmdlist = self.get_command_list()
                self.script_args = EasyDialogs.GetArgv(
                    toplevel_options + self.display_options, cmdlist)

            # We have to parse the command line a bit at a time -- global
            # options, then the first command, then its options, and so on --
            # because each command will be handled by a different class, and
            # the options that are valid for a particular class aren't known
            # until we have loaded the command class, which doesn't happen
            # until we know what the command is.

            self.commands = []
            parser = FancyGetopt(toplevel_options + self.display_options)
            parser.set_negative_aliases(self.negative_opt)
            parser.set_aliases({'licence': 'license'})
            args = parser.getopt(args=self.script_args, object=self)
            option_order = parser.get_option_order()
            log.set_verbosity(self.verbose)

            # for display options we return immediately
            if self.handle_display_options(option_order):
                return

            while args:
                args = self._parse_command_opts(parser, args)
                if args is None:            # user asked for help (and got it)
                    return

            # Handle the cases of --help as a "global" option, ie.
            # "setup.py --help" and "setup.py --help command ...".  For the
            # former, we show global options (--verbose, --dry-run, etc.)
            # and display-only options (--name, --version, etc.); for the
            # latter, we omit the display-only options and show help for
            # each command listed on the command line.
            if self.help:
                self._show_help(parser,
                                display_options=len(self.commands) == 0,
                                commands=self.commands)
                return

            # Oops, no commands found -- an end-user error
            if not self.commands:
                raise DistutilsArgError, "no commands supplied"

            # All is well: return true
            return 1

    def _get_toplevel_options(self):
        """Return the non-display options recognized at the top level.

        This includes options that are recognized *only* at the top
        level as well as options recognized for commands.
        """
        if sys.version < '2.4':
            toplevel_options = self.global_options
        else:
            toplevel_options = Distribution._get_toplevel_options(self)
        return toplevel_options + self.toplevel_options

    def finalize_options(self):
        if sys.version < '2.5':
            # Run the setter functions for the metadata fields that have them.
            # Only those fields that have a supplied value (not None) will
            # be considered.
            for name, value in vars(self.metadata).items():
                if value is not None:
                    try:
                        setter = getattr(self.metadata, 'set_' + name)
                    except AttributeError:
                        pass
                    else:
                        setter(value)

        requires_python = self.get_requires_python()
        if requires_python:
            requires_python = 'Python (%s)' % ', '.join(requires_python)
            requires_python = Version.VersionPredicate(requires_python)
            python_version = version.StrictVersion()
            python_version.version = sys.version_info[:3]
            python_version.prerelease = sys.version_info[3:]
            if not requires_python.satisfied_by(python_version):
                raise DistutilsSetupError(
                    "%s requires %s" % (self.metadata.name, requires_python))

        # Initialize the containter type data variables before dealing
        # with the information from the package defintions.
        if self.packages is None:
            self.packages = []
        if self.package_dir is None:
            self.package_dir = {}
        if self.py_modules is None:
            self.py_modules = []
        if self.libraries is None:
            self.libraries = []
        if self.headers is None:
            self.headers = []
        if self.ext_modules is None:
            self.ext_modules = []
        if self.include_dirs is None:
            self.include_dirs = []
        if self.scripts is None:
            self.scripts = []
        if self.data_files is None:
            self.data_files = []
        if self.package_file is None:
            self.package_file = self.script_name
        if self.namespace_packages is None:
            self.namespace_packages = []

        # Per PEP 314, only use License and Platform if they can't be
        # handled by an appropriate classifier. Or, in our case, aren't
        # being handled by a classifier entry.
        has_platform = has_license = False
        for classifier in self.get_classifiers():
            category = classifier.split('::', 1)[0]
            category = category.strip().title()
            if category == 'Operating System':
                has_platform = True
            elif category == 'License':
                has_license = True

        if self.metadata.license and has_license:
            raise DistutilsSetupError("license keyword conflicts with"
                                      " classifiers list")

        if self.metadata.platforms and has_platform:
            raise DistutilsSetupError("platforms keyword conflicts with"
                                      " classifiers list")

        # Finalize "private" variables; those that are not part of the
        # setup arguments.
        self._allfiles = None

        Distribution.finalize_options(self)

    def print_commands(self):
        """
        Overridden to add the commands defined by 'command_mapping' to the
        list of "standard commands".
        """
        std_commands = []
        is_std = {}
        for command in self.standard_commands:
            std_commands.append(command)
            is_std[command] = True
            klass = self.get_command_class(command)
            for command, method in klass.sub_commands:
                std_commands.append(command)
                is_std[command] = True

        extra_commands = []
        for command in self.cmdclass:
            if command not in is_std:
                extra_commands.append(command)

        max_length = max(map(len, (std_commands + extra_commands)))

        self.print_command_list(std_commands, "Standard commands", max_length)

        if extra_commands:
            print
            self.print_command_list(extra_commands, "Extra commands",
                                    max_length)
        return

    def get_command_list(self):
        """
        Overridden to add the commands defined by 'command_mapping' to the
        list of (command, description) tuples.
        """
        for command in self.command_mapping:
            self.get_command_class(command)
        return Distribution.get_command_list(self)

    def print_option_list(self, options, header, max_length):
        # Generate lines of help text.
        line_width = Terminfo.GetColumns()
        opt_width = max_length + 2 + 2 + 2  # room for indent + dashes + gutter
        text_width = line_width - opt_width
        big_indent = ' ' * opt_width

        print header

        for option in options:
            long, short, help = option[:3]
            if long[-1] == '=':
                long = long[0:-1]

            # Case 1: no short option at all
            if short is None:
                opt_names = long

            # Case 2: we have a short option, so we have to include it
            # just after the long option
            else:
                opt_names = "%s (-%s)" % (long, short)

            text = wrap_text(help, text_width)
            if text:
                print "  --%-*s  %s" % (max_length, opt_names, text[0])
                for line in text[1:]:
                    print big_indent + line
            else:
                print "  --%-*s" % (max_length, opt_names)

        print
        return

    def _show_help (self, parser, global_options=1, display_options=1,
                    commands=[]):
        # Gather the options for the distribution
        options = []
        if global_options:
            if display_options:
                global_options = self._get_toplevel_options()
            else:
                global_options = self.global_options
            options.extend(global_options)

        if display_options:
            display_options = self.display_options
            options.extend(display_options)

        # Gather the options for the requested commands
        commands = []
        for command in self.commands:
            klass = self.get_command_class(command)
            command_name = getattr(klass, 'command_name', klass.__name__)
            command_options = klass.user_options
            if hasattr(klass, 'help_options'):
                command_options = command_options + klass.help_options
            commands.append((command_name, command_options))
            options.extend(command_options)

        # Determine maximum length of option names
        max_length = 0
        for option in options:
            long = option[0]
            short = option[1]
            l = len(long)
            if long[-1] == '=':
                l = l - 1
            if short is not None:
                l = l + 5                   # " (-x)" where short == 'x'
            if l > max_length:
                max_length = l

        # Now print the option tables
        if global_options:
            self.print_option_list(global_options, "Global options:",
                                   max_length)

        if display_options:
            self.print_option_list(display_options,
                                   "Information display options (just display"
                                   " information, ignore any commands):",
                                   max_length)

        for name, options in commands:
            self.print_option_list(options,
                                   "Options for '%s' command:" % name,
                                   max_length)

        print gen_usage(self.script_name)
        return

    # -- Command class/object methods ----------------------------------

    def get_command_class(self, command):
        """
        Extends Distribution.get_command_class() to search 'command_mapping'
        for modules that implement that requested command.
        """
        # Try user defined classes first (and already loaded classes)
        klass = self.cmdclass.get(command)
        if klass:
            return klass

        if command in self.command_aliases:
            command = self.command_aliases[command]

        base_name = self.command_mapping.get(command)
        if base_name is None:
            return Distribution.get_command_class(self, command)

        command_package = 'Ft.Lib.DistExt'
        module_name = command_package + '.' + base_name
        klass_name = base_name
        try:
            module = __import__(module_name, {}, {}, [klass_name])
        except ImportError:
            # If the module exists but is just broken, re-raise the existing
            # exception as this is (most likely) a developer error.
            if sys.exc_info()[-1].tb_next is not None:
                raise
            raise DistutilsModuleError(
                "invalid command '%s' (no module named '%s')" %
                (command, module_name))

        try:
            klass = getattr(module, klass_name)
        except AttributeError:
            raise DistutilsModuleError(
                "invalid command '%s' (no class '%s' in module '%s')" %
                (command, klass_name, module_name))

        # Make sure that the command provides the proper command name
        try:
            if command != klass.command_name:
                raise AttributeError('command_name')
        except AttributeError:
            raise DistutilsClassError(
                "command class %s must define 'command_name' as %r" %
                (klass, command))

        self.cmdclass[command] = klass
        return klass

    # -- Methods that operate on the Distribution ----------------------

    def announce (self, msg, level=1):
        """If the current verbosity level is of greater than or equal to
        'level' print 'msg' to stdout.
        """
        log.log(level, msg)

    # -- Distribution query methods ------------------------------------

    def has_l10n(self):
        # Used for both build and generate
        return len(self.l10n) > 0

    def has_sysconf(self):
        # Used for install
        return len(self.sysconf_files) > 0

    def has_localstate(self):
        # Used for install
        return len(self.localstate_files) > 0

    def has_docs(self):
        # Used for both build and install
        # Both scripts and modules have generated documentation
        return (len(self.doc_files) > 0 or
                self.has_modules() or self.has_scripts())

    def has_text(self):
        return self.license_file is not None or len(self.doc_files) > 0

    def has_devel(self):
        # Used for install
        return len(self.devel_files) > 0

    def has_bgen(self):
        # Used for both sdist and generate
        return self.bgen_files and len(self.bgen_files) > 0

# ----------------------------------------------------------------------
# Upgade distutils core support to 2.5+ features

import re, operator
from distutils import dist
from distutils.util import rfc822_escape

class DistributionMetadata(dist.DistributionMetadata):

    _METHOD_BASENAMES = dist.DistributionMetadata._METHOD_BASENAMES + (
        'requires_python', 'requires_external')

    requires_python = None
    requires_external = None
    copyright = None

    def get_requires_python(self):
        return self.requires_python or []

    def set_requires_python(self, value):
        if not isinstance(value, list):
            value = [ v.strip() for v in value.split(',') ]
        for v in value:
            Version.SplitComparison(v)
        self.requires_python = value

    def get_requires_external(self):
        return self.requires_external or []

    def set_requires_external(self, value):
        for v in value:
            Version.SplitComparison(v)
        self.requires_external = value

    if sys.version < '2.5':
        requires = None
        provides = None
        obsoletes = None

        _METHOD_BASENAMES += ('requires', 'provides', 'obsoletes')

        def get_requires(self):
            return self.requires or []

        def set_requires(self, value):
            for v in value:
                Version.VersionPredicate(v)
            self.requires = value

        def get_provides(self):
            return self.provides or []

        def set_provides(self, value):
            for v in value:
                Version.SplitProvision(v)
            self.provides = value

        def get_obsoletes(self):
            return self.obsoletes or []

        def set_obsoletes(self, value):
            for v in value:
                Version.VersionPredicate(v)
            self.obsoletes = value

        def write_pkg_info(self, base_dir):
            """
            Write the PKG-INFO file into the release tree.
            """
            pkg_info = open(os.path.join(base_dir, 'PKG-INFO'), 'w')
            self.write_pkg_file(pkg_info)
            pkg_info.close()

    if sys.version < '2.3':
        classifiers = None
        download_url = None

        _METHOD_BASENAMES += ('classifiers', 'download_url')

        def get_classifiers(self):
            return self.classifiers or []

        def get_download_url(self):
            return self.download_url or "UNKNOWN"

    # -- PKG-INFO and .egg-info utility methods --------------------

    def from_stream(cls, stream):
        headers = email.message_from_file(stream)
        fields = {}
        for header, value in headers.items():
            # clean up the keys and values, normalise "-" to "_"
            field = header.lower().replace('-', '_')
            value = value.strip()

            # Platform, Classifiers, Requires, Provides, Obsoletes,
            # Requires-External
            if field in fields:
                old = fields[field]
                if isinstance(old, list):
                    old.append(value)
                else:
                    fields[field] = [old, value]
            else:
                fields[field] = value

        # remove fields only needed for the Python Package Index
        for field in ('metadata_version', 'target_version'):
            if field in fields:
                del fields[field]

        # convert comma-separated items into lists
        for field in ('keywords', 'requires_python'):
            if field in fields:
                values = fields[field].split(',')
                values = [ value.strip() for value in values ]
                fields[field] = [ value for value in values if value ]

        # ensure multiple-use fields are lists
        for field in ('platform', 'classifier', 'requires', 'provides',
                     'obsoletes', 'requires_external'):
            if field in fields:
                value = fields[field]
                if not isinstance(value, list):
                    fields[field] = [value]

        # convert PKG-INFO field names to the corresponding metadata names
        for field, attr in (('platform', 'platforms'),
                            ('classifier', 'classifiers'),
                            ('home_page', 'url'),
                            ('summary', 'description'),
                            ):
            if field in fields:
                fields[attr] = fields[field]
                del fields[field]

        self = cls()
        for name, value in fields.items():
            if hasattr(self, 'set_' + name):
                getattr(self, 'set_' + name)(value)
            elif hasattr(self, name):
                setattr(self, name, value)
            else:
                warnings.warn("unknown metadata attribute: %s" % name)
        return self
    from_stream = classmethod(from_stream)

    def from_string(cls, string):
        from cStringIO import StringIO
        return cls.from_stream(StringIO(string))
    from_string = classmethod(from_string)

    def from_filename(cls, filename):
        fp = open(filename)
        try:
            return cls.from_stream(fp)
        finally:
            fp.close()
    from_filename = classmethod(from_filename)

    def write_pkg_file(self, file):
        """
        Write the PKG-INFO format data to a file object.

        Supports metadata version 1.2 (PEP 345), 1.1 (PEP 314) and
        1.0 (PEP 241) in a lowest common denominator fashion.
        """
        # Use lowest possible version for metadata version
        if self.requires_python or self.requires_external:
            # PEP 345 fields
            version = '1.2'
        elif (self.download_url or self.classifiers or
              self.provides or self.requires or self.obsoletes):
            # PEP 314 fields
            version = '1.1'
        else:
            # PEP 241 fields
            version = '1.0'
        file.write('Metadata-Version: %s\n' % version)

        # PEP 241 (http://python.org/dev/peps/pep-0241.html) fields:
        #   Name
        #   Version
        #   Platform (multiple use)     [obsolete]
        #   Summary
        #   Description (optional)
        #   Keywords (comma-separated list, optional)
        #   Home-page (optional)
        #   Author (optional)
        #   Author-email
        #   License                     [obsolete]
        file.write('Name: %s\n' % self.get_name() )
        file.write('Version: %s\n' % self.get_version() )
        file.write('Summary: %s\n' % self.get_description() )
        file.write('Home-page: %s\n' % self.get_url() )
        file.write('Author: %s\n' % self.get_contact() )
        file.write('Author-email: %s\n' % self.get_contact_email() )

        if self.long_description:
            description = rfc822_escape(self.long_description)
            file.write('Description: %s\n' % description)

        if self.keywords:
            keywords = ','.join(self.keywords)
            file.write('Keywords: %s\n' % keywords)

        # Per PEP 314, only use License and Platform if they can't be
        # handled by an appropriate classifier. Or, in our case, aren't
        # being handled by a classifier entry.
        has_platform = has_license = False
        for classifier in self.get_classifiers():
            category = classifier.split('::', 1)[0]
            category = category.strip().title()
            if category == 'Operating System':
                has_platform = True
            elif category == 'License':
                has_license = True

        if self.license:
            if has_license:
                raise DistutilsSetupError("license keyword conflicts with"
                                          " classifiers list")
            file.write('License: %s\n' % self.license)

        if self.platforms:
            if has_platform:
                raise DistutilsSetupError("platforms keyword conflicts with"
                                          " classifiers list")
            for platform in self.platforms:
                file.write('Platform: %s\n' % platform)

        # PEP 314 (http://python.org/dev/peps/pep-0314.html) fields:
        #   Supported-Platform (multiple use) [binary dists only; unused]
        #   Download-URL
        #   Classifier (multiple use)
        #   Requires (multiple use)
        #   Provides (multiple use)
        #   Obsoletes (multiple use)
        if self.download_url:
            file.write('Download-URL: %s\n' % self.get_download_url())
        for value in self.get_classifiers():
            file.write('Classifier: %s\n' % value)
        for value in self.get_requires():
            file.write('Requires: %s\n' % value)
        for value in self.get_provides():
            file.write('Provides: %s\n' % value)
        for value in self.get_obsoletes():
            file.write('Obsoletes: %s\n' % value)

        # PEP 345 (http://python.org/dev/peps/pep-0345.html) fields:
        #   Requires-Python (comma separated list)
        #   Requires-External (multiple use)
        #   Copyright
        if self.requires_python:
            value = ','.join(self.get_requires_python())
            file.write('Requires-Python: %s\n' % value)
        for value in self.get_requires_external():
            file.write('Requires-External: %s\n' % value)
        if self.copyright:
            file.write('Copyright: %s\n' % self.copyright)
        return

# Unfortunately, this is the only way to supply a different metadata class
dist.DistributionMetadata = DistributionMetadata
