[Python-checkins] distutils2: Branch merge

tarek.ziade python-checkins at python.org
Sun Jan 23 15:48:24 CET 2011


tarek.ziade pushed 9299dd4ee3fe to distutils2:

http://hg.python.org/distutils2/rev/9299dd4ee3fe
changeset:   892:9299dd4ee3fe
parent:      887:cb2aa326f4c3
parent:      891:d879ec1da01f
user:        ?ric Araujo <merwok at netwok.org>
date:        Fri Jan 21 21:00:54 2011 +0100
summary:
  Branch merge

files:
  distutils2/dist.py
  distutils2/mkcfg.py
  distutils2/run.py
  distutils2/tests/test_command_bdist.py
  distutils2/tests/test_command_cmd.py
  distutils2/tests/test_command_register.py
  distutils2/tests/test_config.py
  distutils2/tests/test_depgraph.py
  distutils2/tests/test_mixin2to3.py
  setup.py

diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -6,17 +6,19 @@
 ---------
 
 - The setup runner supports more options:
-- XXX fill changes done in commands + compilers
-- Issue 10409: Fixed the Licence selector in mkcfg
+- XXX fill changes done in commands + compilers [tarek]
+- Issue #10409: Fixed the Licence selector in mkcfg [tarek]
+- Issue #9558: Fix build_ext with VS 8.0 [éric]
+- Issue #6007: Add disclaimer about MinGW compatibility in docs [éric]
 
 1.0a3 - 2010-10-08
 ------------------
 
-- Provided a Tox configuration for cross-python testing [holger]
+- Provided a Tox configuration for cross-Python testing [holger]
 - Fixed the installation when using easy_install and Pip by switching
   setup.py to distutils1 [holger/tarek]
 - Added missing c/h files in the MANIFEST so they are always present
-  no matter which python version was used to build it. [holger/tarek]
+  no matter which Python version was used to build it. [holger/tarek]
 - Added the new setup runner that uses only setup.cfg
 - Renamed mkpkg to mkcfg [tarek]
 - Renamed install_tools to install [alexis]
diff --git a/distutils2/_backport/pkgutil.py b/distutils2/_backport/pkgutil.py
--- a/distutils2/_backport/pkgutil.py
+++ b/distutils2/_backport/pkgutil.py
@@ -343,8 +343,7 @@
     from zipimport import zipimporter
 
     def iter_zipimport_modules(importer, prefix=''):
-        dirlist = zipimport._zip_directory_cache[importer.archive].keys()
-        dirlist.sort()
+        dirlist = sorted(zipimport._zip_directory_cache[importer.archive])
         _prefix = importer.prefix
         plen = len(_prefix)
         yielded = {}
@@ -680,7 +679,6 @@
                                   dir.endswith('.egg')):
                 yield EggInfoDistribution(dist_path)
 
-
 def _generate_cache(use_egg_info=False):
     global _cache_generated, _cache_generated_egg
 
@@ -738,6 +736,9 @@
         if _cache_enabled and not path in _cache_path:
             _cache_path[path] = self
 
+    def __repr__(self):
+        return '%s-%s at %s' % (self.name, self.metadata.version, self.path)
+
     def _get_records(self, local=False):
         RECORD = os.path.join(self.path, 'RECORD')
         record_reader = csv_reader(open(RECORD, 'rb'), delimiter=',')
@@ -856,9 +857,9 @@
         r'(?P<rest>(?:\s*,\s*(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)*)\s*' \
         r'(?P<extras>\[.*\])?')
 
-    def __init__(self, path):
+    def __init__(self, path, display_warnings=False):
         self.path = path
-
+        self.display_warnings = display_warnings
         if _cache_enabled and path in _cache_path_egg:
             self.metadata = _cache_path_egg[path].metadata
             self.name = self.metadata['Name']
@@ -910,25 +911,35 @@
         else:
             raise ValueError('The path must end with .egg-info or .egg')
 
-        provides = "%s (%s)" % (self.metadata['name'],
-                                self.metadata['version'])
-        if self.metadata['Metadata-Version'] == '1.2':
+
+        if requires is not None:
+            if self.metadata['Metadata-Version'] == '1.1':
+                # we can't have 1.1 metadata *and* Setuptools requires
+                for field in ('Obsoletes', 'Requires', 'Provides'):
+                    del self.metadata[field]
+
+            provides = "%s (%s)" % (self.metadata['name'],
+                                    self.metadata['version'])
             self.metadata['Provides-Dist'] += (provides,)
-        else:
-            self.metadata['Provides'] += (provides,)
+
         reqs = []
+
         if requires is not None:
             for line in yield_lines(requires):
-                if line[0] == '[':
+                if line[0] == '[' and self.display_warnings:
                     warnings.warn('distutils2 does not support extensions '
                                   'in requires.txt')
                     break
                 else:
                     match = self._REQUIREMENT.match(line.strip())
                     if not match:
