Wie schlecht ist die Idee, Python-Dateien als Konfigurationsdateien zu verwenden?

72

Ich habe immer JSON- Dateien für die Konfiguration meiner Anwendungen verwendet. Ich habe angefangen, sie zu verwenden, als ich viel Java codiert habe, und jetzt arbeite ich hauptsächlich an der serverseitigen und datentechnischen Python-Entwicklung und bin mir nicht sicher, ob JSON der richtige Weg ist.

Ich habe gesehen, wie Sellerie aktuelle Python-Dateien für die Konfiguration verwendet. Anfangs war ich skeptisch. Die Idee, einfache Python-Datenstrukturen für die Konfiguration zu verwenden, wächst jedoch langsam. Einige Profis:

  • Die Datenstrukturen stimmen mit denen überein, in die ich normalerweise programmiere. Daher muss ich die Einstellung nicht ändern.
  • Meine IDE (PyCharm) versteht den Zusammenhang zwischen Konfiguration und Code. Ctrl+ Bermöglicht das einfache Wechseln zwischen Konfiguration und Code.
  • Ich brauche nicht mit IMO unnötig strengen JSON zu arbeiten . Ich betrachte Sie doppelte Anführungszeichen, keine nachgestellten Kommas und keine Kommentare.
  • Ich kann Testkonfigurationen in die Anwendung schreiben, an der ich arbeite, und sie dann einfach in eine Konfigurationsdatei portieren, ohne Konvertierung und JSON-Analyse durchführen zu müssen.
  • Bei Bedarf können sehr einfache Skripte in der Konfigurationsdatei erstellt werden. (Obwohl dies sehr, sehr begrenzt sein sollte.)

Meine Frage lautet also: Wenn ich wechsle, wie schieße ich mir in den Fuß?

Kein ungelernter Endbenutzer verwendet die Konfigurationsdateien. Alle Änderungen an den Konfigurationsdateien werden derzeit für Git festgeschrieben und im Rahmen einer kontinuierlichen Bereitstellung auf unseren Servern bereitgestellt. Es gibt keine manuellen Konfigurationsänderungen, es sei denn, es liegt ein Notfall vor oder es befindet sich in der Entwicklung.

(Ich habe über YAML nachgedacht , aber etwas daran ärgert mich. Im Moment ist es also vom amerikanischen Tisch verschwunden.)

André Christoffer Andersen
quelle
39
Unqualifiziert ist nicht Ihr Problem. Bösartig ist.
Blrfl
9
Was meinst du mit "off the american table" ?
Peter Mortensen
24
"Also, jetzt ist es vom amerikanischen Tisch." === "Im Moment ist es also, wie der Amerikaner sagt, vom Tisch."
Bischof
7
Wenn Sie JSON nicht mögen, sollten Sie yaml ausprobieren. Ich mag es für configs sehr. Insbesondere bei größeren Zeichenfolgen ist YAML besser lesbar als JSON.
Christian Sauer
5
@bishop "off the table" in britischem Englisch bedeutet, dass es nicht mehr in Betracht gezogen wird, wenn die Anträge des Parlaments in der Mitte des Unterhauses als Referenz für die Diskussion auf den Tisch gelegt und daher auch "zur Diskussion gestellt" werden (Parlamentsprotokoll 1799 - books.google.co.uk/… ), AFAIK die US-Bedeutung ist die gleiche, aber ich weiß nicht, ob Sie einen Tisch in Ihrem Parlament haben.
Pete Kirkham

Antworten:

92

Die Verwendung einer Skriptsprache anstelle einer Konfigurationsdatei sieht auf den ersten Blick großartig aus: Sie haben die volle Leistungsfähigkeit dieser Sprache zur Verfügung und können einfach eval()oder importes. In der Praxis gibt es einige Fallstricke:

  • Es ist eine Programmiersprache, die gelernt werden muss. Um die Konfiguration zu bearbeiten, müssen Sie diese Sprache ausreichend gut beherrschen. Konfigurationsdateien haben normalerweise ein einfacheres Format, bei dem es schwieriger ist, einen Fehler zu machen.

  • Es ist eine Programmiersprache, was bedeutet, dass die Konfiguration schwierig zu debuggen sein kann. Mit einer normalen Konfigurationsdatei sehen Sie sich diese an und sehen, welche Werte für jede Eigenschaft bereitgestellt werden. Bei einem Skript müssen Sie es möglicherweise zuerst ausführen, um die Werte anzuzeigen.

  • Es ist eine Programmiersprache, die es schwierig macht, eine klare Trennung zwischen der Konfiguration und dem eigentlichen Programm aufrechtzuerhalten. Manchmal möchten Sie diese Art von Erweiterbarkeit, aber an diesem Punkt suchen Sie wahrscheinlich eher ein echtes Pluginsystem.

  • Es ist eine Programmiersprache, was bedeutet, dass die Konfiguration alles kann, was die Programmiersprache kann. Entweder verwenden Sie eine Sandbox-Lösung, die einen Großteil der Flexibilität der Sprache zunichte macht, oder Sie setzen großes Vertrauen in den Konfigurationsautor.

