Coverage for gws-app/gws/spec/generator/util.py: 59%

44 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-05 13:35 +0200

1"""File, JSON and ini helpers for the generator.""" 

2 

3import os 

4import re 

5import json 

6 

7from gws.lib.cli import ( 

8 find_files, 

9 find_dirs, 

10 read_file, 

11 write_file, 

12 ensure_dir, 

13) 

14 

15 

16def _json(x): 

17 if isinstance(x, bytes): 

18 return x.hex() 

19 try: 

20 return vars(x) 

21 except: 

22 return repr(x) 

23 

24 

25def write_json(path, obj): 

26 """Write an object to a JSON file. 

27 

28 Objects are written as their attribute dicts, bytes as hex strings, 

29 other values that are not JSON serializable as their ``repr``. 

30 

31 Args: 

32 path: File path. 

33 obj: Object to write. 

34 """ 

35 

36 write_file(path, json.dumps(obj, default=_json, indent=4, sort_keys=True)) 

37 

38 

39def read_json(path): 

40 """Read a JSON file. 

41 

42 Args: 

43 path: File path. 

44 

45 Returns: 

46 The parsed JSON value. 

47 """ 

48 

49 return json.loads(read_file(path)) 

50 

51 

52def parse_ini(text): 

53 """Parse an ini-style strings file. 

54 

55 Lines starting with ``;``, ``#`` or ``//`` are comments. A line without 

56 ``=`` continues the value of the previous key. ``\\n`` in values is 

57 converted to a newline. 

58 

59 Args: 

60 text: File content. 

61 

62 Returns: 

63 A dict of sections, each a dict of keys and values. 

64 

65 Raises: 

66 ``ValueError``: If a line cannot be parsed. 

67 """ 

68 

69 dct = {} 

70 section = '' 

71 key = '' 

72 

73 for ln in text.strip().splitlines(): 

74 ln = ln.strip() 

75 if ln.startswith((';', '#', '//')): 

76 continue 

77 if ln.startswith('['): 

78 section = ln[1:-1].strip() 

79 continue 

80 m = re.match(r'^([a-zA-Z0-9_.]+)\s*=(.*)', ln) 

81 if m: 

82 key = m.group(1).strip() 

83 val = m.group(2) 

84 dct.setdefault(section, {})[key] = val.strip().replace('\\n', '\n') 

85 elif key: 

86 dct[section][key] += '\n' + ln.strip() 

87 elif ln: 

88 raise ValueError(f'invalid ini string {ln!r}') 

89 

90 return dct 

91 

92 

93def make_ini(dct): 

94 """Create an ini-style text from a dict of sections. 

95 

96 Keys are sorted, newlines in values are written as ``\\n``. 

97 

98 Args: 

99 dct: A dict of sections, each a dict of keys and values. 

100 

101 Returns: 

102 The ini text. 

103 """ 

104 

105 buf = [] 

106 

107 for sec, rows in dct.items(): 

108 buf.append('[' + sec + ']') 

109 for k, v in sorted(rows.items()): 

110 buf.append(k + '=' + v.replace('\n', '\\n')) 

111 buf.append('') 

112 

113 return '\n'.join(buf)