-                        raise ValueError('Distribution %s has ill formed '
-                                         'requires.txt file (%s)' %
-                                         (self.name, line))
+                        # this happens when we encounter extras
+                        # since they are written at the end of the file
+                        # we just exit
+                        break
+                        #raise ValueError('Distribution %s has ill formed '
+                        #                 'requires.txt file (%s)' %
+                        #                 (self.name, line))
                     else:
                         if match.group('extras'):
                             s = (('Distribution %s uses extra requirements '
@@ -946,14 +957,17 @@
                             reqs.append(name)
                         else:
                             reqs.append('%s (%s)' % (name, version))
-            if self.metadata['Metadata-Version'] == '1.2':
+
+            if len(reqs) > 0:
                 self.metadata['Requires-Dist'] += reqs
-            else:
-                self.metadata['Requires'] += reqs
+
 
         if _cache_enabled:
             _cache_path_egg[self.path] = self
 
+    def __repr__(self):
+        return '%s-%s at %s' % (self.name, self.metadata.version, self.path)
+
     def get_installed_files(self, local=False):
         return []
 
diff --git a/distutils2/_backport/shutil.py b/distutils2/_backport/shutil.py
--- a/distutils2/_backport/shutil.py
+++ b/distutils2/_backport/shutil.py
@@ -349,7 +349,7 @@
     compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'compress': '.Z'}
 
     # flags for compression program, each element of list will be an argument
-    if compress is not None and compress not in compress_ext.keys():
+    if compress is not None and compress not in compress_ext:
         raise ValueError, \
               ("bad value for 'compress': must be None, 'gzip', 'bzip2' "
                "or 'compress'")
@@ -487,7 +487,7 @@
     Each element of the returned sequence is a tuple (name, description)
     """
     formats = [(name, registry[2]) for name, registry in
-               _ARCHIVE_FORMATS.items()]
+               _ARCHIVE_FORMATS.iteritems()]
     formats.sort()
     return formats
 
diff --git a/distutils2/_backport/sysconfig.py b/distutils2/_backport/sysconfig.py
--- a/distutils2/_backport/sysconfig.py
+++ b/distutils2/_backport/sysconfig.py
@@ -102,9 +102,8 @@
 
 
 def _extend_dict(target_dict, other_dict):
-    target_keys = target_dict.keys()
-    for key, value in other_dict.items():
-        if key in target_keys:
+    for key, value in other_dict.iteritems():
+        if key in target_dict:
             continue
         target_dict[key] = value
 
@@ -713,7 +712,7 @@
 
 
 def _print_dict(title, data):
-    for index, (key, value) in enumerate(sorted(data.items())):
+    for index, (key, value) in enumerate(sorted(data.iteritems())):
         if index == 0:
             print '%s: ' % (title)
         print '\t%s = "%s"' % (key, value)
diff --git a/distutils2/_backport/tests/test_pkgutil.py b/distutils2/_backport/tests/test_pkgutil.py
--- a/distutils2/_backport/tests/test_pkgutil.py
+++ b/distutils2/_backport/tests/test_pkgutil.py
@@ -249,7 +249,7 @@
             dist = Distribution(distinfo_dir)
             for path, md5_, size in dist.get_installed_files():
                 record_data = self.records[dist.path]
-                self.assertTrue(path in record_data.keys())
+                self.assertIn(path, record_data)
                 self.assertEqual(md5_, record_data[path][0])
                 self.assertEqual(size, record_data[path][1])
 
@@ -318,7 +318,7 @@
         self.assertEqual(sorted(found), sorted(distinfo_record_paths))
         # Test for the iteration of local absolute paths
         distinfo_record_paths = [os.path.join(sys.prefix, path)
-            for path in self.records[distinfo_dir].keys()]
+            for path in self.records[distinfo_dir]]
         found = [path for path in dist.get_distinfo_files(local=True)]
         self.assertEqual(sorted(found), sorted(distinfo_record_paths))
 
@@ -382,7 +382,7 @@
             if not isinstance(dist, Distribution):
                 self.fail("item received was not a Distribution instance: "
                     "%s" % type(dist))
-            if dist.name in dict(fake_dists).keys() and \
+            if dist.name in dict(fake_dists) and \
                dist.path.startswith(self.fake_dists_path):
                 found_dists.append((dist.name, dist.metadata['version'],))
             else:
@@ -405,7 +405,7 @@
                     isinstance(dist, EggInfoDistribution)):
                 self.fail("item received was not a Distribution or "
                           "EggInfoDistribution instance: %s" % type(dist))
-            if dist.name in dict(fake_dists).keys() and \
+            if dist.name in dict(fake_dists) and \
                dist.path.startswith(self.fake_dists_path):
                 found_dists.append((dist.name, dist.metadata['version']))
             else:
diff --git a/distutils2/command/__init__.py b/distutils2/command/__init__.py
--- a/distutils2/command/__init__.py
+++ b/distutils2/command/__init__.py
@@ -31,8 +31,8 @@
 
 
 def get_command_names():
-    return sorted(_COMMANDS.keys())
     """Return registered commands"""
+    return sorted(_COMMANDS)
 
 
 def set_command(location):
diff --git a/distutils2/command/build_ext.py b/distutils2/command/build_ext.py
--- a/distutils2/command/build_ext.py
+++ b/distutils2/command/build_ext.py
@@ -257,7 +257,7 @@
 
             elif MSVC_VERSION == 8:
                 self.library_dirs.append(os.path.join(sys.exec_prefix,
-                                         'PC', 'VS8.0', 'win32release'))
+                                         'PC', 'VS8.0'))
             elif MSVC_VERSION == 7:
                 self.library_dirs.append(os.path.join(sys.exec_prefix,
                                          'PC', 'VS7.1'))
diff --git a/distutils2/command/build_py.py b/distutils2/command/build_py.py
--- a/distutils2/command/build_py.py
+++ b/distutils2/command/build_py.py
@@ -68,7 +68,7 @@
         self.package_data = self.distribution.package_data
         self.package_dir = {}
         if self.distribution.package_dir:
-            for name, path in self.distribution.package_dir.items():
+            for name, path in self.distribution.package_dir.iteritems():
                 self.package_dir[name] = convert_path(path)
         self.data_files = self.get_data_files()
 
diff --git a/distutils2/command/check.py b/distutils2/command/check.py
--- a/distutils2/command/check.py
+++ b/distutils2/command/check.py
@@ -76,11 +76,11 @@
             raise DistutilsSetupError('The docutils package is needed.')
 
     def check_hooks_resolvable(self):
-        for options in self.distribution.command_options.values():
+        for options in self.distribution.command_options.itervalues():
             for hook_kind in ("pre_hook", "post_hook"):
                 if hook_kind not in options:
                     break
-                for hook_name in options[hook_kind][1].values():
+                for hook_name in options[hook_kind][1].itervalues():
                     try:
                         resolve_name(hook_name)
                     except ImportError:
diff --git a/distutils2/command/install_dist.py b/distutils2/command/install_dist.py
--- a/distutils2/command/install_dist.py
+++ b/distutils2/command/install_dist.py
@@ -424,7 +424,7 @@
         """Set the install directories by applying the install schemes."""
         # it's the caller's problem if they supply a bad name!
         scheme = get_paths(name, expand=False)
-        for key, value in scheme.items():
+        for key, value in scheme.iteritems():
             if key == 'platinclude':
                 key = 'headers'
                 value = os.path.join(value, self.distribution.metadata['Name'])
diff --git a/distutils2/command/register.py b/distutils2/command/register.py
--- a/distutils2/command/register.py
+++ b/distutils2/command/register.py
@@ -11,7 +11,6 @@
 import urlparse
 import StringIO
 import logging
-from warnings import warn
 
 from distutils2.command.cmd import Command
 from distutils2 import logger
@@ -33,25 +32,19 @@
          "stop the registration if the metadata is not fully compliant")
         ]
 
-    boolean_options = ['show-response', 'verify', 'list-classifiers',
-                       'strict']
+    boolean_options = ['show-response', 'list-classifiers', 'strict']
 
     def initialize_options(self):
         self.repository = None
         self.realm = None
         self.show_response = 0
         self.list_classifiers = 0
-        self.strict = 0
 
     def finalize_options(self):
         if self.repository is None:
             self.repository = DEFAULT_REPOSITORY
         if self.realm is None:
             self.realm = DEFAULT_REALM
-        # setting options for the `check` subcommand
-        check_options = {'strict': ('register', self.strict),
-                         'all': ('register', 1)}
-        self.distribution.command_options['check'] = check_options
 
     def run(self):
         self.finalize_options()
@@ -67,16 +60,6 @@
         else:
             self.send_metadata()
 
-    def check_metadata(self):
-        """Deprecated API."""
-        warn("distutils.command.register.check_metadata is deprecated, \
-              use the check command instead", PendingDeprecationWarning)
-        check = self.distribution.get_command_obj('check')
-        check.ensure_finalized()
-        check.strict = self.strict
-        check.all = 1
-        check.run()
-
     def _set_config(self):
         ''' Reads the configuration file and set attributes.
         '''
@@ -252,7 +235,7 @@
         sep_boundary = '\n--' + boundary
         end_boundary = sep_boundary + '--'
         body = StringIO.StringIO()
-        for key, value in data.items():
+        for key, value in data.iteritems():
             # handle multiple entries for the same name
             if not isinstance(value, (tuple, list)):
                 value = [value]
diff --git a/distutils2/command/sdist.py b/distutils2/command/sdist.py
--- a/distutils2/command/sdist.py
+++ b/distutils2/command/sdist.py
@@ -371,8 +371,7 @@
         need_dir = {}
         for file in files:
             need_dir[os.path.join(base_dir, os.path.dirname(file))] = 1
-        need_dirs = need_dir.keys()
-        need_dirs.sort()
+        need_dirs = sorted(need_dir)
 
         # Now create them
         for dir in need_dirs:
diff --git a/distutils2/command/upload.py b/distutils2/command/upload.py
--- a/distutils2/command/upload.py
+++ b/distutils2/command/upload.py
@@ -140,7 +140,7 @@
         body = StringIO()
         file_fields = ('content', 'gpg_signature')
 
-        for key, values in data.items():
+        for key, values in data.iteritems():
             # handle multiple entries for the same name
             if not isinstance(values, (tuple, list)):
                 values = [values]
diff --git a/distutils2/compiler/__init__.py b/distutils2/compiler/__init__.py
--- a/distutils2/compiler/__init__.py
+++ b/distutils2/compiler/__init__.py
@@ -127,7 +127,7 @@
     from distutils2.fancy_getopt import FancyGetopt
     compilers = []
 
-    for name, cls in _COMPILERS.items():
+    for name, cls in _COMPILERS.iteritems():
         if isinstance(cls, str):
             cls = resolve_name(cls)
             _COMPILERS[name] = cls
diff --git a/distutils2/compiler/ccompiler.py b/distutils2/compiler/ccompiler.py
--- a/distutils2/compiler/ccompiler.py
+++ b/distutils2/compiler/ccompiler.py
@@ -116,8 +116,8 @@
         # named library files) to include on any link
         self.objects = []
 
-        for key in self.executables.keys():
-            self.set_executable(key, self.executables[key])
+        for key, value in self.executables.iteritems():
+            self.set_executable(key, value)
 
     def set_executables(self, **args):
         """Define the executables (and options for them) that will be run
@@ -145,12 +145,12 @@
         # discovered at run-time, since there are many different ways to do
         # basically the same things with Unix C compilers.
 
-        for key in args.keys():
+        for key, value in args.iteritems():
             if key not in self.executables:
                 raise ValueError, \
                       "unknown executable '%s' for class %s" % \
                       (key, self.__class__.__name__)
-            self.set_executable(key, args[key])
+            self.set_executable(key, value)
 
     def set_executable(self, key, value):
         if isinstance(value, str):
diff --git a/distutils2/compiler/cygwinccompiler.py b/distutils2/compiler/cygwinccompiler.py
--- a/distutils2/compiler/cygwinccompiler.py
+++ b/distutils2/compiler/cygwinccompiler.py
@@ -358,27 +358,3 @@
     except IOError, exc:
         return (CONFIG_H_UNCERTAIN,
                 "couldn't read '%s': %s" % (fn, exc.strerror))
-
-class _Deprecated_SRE_Pattern(object):
-    def __init__(self, pattern):
-        self.pattern = pattern
-
-    def __getattr__(self, name):
-        if name in ('findall', 'finditer', 'match', 'scanner', 'search',
-                    'split', 'sub', 'subn'):
-            warn("'distutils.cygwinccompiler.RE_VERSION' is deprecated "
-                 "and will be removed in the next version", DeprecationWarning)
-        return getattr(self.pattern, name)
-
-RE_VERSION = _Deprecated_SRE_Pattern(re.compile('(\d+\.\d+(\.\d+)*)'))
-
-def get_versions():
-    """ Try to find out the versions of gcc, ld and dllwrap.
-
-    If not possible it returns None for it.
-    """
-    warn("'distutils.cygwinccompiler.get_versions' is deprecated "
-         "use 'distutils.util.get_compiler_versions' instead",
-         DeprecationWarning)
-
-    return get_compiler_versions()
diff --git a/distutils2/compiler/msvc9compiler.py b/distutils2/compiler/msvc9compiler.py
--- a/distutils2/compiler/msvc9compiler.py
+++ b/distutils2/compiler/msvc9compiler.py
@@ -153,7 +153,7 @@
                 self.macros["$(FrameworkVersion)"] = d["version"]
 
     def sub(self, s):
-        for k, v in self.macros.items():
+        for k, v in self.macros.iteritems():
             s = s.replace(k, v)
         return s
 
@@ -271,7 +271,7 @@
             result[key] = removeDuplicates(value)
 
     if len(result) != len(interesting):
-        raise ValueError(str(list(result.keys())))
+        raise ValueError(str(list(result)))
 
     return result
 
diff --git a/distutils2/compiler/msvccompiler.py b/distutils2/compiler/msvccompiler.py
--- a/distutils2/compiler/msvccompiler.py
+++ b/distutils2/compiler/msvccompiler.py
@@ -146,7 +146,7 @@
             self.macros["$(FrameworkVersion)"] = d["version"]
 
     def sub(self, s):
-        for k, v in self.macros.items():
+        for k, v in self.macros.iteritems():
             s = string.replace(s, k, v)
         return s
 
diff --git a/distutils2/config.py b/distutils2/config.py
--- a/distutils2/config.py
+++ b/distutils2/config.py
@@ -98,7 +98,7 @@
 
         # setting the metadata values
         if 'metadata' in content:
-            for key, value in content['metadata'].items():
+            for key, value in content['metadata'].iteritems():
                 key = key.replace('_', '-')
                 value = self._multiline(value)
                 if key == 'project-url':
@@ -124,7 +124,7 @@
 
         if 'files' in content:
             files = dict([(key, self._multiline(value))
-                          for key, value in content['files'].items()])
+                          for key, value in content['files'].iteritems()])
             self.dist.packages = []
             self.dist.package_dir = {}
 
