123 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			123 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								import json
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from functools import reduce
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								INDENT = '  '
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class CollapseList(list):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class CollapseDict(dict):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								class AlignedDict(dict):
							 | 
						||
| 
								 | 
							
								    def __init__(self, src_dict, depth):
							 | 
						||
| 
								 | 
							
								        self.depth = depth - 1
							 | 
						||
| 
								 | 
							
								        super().__init__(src_dict)
							 | 
						||
| 
								 | 
							
								class SortedDict(dict):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def is_scalar(value):
							 | 
						||
| 
								 | 
							
								    return not is_list(value) and not is_dict(value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def is_list(value):
							 | 
						||
| 
								 | 
							
								    return isinstance(value, list) or isinstance(value, tuple)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def is_dict(value):
							 | 
						||
| 
								 | 
							
								    return isinstance(value, dict)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def dump_scalar(obj, ensure_ascii=False):
							 | 
						||
| 
								 | 
							
								    return json.dumps(obj, ensure_ascii=ensure_ascii)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def dump_list(obj, current_indent='', ensure_ascii=False):
							 | 
						||
| 
								 | 
							
								    entries = [dump_obj(value, current_indent + INDENT, ensure_ascii=ensure_ascii) for value in obj]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if len(entries) == 0:
							 | 
						||
| 
								 | 
							
								        return '[]'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if isinstance(obj, CollapseList):
							 | 
						||
| 
								 | 
							
								        values_format = '{value}'
							 | 
						||
| 
								 | 
							
								        output_format = '[{values}]'
							 | 
						||
| 
								 | 
							
								        join_format   = ', '
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        values_format = '{indent}{value}'
							 | 
						||
| 
								 | 
							
								        output_format = '[\n{values}\n{indent}]'
							 | 
						||
| 
								 | 
							
								        join_format   = ',\n'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    output = output_format.format(
							 | 
						||
| 
								 | 
							
								        indent=current_indent,
							 | 
						||
| 
								 | 
							
								        values=join_format.join([values_format.format(
							 | 
						||
| 
								 | 
							
								            value=entry,
							 | 
						||
| 
								 | 
							
								            indent=current_indent + INDENT
							 | 
						||
| 
								 | 
							
								        ) for entry in entries])
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return output
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_keys(obj, depth):
							 | 
						||
| 
								 | 
							
								    if depth == 0:
							 | 
						||
| 
								 | 
							
								        yield from obj.keys()
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        for value in obj.values():
							 | 
						||
| 
								 | 
							
								            yield from get_keys(value, depth - 1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def dump_dict(obj, current_indent='', sub_width=None, ensure_ascii=False):
							 | 
						||
| 
								 | 
							
								    entries = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    key_width = None
							 | 
						||
| 
								 | 
							
								    if sub_width is not None:
							 | 
						||
| 
								 | 
							
								        sub_width = (sub_width[0]-1, sub_width[1])
							 | 
						||
| 
								 | 
							
								        if sub_width[0] == 0:
							 | 
						||
| 
								 | 
							
								            key_width = sub_width[1]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if isinstance(obj, AlignedDict):
							 | 
						||
| 
								 | 
							
								        sub_keys = get_keys(obj, obj.depth)
							 | 
						||
| 
								 | 
							
								        sub_width = (obj.depth, reduce(lambda acc, entry: max(acc, len(entry)), sub_keys, 0))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for key, value in obj.items():        
							 | 
						||
| 
								 | 
							
								        entries.append((dump_scalar(str(key), ensure_ascii), dump_obj(value, current_indent + INDENT, sub_width, ensure_ascii)))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if key_width is None:
							 | 
						||
| 
								 | 
							
								        key_width = reduce(lambda acc, entry: max(acc, len(entry[0])), entries, 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if len(entries) == 0:
							 | 
						||
| 
								 | 
							
								        return '{}'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if isinstance(obj, SortedDict):
							 | 
						||
| 
								 | 
							
								        entries.sort(key=lambda item: item[0])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if isinstance(obj, CollapseDict):
							 | 
						||
| 
								 | 
							
								        values_format = '{key} {value}'
							 | 
						||
| 
								 | 
							
								        output_format = '{{{values}}}'
							 | 
						||
| 
								 | 
							
								        join_format   = ', '
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        values_format = '{indent}{key:{padding}}{value}'
							 | 
						||
| 
								 | 
							
								        output_format = '{{\n{values}\n{indent}}}'
							 | 
						||
| 
								 | 
							
								        join_format   = ',\n'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    output = output_format.format(
							 | 
						||
| 
								 | 
							
								        indent=current_indent,
							 | 
						||
| 
								 | 
							
								        values=join_format.join([values_format.format(
							 | 
						||
| 
								 | 
							
								            key='{key}:'.format(key=key), 
							 | 
						||
| 
								 | 
							
								            value=value,
							 | 
						||
| 
								 | 
							
								            indent=current_indent + INDENT,
							 | 
						||
| 
								 | 
							
								            padding=key_width + 2,
							 | 
						||
| 
								 | 
							
								        ) for (key, value) in entries])
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return output
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def dump_obj(obj, current_indent='', sub_width=None, ensure_ascii=False):
							 | 
						||
| 
								 | 
							
								    if is_list(obj):
							 | 
						||
| 
								 | 
							
								        return dump_list(obj, current_indent, ensure_ascii)
							 | 
						||
| 
								 | 
							
								    elif is_dict(obj):
							 | 
						||
| 
								 | 
							
								        return dump_dict(obj, current_indent, sub_width, ensure_ascii)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        return dump_scalar(obj, ensure_ascii)
							 |