add configuration handling module
This module contains the logic to load default, system-wide and, if specified, additional configuration files and merges them into two dictionaries: `config` and `email_overrides`. Signed-off-by: Nils Philippsen <nils@redhat.com>
This commit is contained in:
parent
3bde7e6e54
commit
c71b3cc0ad
2 changed files with 152 additions and 0 deletions
151
distgit_bugzilla_sync/config.py
Normal file
151
distgit_bugzilla_sync/config.py
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
# distgit_bugzilla_sync.config - handle configuration
|
||||||
|
#
|
||||||
|
# Copyright © 2018-2019 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
# copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
# General Public License v.2, or (at your option) any later version. This
|
||||||
|
# program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY expressed or implied, including the implied warranties of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
# Public License for more details. You should have received a copy of the GNU
|
||||||
|
# General Public License along with this program; if not, write to the Free
|
||||||
|
# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the source
|
||||||
|
# code or documentation are not subject to the GNU General Public License and
|
||||||
|
# may only be used or replicated with the express permission of Red Hat, Inc.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import toml
|
||||||
|
|
||||||
|
|
||||||
|
_here = os.path.dirname(__file__)
|
||||||
|
_default_conf_root = os.path.join(_here, 'default-config-files')
|
||||||
|
_system_conf_root = '/etc'
|
||||||
|
|
||||||
|
config_files = {
|
||||||
|
'default': {
|
||||||
|
'configuration': os.path.join(_default_conf_root, 'configuration.toml'),
|
||||||
|
'email_overrides': os.path.join(_default_conf_root, 'email_overrides.toml'),
|
||||||
|
},
|
||||||
|
'system': {
|
||||||
|
'configuration': os.path.join(_system_conf_root, 'configuration.toml'),
|
||||||
|
'email_overrides': os.path.join(_system_conf_root, 'email_overrides.toml'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigDict(dict):
|
||||||
|
"""Dictionary class for configuration data.
|
||||||
|
|
||||||
|
This contains a modified update() method which allows deep-merging other
|
||||||
|
dictionaries."""
|
||||||
|
|
||||||
|
def update(self, other: dict):
|
||||||
|
"""Merge data from another dictionary into this one.
|
||||||
|
|
||||||
|
Fails if any node isn't of the same type in both, to detect
|
||||||
|
configuration format issues early."""
|
||||||
|
self_set = set(self)
|
||||||
|
other_set = set(other)
|
||||||
|
|
||||||
|
type_error = False
|
||||||
|
|
||||||
|
# keys that are in both self and other
|
||||||
|
for k in self_set & other_set:
|
||||||
|
self_val = self[k]
|
||||||
|
other_val = other[k]
|
||||||
|
if isinstance(other_val, dict):
|
||||||
|
if isinstance(self_val, ConfigDict):
|
||||||
|
self_val.update(other_val)
|
||||||
|
elif isinstance(self_val, dict):
|
||||||
|
self[k] = ConfigDict(self_val)
|
||||||
|
self[k].update(other_val)
|
||||||
|
else:
|
||||||
|
type_error = True
|
||||||
|
elif isinstance(other_val, type(self_val)):
|
||||||
|
self[k] = other_val
|
||||||
|
else:
|
||||||
|
type_error = True
|
||||||
|
|
||||||
|
if type_error:
|
||||||
|
raise TypeError(
|
||||||
|
f"[{k!r}] values of incompatible types: {self_val!r}, {other_val!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# add keys -> values which are missing
|
||||||
|
for k in other_set - self_set:
|
||||||
|
v = other[k]
|
||||||
|
if isinstance(v, dict):
|
||||||
|
self[k] = ConfigDict(v)
|
||||||
|
else:
|
||||||
|
self[k] = v
|
||||||
|
|
||||||
|
|
||||||
|
def load_configuration(addl_config_file=None, addl_email_overrides_file=None):
|
||||||
|
"""Load stored configuration.
|
||||||
|
|
||||||
|
This function loads default, system-wide, and if specified, additional
|
||||||
|
configuration files into the `config` and `email_overrides`
|
||||||
|
dictionaries."""
|
||||||
|
# Load default files.
|
||||||
|
try:
|
||||||
|
default_config = toml.load(config_files['default']['configuration'])
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Default configuration file {config_files['default']['configuration']} not found."
|
||||||
|
) from e
|
||||||
|
|
||||||
|
try:
|
||||||
|
default_email_overrides = toml.load(config_files['default']['email_overrides'])
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Default email overrides file {config_files['default']['email_overrides']} not found."
|
||||||
|
) from e
|
||||||
|
|
||||||
|
# Attempt to load system-wide files.
|
||||||
|
try:
|
||||||
|
system_config = toml.load(config_files['system']['configuration'])
|
||||||
|
except FileNotFoundError:
|
||||||
|
system_config = {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
system_email_overrides = toml.load(config_files['system']['email_overrides'])
|
||||||
|
except FileNotFoundError:
|
||||||
|
system_email_overrides = {}
|
||||||
|
|
||||||
|
# Load additional files (say, specified on the command line), if any.
|
||||||
|
if addl_config_file:
|
||||||
|
try:
|
||||||
|
addl_config = toml.load(addl_config_file)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Additional configuration file {addl_config_file} not found."
|
||||||
|
) from e
|
||||||
|
else:
|
||||||
|
addl_config = {}
|
||||||
|
|
||||||
|
if addl_email_overrides_file:
|
||||||
|
try:
|
||||||
|
addl_email_overrides = toml.load(addl_email_overrides_file)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Additional email overrides file {addl_email_overrides_file} not found."
|
||||||
|
) from e
|
||||||
|
else:
|
||||||
|
addl_email_overrides = {}
|
||||||
|
|
||||||
|
config.clear()
|
||||||
|
config.update(default_config)
|
||||||
|
config.update(system_config)
|
||||||
|
config.update(addl_config)
|
||||||
|
|
||||||
|
email_overrides.clear()
|
||||||
|
email_overrides.update(default_email_overrides)
|
||||||
|
email_overrides.update(system_email_overrides)
|
||||||
|
email_overrides.update(addl_email_overrides)
|
||||||
|
|
||||||
|
|
||||||
|
config = ConfigDict()
|
||||||
|
email_overrides = ConfigDict()
|
|
@ -4,4 +4,5 @@ dogpile.cache
|
||||||
python-fedora
|
python-fedora
|
||||||
PyYAML
|
PyYAML
|
||||||
requests
|
requests
|
||||||
|
toml
|
||||||
urllib3
|
urllib3
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue