Как обновить файл yaml с помощью python

У меня есть файл some.yaml с указанным ниже содержимым.

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

Файл yaml должен быть проанализирован и обновлен, как показано ниже.

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

Как мне проанализировать значения и обновить их соответствующим образом?


person Chetan    schedule 17.02.2015    source источник
comment
Если вы используете PyYaml, вы можете использовать Loader для загрузки данных и Dumper для записи данных в файл. Загружаемые данные представляют собой обычный словарь в Python, поэтому вы можете получить доступ к элементу по ключу и, таким образом, изменить его по своему усмотрению.   -  person Ha Dang    schedule 17.02.2015
comment
Неясно, хотите ли вы механизм шаблонов (и неправильно направляете полезные ответы, будучи слишком узкими) или просто хотите проанализировать YAML и заменить некоторые строковые значения на проанализированные данные. Будьте более конкретными и объясните, чего вы хотите достичь и почему.   -  person MariusSiuram    schedule 17.02.2015
comment
Это файл. Мне нужно разобрать файл yaml и заменить содержимое.   -  person Chetan    schedule 17.02.2015
comment
Зачем вам парсить YAML, если вы заинтересованы только в замене некоторого контента? Вас интересует фактическая структура данных или вам нужен только результирующий файл?   -  person MariusSiuram    schedule 17.02.2015
comment
Я новичок в ямле. Я хочу заменить текст. Я использовал слово parse, означающее чтение файла. Извините, если это имеет другое назначение.   -  person Chetan    schedule 17.02.2015


Ответы (5)


Пакет ruamel.yaml был специально улучшен (я начал с PyYAML) для этого вид туда и обратно, программный, обновление.

Если вы начнете с (обратите внимание, что я удалил лишние начальные пробелы):

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

и запустите:

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)

Вывод будет:

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

Порядок ключей сопоставления (host, username и password), стиль и комментарии сохраняются без каких-либо дополнительных действий.

Вместо того, чтобы угадывать отступ и отступ последовательности блоков, вы можете выполнить традиционную ручную загрузку и самостоятельно установить значения отступа:

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

Если вы посмотрите на историю этого ответа, вы увидите, как это сделать с помощью более ограниченного API, похожего на PyYAML.

person Anthon    schedule 13.04.2015
comment
Привет, я заметил, что в пакете ruamel.yaml изменились загрузка и выгрузка, поэтому вы можете обновить этот ответ. - person Hasnep; 02.08.2018
comment
Кажется, снова изменился - person alkanen; 20.06.2019
comment
@alkanen Можете ли вы превратить свое утверждение во что-то полезное? - person Anthon; 22.06.2019
comment
Конечно, извини. Я попытался последовать вашему примеру, но load_yaml_guess_indent больше не существует в ruamel.yaml. Если бы этот метод был из другой библиотеки, чем ruamel, возможно, вы могли бы обновить пример, чтобы указать это? В любом случае код теперь не будет выполняться, потому что load_yaml_guess_indent() вызывается как функция верхнего уровня, которой не существует. Тем не менее, спасибо, что указали мне правильное направление, чтобы я мог решить свою проблему! - person alkanen; 23.06.2019
comment
@alkanen import ruamel.yaml никогда не было достаточно, чтобы импортировать функцию угадывания, я забыл скопировать еще одну строку из своей тестовой программы. Теперь я решил это по-другому, и теперь он должен работать. - person Anthon; 23.06.2019
comment
Могу ли я обновить один ключ? - person alper; 25.07.2021
comment
@alper Я не уверен, что такое один ключ, если вы намеревались написать один ключ, я не уверен, имеете ли вы в виду изменение ключа, но сохранение его значения (что возможно, но, поскольку комментарии связаны с ключами, не имеют желаемый эффект без лишних усилий). Что вы пробовали, что не сработало? Я предлагаю вам опубликовать полный вопрос в Stack Overflow вместо того, что я считаю неполным предложением в качестве комментария. - person Anthon; 25.07.2021
comment
Я хотел изменить значение только одного ключа и оставить остальные переменные такими, какие они есть. - person alper; 25.07.2021
comment
@alper В файле YAML нет переменных. Все еще не уверен, что вы пытаетесь сделать и что не работает. Приведенная выше программа загружает YAML в структуру данных Python, а затем обновляет значения для трех ключей в одном словаре, вложенном в эту структуру данных. Если вы хотите обновить только одну из них, я думаю, очевидно, что вы можете это сделать, когда прокомментируете две другие строки. Но это так тривиально, я думаю, вы хотите сделать что-то другое. - person Anthon; 25.07.2021
comment
Извините за неясность. Насколько я понимаю, yaml.dump() перезаписывает файл с нуля, как cat "hello" > file.txt. Вместо этого мне просто интересно, можно ли изменить только соответствующий раздел обновленных значений, чтобы быть более эффективным. - person alper; 25.07.2021

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

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))
person Chetan    schedule 17.02.2015
comment
Вы, вероятно, должны упомянуть, что это удаляет начальные пробелы и не гарантирует сохранения порядка ключей сопоставления (когда я запускаю это, я получаю host, password, username вместо host, username, «пароль»). - person Anthon; 13.04.2015
comment
Используйте yaml.load(stream, Loader=yaml.FullLoader), если вы доверяете источнику файла YAML. См. ‹github.com/yaml/pyyaml/wiki/PyYAML. -yaml.load(input)-Deprecation› подробнее - person Besi; 05.01.2020
comment
Антон, я тоже согласен с Беси. У меня такая же проблема. Это Загрузчик. stackoverflow.com/questions/ 12012774/ - person Camilo Abboud; 18.08.2020

Я не знаю, нужен ли вам YAML. Помимо использования тега YAML, похоже, документ YAML вас не интересует. Так почему бы не использовать Jinja2 или какой-нибудь язык шаблонов?

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"
)

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


Бонус: пример использования

Я работал с очень сложными документами YAML, для которых есть неизвестные теги

...
  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
...

Выполнение корректного разбора этого документа сложно и требует много времени. Мне нужно только заполнить некоторые значения, а YAML отправляется в стороннее приложение. Таким образом, вместо того, чтобы анализировать YAML или пытаться сгенерировать действительный документ напрямую с помощью pyyaml, проще (более эффективно с точки зрения времени и менее подвержено ошибкам) ​​создавать его непосредственно с помощью шаблонов. Кроме того, языки шаблонов можно легко использовать с циклами для заполнения полей с динамическим размером.

person MariusSiuram    schedule 17.02.2015

Вот как я создаю шаблоны docker-crane для разработки, производства, сцены и т. д.

  1. мкдир кран_шаблоны
  2. коснитесь крана_templates/init.py
  3. Добавьте содержимое шаблона с помощью nanocrane_templates/some.yaml
  4. Нано-кран_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. Питон Crane_gen.py > результат.yaml

Ответ, вдохновленный @MariusSiuram

person Kostyantyn    schedule 05.05.2016

Вот пример использования PyYaml. Я так понимаю, у вас что-то вроде шаблона в формате yaml, и вы должны заменить места в угловых скобках реальными значениями.

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
person Deck    schedule 17.02.2015