[Python-checkins] distutils2: branch merge

tarek.ziade python-checkins at python.org
Sat Nov 13 18:32:53 CET 2010


tarek.ziade pushed 1244d4ab6090 to distutils2:

http://hg.python.org/distutils2/rev/1244d4ab6090
changeset:   822:1244d4ab6090
parent:      821:51972a81f7e7
parent:      816:7ebf14ab2840
user:        Alexis Metaireau <ametaireau at gmail.com>
date:        Sat Nov 13 16:58:33 2010 +0000
summary:     branch merge
files:       distutils2/extension.py

diff --git a/distutils2/command/__init__.py b/distutils2/command/__init__.py
--- a/distutils2/command/__init__.py
+++ b/distutils2/command/__init__.py
@@ -2,27 +2,53 @@
 
 Package containing implementation of all the standard Distutils
 commands."""
+from distutils2.errors import DistutilsModuleError
+from distutils2.util import resolve_name
 
+_COMMANDS = {
+    'check': 'distutils2.command.check.check',
+    'test': 'distutils2.command.test.test',
+    'build': 'distutils2.command.build.build',
+    'build_py': 'distutils2.command.build_py.build_py',
+    'build_ext': 'distutils2.command.build_ext.build_ext',
+    'build_clib': 'distutils2.command.build_clib.build_clib',
+    'build_scripts': 'distutils2.command.build_scripts.build_scripts',
+    'clean': 'distutils2.command.clean.clean',
+    'install_dist': 'distutils2.command.install_dist.install_dist',
+    'install_lib': 'distutils2.command.install_lib.install_lib',
+    'install_headers': 'distutils2.command.install_headers.install_headers',
+    'install_scripts': 'distutils2.command.install_scripts.install_scripts',
+    'install_data': 'distutils2.command.install_data.install_data',
+    'install_distinfo':
+        'distutils2.command.install_distinfo.install_distinfo',
+    'sdist': 'distutils2.command.sdist.sdist',
+    'bdist': 'distutils2.command.bdist.bdist',
+    'bdist_dumb': 'distutils2.command.bdist_dumb.bdist_dumb',
+    'bdist_wininst': 'distutils2.command.bdist_wininst.bdist_wininst',
+    'register': 'distutils2.command.register.register',
+    'upload': 'distutils2.command.upload.upload',
+    'upload_docs': 'distutils2.command.upload_docs.upload_docs'}
 
-__all__ = ['check',
-           'test',
-           'build',
-           'build_py',
-           'build_ext',
-           'build_clib',
-           'build_scripts',
-           'clean',
-           'install_dist',
-           'install_lib',
-           'install_headers',
-           'install_scripts',
-           'install_data',
-           'install_distinfo',
-           'sdist',
-           'bdist',
-           'bdist_dumb',
-           'bdist_wininst',
-           'register',
-           'upload',
-           'upload_docs'
-          ]
+
+def get_command_names():
+    """Returns registered commands"""
+    return sorted(_COMMANDS.keys())
+
+
+def set_command(location):
+    klass = resolve_name(location)
+    # we want to do the duck-type checking here
+    # XXX
+    _COMMANDS[klass.get_command_name()] = klass
+
+
+def get_command_class(name):
+    """Return the registered command"""
+    try:
+        klass = _COMMANDS[name]
+        if isinstance(klass, str):
+            klass = resolve_name(klass)
+            _COMMANDS[name] = klass
+        return klass
+    except KeyError:
+        raise DistutilsModuleError("Invalid command %s" % name)
diff --git a/distutils2/command/build_ext.py b/distutils2/command/build_ext.py
--- a/distutils2/command/build_ext.py
+++ b/distutils2/command/build_ext.py
@@ -14,7 +14,7 @@
                                DistutilsPlatformError, DistutilsSetupError)
 from distutils2.compiler import customize_compiler, show_compilers
 from distutils2.util import newer_group
-from distutils2.extension import Extension
+from distutils2.compiler.extension import Extension
 from distutils2 import logger
 try:
     import sysconfig
diff --git a/distutils2/command/cmd.py b/distutils2/command/cmd.py
--- a/distutils2/command/cmd.py
+++ b/distutils2/command/cmd.py
@@ -296,11 +296,12 @@
 
     # -- Convenience methods for commands ------------------------------
 
-    def get_command_name(self):
-        if hasattr(self, 'command_name'):
-            return self.command_name
+    @classmethod
+    def get_command_name(cls):
+        if hasattr(cls, 'command_name'):
+            return cls.command_name
         else:
-            return self.__class__.__name__
+            return cls.__name__
 
     def set_undefined_options(self, src_cmd, *options):
         """Set values of undefined options from another command.
diff --git a/distutils2/command/sdist.py b/distutils2/command/sdist.py
--- a/distutils2/command/sdist.py
+++ b/distutils2/command/sdist.py
@@ -15,6 +15,7 @@
 except ImportError:
     from distutils2._backport.shutil import get_archive_formats
 
