YAML — это формат файла, обычно используемый для сериализации данных. Существует множество проектов, использующих файлы YAML для настройки, таких как Docker-compose, pre-commit, TravisCI, AWS Cloudformation, ESLint, Kubernetes, Ansible и многие другие. Знание особенностей YAML поможет в работе с ними.
Сначала рассмотрим основы: YAML — это надмножество JSON (источник). Каждый корректный файл JSON также корректен как YAML. Это означает, что в вашем распоряжении есть все форматы, которые вы ждете: целые числа, числа с плавающей точкой, строки, булевы значения, null. Также последовательности (sequence) и отображения (map) . В зависимости от языка программирования вы можете заменить последовательность на массив или список, а отображение на словарь.
Обычно это выглядит так:
mysql:
host: localhost
user: root
password: something
preprocessing_queue: # Line comments are available!
- name: preprocessing.scale_and_center
width: 32
height: 32
- preprocessing.dot_reduction
use_anonymous: true
Эквивалентная нотация
В YAML есть много эквивалентных способов описания:
list_by_dash:
- foo
- bar
list_by_square_bracets: [foo, bar]
map_by_indentation:
foo: bar
bar: baz
map_by_curly_braces: {foo: bar, bar: baz}
string_no_quotes: Monty Python
string_double_quotes: "Monty Python"
string_single_quotes: 'Monty Python'
bool_english: yes
bool_english_no: no
bool_python: True
bool_json: true
Тут несколько слов предостережения:
language: no # ISO 639-1 code for the Norwegian language
Это “no”
интерпретируется как false
. Писать надо "no"
или 'no'
.
В общем, я рекомендую использовать true
и false
точно так же, как для булевых значений в JSON, но YAML поддерживает одиннадцать способов записи булевых значений. При желании применить кавычки для строк я бы также рекомендовал пользоваться “
, как в JSON. Запомнить “no” все равно нужно, но, по крайней мере, для новичков в YAML файл будет выглядеть более привычно.
Есть и другие примеры, которые столь же коварны:
013
отображается на11
, так как ведущий ноль запускает восьмеричную нотацию.4:30
отображается на270
. Мне сообщали, что это значение автоматически преобразуется в секунды, поскольку интерпретируется как длительность: 4*60 + 30 = 270 . Интересно, что этот паттерн до сих пор работает с1:1:1:1:1:1:1:1:4:30
.
Длинные строки
disclaimer: >
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
In nec urna pellentesque, imperdiet urna vitae, hendrerit
odio. Donec porta aliquet laoreet. Sed viverra tempus fringilla.
Это эквивалентно следующему JSON (перенос строки добавлен для удобства чтения — пожалуйста, не обращайте внимания):
{"disclaimer": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In nec urna pellentesque, imperdiet urna vitae, hendrerit odio. Donec porta aliquet laoreet. Sed viverra tempus fringilla."}
Строки с несколькими линиями
mail_signature: |
Martin Thoma
Tel. +49 123 4567
Эквивалент в JSON выглядит так:
{"mail_signature": "Martin Thoma\nTel. +49 123 4567"}
Обратите внимание: начальные пробелы игнорируются. Первая строка (“Martin Thoma”) определяет количество игнорируемых начальных пробелов.
Якорь
email: &emailAddress "[email protected]"
id: *emailAddress
В JSON это эквивалентно:
{"email": "[email protected]", "id": "[email protected]"}
&
определяет переменную EmailAddress
значением “[email protected]
. Затем *
указывает, что далее следует имя переменной.
То же самое можно сделать и с отображениями:
foo: &default_settings
db:
host: localhost
name: main_db
port: 1337
email:
admin: [email protected]
prod: *default_settings
dev: *default_settings
В результате получаем:
{ "dev": { "db": {"host":
"localhost",
"name": "main_db",
"port": 1337},
"email": {"admin": "[email protected]"}},
"foo": { "db": {"host": "localhost",
"name": "main_db",
"port": 1337},
"email": {"admin": "[email protected]"}},
"prod": { "db": {"host": "localhost",
"name": "main_db",
"port": 1337},
"email": {"admin": "[email protected]"}}}
Теперь вам, возможно, захочется ввести пароль для окружений dev и prod. Вы можете сделать это с помощью ключа слияния (merge key) <<
:
foo: &default_settings
db:
host: localhost
name: main_db
port: 1337
email:
admin: [email protected]
prod:
<<: *default_settings
app:
port: 80
dev: *default_settings
Что в JSON будет эквивалентно:
{ "foo": { "db": {"host": "localhost",
"name": "main_db",
"port": 1337},
"email": {"admin": "[email protected]"}},
"prod": { "app": {"port": 80},
"db": {"host": "localhost",
"name": "main_db",
"port": 1337},
"email": {"admin": "[email protected]"}},
"dev": { "db": {"host": "localhost",
"name": "main_db",
"port": 1337},
"email": {"admin": "[email protected]"}},}
Приведение типов
Двойной восклицательный знак !!
имеет в YAML особое значение. Он называется “обработчик вторичных тэгов” и является сокращением для !tag:yaml.org,2002:
(источник).
Вы можете выполнять простые преобразования, такие как:
price: !!float 42
id: !!str 42
Или более сложные — к примеру, сопоставление с типами Python по умолчанию, которые не указаны непосредственно в YAML:
tuple_example: !!python/tuple
- 1337
- 42
set_example: !!set {1337, 42}
date_example: !!timestamp 2020-12-31
Можно прочитать это так:
import yaml
import pprintwith open("example.yaml") as fp:
data = fp.read()pp = pprint.PrettyPrinter(indent=4)pased = yaml.unsafe_load(data)
pp.pprint(pased)
И получить следующее:
{ 'date_example': datetime.date(2020, 12, 31),
'set_example': {1337, 42},
'tuple_example': (1337, 42)}
В этом примере используется специфичный для Python тэг !!python/tuple
и некоторые стандартные тэги YAML. Здесь можно познакомиться с хорошим обзором PyYAML.
## Стандартные тэги YAML
YAML Python 3
!!null None
!!bool bool
!!int int
!!float float
!!binary bytes
!!timestamp datetime.datetime
!!omap, !!pairs list of pairs
!!set set
!!str str
!!seq list
!!map dict## Python-specific tags
YAML Python 3
!!python/none None
!!python/bool bool
!!python/bytes bytes
!!python/str str
!!python/unicode str
!!python/int int
!!python/long int
!!python/float float
!!python/complex complex
!!python/list list
!!python/tuple tuple
!!python/dict dict## Complex Python tags
!!python/name:module.name module.name
!!python/module:package.module package.module
!!python/object:module.cls module.cls instance
!!python/object/new:module.cls module.cls instance
!!python/object/apply:module.f value of f(...)
Обратите внимание, что загружать нестандартные тэги — небезопасно. С помощью !!python/object/apply:module.f
есть возможность выполнить произвольный код. В PyYAML для этого понадобится yaml.unsafe_load
. Следовательно лучше им не пользоваться.
Несколько документов в одном YAML
В YAML три тире служат разделителем документов.
foo: bar
---
fizz: buzz
В Python вы можете загрузить это в таком виде с помощью PyYAML:
import yaml
with open("example.yaml") as fp:
data = fp.read()
parsed = yaml.safe_load_all(data) # parsed - это генератор
Преобразовав parsed
в список и выведя на экран, вы увидите:
[{'foo': 'bar'}, {'fizz': 'buzz'}]
Обратите внимание: это НЕ альтернативная нотация для списков. Это разные документы.
Генератор статических сайтов Pelican использует этот механизм, чтобы отличить метаданные от контента. Статический генератор сайтов Jekyl тоже пользуется им. И oc process
также генерирует такие YAML.
Читайте также:
- Создаем собственную Alexa в 20 строках Python
- 7 способов повысить свою продуктивность в Python
- Классы данных в Python и их ключевые особенности
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи: Martin Thoma, “6 YAML Features most programmers don’t know”