Die Verwendung eines Skripts für die Konfiguration ist also wahrscheinlich in Ordnung, wenn die Zielgruppe Ihres Tools Entwickler sind, z. B. Sphinx config oder die Datei setup.py in Python-Projekten. Andere Programme mit ausführbarer Konfiguration sind Shells wie Bash und Editoren wie Vim.

Die Verwendung einer Programmiersprache zur Konfiguration ist erforderlich, wenn die Konfiguration viele bedingte Abschnitte enthält oder Callbacks / Plugins bereitstellt. Die direkte Verwendung eines Skripts anstelle von eval () - einige Konfigurationsfelder sind in der Regel leichter zu debuggen (denken Sie an die Stack-Traces und Zeilennummern!).

Die direkte Verwendung einer Programmiersprache kann auch eine gute Idee sein, wenn sich Ihre Konfiguration so wiederholt, dass Sie Skripte schreiben, um die Konfiguration automatisch zu generieren. Aber vielleicht könnte ein besseres Datenmodell für die Konfiguration die Notwendigkeit einer solchen expliziten Konfiguration beseitigen? Beispielsweise kann es hilfreich sein, wenn die Konfigurationsdatei Platzhalter enthält, die Sie später erweitern. Eine andere Eigenschaft, die manchmal auftritt, sind mehrere Konfigurationsdateien mit unterschiedlicher Priorität, die sich gegenseitig überschreiben können, die jedoch einige eigene Probleme mit sich bringen.

In den meisten Fällen eignen sich INI-Dateien, Java-Eigenschaftendateien oder YAML-Dokumente besser für die Konfiguration. Für komplexe Datenmodelle kann auch XML anwendbar sein. Wie Sie bereits bemerkt haben, weist JSON einige Aspekte auf, die es als von Menschen bearbeitbare Konfigurationsdatei ungeeignet machen, obwohl es ein gutes Datenaustauschformat ist.

amon
quelle
25
Es gibt einige Konfigurationsdateiformate, die am bekanntesten "versehentlich vollständig" sind sendmail.cf. Dies würde darauf hinweisen, dass die Verwendung einer tatsächlichen Skriptsprache von Vorteil sein könnte, da diese tatsächlich so konzipiert wurde , dass sie vollständig ist. Allerdings , Turing-Vollständigkeit und „Tetris-Vollständigkeit“ sind zwei verschiedene Dinge, und während sendmail.cfbeliebige Funktionen berechnen kann, kann es nicht Ihre senden /etc/passwdüber das Netz oder formatieren Sie Ihre Festplatte, der Python oder Perl Lage wäre.
Jörg W Mittag
3
@ JörgWMittag Turin-Vollständigkeit bedeutet nicht, Dinge über das Netzwerk senden oder auf die Festplatte zugreifen zu können. Das heißt, bei Turin-Vollständigkeit geht es nicht um E / A, sondern um die Verarbeitung. Zum Beispiel wird CSS als vollständig in Turin betrachtet, aber es beeinträchtigt nicht Ihren permanenten Speicher. Sie haben an anderer Stelle gesagt, dass "Idris eine total reine funktionale Sprache ist, also per definitionem nicht Turing-vollständig", was nicht folgt, und anscheinend ist es Turin vollständig. Ich war davon überzeugt, dass Ihre Verwendung von Testris-complete bedeutet, dass die Sprache Turin vollständig ist, aber nicht in der Lage ist, vollständige E / A-Vorgänge durchzuführen. Dies ist anscheinend nicht das, was Sie meinen.
Theraot
6
@Theraot: "Total" bedeutet, dass immer zurückgegeben wird. Eine Turing-Maschine kann eine Endlosschleife ausführen, dh sie kann nicht zurückkehren. Ergo kann Idris nicht alles tun, was eine Turing-Maschine tut, was bedeutet, dass sie nicht vollständig ist. Dies gilt für alle abhängig typisierten Sprachen. Der springende Punkt einer abhängig typisierten Sprache ist, dass Sie beliebige Eigenschaften für Programme festlegen können, während Sie in einer Turing-complete-Sprache nicht einmal triviale Eigenschaften festlegen können, z. B. "Hält dieses Programm an?". Gesamtsprachen sind per Definition nicht Turing-vollständig, da Turing-Maschinen partiell sind.
Jörg W Mittag
10
Die Definition von "Turing-complete" lautet "kann eine Turing-Maschine implementieren". Die Definition von "Tetris-complete" lautet "kann Tetris implementieren". Der springende Punkt dieser Definition ist, dass Turing-Vollständigkeit in der realen Welt einfach nicht sehr interessant ist. Es gibt viele nützliche Sprachen, die nicht vollständig für Turing sind, z. B. HTML, SQL (vor 1999), verschiedene DSLs usw. OTOH, Turing-Vollständigkeit bedeutet nur, dass Sie Funktionen über natürliche Zahlen berechnen können Drucken auf dem Bildschirm, Zugriff auf das Netzwerk, Interaktion mit dem Benutzer, dem Betriebssystem und der Umgebung - all dies ist wichtig.
Jörg W Mittag
4
Der Grund, warum Edwin Brady dieses Beispiel verwendet hat, ist, dass viele Leute denken, dass Sprachen, die nicht vollständig sind, nicht für die allgemeine Programmierung verwendet werden können. Ich selbst habe auch gedacht, dass es sich bei vielen interessanten Programmen im Wesentlichen um Endlosschleifen handelt, die wir nicht stoppen möchten , z. B. Server, Betriebssysteme, Ereignisschleifen in einer GUI, Spieleschleifen. Viele Programme verarbeiten unendliche Daten, zB Ereignisströme. Früher dachte ich, dass man das nicht in einer ganzen Sprache schreiben kann, aber seitdem habe ich gelernt, dass man das kann , und deshalb finde ich es eine gute Idee, einen Begriff für diese Fähigkeit zu haben.
Jörg W Mittag
50

