[Python-checkins] distutils2: Updated with Tareks branch

tarek.ziade python-checkins at python.org
Thu Aug 19 08:34:13 CEST 2010


tarek.ziade pushed 2f24bdbbaf20 to distutils2:

http://hg.python.org/distutils2/rev/2f24bdbbaf20
changeset:   564:2f24bdbbaf20
parent:      563:aeaf5439d13c
parent:      375:fdc0f44dc5ec
user:        Zubin Mithra <zubin.mithra at gmail.com>
date:        Sat Jul 17 02:24:57 2010 +0530
summary:     Updated with Tareks branch
files:       src/distutils2/command/build_py.py, src/distutils2/tests/test_util.py

diff --git a/docs/source/metadata.rst b/docs/source/metadata.rst
--- a/docs/source/metadata.rst
+++ b/docs/source/metadata.rst
@@ -17,7 +17,7 @@
 Reading metadata
 ================
 
-The :class:`DistributionMetadata` class can be instanciated with the path of
+The :class:`DistributionMetadata` class can be instantiated with the path of
 the metadata file, and provides a dict-like interface to the values::
 
     >>> from distutils2.metadata import DistributionMetadata
@@ -32,14 +32,14 @@
     ["pywin32; sys.platform == 'win32'", "Sphinx"]
 
 The fields that supports environment markers can be automatically ignored if
-the object is instanciated using the ``platform_dependant`` option.
+the object is instantiated using the ``platform_dependent`` option.
 :class:`DistributionMetadata` will interpret in the case the markers and will
 automatically remove the fields that are not compliant with the running
 environment. Here's an example under Mac OS X. The win32 dependency
 we saw earlier is ignored::
 
     >>> from distutils2.metadata import DistributionMetadata
-    >>> metadata = DistributionMetadata('PKG-INFO', platform_dependant=True)
+    >>> metadata = DistributionMetadata('PKG-INFO', platform_dependent=True)
     >>> metadata['Requires-Dist']
     ['bar']
 
@@ -53,7 +53,7 @@
 
     >>> from distutils2.metadata import DistributionMetadata
     >>> context = {'sys.platform': 'win32'}
