This is how I handle configuration in my python code, currently, but I guess it might look magic to people, and it is cumbersome to code. How can this be improved? Are there better solutions?
The purpose is to have a general working configuration stored in UPPER CASE in global variables at the beginning of the file. When first run, it creates a config.json
file with that configuration. That can be edited by the user, and it would supersede the default configuration, if run again. In order to still benefit from typing hints in modern IDEs, the cfg[]
dict is not used directly, but the configuration is written back to the global variables.
This is cumbersome to code, and it contains a lot of repetition. Every variable is touched multiple times.
How can I improve this system and make it more elegant and transparent?
import json
from typing import Any, Dict
CONFIG_FILE = 'config.json'
GLOBAL_CONFIG_EXAMPLE_PORT = 3
def read_config() -> Dict[str, Any]:
try:
with open(CONFIG_FILE) as config_file:
return json.load(config_file)
except FileNotFoundError:
pass
# generate standard config file
cfg = {
'example': {
'port': GLOBAL_CONFIG_EXAMPLE_PORT,
},
}
with open(CONFIG_FILE, 'w') as f:
json.dump(cfg, f)
set_global_variables(cfg)
return cfg
def set_global_variables(cfg: Dict[str, Any]):
global GLOBAL_CONFIG_EXAMPLE_PORT
GLOBAL_CONFIG_EXAMPLE_PORT = cfg['example']['port']
def main():
cfg: Dict[str, Any] = read_config()
print(cfg['example']['port'], GLOBAL_CONFIG_EXAMPLE_PORT)
if __name__ == "__main__":
main()
A real-life config file would be this:
{
"mqtt": {
"host": "10.21.1.77",
"port": 1883,
"topic": "EXTENSE/Lab/XYZ/move/#",
"topic_ack": "EXTENSE/Lab/XYZ/move/ack"
},
"signal": {
"save": true,
"length": 60
},
"sensor": {
"num": 5,
"trigger": 4
},
"logging": {
"level": 10,
"filename": "log.txt",
"console": true,
"format": "%(asctime)s %(levelname)s: %(message)s"
}
}
PS: How would you go about writing a test for this, efficiently?
Addendum: Is it feasible and sensible to do some magic on the variable name which is all upper case, split them by underscore, and create the dict automatically?