blob: 32f74ce91e2f2b1f55c6226bc76b9b78cc01f6fd [file] [log] [blame]
#!/usr/bin/env python3
import io
import json
import re
import sys
from collections import OrderedDict
def extract_numbers(s):
"""
>>> extract_numbers("CLK_HROW_WR10END2_3")
('CLK_HROW_WR', 10, 'END', 2, '_', 3)
>>> extract_numbers("VBRK_WR1END2")
('VBRK_WR', 1, 'END', 2)
"""
bits = []
for m in re.finditer("([^0-9]*)([0-9]*)", s):
if m.group(1):
bits.append(m.group(1))
if m.group(2):
bits.append(int(m.group(2)))
return tuple(bits)
def sort(data):
"""Sort data types via "natural" numbers.
Supports all the basic Python data types.
>>> o = sort({
... 't1': {'c','b'}, # Set
... 't2': ('a2', 'a10', 'e'), # Tuple
... 't3': [5, 3, 2], # List
... 't4': { # Dictionary
... 'a4': ('b2', 'b3'),
... 'a2': ['c1', 'c2', 'c0', 'c10'],
... },
... 't5': ['a1b5', 'a2b1', 'a1b1'],
... })
>>> for t in o:
... print(t+':', o[t])
t1: ('b', 'c')
t2: ('a2', 'a10', 'e')
t3: (5, 3, 2)
t4: OrderedDict([('a2', ('c1', 'c2', 'c0', 'c10')), ('a4', ('b2', 'b3'))])
t5: ('a1b5', 'a2b1', 'a1b1')
Don't mangle "pairs"
>>> sort([('b', 'c'), ('2', '1')])
(('b', 'c'), ('2', '1'))
"""
def key(o):
if o is None:
return None
elif isinstance(o, str):
return extract_numbers(o)
elif isinstance(o, int):
return o
elif isinstance(o, (list, tuple)):
return tuple(key(i) for i in o)
elif isinstance(o, dict):
return tuple((key(k), key(v)) for k, v in o.items())
elif isinstance(o, set):
return tuple(key(k) for k in o)
raise ValueError(repr(o))
def rsorter(o):
if isinstance(o, dict):
nitems = []
for k, v in o.items():
nitems.append((key(k), k, rsorter(v)))
nitems.sort(key=lambda n: n[0])
new_dict = OrderedDict()
for _, k, v in nitems:
new_dict[k] = v
return new_dict
elif isinstance(o, set):
return tuple(sorted((rsorter(v) for v in o), key=key))
elif isinstance(o, (tuple, list)):
return tuple(rsorter(v) for v in o)
else:
return o
return rsorter(data)
def pprint(f, data):
detach = False
if not isinstance(f, io.TextIOBase):
detach = True
f = io.TextIOWrapper(f)
data = sort(data)
json.dump(data, f, indent=4)
f.write('\n')
f.flush()
if detach:
f.detach()
if __name__ == "__main__":
if len(sys.argv) == 1:
import doctest
doctest.testmod()
else:
assert len(sys.argv) == 2
d = json.load(open(sys.argv[1]))
pprint(sys.stdout, d)