Наверняка вы зашли в эту статью, потому что экспериментируете с Docker или Ansible, или просто вам захотелось разобраться с YAML. Так или иначе вы в нужном месте!
Коснемся сначала истории YAML, а потом перейдём к разбору синтаксиса и познакомимся с основными конструкциями YAML: строками, списками и словарями. В конце закрепим новые знания созданием несложного YAML-файла.
Если времени вникать в статью нет, можно прочитать краткий мануал из базы знаний.
Немного об истории YAML
YAML (Yet Another Markup Language) — это ещё один язык разметки. Да, именно так он изначально официально назывался. Конкурировать со «взрослыми» языками разметки, такими как JSON и XML с таким названием сложно, поэтому Кларк Эванс — автор концепции разметки, изменил расшифровку YAML на «Ain't Markup Language» — не язык разметки. Что же изменилось? Изменилась концепция самой разметки.
Доминанта сместилась с упора на удобную разметку в сторону работы с данными, при этом удобный человекочитаемый формат, конечно же, сохранился. Благодаря такому решению, со временем YAML стал одним из самых популярных языков разметки. Особо приглянулся он сетевым- и инфраструктурным инженерам, благодаря целям, которые Кларк Эванс изначально поставил перед своим детищем:
- быть понятным человеку;
- поддерживать структуры данных, родственные для языков программирования;
- быть переносимым между языками программирования;
- использовать цельную модель данных для поддержки обычного инструментария;
- поддерживать потоковую обработку;
- быть выразительным и расширяемым;
- быть легким в реализации и использовании.
Сегодня YAML — основной формат для файлов конфигурации Ruby on Rails, Dancer, Symfony, GAE framework, Google App Engine и Dart. Является основным языком описания классов, ресурсов и манифестов для пакетов приложений OpenStack Murano Project и Swagger.io.
Основная фишка 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, воспользуйтесь таким приемом:
- Создайте в удобной для вас директории пустой тестовый YAML-файл;
- Вызовите командой из этой директории интерпретатор Python;
- Введите: 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-валидаторами.
Со строками разобрались, можно переходить к следующей структуре данных — списку.
Список
Список — это упорядоченная коллекция данных, доступ к которым возможен по их индексам. Вот пример простого списка, где все значения строки:
---
- 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 файл и разбираться в том, как это всё работает. Этим мы и займемся в ближайших публикациях.