typsensitiver Konfigurationsparser

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

Hallo Freunde,

ich möchte meine Programme gern flexibler gestalten und variable optionen per Konfigurationsdatei einlesen, statt die Dateiköpfe anzupassen (auch die von Modulen). Beispielsweise um unterschiedliche feste Einstellungen für jeden Benutzer zu verwenden.

Bei meiner Suche fand ich Unmengen von Treffern für ConfigParser. Der scheint aber nur Strings zurückzugeben. Wenn ich nun einen Wert einbinde, der einen bestimmten Typ haben soll, muss ich momentan immer eine explizite Konvertierung durchführen.

Beispielsweise:

Code: Alles auswählen

freq = float(config.get("fft", "freq"))
Diese lange Anweisung ist vielleicht formal korrekt, aber nicht pythonisch kurz/prägnant/effektiv.

Gibt es eine Möglichkeit, sich die Optionswerte typgerecht konvertiert zu holen? Ohne eval-Workaround!
Oder gibt es einen Konfigurationsparser, der die Konvertierung beim Einlesen durchführt (also Kommazahlen -> float, Text in Anführungsstrichen -> string/unicode)? Ich habe kein Problem damit, Strings in der Konfigurationsdatei in Anführungszeichen zu setzen.

Viele Grüße,
Michael
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Weiß nicht ob es da was fertiges gibt... Aber könntest du doch eben eine Wrapper Klasse machen, die dann get_float() und get_int() usw. hat... Die Funktionen können dann auch brauchbare Fehler melden, wenn der Typ nicht stimmt...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

Um Konfigurationsparameter zu speichern, gibt es ja noch genug andere Formate. YAML ist, glaube ich, sehr beliebt und typisiert.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

jens hat geschrieben:Aber könntest du doch eben eine Wrapper Klasse machen, die dann get_float() und get_int() usw. hat...
Stimmt, an die Variante hatte ich noch nicht gedacht. Lieber wäre es mir natürlich, wenn es etwas Fertiges gäbe, das die Daten schon beim Einlesen konvertiert und so diesen wiederholten Vorgang verhindert. Andererseits werden die Funktionen auch nicht soo oft aufgerufen und es geht mir in erster Linie um Übersichtlichkeit und weniger um Performance. Wo ich sie in Schleifen verwende, erstelle ich ohnehin lokale Variablen.

Also danke nochmal, dann erstelle ich wahrscheinlich fget, iget, sget usw. Methoden, falls es kein anderes Modul gibt. :-)
Diese Nachricht zersört sich in 5 Sekunden selbst ...
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Indem du explizit den Typ für jeden Konfigurationswert angibst, wird die Konfiguration mehr oder weniger validiert. Was wenn der User eine Zahl angibt statt einem String? Der Parser wird nicht magisch wissen was du eigentlich haben willst und irgendwo in deinem Code wird es dann für den User zu einer verwirrenden Exception kommen, es sei den du testest wieder jedesmal ob du den richtigen Typ zurückbekommst. Ich hab aber den Eindruck letzteres möchtest du nicht unbedingt.
BlackJack

@Michael Schneider: `ConfigParser`-Objekte haben bereits neben der `get()`-Methode auch `getboolean()`, `getfloat()`, und `getint()`. Da muss man keinen Wrapper für schreiben.

Wenn das Dateiformat wichtig ist gibt es `ConfigObj` wo man eine Spezifikation für die Datei erstellen kann, mit Typen, Wertebereichen, und Defaultwerten und die Configurationsdatei dann dagegen validideren kann, inklusive Typumwandlungen.

Ansonsten würde ich mich Sirius3 anschliessen und JSON oder YAML vorschlagen. Die kennen schon die Grundtypen, es sind Verschachtelungen möglich, und `json` ist in der Standardbibliothek vorhanden während YAML-Parser zwar extra installiert werden müssten, das Format aber eine zusätzliche Typannotation vorsieht, mit der man eigene Typen ”erfinden” kann wenn man das braucht.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

BlackJack hat geschrieben:@Michael Schneider: `ConfigParser`-Objekte haben bereits neben der `get()`-Methode auch `getboolean()`, `getfloat()`, und `getint()`. Da muss man keinen Wrapper für schreiben.
Stimmt, das sind Funktionen von RawConfigParser, über die ich vorhin leider drüberweg gesprungen bin.
Danke, ich werde das dann wohl mit JSON machen.
Diese Nachricht zersört sich in 5 Sekunden selbst ...
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Ich kann JSON für Konfiguration überhaupt nicht empfehlen. Solange man nur eine handvoll von Werten hat kommt man damit vielleicht ganz gut zurecht, man wird aber eher früher als später mehr haben und Kommentare haben wollen, die JSON nicht erlaubt. Allein die Tatsache dass YAML Kommentare kennt, macht es schon wesentlich attraktiver.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und man kann auch einfach normale Python-Dateien für die Konfiguration verwenden. Also eine Datei, wo man nur Schlüssel-Wert-Pärchen hat. Das ist sehr flexibel, wenn man auch mit Python-spezifischen Objekten arbeiten möchte, die mal keine Zahlen, Wahrheitswerte oder Zeichenketten sind.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

snafu hat geschrieben:Und man kann auch einfach normale Python-Dateien für die Konfiguration verwenden. Also eine Datei, wo man nur Schlüssel-Wert-Pärchen hat.
Genau das habe ich gerade gemacht. Die Attribute können schön kurz angesprochen werden. Aber geänderte Werte wieder rauszuschreiben ist nicht ganz so bequem. :-)
Diese Nachricht zersört sich in 5 Sekunden selbst ...
BlackJack

