Patchwork exchange: use rich class for sorting clone bundle entries

login
register
mail settings
Submitter Gregory Szorc
Date Dec. 26, 2016, 7:16 p.m.
Message ID <b3a88bb23d372e388b3e.1482779818@gps-mbp.local>
Download mbox | patch
Permalink /patch/18044/
State Accepted
Headers show

Comments

Gregory Szorc - Dec. 26, 2016, 7:16 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@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.
Augie Fackler - Dec. 26, 2016, 8:32 p.m.
> On Dec 26, 2016, at 2:16 PM, Gregory Szorc <gregory.szorc@gmail.com> wrote:
> 
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@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

queued, thanks

> 
> 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()
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

Patch

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()