@@ -223,7 +223,7 @@
         # If there was a "global" section in the config file, use it
         # to set Distribution options.
         if 'global' in self.dist.command_options:
-            for (opt, (src, val)) in self.dist.command_options['global'].items():
+            for (opt, (src, val)) in self.dist.command_options['global'].iteritems():
                 alias = self.dist.negative_opt.get(opt)
                 try:
                     if alias:
diff --git a/distutils2/depgraph.py b/distutils2/depgraph.py
--- a/distutils2/depgraph.py
+++ b/distutils2/depgraph.py
@@ -66,18 +66,28 @@
         """
         self.missing[distribution].append(requirement)
 
+    def _repr_dist(self, dist):
+        return '%s %s' % (dist.name, dist.metadata['Version'])
+
+    def repr_node(self, dist, level=1):
+        """Prints only a subgraph"""
+        output = []
+        output.append(self._repr_dist(dist))
+        for other, label in self.adjacency_list[dist]:
+            dist = self._repr_dist(other)
+            if label is not None:
+                dist = '%s [%s]' % (dist, label)
+            output.append('    ' * level + '%s' % dist)
+            suboutput = self.repr_node(other, level+1)
+            subs = suboutput.split('\n')
+            output.extend(subs[1:])
+        return '\n'.join(output)
+
     def __repr__(self):
         """Representation of the graph"""
-        def _repr_dist(dist):
-            return '%s %s' % (dist.name, dist.metadata['Version'])
         output = []
         for dist, adjs in self.adjacency_list.iteritems():
-            output.append(_repr_dist(dist))
-            for other, label in adjs:
-                dist = _repr_dist(other)
-                if label is not None:
-                    dist = '%s [%s]' % (dist, label)
-                output.append('    %s' % dist)
+            output.append(self.repr_node(dist))
         return '\n'.join(output)
 
 
@@ -129,7 +139,9 @@
     # first, build the graph and find out the provides
     for dist in dists:
         graph.add_distribution(dist)
-        provides = dist.metadata['Provides-Dist'] + dist.metadata['Provides']
+        provides = (dist.metadata['Provides-Dist'] + dist.metadata['Provides'] +
+                    ['%s (%s)' % (dist.name, dist.metadata['Version'])])
+
 
         for p in provides:
             comps = p.strip().rsplit(" ", 1)
@@ -149,7 +161,13 @@
     for dist in dists:
         requires = dist.metadata['Requires-Dist'] + dist.metadata['Requires']
         for req in requires:
-            predicate = VersionPredicate(req)
+            try:
+                predicate = VersionPredicate(req)
+            except IrrationalVersionError:
+                # XXX compat-mode if cannot read the version
+                name = req.split()[0]
+                predicate = VersionPredicate(name)
+
             name = predicate.name
 
             if not name in provided:
@@ -172,7 +190,6 @@
                         break
                 if not matched:
                     graph.add_missing(dist, req)
-
     return graph
 
 
diff --git a/distutils2/dist.py b/distutils2/dist.py
--- a/distutils2/dist.py
+++ b/distutils2/dist.py
@@ -227,14 +227,14 @@
             options = attrs.get('options')
             if options is not None:
                 del attrs['options']
-                for (command, cmd_options) in options.items():
+                for (command, cmd_options) in options.iteritems():
                     opt_dict = self.get_option_dict(command)
-                    for (opt, val) in cmd_options.items():
+                    for (opt, val) in cmd_options.iteritems():
                         opt_dict[opt] = ("setup script", val)
 
             # Now work on the rest of the attributes.  Any attribute that's
             # not already defined is invalid!
-            for key, val in attrs.items():
+            for key, val in attrs.iteritems():
                 if self.metadata.is_metadata_field(key):
                     self.metadata[key] = val
                 elif hasattr(self, key):
@@ -279,8 +279,7 @@
         from pprint import pformat
 
         if commands is None:             # dump all command option dicts
-            commands = self.command_options.keys()
-            commands.sort()
+            commands = sorted(self.command_options)
 
         if header is not None:
             self.announce(indent + header)
@@ -477,7 +476,7 @@
         # Put the options from the command line into their official
         # holding pen, the 'command_options' dictionary.
         opt_dict = self.get_option_dict(command)
-        for (name, value) in vars(opts).items():
+        for (name, value) in vars(opts).iteritems():
             opt_dict[name] = ("command line", value)
 
         return args
@@ -679,7 +678,7 @@
 
         logger.debug("  setting options for '%s' command:" % command_name)
 
-        for (option, (source, value)) in option_dict.items():
+        for (option, (source, value)) in option_dict.iteritems():
             logger.debug("    %s = %s (from %s)" % (option, value, source))
             try:
                 bool_opts = [x.replace('-', '_')
diff --git a/distutils2/fancy_getopt.py b/distutils2/fancy_getopt.py
--- a/distutils2/fancy_getopt.py
+++ b/distutils2/fancy_getopt.py
@@ -107,7 +107,7 @@
 
     def _check_alias_dict (self, aliases, what):
         assert isinstance(aliases, dict)
-        for (alias, opt) in aliases.items():
+        for (alias, opt) in aliases.iteritems():
             if alias not in self.option_index:
                 raise DistutilsGetoptError, \
                       ("invalid %s '%s': "
diff --git a/distutils2/index/dist.py b/distutils2/index/dist.py
--- a/distutils2/index/dist.py
+++ b/distutils2/index/dist.py
@@ -101,7 +101,7 @@
     def is_final(self):
         """proxy to version.is_final"""
         return self.version.is_final
-    
+
     def fetch_distributions(self):
         if self.dists is None:
             self._index.get_distributions(self.name, '%s' % self.version)
@@ -127,7 +127,7 @@
             self.dists[dist_type] = DistInfo(self, dist_type,
                                              index=self._index, **params)
         if python_version:
-            self.dists[dist_type].python_version = python_version 
+            self.dists[dist_type].python_version = python_version
 
     def get_distribution(self, dist_type=None, prefer_source=True):
         """Return a distribution.