+1 für alles in Amons Antwort . Ich möchte folgendes hinzufügen:

Sie werden es bereuen, Python-Code als Konfigurationssprache verwendet zu haben, wenn Sie zum ersten Mal dieselbe Konfiguration aus Code importieren möchten, der in einer anderen Sprache geschrieben ist. Wenn beispielsweise Code, der Teil Ihres Projekts ist und in C ++ oder Ruby geschrieben wurde, oder etwas anderes in die Konfiguration geladen werden muss, müssen Sie den Python-Interpreter als Bibliothek verknüpfen oder die Konfiguration in einem Python-Coprozess analysieren die umständlich, schwierig oder hoch über dem Kopf sind.

Der gesamte Code, der diese Konfiguration heute importiert, ist möglicherweise in Python geschrieben, und Sie denken möglicherweise, dass dies auch morgen der Fall sein wird, aber wissen Sie es mit Sicherheit?

Sie sagten, Sie würden Logik (alles andere als statische Datenstrukturen) in Ihrer Konfiguration sparsam verwenden, wenn überhaupt, was gut ist, aber wenn etwas davon überhaupt vorhanden ist, werden Sie es in Zukunft schwierig finden, es rückgängig zu machen, damit Sie kann zu einer deklarativen Konfigurationsdatei zurückkehren.

EDIT for the record: Mehrere Personen haben diese Antwort dahingehend kommentiert, wie wahrscheinlich oder unwahrscheinlich es ist, dass ein Projekt jemals vollständig in einer anderen Sprache neu geschrieben werden kann. Es ist fair zu sagen, dass ein vollständiges abwärtskompatibles Umschreiben wahrscheinlich selten zu sehen ist. Was ich eigentlich im Sinn hatte, waren Teile desselben Projekts (und die Notwendigkeit, auf dieselbe Konfiguration zuzugreifen), die in verschiedenen Sprachen geschrieben waren. Beispiel: Bereitstellen des Stacks in C ++ für Geschwindigkeit, Bereinigen der Batch-Datenbank in Python und einige Shell-Skripte als Klebstoff. Also überleg auch für diesen Fall :)