+from distutils2.command import get_command_names
 from distutils2.command.cmd import Command
 from distutils2.errors import (DistutilsPlatformError, DistutilsOptionError,
                                DistutilsTemplateError, DistutilsModuleError)
@@ -250,7 +251,7 @@
             if files:
                 self.filelist.extend(files)
 
-        for cmd_name in self.distribution.get_command_names():
+        for cmd_name in get_command_names():
             try:
                 cmd_obj = self.get_finalized_command(cmd_name)
             except DistutilsOptionError:
diff --git a/distutils2/compiler/__init__.py b/distutils2/compiler/__init__.py
--- a/distutils2/compiler/__init__.py
+++ b/distutils2/compiler/__init__.py
@@ -13,7 +13,7 @@
     Mainly needed on Unix, so we can plug in the information that
     varies across Unices and is stored in Python's Makefile.
     """
-    if compiler.compiler_type == "unix":
+    if compiler.name == "unix":
         (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \
             sysconfig.get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
                                        'CCSHARED', 'LDSHARED', 'SO', 'AR',
@@ -105,16 +105,19 @@
     return 'unix'
 
 
-_COMPILERS = {'unix': 'distutils2.compiler.unixccompiler.UnixCCompiler',
-              'msvc': 'distutils2.compiler.msvccompiler.MSVCCompiler',
-              'cygwin': 'distutils2.compiler.cygwinccompiler.CygWinCCompiler',
-              'mingw32': 'distutils2.compiler.cygwinccompiler.Mingw32CCompiler',
-              'bcpp': 'distutils2.compilers.bcppcompiler.BCPPCompiler'}
+_COMPILERS = {
+    'unix': 'distutils2.compiler.unixccompiler.UnixCCompiler',
+    'msvc': 'distutils2.compiler.msvccompiler.MSVCCompiler',
+    'cygwin': 'distutils2.compiler.cygwinccompiler.CygWinCCompiler',
+    'mingw32': 'distutils2.compiler.cygwinccompiler.Mingw32CCompiler',
+    'bcpp': 'distutils2.compilers.bcppcompiler.BCPPCompiler'}
 
 
-def set_compiler(name, location):
+def set_compiler(location):
     """Add or change a compiler"""
-    _COMPILERS[name] = location
+    klass = resolve_name(location)
+    # XXX we want to check the class here
+    _COMPILERS[klass.name] = klass
 
 
 def show_compilers():
@@ -124,8 +127,11 @@
     from distutils2.fancy_getopt import FancyGetopt
     compilers = []
 
-    for compiler, location in _COMPILERS.items():
-        klass = resolve_name(location)
+    for name, klass in _COMPILERS.items():
+        if isinstance(klass, str):
+            klass = resolve_name(klass)
+            _COMPILERS[name] = klass
+
         compilers.append(("compiler=" + compiler, None, klass.description))
 
     compilers.sort()
@@ -151,24 +157,22 @@
         if compiler is None:
             compiler = get_default_compiler(plat)
 
-        location = _COMPILERS[compiler]
+        klass = _COMPILERS[compiler]
     except KeyError:
         msg = "don't know how to compile C/C++ code on platform '%s'" % plat
         if compiler is not None:
             msg = msg + " with '%s' compiler" % compiler
         raise DistutilsPlatformError(msg)
 
-    try:
-        cls = resolve_name(location)
-    except ImportError:
-        raise DistutilsModuleError(
-              "can't compile C/C++ code: unable to load '%s'" % \
-              location)
+    if isinstance(klass, str):
+        klass = resolve_name(klass)
+        _COMPILERS[compiler] = klass
+
 
     # XXX The None is necessary to preserve backwards compatibility
     # with classes that expect verbose to be the first positional
     # argument.
-    return cls(None, dry_run, force)
+    return klass(None, dry_run, force)
 
 
 def gen_preprocess_options(macros, include_dirs):
diff --git a/distutils2/compiler/bcppcompiler.py b/distutils2/compiler/bcppcompiler.py
--- a/distutils2/compiler/bcppcompiler.py
+++ b/distutils2/compiler/bcppcompiler.py
@@ -26,7 +26,7 @@
     compiler, as defined by the CCompiler abstract class.
     """
 
-    compiler_type = 'bcpp'
+    name = 'bcpp'
     description = 'Borland C++ Compiler'
 
     # Just set this so CCompiler's constructor doesn't barf.  We currently
diff --git a/distutils2/compiler/ccompiler.py b/distutils2/compiler/ccompiler.py
--- a/distutils2/compiler/ccompiler.py
+++ b/distutils2/compiler/ccompiler.py
@@ -15,6 +15,7 @@
 from distutils2 import logger
 from distutils2.compiler import gen_preprocess_options
 
