[PATCH] exchange: use rich class for sorting clone bundle entries

Gregory Szorc gregory.szorc at gmail.com
Mon Dec 26 14:16:58 EST 2016


# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1482779489 25200
#      Mon Dec 26 12:11:29 2016 -0700
# Node ID b3a88bb23d372e388b3e65be30f660686bca6f7e
# Parent  0064a1eb28e246ded9b726c696d048143d1b23f1
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.

diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -1901,20 +1901,24 @@ def filterclonebundleentries(repo, entri
         newentries.append(entry)
 
     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:
                 return -1
@@ -1943,9 +1947,35 @@ def sortclonebundleentries(ui, entries):
         # If we got here we couldn't sort by attributes and prefers. Fall
         # 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."""
     lock = repo.lock()


More information about the Mercurial-devel mailing list