[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