+
 class CCompiler(object):
     """Abstract base class to define the interface that must be implemented
     by real compiler classes.  Also has some utility methods used by
@@ -29,11 +30,11 @@
     attributes may be varied on a per-compilation or per-link basis.
     """
 
-    # 'compiler_type' is a class attribute that identifies this class.  It
+    # 'name' is a class attribute that identifies this class.  It
     # keeps code that wants to know what kind of compiler it's dealing with
     # from having to import all possible compiler classes just to do an
     # 'isinstance'.
-    compiler_type = None
+    name = None
     description = None
 
     # XXX things not handled by this compiler abstraction model:
diff --git a/distutils2/compiler/cygwinccompiler.py b/distutils2/compiler/cygwinccompiler.py
--- a/distutils2/compiler/cygwinccompiler.py
+++ b/distutils2/compiler/cygwinccompiler.py
@@ -85,7 +85,7 @@
 class CygwinCCompiler(UnixCCompiler):
     """ Handles the Cygwin port of the GNU C compiler to Windows.
     """
-    compiler_type = 'cygwin'
+    name = 'cygwin'
     description = 'Cygwin port of GNU C Compiler for Win32'
     obj_extension = ".o"
     static_lib_extension = ".a"
@@ -110,7 +110,7 @@
 
         self.gcc_version, self.ld_version, self.dllwrap_version = \
             get_compiler_versions()
-        self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" %
+        self.debug_print(self.name + ": gcc %s, ld %s, dllwrap %s\n" %
                          (self.gcc_version,
                           self.ld_version,
                           self.dllwrap_version) )
@@ -272,7 +272,8 @@
 class Mingw32CCompiler(CygwinCCompiler):
     """ Handles the Mingw32 port of the GNU C compiler to Windows.
     """
-    compiler_type = 'mingw32'
+    name = 'mingw32'
+    description = 'MinGW32 compiler'
 
     def __init__(self, verbose=0, dry_run=0, force=0):
 
diff --git a/distutils2/extension.py b/distutils2/compiler/extension.py
rename from distutils2/extension.py
rename to distutils2/compiler/extension.py
diff --git a/distutils2/compiler/msvc9compiler.py b/distutils2/compiler/msvc9compiler.py
--- a/distutils2/compiler/msvc9compiler.py
+++ b/distutils2/compiler/msvc9compiler.py
@@ -285,7 +285,7 @@
     """Concrete class that implements an interface to Microsoft Visual C++,
        as defined by the CCompiler abstract class."""
 
-    compiler_type = 'msvc'
+    name = 'msvc'
     description = 'Microsoft Visual C++'
 
     # Just set this so CCompiler's constructor doesn't barf.  We currently
diff --git a/distutils2/compiler/msvccompiler.py b/distutils2/compiler/msvccompiler.py
--- a/distutils2/compiler/msvccompiler.py
+++ b/distutils2/compiler/msvccompiler.py
@@ -205,7 +205,7 @@
     """Concrete class that implements an interface to Microsoft Visual C++,
        as defined by the CCompiler abstract class."""
 
-    compiler_type = 'msvc'
+    name = 'msvc'
     description = "Microsoft Visual C++"
 
     # Just set this so CCompiler's constructor doesn't barf.  We currently
diff --git a/distutils2/compiler/unixccompiler.py b/distutils2/compiler/unixccompiler.py
--- a/distutils2/compiler/unixccompiler.py
+++ b/distutils2/compiler/unixccompiler.py
@@ -106,7 +106,7 @@
 
 class UnixCCompiler(CCompiler):
 
-    compiler_type = 'unix'
+    name = 'unix'
     description = 'Standard UNIX-style compiler'
 
     # These are used by CCompiler in two places: the constructor sets
diff --git a/distutils2/config.py b/distutils2/config.py
--- a/distutils2/config.py
+++ b/distutils2/config.py
@@ -9,7 +9,7 @@
 from distutils2 import logger
 from distutils2.util import check_environ, resolve_name
 from distutils2.compiler import set_compiler
