Чтение и изменение объектов в файле .yaml (с использованием PyYAML) - PullRequest
0 голосов
/ 21 декабря 2018

Я часами оглядывался по сторонам, не мог получить ничего, кроме широкого представления.Это означало, что я знал намного больше, чем я.

main:
    user-settings:
        accountID: 666
        timestamp: 00:00
    client settings:
        nickname: "mainPC"
        region: "OK"

Как напечатать, например, "accountID" и "region"?

Если я хотел изменить accountID с 666 на102, как бы я это сделал?

1 Ответ

0 голосов
/ 22 декабря 2018

Вы действительно не можете делать то, что просите в PyYAML, даже для небольшого примера, который у вас есть.Печать запрошенного значения работает, но когда вы обновите свой YAML, вы увидите, что он изменяет несколько выходных строк в дополнение к значению accountID.Если предположить, что ваш YAML-файл test.yaml:

from pathlib import Path
import yaml

yaml_file = Path('test.yaml')

with yaml_file.open() as fp:
    data = yaml.safe_load(fp)
user_settings = data['main']['user-settings']
print('acount id:', user_settings['accountID'])
user_settings['accountID'] = 102
print('region:   ', data['main']['client settings']['region'])

with yaml_file.open('w') as fp:
    yaml.safe_dump(data, fp, indent=4, default_flow_style=False)

, то вы получите:

acount id: 666
region:    OK

И содержимое test.yaml будет:

main:
    client settings:
        nickname: mainPC
        region: OK
    user-settings:
        accountID: 102
        timestamp: 00:00

Youполучите Python dict для любого сопоставления в файле YAML и Python list, если бы в YAML была последовательность.Чтобы напечатать желаемое значение, вы просто пересекаете вложенные dict s.

Как видите, вы теряете кавычки вокруг OK и mainPC и получаете их около 00:00.Последнее связано с тем, что PyYAML не был обновлен для обработки текущего стандарта YAML 1.2, выпущенного еще в 2009 году. В YAML 1.1 предполагается, что скаляры в форме XX: YY равны sexagesimals и преобразованы (и выгружены) какцелые числа (или с плавающей запятой, если была десятичная точка).То, что ваш 00:00 загружен как строка, происходит из-за начального нуля.Если бы это было 12:00, то ваш выходной файл был бы timestamp: 720.

Порядок ключей в вашем отображении также не сохраняется.Семантически это не имеет никакого значения, но если этот файл находится под контролем ревизии, такие изменения быстро приводят в замешательство.

Вы должны использовать safe_load(), так как «стандартный» load() может быть небезопасен при неконтролируемом вводе YAMLи хотя это задокументировано, большинство людей не осознают этого или того, что это по сути никогда не требуется.(Небезопасно, как в «harddrive wiped», или еще хуже.)


Если вы хотите обновить файл YAML, я рекомендую использовать ruamel.yaml (отказ от ответственности: я авторэтого пакета).

from pathlib import Path
import ruamel.yaml

yaml_file = Path('test.yaml')

yaml = ruamel.yaml.YAML()
yaml.indent(mapping=4)
yaml.preserve_quotes = True
data = yaml.load(yaml_file)

user_settings = data['main']['user-settings']
print('acount id:', user_settings['accountID'])
user_settings['accountID'] = 102
print('region:   ', data['main']['client settings']['region'])

yaml.dump(data, yaml_file)

, который также выводит:

acount id: 666
region:    OK

И содержимое test.yaml будет:

main:
    user-settings:
        accountID: 102
        timestamp: 00:00
    client settings:
        nickname: "mainPC"
        region: "OK"

Не толькокавычки правильно сохранены, но не обязательно заключать в кавычки 00:00, потому что ruamel.yaml обрабатывает YAML 1.2, в котором шестнадцатеричные значения вообще отброшены¹Порядок ключей в сопоставлениях сохраняется.

Если бы в test.yaml были комментарии, то PyYAML отбросило бы их, тогда как ruamel.yaml также сохранило бы их.

В ruamel.yaml по умолчанию YAML() используется безопасный загрузчик.


¹ Вы можете использовать sexagesimals, если создаете класс Sexagesimal и помечаете их соответствующим образом.В этом случае вы также можете решить иметь начальные нули (!sexagesimal 00:00) и правильно сбросить эти значения обратно как скаляры с тегами без кавычек.

...