[Python-checkins] distutils2: merged upstream

tarek.ziade python-checkins at python.org
Sun Sep 19 10:20:22 CEST 2010


tarek.ziade pushed cc240818f9f9 to distutils2:

http://hg.python.org/distutils2/rev/cc240818f9f9
changeset:   621:cc240818f9f9
parent:      620:b81715424b1c
parent:      556:272a3087dd6e
user:        Konrad Delong <konryd at gmail.com>
date:        Fri Aug 13 18:19:28 2010 +0200
summary:     merged upstream
files:       src/distutils2/install_with_deps.py, src/distutils2/tests/test_install_with_deps.py

diff --git a/docs/source/library/distutils2.version.rst b/docs/source/library/distutils2.version.rst
--- a/docs/source/library/distutils2.version.rst
+++ b/docs/source/library/distutils2.version.rst
@@ -5,12 +5,12 @@
 Distutils2 ships with a python package capable to work with version numbers.
 It's an implementation of version specifiers `as defined in PEP 345
 <http://www.python.org/dev/peps/pep-0345/#version-specifiers>`_ about
-Metadata.
+Metadatas.
 
-`distutils2.version.NormalizedVersion`
-======================================
+NormalizedVersion 
+==================
 
-A Normalized version corresponds to a specific version of a distribution, as
+A Normalized version is a specific version of a distribution, as
 described in the PEP 345. So, you can work with the `NormalizedVersion` like
 this::
 
@@ -31,11 +31,12 @@
 
 NormalizedVersion is used internally by `VersionPredicate` to do his stuff.
 
-`distutils2.version.suggest_normalized_version`
------------------------------------------------
+Suggest a normalized version
+----------------------------
 
-You also can let the normalized version be suggested to you, using the
-`suggest_normalized_version` function::
+Before this version scheme, there was existing others ones. Distutils2 provides
+a tool that suggests a normalized version: the `suggest_normalized_version` 
+function::
 
     >>> suggest_normalized_version('2.1-rc1') 
     2.1c1
@@ -46,8 +47,8 @@
     >>> print suggest_normalized_version('not a version')
     None
 
-`distutils2.version.VersionPredicate`
-=====================================
+Dealing with version predicates 
+===============================
 
 `VersionPredicate` knows how to parse stuff like "ProjectName (>=version)", the
 class also provides a `match` method to test if a version number is the version
@@ -59,6 +60,11 @@
     >>> version.match("1.1.1")
     True
 
-`is_valid_predicate`
---------------------
+You could test if a predicate is a valid one, usng the `is_valid_predicate`
+function::
 
+    >>> is_valid_predicate('FooBar (1.1)')
+    True
+    >>> is_valid_predicate('FooBar (+1.1)')
+    False
+
diff --git a/src/distutils2/errors.py b/src/distutils2/errors.py
--- a/src/distutils2/errors.py
+++ b/src/distutils2/errors.py
@@ -80,6 +80,10 @@
     """Byte compile error."""
 
 
+class DistutilsIndexError(DistutilsError):
+    """Any problem occuring during using the indexes."""
+
+
 # Exception classes used by the CCompiler implementation classes
 class CCompilerError(Exception):
     """Some compile/link operation failed."""
diff --git a/src/distutils2/index/errors.py b/src/distutils2/index/errors.py
--- a/src/distutils2/index/errors.py
+++ b/src/distutils2/index/errors.py
@@ -2,30 +2,26 @@
 
 All errors and exceptions raised by PyPiIndex classes.
 """
-from distutils2.errors import DistutilsError
+from distutils2.errors import DistutilsIndexError
 
 
-class IndexesError(DistutilsError):
-    """The base class for errors of the index python package."""
-
-
-class ProjectNotFound(IndexesError):
+class ProjectNotFound(DistutilsIndexError):
     """Project has not been found"""
 
 
-class DistributionNotFound(IndexesError):
+class DistributionNotFound(DistutilsIndexError):
     """The release has not been found"""
 
 
-class ReleaseNotFound(IndexesError):
+class ReleaseNotFound(DistutilsIndexError):
     """The release has not been found"""
 
 
-class CantParseArchiveName(IndexesError):
+class CantParseArchiveName(DistutilsIndexError):
     """An archive name can't be parsed to find distribution name and version"""
 
 
-class DownloadError(IndexesError):
+class DownloadError(DistutilsIndexError):
     """An error has occurs while downloading"""
 
 
@@ -33,13 +29,13 @@
     """Compared hashes does not match"""
 
 
