Wie kann ich eine .yml-Datei aktualisieren, indem ich die bereits vorhandene Jinja-Syntax mit Python ignoriere?

8

Ich habe einige Preprocessing mit einigen bestehenden .yml-Dateien zu tun - einige von ihnen haben jedoch Jinja Template-Syntax in ihnen eingebettet:

%Vor%

Ich möchte diese Datei lesen und val3 unter myArray als solche hinzufügen:

%Vor%

Ich habe versucht, die jinja-Templates manuell zu schreiben, aber sie wurden mit einfachen Anführungszeichen um sie herum geschrieben: '{{ jinja.variable }}'

Was ist der empfohlene Weg für mich, solche .yml-Dateien zu lesen und zu modifizieren, wenn auch mit bereits existierender Jinja-Syntax? Ich würde gerne hinzufügen Informationen zu diesen Dateien behalten alles andere gleich.

Ich habe das oben mit PyYAML auf Python 2.7 +

versucht     
PhD 07.06.2017, 20:36
quelle

3 Antworten

5

Die Lösung in dieser Antwort wurde mit einem Plugin-Mechanismus in ruamel.yaml integriert. Am Ende dieses Beitrags finden Sie schnell und einfach Anweisungen zur Verwendung.

Beim Aktualisieren einer YAML-Datei, die jinja2 "code" enthält, gibt es drei Aspekte:

  • macht den jinja2-Code für den YAML-Parser
  • akzeptabel
  • stellen Sie sicher, dass der akzeptable Wert umgekehrt werden kann (d. h. die Änderungen sollten eindeutig sein, sodass sie nur umgekehrt werden)
  • Das Layout der YAML-Datei beibehalten, damit die aktualisierte Datei, die von jinja2 verarbeitet wurde, noch eine gültige YAML-Datei erzeugt, die erneut geladen werden kann.

Beginnen wir damit, Ihr Beispiel etwas realistischer zu machen, indem Sie eine jinja2-Variablendefinition und for-loop hinzufügen und einige Kommentare hinzufügen ( input.yaml ):

%Vor%