-
+from distutils2.command import set_command
 
 class Config(object):
     """Reads configuration files and work with the Distribution instance
@@ -94,20 +94,6 @@
                 self.setup_hook = resolve_name(setup_hook)
                 self.run_hook(content)
 
-            if 'commands' in content['global']:
-                commands = self._multiline(content['global']['commands'])
-                if isinstance(commands, str):
-                    commands = [commands]
-
-                for command in commands:
-                    command = command.split('=')
-                    if len(command) != 2:
-                        # Issue XXX a warning
-                        continue
-                    name, location = command[0].strip(), command[1].strip()
-                    self.dist.cmdclass[name] = resolve_name(location)
-
-
         metadata = self.dist.metadata
 
         # setting the metadata values
@@ -195,9 +181,12 @@
                 self._read_setup_cfg(parser)
 
             for section in parser.sections():
-                if section == 'compilers':
-                    self._load_compilers(parser.items(section))
-                    continue
+                if section == 'global':
+                    if parser.has_option('global', 'compilers'):
+                        self._load_compilers(parser.get('global', 'compilers'))
+
+                    if parser.has_option('global', 'commands'):
+                        self._load_commands(parser.get('global', 'commands'))
 
                 options = parser.options(section)
                 opt_dict = self.dist.get_option_dict(section)
@@ -247,5 +236,15 @@
                     raise DistutilsOptionError(msg)
 
     def _load_compilers(self, compilers):
-        for name, location in compilers:
-            set_compiler(name, location)
+        compilers = self._multiline(compilers)
+        if isinstance(compilers, str):
+            compilers = [compilers]
+        for compiler in compilers:
+            set_compiler(compiler.strip())
+
+    def _load_commands(self, commands):
+        commands = self._multiline(commands)
+        if isinstance(commands, str):
+            commands = [commands]
+        for command in commands:
+            set_command(command.strip())
diff --git a/distutils2/dist.py b/distutils2/dist.py
--- a/distutils2/dist.py
+++ b/distutils2/dist.py
@@ -18,6 +18,7 @@
 from distutils2 import logger
 from distutils2.metadata import DistributionMetadata
 from distutils2.config import Config
+from distutils2.command import get_command_class
 
 # Regex to define acceptable Distutils command names.  This is not *quite*
 # the same as a Python NAME -- I don't allow leading underscores.  The fact
@@ -153,14 +154,6 @@
         # for the setup script to override command classes
         self.cmdclass = {}
 
-        # 'command_packages' is a list of packages in which commands
-        # are searched for.  The factory for command 'foo' is expected
-        # to be named 'foo' in the module 'foo' in one of the packages
-        # named here.  This list is searched from the left; an error
-        # is raised if no named package provides the command being
-        # searched for.  (Always access using get_command_packages().)
-        self.command_packages = None
-
         # 'script_name' and 'script_args' are usually set to sys.argv[0]
         # and sys.argv[1:], but they can be overridden when the caller is
         # not necessarily a setup script run from the command line.
@@ -388,11 +381,6 @@
                             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):
@@ -425,10 +413,12 @@
         # 1) know that it's a valid command, and 2) know which options
         # it takes.
         try:
-            cmd_class = self.get_command_class(command)
+            cmd_class = get_command_class(command)
         except DistutilsModuleError, msg:
             raise DistutilsArgError(msg)
 
+        # XXX We want to push this in distutils.command
+        #
         # Require that the command class be derived from Command -- want
         # to be sure that the basic "command" interface is implemented.
         for meth in ('initialize_options', 'finalize_options', 'run'):
@@ -545,7 +535,7 @@
             if isinstance(command, type) and issubclass(command, Command):
                 cls = command
             else:
-                cls = self.get_command_class(command)
+                cls = get_command_class(command)
             if (hasattr(cls, 'help_options') and
                 isinstance(cls.help_options, list)):
                 parser.set_option_table(cls.user_options +
@@ -606,7 +596,7 @@
         for cmd in commands:
             cls = self.cmdclass.get(cmd)
             if not cls:
-                cls = self.get_command_class(cmd)
+                cls = get_command_class(cmd)
             try:
                 description = cls.description
             except AttributeError:
@@ -648,94 +638,9 @@
                                     "Extra commands",
                                     max_length)
 
-    def get_command_list(self):
-        """Get a list of (command, description) tuples.
-
-        The list is divided into standard commands (listed in
-        distutils2.command.__all__) and extra commands (given in
-        self.cmdclass and not standard commands).  The descriptions come
-        from the command class attribute 'description'.
-        """
-        # Currently this is only used on Mac OS, for the Mac-only GUI
-        # Distutils interface (by Jack Jansen)
-
-        rv = []
-        for cls in self.get_command_classes():
-            try:
-                description = cls.description
-            except AttributeError:
-                description = "(no description available)"
-            rv.append((cls, description))
-        return rv
 
     # -- Command class/object methods ----------------------------------
 
-    def get_command_packages(self):
-        """Return a list of packages from which commands are loaded."""
-        pkgs = self.command_packages
-        if not isinstance(pkgs, list):
-            if pkgs is None:
-                pkgs = ''
-            pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != '']
-            if "distutils2.command" not in pkgs:
-                pkgs.insert(0, "distutils2.command")
-            self.command_packages = pkgs
-        return pkgs
-
-    def get_command_names(self):
-        """Return a list of all command names."""
-        return [getattr(cls, 'command_name', cls.__name__)
-                for cls in self.get_command_classes()]
-
-    def get_command_classes(self):
-        """Return a list of all command classes."""
-        std_commands, extra_commands = self._get_command_groups()
-        classes = []
-        for cmd in (std_commands + extra_commands):
-            try:
-                cls = self.cmdclass[cmd]
-            except KeyError:
-                cls = self.get_command_class(cmd)
-            classes.append(cls)
-        return classes
-
-    def get_command_class(self, command):
-        """Return the class that implements the Distutils command named by
-        'command'.  First we check the 'cmdclass' dictionary; if the
-        command is mentioned there, we fetch the class object from the
-        dictionary and return it.  Otherwise we load the command module
-        ("distutils.command." + command) and fetch the command class from
-        the module.  The loaded class is also stored in 'cmdclass'
-        to speed future calls to 'get_command_class()'.
-
-        Raises DistutilsModuleError if the expected module could not be
-        found, or if that module does not define the expected class.
-        """
-        cls = self.cmdclass.get(command)
-        if cls:
-            return cls
-
-        for pkgname in self.get_command_packages():
-            module_name = "%s.%s" % (pkgname, command)
-            class_name = command
-
-            try:
-                __import__(module_name)
-                module = sys.modules[module_name]
-            except ImportError:
-                continue
-
-            try:
-                cls = getattr(module, class_name)
-            except AttributeError:
-                raise DistutilsModuleError(
-                      "invalid command '%s' (no class '%s' in module '%s')" %
-                      (command, class_name, module_name))
-
-            self.cmdclass[command] = cls
-            return cls
-
-        raise DistutilsModuleError("invalid command '%s'" % command)
 
     def get_command_obj(self, command, create=1):
         """Return the command object for 'command'.  Normally this object
