[Python-checkins] distutils2: now distutils2 uses set_command to set its own commands
tarek.ziade
python-checkins at python.org
Sat Nov 13 01:30:19 CET 2010
tarek.ziade pushed e44df92acbdf to distutils2:
http://hg.python.org/distutils2/rev/e44df92acbdf
changeset: 813:e44df92acbdf
tag: tip
user: Tarek Ziade <tarek at ziade.org>
date: Sat Nov 13 01:29:47 2010 +0100
summary: now distutils2 uses set_command to set its own commands
files: distutils2/command/__init__.py, distutils2/command/cmd.py, distutils2/command/sdist.py, distutils2/compiler/__init__.py, distutils2/config.py, distutils2/dist.py, distutils2/tests/test_command_test.py, distutils2/tests/test_config.py, distutils2/tests/test_dist.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/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
@@ -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 teh class here
+ _COMPILERS[klass.compiler_type] = 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/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_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,15 +74,17 @@
[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
"""
@@ -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()
--
Repository URL: http://hg.python.org/distutils2
More information about the Python-checkins
mailing list