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
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
1"""Read and write ini files.
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.
8Example::
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"""
15import configparser
16import io
19def from_paths(*paths: str) -> dict:
20 """Read ini files into a nested dict.
22 Args:
23 *paths: Paths to ini files. Missing files are skipped, later files override earlier ones.
25 Returns:
26 A dict with section names as keys and dicts of options as values.
27 """
29 res = {}
30 cc = _from_paths(paths)
32 for sec in cc.sections():
33 for opt in cc.options(sec):
34 res.setdefault(sec, {})[opt] = cc.get(sec, opt)
36 return res
39def from_paths_flat(*paths: str) -> dict:
40 """Read ini files into a flat dict.
42 Args:
43 *paths: Paths to ini files. Missing files are skipped, later files override earlier ones.
45 Returns:
46 A dict with ``section.option`` keys.
47 """
48 res = {}
49 cc = _from_paths(paths)
51 for sec in cc.sections():
52 for opt in cc.options(sec):
53 res[f'{sec}.{opt}'] = cc.get(sec, opt)
55 return res
58def _from_paths(paths):
59 """Read ini files into a case-sensitive ``ConfigParser``."""
60 cc = configparser.ConfigParser()
61 cc.optionxform = lambda optionstr: str(optionstr)
63 for path in paths:
64 cc.read(path)
66 return cc
69def to_string(d: dict) -> str:
70 """Convert a flat dict to ini text.
72 Keys are split at the first dot into a section name and an option name.
73 Option names are lowercased.
75 Args:
76 d: A dict with ``section.option`` keys and string values.
78 Returns:
79 Ini file content.
80 """
82 cc = configparser.ConfigParser()
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)
90 with io.StringIO() as fp:
91 cc.write(fp, space_around_delimiters=False)
92 return fp.getvalue()