Celada
quelle
1
@Mast, entschuldige, aber ich folge nicht. Der Name der Datei (unabhängig davon, ob sie mit .py endet oder nicht) ist weder hier noch dort. Der Punkt, den ich versuche, ist, dass, wenn es in Python geschrieben ist, Sie einen Python-Interpreter benötigen, um es zu lesen.
Celada
12
@ Mast Ich denke, Sie analysieren es falsch. Der Punkt, den ich aus dieser Antwort (sowohl original als auch bearbeitet) gezogen habe, ist, dass die Wahl, Konfigurationsdateien in einer Programmiersprache zu schreiben, es schwieriger macht, Code in einer anderen Sprache zu schreiben. Sie möchten beispielsweise Ihre App auf Anrdoid / iPhone portieren und verwenden eine andere Sprache. Sie müssen sich entweder (a) auf einen Python-Interpreter in der Handy-App verlassen (nicht ideal), (b) die Konfiguration in einem sprachunabhängigen Format neu schreiben und den verwendeten Python-Code neu schreiben oder (c) beibehalten Zwei Konfigurationsformate für die Zukunft.
Jon Bentley
4
@ JonBentley Ich nehme an, dass die Besorgnis relevant ist, wenn mehrsprachige Projekte geplant sind. Diesen Eindruck habe ich vom OP nicht bekommen. Darüber hinaus erfordert die Verwendung von Textdateien für die Konfiguration immer noch zusätzlichen Code (in allen Sprachen) für das eigentliche Parsen / Konvertieren von Werten. Technisch gesehen kann key=valueich nicht verstehen, warum ein Java / C ++ - Programm die Python-Datei nicht als reine Textdatei lesen und gleich analysieren konnte, wenn sie zu etwas anderem in wechseln mussten , wenn sie die Python-Seite auf Zuweisungen für config beschränkten die Zukunft. Ich sehe keinen Bedarf für einen vollwertigen Python-Interpreter.
Code_dredd
3
@ray Richtig, aber die Antwort ist immer noch nützlich, da Fragen nicht nur auf die Person zutreffen sollten, die sie veröffentlicht. Wenn Sie ein standardisiertes Format (z. B. INI, JSON, YAML, XML usw.) verwenden, verwenden Sie wahrscheinlich eine vorhandene Analysebibliothek, anstatt Ihre eigene zu schreiben. Dies reduziert den zusätzlichen Aufwand auf eine Adapterklasse, die mit der Parsing-Bibliothek verbunden werden kann. Wenn Sie sich auf key = value beschränken, werden die meisten Gründe des OP beseitigt, Python zu verwenden, und Sie können genauso gut ein anerkanntes Format verwenden.
Jon Bentley
3
Ich musste dies vor ein paar Jahren tun, als ein in Lua geschriebenes Tool Lua-Skript als Konfiguration verwendete. Dann wollten sie, dass wir ein neues Tool in C # schreiben, und baten uns ausdrücklich, das Lua-Konfigurationsskript zu verwenden. Sie hatten insgesamt 2 Zeilen, die tatsächlich programmierbar und nicht einfach x = y waren, aber ich musste wegen ihnen noch etwas über Open-Source-Lua-Interpreter für .net lernen. Es ist kein rein theoretisches Argument.
Kevin Fee
21

Die anderen Antworten sind bereits sehr gut, ich werde nur meine Erfahrungen mit der praktischen Anwendung in ein paar Projekten einbringen.

Vorteile

Sie sind meistens schon buchstabiert:

  • Wenn Sie sich in einem Python-Programm befinden, ist das Parsen ein Kinderspiel ( eval). es funktioniert auch bei komplexeren Datentypen automatisch (in unserem Programm haben wir geometrische Punkte und Transformationen, die einfach durch repr/ ausgegeben / geladen werden eval);
  • Das Erstellen einer "gefälschten Konfiguration" mit nur wenigen Codezeilen ist trivial.
  • Sie haben bessere Strukturen und, IMO, eine viel bessere Syntax als JSON (nur Kommentare und keine doppelten Anführungszeichen um Wörterbuchschlüssel zu setzen, ist ein großer Gewinn für die Lesbarkeit).

