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