Die JSON-Datei, die die Einstellungsdatei empfängt und von jinja2 generiert wird, wird bei der Bereitstellung verwendet. Ich möchte überprüfen, ob diese generierte JSON-Datei ein gültiger JSON ist.
Ein Bild wie j2cli.
Verwenden Sie die folgenden zwei Dateien.
Zum Beispiel, wenn die Einstellungsdatei wie folgt lautet
{
"name": "staging"
}
Verwenden Sie eine solche Vorlage.
{
"app-name": "{{name}}-app",
"batch-name": "{{name}}-batch"
}
Infolgedessen wird der folgende JSON ausgegeben. Dies wird als eigentliche Konfigurationsdatei verwendet.
{
"app-name": "staging-app",
"batch-name": "staging-batch"
}
Bei dev-ähnlichen Einstellungen kann die Ausgabe wie folgt aussehen.
{
"app-name": "dev-app",
"batch-name": "dev-batch"
}
Der generierte JSON ist möglicherweise ungültig. Es scheint, dass Sie es endlich bemerken werden, wenn es in der Produktion bereitgestellt wird. Ich möchte im Voraus überprüfen.
Die Richtlinie lautet wie folgt.
Im Python-Code sieht es so aus.
import jinja2
import json
env = jinja2.Environment(loader=jinja2.FileSystemLoader(["."]), undefined=jinja2.StrictUndefined)
t = env.get_or_select_template(template_path)
json.loads(t.render(**config)) #Fehler, wenn ungültig
Es gibt jedoch einige Einschränkungen.
{"foo": "foo-{{name}}"}
Wenn Sie mit jinja2 ausgeben, ohne etwas in einer solchen Vorlage festzulegen, tritt Folgendes auf, es tritt jedoch kein Fehler auf.
{"foo": "foo-"}
Dies kann durch Ändern der undefinierten Einstellung von jinja2 erkannt werden. Wenn Sie "undefined = jinja2.StrictUndefined" hinzufügen, tritt "UndefinedError" auf.
Es ist schlecht, wenn die Vorlage fehlerhaft ist und zu viel "}" enthält. Obwohl der generierte JSON als JSON gültig ist. Nicht die erwartete Ausgabe.
Wenn Sie versehentlich die folgende Vorlage schreiben.
{
"app-name": "{{name}}}-app"
}
Das Folgende ist ein gültiger JSON. Nicht die erwartete Ausgabe.
{
"app-name": "staging}-app"
}
Vorerst sollten Sie auch den Wert von dict überprüfen.
Basierend auf dem oben Gesagten habe ich ein Skript erstellt, das prüft, ob das Ergebnis der Ausgabe mit jinja2 ein gültiger JSON ist. Verwenden Sie es so. Wenn die variable Umgebung nicht vorhanden ist, tritt ein Fehler auf, wie unten gezeigt. Ein Beispiel für die Verwendung ist ein Fehler, wenn zwei Einstellungen mit dem Namen "Umgebung" nicht in der Einstellungsdatei vorhanden sind.
$ python check.py --conf-dir conf/ --template-dir template/ || echo ng
template/back.json.j2(conf/master.json): UndefinedError 'environ' is undefined
template/back.json.j2(conf/production.json): UndefinedError 'environ' is undefined
template/back.json.j2(conf/rook.json): UndefinedError 'environ' is undefined
ng
Die Implementierung ist wie folgt.
import sys
import os.path
import argparse
import jinja2
import json
def validate_conf(d, path=None):
path = path or []
if hasattr(d, "items"):
for k, v in d.items():
path.append(k)
validate_conf(v, path=path)
path.pop()
elif isinstance(d, (list, tuple)):
for i, x in enumerate(d):
path.append(i)
validate_conf(x, path=path)
path.pop()
elif isinstance(d, str):
v = d
if "{" in v:
raise ValueError("invalid value: '{{' is included. path={}".format(path))
if "}" in v:
raise ValueError("invalid value: '}}' is included. path={}".format(path))
return d
def check(env, config, template_path):
t = env.get_or_select_template(template_path)
data = json.loads(t.render(**config))
return validate_conf(data)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--conf-dir", required=True)
parser.add_argument("--template-dir", required=True)
args = parser.parse_args()
env = jinja2.Environment(loader=jinja2.FileSystemLoader(["."]), undefined=jinja2.StrictUndefined)
status = 0
for root, _, conf_files in os.walk(args.conf_dir):
for conf_file in conf_files:
try:
confpath = os.path.join(root, conf_file)
with open(confpath) as rf:
conf = json.load(rf)
except Exception as e:
sys.stderr.write("{confpath}: {e.__class__.__name__} {e}\n".format(confpath=confpath, e=e))
status = 1
continue
for root2, _, template_files in os.walk(args.template_dir):
for template_file in template_files:
try:
filepath = os.path.join(root2, template_file)
check(env, conf, filepath)
except Exception as e:
sys.stderr.write("{filepath}({confpath}): {e.__class__.__name__} {e}\n".format(
confpath=confpath, filepath=filepath, e=e)
)
status = 1
sys.exit(status)
if __name__ == "__main__":
main()