Формат дампа PyYAML
Я знаю, что есть несколько вопросов об этом на SO, но я не смог найти то, что искал.
Я использую pyyaml для чтения (.load()
) .yml
файл, изменить или добавить ключ, а затем записать его (.dump()
) снова. Проблема в том, что я хочу сохранить формат файла post-dump, но он меняется.
Например, я редактирую ключ en.test.index.few
сказать "Bye"
вместо "Hello"
Python:
with open(path, 'r', encoding = "utf-8") as yaml_file:
self.dict = pyyaml.load(yaml_file)
Затем, после смены ключа:
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:
До:
en:
test:
new: "Bye"
index:
few: "Hello"
anothertest: "Something"
После:
en:
anothertest: Something
test:
index:
few: Hello
new: Bye
Есть ли способ сохранить тот же формат? Например, квоты и порядок. Я использую не тот инструмент для этого?
Я знаю, может быть, оригинальный файл не совсем корректен, но я не могу его контролировать (это файл Ruby on Rails i18n).
Большое спасибо.
3 ответа
использованиеruamel.yaml
вместо.
Библиотека Бой! Повесть о двух библиотеках
PyYAML фактически мертв и существует уже несколько лет. Чтобы усугубить ситуацию, официальный проект дома на http://pyyaml.org/ похоже, недавно был снят. На этом сайте размещен трекер проблем PyYAML, документация и файлы для загрузки. На момент написания статьи все прошло. Это не что иное как бедственное. Добро пожаловать в очередной день с открытым исходным кодом.
ruamel.yaml
активно поддерживается. В отличие от PyYAML, ruamel.yaml
поддерживает:
- YAML <= 1,2. PyYAML поддерживает только YAML <= 1.1. Это жизненно важно, поскольку YAML 1.2 намеренно нарушает обратную совместимость с YAML 1.1 в нескольких крайних случаях. Это обычно было бы плохо. В этом случае это делает YAML 1.2 строгим надмножеством JSON. Поскольку YAML 1.1 не является строгим надмножеством JSON, это хорошая вещь.
- Сохранение туда и обратно. При звонке
yaml.dump()
выгрузить словарь, загруженный предыдущим вызовомyaml.load()
:- PyYAML наивно игнорирует все входное форматирование - включая комментарии, упорядочение, цитирование и пробелы. Отбрасывается как большая часть цифрового мусора в ближайшую доступную корзину битов.
ruamel.yaml
умно уважает все входное форматирование. Все. Вся стилистическая энчилада. Весь литературный шебанг. Все.
Миграция библиотек: след кодовых слез
поскольку ruamel.yaml
является вилкой PyYAML и, следовательно, соответствует API PyYAML, переключаясь с PyYAML на ruamel.yaml
в существующих приложениях обычно так же просто, как заменить все экземпляры этого:
# This imports PyYAML. Stop doing this.
import yaml
...с этим:
# This imports "ruamel.yaml". Always do this.
from ruamel import yaml
Вот и все.
Никаких других изменений не требуется. yaml.load()
а также yaml.dump()
функции должны продолжать работать так, как ожидалось, с дополнительными преимуществами поддержки YAML 1.2 и активного получения исправлений ошибок.
Сохранение в оба конца и что оно может сделать для вас
Для обратной совместимости с PyYaml, yaml.load()
а также yaml.dump()
функции не выполняют сохранение туда и обратно по умолчанию. Для этого явно передайте:
- Необязательный
Loader=ruamel.yaml.RoundTripLoader
параметр ключевого слова дляyaml.load()
, - Необязательный
Dumper=ruamel.yaml.RoundTripDumper
параметр ключевого слова дляyaml.dump()
,
Пример любезно "позаимствован" из ruamel.yaml
документация:
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='')
Сделано. Комментарии, порядок, цитирование и пробелы теперь будут сохранены без изменений.
ТЛ; др
Всегда используйте ruamel.yaml
, Никогда не используйте PyYAML. ruamel.yaml
жизни. PyYAML - это зловонный труп, гниющий в скальной почве PyPi.
Да здравствует ruamel.yaml
,
В моем случае я хочу "
если значение содержит {
или }
иначе ничего. Например:
en:
key1: value is 1
key2: 'value is {1}'
Чтобы выполнить это, скопируйте функцию represent_str()
из файла представитель.py в модуле PyYaml и использовать другой стиль, если строка содержит {
или }
:
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)
Чтобы использовать его в своем коде:
import yaml
def represent_str(self, data):
...
yaml.add_representer(str, represent_str)
В этом случае нет различий между ключами и значениями, и мне этого достаточно. Если вы хотите другой стиль для ключей и значений, выполните то же самое с функцией represent_mapping
Первый
Для представления данных словаря используется следующий код:
mapping = list(mapping.items())
try:
mapping = sorted(mapping)
except TypeError:
pass
Вот почему заказ изменился
второй
Информация о том, как был представлен скалярный тип (с двойной кавычкой или нет), теряется при чтении (это основной подход библиотеки)
Резюме
Вы можете создать собственный класс на основе 'Dumper' и перегрузить метод 'present_mapping' для изменения поведения представления словаря.
Для сохранения информации о двойных кавычках для скаляров необходимо также создать собственный класс на основе 'Loader', но я боюсь, что это повлияет и на другие классы и сделает это сложно