Zwei Config Dateien, Werte bevorzugen
Verfasst: Montag 15. März 2021, 18:39
Hallo,
ich möchte für mein Programm eine Config Datei im TOML Format erstellen.
Genauer gesagt zwei Dateien: Eine Default Config und eine User Config (welche die Default Werte überschreibt).
Also wenn ein Wert in der User Datei vorhanden ist, dann soll dieser genommen werden, ansonsten wird der Wert aus der Default Datei geholt.
Die TOML Datei wird beim einlesen ein Dict. Heißt ich kann die Werte theoretisch ganz easy erreichen.
Um das ganze Konstrukt an verschiedenen Stellen einfach zu integrieren würde ich dafür gerne eine Klasse schreiben.
Zum lesen eines Wertes aus der Datei soll die Methode .get() dienen.
Zum schreiben .set()
.get() hat bereits super funktioniert. Dann habe ich try excepts eingebaut und es ging nicht mehr.
Dann habe ich es mit zwei weiteren unterschiedlichen Anläufen probiert. Hat auch nicht funktioniert.
Habs mir quasi zerschossen.
(Ja ich sollte git intensiver nutzen...)
.set() habe ich noch gar nicht hinbekommen. Aber ist jetzt mal außen vor, da bin ich noch nicht tiefer eingetaucht.
Wichtig wäre mir, dass .get() wieder ordentlich funktioniert.
Es wäre super hilfreich wenn mir hier jemand einen Ansatz aufzeigen könnte. Meine Denkansätze sind kreuz und quer.
Aktuell sieht die Klasse so aus:
Und das ist der aktuelle Fehler:
Falls notwendig, hier noch schnell die zwei Test Config Dateien:
PS: Das ist so ziemlich die erste größere Klasse die ich schreibe und versuche halbwegs sinnvoll zu strukturieren.
Wäre super wenn da vielleicht auch nochmal jemand ne Rückmeldung geben könnte, ob das so in etwa in Ordnung ist, oder was man besser machen könnte.
Vielen lieben Dank im voraus!
ich möchte für mein Programm eine Config Datei im TOML Format erstellen.
Genauer gesagt zwei Dateien: Eine Default Config und eine User Config (welche die Default Werte überschreibt).
Also wenn ein Wert in der User Datei vorhanden ist, dann soll dieser genommen werden, ansonsten wird der Wert aus der Default Datei geholt.
Die TOML Datei wird beim einlesen ein Dict. Heißt ich kann die Werte theoretisch ganz easy erreichen.
Um das ganze Konstrukt an verschiedenen Stellen einfach zu integrieren würde ich dafür gerne eine Klasse schreiben.
Zum lesen eines Wertes aus der Datei soll die Methode .get() dienen.
Zum schreiben .set()
.get() hat bereits super funktioniert. Dann habe ich try excepts eingebaut und es ging nicht mehr.
Dann habe ich es mit zwei weiteren unterschiedlichen Anläufen probiert. Hat auch nicht funktioniert.
Habs mir quasi zerschossen.

