Coverage for gws-app/gws/lib/inifile/__init__.py: 100%

32 statements  

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

1"""Read and write ini files. 

2 

3Reads one or more ini files with ``configparser`` into a nested dict (section, then option) 

4or into a flat dict with ``section.option`` keys, and writes a flat dict back as ini text. 

5When reading, option names keep their case, files that do not exist are skipped, and values 

6from later files override earlier ones. 

7 

8Example:: 

9 

10 d = gws.lib.inifile.from_paths_flat('/data/a.ini', '/data/b.ini') 

11 # {'section.option': 'value', ...} 

12 text = gws.lib.inifile.to_string({'section.option': 'value'}) 

13""" 

14 

15import configparser 

16import io 

17 

18 

19def from_paths(*paths: str) -> dict: 

20 """Read ini files into a nested dict. 

21 

22 Args: 

23 *paths: Paths to ini files. Missing files are skipped, later files override earlier ones. 

24 

25 Returns: 

26 A dict with section names as keys and dicts of options as values. 

27 """ 

28 

29 res = {} 

30 cc = _from_paths(paths) 

31 

32 for sec in cc.sections(): 

33 for opt in cc.options(sec): 

34 res.setdefault(sec, {})[opt] = cc.get(sec, opt) 

35 

36 return res 

37 

38 

39def from_paths_flat(*paths: str) -> dict: 

40 """Read ini files into a flat dict. 

41 

42 Args: 

43 *paths: Paths to ini files. Missing files are skipped, later files override earlier ones. 

44 

45 Returns: 

46 A dict with ``section.option`` keys. 

47 """ 

48 res = {} 

49 cc = _from_paths(paths) 

50 

51 for sec in cc.sections(): 

52 for opt in cc.options(sec): 

53 res[f'{sec}.{opt}'] = cc.get(sec, opt) 

54 

55 return res 

56 

57 

58def _from_paths(paths): 

59 """Read ini files into a case-sensitive ``ConfigParser``.""" 

60 cc = configparser.ConfigParser() 

61 cc.optionxform = lambda optionstr: str(optionstr) 

62 

63 for path in paths: 

64 cc.read(path) 

65 

66 return cc 

67 

68 

69def to_string(d: dict) -> str: 

70 """Convert a flat dict to ini text. 

71 

72 Keys are split at the first dot into a section name and an option name. 

73 Option names are lowercased. 

74 

75 Args: 

76 d: A dict with ``section.option`` keys and string values. 

77 

78 Returns: 

79 Ini file content. 

80 """ 

81 

82 cc = configparser.ConfigParser() 

83 

84 for k, v in d.items(): 

85 sec, _, name = k.partition('.') 

86 if not cc.has_section(sec): 

87 cc.add_section(sec) 

88 cc.set(sec, name, v) 

89 

90 with io.StringIO() as fp: 

91 cc.write(fp, space_around_delimiters=False) 

92 return fp.getvalue()