1
2# attempt to regain python 2 sorting
3# code below is from https://stackoverflow.com/questions/26575183/how-can-i-get-2-x-like-sorting-behaviour-in-python-3-x
4#===========================
5from numbers import Number
6
7
8# decorator for type to function mapping special cases
9def per_type_cmp(type_):
10    try:
11        mapping = per_type_cmp.mapping
12    except AttributeError:
13        mapping = per_type_cmp.mapping = {}
14    #end try
15    def decorator(cmpfunc):
16        mapping[type_] = cmpfunc
17        return cmpfunc
18    #end def decorator
19    return decorator
20#ned def per_type_cmp
21
22class python2_sort_key(object):
23    _unhandled_types = {complex}
24
25    def __init__(self, ob):
26       self._ob = ob
27    #end def __init__
28
29    def __lt__(self, other):
30        _unhandled_types = self._unhandled_types
31        self, other = self._ob, other._ob  # we don't care about the wrapper
32
33        # default_3way_compare is used only if direct comparison failed
34        try:
35            return self < other
36        except TypeError:
37            pass
38        #end try
39
40        # hooks to implement special casing for types, dict in Py2 has
41        # a dedicated __cmp__ method that is gone in Py3 for example.
42        for type_, special_cmp in per_type_cmp.mapping.items():
43            if isinstance(self, type_) and isinstance(other, type_):
44                return special_cmp(self, other)
45            #end if
46        #end for
47
48        # explicitly raise again for types that won't sort in Python 2 either
49        if type(self) in _unhandled_types:
50            raise TypeError('no ordering relation is defined for {}'.format(
51                type(self).__name__))
52        #end if
53        if type(other) in _unhandled_types:
54            raise TypeError('no ordering relation is defined for {}'.format(
55                type(other).__name__))
56        #end if
57
58        # default_3way_compare from Python 2 as Python code
59        # same type but no ordering defined, go by id
60        if type(self) is type(other):
61            return id(self) < id(other)
62        #end if
63
64        # None always comes first
65        if self is None:
66            return True
67        #end if
68        if other is None:
69            return False
70        #end if
71
72        # Sort by typename, but numbers are sorted before other types
73        self_tname = '' if isinstance(self, Number) else type(self).__name__
74        other_tname = '' if isinstance(other, Number) else type(other).__name__
75
76        if self_tname != other_tname:
77            return self_tname < other_tname
78        #end if
79
80        # same typename, or both numbers, but different type objects, order
81        # by the id of the type object
82        return id(type(self)) < id(type(other))
83    #end def __lt__
84#end class python2_sort_key
85
86@per_type_cmp(dict)
87def dict_cmp(a, b, _s=object()):
88    if len(a) != len(b):
89        return len(a) < len(b)
90    #end if
91    adiff = min((k for k in a if a[k] != b.get(k, _s)), key=python2_sort_key, default=_s)
92    if adiff is _s:
93        # All keys in a have a matching value in b, so the dicts are equal
94        return False
95    #end if
96    bdiff = min((k for k in b if b[k] != a.get(k, _s)), key=python2_sort_key)
97    if adiff != bdiff:
98        return python2_sort_key(adiff) < python2_sort_key(bdiff)
99    #end if
100    return python2_sort_key(a[adiff]) < python2_sort_key(b[bdiff])
101#end def dict_cmp
102
103def sorted_py2(iterable):
104    return sorted(iterable,key=python2_sort_key)
105#end def sorted_py2
106#===========================
107
108
109
110
111def to_str(s):
112    if isinstance(s,bytes):
113        return str(s,encoding='utf-8')
114    else:
115        return str(s)
116    #end if
117#end def to_str
118