Nachteile

  • böswillige Benutzer können alles tun, was Ihr Hauptprogramm kann; Ich halte dies nicht für ein großes Problem, da ein Benutzer im Allgemeinen, wenn er eine Konfigurationsdatei ändern kann, bereits alles tun kann, was die Anwendung kann.
  • Wenn Sie nicht mehr in einem Python-Programm sind, haben Sie jetzt ein Problem. Während einige unserer Konfigurationsdateien für ihre ursprüngliche Anwendung privat blieben, wurde eine speziell zum Speichern von Informationen verwendet, die von mehreren verschiedenen Programmen verwendet werden, von denen sich die meisten derzeit in C ++ befinden und die jetzt einen zusammengehackten Parser für ein schlecht definiertes kleines Programm haben Teilmenge von Python repr. Das ist offensichtlich eine schlechte Sache.
  • Auch wenn Ihr Programm in Python bleibt, können Sie die Python-Version ändern. Angenommen, Ihre Anwendung wurde in Python 2 gestartet. nach vielen Tests Sie es geschafft , es zu Python 3 zu migrieren - leider Sie nicht wirklich alle Ihre Code testen - Sie haben alle Konfigurationsdateien rumliegen auf Ihrer Kunden Maschinen, geschrieben für Python 2 und auf dem Sie don habe nicht wirklich die Kontrolle. Sie können nicht einmal einen "Kompatibilitätsmodus" zum Lesen alter Konfigurationsdateien bereitstellen (was häufig für Dateiformate durchgeführt wird), es sei denn, Sie möchten den Python 2-Interpreter bündeln / aufrufen!
  • Selbst wenn Sie sich in Python befinden, ist das Ändern der Konfigurationsdatei in Code ein echtes Problem, da ... das Ändern von Code überhaupt nicht trivial ist, insbesondere von Code, der eine umfangreiche Syntax aufweist und nicht in LISP oder ähnlichem vorliegt. Ein Programm von uns hat eine Konfigurationsdatei , die Python ist, die ursprünglich von Hand geschrieben, aber das stellte sich später heraus , es wäre nützlich , über Software zu manipulieren (eine bestimmte Einstellung ist eine Liste der Dinge , die ist Art und Weise einfacher mit Hilfe eines GUI neu zu ordnen). Das ist ein großes Problem, weil:

    • Selbst wenn Sie nur eine Analyse durchführen → AST → Roundtrip zum Umschreiben ist nicht trivial (Sie werden feststellen, dass die Hälfte der vorgeschlagenen Lösungen später als "veraltet, nicht verwendet, funktioniert nicht in allen Fällen" markiert wird).
    • Selbst wenn sie funktionierten, ist AST viel zu niedrig. Sie sind im Allgemeinen daran interessiert, das Ergebnis der in der Datei durchgeführten Berechnungen zu manipulieren , nicht die Schritte, die dazu geführt haben.
    • Das bringt uns zu der einfachen Tatsache, dass Sie die Werte, an denen Sie interessiert sind, nicht einfach bearbeiten können, da sie möglicherweise durch eine komplexe Berechnung generiert werden, die Sie durch Ihren Code nicht verstehen / manipulieren können.

    Vergleichen Sie dies mit JSON, INI oder (Gott bewahre!) XML, wo die In-Memory-Darstellung immer bearbeitet und zurückgeschrieben werden kann, entweder ohne Datenverlust (XML, wo die meisten DOM-Parser Leerzeichen in Textknoten und Kommentarknoten belassen können) oder Mindestens einige Formatierungen gehen verloren (JSON, bei dem das Format selbst nicht viel mehr zulässt als die Rohdaten, die Sie lesen).


Es gibt also wie üblich keine eindeutige Lösung. Meine derzeitige Politik zu diesem Thema lautet:

  • wenn die Konfigurationsdatei ist:

    • Mit Sicherheit für eine Python-Anwendung und für eine private Anwendung - wie in diesem Fall wird niemand anders jemals versuchen, daraus zu lesen.
    • handgeschrieben;
    • von einer vertrauenswürdigen Quelle kommen;
    • Die Verwendung von Datentypen für Zielanwendungen ist eine echte Prämie.

    Eine Python-Datei kann eine gültige Idee sein.

  • wenn stattdessen:

    • Es besteht möglicherweise die Möglichkeit, dass eine andere Anwendung daraus gelesen wird.
    • Es besteht die Möglichkeit, dass diese Datei von einer Anwendung bearbeitet wird, möglicherweise sogar von meiner Anwendung selbst.
    • wird von einer nicht vertrauenswürdigen Quelle bereitgestellt.

    Ein "Nur-Daten" -Format ist möglicherweise eine bessere Idee.

Beachten Sie, dass es nicht erforderlich ist, eine einzige Auswahl zu treffen - ich habe kürzlich eine Anwendung geschrieben, die beide Ansätze verwendet. Ich habe eine fast nie geänderte Datei mit ersten Einstellungen, handschriftlichen Einstellungen, bei denen es Vorteile gibt, nette Python-Boni zu haben, und eine JSON-Datei für die Konfiguration, die über die Benutzeroberfläche bearbeitet wird.

Matteo Italia
quelle
1
sehr guter punkt zum generieren oder umschreiben von configs! Aber nur wenige andere Formate als XML können Kommentare in der Ausgabe speichern, was ich für die Konfiguration als äußerst wichtig erachte. Andere Formate führen manchmal ein note:Feld ein, das für die Konfiguration ignoriert wird.
amon
2
"Wenn ein Benutzer eine Konfigurationsdatei ändern kann, kann er bereits das tun, was die Anwendung kann" - das ist nicht ganz richtig. Wie wäre es mit dem Testen einer glänzenden Konfigurationsdatei, die jemand, den du nicht kennst, in Pastebin hochgeladen hat?
Dmitry Grigoryev
2
@DmitryGrigoryev: Wenn Sie auf dieses Ziel abzielen, können Sie Ihrem Opfer auch anweisen, einige zu kopieren und einzufügen curl ... | bash, was noch weniger problematisch ist. :-P
Matteo Italia
@DmitryGrigoryev: Und so etwas könnte es jemandem ermöglichen, ein Produktionssystem am ersten Arbeitstag komplett zu ruinieren. Wenn 'eval' Ihr Parser ist, gibt es keine Möglichkeit, es auf Probleme zu überprüfen, bevor es gelesen wurde. (der gleiche Grund, warum Shell-Skripte in der Produktion so schlecht sind). INI, YAML oder JSON sind in dieser Hinsicht sicher.
Joe
1
@DmitryGrigoryev: Mein Punkt ist nur, dass, wenn Ihr Opfertyp dumm genug ist, eine Konfigurationsdatei blind zu kopieren und einzufügen, Sie ihn / sie wahrscheinlich dazu verleiten können, alles auf seinem Computer mit weniger schrägen Methoden zu tun ("fügen Sie dies in eine Konsole ein, um Beheben Sie Ihr Problem! "). Auch bei nicht ausführbaren Konfigurationsdateien besteht ein hohes Schadensrisiko - selbst wenn Sie in böswilliger Absicht auf die Protokollierung kritischer Dateien verweisen (wenn die Anwendung mit ausreichenden Berechtigungen ausgeführt wird), kann dies zu Chaos auf dem System führen. Das ist der Grund, warum ich denke, dass es in der Praxis keinen großen Unterschied in Bezug auf die Sicherheit gibt.
Matteo Italia
8