@@ -748,7 +653,7 @@
             logger.debug("Distribution.get_command_obj(): " \
                          "creating '%s' command object" % command)
 
-            cls = self.get_command_class(command)
+            cls = get_command_class(command)
             cmd_obj = self.command_obj[command] = cls(self)
             self.have_run[command] = 0
 
diff --git a/distutils2/tests/test_command_build.py b/distutils2/tests/test_command_build.py
--- a/distutils2/tests/test_command_build.py
+++ b/distutils2/tests/test_command_build.py
@@ -4,10 +4,7 @@
 
 from distutils2.command.build import build
 from distutils2.tests import unittest, support
-try:
-    from sysconfig import get_platform
-except ImportError:
-    from distutils2._backport.sysconfig import get_platform
+from distutils2._backport.sysconfig import get_platform
 
 class BuildTestCase(support.TempdirManager,
                     support.LoggingCatcher,
diff --git a/distutils2/tests/test_command_build_ext.py b/distutils2/tests/test_command_build_ext.py
--- a/distutils2/tests/test_command_build_ext.py
+++ b/distutils2/tests/test_command_build_ext.py
@@ -5,17 +5,13 @@
 
 import distutils2.tests
 from distutils2.tests import unittest
-from distutils2.extension import Extension
+from distutils2.compiler.extension import Extension
 from distutils2.dist import Distribution
 from distutils2.command.build_ext import build_ext
 from distutils2.tests import support
-from distutils2.extension import Extension
 from distutils2.errors import (UnknownFileError, DistutilsSetupError,
                                CompileError)
-try:
-    import sysconfig
-except ImportError:
-    from distutils2._backport import sysconfig
+from distutils2._backport import sysconfig
 
 
 # http://bugs.python.org/issue4373
diff --git a/distutils2/tests/test_command_install_lib.py b/distutils2/tests/test_command_install_lib.py
--- a/distutils2/tests/test_command_install_lib.py
+++ b/distutils2/tests/test_command_install_lib.py
@@ -3,7 +3,7 @@
 import os
 
 from distutils2.command.install_lib import install_lib
-from distutils2.extension import Extension
+from distutils2.compiler.extension import Extension
 from distutils2.tests import unittest, support
 from distutils2.errors import DistutilsOptionError
 
diff --git a/distutils2/tests/test_command_test.py b/distutils2/tests/test_command_test.py
--- a/distutils2/tests/test_command_test.py
+++ b/distutils2/tests/test_command_test.py
@@ -9,10 +9,11 @@
 from operator import getitem, setitem, delitem
 from StringIO import StringIO
 
-from distutils2.command.cmd import Command
+from distutils2.command.build import build
 from distutils2.tests import unittest
 from distutils2.tests.support import TempdirManager, LoggingCatcher
 from distutils2.command.test import test
+from distutils2.command import set_command
 from distutils2.dist import Distribution
 from distutils2._backport import pkgutil
 
@@ -31,6 +32,17 @@
 
 here = os.path.dirname(os.path.abspath(__file__))
 
+_RECORD = []
+
+class MockBuildCmd(build):
+    build_lib = "mock build lib"
+    command_name = 'build'
+    plat_name = 'whatever'
+    def initialize_options(self): pass
+    def finalize_options(self): pass
+    def run(self): _RECORD.append("build run")
+
+
 class TestTest(TempdirManager,
                LoggingCatcher,
                unittest.TestCase):
@@ -111,20 +123,17 @@
         self.assertEqual(record, ["suite", "run"])
 
     def test_builds_before_running_tests(self):
-        dist = Distribution()
-        cmd = test(dist)
-        cmd.runner = self.prepare_named_function(lambda: None)
-        record = []
-        class MockBuildCmd(Command):
-            build_lib = "mock build lib"
-            def initialize_options(self): pass
-            def finalize_options(self): pass
-            def run(self): record.append("build run")
-        dist.cmdclass['build'] = MockBuildCmd
-
-        cmd.ensure_finalized()
-        cmd.run()
-        self.assertEqual(record, ['build run'])
+        set_command('distutils2.tests.test_command_test.MockBuildCmd')
+        try:
+            dist = Distribution()
+            cmd = test(dist)
+            cmd.runner = self.prepare_named_function(lambda: None)
+            _RECORD[:] = []
+            cmd.ensure_finalized()
+            cmd.run()
+            self.assertEqual(_RECORD, ['build run'])
+        finally:
+            set_command('distutils2.command.build.build')
 
     def _test_works_with_2to3(self):
         pass
@@ -157,7 +166,6 @@
     def test_custom_runner(self):
         dist = Distribution()
         cmd = test(dist)
-
         record = []
         cmd.runner = self.prepare_named_function(lambda: record.append("runner called"))
         cmd.ensure_finalized()
@@ -189,12 +197,12 @@
     def test_calls_discover(self):
         self.safely_replace(ut1.TestLoader, "discover", delete=True)
         mock_ut2 = self.prepare_mock_ut2()
-        record = []
-        mock_ut2.TestLoader.discover = lambda self, path: record.append(path)
+        _RECORD[:] = []
+        mock_ut2.TestLoader.discover = lambda self, path: _RECORD.append(path)
         dist = Distribution()
         cmd = test(dist)
         cmd.run()
-        self.assertEqual(record, [os.curdir])
+        self.assertEqual(_RECORD, [os.curdir])
 
 def test_suite():
     return unittest.makeSuite(TestTest)
diff --git a/distutils2/tests/test_config.py b/distutils2/tests/test_config.py
--- a/distutils2/tests/test_config.py
+++ b/distutils2/tests/test_config.py
@@ -74,20 +74,22 @@
 
 [global]
 commands =
-    foo = distutils2.tests.test_config.Foo
+    distutils2.tests.test_config.Foo
+
+compilers =
+    distutils2.tests.test_config.DCompiler
 
 setup_hook = distutils2.tests.test_config.hook
 
+
+
 [install_dist]
 sub_commands = foo
-
-[compilers]
-d = distutils2.tests.test_config.DCompiler
 """
 
 
 class DCompiler(object):
-    compiler_type = 'd'
+    name = 'd'
     description = 'D Compiler'
 
     def __init__(self, *args):
@@ -102,12 +104,19 @@
     def __init__(self, dist):
         self.distribution = dist
 
+    @classmethod
+    def get_command_name(self):
+        return 'foo'
+
     def run(self):
         self.distribution.foo_was_here = 1
 
     def nothing(self):
         pass
 
+    def get_source_files(self):
+        return []
+
     ensure_finalized = finalize_options = initialize_options = nothing
 
 
@@ -184,7 +193,7 @@
         self.assertEqual(dist.package_dir['two'], 'src')
 
         # make sure we get the foo command loaded !
-        self.assertEquals(dist.cmdclass['foo'], Foo)
+        self.assertTrue(isinstance(dist.get_command_obj('foo'), Foo))
 
         # did the README got loaded ?
         self.assertEquals(dist.metadata['description'], 'yeah')
diff --git a/distutils2/tests/test_dist.py b/distutils2/tests/test_dist.py
--- a/distutils2/tests/test_dist.py
+++ b/distutils2/tests/test_dist.py
@@ -8,6 +8,7 @@
 
 import distutils2.dist
 from distutils2.dist import Distribution, fix_help_options
+from distutils2.command import set_command
 from distutils2.command.cmd import Command
 from distutils2.errors import DistutilsModuleError, DistutilsOptionError
 from distutils2.tests import TESTFN, captured_stdout
@@ -66,52 +67,6 @@
         finally:
             distutils2.dist.DEBUG = False
 
-    def test_command_packages_unspecified(self):
-        sys.argv.append("build")
-        d = create_distribution()
-        self.assertEqual(d.get_command_packages(), ["distutils2.command"])
-
-    def test_command_packages_cmdline(self):
-        from distutils2.tests.test_dist import test_dist
-        sys.argv.extend(["--command-packages",
-                         "foo.bar,distutils2.tests",
-                         "test_dist",
-                         "-Ssometext",
-                         ])
-        d = create_distribution()
-        # let's actually try to load our test command:
-        self.assertEqual(d.get_command_packages(),
-                         ["distutils2.command", "foo.bar", "distutils2.tests"])
-        cmd = d.get_command_obj("test_dist")
-        self.assertTrue(isinstance(cmd, test_dist))
-        self.assertEqual(cmd.sample_option, "sometext")
-
-    def test_command_packages_configfile(self):
-        sys.argv.append("build")
-        f = open(TESTFN, "w")
-        try:
-            print >> f, "[global]"
-            print >> f, "command_packages = foo.bar, splat"
-            f.close()
-            d = create_distribution([TESTFN])
-            self.assertEqual(d.get_command_packages(),
-                             ["distutils2.command", "foo.bar", "splat"])
-
-            # ensure command line overrides config:
-            sys.argv[1:] = ["--command-packages", "spork", "build"]
-            d = create_distribution([TESTFN])
-            self.assertEqual(d.get_command_packages(),
-                             ["distutils2.command", "spork"])
-
-            # Setting --command-packages to '' should cause the default to
-            # be used even if a config file specified something else:
-            sys.argv[1:] = ["--command-packages", "", "build"]
-            d = create_distribution([TESTFN])
-            self.assertEqual(d.get_command_packages(), ["distutils2.command"])
-
-        finally:
-            os.unlink(TESTFN)
-
     def test_write_pkg_file(self):
         # Check DistributionMetadata handling of Unicode fields
         tmp_dir = self.mkdtemp()
@@ -188,18 +143,6 @@
         self.assertEqual(dist.metadata['platform'], ['one', 'two'])
         self.assertEqual(dist.metadata['keywords'], ['one', 'two'])
 
-    def test_get_command_packages(self):
-        dist = Distribution()
-        self.assertEqual(dist.command_packages, None)
-        cmds = dist.get_command_packages()
-        self.assertEqual(cmds, ['distutils2.command'])
-        self.assertEqual(dist.command_packages,
-                         ['distutils2.command'])
-
-        dist.command_packages = 'one,two'
-        cmds = dist.get_command_packages()
-        self.assertEqual(cmds, ['distutils2.command', 'one', 'two'])
-
     def test_announce(self):
         # make sure the level is known
         dist = Distribution()
@@ -250,12 +193,9 @@
         self.write_file((temp_home, "config2.cfg"),
                         '[test_dist]\npre-hook.b = type')
 
-        sys.argv.extend(["--command-packages",
-                         "distutils2.tests",
-                         "test_dist"])
+        set_command('distutils2.tests.test_dist.test_dist')
         dist = create_distribution(config_files)
         cmd = dist.get_command_obj("test_dist")
-
         self.assertEqual(cmd.pre_hook, {"a": 'type', "b": 'type'})
 
     def test_hooks_get_run(self):
@@ -278,10 +218,7 @@
             record.append('post-%s' % cmd.get_command_name())
         '''))
 
-        sys.argv.extend(["--command-packages",
-                         "distutils2.tests",
-                         "test_dist"])
-
+        set_command('distutils2.tests.test_dist.test_dist')
         d = create_distribution([config_file])
         cmd = d.get_command_obj("test_dist")
 
@@ -329,10 +266,7 @@
             [test_dist]
             pre-hook.test = distutils2.tests.test_dist.__doc__'''))
 
-        sys.argv.extend(["--command-packages",
-                         "distutils2.tests",
-                         "test_dist"])
-
+        set_command('distutils2.tests.test_dist.test_dist')
         d = create_distribution([config_file])
         cmd = d.get_command_obj("test_dist")
         cmd.ensure_finalized()
diff --git a/distutils2/tests/test_extension.py b/distutils2/tests/test_extension.py
--- a/distutils2/tests/test_extension.py
+++ b/distutils2/tests/test_extension.py
@@ -1,7 +1,7 @@
 """Tests for distutils.extension."""
 import os
 
-from distutils2.extension import Extension
+from distutils2.compiler.extension import Extension
 from distutils2.tests import unittest
 
 class ExtensionTestCase(unittest.TestCase):
diff --git a/docs/source/setupcfg.rst b/docs/source/setupcfg.rst
new file mode 100644
--- /dev/null
+++ b/docs/source/setupcfg.rst
@@ -0,0 +1,161 @@
+==================
+The setup.cfg file
+==================
+
+This document describes the :file:`setup.cfg`, a ini-like file used by
+Distutils2 to replace the :file:`setup.py` file.
+
+Each section contains a description of its options.
+
+- Options that are marked *\*multi* can have multiple values, one value
+  per line.
+- Options that are marked *\*optional* can be omited.
+- Options that are marked *\*environ* can use environement markes, as described
+  in PEP 345.
+
+The sections are:
+
+- global
+- metadata
+- files
+- command sections
+
+
+global
+======
+
+Contains global options for Distutils2. This section is shared with Distutils1.
+
+- **commands**: Defined Distutils2 command. A command is defined by its fully
+  qualified name.
+
+  Examples::
+
+    [global]
+    commands =
+        package.sdist.CustomSdistCommand
+
+  *\*optional* *\*multi*
+
+- **compilers**: Defined Distutils2 compiler. A compiler is defined by its fully
+  qualified name. 
+
+  Example::
+
+    [global]
+    compiler =
+        package.compilers.CustomCCompiler
+
+  *\*optional* *\*multi*
+
+- **setup_hook**: defines a callable that will be called right after the
+  :file:`setup.cfg` file is read. The callable receives the configuration
+  in form of a mapping and can make some changes to it. *\*optional*
+
+
+metadata
+========
+
+The metadata section contains the metadata for the project as described in
+PEP 345.
+
+
+Fields:
+
+- **name**: Name of the project.
+- **version**: Version of the project. Must comply with PEP 386.
+- **platform**: Platform specification describing an operating system supported
+  by the distribution which is not listed in the "Operating System" Trove
+  classifiers. *\*multi* *\*optional*
+- **supported-platform**: Binary distributions containing a PKG-INFO file will
+  use the Supported-Platform field in their metadata to specify the OS and
+  CPU for which the binary distribution was compiled.  The semantics of
+  the Supported-Platform field are freeform. *\*multi* *\*optional*
+- **summary**: A one-line summary of what the distribution does.
+  (Used to be called *description* in Distutils1.)
+- **description**: A longer description. (Used to be called *long_description*
+  in Distutils1.) A file can be provided in the *description-file* field.
+  *\*optional*
+- **description-file**: path to a text file that will be used for the
+  **description** field. *\*optional*
+- **keywords**: A list of additional keywords to be used to assist searching
+  for the distribution in a larger catalog. Comma or space-separated. *\*optional*
+- **home-page**: The URL for the distribution's home page.
+- **download-url**: The URL from which this version of the distribution
+  can be downloaded. *\*optional*
+- **author**: Author's name. *\*optional*
+- **author-email**: Author's e-mail. *\*optional*
+- **maintainer**: Maintainer's name. *\*optional*
+- **maintainer-email**: Maintainer's e-mail. *\*optional*
+- **license**: A text indicating the term of uses, when a trove classifier does
+  not match. *\*optional*.
+- **classifiers**: Classification for the distribution, as described in PEP 301.
+  *\*optional* *\*multi* *\*environ*
+- **requires-dist**: name of another distutils project required as a dependency.
+  The format is *name (version)* where version is an optional
+  version declaration, as described in PEP 345. *\*optional* *\*multi* *\*environ*
+- **provides-dist**: name of another distutils project contained whithin this
+  distribution. Same format than *requires-dist*. *\*optional* *\*multi* *\*environ*
+- **obsoletes-dist**: name of another distutils project this version obsoletes.
+  Same format than *requires-dist*. *\*optional* *\*multi* *\*environ*
+- **requires-python**: Specifies the Python version the distribution requires.
+  The value is a version number, as described in PEP 345.
+  *\*optional* *\*multi* *\*environ*
+- **requires-externals**: a dependency in the system. This field is free-form,
+  and just a hint for downstream maintainers. *\*optional* *\*multi* *\*environ*
+- **project-url**: A label, followed by a browsable URL for the project.
+  "label, url". The label is limited to 32 signs. *\*optional* *\*multi*
+
+
+Example::
+
+    [metadata]
+    name = pypi2rpm
+    version = 0.1
+    author = Tarek Ziade
+    author_email = tarek at ziade.org
+    summary = Script that transforms a sdist archive into a rpm archive
+    description-file = README
+    home_page = http://bitbucket.org/tarek/pypi2rpm
+
+    classifier = Development Status :: 3 - Alpha
+        License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)
+
+
+
+files
+=====
+
+This section describes the files included in the project.
+
+- **packages**: a list of packages the project includes *\*optional* *\*multi*
+- **modules**: a list of packages the project includes *\*optional* *\*multi*
+- **scripts**: a list of scripts the project includes *\*optional* *\*multi*
+- **extra_files**: a list of patterns to include extra files *\*optional* *\*multi*
+
+Example::
+
+    [files]
+    packages =
+            pypi2rpm
+            pypi2rpm.command
+
+    scripts =
+            pypi2rpm/pypi2rpm.py
+
+    extra_files =
+            setup.py
+
+
+command sections
+================
+
+Each command can have its options described in :file:`setup.cfg`
+
+
+Example::
+
+    [sdist]
+    manifest_makers = package.module.Maker
+
+

--
Repository URL: http://hg.python.org/distutils2


More information about the Python-checkins mailing list