[Python-checkins] distutils2: Full support and tests for prefer_source and prefer_final

tarek.ziade python-checkins at python.org
Sun Jul 4 11:48:40 CEST 2010


tarek.ziade pushed b0fa106d089d to distutils2:

http://hg.python.org/distutils2/rev/b0fa106d089d
changeset:   344:b0fa106d089d
user:        Alexis Metaireau <ametaireau at gmail.com>
date:        Fri Jul 02 15:54:00 2010 +0200
summary:     Full support and tests for prefer_source and prefer_final
files:       src/distutils2/pypi/dist.py, src/distutils2/pypi/simple.py, src/distutils2/tests/test_pypi_dist.py, src/distutils2/tests/test_pypi_simple.py

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
@@ -7,12 +7,14 @@
 import urlparse
 import urllib
 import tempfile
+from operator import attrgetter
+
 try:
     import hashlib
 except ImportError:
     from distutils2._backport import hashlib
 
-from distutils2.version import suggest_normalized_version
+from distutils2.version import suggest_normalized_version, NormalizedVersion
 from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName
 
 EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz .egg".split()
@@ -57,26 +59,26 @@
 
         name, version = split_archive_name(archive_name)
         if extension_matched is True:
-            return PyPIDistribution(name, version, url=url, hashname="md5",
-                                    hashval=md5_hash, is_external=is_external)
+            return PyPIDistribution(name, version, url=url, url_hashname="md5",
+                                    url_hashval=md5_hash, url_is_external=is_external)
 
-    def __init__(self, name, version, type=None, url=None, hashname=None,
-                 hashval=None, is_external=True):
+    def __init__(self, name, version, type=None, url=None, url_hashname=None,
+                 url_hashval=None, url_is_external=True):
         """Create a new instance of PyPIDistribution.
 
         :param name: the name of the distribution
         :param version: the version of the distribution
         :param type: the type of the dist (eg. source, bin-*, etc.)
         :param url: URL where we found this distribution
-        :param hashname: the name of the hash we want to use. Refer to the
+        :param url_hashname: the name of the hash we want to use. Refer to the
                          hashlib.new documentation for more information.
-        :param hashval: the hash value.
-        :param is_external: we need to know if the provided url comes from an
+        :param url_hashval: the hash value.
+        :param url_is_external: we need to know if the provided url comes from an
                             index browsing, or from an external resource.
 
         """
         self.name = name
-        self.version = version
+        self.version = NormalizedVersion(version)
         self.type = type
         # set the downloaded path to None by default. The goal here
         # is to not download distributions multiple times
@@ -89,7 +91,7 @@
         # order to dont make the selection process multiple times.
         self._urls = []
         self._url = None
-        self.add_url(url, hashname, hashval, is_external)
+        self.add_url(url, url_hashname, url_hashval, url_is_external)
 
     def add_url(self, url, hashname=None, hashval=None, is_external=True):
         """Add a new url to the list of urls"""
@@ -124,6 +126,16 @@
                 self._url = self._urls[0]
         return self._url
 
+    @property
+    def is_source(self):
+        """return if the distribution is a source one or not"""
+        return self.type == 'source'
+
+    @property
+    def is_final(self):
+        """proxy to version.is_final"""
+        return self.version.is_final
+
     def download(self, path=None):
         """Download the distribution to a path, and return it.
 
@@ -156,8 +168,9 @@
                     % (hashval.hexdigest(), expected_hashval))
 
     def __repr__(self):
-        return "<%s %s (%s)>" \
-            % (self.__class__.__name__, self.name, self.version)
+        return "%s %s %s %s" \
+            % (self.__class__.__name__, self.name, self.version,
+               self.type or "")
 
     def _check_is_comparable(self, other):
         if not isinstance(other, PyPIDistribution):
@@ -210,13 +223,28 @@
             [dist for dist in self if dist.name == predicate.name and
             predicate.match(dist.version)])
 
-    def get_last(self, predicate):
+    def get_last(self, predicate, prefer_source=None, prefer_final=None):
         """Return the most up to date version, that satisfy the given
         predicate
         """
         distributions = self.filter(predicate)
-        distributions.sort()
-        return distributions[-1]
+        distributions.sort_distributions(prefer_source, prefer_final, reverse=True)
+        return distributions[0]
+
+    def get_same_name_and_version(self):
+        """Return lists of PyPIDistribution objects that refer to the same
+        name and version number. This do not consider the type (source, binary,
+        etc.)"""
+        processed = []
+        duplicates = []
+        for dist in self:
+            if (dist.name, dist.version) not in processed:
+                processed.append((dist.name, dist.version))
+                found_duplicates = [d for d in self if d.name == dist.name and
+                                    d.version == dist.version]
+                if len(found_duplicates) > 1:
+                    duplicates.append(found_duplicates)
+        return duplicates
 
     def append(self, o):
         """Append a new distribution to the list.
