сделайте конфигурационный yaml более читабельным для вложенного списка

#python-3.x #dictionary #yaml #ruamel.yaml

Вопрос:

У меня есть этот образец YAML:

 people:
      name: abc
      address: '55 Oxford Street, San Jose 95134. CA'
      occupation: 'Travel Blogger'
      Hobby: 'Travelling'
      additional_interests: []
      other_info: [['gender', 'male'], ['no_of_cars', 'three'],
             ['hair_color', 'black'], ['eye_color', 'brown']]
 

Который я хочу отобразить таким образом, чтобы выходной yaml был:

 people:
      name: abc
      address: '55 Oxford Street, San Jose 95134. CA'
      occupation: 'Travel Blogger'
      Hobby: 'Travelling'
      additional_interests: []
      other_info: [
          ['gender', 'male'], 
          ['no_of_cars', 'three'],
          ['hair_color', 'black'], 
          ['eye_color', 'brown']
      ]
 

Я использую следующий код:

 from ruamel.yaml import YAML as Ruamel
from pathlib import Path
yaml = Ruamel(typ='rt')
config_file = Path('/Users/test_config.yaml')
configs = yaml.load(config_file.read_text())
component_file="xyz.txt"
with open(component_file, 'w') as component:
    for key, value in configs.items():
        yaml.default_flow_style=None
        yaml.dump({key: value}, component)
 

Идея здесь состоит в том, чтобы преобразовать существующие файлы YAML в более удобочитаемый формат. Любые указатели, как я могу получить формат, который я ищу — для вложенного списка в YAML?

Ответ №1:

Если используется, вам нужно установить только yaml.default_flow_style один раз. но в режиме туда и обратно это влияет только на вновь созданные dict s и list s. Все загруженные структуры данных имеют свой стиль блока/потока, явно заданный и сохраненный, и на них это не влияет .default_flow_style . Также почти всегда плохая идея-сбрасывать документ YAML с шагом в файл, таким образом вы можете легко создавать недопустимые/не загружаемые документы YAML.

Как задокументировано, ruamel.yaml явно пытается сохранить стиль блока и, наоборот (в основном) нормализует стиль потока. Хороший тест, чтобы увидеть, сможете ли вы достичь того, чего хотите, в качестве вывода,-это обойти предполагаемый формат YAML, чтобы узнать, ruamel.yaml может ли он повторно сгенерировать ваше форматирование (и если он проверяет узлы на предмет их специальных атрибутов, если вы хотите создать их с нуля).

В вашем случае вы заметите, что ruamel.yaml он специально не обрабатывает эти списки в стиле вложенного потока и нормализует их в нежелательный формат образца.

Если бы между элементами вложенной последовательности были комментарии, вы бы заметили, что ruamel.yaml в настоящее время не можете правильно с ними справиться. Они не теряются, но вставляются в отдельную строку, что является хорошим показателем того, что попытка вставить пустые комментарии между элементами внешней последовательности также приведет к появлению дополнительных новых строк.

Моя основная рекомендация — не вкладывать последовательности стилей потока.

 import sys
import ruamel.yaml

yaml_str = """
people:
      name: abc
      address: '55 Oxford Street, San Jose 95134. CA'
      occupation: 'Travel Blogger'
      Hobby: 'Travelling'
      additional_interests: []
      other_info: [['gender', 'male'], ['no_of_cars', 'three'],
             ['hair_color', 'black'], ['eye_color', 'brown']]
"""

def no_nested_flow_style_seq(d):
    done = False
    if isinstance(d, dict):
        for k, v in d.items():
            no_nested_flow_style_seq(v)  # you can have sequences as keys in YAML, but it is rare
    elif isinstance(d, list):
        for elem in d:
            if not done and isinstance(elem, list):
                done = True
                d.fa.set_block_style()
            no_nested_flow_style_seq(elem)


yaml = ruamel.yaml.YAML()
# yaml.preserve_quotes = True
data = yaml.load(yaml_str)
no_nested_flow_style_seq(data)
yaml.dump(data, sys.stdout)
 

что дает:

 people:
  name: abc
  address: 55 Oxford Street, San Jose 95134. CA
  occupation: Travel Blogger
  Hobby: Travelling
  additional_interests: []
  other_info:
  - [gender, male]
  - [no_of_cars, three]
  - [hair_color, black]
  - [eye_color, brown]
 

Чтобы получить размер отступа в 6 пробелов для последовательностей сопоставления (в стиле блоков)
, как в вашем примере вывода, вы можете
установить yaml.indent(mapping=6, sequence=6, offset=4) перед сбросом.
Однако, что касается. удобочитаемость для человека, есть статья «Отступы
и понятность программы» (1983, Миара, Муссельман, Наварро и Шнейдерман
(последний из известных диаграмм Насси-Шнейдермана).
в сообщениях ACM), что показывает, что понятность для языков программирования
оптимальна для отступов 2-4 пробелов и уменьшается при более высоких значениях.
Я не знаю о конкретных исследованиях для удобочитаемых форматов данных, и такие
вещи сильно зависят от того, к чему вы привыкли, и поэтому трудно
судить по личному опыту, но
Я рекомендую не использовать такие большие отступы для YAML.

Вышесказанное загружается в ту же структуру данных, что и оба ваших образца YAML (при любой нормальной обработке информация о форматировании, конечно, где-то хранится, но, безусловно, в других программах, не использующих ru).yaml в режиме туда и обратно не видит разницы).. Вы можете установить yaml.preserve_quotes = True , но ИМО вам нужно будет разобраться с несоответствиями цитирования ( abc vs 'Travelling' ), если люди будут просматривать эти файлы. Настройка, которая также загружает ту же структуру данных.

Если формат данных можно изменить, следует рассмотреть возможность изменения значения для other_info на список диктов:

   - gender: male
  - no_of_cars: three
  - hair_color: black
  - eye_color: brown
 

или в список помеченных скаляров, который позволит вам загружать их в соответствующие экземпляры классов
(и, например, проверять допустимое скалярное значение):

   - !Gender male
  - !No_Of_Cars three
  - !Hair_Color black
  - !Eye_Color brown