koder aka kdanilov | cff7b2e | 2015-04-18 20:48:15 +0300 | [diff] [blame] | 1 | __doc__ = "functions for make pretty yaml files" |
| 2 | __all__ = ['dumps'] |
| 3 | |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame^] | 4 | from typing import Any, Iterable |
koder aka kdanilov | cff7b2e | 2015-04-18 20:48:15 +0300 | [diff] [blame] | 5 | |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame^] | 6 | |
| 7 | def dumps_simple(val: Any) -> str: |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 8 | bad_symbols = set(" \r\t\n,':{}[]><;") |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 9 | |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame^] | 10 | if isinstance(val, str): |
| 11 | val = val.encode('utf8') |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 12 | |
koder aka kdanilov | 7248c7b | 2015-05-31 22:53:03 +0300 | [diff] [blame] | 13 | try: |
| 14 | float(val) |
| 15 | val = repr(val) |
| 16 | except ValueError: |
| 17 | if len(bad_symbols & set(val)) != 0: |
| 18 | val = repr(val) |
| 19 | |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 20 | return val |
| 21 | elif val is True: |
| 22 | return 'true' |
| 23 | elif val is False: |
| 24 | return 'false' |
| 25 | elif val is None: |
| 26 | return 'null' |
| 27 | |
| 28 | return str(val) |
| 29 | |
| 30 | |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame^] | 31 | def is_simple(val: Any) -> bool: |
| 32 | simple_type = isinstance(val, (str, int, bool, float)) |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 33 | return simple_type or val is None |
| 34 | |
| 35 | |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame^] | 36 | def all_nums(vals: Iterable[Any]) -> bool: |
| 37 | return all(isinstance(val, (int, float)) for val in vals) |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 38 | |
| 39 | |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame^] | 40 | def dumpv(data: Any, tab_sz: int=4, width: int=160, min_width: int=40) -> str: |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 41 | tab = ' ' * tab_sz |
| 42 | |
| 43 | if width < min_width: |
| 44 | width = min_width |
| 45 | |
| 46 | res = [] |
| 47 | if is_simple(data): |
| 48 | return [dumps_simple(data)] |
| 49 | |
| 50 | if isinstance(data, (list, tuple)): |
| 51 | if all(map(is_simple, data)): |
| 52 | if all_nums(data): |
koder aka kdanilov | a047e1b | 2015-04-21 23:16:59 +0300 | [diff] [blame] | 53 | one_line = "[{0}]".format(", ".join(map(dumps_simple, data))) |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 54 | else: |
koder aka kdanilov | a047e1b | 2015-04-21 23:16:59 +0300 | [diff] [blame] | 55 | one_line = "[{0}]".format(",".join(map(dumps_simple, data))) |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 56 | elif len(data) == 0: |
| 57 | one_line = "[]" |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 58 | else: |
| 59 | one_line = None |
| 60 | |
| 61 | if one_line is None or len(one_line) > width: |
| 62 | pref = "-" + ' ' * (tab_sz - 1) |
| 63 | |
| 64 | for val in data: |
| 65 | items = dumpv(val, tab_sz, width - tab_sz, min_width) |
| 66 | items = [pref + items[0]] + \ |
| 67 | [tab + item for item in items[1:]] |
| 68 | res.extend(items) |
| 69 | else: |
| 70 | res.append(one_line) |
| 71 | elif isinstance(data, dict): |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 72 | if len(data) == 0: |
| 73 | res.append("{}") |
| 74 | else: |
| 75 | assert all(map(is_simple, data.keys())) |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 76 | |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 77 | one_line = None |
| 78 | if all(map(is_simple, data.values())): |
| 79 | one_line = ", ".join( |
| 80 | "{0}: {1}".format(dumps_simple(k), dumps_simple(v)) |
| 81 | for k, v in sorted(data.items())) |
| 82 | one_line = "{" + one_line + "}" |
| 83 | if len(one_line) > width: |
| 84 | one_line = None |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 85 | |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 86 | if one_line is None: |
| 87 | for k, v in data.items(): |
| 88 | key_str = dumps_simple(k) + ": " |
| 89 | val_res = dumpv(v, tab_sz, width - tab_sz, min_width) |
| 90 | |
| 91 | if len(val_res) == 1 and \ |
| 92 | len(key_str + val_res[0]) < width and \ |
koder aka kdanilov | bb6d6cd | 2015-06-20 02:55:07 +0300 | [diff] [blame] | 93 | not isinstance(v, dict) and \ |
| 94 | not val_res[0].strip().startswith('-'): |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 95 | res.append(key_str + val_res[0]) |
| 96 | else: |
| 97 | res.append(key_str) |
| 98 | res.extend(tab + i for i in val_res) |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 99 | else: |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 100 | res.append(one_line) |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 101 | else: |
koder aka kdanilov | 4af1c1d | 2015-05-18 15:48:58 +0300 | [diff] [blame] | 102 | try: |
| 103 | get_yamable = data.get_yamable |
| 104 | except AttributeError: |
| 105 | raise ValueError("Can't pack {0!r}".format(data)) |
| 106 | res = dumpv(get_yamable(), tab_sz, width, min_width) |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 107 | |
| 108 | return res |
| 109 | |
| 110 | |
koder aka kdanilov | 3b4da8b | 2016-10-17 00:17:53 +0300 | [diff] [blame^] | 111 | def dumps(data: Any, tab_sz: int=4, width: int=120, min_width: int=40) -> str: |
koder aka kdanilov | f4b82c2 | 2015-04-11 13:35:25 +0300 | [diff] [blame] | 112 | return "\n".join(dumpv(data, tab_sz, width, min_width)) |