@@ -225,16 +253,30 @@
         URL informations and add a new new url for the existing one.
         """
         similar_dists = [d for d in self if d.name == o.name and
-                         d.version == o.version]
+                         d.version == o.version and d.type == o.type]
         if len(similar_dists) > 0:
             dist = similar_dists[0]
             dist.add_url(**o.url)
         else:
             super(PyPIDistributions, self).append(o)
 
-    def sort(self, prefer_source=None, prefer_final=None, *args, **kwargs):
-        return super(PyPIDistributions, self).sort(*args, **kwargs)
+    def sort_distributions(self, prefer_source=None, prefer_final=None,
+                           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")
+        sort_by.append("version")
+
+        if prefer_source is not None:
+            if prefer_source is True:
+                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
@@ -136,7 +136,8 @@
         # filter with requirements and return the results
         if requirements.name in self._distributions:
             dists = self._distributions[requirements.name].filter(requirements)
-            dists.sort(prefer_source=prefer_source, prefer_final=prefer_final)
+            dists.sort_distributions(prefer_source=prefer_source,
+                                     prefer_final=prefer_final)
         else:
             dists = []
 
@@ -262,8 +263,9 @@
                     if self._is_distribution(link) or is_download:
                         self._processed_urls.append(link)
                         # it's a distribution, so create a dist object
-                        self._register_dist(PyPIDistribution.from_url(link,
-                            project_name, is_external=not self.index_url in url))
+                        dist = PyPIDistribution.from_url(link, project_name,
+                                    is_external=not self.index_url in url)
+                        self._register_dist(dist)
                     else:
                         if self._is_browsable(link) and follow_links:
                             self._process_url(link, project_name,
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
@@ -21,18 +21,18 @@
         given on construction"""
         dist = Dist("FooBar", "1.1")
         self.assertEqual("FooBar", dist.name)
-        self.assertEqual("1.1", dist.version)
+        self.assertEqual("1.1", "%s" % dist.version)
 
     def test_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
-                'version': '1.1.0',
+                'version': '1.1',
             },
             'Foo-Bar-1.1.0.zip': {
                 'name': 'foo-bar',  # keep the dash
-                'version': '1.1.0',
+                'version': '1.1',
             },
             'foobar-1.1b2.tar.gz#md5=123123123123123': {
                 'name': 'foobar',
@@ -60,7 +60,10 @@
                     for val in value.keys():
                         self.assertEqual(value[val], mylist[val])
                 else:
-                    self.assertEqual(getattr(dist, attribute), value)
+                    if attribute == "version":
+                        self.assertEqual("%s" % getattr(dist, "version"), value)
+                    else:
+                        self.assertEqual(getattr(dist, attribute), value)
 
     def test_get_url(self):
         """Test that the url property works well"""
@@ -114,12 +117,12 @@
         url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address
         # check md5 if given
         dist = Dist("FooBar", "0.1", url=url,
-            hashname="md5", hashval="d41d8cd98f00b204e9800998ecf8427e")
+            url_hashname="md5", url_hashval="d41d8cd98f00b204e9800998ecf8427e")
         dist.download()
 
         # a wrong md5 fails
         dist2 = Dist("FooBar", "0.1", url=url,
-            hashname="md5", hashval="wrongmd5")
+            url_hashname="md5", url_hashval="wrongmd5")
         self.assertRaises(HashDoesNotMatch, dist2.download)
 
         # we can omit the md5 hash
