Création d'une balise personnalisée dans PyYAML
J'essaie d'utiliser PyYAML de Python pour créer une balise personnalisée qui me permettra de récupérer des variables d'environnement avec mon YAML.
import os
import yaml
class EnvTag(yaml.YAMLObject):
yaml_tag = u'!Env'
def __init__(self, env_var):
self.env_var = env_var
def __repr__(self):
return os.environ.get(self.env_var)
settings_file = open('conf/defaults.yaml', 'r')
settings = yaml.load(settings_file)
Et à l'intérieur de se defaults.yaml
trouve simplement:
example: !ENV foo
L'erreur que je reçois:
yaml.constructor.ConstructorError:
could not determine a constructor for the tag '!ENV' in
"defaults.yaml", line 1, column 10
Je prévois également d'avoir plus d'une balise personnalisée (en supposant que je puisse faire fonctionner celle-ci)
Votre classe PyYAML a rencontré quelques problèmes:
yaml_tag
est sensible à la casse, donc!Env
et!ENV
sont des balises différentes.- Ainsi, selon la documentation,
yaml.YAMLObject
utilise des méta-classes pour se définir, et a par défautto_yaml
et desfrom_yaml
fonctions pour ces cas. Par défaut, cependant, ces fonctions nécessitent que votre argument de votre balise personnalisée (dans ce cas!ENV
) soit un mappage . Donc, pour travailler avec les fonctions par défaut, votredefaults.yaml
fichier doit ressembler à ceci (juste par exemple) à la place:
example: !ENV {env_var: "PWD", test: "test"}
Votre code fonctionnera alors inchangé, dans mon cas, print(settings)
il en résulte maintenant {'example': /home/Fred}
Mais vous utilisez à la load
place de safe_load
- dans leur réponse ci-dessous, Anthon a souligné que cela est dangereux car le YAML analysé peut écraser / lire des données n'importe où sur le disque.
Vous pouvez toujours utiliser facilement votre format de fichier YAML, - il example: !ENV foo
vous suffit de définir un format approprié to_yaml
et from_yaml
en classe EnvTag
, qui peut analyser et émettre des variables scalaires comme la chaîne "foo".
Donc:
import os
import yaml
class EnvTag(yaml.YAMLObject):
yaml_tag = u'!ENV'
def __init__(self, env_var):
self.env_var = env_var
def __repr__(self):
v = os.environ.get(self.env_var) or ''
return 'EnvTag({}, contains={})'.format(self.env_var, v)
@classmethod
def from_yaml(cls, loader, node):
return EnvTag(node.value)
@classmethod
def to_yaml(cls, dumper, data):
return dumper.represent_scalar(cls.yaml_tag, data.env_var)
# Required for safe_load
yaml.SafeLoader.add_constructor('!ENV', EnvTag.from_yaml)
# Required for safe_dump
yaml.SafeDumper.add_multi_representer(EnvTag, EnvTag.to_yaml)
settings_file = open('defaults.yaml', 'r')
settings = yaml.safe_load(settings_file)
print(settings)
s = yaml.safe_dump(settings)
print(s)
Lorsque ce programme est exécuté, il génère:
{'example': EnvTag(foo, contains=)}
{example: !ENV 'foo'}
Ce code a l'avantage de (1) utiliser le pyyaml original, donc rien de plus à installer et (2) ajouter un représentant. :)