Format PyYAML dump

je sais qu'il y a quelques questions à ce sujet, mais je ne pouvais pas trouver ce que je cherchais.

j'utilise pyyaml pour lire ( .load() ) un fichier .yml , modifier ou ajouter une clé, puis l'écrire ( .dump() ) à nouveau. Le problème est que je veux garder le format de fichier post-dump, mais il change.

par exemple, je modifie la touche en.test.index.few pour dire "Bye" au lieu de "Hello"

Python:

with open(path, 'r', encoding = "utf-8") as yaml_file:
    self.dict = pyyaml.load(yaml_file)

puis, en changeant la clé:

with open(path, 'w', encoding = "utf-8") as yaml_file:
    dump = pyyaml.dump(self.dict, default_flow_style = False, allow_unicode = True, encoding = None)
    yaml_file.write( dump )

Yaml:

avant:

en:
  test:
    new: "Bye"
    index:
      few: "Hello"
  anothertest: "Something"

après:

en:
  anothertest: Something
  test:
    index:
      few: Hello
    new: Bye

y a-t-il un moyen de conserver le même format?, par exemple les qoutes et l'ordre. Suis-je à l'aide de la mauvais outil pour cela?

je sais que le fichier original n'est peut-être pas tout à fait correct, mais je n'ai aucun contrôle sur lui (c'est un fichier Ruby on Rails i18n).

Merci beaucoup.

24
demandé sur NicoSantangelo 2013-12-27 21:54:05

3 réponses

utilisez ruamel.yaml à la place.

Bataille De Bibliothèque! Un Conte de Deux Bibliothèques

PyYAML est dead et a été pendant plusieurs années. Pour compliquer les choses, le projet officiel de la maison à http://pyyaml.org semble avoir été enlevé récemment. Ce site hébergeait le suivi des problèmes PyYAML, de la documentation et des téléchargements. Comme d' cette écriture, tous sont partis. Ce n'est rien de moins calamiteuse. Bienvenue à une autre journée en open-source.

ruamel.yaml est activement maintenu . Contrairement à PyYAML, ruamel.yaml supports:

  • YAML <= 1.2. PyYAML supporte seulement YAML < = 1.1. C'est vital, car YAML 1.2 intentionnellement casse compatibilité arrière avec YAML 1.1 dans plusieurs cas de bord. Ce serait habituellement une mauvaise chose. Dans ce cas, cela fait de YAML 1.2 un surensemble strict de JSON. Puisque YAML 1.1 est et non un surset strict de JSON, c'est une bonne chose.
  • Aller-retour conservation. en appelant yaml.dump() pour décharger un dictionnaire chargé par un appel préalable à yaml.load() :
    • PyYAML ignore naïvement tout le formatage des entrées – y compris les commentaires, l'ordre, les citations et les espaces. Jeté comme autant de déchets numériques dans le seau de bits disponible le plus proche.
    • ruamel.yaml habilement le respecte tous entrée mise en forme. Tout. Toute l'enchilada stylistique. L'ensemble de la littéraire shebang. tout.

Bibliothèque de la Migration: Le Sentier du Code des Larmes

depuis ruamel.yaml est une fourche PyYAML et est donc conforme à la API PyYAML, passer de PyYAML à ruamel.yaml dans les applications existantes est généralement aussi simple que de remplacer toutes les instances de ce:

# This imports PyYAML. Stop doing this.
import yaml

...avec ceci:

# This imports "ruamel.yaml". Always do this.
from ruamel import yaml

C'est tout.

Aucun autre changement ne devrait être nécessaire. Les fonctions yaml.load() et yaml.dump() devraient continuer à se comporter comme prévu – avec les avantages supplémentaires de soutenir maintenant YAML 1.2 et activement recevoir des corrections de bugs.

Aller-retour de la Préservation et de Ce qu'Il Peut Faire pour Vous

pour la rétrocompatibilité avec PyYaml, les fonctions yaml.load() et yaml.dump() font et non assurent la préservation de roundtrip par défaut. Pour ce faire, passer explicitement:

  • L'option "1519150920 mot clé" paramètre yaml.load() .
  • le mot-clé optionnel Dumper=ruamel.yaml.RoundTripDumper paramètre à yaml.dump() .

exemple emprunté " à ruamel.yaml documentation :

import ruamel.yaml

inp = """\
# example
name:
  # Yet another Great Duke of Hell. He's not so bad, really.
  family: TheMighty
  given: Ashtaroth
"""

code = ruamel.yaml.load(inp, Loader=ruamel.yaml.RoundTripLoader)
code['name']['given'] = 'Astarte'  # Oh no you didn't.

print(ruamel.yaml.dump(code, Dumper=ruamel.yaml.RoundTripDumper), end='')

C'est fait. les commentaires, l'ordre, les citations et les espaces seront préservés intacts.

tl; dr

toujours utiliser ruamel.yaml . N'utilisez jamais PyYAML. ruamel.yaml vit. PyYAML est un cadavre fétide qui pourrit dans la moisissure. charnier terrain de PyPi.

Longue vie ruamel.yaml .

46
répondu Cecil Curry 2016-04-21 05:32:18

Première

pour représenter les données du dictionnaire est utilisé le code suivant:

mapping = list(mapping.items())
    try:
        mapping = sorted(mapping)
    except TypeError:
        pass

c'est pourquoi la commande est changée

Deuxième

l'Information sur la façon dont le type scalaire a été présenté (avec double citation ou non) est perdue en lisant (c'est l'approche principale de la bibliothèque)

résumé

vous pouvez créer votre propre classe basée sur 'Dumper' et la méthode de surcharge 'represent_mapping' pour changer le comportement comment le dictionnaire sera présenté

pour sauvegarder l'information sur les doubles citations pour scalar, vous devez aussi créer votre propre classe basée sur' Loader', mais je crains que cela affectera et d'autres classes et le fera difficile

2
répondu Habibutsu 2014-02-19 12:26:54

Dans mon cas, je veux " si la valeur contient un { ou un } , sinon rien. Par exemple:

 en:
   key1: value is 1
   key2: 'value is {1}'

pour effectuer cela, copier la fonction represent_str() à partir du fichier representer.py dans le module PyYaml et utilisez un autre style si la chaîne contient { ou un } :

def represent_str(self, data):
    tag = None
    style = None
    # Add these two lines:
    if '{' in data or '}' in data:
        style = '"'
    try:
        data = unicode(data, 'ascii')
        tag = u'tag:yaml.org,2002:str'
    except UnicodeDecodeError:
        try:
            data = unicode(data, 'utf-8')
            tag = u'tag:yaml.org,2002:str'
        except UnicodeDecodeError:
            data = data.encode('base64')
            tag = u'tag:yaml.org,2002:binary'
            style = '|'
    return self.represent_scalar(tag, data, style=style)

pour l'utiliser dans votre code:

import yaml

def represent_str(self, data):
  ...

yaml.add_representer(str, represent_str)

dans ce cas, non les différences entre les clés et les valeurs et ça me suffit. Si vous voulez un style différent pour les clés et les valeurs, effectuer la même chose avec la fonction represent_mapping

-1
répondu Nelson G. 2016-10-14 13:44:23