YAML для начинающих

Наверняка вы зашли в эту статью, потому что экспериментируете с Docker или Ansible, или просто вам захотелось разобраться с YAML. Так или иначе вы в нужном месте!

Коснемся сначала истории YAML, а потом перейдём к разбору синтаксиса и познакомимся с основными конструкциями YAML: строками, списками и словарями. В конце закрепим новые знания созданием несложного YAML-файла.

Если времени вникать в статью нет, можно прочитать краткий мануал из базы знаний.

Немного об истории YAML

История YAML

YAML (Yet Another Markup Language) — это ещё один язык разметки. Да, именно так он изначально официально назывался. Конкурировать со «взрослыми» языками разметки, такими как JSON и XML с таким названием сложно, поэтому Кларк Эванс — автор концепции разметки, изменил расшифровку YAML на «Ain't Markup Language» — не язык разметки. Что же изменилось? Изменилась концепция самой разметки.

Доминанта сместилась с упора на удобную разметку в сторону работы с данными, при этом удобный человекочитаемый формат, конечно же, сохранился. Благодаря такому решению, со временем YAML стал одним из самых популярных языков разметки. Особо приглянулся он сетевым- и инфраструктурным инженерам, благодаря целям, которые Кларк Эванс изначально поставил перед своим детищем:

  1. быть понятным человеку;
  2. поддерживать структуры данных, родственные для языков программирования;
  3. быть переносимым между языками программирования;
  4. использовать цельную модель данных для поддержки обычного инструментария;
  5. поддерживать потоковую обработку;
  6. быть выразительным и расширяемым;
  7. быть легким в реализации и использовании.

Сегодня YAML — основной формат для файлов конфигурации Ruby on Rails, Dancer, Symfony, GAE framework, Google App Engine и Dart. Является основным языком описания классов, ресурсов и манифестов для пакетов приложений OpenStack Murano Project и Swagger.io.

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

Синтаксис YAML

Синтаксис YAML

Несмотря на то, что YAML конкурирует с XML и JSON, подходы к построению семантики у него совершенно иные. XML для обозначения логических блоков и структур данных использует парные теги, например, <message>Текст</message>, а JSON для организации структур данных использует словари ({key: value}) и списки ([item_1, item_2, …]).

В YAML некоторые элементы синтаксиса хорошо знакомы «питаностам», например, отступы (пробелы) для указания структуры документа и знак # — однострочный комментарий. Также некоторые структуры YAML можно записать однострочной записью очень похожей на синтаксис Python, например, словарь можно записать так: {ключ : значение, ключ : значение}.

Синтаксис YAML очень прост, достаточно запомнить несколько простых правил:

  • файл должен начинаться с 3 дефисов— «---», а заканчиваться 3 точками — «…»;
  • первый блок (словарь или список) — задает рабочее количество отступов, которое будет использовано во всём документе;
  • после знака «-», обозначающего элемент списка, ставится пробел;
  • все именованные наборы данных и строки должны иметь уникальные названия.

Как мы сказали вначале, YAML имеет простой человекочитаемый синтаксис — это его основной плюс и одновременно минус, так как такой свободный синтаксис способствует большому количеству ошибок. Валидаторы YAML-файлов помогают избежать ошибок.

Для Linux можно воспользоваться yamllint. Он наглядно выводит отчет об ошибках. Нам же для валидации более удобно использовать встроенную библиотеку Python — yaml.

Если вы тоже используете Python, воспользуйтесь таким приемом:

  1. Создайте в удобной для вас директории пустой тестовый YAML-файл;
  2. Вызовите командой из этой директории интерпретатор Python;
  3. Введите: import yaml, а затем print(yaml.load(open("название файла"))).

Всё! Можно изменять содержимое файла (не забудьте сохранить изменения), вызывать команду печати и сразу видеть результат интерпретации.

Итак, теперь у нас есть все инструменты, давайте переходить к первой и самой простой YAML-конструкции — строке.

Строка

Строка — простая примитивная запись данных по типу ключ : значение. В YAML строки можно не брать в кавычки, за исключением случая, когда мы прописываем конвейеры команд в Linux:

---
string: This is a string.
command: "sh interface | include Queueing strategy:"
...

В интерпретации Python строка в YAML — это словарь, а значит в качестве ключа или названия строки может быть текст и цифры. Дополним пример выше строкой с цифрой в ключе и выведем это всё на печать через Python:

{'string': 'This is a string.', 'command': 'sh interface | include Queueing strategy:', 2: 'This is two'}

Кстати, пустой YAML-файл Python вернёт не как пустой словарь — {}, а как None.