@@ -143,10 +146,10 @@
     def test_hashname(self):
         """Invalid hashnames raises an exception on assignation"""
         # should be ok
-        Dist("FooBar", "0.1", hashname="md5", hashval="value")
+        Dist("FooBar", "0.1", url_hashname="md5", url_hashval="value")
 
         self.assertRaises(UnsupportedHashName, Dist, "FooBar", "0.1",
-                          hashname="invalid_hashname", hashval="value")
+                          url_hashname="invalid_hashname", url_hashval="value")
 
 
 class TestPyPIDistributions(unittest.TestCase):
@@ -176,30 +179,35 @@
         # If no object matches, just add "normally" the object to the list.
 
         dists = Dists([
-            Dist("FooBar", "1.1", url="external_url"),
+            Dist("FooBar", "1.1", url="external_url", type="source"),
         ])
         self.assertEqual(1, len(dists))
         dists.append(Dist("FooBar", "1.1", url="internal_url",
-                     is_external=False))
+                          url_is_external=False, type="source"))
         self.assertEqual(1, len(dists))
         self.assertEqual(2, len(dists[0]._urls))
 
-        dists.append(Dist("Foobar", "1.1.1"))
+        dists.append(Dist("Foobar", "1.1.1", type="source"))
         self.assertEqual(2, len(dists))
 
+        # when adding a distribution whith a different type, a new distribution
+        # has to be added.
+        dists.append(Dist("Foobar", "1.1.1", type="binary"))
+        self.assertEqual(3, len(dists))
+
     def test_prefer_final(self):
         """Ordering support prefer_final"""
 
         fb10 = Dist("FooBar", "1.0")  # final distribution
-        fb11a = Dist("FooBar", "1.1a")  # alpha
-        fb12a = Dist("FooBar", "1.2a")  # alpha
-        fb12b = Dist("FooBar", "1.2b")  # beta
+        fb11a = Dist("FooBar", "1.1a1")  # alpha
+        fb12a = Dist("FooBar", "1.2a1")  # alpha
+        fb12b = Dist("FooBar", "1.2b1")  # beta
         dists = Dists([fb10, fb11a, fb12a, fb12b])
 
-        dists.sort(prefer_final=True)
+        dists.sort_distributions(prefer_final=True)
         self.assertEqual(fb10, dists[0])
 
-        dists.sort(prefer_final=False)
+        dists.sort_distributions(prefer_final=False)
         self.assertEqual(fb12b, dists[0])
 
     def test_prefer_source(self):
@@ -209,16 +217,27 @@
         fb2_binary = Dist("FooBar", "2.0", type="binary")
         dists = Dists([fb_binary, fb_source])
 
-        dists.sort(prefer_source=True)
+        dists.sort_distributions(prefer_source=True)
         self.assertEqual(fb_source, dists[0])
 
-        dists.sort(prefer_source=False)
+        dists.sort_distributions(prefer_source=False)
         self.assertEqual(fb_binary, dists[0])
 
         dists.append(fb2_binary)
-        dists.sort(prefer_source=True)
+        dists.sort_distributions(prefer_source=True)
         self.assertEqual(fb2_binary, dists[0])
 
+    def test_get_same_name_and_version(self):
+        """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")
+        dists = Dists([fb_binary, fb_source, fb2_binary])
+        duplicates = dists.get_same_name_and_version()
+        self.assertTrue(1, len(duplicates))
+        self.assertIn(fb_source, duplicates[0])
+
 
 def test_suite():
     suite = unittest.TestSuite()
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
@@ -89,9 +89,6 @@
         """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
-        # We query only for the version 1.1, so all distributions must be
-        # filled in the package_index (as the url has been scanned), but
-        # "get" must only return the one we want.
         index = self._get_simple_index(server)
         last_distribution = index.get("foobar")
 
@@ -103,7 +100,7 @@
         self.assertEqual(len(index._distributions["foobar"]), 4)
 
         # and returned the most recent one
-        self.assertEqual(last_distribution.version, '2.0.1')
+        self.assertEqual("%s" % last_distribution.version, '2.0.1')
 
     def test_is_browsable(self):
         index = simple.SimpleIndex(follow_externals=False)

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


More information about the Python-checkins mailing list