@@ -302,7 +302,7 @@
 
     def unpack(self, path=None):
         """Unpack the distribution to the given path.
-        
+
         If not destination is given, creates a temporary location.
 
         Returns the location of the extracted files (root).
@@ -310,10 +310,10 @@
         if not self._unpacked_dir:
             if path is None:
                 path = tempfile.mkdtemp()
-            
+
             filename = self.download()
             content_type = mimetypes.guess_type(filename)[0]
-     
+
             if (content_type == 'application/zip'
                 or filename.endswith('.zip')
                 or filename.endswith('.pybundle')
@@ -351,7 +351,7 @@
     """
     def __init__(self, name, releases=None, contains_hidden=False, index=None):
         self.set_index(index)
-        self.releases = [] 
+        self.releases = []
         self.name = name
         self.contains_hidden = contains_hidden
         if releases:
@@ -376,6 +376,8 @@
         """
         predicate = get_version_predicate(requirements)
         releases = self.filter(predicate)
+        if len(releases) == 0:
+            return None
         releases.sort_releases(prefer_final, reverse=True)
         return releases[0]
 
@@ -404,11 +406,11 @@
                 raise ValueError("%s is not the same project than %s" %
                                  (release.name, self.name))
             version = '%s' % release.version
-                
+
             if not version in self.get_versions():
                 # append only if not already exists
                 self.releases.append(release)
-            for dist in release.dists.values():
+            for dist in release.dists.itervalues():
                 for url in dist.urls:
                     self.add_release(version, dist.dist_type, **url)
         else:
@@ -445,8 +447,7 @@
             reverse=reverse, *args, **kwargs)
 
     def get_release(self, version):
-        """Return a release from it's version.
-        """
+        """Return a release from its version."""
         matches = [r for r in self.releases if "%s" % r.version == version]
         if len(matches) != 1:
             raise KeyError(version)
diff --git a/distutils2/index/simple.py b/distutils2/index/simple.py
--- a/distutils2/index/simple.py
+++ b/distutils2/index/simple.py
@@ -1,7 +1,7 @@
 """index.simple
 
 Contains the class "SimpleIndexCrawler", a simple spider to find and retrieve