Die Zeilen, die mit {% beginnen, enthalten kein YAML, also werden wir diese zu Kommentaren machen (vorausgesetzt, dass die Kommentare beim Round-Trip erhalten bleiben, siehe unten). Da YAML-Skalare nicht mit { beginnen können, ohne zitiert zu werden, ändern wir die {{ in <{ . Dies geschieht im folgenden Code durch den Aufruf von sanitize() (der auch die verwendeten Muster speichert und umgekehrt in sanitize.reverse (unter Verwendung der gespeicherten Muster).

Die Erhaltung Ihres YAML-Codes (Blockstil usw.) erfolgt am besten mit ruamel.yaml ( Disclaimer: Ich bin der Autor dieses Pakets), auf diese Weise müssen Sie sich keine Gedanken darüber machen, wie Flow-Style-Elemente in der Eingabe in einen Block-Stil verwandelt werden, wie bei der eher rohen default_flow_style=False , die die anderen Antworten verwenden. ruamel.yaml behält auch Kommentare bei, sowohl diejenigen, die ursprünglich in der Datei waren, als auch solche, die vorübergehend eingefügt wurden, um jinja2-Konstrukte beginnend mit %{ "auskommentieren" zu können.

Der resultierende Code:

%Vor%

welches druckt (spezifiziert einen zweiten Parameter zu update_one() um in eine Datei zu schreiben) mit Python 2.7:

%Vor%

Wenn sich weder #{ noch <{ in einer der ursprünglichen Eingaben befinden, kann die Bereinigung und Zurücksetzung mit einfachen einzeiligen Funktionen erfolgen (siehe diese Versionen dieses Posts ), und dann brauchst du die Klasse Sanitize

nicht

Ihr Beispiel ist eingerückt mit einer Position (Schlüssel B ) sowie zwei Positionen (den Sequenzelementen), ruamel.yaml hat nicht diese feine Kontrolle über die Ausgabeeinrückung (und ich kenne keine YAML Parser, der das tut). Der Einzug (standardmäßig 2) wird auf beide YAML-Zuordnungen wie auf Sequenzelemente angewendet (gemessen am Anfang des Elements, nicht auf dem Gedankenstrich). Dies hat keinen Einfluss auf das erneute Lesen der YAML und ist auch auf die Ausgabe der anderen zwei Antworten zurückzuführen (ohne dass sie auf diese Änderung hingewiesen haben).

Beachten Sie auch, dass YAML().load() sicher ist (dh keine beliebigen potentiell schädlichen Objekte lädt), während das yaml.load() , wie es in den anderen Antworten verwendet wird, definitiv unsicher ist , heißt es in der Dokumentation und wird sogar im WikiPedia-Artikel zu YAML erwähnt. Wenn Sie yaml.load() verwenden, müssten Sie jede Eingabedatei überprüfen, um sicherzustellen, dass keine markierten Objekte vorhanden sind, die dazu führen könnten, dass Ihre Disc gelöscht wird (oder schlimmer).

Wenn Sie Ihre Dateien wiederholt aktualisieren müssen und Kontrolle über das jinja2-Template haben, ist es möglicherweise besser, die Muster für jinja2 einmal zu ändern und sie nicht rückgängig zu machen, und dann die entsprechenden block_start_string , variable_start_string (und möglich block_end_string und variable_end_string ) an die jinja2.FileSystemLoader , die als Loader zum jinja2.Environment hinzugefügt wurden.

Wenn das obige zu kompliziert erscheint, dann in einem virtualenv tun:

%Vor%

vorausgesetzt, Sie haben die input.yaml , bevor Sie ausführen können:

%Vor%

um die diff Ausgabe zu erhalten:

%Vor%

ruamel.yaml 0.15.7 implementiert einen neuen Plug-in-Mechanismus und ruamel.yaml.jinja2 ist ein Plug-in, das den Code in dieser Antwort für den Benutzer transparent umschließt. Derzeit sind die Informationen für die Umkehrung an die YAML() -Instanz angehängt, also stellen Sie sicher, dass Sie yaml = YAML(typ='jinja2') für jede Datei, die Sie verarbeiten, an die oberste Instanz data anhängen, genau wie die YAML-Kommentare. .

    
Anthon 13.06.2017 08:08
quelle
2

Eine Möglichkeit besteht darin, den Parser jinja2 selbst zu verwenden, um die Vorlage zu analysieren und ein alternatives Format auszugeben.

Jinja2-Code:

Dieser Code erbt von den Klassen Jinja2 Parser , Lexer und Environment , um innerhalb variabler Blöcke zu analysieren (normalerweise {{ }} ). Anstatt die Variablen zu bewerten, ändert dieser Code den Text in etwas, das yaml verstehen kann. Der exakt gleiche Code kann verwendet werden, um den Prozess mit einem Austausch der Trennzeichen umzukehren. Standardmäßig wird es in die Trennzeichen übersetzt, die von snakecharmerb vorgeschlagen werden.

%Vor%

Wie / Warum?

Der jinja2-Parser durchsucht die Vorlagendatei nach Begrenzern. Wenn Trennzeichen gefunden werden, wird das entsprechende Material zwischen den Trennzeichen analysiert. Die Änderungen im Code fügen sich hier in den Lexer und Parser ein, um den während der Vorlagenkompilierung erfassten Text zu erfassen. Wenn dann der Terminierungsbegrenzer gefunden wird, werden die analysierten Token zu einer Zeichenfolge zusammengefasst und als jinja2.nodes.Const parse-Knoten in eingefügt Ort des kompilierten Jinja-Codes, so dass beim Rendern der Vorlage die Zeichenfolge anstelle einer Variablenerweiterung eingefügt wird.

Der MyEnvironment () - Code wird zum Anhängen der benutzerdefinierten Parser- und Lexer-Erweiterungen verwendet. Und während es, fügte einige Parameter Verarbeitung hinzu.

Der Hauptvorteil dieses Ansatzes ist, dass er ziemlich robust sein sollte, um zu analysieren, was auch immer jinja parsen wird.

Benutzercode:

%Vor%

Testcode:

%Vor%

data.yml

%Vor%

Ergebnisse:

%Vor%     
Stephen Rauch 12.06.2017 07:09
quelle
2

In ihrem aktuellen Format sind Ihre .yml -Dateien Jinja-Vorlagen, die solange nicht gültig sind, bis sie gerendert wurden. Dies liegt daran, dass die Syntax der jinja-Platzhalter mit der yaml-Syntax in Konflikt steht, da geschweifte Klammern ( yaml und { ) für die Darstellung von Mappings in yaml verwendet werden können.

%Vor%

Eine Möglichkeit zur Umgehung dieses Problems besteht darin, die jinja-Platzhalter durch etwas anderes zu ersetzen, die Datei als yaml zu verarbeiten und dann die Platzhalter wieder einzusetzen.

%Vor%

Öffnen Sie die Datei als Textdatei

%Vor%

Der reguläre Ausdruck } stimmt mit allen Jinja-Platzhaltern im Text überein. Die benannte Gruppe r'{{\s*(?P<jinja>[a-zA-Z_][a-zA-Z0-9_]*)\s*}}' im Ausdruck erfasst den Variablennamen. Der reguläre Ausdruck ist der gleiche wie der , der von Jinja2 verwendet wird, um Variablennamen zu finden.

Die re -Funktion kann benannte Gruppen in ihrer Ersetzungszeichenfolge referenzieren jinja Syntax. Wir können diese Funktion verwenden, um die Jinja-Syntax durch etwas zu ersetzen, das nicht mit der Yaml-Syntax in Konflikt steht und nicht bereits in den von Ihnen verarbeiteten Dateien erscheint. Zum Beispiel ersetzen Sie \g durch {{ ... }} .

%Vor%

Laden Sie nun den Text als yaml:

%Vor%

Fügen Sie den neuen Wert hinzu:

%Vor%

Serialisieren Sie zurück zu einer Yaml-Zeichenfolge:

%Vor%

Stelle nun die Jinja-Syntax wieder her.

%Vor%

Und schreibe das Yaml auf die Festplatte.

%Vor%     
snakecharmerb 10.06.2017 07:33
quelle

Tags und Links