web-dev-qa-db-fra.com

Comment mettre à jour le fichier yaml en utilisant python

J'ai un some.yaml fichier avec le contenu ci-dessous.

    init_config: {}
    instances:
        - Host: <IP>
          username: <username>
          password: <password>

Le fichier yaml doit être analysé et mis à jour comme ci-dessous.

    init_config: {}
    instances:
        - Host: 1.2.3.4
          username: Username
          password: Password

Comment analyser les valeurs et les mettre à jour de manière appropriée?

16
Chetan

Le paquet ruamel.yaml a été spécifiquement amélioré (par moi à partir de PyYAML) pour faire ce genre de mise à jour aller-retour, programmatique,.

Si vous commencez par (veuillez noter que j'ai supprimé les espaces initiaux supplémentaires):

init_config: {}
instances:
    - Host: <IP>              # update with IP
      username: <username>    # update with user name
      password: <password>    # update with password

et courir:

import ruamel.yaml

file_name = 'input.yaml'
config, ind, bsi = ruamel.yaml.util.load_yaml_guess_indent(open(file_name))

instances = config['instances']
instances[0]['Host'] = '1.2.3.4'
instances[0]['username'] = 'Username'
instances[0]['password'] = 'Password'

with open('output.yaml', 'w') as fp:
    yaml.dump(config, fp)

La sortie sera:

init_config: {}
instances:
    - Host: 1.2.3.4           # update with IP
      username: Username      # update with user name
      password: Password      # update with password

L'ordre des clés de mappage (Host, username et password), le style et les commentaires sont conservés sans autre action spécifique.

Au lieu d'avoir deviné le retrait et la séquence de bloc, vous pouvez effectuer un chargement traditionnel manuel et définir vous-même les valeurs de retrait:

yaml = ruamel.yaml.YAML()
yaml.indent(mapping=6, sequence=4)
with open(file_name) as fp:
    config = yaml.load(fp)

Si vous regardez l'historique de cette réponse, vous pouvez voir comment le faire avec une API plus limitée, comme PyYAML.

17
Anthon

C'est ainsi que je peux lire le fichier ci-dessus que j'ai mentionné, analyser et mettre à jour au besoin.

import yaml

fname = "some.yaml"

stream = open(fname, 'r')
data = yaml.load(stream)

data['instances'][0]['Host'] = '1.2.3.4'
data['instances'][0]['username'] = 'Username'
data['instances'][0]['password'] = 'Password'

with open(fname, 'w') as yaml_file:
    yaml_file.write( yaml.dump(data, default_flow_style=False))
8
Chetan

Je ne sais pas si tu as besoin de YAML. Hormis l'utilisation de la balise YAML, il semble que vous n'ayez aucun intérêt pour le document YAML. Alors pourquoi ne pas utiliser Jinja2 ou un langage de modèle?

from jinja2 import Template

tmpl = Template(u'''\
    init_config: {}
    instances:
         - Host: {{ IP }}
           username: {{ username }}
           password: {{ password }}
''')

print tmpl.render(
     IP=u"1.2.3.4",
     username=u"Username",
     password=u"Password"
)

Je ne sais pas si c'est une bonne idée, mais si vous avez seulement besoin d'obtenir un fichier avec certains champs modifiés, vous n'avez pas besoin d'analyser réellement le document YAML et pouvez bénéficier directement d'un langage de modèle.


Bonus: Cas d'utilisation

J'ai travaillé avec des documents YAML très complexes, pour lesquels il existe des balises inconnues

...
  propertiesIDs: { 1, 2, 3, 4 }
  globalID: !myapplication.InterfaceID &primitiveID

replication: !myapplication.replication
  beginDate: 2012-09-10T20:00:03
  endDate: 2020-09-10T20:00:04
  replicant_uuid:
    ? 17169504-B6AB-11E4-8437-36E258BB2172
    ? 206B5842-B6AB-11E4-AAC3-36E258BB2172
...

L'exécution d'une analyse valide de ce document est difficile et prend du temps. Il me suffit de renseigner certaines valeurs et le YAML est envoyé à une application tierce. Ainsi, au lieu d'analyser le YAML ou d'essayer de générer un document valide directement à l'aide de pyyaml, il est plus simple (plus efficace en termes de temps, moins sujet aux bogues) de le générer directement via des modèles. De plus, les langages de modèles peuvent facilement être utilisés avec des boucles pour remplir des champs de taille dynamique.

4
MariusSiuram

Voici comment je génère des modèles de docker-crane pour le développement, la production, la scène, etc ...

  1. mkdir crane_templates
  2. touchez crane_templates/init. py
  3. Ajouter du contenu de modèle avec nano crane_templates/some.yaml
  4. Nano crane_gen.py

--- crane_gen.py ---

#!/usr/bin/env python
from jinja2 import Environment, PackageLoader

env = Environment(loader=PackageLoader('crane_templates', './'))
tmpl = env.get_template('crane.yaml.tmpl')

result = tmpl.render(
     IP=u"1.2.3.4",
     username=u"Username",
     password=u"Password"
)

5. python crane_gen.py> result.yaml

Réponse inspirée de @MariusSiuram

1
Kostyantyn

Voici un exemple utilisant PyYaml. Si je comprends bien, vous avez quelque chose comme un modèle au format yaml, et vous devez remplacer les espaces entre crochets par des valeurs réelles.

import yaml

s = """
    init_config: {}
    instances:
        - Host: <IP>
          username: <username>
          password: <password>
"""

dict_obj = yaml.load(s) # loads string in internal data structure - dict
dict_obj['instances'][0]['Host'] = 'localhost' # change values
print yaml.dump(dict_obj) # dumps dict to yaml format back
0
Deck