-distributions on the Python Package Index, using it's "simple" API,
+distributions on the Python Package Index, using its "simple" API,
 avalaible at http://pypi.python.org/simple/
 """
 from fnmatch import translate
@@ -14,6 +14,7 @@
 import logging
 import os
 
+from distutils2 import logger
 from distutils2.index.base import BaseClient
 from distutils2.index.dist import (ReleasesList, EXTENSIONS,
                                    get_infos_from_url, MD5_HASH)
@@ -167,6 +168,7 @@
         if predicate.name.lower() in self._projects and not force_update:
             return self._projects.get(predicate.name.lower())
         prefer_final = self._get_prefer_final(prefer_final)
+        logger.info('Reading info on PyPI about %s' % predicate.name)
         self._process_index_page(predicate.name)
 
         if predicate.name.lower() not in self._projects:
diff --git a/distutils2/index/wrapper.py b/distutils2/index/wrapper.py
--- a/distutils2/index/wrapper.py
+++ b/distutils2/index/wrapper.py
@@ -1,5 +1,4 @@
-import xmlrpc
-import simple
+from distutils2.index import simple, xmlrpc
 
 _WRAPPER_MAPPINGS = {'get_release': 'simple',
                      'get_releases': 'simple',
@@ -58,7 +57,7 @@
 
         # instantiate the classes and set their _project attribute to the one
         # of the wrapper.
-        for name, cls in index_classes.items():
+        for name, cls in index_classes.iteritems():
             obj = self._indexes.setdefault(name, cls())
             obj._projects = self._projects
             obj._index = self
diff --git a/distutils2/index/xmlrpc.py b/distutils2/index/xmlrpc.py
--- a/distutils2/index/xmlrpc.py
+++ b/distutils2/index/xmlrpc.py
@@ -159,8 +159,15 @@
                     index=self._index))
             except IrrationalVersionError, e:
                 logging.warn("Irrational version error found: %s" % e)
+        return [self._projects[p['name'].lower()] for p in projects]
 
-        return [self._projects[p['name'].lower()] for p in projects]
+    def get_all_projects(self):
+        """Return the list of all projects registered in the package index"""
+        projects = self.proxy.list_packages()
+        for name in projects:
+            self.get_releases(name, show_hidden=True)
+
+        return [self._projects[name.lower()] for name in set(projects)]
 
     @property
     def proxy(self):
diff --git a/distutils2/install.py b/distutils2/install.py
--- a/distutils2/install.py
+++ b/distutils2/install.py
@@ -123,13 +123,13 @@
     try:
         if install:
             installed_files = install_dists(install, install_path)  # install to tmp first
-        for files in temp_files.values():
+        for files in temp_files.itervalues():
             for old, new in files:
                 os.remove(new)
 
     except Exception,e:
         # if an error occurs, put back the files in the good place.
-        for files in temp_files.values():
+        for files in temp_files.itervalues():
             for old, new in files:
                 shutil.move(new, old)
 
@@ -183,7 +183,7 @@
     infos = {'install': [], 'remove': [], 'conflict': []}
 
     # Get what the missing deps are
-    for dists in depgraph.missing.values():
+    for dists in depgraph.missing.itervalues():
         if dists:
             logging.info("missing dependencies found, installing them")
             # we have missing deps
@@ -203,7 +203,7 @@
     """extends the lists contained in the `info` dict with those contained
     in the `new_info` one
     """
-    for key, value in infos.items():
+    for key, value in infos.iteritems():
         if key in new_infos:
             infos[key].extend(new_infos[key])
 
diff --git a/distutils2/metadata.py b/distutils2/metadata.py
--- a/distutils2/metadata.py
+++ b/distutils2/metadata.py
@@ -195,8 +195,10 @@
     # also document the mapping API and UNKNOWN default key
 
     def __init__(self, path=None, platform_dependent=False,
-                 execution_context=None, fileobj=None, mapping=None):
+                 execution_context=None, fileobj=None, mapping=None,
+                 display_warnings=False):
         self._fields = {}
+        self.display_warnings = display_warnings
         self.version = None
         self.docutils_support = _HAS_DOCUTILS
         self.platform_dependent = platform_dependent
@@ -387,21 +389,22 @@
             else:
                 value = []
 
-        if name in _PREDICATE_FIELDS and value is not None:
-            for v in value:
-                # check that the values are valid predicates
-                if not is_valid_predicate(v.split(';')[0]):
-                    logger.warn('"%s" is not a valid predicate (field "%s")' %
-                         (v, name))
-        # FIXME this rejects UNKNOWN, is that right?
-        elif name in _VERSIONS_FIELDS and value is not None:
-            if not is_valid_versions(value):
-                logger.warn('"%s" is not a valid version (field "%s")' %
-                     (value, name))
-        elif name in _VERSION_FIELDS and value is not None:
-            if not is_valid_version(value):
-                logger.warn('"%s" is not a valid version (field "%s")' %
-                     (value, name))
+        if self.display_warnings:
+            if name in _PREDICATE_FIELDS and value is not None:
+                for v in value:
+                    # check that the values are valid predicates
+                    if not is_valid_predicate(v.split(';')[0]):
+                        logger.warn('"%s" is not a valid predicate (field "%s")' %
+                            (v, name))
+            # FIXME this rejects UNKNOWN, is that right?
+            elif name in _VERSIONS_FIELDS and value is not None:
+                if not is_valid_versions(value):
+                    logger.warn('"%s" is not a valid version (field "%s")' %
+                        (value, name))
+            elif name in _VERSION_FIELDS and value is not None:
+                if not is_valid_version(value):
+                    logger.warn('"%s" is not a valid version (field "%s")' %
+                        (value, name))
 
         if name in _UNICODEFIELDS:
             value = self._encode_field(value)
diff --git a/distutils2/mkcfg.py b/distutils2/mkcfg.py
--- a/distutils2/mkcfg.py
+++ b/distutils2/mkcfg.py
@@ -367,7 +367,7 @@
         if not trove:
             return
 
-        for key in sorted(trove.keys()):
+        for key in sorted(trove):
             if len(trove[key]) == 0:
                 if ask_yn('Add "%s"' % desc[4:] + ' :: ' + key, 'n') == 'y':
                     classifiers[desc[4:] + ' :: ' + key] = 1
@@ -455,7 +455,7 @@
                            "number.")
 
     def _dotted_packages(self, data):
-        packages = sorted(data.keys())
+        packages = sorted(data)
         modified_pkgs = []
         for pkg in packages:
             pkg = pkg.lstrip('./')
diff --git a/distutils2/run.py b/distutils2/run.py
--- a/distutils2/run.py
+++ b/distutils2/run.py
@@ -7,6 +7,8 @@
                                DistutilsError, CCompilerError)
 from distutils2.dist import Distribution
 from distutils2 import __version__
+from distutils2._backport.pkgutil import get_distributions, get_distribution
+from distutils2.depgraph import generate_graph
 
 # This is a barebones help message generated displayed when the user
 # runs the setup script with no arguments at all.  More useful help
@@ -116,20 +118,64 @@
     """Main entry point for Distutils2"""
     parser = OptionParser()
     parser.disable_interspersed_args()
+    parser.usage = '%prog [options] cmd1 cmd2 ..'
+
     parser.add_option("-v", "--version",
                   action="store_true", dest="version", default=False,
                   help="Prints out the version of Distutils2 and exits.")
 
+    parser.add_option("-s", "--search",
+                  action="store", dest="search", default=None,
+                  help="Search for installed distributions.")
+
+    parser.add_option("-g", "--graph",
+                  action="store", dest="graph", default=None,
+                  help="Display the graph for a given installed distribution.")
+
+    parser.add_option("-f", "--full-graph",
+                  action="store_true", dest="fgraph", default=False,
+                  help="Display the full graph for installed distributions.")
+
     options, args = parser.parse_args()
     if options.version:
         print('Distutils2 %s' % __version__)
 #        sys.exit(0)
 
+    if options.search is not None:
+        search = options.search.lower()
+        for dist in get_distributions(use_egg_info=True):
+            name = dist.name.lower()
+            if search in name:
+                print('%s %s at %s' % (dist.name, dist.metadata['version'],
+                                     dist.path))
+
+        sys.exit(0)
+
+    if options.graph is not None:
+        name = options.graph
+        dist = get_distribution(name, use_egg_info=True)
+        if dist is None:
+            print('Distribution not found.')
+        else:
+            dists = get_distributions(use_egg_info=True)
+            graph = generate_graph(dists)
+            print(graph.repr_node(dist))
+
+        sys.exit(0)
+
+    if options.fgraph:
+        dists = get_distributions(use_egg_info=True)
+        graph = generate_graph(dists)
+        print(graph)
+        sys.exit(0)
+
     if len(args) == 0:
         parser.print_help()
+        sys.exit(0)
 
     return commands_main()
 #    sys.exit(0)
 
+
 if __name__ == '__main__':
     main()
diff --git a/distutils2/tests/pypi_server.py b/distutils2/tests/pypi_server.py
--- a/distutils2/tests/pypi_server.py
+++ b/distutils2/tests/pypi_server.py
@@ -400,7 +400,7 @@
                 self._dists.append(dist)
         return [r.search_result() for r in results]
 
-    def list_package(self):
+    def list_packages(self):
         return [d.name for d in self._dists]
 
     def package_releases(self, package_name, show_hidden=False):
diff --git a/distutils2/tests/test_command_bdist.py b/distutils2/tests/test_command_bdist.py
--- a/distutils2/tests/test_command_bdist.py
+++ b/distutils2/tests/test_command_bdist.py
@@ -40,10 +40,9 @@
         # XXX an explicit list in bdist is
         # not the best way to  bdist_* commands
         # we should add a registry
-        formats = ['zip', 'gztar', 'bztar', 'ztar', 'tar', 'wininst', 'msi']
-        formats.sort()
-        found = cmd.format_command.keys()
-        found.sort()
+        formats = sorted(('zip', 'gztar', 'bztar', 'ztar',
+                          'tar', 'wininst', 'msi'))
+        found = sorted(cmd.format_command)
         self.assertEqual(found, formats)
 
     def test_skip_build(self):
diff --git a/distutils2/tests/test_command_cmd.py b/distutils2/tests/test_command_cmd.py
--- a/distutils2/tests/test_command_cmd.py
+++ b/distutils2/tests/test_command_cmd.py
@@ -66,12 +66,20 @@
         cmd.ensure_string_list('option1')
         self.assertEqual(cmd.option1, ['ok', 'dok'])
 
-        cmd.option2 = ['xxx', 'www']
-        cmd.ensure_string_list('option2')
+        cmd.yes_string_list = ['one', 'two', 'three']
+        cmd.yes_string_list2 = 'ok'
+        cmd.ensure_string_list('yes_string_list')
+        cmd.ensure_string_list('yes_string_list2')
+        self.assertEqual(cmd.yes_string_list, ['one', 'two', 'three'])
+        self.assertEqual(cmd.yes_string_list2, ['ok'])
 
-        cmd.option3 = ['ok', 2]
-        self.assertRaises(DistutilsOptionError, cmd.ensure_string_list,
-                          'option3')
+        cmd.not_string_list = ['one', 2, 'three']
+        cmd.not_string_list2 = object()
+        self.assertRaises(DistutilsOptionError,
+                          cmd.ensure_string_list, 'not_string_list')
+
+        self.assertRaises(DistutilsOptionError,
+                          cmd.ensure_string_list, 'not_string_list2')
 
     def test_ensure_filename(self):
         cmd = self.cmd
diff --git a/distutils2/tests/test_command_register.py b/distutils2/tests/test_command_register.py
--- a/distutils2/tests/test_command_register.py
+++ b/distutils2/tests/test_command_register.py
@@ -86,6 +86,8 @@
     def tearDown(self):
         getpass.getpass = self._old_getpass
         urllib2.build_opener = self.old_opener
+        if hasattr(register_module, 'raw_input'):
+            del register_module.raw_input
         super(RegisterTestCase, self).tearDown()
 
     def _get_cmd(self, metadata=None):
@@ -108,7 +110,6 @@
 
         # patching raw_input and getpass.getpass
         # so register gets happy
-        #
         # Here's what we are faking :
         # use your existing login (choice 1.)
         # Username : 'tarek'
@@ -116,11 +117,7 @@
         # Save your login (y/N)? : 'y'
         inputs = RawInputs('1', 'tarek', 'y')
         register_module.raw_input = inputs.__call__
-        # let's run the command
-        try:
-            cmd.run()
-        finally:
-            del register_module.raw_input
+        cmd.run()
 
         # we should have a brand new .pypirc file
         self.assertTrue(os.path.exists(self.rc))
@@ -134,8 +131,8 @@
         # if we run the command again
         def _no_way(prompt=''):
             raise AssertionError(prompt)
+
         register_module.raw_input = _no_way
-
         cmd.show_response = 1
         cmd.run()
 
@@ -164,13 +161,10 @@
         cmd = self._get_cmd()
         inputs = RawInputs('2', 'tarek', 'tarek at ziade.org')
         register_module.raw_input = inputs.__call__
-        try:
-            # let's run the command
-            # FIXME does this send a real request? use a mock server
-            # also, silence self.announce (with LoggingCatcher)
-            cmd.run()
-        finally:
-            del register_module.raw_input
+        # let's run the command
+        # FIXME does this send a real request? use a mock server
+        # also, silence self.announce (with LoggingCatcher)
+        cmd.run()
 
         # we should have send a request
         self.assertTrue(self.conn.reqs, 1)
@@ -184,11 +178,7 @@
         cmd = self._get_cmd()
         inputs = RawInputs('3', 'tarek at ziade.org')
         register_module.raw_input = inputs.__call__
-        try:
-            # let's run the command
-            cmd.run()
-        finally:
-            del register_module.raw_input
+        cmd.run()
 
         # we should have send a request
         self.assertTrue(self.conn.reqs, 1)
@@ -208,6 +198,8 @@
         cmd = self._get_cmd({})
         cmd.ensure_finalized()
         cmd.strict = 1
+        inputs = RawInputs('1', 'tarek', 'y')
+        register_module.raw_input = inputs.__call__
         self.assertRaises(DistutilsSetupError, cmd.run)
 
         # metadata is OK but long_description is broken
@@ -229,22 +221,14 @@
         cmd.strict = 1
         inputs = RawInputs('1', 'tarek', 'y')
         register_module.raw_input = inputs.__call__
-        # let's run the command
-        try:
-            cmd.run()
-        finally:
-            del register_module.raw_input
+        cmd.run()
 
         # strict is not by default
         cmd = self._get_cmd()
         cmd.ensure_finalized()
         inputs = RawInputs('1', 'tarek', 'y')
         register_module.raw_input = inputs.__call__
-        # let's run the command
-        try:
-            cmd.run()
-        finally:
-            del register_module.raw_input
+        cmd.run()
 
     def test_register_pep345(self):
         cmd = self._get_cmd({})
diff --git a/distutils2/tests/test_command_sdist.py b/distutils2/tests/test_command_sdist.py
--- a/distutils2/tests/test_command_sdist.py
+++ b/distutils2/tests/test_command_sdist.py
@@ -363,7 +363,7 @@
         finally:
             f.close()
 
-        self.assertEquals(len(manifest), 4)
+        self.assertEqual(len(manifest), 4)
 
         # adding a file
         self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
@@ -383,7 +383,7 @@
             f.close()
 
         # do we have the new file in MANIFEST ?
-        self.assertEquals(len(manifest2), 5)
+        self.assertEqual(len(manifest2), 5)
         self.assertIn('doc2.txt', manifest2[-1])
 
     def test_manifest_marker(self):
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
@@ -204,7 +204,7 @@
                          'FooBarBazTest')
 
         # did the README got loaded ?
-        self.assertEquals(dist.metadata['description'], 'yeah')
+        self.assertEqual(dist.metadata['description'], 'yeah')
 
         # do we have the D Compiler enabled ?
         from distutils2.compiler import new_compiler, _COMPILERS
@@ -238,7 +238,7 @@
         finally:
             sys.argv[:] = old_sys
 
-        self.assertEquals(dist.foo_was_here, 1)
+        self.assertEqual(dist.foo_was_here, 1)
 
 
 def test_suite():
diff --git a/distutils2/tests/test_cygwinccompiler.py b/distutils2/tests/test_cygwinccompiler.py
--- a/distutils2/tests/test_cygwinccompiler.py
+++ b/distutils2/tests/test_cygwinccompiler.py
@@ -8,10 +8,9 @@
 from distutils2.tests import captured_stdout
 
 from distutils2.compiler import cygwinccompiler
-from distutils2.compiler.cygwinccompiler import (CygwinCCompiler, check_config_h,
-                                       CONFIG_H_OK, CONFIG_H_NOTOK,
-                                       CONFIG_H_UNCERTAIN, get_versions,
-                                       get_msvcr, RE_VERSION)
+from distutils2.compiler.cygwinccompiler import (
+    CygwinCCompiler, check_config_h, get_msvcr,
+    CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_UNCERTAIN)
 from distutils2.util import get_compiler_versions
 from distutils2.tests import unittest, support
 
diff --git a/distutils2/tests/test_index_dist.py b/distutils2/tests/test_index_dist.py
--- a/distutils2/tests/test_index_dist.py
+++ b/distutils2/tests/test_index_dist.py
@@ -237,6 +237,10 @@
 #        dists.sort_distributions(prefer_source=True)
 #        self.assertEqual(fb2_binary, dists[0])
 
+    def test_get_last(self):
+        dists = ReleasesList('Foo')
+        self.assertEqual(dists.get_last('Foo 1.0'), None)
+
 
 def test_suite():
     suite = unittest.TestSuite()
diff --git a/distutils2/tests/test_index_simple.py b/distutils2/tests/test_index_simple.py
--- a/distutils2/tests/test_index_simple.py
+++ b/distutils2/tests/test_index_simple.py
@@ -293,8 +293,8 @@
 <a href="../download" rel="download">link2</a>
 <a href="../simpleurl">link2</a>
         """