-class UnsupportedHashName(IndexesError):
+class UnsupportedHashName(DistutilsIndexError):
     """A unsupported hashname has been used"""
 
 
-class UnableToDownload(IndexesError):
+class UnableToDownload(DistutilsIndexError):
     """All mirrors have been tried, without success"""
 
 
-class InvalidSearchField(IndexesError):
+class InvalidSearchField(DistutilsIndexError):
     """An invalid search field has been used"""
diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py
--- a/src/distutils2/index/simple.py
+++ b/src/distutils2/index/simple.py
@@ -17,7 +17,7 @@
 from distutils2.index.base import BaseClient
 from distutils2.index.dist import (ReleasesList, EXTENSIONS,
                                    get_infos_from_url, MD5_HASH)
-from distutils2.index.errors import (IndexesError, DownloadError,
+from distutils2.index.errors import (DistutilsIndexError, DownloadError,
                                      UnableToDownload, CantParseArchiveName,
                                      ReleaseNotFound, ProjectNotFound)
 from distutils2.index.mirrors import get_mirrors
@@ -395,7 +395,7 @@
             fp = urllib2.urlopen(request)
         except (ValueError, httplib.InvalidURL), v:
             msg = ' '.join([str(arg) for arg in v.args])
-            raise IndexesError('%s %s' % (url, msg))
+            raise DistutilsIndexError('%s %s' % (url, msg))
         except urllib2.HTTPError, v:
             return v
         except urllib2.URLError, v:
diff --git a/src/distutils2/install_with_deps.py b/src/distutils2/install_tools.py
rename from src/distutils2/install_with_deps.py
rename to src/distutils2/install_tools.py
--- a/src/distutils2/install_with_deps.py
+++ b/src/distutils2/install_tools.py
@@ -15,155 +15,79 @@
 """
 
 
-def get_deps(requirements, index):
-    """Return the dependencies of a project, as a depgraph object.
-
-    Build a :class depgraph.DependencyGraph: for the given requirements
-
-    If the project does not uses Metadata < 1.1, dependencies can't be handled
-    from here, so it returns an empty list of dependencies.
-
-    :param requirements: is a string containing the version predicate to take
-                         the project name and version specifier from.
-    :param index: the index to use for making searches.
-    """
-    deps = []
-    release = index.get_release(requirements)
-    requires = release.metadata['Requires-Dist'] + release.metadata['Requires']
-    deps.append(release) # include the release we are computing deps.
-    for req in requires:
-        deps.extend(get_deps(req, index))
-    return deps
-
-
-def install(requirements, index=None, interactive=True, upgrade=True,
-            prefer_source=True, prefer_final=True):
-    """Given a list of distributions to install, a list of distributions to
-    remove, and a list of conflicts, proceed and do what's needed to be done.
-
-    :param requirements: is a *string* containing the requirements for this
-                         project (for instance "FooBar 1.1" or "BarBaz (<1.2)
-    :param index: If an index is specified, use this one, otherwise, use
-                  :class index.ClientWrapper: to get project metadatas.
-    :param interactive: if set to True, will prompt the user for interactions
-                        of needed. If false, use the default values.
-    :param upgrade: If a project exists in a newer version, does the script
-                    need to install the new one, or keep the already installed
-                    version.
-    :param prefer_source: used to tell if the user prefer source distributions
-                          over built dists.
-    :param prefer_final: if set to true, pick up the "final" versions (eg.
-                         stable) over the beta, alpha (not final) ones.
-    """
-    # get the default index if none is specified
-    if not index:
-        index = wrapper.WrapperClient()
-
-    # check if the project is already installed.
-    installed_release = get_installed_release(requirements)
-
-    # if a version that satisfy the requirements is already installed
-    if installed_release and (interactive or upgrade):
-        new_releases = index.get_releases(requirements)
-        if (new_releases.get_last(requirements).version >
-            installed_release.version):
-            if interactive:
-                # prompt the user to install the last version of the package.
-                # set upgrade here.
-                print "You want to install a package already installed on your"
-                "system. A new version exists, you could just use the version"
-                "you have, or upgrade to the latest version"
-
-                upgrade = raw_input("Do you want to install the most recent one ? (Y/n)") or "Y"
-                if upgrade in ('Y', 'y'):
-                    upgrade = True
-                else:
-                    upgrade = False
-            if not upgrade:
-                return
-
-    # create the depgraph from the dependencies of the release we want to
-    # install
-    graph = generate_graph(get_deps(requirements, index))
-    from ipdb import set_trace
-    set_trace()
-    installed = [] # to uninstall on errors
-    try:
-        for release in graph.adjacency_list:
-            dist = release.get_distribution()
-            install(dist)
-            installed.append(dist)
-            print "%s have been installed on your system" % requirements
-    except:
-        print "an error has occured, uninstalling"
-        for dist in installed:
-            uninstall_dist(dist)
-
 class InstallationException(Exception):
     pass
 
-def get_install_info(requirements, index=None, already_installed=None):
+
+def _update_infos(infos, new_infos):
+    """extends the lists contained in the `info` dict with those contained
+    in the `new_info` one
+    """
+    for key, value in infos.items():
+        if key in new_infos:
+            infos[key].extend(new_infos[key])
+
+
+def get_infos(requirements, index=None, installed=None,
+                     prefer_final=True):
     """Return the informations on what's going to be installed and upgraded.
 
     :param requirements: is a *string* containing the requirements for this
                          project (for instance "FooBar 1.1" or "BarBaz (<1.2)")
     :param index: If an index is specified, use this one, otherwise, use
                   :class index.ClientWrapper: to get project metadatas.
-    :param already_installed: a list of already installed distributions.
+    :param installed: a list of already installed distributions.
+    :param prefer_final: when picking up the releases, prefer a "final" one
+                         over a beta/alpha/etc one.
 
-    The results are returned in a dict. For instance::
+    The results are returned in a dict, containing all the operations
+    needed to install the given requirements::
 
         >>> get_install_info("FooBar (<=1.2)")
         {'install': [<FooBar 1.1>], 'remove': [], 'conflict': []}
 
     Conflict contains all the conflicting distributions, if there is a
     conflict.
-
     """
-    def update_infos(new_infos, infos):
-        for key, value in infos.items():
-            if key in new_infos:
-                infos[key].extend(new_infos[key])
-        return new_infos
 
     if not index:
         index = wrapper.ClientWrapper()
-    logging.info("searching releases for %s" % requirements)
 
-    # 1. get all the releases that match the requirements
+    if not installed:
+        installed = get_distributions()
+
+    # Get all the releases that match the requirements
     try:
         releases = index.get_releases(requirements)
     except (ReleaseNotFound, ProjectNotFound), e:
-       raise InstallationException('Release not found: "%s"' % requirements)
+        raise InstallationException('Release not found: "%s"' % requirements)
 
-    # 2. pick up a release, and try to get the dependency tree
-    release = releases.get_last(requirements)
+    # Pick up a release, and try to get the dependency tree
+    release = releases.get_last(requirements, prefer_final=prefer_final)
+
+    # Iter since we found something without conflicts
     metadata = release.fetch_metadata()
 
-    # 3. get the distributions already_installed on the system
-    # 4. and add the one we want to install
-    if not already_installed:
-        already_installed = get_distributions()
+    # Get the distributions already_installed on the system
+    # and add the one we want to install
 
-    logging.info("fetching %s %s dependencies" % (
-                 release.name, release.version))
-    distributions = already_installed + [release]
+    distributions = installed + [release]
     depgraph = generate_graph(distributions)
 
-    # store all the already_installed packages in a list, in case of rollback.
-    infos = {'install':[], 'remove': [], 'conflict': []}
+    # Store all the already_installed packages in a list, in case of rollback.
+    infos = {'install': [], 'remove': [], 'conflict': []}
 
-    # 5. get what the missing deps are
+    # Get what the missing deps are
     for dists in depgraph.missing.values():
         if dists:
             logging.info("missing dependencies found, installing them")
             # we have missing deps
             for dist in dists:
-                update_infos(get_install_info(dist, index, already_installed),
-                             infos)
+                _update_infos(infos,
+                             get_infos(dist, index, installed))
 
-    # 6. fill in the infos
-    existing = [d for d in already_installed if d.name == release.name]
+    # Fill in the infos
+    existing = [d for d in installed if d.name == release.name]
     if existing:
         infos['remove'].append(existing[0])
         infos['conflict'].extend(depgraph.reverse_list[existing[0]])
diff --git a/src/distutils2/tests/test_install_with_deps.py b/src/distutils2/tests/test_install_tools.py
rename from src/distutils2/tests/test_install_with_deps.py
rename to src/distutils2/tests/test_install_tools.py
--- a/src/distutils2/tests/test_install_with_deps.py
+++ b/src/distutils2/tests/test_install_tools.py
@@ -4,10 +4,11 @@
 from distutils2.tests import run_unittest
 from distutils2.tests.support import unittest
 from distutils2.index.xmlrpc import Client
-from distutils2.install_with_deps import (get_install_info, 
+from distutils2.install_tools import (get_infos,
                                           InstallationException)
 from distutils2.metadata import DistributionMetadata
 
+
 class FakeDist(object):
     """A fake distribution object, for tests"""
     def __init__(self, name, version, deps):
@@ -20,16 +21,25 @@
     def __repr__(self):
         return '<FakeDist %s>' % self.name
 
+
 def get_fake_dists(dists):
     objects = []
     for (name, version, deps) in dists:
-       objects.append(FakeDist(name, version, deps))
+        objects.append(FakeDist(name, version, deps))
     return objects
 
+
 class TestInstallWithDeps(unittest.TestCase):
     def _get_client(self, server, *args, **kwargs):
         return Client(server.full_address, *args, **kwargs)
 
+    def _get_results(self, output):
+        """return a list of results"""
+        installed = [(o.name, '%s' % o.version) for o in output['install']]
+        remove = [(o.name, '%s' % o.version) for o in output['remove']]
+        conflict = [(o.name, '%s' % o.version) for o in output['conflict']]
+        return (installed, remove, conflict)
+
     @use_xmlrpc_server()
     def test_existing_deps(self, server):
         # Test that the installer get the dependencies from the metadatas
@@ -55,17 +65,17 @@
              'url': archive_path},
             ])
         installed = get_fake_dists([('bacon', '0.1', []),])
-        output = get_install_info("choxie", index=client, 
-                                  already_installed=installed)
+        output = get_infos("choxie", index=client,
+                           installed=installed)
 
         # we dont have installed bacon as it's already installed on the system.
         self.assertEqual(0, len(output['remove']))
         self.assertEqual(2, len(output['install']))
-        readable_output = [(o.name, '%s' % o.version) 
+        readable_output = [(o.name, '%s' % o.version)
                            for o in output['install']]
         self.assertIn(('towel-stuff', '0.1'), readable_output)
         self.assertIn(('choxie', '2.0.0.9'), readable_output)
-   
+
     @use_xmlrpc_server()
     def test_upgrade_existing_deps(self, server):
         # Tests that the existing distributions can be upgraded if needed.
@@ -85,11 +95,11 @@
              'requires_dist': [],
              'url': archive_path},
             ])
-        
-        output = get_install_info("choxie", index=client, already_installed= 
-                         get_fake_dists([('bacon', '0.1', []),]))
+
+        output = get_infos("choxie", index=client, installed=
+                           get_fake_dists([('bacon', '0.1', []),]))
         installed = [(o.name, '%s' % o.version) for o in output['install']]
-        
+
         # we need bacon 0.2, but 0.1 is installed.
         # So we expect to remove 0.1 and to install 0.2 instead.
         remove = [(o.name, '%s' % o.version) for o in output['remove']]
@@ -101,7 +111,7 @@
 
     @use_xmlrpc_server()
     def test_conflicts(self, server):
-        # Tests that conflicts are detected 
+        # Tests that conflicts are detected
         client = self._get_client(server)
         archive_path = '%s/distribution.tar.gz' % server.full_address
         server.xmlrpc.set_distributions([
@@ -118,16 +128,14 @@
              'requires_dist': [],
              'url': archive_path},
             ])
-        already_installed = [('bacon', '0.1', []), 
-                             ('chicken', '1.1', ['bacon (0.1)'])] 
-        output = get_install_info("choxie", index=client, already_installed= 
-                         get_fake_dists(already_installed))
-        
+        already_installed = [('bacon', '0.1', []),
+                             ('chicken', '1.1', ['bacon (0.1)'])]
+        output = get_infos("choxie", index=client, installed=
+                           get_fake_dists(already_installed))
+
         # we need bacon 0.2, but 0.1 is installed.
         # So we expect to remove 0.1 and to install 0.2 instead.
-        installed = [(o.name, '%s' % o.version) for o in output['install']]
-        remove = [(o.name, '%s' % o.version) for o in output['remove']]
-        conflict = [(o.name, '%s' % o.version) for o in output['conflict']]
+        installed, remove, conflict = self._get_results(output)
         self.assertIn(('choxie', '2.0.0.9'), installed)
         self.assertIn(('towel-stuff', '0.1'), installed)
         self.assertIn(('bacon', '0.2'), installed)
@@ -139,7 +147,7 @@
         # Test that the isntalled raises an exception if the project does not
         # exists.
         client = self._get_client(server)
-        self.assertRaises(InstallationException, get_install_info, 
+        self.assertRaises(InstallationException, get_infos,
                           'unexistant project', index=client)
 
 

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


More information about the Python-checkins mailing list