Die Hauptfrage ist: Soll Ihre Konfigurationsdatei in einer vollständigen Turing-Sprache vorliegen (wie Python)? Wenn Sie dies wünschen, können Sie auch eine andere Skriptsprache (Turing complete) wie Guile oder Lua einbetten (da diese als "einfacher" zu verwenden oder einzubetten ist als Python). Lesen Sie das Kapitel zu Erweitern und Python einbinden ). Ich werde darauf nicht weiter eingehen (da andere Antworten - z. B. von Amon - ausführlich darauf eingegangen sind), aber beachten Sie, dass das Einbetten einer Skriptsprache in Ihre Anwendung eine wichtige architektonische Wahl ist , die Sie sehr früh in Betracht ziehen sollten; Ich empfehle wirklich nicht, diese Wahl später zu treffen!

Ein bekanntes Beispiel für ein durch "Skripte" konfigurierbares Programm ist der GNU-Emacs- Editor (oder wahrscheinlich AutoCAD im proprietären Bereich). Beachten Sie daher, dass einige Benutzer, wenn Sie Skripte akzeptieren, diese Funktion möglicherweise ausgiebig verwenden und ein Skript mit mehreren tausend Zeilen erstellen würden. Daher ist die Auswahl einer ausreichend guten Skriptsprache wichtig.

(Zumindest auf POSIX-Systemen) können Sie es jedoch als zweckmäßig erachten, die Konfigurationsdatei zum Zeitpunkt der Initialisierung dynamisch berechnen zu lassen Text, der aus einer Datei oder einem Befehl stammt). Dazu können Sie einfach die Konvention übernehmen (und dokumentieren ), dass ein Konfigurationsdateipfad, der mit a !oder a beginnt, |tatsächlich ein Shell- Befehl ist , den Sie als Pipeline lesen würden . Dadurch hat der Benutzer die Wahl, welche "Präprozessor" - oder "Skriptsprache" er am besten kennt.

(Sie müssen Ihrem Benutzer in Bezug auf Sicherheitsprobleme vertrauen, wenn Sie eine dynamisch berechnete Konfiguration akzeptieren.)

In Ihrem Initialisierungscode mainwürde Ihr (zum Beispiel) ein --config Argument akzeptieren confargund einiges FILE*configf;daraus abrufen. Wenn dieses Argument mit !(dh wenn (confarg[0]=='!')....) beginnt , würden Sie configf = popen(confarg+1, "r");diese Pipe mit verwenden und schließen pclose(configf);. Andernfalls würden Sie configf=fopen(confarg, "r");diese Datei verwenden und mit schließen fclose(configf);(Fehlerprüfung nicht vergessen). Siehe Pipe (7) , Popen (3) , Fopen (3) . Eine in Python codierte Anwendung enthält Informationen zu os.popen usw.

(Dokument auch für den seltsamen Benutzer, der eine Konfigurationsdatei mit dem Namen !foo.configpass übergeben möchte ./!foo.config, um den popenobigen Trick zu umgehen )

BTW, wie ein Trick ist nur eine Bequemlichkeit (zu vermeiden , dass die erweiterte Benutzer zB Code erfordern einige Shell - Skript zu generieren eine Konfigurationsdatei ). Wenn der Benutzer einen Fehler melden möchte, sollte er Ihnen die generierte Konfigurationsdatei senden ...

Beachten Sie, dass Sie Ihre Anwendung auch so gestalten können, dass Sie Plugins zum Zeitpunkt der Initialisierung verwenden und laden können , z. B. mit dlopen (3) (und Sie müssen Ihrem Benutzer in Bezug auf dieses Plugin vertrauen). Auch dies ist eine sehr wichtige architektonische Entscheidung (und Sie müssen einige recht stabile APIs und Konventionen für diese Plugins und Ihre Anwendung definieren und bereitstellen ).