В YAML также предусмотрен механизм работы с длинными строками. Длинные строки можно записать в блочном формате:

---
string:
  Это очень длинная строка,
  которую сложно читать в однострочной записи,
  так как она вылезает за пределы окна.
...

В этом примере нужно обратить внимание на отступ у текстовых строк. Если их не поставить — будет возвращена ошибка. Кстати, блочный стиль записи текста имеет три режима интерпретации:

  • без добавления знака переноса каретки в конце строки — «\n»;
  • с добавлением «\n» на каждой строке;
  • с добавлением «\n» только в конце текста.

Для добавления «\n» в конец строки в YAML применяется специальный символ конкатенации «>», который добавляется после названия строки на одной строчке:

string: >

Взглянем на вывод Python в терминале:

{'string': 'Это очень длинная строка, которую сложно читать в однострочной записи, так как она вылезает за пределы окна.\n'}

Можно также вывести текст со всеми знаками переноса каретки на новою строку. Для этого используется другой спец. знак в YAML — «|». Он применяется так же как и «>»:

string: |

Вывод Python будет таким:

{'string': 'Это очень длинная строка,\nкоторую сложно читать в однострочной записи,\nтак как она вылезает за пределы окна.\n'}

Всегда помните про отступы в блоках. Если не поставить отступы перед каждым переносом строки — будет ошибка. Для избежания подобных неприятностей пользуйтесь YAML-валидаторами.

Со строками разобрались, можно переходить к следующей структуре данных — списку.

Список

Список YAML

Список — это упорядоченная коллекция данных, доступ к которым возможен по их индексам. Вот пример простого списка, где все значения строки:

---
- element1
- element2
- element3
...

Так же как в Python, YAML-списки могут содержать цифры и булевы значения:

---
- 2
- element2
- yes
...

Обратите внимание, здесь мы использовали слово «yes», которое в YAML имеет значение «True» — логическое «Да». Эта одна из особенностей синтаксиса YAML. Python выводит на печать наш список так:

[2, 'element2', True]

Списки могут быть также именованными и вложенными. Именованный список по синтаксису похож на строку. Задается название списка, ставится двоеточие, а элементы перечисляются через «-» с одинарным отступом:

---
list:
  - 2
  - element2
  - true
...

Python воспринимает именованный список как словарь, где ключом является название списка, а значением — сам словарь. Вот как это выглядит при печати:

{'list': [2, 'element2', True]}

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

---
list_level_1:
  - 2
  - element2
  - true
  - list_level_2:
    - element1
    - element2
    - element3
...

Такой список в выводе Python уже выглядит сложным для восприятия, даже с применением альтернативного модуля печати pprint():

{'list_level_1': [2,'element2',True,{'list_level_2': ['element1', 'element2', 'element3']}]}

Если вам нужно получить просто список с вложенными списками, тогда вместо названия списка ставится «-», а у элементов вложенного списка — отступы:

---
-
  - 2
  - element2
  - true
-
  - list_level_2
  - element1
  - element2
  - element3
...

Python такую запись интерпретирует так:

[[2, 'element2', True], ['list_level_2', 'element1', 'element2', 'element3']]

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

Словарь

Словарь — это набор данных по типу «ключ : значение». Словарь в YAML можно записать блоком:

---
author:
  name: Ivan Katkov
  job: Tech writer
  skill: Normal
...

Такой словарь содержит ключ — автор, значение — словарь с вложенными элементами. Вывод Python будет таким:

{'author': {'job': 'Tech writer', 'name': 'Ivan Katkov', 'skill': 'Normal'}}

Словарь так же как и строку или список можно записать одной строчкой:

author: {name: Ivan Katkov, job : Tech writer, skill: Normal}

Словари могут иметь вложенность. Например, тут словарь authors имеет два вложенных словаря, у которых ключи — это имена авторов, а их значения — атрибуты авторов (возраст, специализация, уровень подготовки, список публикаций).

---
authors:
  Ivan Katkov:
    age: 35
    speciality: IT
    skill: High
    publications:
      - publication1
      - publication2
      - publication3
  Fedor Pupkin:
    age: 30
    speciality: IT
    skill: Normal 
    publications:
      - publication1
      - publication2
      - publication3
...

Блочная запись YAML выглядит хорошо и опрятно. Если бы мы писали подобную структуру однострочником, это выглядило бы не слишком понятно:

{'authors': {'Ivan Katkov': {'age': 35, 'speciality': 'IT', 'skill': 'Hight', 'publications': ['publication1', 'publication2', 'publication3']}, 'Fedor Pupkin': {'age': 30, 'speciality': 'IT', 'skill': 'Normal', 'publications': ['publication1', 'publication2', 'publication3']}}}

Итак, мы рассмотрели основы синтаксиса YAML и познакомились с базовыми структурами разметки: строками, словарями, списками. Давайте теперь для закрепления соберём всё вместе и разберём пример, где используется всё это вместе.

Практика и закрепление материала

Давайте создадим YAML-файл, который будет содержать краткое резюме. В данном случае моё :) Помним, что YAML-файл должен открываться 3 дефисами и закрывать 3 точками. Добавим их и три строки: имя автора, позицию, уровень скилов и статус:

---
name: Ivan Katkov
job: Tech writer
skill: Normal
employed: true
...

Помним, что YAML абсолютно лоялен к записи булевых значений, поэтому значения: «yes», «YES», «true», «True» — будут интерпретированы как логическое true.

Далее добавим список любимой еды, чтобы потенциальные коллеги знали, что мы любим:

favorite food:
  - Doshik
  - Pivchik
  - Kartofan4ik
  - Ogur4iki

Обращаем внимание на отступ до дефиса и пробел после него. Если не поставить пробел после дефиса, то все последующие элементы будут восприняты как единая запись. Взглянем, на вывод Python в терминал и убедимся, что всё нормально:

{'name': 'Ivan Katkov', 'job': 'Techwriter', 'skill': 'Normal', 'employed': True, 'favorite food': ['Doshik', 'Pivchik', 'Kartofan4ik', 'Ogur4iki']}

Всё отлично! С ошибкой пропущенного пробела после дефиса — запись выглядела бы так:

{'name': 'Ivan Katkov', 'job': 'Techwriter', 'skill': 'Normal', 'employed': True, 'favorite food': '-Doshik - Pivchik - Kartofan4ik - Ogur4iki'}

Едем дальше. Добавим теперь словарь с вложенными словарями, который будет содержать уровень знания языков программирования, библиотек и фреймворков:

languages:
  python:
    level: Junior
    libraries: [Pandas, NumPy, Pytorch]
    frameworks: [Flask, Django]
  rust:
    level: Pre-junior
    libraries: None
    frameworks: None
  Javascript: 
    level: Pre-junior
    libraries: None
    frameworks: Angular

Выглядит просто и элегантно. Главное помнить про отступы. А вот вывод Python в терминал уже может заставить поломать немного голову. Отложим пока финальное сравнение записей, сделаем лучше финальный штрих — добавим многострочную запись с применением знака конкатенации «>»:

education: >
  Saint-Petersburg
  State Institute
  of Culture

Отлично! Наше резюме закончено. Давайте теперь посмотрим одновременно на YAML-файл и его интерпретацию в Python:

Содержимое YAML-файла

---
name: Ivan Katkov
job: Techwriter
skill: Normal
employed: true

 

favorite food:
  - Doshik
  - Pivchik
  - Kartofan4ik
  - Ogur4iki

 

languages:
  python:
    level: Junior
    libraries: [Pandas, NumPy, Pytorch]
    frameworks: [Flask, Django]
  rust:
    level: Pre-junior
    libraries: None
    frameworks: None
  Javascript: 
    level: Pre-junior
    libraries: None
    frameworks: Angular

 

education: >
  Saint-Petersburg
  State Institute
  of Culture
...

 

Вывод Python в терминал

{     'name': 'Ivan Katkov',
    'job': 'Techwriter',
    'skill': 'Normal',
    'employed': True,
    'favorite food': ['Doshik', 'Pivchik', 'Kartofan4ik', 'Ogur4iki'],
    'languages': {
        'python': {
            'level': 'Junior',
            'libraries': ['Pandas', 'NumPy', 'Pytorch'],
            'frameworks': ['Flask', 'Django']}, 
        'rust': {
            'level': 'Pre-junior',
            'libraries': 'None',
            'frameworks': 'None'},
        'Javascript': {
            'level': 'Pre-junior',
            'libraries': 'None',
            'frameworks': 'Angular'}},
    'education': 'Saint-Petersburg State Institute of Culture\n'
}

 

Согласитесь, YAML-файл читается намного проще, чем словарь Python, примерно также будет выглядеть и JSON. Однако у YAML есть серьёзные подводные камни:

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

Теперь, когда мы овладели в достаточной степени знанием синтаксиса YAML можно на боевую создавать Docker-compose файл и разбираться в том, как это всё работает. Этим мы и займемся в ближайших публикациях.