Und die Konfigurationsdaten in anderen Sprachen zu verwenden ist nicht so einfach.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Michael Schneider hat geschrieben:Aber geänderte Werte wieder rauszuschreiben ist nicht ganz so bequem. :-)
Dass der Benutzer einfach direkt in die Python-Datei schreibt, ist keine Option? Man kann bei Bedarf ja auch eine einfach Funktion zum Einlesen schreiben, die zusätzlich prüft, ob die Konfigurationsdatei die korrekten Bezeichnungen enthält. Für letzteres als kleiner Denkanstoß:

Code: Alles auswählen

set(dir(dein_config_modul)) == set(erwartete_bezeichnungen)
Bei Sets kann man auch mit Teilmengen arbeiten. Das hieße dann zu schauen, ob die Namen aus dem `config`-Modul eine Teilmenge der erwarteten Namen sind. Da ist eigentlich viel möglich mit relativ wenig Aufwand.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

BlackJack hat geschrieben:Und die Konfigurationsdaten in anderen Sprachen zu verwenden ist nicht so einfach.
Stimmt. Nur ist das nicht immer relevant. Spontan wüsste ich jetzt auch keinen Anwendungsfall, wo man die Konfiguration für das eigene Programm in einer anderen Sprache verwenden möchte.

Wie gesagt: Es kommt auch darauf an, was man in der Datei als Typen für die Werte benutzt. Integer, Strings und Booleans sind natürlich prima austauschbar. Aber was ist, wenn ich an einer Stelle mal eine bestimmte Klasse zwecks spezifischer Implementierung angeben möchte? Dann ist es einfacher, die Klasse auf dem Python-Weg ggf mit vorherigem Import eines Moduls zu referenzieren. Die Alternative wären halt Strings, welche beim Einlesen an `__import__()` übergeben werden. Aber warum sollte man das so kompliziert lösen wollen? Auch möchte man vielleicht einen Wahrheitswert aufgrund eines Tests zur Laufzeit angeben. Da sind ebenfalls Python-Files eine praktische Lösung, anstatt für sowas einen eigenen Parser zu erfinden.

Wenn es generell um den Austausch von Informationen geht, dann ist JSON wiederum deutlich geeigneter. Aber hier geht es ja um eine Konfigurationsdatei.
BlackJack

@snafu: Nur weil Dir nichts einfällt heisst das ja nicht das es solche Fälle nicht gibt. Ich hatte gerade so einen Fall das ich ganz gerne die Konfigurationsdaten eines in PHP geschriebenen Programms gelesen hätte. Das wäre echt sehr praktisch gewesen Teile von den Daten jetzt nicht parallel noch mal in einer anderen Konfigurationsdatei zu haben. Blöderweise hatte der Programmierer auch keine Ahnung dass das mal jemand nicht mit PHP machen wollte und hat die als PHP-Quelltext geschrieben.

Wenn man *in* der Konfigurationsdatei Programmcode braucht, dann ist die IMHO zu kompliziert. Dann müsste man ja auch Unit-Tests für *Konfigurationsdateien* schreiben.

Das was Du da beschreibst mit ganzen Klassen ist dann in der Tat etwas mehr und komplexer als Konfigurationsdateien und wird allgemein als Plugin-System bezeichnet.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und was ist dann z.B. mit den in Django üblichen Settings-Dateien, wo ja ebenfalls der Ansatz gewählt wird, Python-Dateien zu benutzen? Deiner Meinung nach der völlig falsche Ansatz? Oder gar ein Plugin-System? :o
BlackJack hat geschrieben:Blöderweise hatte der Programmierer auch keine Ahnung dass das mal jemand nicht mit PHP machen wollte und hat die als PHP-Quelltext geschrieben.
Mit Vorteilen auf der einen Seite entstehen meistens leider auch Nachteile auf der anderen Seite. Soll man sich jetzt von einer Idee verabschieden, die für 99% der Anwender hilfreich ist, weil sie für 1% der Anwender in einem ganz bestimmten Fall einen Nachteil bringt?
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

snafu hat geschrieben:
Michael Schneider hat geschrieben:Aber geänderte Werte wieder rauszuschreiben ist nicht ganz so bequem. :-)
Dass der Benutzer einfach direkt in die Python-Datei schreibt, ist keine Option?
Das ist jetzt der Fall. Ich meinte, dass der User z.B. die Größe von Fenstern auf seinen Bildschirm abstimmen (d.h. nicht unbedingt Vollbild) und die Konfiguration wieder rausschreiben kann. Der ConfigParser hat dafür die .write Methode.
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Nutze doch einfach `setattr(config_modul, name, wert)`.
Benutzeravatar
Michael Schneider
User
Beiträge: 569
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Brandenburg

snafu hat geschrieben:Nutze doch einfach `setattr(config_modul, name, wert)`.
Ich meinte das Speichern in die Datei, damit die Settings beim nächsten Start gleich wieder geladen werden. Also wofür die config gedacht ist. Das kann man natürlich auch in eine extra Speicherdatei schreiben, was aber umständlicher und komplizierter wäre.
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Tja, da käme dann wohl wieder JSON oder YAML ins Spiel. Das Speichern einer durch Programmcode veränderten Config-Datei im Python-Format ist deutlich komplizierter als das Einlesen oder als wenn der Benutzer die Datei direkt editiert. Theoretisch könnte man vieleicht was mithilfe des `ast`-Modus basteln, aber da sollte man sich gut überlegen, ob man wirklich soviel Aufwand betreiben möchte.

Das wäre übrigens ein für mich deutlich gewichtigerer Grund, wann man auf Python-Dateien für die Konfiguration verzichten sollte.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Achso, es gäbe noch das `pickle`-Modul. Eine gepickelte Python-Datei ist dann allerdings nicht mehr für Menschen lesbar.
Antworten