Für eine Anwendung, die in einer Skriptsprache wie Python codiert ist, können Sie auch ein Programmargument für eval oder exec oder ähnliche Grundelemente akzeptieren . Wiederum sind die Sicherheitsprobleme das Anliegen des (fortgeschrittenen) Benutzers.

In Bezug auf das Textformat für Ihre Konfigurationsdatei (ob generiert oder nicht) glaube ich, dass Sie es meistens gut dokumentieren müssen (und die Wahl eines bestimmten Formats ist nicht so wichtig, ich empfehle jedoch, dass Ihr Benutzer es setzen kann einige übersprungene Kommentare drin). Sie könnten JSON (vorzugsweise mit einem JSON-Parser, der Kommentare mit der üblichen //bis-eol-Methode oder /*... */... akzeptiert und überspringt ) oder YAML oder XML oder INI oder Ihr eigenes Ding verwenden. Das Parsen einer Konfigurationsdatei ist relativ einfach (und Sie werden viele Bibliotheken finden, die sich auf diese Aufgabe beziehen).

Basile Starynkevitch
quelle
+1 für die Erwähnung der Turing-Vollständigkeit von Programmiersprachen. Einige interessante Arbeiten zeigen, dass die Einschränkung der Rechenleistung des Eingabeformats der Schlüssel zur Sicherung der Eingabehandhabungsebene ist. Die Verwendung einer Programmiersprache von Turing-complete geht in die entgegengesetzte Richtung.
Matheus Moreira
2

Haben Sie zur Antwort von amon noch Alternativen in Betracht gezogen? JSON ist vielleicht mehr als Sie brauchen, aber Python-Dateien werden Ihnen wahrscheinlich aus den oben genannten Gründen in Zukunft Probleme bereiten.

Python verfügt jedoch bereits über einen Konfigurationsparser für eine sehr einfache Konfigurationssprache, die alle Ihre Anforderungen erfüllen kann. Das ConfigParserModul implementiert eine einfache Konfigurationssprache.

CodeMonkey
quelle
1
Die Verwendung von "ähnlichen" Microsoft Windows INI-Dateien scheint eine schlechte Idee zu sein, da es sich nicht um ein besonders flexibles Format handelt und "ähnliche" undokumentierte Inkompatibilitäten implizieren.
Pete Kirkham
1
@PeteKirkham Nun, es ist einfach, dokumentiert und Teil der Python-Standardbibliothek. Es könnte die perfekte Lösung für die Anforderungen von OPs sein, da er nach etwas sucht, das direkt von Python unterstützt wird und einfacher als JSON ist. Solange er seine Bedürfnisse nicht näher spezifiziert, denke ich, dass diese Antwort für ihn hilfreich sein kann.
CodeMonkey
1
Ich wollte im Wesentlichen Folgendes vorschlagen: Sehen Sie, welche Arten von Konfigurationsdateien Python-Bibliotheken unterstützen, und wählen Sie eine davon aus. Außerdem hat Powershell den Begriff von Datenabschnitten, die begrenzte Powershell-Sprachkonstruktionen ermöglichen und vor bösartigem Code schützen. Wenn Python eine Bibliothek hat, die eine begrenzte Teilmenge von Python für die Konfiguration unterstützt, mindert dies zumindest einen der Nachteile gegen die Idee im OP.
19.
1
@PeteKirkham Es ist eher ein Problem umgekehrt. Windows neigt dazu, eine Menge undokumentierten Müll zu haben, der auf Sie explodiert. Python ist in der Regel gut dokumentiert und unkompliziert. Das heißt, wenn Sie nur einfache Schlüssel / Wert-Paare ( möglicherweise mit Abschnitten) benötigen, ist dies eine ziemlich gute Wahl. Ich vermute, dass dies 90% der Anwendungsfälle abdeckt. Wenn die Konfigurationsdateien von .NET ini wären und nicht das monströse XML mit einem Schema, das tatsächlich Code als Konfiguration maskiert, wären wir alle viel besser dran.
jpmc26
1
@PeteKirkham Nicht wirklich. INI eignet sich in erster Linie für einfache Anwendungsfälle. Möglicherweise können Sie Inkompatibilitäten vermeiden. Sie spielen auch keine Rolle, wenn Sie die Datei nicht in zwei verschiedenen Sprachen verwenden, und selbst wenn dies der Fall ist, können Sie wahrscheinlich offene Implementierungen in einer beliebigen Sprache finden (sodass Sie entweder keine Inkompatibilitäten haben oder zumindest genau wissen, welche Sie sind). Ich bin damit einverstanden, dass Sie ein anderes Format verwenden sollten, wenn Ihr Anwendungsfall tatsächlich so komplex ist, dass Sie darauf stoßen, oder wenn Sie keine vorhandene Implementierung finden, der Sie vertrauen können, aber das ist nicht üblich.
jpmc26
1

Ich habe lange mit einer bekannten Software gearbeitet , deren Konfigurationsdateien in TCL geschrieben sind. Die Idee ist also nicht neu. Dies funktionierte recht gut, da Benutzer, die die Sprache nicht kannten, einfache Konfigurationsdateien mit einer einzigen set name valueAnweisung schreiben / bearbeiten konnten , während fortgeschrittenere Benutzer und Entwickler mit dieser Anweisung ausgefeilte Tricks anwenden konnten.

Ich denke nicht, dass "das Debuggen der Konfigurationsdateien schwierig werden kann" ein berechtigtes Anliegen ist. Solange Ihre Anwendung Benutzer nicht zum Schreiben von Skripten zwingt, können Ihre Benutzer immer einfache Zuweisungen in ihren Konfigurationsdateien verwenden, was im Vergleich zu JSON oder XML kaum schwieriger zu erreichen ist.

Das Umschreiben der Konfiguration ist ein Problem, obwohl es nicht so schlimm ist, wie es scheint. Das Aktualisieren von beliebigem Code ist nicht möglich, aber das Laden der Konfiguration aus einer Datei, das Ändern und Zurückspeichern ist möglich. Wenn Sie Skripte in einer Konfigurationsdatei ausführen, die nicht schreibgeschützt ist, erhalten Sie nach dem Speichern nur eine entsprechende Liste von set name valueAnweisungen. Ein guter Hinweis, dass dies passieren wird, ist ein "Nicht bearbeiten" -Kommentar am Anfang der Datei.

Zu beachten ist, dass Ihre Konfigurationsdateien von einfachen, auf Regex basierenden Tools wie z. B. nicht zuverlässig gelesen werden können. sedSoweit ich weiß, ist dies bei Ihren aktuellen JSON-Dateien bereits nicht der Fall, sodass Sie nicht viel verlieren können.

Stellen Sie einfach sicher, dass Sie beim Ausführen Ihrer Konfigurationsdateien die entsprechenden Sandbox- Techniken verwenden.

Dmitry Grigoryev
quelle
1
"Software" ist ein unzähliges Substantiv, daher sollte es " eine bekannte Software" sein.
jpmc26
1

Neben all den gültigen Punkten anderer guter Antworten (wow, sie haben sogar das Turing-complete-Konzept erwähnt) gibt es tatsächlich ein paar gute praktische Gründe, eine Python-Datei NICHT als Konfiguration zu verwenden, selbst wenn Sie an einer Python-Datei arbeiten. nur projekt.

  1. Die Einstellungen in einer Python-Quelldatei sind technisch gesehen Teil des ausführbaren Quellcodes und keine schreibgeschützten Datendateien. Wenn Sie diesen Weg gehen, tun Sie dies in der Regel import config, da diese Art von "Bequemlichkeit" vermutlich einer der Hauptgründe war, warum die Leute anfingen, eine Python-Datei als Konfiguration zu verwenden. Jetzt neigen Sie dazu, diese config.py in Ihr Repo zu übernehmen, da Ihr Endbenutzer sonst auf einen verwirrenden ImportError stoßen würde, wenn er versucht, Ihr Programm zum ersten Mal auszuführen.

  2. Unter der Annahme, dass Sie die Datei config.py tatsächlich in Ihr Repo übernehmen, hätten Ihre Teammitglieder jetzt wahrscheinlich unterschiedliche Einstellungen in unterschiedlichen Umgebungen. Stellen Sie sich vor, eines Tages schreibt ein Mitglied versehentlich seine lokale Konfigurationsdatei in das Repo.

  3. Last but not least könnte Ihr Projekt Passwörter in der Konfigurationsdatei haben. (Dies ist eine strittige Praxis, aber sie passiert trotzdem.) Wenn Ihre Konfigurationsdatei in repo vorhanden ist, besteht das Risiko, dass Sie Ihren Berechtigungsnachweis für ein öffentliches Repo festlegen.

Mit einer reinen Datenkonfigurationsdatei wie dem universellen JSON-Format können Sie nun alle drei oben genannten Probleme vermeiden, da Sie den Benutzer auffordern können, eine eigene config.json-Datei zu erstellen und diese in Ihr Programm einzufügen.

PS: Es ist wahr, dass JSON viele Einschränkungen hat. 2 der vom OP genannten Einschränkungen können durch etwas Kreativität gelöst werden.

RayLuo
quelle