-        found_links = dict(crawler._default_link_matcher(content,
-                                                         base_url)).keys()
+        found_links = set(dict(crawler._default_link_matcher(content,
+                                                             base_url)))
         self.assertIn('http://example.org/some/homepage', found_links)
         self.assertIn('http://example.org/some/simpleurl', found_links)
         self.assertIn('http://example.org/some/download', found_links)
diff --git a/distutils2/tests/test_index_xmlrpc.py b/distutils2/tests/test_index_xmlrpc.py
--- a/distutils2/tests/test_index_xmlrpc.py
+++ b/distutils2/tests/test_index_xmlrpc.py
@@ -25,6 +25,24 @@
                           invalid="test")
 
     @use_xmlrpc_server()
+    def test_get_all_projects(self, server):
+        client = self._get_client(server)
+        server.xmlrpc.set_distributions([
+            {'name': 'FooBar', 'version': '1.1'},
+            {'name': 'FooBar', 'version': '1.2'},
+            {'name': 'Foo', 'version': '1.1'},
+        ])
+        results = client.get_all_projects()
+        self.assertEqual(2, len(results))
+
+        # check we do have two releases for Foobar's project
+        self.assertEqual(2, len(results[0].releases))
+
+        names = [r.name for r in results]
+        self.assertIn('FooBar', names)
+        self.assertIn('Foo', names)
+
+    @use_xmlrpc_server()
     def test_get_releases(self, server):
         client = self._get_client(server)
         server.xmlrpc.set_distributions([
diff --git a/distutils2/tests/test_install.py b/distutils2/tests/test_install.py
--- a/distutils2/tests/test_install.py
+++ b/distutils2/tests/test_install.py
@@ -214,7 +214,7 @@
 
         for dict1, dict2, expect in tests:
             install._update_infos(dict1, dict2)
-            for key in expect.keys():
+            for key in expect:
                 self.assertEqual(expect[key], dict1[key])
 
     def test_install_dists_rollback(self):
@@ -284,7 +284,7 @@
         install.install_from_infos(install=to_install,
                                          install_path=install_path)
         for dist in to_install:
-            self.assertEquals(dist.install_called_with, (install_path,))
+            self.assertEqual(dist.install_called_with, (install_path,))
 
 def test_suite():
     suite = unittest.TestSuite()
diff --git a/distutils2/tests/test_metadata.py b/distutils2/tests/test_metadata.py
--- a/distutils2/tests/test_metadata.py
+++ b/distutils2/tests/test_metadata.py
@@ -117,7 +117,7 @@
         metadata['Name'] = "baz; sys.platform == 'blah'"
         # FIXME is None or 'UNKNOWN' correct here?
         # where is that documented?
-        self.assertEquals(metadata['Name'], None)
+        self.assertEqual(metadata['Name'], None)
 
         # test with context
         context = {'sys.platform': 'okook'}
diff --git a/distutils2/tests/test_mixin2to3.py b/distutils2/tests/test_mixin2to3.py
--- a/distutils2/tests/test_mixin2to3.py
+++ b/distutils2/tests/test_mixin2to3.py
@@ -25,7 +25,7 @@
         converted_code_content = "print('test')\n"
         new_code_content = "".join(open(code_name).readlines())
 
-        self.assertEquals(new_code_content, converted_code_content)
+        self.assertEqual(new_code_content, converted_code_content)
 
     @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
     def test_doctests_only(self):
@@ -45,7 +45,7 @@
         converted_doctest_content = '\n'.join(converted_doctest_content)
         new_doctest_content = "".join(open(doctest_name).readlines())
 
-        self.assertEquals(new_doctest_content, converted_doctest_content)
+        self.assertEqual(new_doctest_content, converted_doctest_content)
 
     @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
     def test_additional_fixers(self):
@@ -64,7 +64,7 @@
                             fixers=['distutils2.tests.fixer'])
         converted_code_content = "isinstance(x, T)"
         new_code_content = "".join(open(code_name).readlines())
-        self.assertEquals(new_code_content, converted_code_content)
+        self.assertEqual(new_code_content, converted_code_content)
 
 def test_suite():
     return unittest.makeSuite(Mixin2to3TestCase)
diff --git a/distutils2/tests/test_util.py b/distutils2/tests/test_util.py
--- a/distutils2/tests/test_util.py
+++ b/distutils2/tests/test_util.py
@@ -144,7 +144,7 @@
         os.path.join = _join
 
         self.assertEqual(convert_path('/home/to/my/stuff'),
-                          '/home/to/my/stuff')
+                         '/home/to/my/stuff')
 
         # win
         os.sep = '\\'
