exchange: use rich class for sorting clone bundle entries
Python 3 removed the "cmp" argument from sorted(). Custom sorting in
Python 3 must be implemented with the dunder comparison methods on
types and/or with a "key" function.
This patch converts our custom "cmp" function to a custom type.
The implementation is very similar to functools.cmp_to_key(). However,
cmp_to_key() doesn't exist in Python 2, so we can't use it.
This was the only use of the "cmp" argument to sorted() in the code
base.
--- a/mercurial/exchange.py Mon Dec 26 00:02:42 2016 +0000
+++ b/mercurial/exchange.py Mon Dec 26 12:11:29 2016 -0700
@@ -1902,18 +1902,22 @@
return newentries
-def sortclonebundleentries(ui, entries):
- prefers = ui.configlist('ui', 'clonebundleprefers', default=[])
- if not prefers:
- return list(entries)
+class clonebundleentry(object):
+ """Represents an item in a clone bundles manifest.
+
+ This rich class is needed to support sorting since sorted() in Python 3
+ doesn't support ``cmp`` and our comparison is complex enough that ``key=``
+ won't work.
+ """
- prefers = [p.split('=', 1) for p in prefers]
+ def __init__(self, value, prefers):
+ self.value = value
+ self.prefers = prefers
- # Our sort function.
- def compareentry(a, b):
- for prefkey, prefvalue in prefers:
- avalue = a.get(prefkey)
- bvalue = b.get(prefkey)
+ def _cmp(self, other):
+ for prefkey, prefvalue in self.prefers:
+ avalue = self.value.get(prefkey)
+ bvalue = other.value.get(prefkey)
# Special case for b missing attribute and a matches exactly.
if avalue is not None and bvalue is None and avalue == prefvalue:
@@ -1944,7 +1948,33 @@
# back to index order.
return 0
- return sorted(entries, cmp=compareentry)
+ def __lt__(self, other):
+ return self._cmp(other) < 0
+
+ def __gt__(self, other):
+ return self._cmp(other) > 0
+
+ def __eq__(self, other):
+ return self._cmp(other) == 0
+
+ def __le__(self, other):
+ return self._cmp(other) <= 0
+
+ def __ge__(self, other):
+ return self._cmp(other) >= 0
+
+ def __ne__(self, other):
+ return self._cmp(other) != 0
+
+def sortclonebundleentries(ui, entries):
+ prefers = ui.configlist('ui', 'clonebundleprefers', default=[])
+ if not prefers:
+ return list(entries)
+
+ prefers = [p.split('=', 1) for p in prefers]
+
+ items = sorted(clonebundleentry(v, prefers) for v in entries)
+ return [i.value for i in items]
def trypullbundlefromurl(ui, repo, url):
"""Attempt to apply a bundle from a URL."""