From ba69f5bddd355bd8542c4a6b360aaf6fb95914c1 Mon Sep 17 00:00:00 2001
From: Anders Blomdell <anders.blomdell@control.lth.se>
Date: Mon, 26 Aug 2019 16:18:54 +0200
Subject: [PATCH] Adjust sorting from cmp= to key= syntax

---
 src/hostinfo.py        | 110 ++++++++++++++---------------------------
 src/hostinfo/parser.py |  66 ++-----------------------
 2 files changed, 43 insertions(+), 133 deletions(-)

diff --git a/src/hostinfo.py b/src/hostinfo.py
index 604217e..25121d3 100755
--- a/src/hostinfo.py
+++ b/src/hostinfo.py
@@ -75,7 +75,7 @@ if not hasattr(__builtins__, "True"):
     __builtins__["True"] = 1
     __builtins__["False"] = 0
 
-attr_weight = {
+attr_key_table = {
     ('host', 'name') : 1,
     ('disk', 'host') : 1,
     ('disk', 'root') : 2,
@@ -91,13 +91,20 @@ attr_weight = {
     ('subnet', 'netmask') : 4,
     ('subnet', 'gateway') : 5,
     ('subnet', 'name_servers') : 6,
+    None : 99,
 }
 
-class AttributeDict(dict): 
-    __getattr__ = dict.__getitem__
+def attr_key(node, attr): # , b
+    return
+    try:
+        return attr_key_table[node._tag, attr]
+    except:
+        return attr_key_table[None]
+
+def network_order(n):
+    net = hostinfo.util.network(n)
+    return (net.version, net)
 
-def network_order(a, b):
-    a_net = hostinfo.util.network(a)
     b_net = hostinfo.util.network(b)
     if not a_net or not b_net:
         return cmp(a,b)
@@ -105,7 +112,7 @@ def network_order(a, b):
         return cmp(a_net, b_net)
     return cmp(a_net.version, b_net.version)
 
-def host_order(a,b):
+def host_order(h):
     def fqn(host):
         n = host.name[0]
         return n and n.endswith('.')
@@ -122,44 +129,32 @@ def host_order(a,b):
     def all_addr(host):
         return ether(host) and ip(host)
 
+    # Virtual hosts (no ethernet, with ip)
+    if h.virtual[0] == 'yes': return (1, h.name[0])
+
     # Fully qualified names
-    if fqn(a) or fqn(b): 
-        if fqn(a) and fqn(b): return cmp(a.name[0], b.name[0])
-        if fqn(a): return -1
-        if fqn(b): return 1
-        pass
-    
+    if fqn(h): return (2, h.name[0])
+
     #  Hosts without ether an ip address (reusable names)
-    if no_addr(a) or no_addr(b):
-        if no_addr(a) and no_addr(b): return cmp(a.name[0], b.name[0])
-        if no_addr(a): return -1
-        if no_addr(b): return 1
-        pass
+    if no_addr(h): return (3, h.name[0])
 
     # Hosts with ip and ether (active machines)
-    if all_addr(a) or all_addr(b):
-        if all_addr(a) and all_addr(b): return cmp(a.name[0], b.name[0])
-        if all_addr(a): return -1
-        if all_addr(b): return 1
-        pass
+    if all_addr(h): return (4, h.name[0])
+
     # This should be machines with ether but no ip (decommissioned)
-    if ip(a): 
+    if ip(h): 
         raise hostinfo.util.HostinfoException('Unexpected ip address %s' %
-                                              a.name[0], where=a)
-    if ip(b): 
-        raise hostinfo.util.HostinfoException('Unexpected ip address %s' %
-                                              b.name[0], where=b)
-    return cmp(a.name[0], b.name[0])
+                                              h.name[0], where=h)
+    return (5, h.name[0])
 
-tag_weight = {
+node_key_table = {
     ('hostinfo', 'soa')         : (1, None),
     ('hostinfo', 'subnet')      : (2, network_order),
     ('hostinfo', 'nameserver')  : (3, None),
-    ('hostinfo', 'netgroup')    : (4,
-                                   lambda a,b: cmp(a.name[0],b.name[0])),
+    ('hostinfo', 'netgroup')    : (4, lambda n: n.name[0]),
     ('hostinfo', 'host')        : (5, host_order),
     ('host', 'role')            : (1, None),
-    ('host', 'automount')       : (2, lambda a,b: cmp(a.host[0],b.host[0])),
+    ('host', 'automount')       : (2, lambda a: a.host[0]),
     ('host', 'mio')             : (3, None),
     ('host', 'qemu')            : (4, None),
     ('interface', 'kickstart')  : (1, None),
@@ -168,51 +163,22 @@ tag_weight = {
     ('interface', 'alias')      : (4, None),
     ('interface', 'dhcpserver') : (5, None),
     ('interface', 'nameserver') : (6, None),
-    ('automount', 'entry')      : (1, lambda a,b: cmp(a.key[0],b.key[0])),
+    ('automount', 'entry')      : (1, lambda e: e.key[0]),
+    (None)                      : (99, None)
 }
 
-def attr_sort(node, a, b):
+def node_key(parent, node):
     try:
-        aw = attr_weight[node._tag, a]
+        (w, subkey) = node_key_table[parent._tag, node._tag]
     except:
-        aw = 0
+        (w, subkey) = node_key_table[None]
+        pass
 
-    try:
-        bw = attr_weight[node._tag, b]
-    except:
-        bw = 0
-
-    if aw and bw:
-        return cmp(aw, bw)
-    elif aw:
-        return -1
-    elif bw:
-        return 1
-    else:
-        return cmp(a, b)
-
-def tag_sort(parent, a, b):
-    try:
-        (aw, sort) = tag_weight[parent._tag, a._tag]
-    except:
-        aw = 0
+    if subkey != None:
+        subkey = subkey(node)
+        pass
 
-    try:
-        (bw, sort) = tag_weight[parent._tag, b._tag]
-    except:
-        bw = 0
-
-    if aw and bw:
-        if aw == bw and sort:
-            return sort(a,b)
-        else:
-            return cmp(aw, bw)
-    elif aw:
-        return -1
-    elif bw:
-        return 1
-    else:
-        return None
+    return (w, subkey)
 
 if __name__ == '__main__':
     optParser = argparse.ArgumentParser(usage="%(prog)s [options] [hostinfo]")
@@ -343,7 +309,7 @@ if __name__ == '__main__':
 
     if options.pretty:
         result = "<?xml version='1.0' encoding='utf-8'?>\n\n"
-        result += tree._xml(attr_sort=attr_sort, tag_sort=tag_sort)
+        result += tree._xml(attr_key=attr_key, node_key=node_key)
         print(result.encode("utf-8"))
         sys.exit(0)
 
diff --git a/src/hostinfo/parser.py b/src/hostinfo/parser.py
index 582871a..20eb8d9 100755
--- a/src/hostinfo/parser.py
+++ b/src/hostinfo/parser.py
@@ -136,58 +136,6 @@ def xml_escape(s):
         
     return result
 
-class AttrSortWrapper:
-    """Wrapper class for sorting attributes
-
-    [see Node._xml(...)]"""
-    def __init__(self, node, sort):
-        self.node = node
-        self.sort = sort
-
-    def __call__(self, a, b):
-        """
-        Return the sort order between attributes 'a' and 'b'.
-
-        If sort returns None, a standard Python comparison is used.
-        """
-        result = None
-        if self.sort:
-            result = self.sort(self.node, a, b)
-        if result == None:
-            if a < b:
-                result = -1
-            elif a > b:
-                result = 1
-            else:
-                result = 0
-        return result
-    
-class TagSortWrapper:
-    """Wrapper class for sorting tags
-    
-    [see Node._xml(...)]"""
-    def __init__(self, parent, sort):
-        self.parent = parent
-        self.sort = sort
-
-    def __call__(self, a, b):
-        """
-        Return the sort order between tags 'a' and 'b'.
-
-        If sort returns None, a standard Python comparison is used.
-        """
-        result = None
-        if self.sort:
-            result = self.sort(self.parent, a, b)
-        if result == None:
-            if a._tag < b._tag:
-                result = -1
-            elif a._tag > b._tag:
-                result = 1
-            else:
-                result = 0
-        return result
-    
 class ChildAccessor:
     """Helper class for iterating over xml trees
     
@@ -374,14 +322,13 @@ class Node:
         else:
             return AttributeAccessor(self, attr)
     
-    def _xml(self, indent=0, attr_sort=None, tag_sort=None, width=80):
+    def _xml(self, indent=0, attr_key=None, node_key=None, width=80):
         """Generate a prettyprinted xml of the tree rooted in this node"""
         result = ""
         line = "%s<%s" % ("  " * indent, self._tag)
         blanks = " " * len(line)
         for k in sorted(self._attribute.keys(),
-                        key=(attr_sort and AttrSortWrapper(self, attr_sort)
-                             or None)):
+                        key=attr_key and (lambda a: attr_key(self, a))):
             s = " %s='%s'" % (k, xml_escape(self._attribute[k]))
             if len(line) + len(s) > width:
                 result += "%s\n" % line
@@ -396,12 +343,9 @@ class Node:
                 result += '\n'.join(self._char)
                 result += '\n'
                 pass
-            print("YYY", (tag_sort and TagSortWrapper(self, tag_sort)
-                          or None), "\n")
             for c in sorted(self._children,
-                            key=(tag_sort and TagSortWrapper(self, tag_sort)
-                                 or None)):
-                result += c._xml(indent + 1, attr_sort, tag_sort, width)
+                            key=node_key and (lambda c: node_key(self, c))):
+                result += c._xml(indent + 1, attr_key, node_key, width)
             result += "%s</%s>\n" % ("  " * indent, self._tag)
         return result
 
@@ -462,7 +406,7 @@ class Comment:
         """Copy node and all children"""
         return self.__copy__()
     
-    def _xml(self, indent=0, attr_sort=None, tag_sort=None, width=80):
+    def _xml(self, indent=0, attr_key=None, node_key=None, width=80):
         """Generate a prettyprinted xml of the tree rooted in this node"""
         result = "%s<!--" % ("  " * indent)
         result += self._attribute['']
-- 
GitLab