@@ -156,9 +156,9 @@
         self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/')
 
         self.assertEqual(convert_path('home/to/my/stuff'),
-                          'home\\to\\my\\stuff')
+                         'home\\to\\my\\stuff')
         self.assertEqual(convert_path('.'),
-                          os.curdir)
+                         os.curdir)
 
     def test_change_root(self):
         # linux/mac
@@ -171,9 +171,9 @@
         os.path.join = _join
 
         self.assertEqual(change_root('/root', '/old/its/here'),
-                          '/root/old/its/here')
+                         '/root/old/its/here')
         self.assertEqual(change_root('/root', 'its/here'),
-                          '/root/its/here')
+                         '/root/its/here')
 
         # windows
         os.name = 'nt'
@@ -190,9 +190,9 @@
         os.path.join = _join
 
         self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'),
-                          'c:\\root\\old\\its\\here')
+                         'c:\\root\\old\\its\\here')
         self.assertEqual(change_root('c:\\root', 'its\\here'),
-                          'c:\\root\\its\\here')
+                         'c:\\root\\its\\here')
 
         # BugsBunny os (it's a great os)
         os.name = 'BugsBunny'
@@ -203,7 +203,7 @@
 
     def test_split_quoted(self):
         self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'),
-                          ['one', 'two', 'three', 'four'])
+                         ['one', 'two', 'three', 'four'])
 
     def test_strtobool(self):
         yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1')
@@ -383,7 +383,7 @@
         run_2to3([file_name])
         new_content = "".join(file_handle.read())
         file_handle.close()
-        self.assertEquals(new_content, converted_content)
+        self.assertEqual(new_content, converted_content)
 
     @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
     def test_run_2to3_on_doctests(self):
@@ -399,7 +399,7 @@
         run_2to3([file_name], doctests_only=True)
         new_content = "".join(file_handle.readlines())
         file_handle.close()
-        self.assertEquals(new_content, converted_content)
+        self.assertEqual(new_content, converted_content)
 
     def test_nt_quote_args(self):
 
diff --git a/distutils2/tests/test_version.py b/distutils2/tests/test_version.py
--- a/distutils2/tests/test_version.py
+++ b/distutils2/tests/test_version.py
@@ -61,9 +61,9 @@
 
     def test_huge_version(self):
 
