comparison libervia/backend/tools/config.py @ 4071:4b842c1fb686

refactoring: renamed `sat` package to `libervia.backend`
author Goffi <goffi@goffi.org>
date Fri, 02 Jun 2023 11:49:51 +0200
parents sat/tools/config.py@524856bd7b19
children 0d7bb4df2343
comparison
equal deleted inserted replaced
4070:d10748475025 4071:4b842c1fb686
1 #!/usr/bin/env python3
2
3
4 # SAT: a jabber client
5 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
6 # Copyright (C) 2013-2016 Adrien Cossa (souliane@mailoo.org)
7
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
17
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21 """ Configuration related useful methods """
22
23 import os
24 import csv
25 import json
26 from typing import Any
27 from configparser import ConfigParser, DEFAULTSECT, NoOptionError, NoSectionError
28 from xdg import BaseDirectory
29 from libervia.backend.core.log import getLogger
30 from libervia.backend.core.constants import Const as C
31 from libervia.backend.core.i18n import _
32 from libervia.backend.core import exceptions
33
34 log = getLogger(__name__)
35
36
37 def fix_config_option(section, option, value, silent=True):
38 """Force a configuration option value
39
40 the option will be written in the first found user config file, a new user
41 config will be created if none is found.
42
43 @param section (str): the config section
44 @param option (str): the config option
45 @param value (str): the new value
46 @param silent (boolean): toggle logging output (must be True when called from sat.sh)
47 """
48 config = ConfigParser()
49 target_file = None
50 for file_ in C.CONFIG_FILES[::-1]:
51 # we will eventually update the existing file with the highest priority,
52 # if it's a user personal file...
53 if not silent:
54 log.debug(_("Testing file %s") % file_)
55 if os.path.isfile(file_):
56 if file_.startswith(os.path.expanduser("~")):
57 config.read([file_])
58 target_file = file_
59 break
60 if not target_file:
61 # ... otherwise we create a new config file for that user
62 target_file = (
63 f"{BaseDirectory.save_config_path(C.APP_NAME_FILE)}/{C.APP_NAME_FILE}.conf"
64 )
65 if section and section.upper() != DEFAULTSECT and not config.has_section(section):
66 config.add_section(section)
67 config.set(section, option, value)
68 with open(target_file, "wb") as configfile:
69 config.write(configfile) # for the next time that user launches sat
70 if not silent:
71 if option in ("passphrase",): # list here the options storing a password
72 value = "******"
73 log.warning(_("Config auto-update: {option} set to {value} in the file "
74 "{config_file}.").format(option=option, value=value,
75 config_file=target_file))
76
77
78 def parse_main_conf(log_filenames=False):
79 """Look for main .ini configuration file, and parse it
80
81 @param log_filenames(bool): if True, log filenames of read config files
82 """
83 config = ConfigParser(defaults=C.DEFAULT_CONFIG)
84 try:
85 filenames = config.read(C.CONFIG_FILES)
86 except Exception as e:
87 log.error(_("Can't read main config: {msg}").format(msg=e), exc_info=True)
88 else:
89 if log_filenames:
90 if filenames:
91 log.info(
92 _("Configuration was read from: {filenames}").format(
93 filenames=', '.join(filenames)))
94 else:
95 log.warning(
96 _("No configuration file found, using default settings")
97 )
98
99 return config
100
101
102 def config_get(config, section, name, default=None):
103 """Get a configuration option
104
105 @param config (ConfigParser): the configuration instance
106 @param section (str): section of the config file (None or '' for DEFAULT)
107 @param name (str): name of the option
108 @param default: value to use if not found, or Exception to raise an exception
109 @return (unicode, list, dict): parsed value
110 @raise: NoOptionError if option is not present and default is Exception
111 NoSectionError if section doesn't exists and default is Exception
112 exceptions.ParsingError error while parsing value
113 """
114 if not section:
115 section = DEFAULTSECT
116
117 try:
118 value = config.get(section, name)
119 except (NoOptionError, NoSectionError) as e:
120 if default is Exception:
121 raise e
122 return default
123
124 if name.endswith("_path") or name.endswith("_dir"):
125 value = os.path.expanduser(value)
126 # thx to Brian (http://stackoverflow.com/questions/186857/splitting-a-semicolon-separated-string-to-a-dictionary-in-python/186873#186873)
127 elif name.endswith("_list"):
128 value = next(csv.reader(
129 [value], delimiter=",", quotechar='"', skipinitialspace=True
130 ))
131 elif name.endswith("_dict"):
132 try:
133 value = json.loads(value)
134 except ValueError as e:
135 raise exceptions.ParsingError("Error while parsing data: {}".format(e))
136 if not isinstance(value, dict):
137 raise exceptions.ParsingError(
138 "{name} value is not a dict: {value}".format(name=name, value=value)
139 )
140 elif name.endswith("_json"):
141 try:
142 value = json.loads(value)
143 except ValueError as e:
144 raise exceptions.ParsingError("Error while parsing data: {}".format(e))
145 return value
146
147
148 def get_conf(
149 conf: ConfigParser,
150 prefix: str,
151 section: str,
152 name: str,
153 default: Any
154 ) -> Any:
155 """Get configuration value from environment or config file
156
157 @param str: prefix to use for the varilable name (see `name` below)
158 @param section: config section to use
159 @param name: unsuffixed name.
160 For environment variable, `LIBERVIA_<prefix>_` will be prefixed (and name
161 will be set to uppercase).
162 For config file, `<prefix>_` will be prefixed (and DEFAULT section will be
163 used).
164 Environment variable has priority over config values. If Environment variable
165 is set but empty string, config value will be used.
166 @param default: default value to use if varilable is set neither in environment,
167 nor in config
168 """
169 # XXX: This is a temporary method until parameters are refactored
170 value = os.getenv(f"LIBERVIA_{prefix}_{name}".upper())
171 return value or config_get(conf, section, f"{prefix}_{name}", default)