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)
 |