-        self.assertEquals(str(V('1980.0')), '1980.0')
+        self.assertEqual(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')
+        self.assertEqual(str(V('1981.0', error_on_huge_major_num=False)), '1981.0')
 
     def test_comparison(self):
         r"""
@@ -196,9 +196,20 @@
 
         self.assertRaises(ValueError, VersionPredicate, '')
 
+        self.assertTrue(VersionPredicate('Hey 2.5').match('2.5.1'))
+
         # XXX need to silent the micro version in this case
         #assert not VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3')
 
+
+        # Make sure a predicate that ends with a number works
+        self.assertTrue(VersionPredicate('virtualenv5 (1.0)').match('1.0'))
+        self.assertTrue(VersionPredicate('virtualenv5').match('1.0'))
+        self.assertTrue(VersionPredicate('vi5two').match('1.0'))
+        self.assertTrue(VersionPredicate('5two').match('1.0'))
+        self.assertTrue(VersionPredicate('vi5two 1.0').match('1.0'))
+        self.assertTrue(VersionPredicate('5two 1.0').match('1.0'))
+
         # test repr
         for predicate in predicates:
             self.assertEqual(str(VersionPredicate(predicate)), predicate)
@@ -220,12 +231,13 @@
         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',
+        self.assertEqual(V('1.0')._parse_numdots('1.0', '1.0',
                                                   pad_zeros_length=3),
                           [1, 0, 0])
 
diff --git a/distutils2/version.py b/distutils2/version.py
--- a/distutils2/version.py
+++ b/distutils2/version.py
@@ -322,8 +322,9 @@
     return None
 
 
-_PREDICATE = re.compile(r"(?i)^\s*([a-z_][\sa-zA-Z_-]*(?:\.[a-z_]\w*)*)(.*)")
-_VERSIONS = re.compile(r"^\s*\((.*)\)\s*$")
+# A predicate is: "ProjectName (VERSION1, VERSION2, ..)
+_PREDICATE = re.compile(r"(?i)^\s*(\w[\s\w-]*(?:\.\w*)*)(.*)")
+_VERSIONS = re.compile(r"^\s*\((?P<versions>.*)\)\s*$|^\s*(?P<versions2>.*)\s*$")
 _PLAIN_VERSIONS = re.compile(r"^\s*(.*)\s*$")
 _SPLIT_CMP = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$")
 
@@ -358,14 +359,25 @@
 
         name, predicates = match.groups()
         self.name = name.strip()
-        predicates = predicates.strip()
-        predicates = _VERSIONS.match(predicates)
-        if predicates is not None:
-            predicates = predicates.groups()[0]
-            self.predicates = [_split_predicate(pred.strip())
-                               for pred in predicates.split(',')]
+        self.predicates = []
+        if predicates is None:
+            return
+
+        predicates = _VERSIONS.match(predicates.strip())
+        if predicates is None:
+            return
+
+        predicates = predicates.groupdict()
+        if predicates['versions'] is not None:
+            versions = predicates['versions']
         else:
-            self.predicates = []
+            versions = predicates.get('versions2')
+
+        if versions is not None:
+            for version in versions.split(','):
+                if version.strip() == '':
+                    continue
+                self.predicates.append(_split_predicate(version))
 
     def match(self, version):
         """Check if the provided version matches the predicates."""
diff --git a/docs/source/install/index.rst b/docs/source/install/index.rst
--- a/docs/source/install/index.rst
+++ b/docs/source/install/index.rst
@@ -927,15 +927,34 @@
 GNU C / Cygwin / MinGW
 ^^^^^^^^^^^^^^^^^^^^^^
 
-These instructions only apply if you're using a version of Python prior  to
-2.4.1 with a MinGW prior to 3.0.0 (with binutils-2.13.90-20030111-1).
-
 This section describes the necessary steps to use Distutils with the GNU C/C++
 compilers in their Cygwin and MinGW distributions. [#]_ For a Python interpreter
 that was built with Cygwin, everything should work without any of these
 following steps.
 
-These compilers require some special libraries. This task is more complex than
+Not all extensions can be built with MinGW or Cygwin, but many can.  Extensions
+most likely to not work are those that use C++ or depend on Microsoft Visual C
+extensions.
+
+To let Distutils compile your extension with Cygwin you have to type::
+
+   python setup.py build --compiler=cygwin
+
+and for Cygwin in no-cygwin mode [#]_ or for MinGW type::
+
+   python setup.py build --compiler=mingw32
+
+If you want to use any of these options/compilers as default, you should
+consider writing it in your personal or system-wide configuration file for
+Distutils (see section :ref:`inst-config-files`.)
+
+Older Versions of Python and MinGW
+""""""""""""""""""""""""""""""""""
+The following instructions only apply if you're using a version of Python
+inferior to 2.4.1 with a MinGW inferior to 3.0.0 (with
+binutils-2.13.90-20030111-1).
+
+These compilers require some special libraries.  This task is more complex than
 for Borland's C++, because there is no program to convert the library.  First
 you have to create a list of symbols which the Python DLL exports. (You can find
 a good program for this task at
@@ -965,18 +984,6 @@
 them too. The converted files have to reside in the same directories as the
 normal libraries do.
 
-To let Distutils compile your extension with Cygwin you now have to type ::
-
-   python setup.py build --compiler cygwin
-
-and for Cygwin in no-cygwin mode [#]_ or for MinGW type::
-
-   python setup.py build --compiler mingw32
-
-If you want to use any of these options/compilers as default, you should
-consider to write it in your personal or system-wide configuration file for
-Distutils (see section :ref:`inst-config-files`.)
-
 
 .. seealso::
 
diff --git a/docs/source/library/distutils2.index.xmlrpc.rst b/docs/source/library/distutils2.index.xmlrpc.rst
--- a/docs/source/library/distutils2.index.xmlrpc.rst
+++ b/docs/source/library/distutils2.index.xmlrpc.rst
@@ -90,7 +90,7 @@
     <ReleaseInfo FooBar 1.1>
 
 Assuming we already have a :class:`distutils2.index.ReleaseInfo` object defined,
-it's possible to pass it ot the xmlrpc client to retrieve and complete it's
+it's possible to pass it ot the xmlrpc client to retrieve and complete its
 metadata::
 
     >>> foobar11 = ReleaseInfo("FooBar", "1.1")
diff --git a/docs/source/library/distutils2.rst b/docs/source/library/distutils2.rst
--- a/docs/source/library/distutils2.rst
+++ b/docs/source/library/distutils2.rst
@@ -24,6 +24,7 @@
     distutils2.version
     distutils2.metadata
     distutils2.depgraph
+    distutils2.install
     distutils2.index
     distutils2.tests.pypi_server
 
diff --git a/runtests.py b/runtests.py
--- a/runtests.py
+++ b/runtests.py
@@ -64,7 +64,7 @@
         # running coverage 2.x
         cov.cache = COVERAGE_FILE
         cov.restore()
-        morfs = [m for m in cov.cexecuted.keys() if "distutils2" in m]
+        morfs = [m for m in cov.cexecuted if "distutils2" in m]
 
     prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"]
     prefixes += ignore_prefixes(unittest)
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -189,9 +189,10 @@
 
     return exts
 
-setup_kwargs = {}
+setup_kwargs = {'scripts': ['distutils2/pysetup']}
+
 if sys.version < '2.6':
-    setup_kwargs['scripts'] = ['distutils2/mkcfg.py']
+    setup_kwargs['scripts'].append('distutils2/mkcfg.py')
 
 if sys.version < '2.5':
     setup_kwargs['ext_modules'] = prepare_hashlib_extensions()

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


More information about the Python-checkins mailing list