-    >>> metadata = DistributionMetadata('PKG-INFO', platform_dependant=True,
+    >>> metadata = DistributionMetadata('PKG-INFO', platform_dependent=True,
     ...                                 execution_context=context)
     ...
     >>> metadata['Requires-Dist'] = ["pywin32; sys.platform == 'win32'",
@@ -71,7 +71,7 @@
     >>> metadata.write('/to/my/PKG-INFO')
 
 The class will pick the best version for the metadata, depending on the values
-provided. If all the values provided exists in all versions, the class will
+provided. If all the values provided exist in all versions, the class will
 use :attr:`metadata.PKG_INFO_PREFERRED_VERSION`. It is set by default to 1.0.
 
 
@@ -79,7 +79,7 @@
 ==================================
 
 Some fields in :pep:`345` have to follow a version scheme in their versions
-predicate. When the scheme is violated, a warning is emited::
+predicate. When the scheme is violated, a warning is emitted::
 
     >>> from distutils2.metadata import DistributionMetadata
     >>> metadata = DistributionMetadata()
@@ -90,6 +90,3 @@
 
 
 .. TODO talk about check()
-
-
-
diff --git a/src/distutils2/_backport/tests/test_pkgutil.py b/src/distutils2/_backport/tests/test_pkgutil.py
--- a/src/distutils2/_backport/tests/test_pkgutil.py
+++ b/src/distutils2/_backport/tests/test_pkgutil.py
@@ -1,7 +1,5 @@
 # -*- coding: utf-8 -*-
 """Tests for PEP 376 pkgutil functionality"""
-import unittest2
-import unittest2.compatibility
 import sys
 import os
 import csv
@@ -15,22 +13,33 @@
     from md5 import md5
 
 from test.test_support import run_unittest, TESTFN
+from distutils2.tests.support import unittest
 
 from distutils2._backport import pkgutil
 
+try:
+    from os.path import relpath
+except ImportError:
+    try:
+        from unittest.compatibility import relpath
+    except ImportError:
+        from unittest2.compatibility import relpath
+
 # TODO Add a test for getting a distribution that is provided by another
-#   distribution.
+# distribution.
 
 # TODO Add a test for absolute pathed RECORD items (e.g. /etc/myapp/config.ini)
 
 # Adapted from Python 2.7's trunk
-class TestPkgUtilData(unittest2.TestCase):
+class TestPkgUtilData(unittest.TestCase):
 
     def setUp(self):
+        super(TestPkgUtilData, self).setUp()
         self.dirname = tempfile.mkdtemp()
         sys.path.insert(0, self.dirname)
 
     def tearDown(self):
+        super(TestPkgUtilData, self).tearDown()
         del sys.path[0]
         shutil.rmtree(self.dirname)
 
@@ -45,15 +54,22 @@
         os.mkdir(package_dir)
         # Empty init.py
         f = open(os.path.join(package_dir, '__init__.py'), "wb")
-        f.close()
+        try:
+            pass
+        finally:
+            f.close()
         # Resource files, res.txt, sub/res.txt
         f = open(os.path.join(package_dir, 'res.txt'), "wb")
-        f.write(RESOURCE_DATA)
-        f.close()
+        try:
+            f.write(RESOURCE_DATA)
+        finally:
+            f.close()
         os.mkdir(os.path.join(package_dir, 'sub'))
         f = open(os.path.join(package_dir, 'sub', 'res.txt'), "wb")
-        f.write(RESOURCE_DATA)
-        f.close()
+        try:
+            f.write(RESOURCE_DATA)
+        finally:
+            f.close()
 
         # Check we can read the resources
         res1 = pkgutil.get_data(pkg, 'res.txt')
@@ -73,13 +89,14 @@
         # Make a package with some resources
         zip_file = os.path.join(self.dirname, zip)
         z = zipfile.ZipFile(zip_file, 'w')
-
-        # Empty init.py
-        z.writestr(pkg + '/__init__.py', "")
-        # Resource files, res.txt, sub/res.txt
-        z.writestr(pkg + '/res.txt', RESOURCE_DATA)
-        z.writestr(pkg + '/sub/res.txt', RESOURCE_DATA)
-        z.close()
+        try:
+            # Empty init.py
+            z.writestr(pkg + '/__init__.py', "")
+            # Resource files, res.txt, sub/res.txt
+            z.writestr(pkg + '/res.txt', RESOURCE_DATA)
+            z.writestr(pkg + '/sub/res.txt', RESOURCE_DATA)
+        finally:
+            z.close()
 
         # Check we can read the resources
         sys.path.insert(0, zip_file)
@@ -92,7 +109,7 @@
         del sys.modules[pkg]
 
 # Adapted from Python 2.7's trunk
-class TestPkgUtilPEP302(unittest2.TestCase):
+class TestPkgUtilPEP302(unittest.TestCase):
 
     class MyTestLoader(object):
         def load_module(self, fullname):
@@ -114,10 +131,12 @@
             return TestPkgUtilPEP302.MyTestLoader()
 
     def setUp(self):
+        super(TestPkgUtilPEP302, self).setUp()
         sys.meta_path.insert(0, self.MyTestImporter())
 
     def tearDown(self):
         del sys.meta_path[0]
+        super(TestPkgUtilPEP302, self).tearDown()
 
     def test_getdata_pep302(self):
         # Use a dummy importer/loader
@@ -135,10 +154,11 @@
         del sys.modules['foo']
 
 
-class TestPkgUtilDistribution(unittest2.TestCase):
-    """Tests the pkgutil.Distribution class"""
+class TestPkgUtilDistribution(unittest.TestCase):
+    # Tests the pkgutil.Distribution class
 
     def setUp(self):
+        super(TestPkgUtilDistribution, self).setUp()
         self.fake_dists_path = os.path.abspath(
             os.path.join(os.path.dirname(__file__), 'fake_dists'))
 
@@ -152,7 +172,7 @@
             return md5_hash.hexdigest()
 
         def record_pieces(file):
-            path = os.path.relpath(file, sys.prefix)
+            path = relpath(file, sys.prefix)
             digest = get_hexdigest(file)
             size = os.path.getsize(file)
             return [path, digest, size]
@@ -172,7 +192,7 @@
             for file in ['INSTALLER', 'METADATA', 'REQUESTED']:
                 record_writer.writerow(record_pieces(
                     os.path.join(distinfo_dir, file)))
-            record_writer.writerow([os.path.relpath(record_file, sys.prefix)])
+            record_writer.writerow([relpath(record_file, sys.prefix)])
             del record_writer # causes the RECORD file to close
             record_reader = csv.reader(open(record_file, 'rb'))
             record_data = []
@@ -187,10 +207,11 @@
         for distinfo_dir in self.distinfo_dirs:
             record_file = os.path.join(distinfo_dir, 'RECORD')
             open(record_file, 'w').close()
+        super(TestPkgUtilDistribution, self).tearDown()
 
     def test_instantiation(self):
-        """Test the Distribution class's instantiation provides us with usable
-        attributes."""
+        # Test the Distribution class's instantiation provides us with usable
+        # attributes.
         # Import the Distribution class
         from distutils2._backport.pkgutil import distinfo_dirname, Distribution
 
@@ -208,7 +229,7 @@
         self.assertTrue(isinstance(dist.requested, type(bool())))
 
     def test_installed_files(self):
-        """Test the iteration of installed files."""
+        # Test the iteration of installed files.
         # Test the distribution's installed files
         from distutils2._backport.pkgutil import Distribution
         for distinfo_dir in self.distinfo_dirs:
@@ -220,17 +241,17 @@
                 self.assertEqual(size, record_data[path][1])
 
     def test_uses(self):
-        """Test to determine if a distribution uses a specified file."""
+        # Test to determine if a distribution uses a specified file.
         # Criteria to test against
         distinfo_name = 'grammar-1.0a4'
         distinfo_dir = os.path.join(self.fake_dists_path,
             distinfo_name + '.dist-info')
         true_path = [self.fake_dists_path, distinfo_name, \
                      'grammar', 'utils.py']
-        true_path = os.path.relpath(os.path.join(*true_path), sys.prefix)
+        true_path = relpath(os.path.join(*true_path), sys.prefix)
         false_path = [self.fake_dists_path, 'towel_stuff-0.1', 'towel_stuff',
             '__init__.py']
-        false_path = os.path.relpath(os.path.join(*false_path), sys.prefix)
+        false_path = relpath(os.path.join(*false_path), sys.prefix)
 
         # Test if the distribution uses the file in question
         from distutils2._backport.pkgutil import Distribution
@@ -239,7 +260,7 @@
         self.assertFalse(dist.uses(false_path))
 
     def test_get_distinfo_file(self):
-        """Test the retrieval of dist-info file objects."""
+        # Test the retrieval of dist-info file objects.
         from distutils2._backport.pkgutil import Distribution
         distinfo_name = 'choxie-2.0.0.9'
         other_distinfo_name = 'grammar-1.0a4'
@@ -272,7 +293,7 @@
                           'ENTRYPOINTS')
 
     def test_get_distinfo_files(self):
-        """Test for the iteration of RECORD path entries."""
+        # Test for the iteration of RECORD path entries.
         from distutils2._backport.pkgutil import Distribution
         distinfo_name = 'towel_stuff-0.1'
         distinfo_dir = os.path.join(self.fake_dists_path,
@@ -289,10 +310,11 @@
         self.assertEqual(sorted(found), sorted(distinfo_record_paths))
 
 
-class TestPkgUtilPEP376(unittest2.TestCase):
-    """Tests for the new functionality added in PEP 376."""
+class TestPkgUtilPEP376(unittest.TestCase):
+    # Tests for the new functionality added in PEP 376.
 
     def setUp(self):
+        super(TestPkgUtilPEP376, self).setUp()
         # Setup the path environment with our fake distributions
         current_path = os.path.abspath(os.path.dirname(__file__))
         self.sys_path = sys.path[:]
@@ -301,17 +323,18 @@
 
     def tearDown(self):
         sys.path[:] = self.sys_path
+        super(TestPkgUtilPEP376, self).tearDown()
 
     def test_distinfo_dirname(self):
-        """Given a name and a version, we expect the distinfo_dirname function
-        to return a standard distribution information directory name."""
+        # Given a name and a version, we expect the distinfo_dirname function
+        # to return a standard distribution information directory name.
 
         items = [# (name, version, standard_dirname)
             # Test for a very simple single word name and decimal
             # version number
             ('docutils', '0.5', 'docutils-0.5.dist-info'),
             # Test for another except this time with a '-' in the name, which
-            #   needs to be transformed during the name lookup
+            # needs to be transformed during the name lookup
             ('python-ldap', '2.5', 'python_ldap-2.5.dist-info'),
             # Test for both '-' in the name and a funky version number
             ('python-ldap', '2.5 a---5', 'python_ldap-2.5 a---5.dist-info'),
@@ -326,7 +349,7 @@
             self.assertEqual(dirname, standard_dirname)
 
     def test_get_distributions(self):
-        """Lookup all distributions found in the ``sys.path``."""
+        # Lookup all distributions found in the ``sys.path``.
         # This test could potentially pick up other installed distributions
         fake_dists = [('grammar', '1.0a4'), ('choxie', '2.0.0.9'),
             ('towel-stuff', '0.1')]
@@ -367,7 +390,7 @@
         self.assertListEqual(sorted(fake_dists), sorted(found_dists))
 
     def test_get_distribution(self):
-        """Test for looking up a distribution by name."""
+        # Test for looking up a distribution by name.
         # Test the lookup of the towel-stuff distribution
         name = 'towel-stuff' # Note: This is different from the directory name
 
@@ -413,7 +436,7 @@
         self.assertEqual(dist.name, 'strawberry')
 
     def test_get_file_users(self):
-        """Test the iteration of distributions that use a file."""
+        # Test the iteration of distributions that use a file.
         from distutils2._backport.pkgutil import get_file_users, Distribution
         name = 'towel_stuff-0.1'
         path = os.path.join(self.fake_dists_path, name,
@@ -423,7 +446,7 @@
             self.assertEqual(dist.name, name)
 
     def test_provides(self):
-        """ Test for looking up distributions by what they provide """
+        # Test for looking up distributions by what they provide
         from distutils2._backport.pkgutil import provides_distribution
         from distutils2.errors import DistutilsError
 
@@ -495,7 +518,7 @@
         checkLists(l, [])
 
     def test_obsoletes(self):
-        """ Test looking for distributions based on what they obsolete """
+        # Test looking for distributions based on what they obsolete
         from distutils2._backport.pkgutil import obsoletes_distribution
         from distutils2.errors import DistutilsError
 
@@ -528,12 +551,12 @@
 
 
 def test_suite():
-    suite = unittest2.TestSuite()
-    testcase_loader = unittest2.loader.defaultTestLoader.loadTestsFromTestCase
-    suite.addTest(testcase_loader(TestPkgUtilData))
-    suite.addTest(testcase_loader(TestPkgUtilDistribution))
-    suite.addTest(testcase_loader(TestPkgUtilPEP302))
-    suite.addTest(testcase_loader(TestPkgUtilPEP376))
+    suite = unittest.TestSuite()
+    load = unittest.defaultTestLoader.loadTestsFromTestCase
+    suite.addTest(load(TestPkgUtilData))
+    suite.addTest(load(TestPkgUtilDistribution))
+    suite.addTest(load(TestPkgUtilPEP302))
+    suite.addTest(load(TestPkgUtilPEP376))
     return suite
 
 
diff --git a/src/distutils2/_backport/tests/test_sysconfig.py b/src/distutils2/_backport/tests/test_sysconfig.py
--- a/src/distutils2/_backport/tests/test_sysconfig.py
+++ b/src/distutils2/_backport/tests/test_sysconfig.py
@@ -88,15 +88,13 @@
             shutil.rmtree(path)
 
     def test_nested_var_substitution(self):
-        """Assert that the {curly brace token} expansion pattern will replace
-        only the inner {something} on nested expressions like {py{something}} on
-        the first pass.
+        # Assert that the {curly brace token} expansion pattern will replace
+        # only the inner {something} on nested expressions like {py{something}} on
+        # the first pass.
 
-        We have no plans to make use of this, but it keeps the option open for
-        the future, at the cost only of disallowing { itself as a piece of a
-        substitution key (which would be weird).
-
-        """
+        # We have no plans to make use of this, but it keeps the option open for
+        # the future, at the cost only of disallowing { itself as a piece of a
+        # substitution key (which would be weird).
         self.assertEqual(_subst_vars('{py{version}}', {'version': '31'}), '{py31}')
 
     def test_get_paths(self):
diff --git a/src/distutils2/command/build_ext.py b/src/distutils2/command/build_ext.py
--- a/src/distutils2/command/build_ext.py
+++ b/src/distutils2/command/build_ext.py
@@ -188,7 +188,24 @@
         if self.package is None:
             self.package = self.distribution.ext_package
 
+        # Ensure that the list of extensions is valid, i.e. it is a list of
+        # Extension objects.
         self.extensions = self.distribution.ext_modules
+        if self.extensions:
+            if not isinstance(self.extensions, (list, tuple)):
+                type_name = (self.extensions is None and 'None'
+                            or type(self.extensions).__name__)
+                raise DistutilsSetupError(
+                    "'ext_modules' must be a sequence of Extension instances,"
+                    " not %s" % (type_name,))
+            for i, ext in enumerate(self.extensions):
+                if isinstance(ext, Extension):
+                    continue                # OK! (assume type-checking done
+                                            # by Extension constructor)
+                type_name = (ext is None and 'None' or type(ext).__name__)
+                raise DistutilsSetupError(
+                    "'ext_modules' item %d must be an Extension instance,"
+                    " not %s" % (i, type_name))
 
         # Make sure Python's include directories (for Python.h, pyconfig.h,
         # etc.) are in the include search path.
@@ -396,86 +413,7 @@
         # Now actually compile and link everything.
         self.build_extensions()
 
-    def check_extensions_list(self, extensions):
-        """Ensure that the list of extensions (presumably provided as a
-        command option 'extensions') is valid, i.e. it is a list of
-        Extension objects.  We also support the old-style list of 2-tuples,
-        where the tuples are (ext_name, build_info), which are converted to
-        Extension instances here.
-
-        Raise DistutilsSetupError if the structure is invalid anywhere;
-        just returns otherwise.
-        """
-        if not isinstance(extensions, list):
-            raise DistutilsSetupError, \
-                  "'ext_modules' option must be a list of Extension instances"
-
-        for i, ext in enumerate(extensions):
-            if isinstance(ext, Extension):
-                continue                # OK! (assume type-checking done
-                                        # by Extension constructor)
-
-            if not isinstance(ext, tuple) or len(ext) != 2:
-                raise DistutilsSetupError, \
-                      ("each element of 'ext_modules' option must be an "
-                       "Extension instance or 2-tuple")
-
-            ext_name, build_info = ext
-
-            log.warn(("old-style (ext_name, build_info) tuple found in "
-                      "ext_modules for extension '%s'"
-                      "-- please convert to Extension instance" % ext_name))
-
-            if not (isinstance(ext_name, str) and
-                    extension_name_re.match(ext_name)):
-                raise DistutilsSetupError, \
-                      ("first element of each tuple in 'ext_modules' "
-                       "must be the extension name (a string)")
-
-            if not isinstance(build_info, dict):
-                raise DistutilsSetupError, \
-                      ("second element of each tuple in 'ext_modules' "
-                       "must be a dictionary (build info)")
-
-            # OK, the (ext_name, build_info) dict is type-safe: convert it
-            # to an Extension instance.
-            ext = Extension(ext_name, build_info['sources'])
-
-            # Easy stuff: one-to-one mapping from dict elements to
-            # instance attributes.
-            for key in ('include_dirs', 'library_dirs', 'libraries',
-                        'extra_objects', 'extra_compile_args',
-                        'extra_link_args'):
-                val = build_info.get(key)
-                if val is not None:
-                    setattr(ext, key, val)
-
-            # Medium-easy stuff: same syntax/semantics, different names.
-            ext.runtime_library_dirs = build_info.get('rpath')
-            if 'def_file' in build_info:
-                log.warn("'def_file' element of build info dict "
-                         "no longer supported")
-
-            # Non-trivial stuff: 'macros' split into 'define_macros'
-            # and 'undef_macros'.
-            macros = build_info.get('macros')
-            if macros:
-                ext.define_macros = []
-                ext.undef_macros = []
-                for macro in macros:
-                    if not (isinstance(macro, tuple) and len(macro) in (1, 2)):
-                        raise DistutilsSetupError, \
-                              ("'macros' element of build info dict "
-                               "must be 1- or 2-tuple")
-                    if len(macro) == 1:
-                        ext.undef_macros.append(macro[0])
-                    elif len(macro) == 2:
-                        ext.define_macros.append(macro)
-
-            extensions[i] = ext
-
     def get_source_files(self):
-        self.check_extensions_list(self.extensions)
         filenames = []
 
         # Wouldn't it be neat if we knew the names of header files too...
@@ -485,11 +423,6 @@
         return filenames
 
     def get_outputs(self):
-        # Sanity check the 'extensions' list -- can't assume this is being
-        # done in the same run as a 'build_extensions()' call (in fact, we
-        # can probably assume that it *isn't*!).
-        self.check_extensions_list(self.extensions)
-
         # And build the list of output (built) filenames.  Note that this
         # ignores the 'inplace' flag, and assumes everything goes in the
         # "build" tree.
@@ -499,9 +432,6 @@
         return outputs
 
     def build_extensions(self):
-        # First, sanity-check the 'extensions' list
-        self.check_extensions_list(self.extensions)
-
         for ext in self.extensions:
             try:
                 self.build_extension(ext)
diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py
--- a/src/distutils2/command/build_py.py
+++ b/src/distutils2/command/build_py.py
@@ -156,8 +156,13 @@
 
         self.byte_compile(self.get_outputs(include_bytecode=0))
 
+    # -- Top-level worker functions ------------------------------------
+
     def get_data_files(self):
-        """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
+        """Generate list of '(package,src_dir,build_dir,filenames)' tuples.
+
+        Helper function for `finalize_options()`.
+        """
         data = []
         if not self.packages:
             return data
@@ -181,7 +186,10 @@
         return data
 
     def find_data_files(self, package, src_dir):
-        """Return filenames for package's data files in 'src_dir'"""
+        """Return filenames for package's data files in 'src_dir'.
+
+        Helper function for `get_data_files()`.
+        """
         globs = (self.package_data.get('', [])
                  + self.package_data.get(package, []))
         files = []
@@ -193,7 +201,10 @@
         return files
 
     def build_package_data(self):
-        """Copy data files into build directory"""
+        """Copy data files into build directory.
+
+        Helper function for `run()`.
+        """
         for package, src_dir, build_dir, filenames in self.data_files:
             for filename in filenames:
                 target = os.path.join(build_dir, filename)
@@ -203,6 +214,8 @@
                 if copied and srcfile in self.distribution.convert_2to3.doctests:
                     self._doctests_2to3.append(outf)
 
+    # XXX - this should be moved to the Distribution class as it is not
+    # only needed for build_py. It also has no dependencies on this class.
     def get_package_dir(self, package):
         """Return the directory, relative to the top of the source
            distribution, where package 'package' should be found
@@ -244,6 +257,8 @@
                     return ''
 
     def check_package(self, package, package_dir):
+        """Helper function for `find_package_modules()` and `find_modules()'.
+        """
         # Empty dir name means current directory, which we can probably
         # assume exists.  Also, os.path.exists and isdir don't know about
         # my "empty string means current dir" convention, so we have to
diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py
--- a/src/distutils2/command/cmd.py
+++ b/src/distutils2/command/cmd.py
@@ -373,8 +373,10 @@
         if os.path.isdir(name) or name == '':
             return
         if dry_run:
+            head = ''
             for part in name.split(os.sep):
-                self.log(part)
+                log.info("created directory %s%s", head, part)
+                head += part + os.sep
             return
         os.makedirs(name, mode)
 
diff --git a/src/distutils2/command/register.py b/src/distutils2/command/register.py
--- a/src/distutils2/command/register.py
+++ b/src/distutils2/command/register.py
@@ -28,8 +28,6 @@
     boolean_options = PyPIRCCommand.boolean_options + [
         'verify', 'list-classifiers', 'strict']
 
-    sub_commands = [('check', lambda self: True)]
-
     def initialize_options(self):
         PyPIRCCommand.initialize_options(self)
         self.list_classifiers = 0
@@ -46,9 +44,8 @@
         self.finalize_options()
         self._set_config()
 
-        # Run sub commands
-        for cmd_name in self.get_sub_commands():
-            self.run_command(cmd_name)
+        # Check the package metadata
+        self.run_command('check')
 
         if self.dry_run:
             self.verify_metadata()
diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py
--- a/src/distutils2/command/sdist.py
+++ b/src/distutils2/command/sdist.py
@@ -45,12 +45,6 @@
 
     description = "create a source distribution (tarball, zip file, etc.)"
 
-    def checking_metadata(self):
-        """Callable used for the check sub-command.
-
-        Placed here so user_options can view it"""
-        return self.metadata_check
-
     user_options = [
         ('template=', 't',
          "name of manifest template file [default: MANIFEST.in]"),
@@ -100,8 +94,6 @@
     default_format = {'posix': 'gztar',
                       'nt': 'zip' }
 
-    sub_commands = [('check', checking_metadata)]
-
     def initialize_options(self):
         # 'template' and 'manifest' are, respectively, the names of
         # the manifest template and manifest file.
@@ -162,9 +154,9 @@
         # manifest
         self.filelist.clear()
 
-        # Run sub commands
-        for cmd_name in self.get_sub_commands():
-            self.run_command(cmd_name)
+        # Check the package metadata
+        if self.metadata_check:
+            self.run_command('check')
 
         # Do whatever it takes to get the list of files to process
         # (process the manifest template, read an existing manifest,
diff --git a/src/distutils2/compiler/ccompiler.py b/src/distutils2/compiler/ccompiler.py
--- a/src/distutils2/compiler/ccompiler.py
+++ b/src/distutils2/compiler/ccompiler.py
@@ -940,8 +940,10 @@
         if os.path.isdir(name) or name == '':
             return
         if self.dry_run:
+            head = ''
             for part in name.split(os.sep):
-                self.log(part)
+                log.info("created directory %s%s", head, part)
+                head += part + os.sep
             return
         os.makedirs(name, mode)
 
diff --git a/src/distutils2/converter/fixers/fix_imports.py b/src/distutils2/converter/fixers/fix_imports.py
--- a/src/distutils2/converter/fixers/fix_imports.py
+++ b/src/distutils2/converter/fixers/fix_imports.py
@@ -36,11 +36,16 @@
             pattern = []
             next = imp.next_sibling
             while next is not None:
+                # Get the first child if we have a Node
+                if not hasattr(next, "value"):
+                    next = next.children[0]
                 pattern.append(next.value)
                 if not hasattr(next, "next_sibling"):
                     next.next_sibling = next.get_next_sibling()
                 next = next.next_sibling
-            if pattern == ['import', 'setup']:
+            
+            if set(pattern).issubset(set(
+                    ['import', ',', 'setup', 'find_packages'])):
                 imp.value = 'distutils2.core'
                 imp.changed()
 
diff --git a/src/distutils2/pypi/dist.py b/src/distutils2/pypi/dist.py
--- a/src/distutils2/pypi/dist.py
+++ b/src/distutils2/pypi/dist.py
@@ -60,7 +60,8 @@
         name, version = split_archive_name(archive_name)
         if extension_matched is True:
             return PyPIDistribution(name, version, url=url, url_hashname="md5",
-                                    url_hashval=md5_hash, url_is_external=is_external)
+                                    url_hashval=md5_hash,
+                                    url_is_external=is_external)
 
     def __init__(self, name, version, type=None, url=None, url_hashname=None,
                  url_hashval=None, url_is_external=True):
@@ -260,24 +261,23 @@
         else:
             super(PyPIDistributions, self).append(o)
 
-    def sort_distributions(self, prefer_source=None, prefer_final=None,
+    def sort_distributions(self, prefer_source=True, prefer_final=False,
                            reverse=True, *args, **kwargs):
         """order the results with the given properties"""
 
         sort_by = []
-        if prefer_final is not None:
-            if prefer_final is True:
-                sort_by.append("is_final")
+        if prefer_final:
+            sort_by.append("is_final")
         sort_by.append("version")
 
-        if prefer_source is not None:
-            if prefer_source is True:
-                sort_by.append("is_source")
+        if prefer_source:
+            sort_by.append("is_source")
 
         super(PyPIDistributions, self).sort(
             key=lambda i: [getattr(i, arg) for arg in sort_by],
             reverse=reverse, *args, **kwargs)
 
+
 def split_archive_name(archive_name, probable_name=None):
     """Split an archive name into two parts: name and version.
 
diff --git a/src/distutils2/pypi/simple.py b/src/distutils2/pypi/simple.py
--- a/src/distutils2/pypi/simple.py
+++ b/src/distutils2/pypi/simple.py
@@ -5,18 +5,18 @@
 avalaible at http://pypi.python.org/simple/
 """
 from fnmatch import translate
+import httplib
+import re
+import socket
+import sys
+import urllib2
 import urlparse
-import sys
-import re
-import urllib2
-import httplib
-import socket
 
 from distutils2.version import VersionPredicate
-from distutils2.pypi.dist import PyPIDistribution, PyPIDistributions, \
-    EXTENSIONS
-from distutils2.pypi.errors import PyPIError, DistributionNotFound, \
-    DownloadError, UnableToDownload
+from distutils2.pypi.dist import (PyPIDistribution, PyPIDistributions,
+                                  EXTENSIONS)
+from distutils2.pypi.errors import (PyPIError, DistributionNotFound,
+                                    DownloadError, UnableToDownload)
 from distutils2 import __version__ as __distutils2_version__
 
 # -- Constants -----------------------------------------------
@@ -60,33 +60,31 @@
 
 class SimpleIndex(object):
     """Provides useful tools to request the Python Package Index simple API
+
+    :param index_url: the url of the simple index to search on.
+    :param follow_externals: tell if following external links is needed or
+                             not. Default is False.
+    :param hosts: a list of hosts allowed to be processed while using
+                  follow_externals=True. Default behavior is to follow all
+                  hosts.
+    :param follow_externals: tell if following external links is needed or
+                             not. Default is False.
+    :param prefer_source: if there is binary and source distributions, the
+                          source prevails.
+    :param prefer_final: if the version is not mentioned, and the last
+                         version is not a "final" one (alpha, beta, etc.),
+                         pick up the last final version.
+    :param mirrors_url: the url to look on for DNS records giving mirror
+                        adresses.
+    :param mirrors: a list of mirrors to check out if problems
+                         occurs while working with the one given in "url"
+    :param timeout: time in seconds to consider a url has timeouted.
     """
 
     def __init__(self, index_url=PYPI_DEFAULT_INDEX_URL, hosts=DEFAULT_HOSTS,
                  follow_externals=False, prefer_source=True,
                  prefer_final=False, mirrors_url=PYPI_DEFAULT_MIRROR_URL,
                  mirrors=None, timeout=SOCKET_TIMEOUT):
-        """Class constructor.
-
-        :param index_url: the url of the simple index to search on.
-        :param follow_externals: tell if following external links is needed or
-                                 not. Default is False.
-        :param hosts: a list of hosts allowed to be processed while using
-                      follow_externals=True. Default behavior is to follow all
-                      hosts.
-        :param follow_externals: tell if following external links is needed or
-                                 not. Default is False.
-        :param prefer_source: if there is binary and source distributions, the
-                              source prevails.
-        :param prefer_final: if the version is not mentioned, and the last
-                             version is not a "final" one (alpha, beta, etc.),
-                             pick up the last final version.
-        :param mirrors_url: the url to look on for DNS records giving mirror
-                            adresses.
-        :param mirrors: a list of mirrors to check out if problems
-                             occurs while working with the one given in "url"
-        :param timeout: time in seconds to consider a url has timeouted.
-        """
         self.follow_externals = follow_externals
 
         if not index_url.endswith("/"):
@@ -213,7 +211,7 @@
         # local files are always considered browable resources
         if self.index_url in url or urlparse.urlparse(url)[0] == "file":
             return True
-        elif self.follow_externals is True:
+        elif self.follow_externals:
             if self._allowed_hosts(urlparse.urlparse(url)[1]):  # 1 is netloc
                 return True
             else:
diff --git a/src/distutils2/tests/conversions/05_after.py b/src/distutils2/tests/conversions/05_after.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/conversions/05_after.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2003-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
+from distutils2.core import setup, find_packages
+
+extra = {}
+
+try:
+    import babel
+    
+    extractors = [
+        ('**.py',                'python', None),
+        ('**/templates/**.html', 'genshi', None),
+        ('**/templates/**.txt',  'genshi',
+         {'template_class': 'genshi.template:NewTextTemplate'}),
+    ]
+    extra['message_extractors'] = {
+        'trac': extractors,
+        'tracopt': extractors,
+    }
+
+    from trac.util.dist import get_l10n_js_cmdclass
+    extra['cmdclass'] = get_l10n_js_cmdclass()
+
+except ImportError, e:
+    pass
+
+setup(
+    name = 'Trac',
+    version = '0.12.1',
+    summary = 'Integrated SCM, wiki, issue tracker and project environment',
+    description = """
+Trac is a minimalistic web-based software project management and bug/issue
+tracking system. It provides an interface to the Subversion revision control
+systems, an integrated wiki, flexible issue tracking and convenient report
+facilities.
+""",
+    author = 'Edgewall Software',
+    author_email = 'info at edgewall.com',
+    license = 'BSD',
+    home_page = 'http://trac.edgewall.org/',
+    download_url = 'http://trac.edgewall.org/wiki/TracDownload',
+    classifiers = [
+        'Environment :: Web Environment',
+        'Framework :: Trac',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Software Development :: Bug Tracking',
+        'Topic :: Software Development :: Version Control',
+    ],
+
+    packages = find_packages(exclude=['*.tests']),
+    package_data = {
+        '': ['templates/*'],
+        'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*',
+                 'htdocs/js/messages/*.*', 'htdocs/css/*.*',
+                 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'],
+        'trac.wiki': ['default-pages/*'],
+        'trac.ticket': ['workflows/*.ini'],
+    },
+
+    test_suite = 'trac.test.suite',
+    zip_safe = True,
+
+    requires_dist = [
+        'setuptools>=0.6b1',
+        'Genshi>=0.6',
+    ],
+    extras_require = {
+        'Babel': ['Babel>=0.9.5'],
+        'Pygments': ['Pygments>=0.6'],
+        'reST': ['docutils>=0.3'],
+        'SilverCity': ['SilverCity>=0.9.4'],
+        'Textile': ['textile>=2.0'],
+    },
+
+    entry_points = """
+        [console_scripts]
+        trac-admin = trac.admin.console:run
+        tracd = trac.web.standalone:main
+
+        [trac.plugins]
+        trac.about = trac.about
+        trac.admin.console = trac.admin.console
+        trac.admin.web_ui = trac.admin.web_ui
+        trac.attachment = trac.attachment
+        trac.db.mysql = trac.db.mysql_backend
+        trac.db.postgres = trac.db.postgres_backend
+        trac.db.sqlite = trac.db.sqlite_backend
+        trac.mimeview.patch = trac.mimeview.patch
+        trac.mimeview.pygments = trac.mimeview.pygments[Pygments]
+        trac.mimeview.rst = trac.mimeview.rst[reST]
+        trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity]
+        trac.mimeview.txtl = trac.mimeview.txtl[Textile]
+        trac.prefs = trac.prefs.web_ui
+        trac.search = trac.search.web_ui
+        trac.ticket.admin = trac.ticket.admin
+        trac.ticket.query = trac.ticket.query
+        trac.ticket.report = trac.ticket.report
+        trac.ticket.roadmap = trac.ticket.roadmap
+        trac.ticket.web_ui = trac.ticket.web_ui
+        trac.timeline = trac.timeline.web_ui
+        trac.versioncontrol.admin = trac.versioncontrol.admin
+        trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz
+        trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs
+        trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop
+        trac.versioncontrol.web_ui = trac.versioncontrol.web_ui
+        trac.web.auth = trac.web.auth
+        trac.web.session = trac.web.session
+        trac.wiki.admin = trac.wiki.admin
+        trac.wiki.interwiki = trac.wiki.interwiki
+        trac.wiki.macros = trac.wiki.macros
+        trac.wiki.web_ui = trac.wiki.web_ui
+        trac.wiki.web_api = trac.wiki.web_api
+        tracopt.mimeview.enscript = tracopt.mimeview.enscript
+        tracopt.mimeview.php = tracopt.mimeview.php
+        tracopt.perm.authz_policy = tracopt.perm.authz_policy
+        tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider
+        tracopt.ticket.commit_updater = tracopt.ticket.commit_updater
+        tracopt.ticket.deleter = tracopt.ticket.deleter
+    """,
+
+    **extra
+)
diff --git a/src/distutils2/tests/conversions/05_before.py b/src/distutils2/tests/conversions/05_before.py
new file mode 100755
--- /dev/null
+++ b/src/distutils2/tests/conversions/05_before.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2003-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://trac.edgewall.org/wiki/TracLicense.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://trac.edgewall.org/log/.
+
+from setuptools import setup, find_packages
+
+extra = {}
+
+try:
+    import babel
+    
+    extractors = [
+        ('**.py',                'python', None),
+        ('**/templates/**.html', 'genshi', None),
+        ('**/templates/**.txt',  'genshi',
+         {'template_class': 'genshi.template:NewTextTemplate'}),
+    ]
+    extra['message_extractors'] = {
+        'trac': extractors,
+        'tracopt': extractors,
+    }
+
+    from trac.util.dist import get_l10n_js_cmdclass
+    extra['cmdclass'] = get_l10n_js_cmdclass()
+
+except ImportError, e:
+    pass
+
+setup(
+    name = 'Trac',
+    version = '0.12.1',
+    description = 'Integrated SCM, wiki, issue tracker and project environment',
+    long_description = """
+Trac is a minimalistic web-based software project management and bug/issue
+tracking system. It provides an interface to the Subversion revision control
+systems, an integrated wiki, flexible issue tracking and convenient report
+facilities.
+""",
+    author = 'Edgewall Software',
+    author_email = 'info at edgewall.com',
+    license = 'BSD',
+    url = 'http://trac.edgewall.org/',
+    download_url = 'http://trac.edgewall.org/wiki/TracDownload',
+    classifiers = [
+        'Environment :: Web Environment',
+        'Framework :: Trac',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Software Development :: Bug Tracking',
+        'Topic :: Software Development :: Version Control',
+    ],
+
+    packages = find_packages(exclude=['*.tests']),
+    package_data = {
+        '': ['templates/*'],
+        'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*',
+                 'htdocs/js/messages/*.*', 'htdocs/css/*.*',
+                 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'],
+        'trac.wiki': ['default-pages/*'],
+        'trac.ticket': ['workflows/*.ini'],
+    },
+
+    test_suite = 'trac.test.suite',
+    zip_safe = True,
+
+    install_requires = [
+        'setuptools>=0.6b1',
+        'Genshi>=0.6',
+    ],
+    extras_require = {
+        'Babel': ['Babel>=0.9.5'],
+        'Pygments': ['Pygments>=0.6'],
+        'reST': ['docutils>=0.3'],
+        'SilverCity': ['SilverCity>=0.9.4'],
+        'Textile': ['textile>=2.0'],
+    },
+
+    entry_points = """
+        [console_scripts]
+        trac-admin = trac.admin.console:run
+        tracd = trac.web.standalone:main
+
+        [trac.plugins]
+        trac.about = trac.about
+        trac.admin.console = trac.admin.console
+        trac.admin.web_ui = trac.admin.web_ui
+        trac.attachment = trac.attachment
+        trac.db.mysql = trac.db.mysql_backend
+        trac.db.postgres = trac.db.postgres_backend
+        trac.db.sqlite = trac.db.sqlite_backend
+        trac.mimeview.patch = trac.mimeview.patch
+        trac.mimeview.pygments = trac.mimeview.pygments[Pygments]
+        trac.mimeview.rst = trac.mimeview.rst[reST]
+        trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity]
+        trac.mimeview.txtl = trac.mimeview.txtl[Textile]
+        trac.prefs = trac.prefs.web_ui
+        trac.search = trac.search.web_ui
+        trac.ticket.admin = trac.ticket.admin
+        trac.ticket.query = trac.ticket.query
+        trac.ticket.report = trac.ticket.report
+        trac.ticket.roadmap = trac.ticket.roadmap
+        trac.ticket.web_ui = trac.ticket.web_ui
+        trac.timeline = trac.timeline.web_ui
+        trac.versioncontrol.admin = trac.versioncontrol.admin
+        trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz
+        trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs
+        trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop
+        trac.versioncontrol.web_ui = trac.versioncontrol.web_ui
+        trac.web.auth = trac.web.auth
+        trac.web.session = trac.web.session
+        trac.wiki.admin = trac.wiki.admin
+        trac.wiki.interwiki = trac.wiki.interwiki
+        trac.wiki.macros = trac.wiki.macros
+        trac.wiki.web_ui = trac.wiki.web_ui
+        trac.wiki.web_api = trac.wiki.web_api
+        tracopt.mimeview.enscript = tracopt.mimeview.enscript
+        tracopt.mimeview.php = tracopt.mimeview.php
+        tracopt.perm.authz_policy = tracopt.perm.authz_policy
+        tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider
+        tracopt.ticket.commit_updater = tracopt.ticket.commit_updater
+        tracopt.ticket.deleter = tracopt.ticket.deleter
+    """,
+
+    **extra
+)
diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py
--- a/src/distutils2/tests/test_build_ext.py
+++ b/src/distutils2/tests/test_build_ext.py
@@ -237,53 +237,6 @@
         cmd.finalize_options()
         self.assertEqual(cmd.swig_opts, ['1', '2'])
 
-    def test_check_extensions_list(self):
-        dist = Distribution()
-        cmd = build_ext(dist)
-        cmd.finalize_options()
-
-        #'extensions' option must be a list of Extension instances
-        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo')
-
-        # each element of 'ext_modules' option must be an
-        # Extension instance or 2-tuple
-        exts = [('bar', 'foo', 'bar'), 'foo']
-        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
-
-        # first element of each tuple in 'ext_modules'
-        # must be the extension name (a string) and match
-        # a python dotted-separated name
-        exts = [('foo-bar', '')]
-        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
-
-        # second element of each tuple in 'ext_modules'
-        # must be a ary (build info)
-        exts = [('foo.bar', '')]
-        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
-
-        # ok this one should pass
-        exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
-                             'some': 'bar'})]
-        cmd.check_extensions_list(exts)
-        ext = exts[0]
-        self.assertTrue(isinstance(ext, Extension))
-
-        # check_extensions_list adds in ext the values passed
-        # when they are in ('include_dirs', 'library_dirs', 'libraries'
-        # 'extra_objects', 'extra_compile_args', 'extra_link_args')
-        self.assertEqual(ext.libraries, 'foo')
-        self.assertTrue(not hasattr(ext, 'some'))
-
-        # 'macros' element of build info dict must be 1- or 2-tuple
-        exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
-                'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})]
-        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
-
-        exts[0][1]['macros'] = [('1', '2'), ('3',)]
-        cmd.check_extensions_list(exts)
-        self.assertEqual(exts[0].undef_macros, ['3'])
-        self.assertEqual(exts[0].define_macros, [('1', '2')])
-
     def test_get_source_files(self):
         modules = [Extension('foo', ['xxx'], optional=False)]
         dist = Distribution({'name': 'xx', 'ext_modules': modules})
diff --git a/src/distutils2/tests/test_pypi_dist.py b/src/distutils2/tests/test_pypi_dist.py
--- a/src/distutils2/tests/test_pypi_dist.py
+++ b/src/distutils2/tests/test_pypi_dist.py
@@ -5,8 +5,8 @@
 import tempfile
 
 from distutils2.tests.pypi_server import use_pypi_server
-from distutils2.tests import support
-from distutils2.tests.support import unittest
+from distutils2.tests import run_unittest
+from distutils2.tests.support import unittest, TempdirManager
 from distutils2.version import VersionPredicate
 from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName
 from distutils2.pypi.dist import (PyPIDistribution as Dist,
@@ -14,19 +14,19 @@
                                   split_archive_name)
 
 
-class TestPyPIDistribution(support.TempdirManager,
+class TestPyPIDistribution(TempdirManager,
                            unittest.TestCase):
-    """tests the pypi.dist.PyPIDistribution class"""
+    """Tests the pypi.dist.PyPIDistribution class"""
 
     def test_instanciation(self):
-        """Test the Distribution class provides us the good attributes when
-        given on construction"""
+        # Test the Distribution class provides us the good attributes when
+        # given on construction
         dist = Dist("FooBar", "1.1")
         self.assertEqual("FooBar", dist.name)
         self.assertEqual("1.1", "%s" % dist.version)
 
-    def test_from_url(self):
-        """Test that the Distribution object can be built from a single URL"""
+    def test_create_from_url(self):
+        # Test that the Distribution object can be built from a single URL
         url_list = {
             'FooBar-1.1.0.tar.gz': {
                 'name': 'foobar',  # lowercase the name
@@ -68,7 +68,7 @@
                         self.assertEqual(getattr(dist, attribute), value)
 
     def test_get_url(self):
-        """Test that the url property works well"""
+        # Test that the url property works well
 
         d = Dist("foobar", "1.1", url="test_url")
         self.assertDictEqual(d.url, {
@@ -90,7 +90,7 @@
         self.assertEqual(2, len(d._urls))
 
     def test_comparaison(self):
-        """Test that we can compare PyPIDistributions"""
+        # Test that we can compare PyPIDistributions
         foo1 = Dist("foo", "1.0")
         foo2 = Dist("foo", "2.0")
         bar = Dist("bar", "2.0")
@@ -103,7 +103,7 @@
         self.assertRaises(TypeError, foo1.__eq__, bar)
 
     def test_split_archive_name(self):
-        """Test we can split the archive names"""
+        # Test we can split the archive names
         names = {
             'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'),
             'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'),
@@ -114,40 +114,39 @@
 
     @use_pypi_server("downloads_with_md5")
     def test_download(self, server):
-        """Download is possible, and the md5 is checked if given"""
+        # Download is possible, and the md5 is checked if given
+
+        add_to_tmpdirs = lambda x: self.tempdirs.append(os.path.dirname(x))
 
         url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address
         # check md5 if given
         dist = Dist("FooBar", "0.1", url=url,
             url_hashname="md5", url_hashval="d41d8cd98f00b204e9800998ecf8427e")
-        dist.download()
+        add_to_tmpdirs(dist.download())
 
         # a wrong md5 fails
         dist2 = Dist("FooBar", "0.1", url=url,
             url_hashname="md5", url_hashval="wrongmd5")
+
         self.assertRaises(HashDoesNotMatch, dist2.download)
+        add_to_tmpdirs(dist2.downloaded_location)
 
         # we can omit the md5 hash
         dist3 = Dist("FooBar", "0.1", url=url)
-        dist3.download()
+        add_to_tmpdirs(dist3.download())
 
         # and specify a temporary location
         # for an already downloaded dist
-        path1 = tempfile.mkdtemp()
+        path1 = self.mkdtemp()
         dist3.download(path=path1)
         # and for a new one
-        path2_base = tempfile.mkdtemp()
+        path2_base = self.mkdtemp()
         dist4 = Dist("FooBar", "0.1", url=url)
         path2 = dist4.download(path=path2_base)
         self.assertTrue(path2_base in path2)
 
-        # remove the temp folders
-        shutil.rmtree(path1)
-        shutil.rmtree(os.path.dirname(path2))
-
     def test_hashname(self):
-        """Invalid hashnames raises an exception on assignation"""
-        # should be ok
+        # Invalid hashnames raises an exception on assignation
         Dist("FooBar", "0.1", url_hashname="md5", url_hashval="value")
 
         self.assertRaises(UnsupportedHashName, Dist, "FooBar", "0.1",
@@ -155,11 +154,10 @@
 
 
 class TestPyPIDistributions(unittest.TestCase):
-    """test the pypi.distr.PyPIDistributions class"""
 
     def test_filter(self):
-        """Test we filter the distributions the right way, using version
-        predicate match method"""
+        # Test we filter the distributions the right way, using version
+        # predicate match method
         dists = Dists((
             Dist("FooBar", "1.1"),
             Dist("FooBar", "1.1.1"),
@@ -173,7 +171,6 @@
         self.assertIn(dists[1], filtered)
 
     def test_append(self):
-        """Test the append method of PyPIDistributions"""
         # When adding a new item to the list, the behavior is to test if
         # a distribution with the same name and version number already exists,
         # and if so, to add url informations to the existing PyPIDistribution
@@ -198,7 +195,7 @@
         self.assertEqual(3, len(dists))
 
     def test_prefer_final(self):
-        """Ordering support prefer_final"""
+        # Can order the distributions using prefer_final
 
         fb10 = Dist("FooBar", "1.0")  # final distribution
         fb11a = Dist("FooBar", "1.1a1")  # alpha
@@ -213,7 +210,7 @@
         self.assertEqual(fb12b, dists[0])
 
     def test_prefer_source(self):
-        """Ordering support prefer_source"""
+        # Ordering support prefer_source
         fb_source = Dist("FooBar", "1.0", type="source")
         fb_binary = Dist("FooBar", "1.0", type="binary")
         fb2_binary = Dist("FooBar", "2.0", type="binary")
@@ -230,8 +227,7 @@
         self.assertEqual(fb2_binary, dists[0])
 
     def test_get_same_name_and_version(self):
-        """PyPIDistributions can return a list of "duplicates"
-        """
+        # PyPIDistributions can return a list of "duplicates"
         fb_source = Dist("FooBar", "1.0", type="source")
         fb_binary = Dist("FooBar", "1.0", type="binary")
         fb2_binary = Dist("FooBar", "2.0", type="binary")
diff --git a/src/distutils2/tests/test_pypi_server.py b/src/distutils2/tests/test_pypi_server.py
--- a/src/distutils2/tests/test_pypi_server.py
+++ b/src/distutils2/tests/test_pypi_server.py
@@ -10,7 +10,7 @@
 class PyPIServerTest(unittest.TestCase):
 
     def test_records_requests(self):
-        """We expect that PyPIServer can log our requests"""
+        # We expect that PyPIServer can log our requests
         server = PyPIServer()
         server.start()
         self.assertEqual(len(server.requests), 0)
@@ -29,8 +29,7 @@
         server.stop()
 
     def test_serve_static_content(self):
-        """PYPI Mocked server can serve static content from disk.
-        """
+        # PYPI Mocked server can serve static content from disk.
 
         def uses_local_files_for(server, url_path):
             """Test that files are served statically (eg. the output from the
diff --git a/src/distutils2/tests/test_pypi_simple.py b/src/distutils2/tests/test_pypi_simple.py
--- a/src/distutils2/tests/test_pypi_simple.py
+++ b/src/distutils2/tests/test_pypi_simple.py
@@ -7,20 +7,18 @@
 import tempfile
 import urllib2
 
-from distutils2.tests import support
+from distutils2.pypi import simple
+from distutils2.tests import support, run_unittest
 from distutils2.tests.support import unittest
-from distutils2.tests.pypi_server import use_pypi_server, PyPIServer, \
-                                         PYPI_DEFAULT_STATIC_PATH
-from distutils2.pypi import simple
-
-from distutils2.errors import DistutilsError
+from distutils2.tests.pypi_server import (use_pypi_server, PyPIServer,
+                                          PYPI_DEFAULT_STATIC_PATH)
 
 
 class PyPISimpleTestCase(support.TempdirManager,
                          unittest.TestCase):
 
     def _get_simple_index(self, server, base_url="/simple/", hosts=None,
-        *args, **kwargs):
+                          *args, **kwargs):
         """Build and return a SimpleSimpleIndex instance, with the test server
         urls
         """
@@ -43,9 +41,7 @@
         # issue 16
         # easy_install inquant.contentmirror.plone breaks because of a typo
         # in its home URL
-        index = simple.SimpleIndex(
-            hosts=('www.example.com',))
-
+        index = simple.SimpleIndex(hosts=('www.example.com',))
         url = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk'
         try:
             v = index._open_url(url)
@@ -84,12 +80,11 @@
             url = 'http://example.com'
             page = ('<a href="http://www.famfamfam.com]('
                     'http://www.famfamfam.com/">')
-            index.process_index(url, page)
+            index._process_url(url, page)
 
     @use_pypi_server("test_found_links")
     def test_found_links(self, server):
-        """Browse the index, asking for a specified distribution version
-        """
+        # Browse the index, asking for a specified distribution version
         # The PyPI index contains links for version 1.0, 1.1, 2.0 and 2.0.1
         index = self._get_simple_index(server)
         last_distribution = index.get("foobar")
@@ -143,8 +138,7 @@
 
     @use_pypi_server("with_externals")
     def test_restrict_hosts(self, server):
-        """Include external pages
-        """
+        # Include external pages
         # Try to request the package index, wich contains links to "externals"
         # resources. They have to  be scanned too.
         index = self._get_simple_index(server, follow_externals=True)
@@ -154,8 +148,7 @@
 
     @use_pypi_server("with_real_externals")
     def test_restrict_hosts(self, server):
-        """Only use a list of allowed hosts is possible
-        """
+        # Only use a list of allowed hosts is possible
         # Test that telling the simple pyPI client to not retrieve external
         # works
         index = self._get_simple_index(server, follow_externals=False)
@@ -163,28 +156,22 @@
         self.assertNotIn(server.full_address + "/external/external.html",
             index._processed_urls)
 
-    @use_pypi_server("with_egg_files")
-    def test_scan_egg_files(self, server):
-        """Assert that egg files are indexed as well"""
-        pass
-
     @use_pypi_server(static_filesystem_paths=["with_externals"],
         static_uri_paths=["simple", "external"])
     def test_links_priority(self, server):
-        """
-        Download links from the pypi simple index should be used before
-        external download links.
-        http://bitbucket.org/tarek/distribute/issue/163/md5-validation-error
+        # Download links from the pypi simple index should be used before
+        # external download links.
+        # http://bitbucket.org/tarek/distribute/issue/163/md5-validation-error
+        #
+        # Usecase :
+        # - someone uploads a package on pypi, a md5 is generated
+        # - someone manually coindexes this link (with the md5 in the url) onto
+        #   an external page accessible from the package page.
+        # - someone reuploads the package (with a different md5)
+        # - while easy_installing, an MD5 error occurs because the external link
+        #   is used
+        # -> The index should use the link from pypi, not the external one.
 
-        Usecase :
-        - someone uploads a package on pypi, a md5 is generated
-        - someone manually coindexes this link (with the md5 in the url) onto
-          an external page accessible from the package page.
-        - someone reuploads the package (with a different md5)
-        - while easy_installing, an MD5 error occurs because the external link
-          is used
-        -> The index should use the link from pypi, not the external one.
-        """
         # start an index server
         index_url = server.full_address + '/simple/'
 
@@ -202,10 +189,10 @@
     @use_pypi_server(static_filesystem_paths=["with_norel_links"],
         static_uri_paths=["simple", "external"])
     def test_not_scan_all_links(self, server):
-        """Do not follow all index page links.
-        The links not tagged with rel="download" and rel="homepage" have
-        to not be processed by the package index, while processing "pages".
-        """
+        # Do not follow all index page links.
+        # The links not tagged with rel="download" and rel="homepage" have
+        # to not be processed by the package index, while processing "pages".
+
         # process the pages
         index = self._get_simple_index(server, follow_externals=True)
         index.find("foobar")
@@ -213,10 +200,6 @@
         # and rel="homepage"
         self.assertIn("%s/simple/foobar/" % server.full_address,
             index._processed_urls)  # it's the simple index page
-
-        # FIXME
-        return
-
         self.assertIn("%s/external/homepage.html" % server.full_address,
             index._processed_urls)  # the external homepage is rel="homepage"
         self.assertNotIn("%s/external/nonrel.html" % server.full_address,
@@ -229,7 +212,7 @@
             index._processed_urls)  # linked from external homepage (rel)
 
     def test_uses_mirrors(self):
-        """When the main repository seems down, try using the given mirrors"""
+        # When the main repository seems down, try using the given mirrors"""
         server = PyPIServer("foo_bar_baz")
         mirror = PyPIServer("foo_bar_baz")
         mirror.start()  # we dont start the server here
@@ -246,7 +229,7 @@
             mirror.stop()
 
     def test_simple_link_matcher(self):
-        """Test that the simple link matcher yields the right links"""
+        # Test that the simple link matcher yields the right links"""
         index = simple.SimpleIndex(follow_externals=False)
 
         # Here, we define:
@@ -280,7 +263,7 @@
         self.assertRaises(StopIteration, generator.next)
 
     def test_browse_local_files(self):
-        """Test that we can browse local files"""
+        # Test that we can browse local files"""
         index_path = os.sep.join(["file://" + PYPI_DEFAULT_STATIC_PATH,
                                   "test_found_links", "simple"])
         index = simple.SimpleIndex(index_path)
@@ -291,4 +274,4 @@
     return unittest.makeSuite(PyPISimpleTestCase)
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest="test_suite")
diff --git a/src/distutils2/tests/test_upload.py b/src/distutils2/tests/test_upload.py
--- a/src/distutils2/tests/test_upload.py
+++ b/src/distutils2/tests/test_upload.py
@@ -1,12 +1,13 @@
 """Tests for distutils.command.upload."""
 # -*- encoding: utf8 -*-
-import os, sys
+import os
+import sys
 
 from distutils2.command.upload import upload
 from distutils2.core import Distribution
 
+from distutils2.tests import support
 from distutils2.tests.pypi_server import PyPIServer, PyPIServerTestCase
-from distutils2.tests import support
 from distutils2.tests.support import unittest
 from distutils2.tests.test_config import PYPIRC, PyPIRCCommandTestCase
 
@@ -29,10 +30,10 @@
         dist = Distribution()
         cmd = upload(dist)
         cmd.finalize_options()
-        for attr, waited in (('username', 'me'), ('password', 'secret'),
-                             ('realm', 'pypi'),
-                             ('repository', 'http://pypi.python.org/pypi')):
-            self.assertEqual(getattr(cmd, attr), waited)
+        for attr, expected in (('username', 'me'), ('password', 'secret'),
+                               ('realm', 'pypi'),
+                               ('repository', 'http://pypi.python.org/pypi')):
+            self.assertEqual(getattr(cmd, attr), expected)
 
     def test_saved_password(self):
         # file with no password
diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py
--- a/src/distutils2/tests/test_util.py
+++ b/src/distutils2/tests/test_util.py
@@ -4,6 +4,8 @@
 from copy import copy
 from StringIO import StringIO
 import subprocess
+import tempfile
+import time
 
 from distutils2.errors import (DistutilsPlatformError,
                                DistutilsByteCompileError,
@@ -257,7 +259,10 @@
 
     def test_newer(self):
         self.assertRaises(DistutilsFileError, util.newer, 'xxx', 'xxx')
-
+        self.newer_f1 = tempfile.NamedTemporaryFile()
+        time.sleep(1)
+        self.newer_f2 = tempfile.NamedTemporaryFile()
+        self.assertTrue(util.newer(self.newer_f2.name, self.newer_f1.name))
 
     def test_find_packages(self):
         # let's create a structure we want to scan:
diff --git a/src/distutils2/tests/test_version.py b/src/distutils2/tests/test_version.py
--- a/src/distutils2/tests/test_version.py
+++ b/src/distutils2/tests/test_version.py
@@ -3,7 +3,7 @@
 import os
 
 from distutils2.version import NormalizedVersion as V
-from distutils2.version import IrrationalVersionError
+from distutils2.version import HugeMajorVersionNumError, IrrationalVersionError
 from distutils2.version import suggest_normalized_version as suggest
 from distutils2.version import VersionPredicate
 from distutils2.tests.support import unittest
@@ -22,6 +22,10 @@
                 (V('1.0.dev345'), '1.0.dev345'),
                 (V('1.0.post456.dev623'), '1.0.post456.dev623'))
 
+    def test_repr(self):
+
+        self.assertEqual(repr(V('1.0')), "NormalizedVersion('1.0')")
+
     def test_basic_versions(self):
 
         for v, s in self.versions:
@@ -44,6 +48,12 @@
         for s in irrational:
             self.assertRaises(IrrationalVersionError, V, s)
 
+    def test_huge_version(self):
+
+        self.assertEquals(str(V('1980.0')), '1980.0')
+        self.assertRaises(HugeMajorVersionNumError, V, '1981.0')
+        self.assertEquals(str(V('1981.0', error_on_huge_major_num=False)), '1981.0')
+
     def test_comparison(self):
         r"""
         >>> V('1.2.0') == '1.2'
@@ -51,12 +61,33 @@
         ...
         TypeError: cannot compare NormalizedVersion and str
 
+        >>> V('1.2') < '1.3'
+        Traceback (most recent call last):
+        ...
+        TypeError: cannot compare NormalizedVersion and str
+
         >>> V('1.2.0') == V('1.2')
         True
         >>> V('1.2.0') == V('1.2.3')
         False
+        >>> V('1.2.0') != V('1.2.3')
+        True
         >>> V('1.2.0') < V('1.2.3')
         True
+        >>> V('1.2.0') < V('1.2.0')
+        False
+        >>> V('1.2.0') <= V('1.2.0')
+        True
+        >>> V('1.2.0') <= V('1.2.3')
+        True
+        >>> V('1.2.3') <= V('1.2.0')
+        False
+        >>> V('1.2.0') >= V('1.2.0')
+        True
+        >>> V('1.2.3') >= V('1.2.0')
+        True
+        >>> V('1.2.0') >= V('1.2.3')
+        False
         >>> (V('1.0') > V('1.0b2'))
         True
         >>> (V('1.0') > V('1.0c2') > V('1.0c1') > V('1.0b2') > V('1.0b1')
@@ -101,6 +132,7 @@
         self.assertEqual(suggest('1.0c2'), '1.0c2')
         self.assertEqual(suggest('walla walla washington'), None)
         self.assertEqual(suggest('2.4c1'), '2.4c1')
+        self.assertEqual(suggest('v1.0'), '1.0')
 
         # from setuptools
         self.assertEqual(suggest('0.4a1.r10'), '0.4a1.post10')
@@ -151,6 +183,8 @@
         self.assertFalse(VersionPredicate('Hey (<=2.5)').match('2.6.0'))
         self.assertTrue(VersionPredicate('Hey (>=2.5)').match('2.5.1'))
 
+        self.assertRaises(ValueError, VersionPredicate, '')
+
         # XXX need to silent the micro version in this case
         #assert not VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3')
 
@@ -164,11 +198,21 @@
         for version in other_versions:
             self.assertFalse(V(version).is_final)
 
+class VersionWhiteBoxTestCase(unittest.TestCase):
+
+    def test_parse_numdots(self):
+        # For code coverage completeness, as pad_zeros_length can't be set or
+        # influenced from the public interface
+        self.assertEquals(V('1.0')._parse_numdots('1.0', '1.0',
+                                                  pad_zeros_length=3),
+                          [1, 0, 0])
+
 
 def test_suite():
     #README = os.path.join(os.path.dirname(__file__), 'README.txt')
     #suite = [doctest.DocFileSuite(README), unittest.makeSuite(VersionTestCase)]
-    suite = [unittest.makeSuite(VersionTestCase)]
+    suite = [unittest.makeSuite(VersionTestCase),
+             unittest.makeSuite(VersionWhiteBoxTestCase)]
     return unittest.TestSuite(suite)
 
 if __name__ == "__main__":
diff --git a/src/distutils2/version.py b/src/distutils2/version.py
--- a/src/distutils2/version.py
+++ b/src/distutils2/version.py
@@ -379,8 +379,6 @@
     def __init__(self, predicate):
         predicate = predicate.strip()
         match = _PLAIN_VERSIONS.match(predicate)
-        if match is None:
-            raise ValueError('Bad predicate "%s"' % predicate)
         self.name = None
         predicates = match.groups()[0]
         self.predicates = [_split_predicate(pred.strip())
@@ -391,8 +389,6 @@
     def __init__(self, predicate):
         predicate = predicate.strip()
         match = _PLAIN_VERSIONS.match(predicate)
-        if match is None:
-            raise ValueError('Bad predicate "%s"' % predicate)
         self.name = None
         self.predicates = _split_predicate(match.groups()[0])
 
diff --git a/src/runtests-cov.py b/src/runtests-cov.py
new file mode 100755
--- /dev/null
+++ b/src/runtests-cov.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+"""Tests for distutils2.
+
+The tests for distutils2 are defined in the distutils2.tests package.
+"""
+
+# TODO:
+
+# The coverage report is only accurate when ran inside a virtualenv
+# created with the --no-site-packages option.  When it's not the case,
+# the built-in ignore list is not accurate and third party packages
+# show-up in the report, lowering the overall coverage.  
+
+# One particular problem it docutils on Ubuntu which has a __file__
+# starting with /usr/lib/python2.6 while the path in the coverage
+# report starts with /usr/share/pyshared.
+
+import sys
+from os.path import dirname
+from optparse import OptionParser
+
+def parse_opts():
+    parser = OptionParser(usage="%prog [OPTIONS]", 
+                          description="run the distutils2 unittests")
+    
+    parser.add_option("-q", "--quiet", help="do not print verbose messages", 
+                      action="store_true", default=False)
+    parser.add_option("-c", "--coverage", action="store_true", default=False,
+                      help="produce a coverage report at the end of the run")
+    parser.add_option("-r", "--report", action="store_true", default=False,
+                      help="produce a coverage report from the last test run")
+    parser.add_option("-m", "--show-missing", action="store_true", 
+                      default=False,
+                      help=("Show line numbers of statements in each module "
+                            "that weren't executed."))
+    
+    opts, args = parser.parse_args()
+    return opts, args
+
+def coverage_report(opts):
+    import coverage
+    import unittest2
+    import docutils
+    cov = coverage.coverage()
+    cov.load()
+
+    cov.report(omit_prefixes=["distutils2/tests", 
+                              "runtests", 
+                              "distutils2/_backport", 
+                              dirname(unittest2.__file__),
+                              dirname(dirname(docutils.__file__))], 
+               show_missing=opts.show_missing)
+
+
+def test_main():
+    opts, args = parse_opts()
+    verbose = not opts.quiet
+    ret = 0
+    
+    if opts.coverage or opts.report:
+        import coverage
+        
+    if opts.coverage:
+        cov = coverage.coverage()
+        cov.erase()
+        cov.start()
+    if not opts.report:
+        ret = run_tests(verbose)
+    if opts.coverage:
+        cov.stop()
+        cov.save()
+
+    if opts.report or opts.coverage:
+        coverage_report(opts)
+    
+    return ret
+    
+def run_tests(verbose):
+    import distutils2.tests
+    from distutils2.tests import run_unittest, reap_children, TestFailed
+    from distutils2._backport.tests import test_suite as btest_suite
+    # XXX just supporting -q right now to enable detailed/quiet output
+    if len(sys.argv) > 1:
+        verbose = sys.argv[-1] != '-q'
+    else:
+        verbose = 1
+    try:
+        try:
+            run_unittest([distutils2.tests.test_suite(), btest_suite()],
+                         verbose_=verbose)
+            return 0
+        except TestFailed:
+            return 1
+    finally:
+        reap_children()
+
+if __name__ == "__main__":
+    try:
+        from distutils2.tests.support import unittest
+    except ImportError:
+        sys.stderr.write('Error: You have to install unittest2')
+        sys.exit(1)
+
+    sys.exit(test_main())
+
diff --git a/src/setup.py b/src/setup.py
--- a/src/setup.py
+++ b/src/setup.py
@@ -226,7 +226,7 @@
       packages=find_packages(),
       cmdclass={'sdist': sdist_hg, 'install': install_hg},
       package_data={'distutils2._backport': ['sysconfig.cfg']},
-      project_url=[('Mailing-list',
+      project_url=[('Mailing list',
                     'http://mail.python.org/mailman/listinfo/distutils-sig/'),
                    ('Documentation',
                     'http://packages.python.org/Distutils2'),

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


More information about the Python-checkins mailing list