Знайомство з YAML: базові поняття та синтаксис
YAML Ain’t Markup Language (з англ. YAML не мова розмітки) — це формат запису даних. Його головна перевага — простий синтаксис, завдяки якому конфігураційні файли легше читати.
Завдяки цьому YAML став одним із ключових інструментів у світі DevOps. Його використовують для:
- налаштування програм;
- опису інфраструктури;
- автоматизації процесів;
- зберігання конфігурацій у найрізноманітніших технологічних стеків — від Python і Ruby до Java та C#.
Тепер, коли ми знаємо, де використовується YAML і чому він такий популярний, настав час познайомитися з його елементами та базовими принципами роботи.
Будова YAML-документа
На перший погляд цей файл може здатися набором загадкових відступів і двокрапок, але не хвилюйтеся — насправді його структура простіша ніж здається. Розглянемо цей приклад:
---
doe: "a deer, a female deer"
ray: "a drop of golden sun"
pi: 3.14159
xmas: true
french-hens: 3
calling-birds:
- huey
- dewey
- louie
- fred
xmas-fifth-day:
calling-birds: four
french-hens: 3
golden-rings: 5
partridges:
count: 1
location: "a pear tree"
turtle-doves: two
1. Початок документа
Файл починається з трьох тире:
---
Формат підтримує зберігання кількох документів в одному, тому парсер (аналізатор складників) використовує потрійне тире як роздільник між ними.
2. Пари «ключ — значення»
Основа YAML — це пари «ключ — значення»:
doe: a deer, a female deer
Тут:
- doe — ключ;
- a deer, a female deer — значення.
Саме з таких пар складається більшість конфігураційних файлів.
3. Типи даних
У нашому прикладі використовується одразу кілька типів даних:
- pi: 3.14159 — число з плаваючою комою;
- xmas: true — логічне значення;
- french-hens: 3 — ціле число.
4. Списки, або масиви
Для створення списків використовуються тире:
calling-birds:
- huey
- dewey
- louie
- fred
У цьому прикладі ключ calling-birds містить список із чотирьох елементів.
Списки часто використовують для опису контейнерів, серверів, користувачів, змінних середовища та інших наборів даних.
5. Відступи
На відміну від JSON чи XML, YAML використовує відступи для позначення структури даних.
Наприклад:
calling-birds:
- huey
- dewey
Елементи списку належать до ключа calling-birds саме завдяки відступу.
Далі ми більш детально розглянемо роль, яку вони виконують.
6. Вкладені структури
Остання частина прикладу демонструє вкладені об’єкти:
xmas-fifth-day:
calling-birds: four
french-hens: 3
golden-rings: 5
partridges:
count: 1
location: "a pear tree"
Тут ключ xmas-fifth-day містить кілька вкладених значень, а ключ partridges — ще один вкладений об’єкт зі своїми полями.
Фактично — це словник усередині словника.
Правила форматування YAML
У YAML пробіли є важливою частиною форматування. Якщо не вказано інше, перехід на новий рядок означає завершення поточного поля. Структура документа формується за допомогою відступів, які можуть складатися з одного або кількох пробілів.
Важливо пам’ятати, що специфікація YAML не рекомендує використовувати табуляцію, оскільки різні інструменти можуть обробляти її по-різному. Щоб уникнути помилок і забезпечити коректне читання файлу, для відступів варто використовувати лише пробіли.
Розглянемо приклад документа, у якому вкладені елементи мають відступ у два пробіли.
foo: bar
pleh: help
stuff:
foo: bar
bar: foo
Тепер подивимось, як Python працює з таким YAML-файлом на практиці. Для прикладу збережемо наш документ у файл foo.yaml.
За допомогою бібліотеки PyYAML Python може зчитати вміст файлу та перетворити його на словник — одну з базових структур даних у мові. Далі ми просто переберемо всі ключі та значення й виведемо їх на екран, щоб побачити, як Python інтерпретує структуру.
import yaml
from yaml import load
try:
from yaml import CLoader as Loader
except ImportError:
from yaml import Loader
if __name__ == '__main__':
stream = open("foo.yaml", 'r')
dictionary = yaml.load(stream)
for key, value in dictionary.items():
print (key + " : " + str(value))
Вихідний результат:
foo : bar
pleh : help
stuff : {'foo': 'bar', 'bar': 'foo'}
Коли Python виводить словник у вигляді рядка, він використовує свій стандартний формат відображення даних. Саме його ми побачимо в прикладі нижче.
З результату видно, що документ YAML було перетворено на словник Python: він містить два рядкових значення та ще один вкладений словник. Завдяки такій структурі YAML дозволяє не просто зберігати окремі дані, а й будувати цілі ієрархії пов’язаних об’єктів.
І це лише базовий приклад. Насправді він здатен підтримує значно складніші структури.
Як додавати коментарі до YAML-файлів
Щоб залишити примітку прямо в документі, достатньо поставити знак решітки # перед текстом пояснення. Такі коментарі можуть займати окремий рядок або розташовуватися поруч із конкретним значенням.
___
# This is a full line comment
foo: bar # this is a comment, too
Коментарі не впливають на обробку документа програмами, тому їх можна використовувати для підказок щодо складних налаштувань.
Числові типи в YAML
YAML вміє автоматично розпізнавати різні типи чисел. Ми вже бачили приклади цілих чисел і чисел з плаваючою комою, але на цьому його можливості не закінчуються.
Наприклад, ціле число може бути записане не лише у звичному десятковому форматі, а й у шістнадцятковому або вісімковому. Завдяки цьому його зручно використовувати в проєктах, де можуть знадобитися різні системи числення.
---
foo: 12345
bar: 0x12d4
plop: 023332
Подивимось, який результат отримаємо, якщо запустимо наш Python-скрипт для цього документа.
foo : 12345
bar : 4820
plop : 9946
Як бачимо префікс 0x вказує на шістнадцяткове число, а початковий нуль — на вісімкове. Такі записи автоматично розпізнаються під час обробки даних.
YAML також підтримує числа з плаваючою комою — як у звичайному десятковому форматі, так і в експоненціальному записі. Це дає змогу зручно працювати як зі звичайними, так і з дуже великими чи малими значеннями.
---
foo: 1230.15
bar: 12.3015e+05
Після обробки цих записів отримаємо такий результат:
foo : 1230.15
bar : 1230150.0
Окрім звичайних чисел, формат підтримує спеціальні значення, такі як нечислове значення (NaN) та нескінченність.
---
foo: .inf
bar: -.Inf
plop: .NAN
У цьому прикладі значення foo інтерпретується як нескінченність, bar — як від’ємна нескінченність, а plop — як спеціальне значення NaN.
Рядки в YAML
YAML працює з рядками у кодуванні Unicode, що дає змогу без проблем використовувати символи різних мов. При цьому більшість рядків можна записувати без лапок.
---
foo: this is a normal string
Після запуску тестової програми побачимо такий результат:
foo: this is a normal string
Якщо ж потрібно, щоб керуючі послідовності були правильно інтерпретовані, значення слід записувати в подвійних лапках.
---
foo: "this is not a normal string\n"
bar: this is not a normal string\n
Різниця полягає в тому, як обробляється послідовність \n.
У першому значенні вона перетворюється на символ нового рядка, тоді як у другому залишається звичайним текстом. Оскільки значення не взято в лапки, \n сприймається не як спеціальна команда, а як два окремі символи.
foo: this is not a normal string
bar: this is not a normal string\n
Якщо рядок взято в одинарні лапки, його вміст інтерпретується буквально. Це означає, що спеціальні послідовності та елементи форматування не обробляються, а зберігаються у тому вигляді, в якому були записані.
Також YAML підтримує багаторядкові значення. Для їхнього запису можна скористатися символом >, який дозволяє розмістити текст у вигляді блоку та зробити документ зручнішим для читання.
bar: >
this is not a normal string it
spans more than
one line
see?
Попри те, що текст записано на кількох рядках, після обробки він перетворюється на один суцільний рядок без символів перенесення.
bar : this is not a normal string it spans more than one line see?
Символ вертикальної риски | працює схожим чином, але в цьому випадку вміст блоку зберігається без змін.
bar: |
this is not a normal string it
spans more than
one line
see?
Тобто після обробки текст зберігає початкову структуру з усіма перенесеннями рядків.
bar : this is not a normal string it
spans more than
one line
see?
Обробка значень Null у YAML
Для позначення нульового значення в YAML можна використовувати символ тильди ~ або порожнє значення без лапок. В обох випадках воно буде інтерпретоване як null.
---
foo: ~
bar: null
Ось що виведе наша програма:
foo : None
bar : None
Як ви могли зауважити Python не любить називати речі так само, як усі. Тому значенню null тут відповідає None.
Логічні значення в YAML
Для позначення логічного значення true можна використовувати кілька варіантів запису, зокрема True, On або Yes.
Аналогічно, значення false можна записати як False, Off або No.
---
foo: True
bar: False
light: On
TV: Off
Створення списків у YAML
Для компактних списків цей формат дозволяє розміщувати всі елементи в одному рядку.
---
items: [ 1, 2, 3, 4, 5 ]
names: [ "one", "two", "three", "four" ]
Для довших списків зазвичай зручніше оформлювати кожен елемент на окремому рядку.
---
items:
- 1
- 2
- 3
- 4
- 5
names:
- "one"
- "two"
- "three"
- "four"
Багаторядковий формат корисний для списків, які містять складні об’єкти. На відміну від скалярів — простих значень на кшталт числа, рядка або логічного значення — такі об’єкти можуть складатися з кількох полів і вкладених елементів.
___
items:
- things:
thing1: huey
things2: dewey
thing3: louie
- other things:
key: value
Елементи масиву можуть містити будь-які допустимі значення YAML. При цьому всі вони не зобов’язані бути одного типу: в одному списку можуть одночасно зберігатися числа, рядки, логічні значення та навіть складні об’єкти.
Визначення словників у YAML
Ми вже бачили, як працюють словники в YAML, але вони підтримують не лише багаторядковий запис. За потреби весь словник можна розмістити в одному рядку, що особливо зручно для невеликих наборів даних.
Якщо цей формат здається знайомим, то недарма — саме так словники зазвичай відображаються під час виведення в Python.
---
foo: { thing1: huey, thing2: louie, thing3: dewey }
Ми вже зустрічали цей приклад раніше.
---
foo: bar
bar: foo
Це означає, що словники можуть містити списки, інші словники та будь-які допустимі значення. Важливо тут не захопитися зі створенням вкладень, аби потім самому не загубитися в структурі.
---
foo:
bar:
- bar
- rab
- plop
Chomp-модифікатори
Під час роботи з багаторядковим текстом YAML дозволяє керувати тим, що відбуватиметься з перенесеннями рядків наприкінці значення. Для цього використовуються chomp-модифікатори.
Наприклад, якщо додати знак плюса + до операторів > або |, YAML збереже фінальний символ нового рядка. Такий режим називається preserve chomp.
bar: >+
this is not a normal string it
spans more than
one line
see?
Тож якщо значення закінчується пробільним символом, наприклад переносом рядка, YAML збереже його. Якщо ж цей символ не потрібен, його можна прибрати за допомогою оператора strip.
bar: |-
this is not a normal string it
spans more than
one line
see?
Робота з кількома документами YAML
Для явного позначення меж документа в YAML використовують маркери — на початку та … наприкінці.
Маркер початку документа потрібен не завжди, але деякі YAML-парсери без нього не працюють. Наприклад, бібліотека Jackson для Java очікує його наявності, тоді як PyYAML для Python може обробити документ і без цього маркера. Оператор завершення документа зазвичай використовують у файлах, де зберігається кілька YAML-документів.
Далі розглянемо, як це виглядає безпосередньо в Python.
import yaml
from yaml import load
try:
from yaml import CLoader as Loader
except ImportError:
from yaml import Loader
if __name__ == '__main__':
stream = open("foo.yaml", 'r')
dictionary = yaml.load_all(stream, Loader)
for doc in dictionary:
print("New document:")
for key, value in doc.items():
print(key + " : " + str(value))
if type(value) is list:
print(str(len(value)))
Функція load_all() у PyYAML зчитує всі документи, що містяться в одному YAML-потоці. Скористайтеся нею, щоб обробити файл із кількома документами.
---
bar: foo
foo: bar
...
---
one: two
three: four
Після запуску скрипт успішно знаходить у файлі два YAML-документи.
New document:
bar : foo
foo : bar
New document:
one : two
three : four
Що варто запам’ятати про YAML
Якщо вас цікавить DevOps, рано чи пізно ви зіткнетеся з YAML, адже цей формат широко використовують для налаштування інфраструктури. Від Docker Compose до Kubernetes — він часто зустрічається в інструментах для автоматизації, контейнеризації тому знання цього формату стане хорошою інвестицією для майбутніх фахівців.
Втім, справжнє розуміння конфігураційних файлів приходить не під час читання документації, а на практиці. Якщо хочете навчитися впевнено працювати з Linux, автоматизацією та сучасними тулзами, наш курс DevOps з нуля допоможе отримати необхідну базу.