Coverage for /home/pradyumna/Languages/python/packages/xdgpspconf/xdgpspconf/config_io.py : 41%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python3
2# -*- coding: utf-8; mode: python; -*-
3# Copyright © 2021 Pradyumna Paranjape
4#
5# This file is part of xdgpspconf.
6#
7# xdgpspconf is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Lesser General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# xdgpspconf is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Lesser General Public License for more details.
16#
17# You should have received a copy of the GNU Lesser General Public License
18# along with xdgpspconf. If not, see <https://www.gnu.org/licenses/>.
19#
20"""
21Read/Write configurations.
23"""
25import configparser
26from pathlib import Path
27from typing import Any, Dict
29import toml
30import yaml
32from xdgpspconf.errors import BadConf
35def parse_yaml(config: Path) -> Dict[str, Any]:
36 """
37 Read configuration.
39 Args:
40 config: path to yaml config file
42 Returns:
43 parsed configuration
44 """
45 with open(config, 'r') as rcfile:
46 conf: Dict[str, Any] = yaml.safe_load(rcfile)
47 if conf is None: # pragma: no cover
48 raise yaml.YAMLError
49 return conf
52def parse_toml(config: Path, section: str = None) -> Dict[str, Any]:
53 """
54 Read configuration.
56 Args:
57 config: path to yaml config file
58 section: section in ``pyproject.toml`` corresponding to project
60 Returns:
61 parsed configuration
62 """
63 if section is not None:
64 with open(config, 'r') as rcfile:
65 conf: Dict[str, Any] = toml.load(rcfile).get(section, {})
66 return conf
67 with open(config, 'r') as rcfile:
68 conf = dict(toml.load(rcfile))
69 if conf is None: # pragma: no cover
70 raise toml.TomlDecodeError
71 return conf
74def parse_ini(config: Path, section: str = None) -> Dict[str, Any]:
75 """
76 Read configuration.
79 Args:
80 config: path to yaml config file
81 section: section in ``pyproject.toml`` corresponding to project
83 Returns:
84 parsed configuration
85 """
86 parser = configparser.ConfigParser()
87 parser.read(config)
88 if section is not None:
89 return {
90 pspcfg.replace(f'{section}.', ''): dict(parser.items(pspcfg))
91 for pspcfg in parser.sections() if f'{section}.' in pspcfg
92 }
93 return {
94 pspcfg: dict(parser.items(pspcfg))
95 for pspcfg in parser.sections()
96 } # pragma: no cover
99def parse_rc(config: Path, project: str = None) -> Dict[str, Any]:
100 """
101 Parse rc file.
103 Args:
104 config: path to configuration file
105 project: name of project (to locate subsection from pyptoject.toml)
107 Returns:
108 configuration sections
110 Raises:
111 BadConf: Bad configuration
113 """
114 if config.name == 'setup.cfg':
115 # declared inside setup.cfg
116 return parse_ini(config, section=project)
117 if config.name == 'pyproject.toml':
118 # declared inside pyproject.toml
119 return parse_toml(config, section=project)
120 try:
121 # yaml configuration format
122 return parse_yaml(config)
123 except yaml.YAMLError:
124 try:
125 # toml configuration format
126 return parse_toml(config)
127 except toml.TomlDecodeError:
128 try:
129 # try generic config-parser
130 return parse_ini(config)
131 except configparser.Error:
132 raise BadConf(config_file=config) from None
135def write_yaml(data: Dict[str, Any],
136 config: Path,
137 force: str = 'fail') -> bool:
138 """
139 Write data to configuration file.
141 Args:
142 data: serial data to save
143 config: configuration file path
144 force: force overwrite {'overwrite','update','fail'}
146 Returns:
147 write success
149 """
150 old_data: Dict[str, Any] = {}
151 if config.is_file():
152 # file already exists
153 if force == 'fail':
154 return False
155 if force == 'update':
156 old_data = parse_yaml(config)
157 data = {**old_data, **data}
158 with open(config, 'w') as rcfile:
159 yaml.dump(data, rcfile)
160 return True
163def write_toml(data: Dict[str, Any],
164 config: Path,
165 force: str = 'fail') -> bool:
166 """
167 Write data to configuration file.
169 Args:
170 data: serial data to save
171 config: configuration file path
172 force: force overwrite {'overwrite', 'update', 'fail'}
174 Returns:
175 write success
177 """
178 old_data: Dict[str, Any] = {}
179 if config.is_file():
180 # file already exists
181 if force == 'fail':
182 return False
183 if force == 'update':
184 old_data = parse_toml(config)
185 data = {**old_data, **data}
186 with open(config, 'w') as rcfile:
187 toml.dump(data, rcfile)
188 return True
191def write_ini(data: Dict[str, Any], config: Path, force: str = 'fail') -> bool:
192 """
193 Write data to configuration file.
195 Args:
196 data: serial data to save
197 config: configuration file path
198 force: force overwrite {'overwrite', 'update', 'fail'}
200 Returns:
201 write success
203 """
204 old_data: Dict[str, Any] = {}
205 if config.is_file():
206 # file already exists
207 if force == 'fail':
208 return False
209 if force == 'update':
210 old_data = parse_ini(config)
211 data = {**old_data, **data}
212 parser = configparser.ConfigParser()
213 parser.update(data)
214 with open(config, 'w') as rcfile:
215 parser.write(rcfile)
216 return True
219def write_rc(data: Dict[str, Any], config: Path, force: str = 'fail') -> bool:
220 """
221 Write data to configuration file.
223 Args:
224 data: serial data to save
225 config: configuration file path
226 force: force overwrite {'overwrite', 'update', 'fail'}
228 Returns:
229 write success
231 """
232 if config.suffix in ('.conf', '.cfg', '.ini'):
233 return write_ini(data, config, force)
234 if config.suffix == '.toml':
235 return write_toml(data, config, force)
236 # assume yaml
237 return write_yaml(data, config, force)