.set() habe ich noch gar nicht hinbekommen. Aber ist jetzt mal außen vor, da bin ich noch nicht tiefer eingetaucht.
Wichtig wäre mir, dass .get() wieder ordentlich funktioniert.
Es wäre super hilfreich wenn mir hier jemand einen Ansatz aufzeigen könnte. Meine Denkansätze sind kreuz und quer.
Aktuell sieht die Klasse so aus:
Code: Alles auswählen
import os, sys, toml, logging
class Config():
'''
Ermöglicht einfachen Zugang zur Konfigurationsdatei.
'''
def __init__(self):
FILENAME = 'config.toml'
# Benutzer Config
self.USER_DIR = os.path.join(os.environ['APPDATA'], 'MyAppName')
self.USER_FILE = os.path.normpath(os.path.join(self.USER_DIR, FILENAME))
# log.debug(f'Config().__init__ USER_FILE = {self.USER_FILE}')
# Default Config
self.DEFAULT_DIR = os.path.join(os.path.dirname(__file__), '..\\')
self.DEFAULT_FILE = os.path.normpath(os.path.join(self.DEFAULT_DIR, FILENAME))
# log.debug(f'Config().__init__ DEFAULT_FILE = {self.DEFAULT_FILE}')
def _create_dir(self):
'''
Erstellt den Config Ordner falls dieser nicht existiert.
'''
if not os.path.isdir(self.USER_DIR):
os.mkdir(self.USER_DIR)
log.debug(f'Config Ordner {self.USER_DIR} wurde erstellt.')
def _write_file(self, cfg):
'''
Schreibt eine Datei.
Falls diese bereits vorhanden ist wird sie überschrieben.
cfg = Config als Dict
'''
with open(self.USER_FILE, 'w') as file:
toml.dump(cfg, file)
def _nested_get(self, dic, keys):
'''
Gibt einen Wert aus einem verschachtelten Dict zurück.
dic = Dict
keys = ['key'] oder ['key', 'subkey']
'''
for key in keys:
dic = dic[key]
return dic
def _load_file(self, file):
cfg = toml.load(file)
log.debug(f'Config().get() cfg = {file}')
log.debug(f'Config().get() cfg = {str(cfg)}')
def get(self, keys):
'''
Holt einen Wert aus der Konfigurationsdatei.
Werte aus Benutzer Datei überschreiben Werte aus Default Datei.
keys = ['key'] oder ['key', 'subkey']
'''
# Wenn User Config File existiert
if os.path.isfile(self.USER_FILE) is True:
# User Config laden
cfg = self._load_file(self.USER_FILE)
# Wert aus User Config holen
try:
return self._nested_get(cfg, keys)
# Wert nicht in User Config vorhanden
except KeyError:
log.warning(f'Config().get() Wert für {keys} nicht in User Config vorhanden.')
# Wenn Default Config File existiert
if os.path.isfile(self.DEFAULT_FILE) is True:
# Default Config laden
cfg = self._load_file(self.DEFAULT_FILE)
# Wert aus Default Config holen
try:
return self._nested_get(cfg, keys)
# Wert nicht in Default Config vorhanden
except KeyError:
log.critical(f'Config().get() Wert für {keys} nicht in Default Config vorhanden.')
sys.exit(1)
# Default Config nicht gefunden
else:
log.critical('Config().get() Default Config File existiert nicht.')
sys.exit(1)
# Wenn Default Config File existiert
elif os.path.isfile(self.DEFAULT_FILE) is True:
log.warning('Config().get() User Config File existiert nicht.')
# Default Config laden
cfg = self._load_file(self.DEFAULT_FILE)
# Wert aus Default Config holen
try:
return self._nested_get(cfg, keys)
# Wert nicht in Default Config vorhanden
except KeyError:
log.critical(f'Config().get() Wert für {keys} nicht in Default Config vorhanden.')
sys.exit(1)
else:
log.critical('Config().get() Weder User noch Default Config File existiert.')
sys.exit(1)
def _nested_set(self, dic, keys, value):
'''
Schreibt einen Wert in ein verschachteltes Dict.
Nicht vorhandene Keys werden erstellt.
dic = Dict
keys = ['key'] oder ['key', 'subkey']
value = ?
Siehe https://stackoverflow.com/a/37704379
'''
for key in keys[:-1]:
dic = dic.setdefault(key, {})
dic[keys[-1]] = value
def set(self, keys, value):
'''
Schreibt einen Wert in die Konfigurationsdatei.
keys = ['key'] oder ['key', 'subkey']
value = ?
'''
# Versuche vorhandene User Config zu bearbeiten
try:
cfg = toml.load(self.USER_FILE)
# debug print
print('cfg: ' + str(cfg))
cfg = self._nested_set(cfg, keys, value)
self._write_file(cfg)
# Neue User Config erstellen
except Exception:
# Config Ordner erstellen, falls er nicht existiert.
self._create_dir()
cfg = self._nested_set(cfg, keys, value)
self._write_file(cfg)
if __name__ == '__main__':
conf = Config()
one = conf.get(['key', 'subkey', 'one'])
print('ONE: ' + str(one))
Code: Alles auswählen
Traceback (most recent call last):
File "C:\Users\MyName\Projekte\MyAppName\...\helpers.py", line 287, in <module>
one = conf.get(['key', 'subkey', 'one'])
File "C:\Users\MyName\Projekte\MyAppName\...\helpers.py", line 197, in get
return self._nested_get(cfg, keys)
File "C:\Users\MyName\Projekte\MyAppName\...\helpers.py", line 138, in _nested_get
dic = dic[key]
TypeError: 'NoneType' object is not subscriptable
Code: Alles auswählen
# User
[key]
file = 'user'
[key.subkey]
two = 'rewrite by user'
# Default
[key]
file = 'default'
[key.subkey]
one = 1
two = 2
three = 3
PS: Das ist so ziemlich die erste größere Klasse die ich schreibe und versuche halbwegs sinnvoll zu strukturieren.
Wäre super wenn da vielleicht auch nochmal jemand ne Rückmeldung geben könnte, ob das so in etwa in Ordnung ist, oder was man besser machen könnte.
Vielen lieben Dank im voraus!
