YAML - кто ты такой?
YAML (YAML Ain't Markup Language) - популярный формат для хранения данных, в особенности, конфигов. К примеру, конфигов для gitlab CI, для хуков линтеров в pre-commit описываются в YAML.
Формат легко читается, для восприятия легче, чем JSON, особенно когда структура сложная. Кроме того, YAML легко преобразуется в dict и с ним удобно работать через код.
Расширение файла может выглядеть и как ”.yml” и как “.yaml”, разницы нет.
Синтаксис и структура
Напишем небольшой конфиг и разберемся, как прописывать разные типы данных в YAML-файле. Представим, что у нас есть несколько сервисов с параметрами и база данных:
Итак, разберем детально что тут происходит:
- строки. Можно писать как с кавычками ("integration") так и без (admin, localhost)
- словари. Можно в одну строку: data: { ”key”: “value” }
можно блоками:
- списки. Можно в одну строку: ["slow", not bad"]
Можно указать каждый элемент на новой строке через дефис:
- якоря. Уникальная штука в YAML, которая позволяет избежать дублирования и дает возможность переиспользовать данные.
Для начала, якорь надо определить:
а потом ссылаться на него в других местах конфига:
и вместо этой ссылки будет подставлено значение host: localhost .
Чтение и запись YAML-файлов с помощью библиотеки PyYaml
PyYaml
Очень популярная библиотека для работы с YAML. Она не встроенная, потому нам надо ее установить:
pip install pyyaml
Чтение YAML-файлов
Чтобы распарсить YAML-файл в словарь Python, в PyYaml присутствует функция safe_load() . Есть еще функция load(), но ее не рекомендуется использовать с данными из непроверенных источников, т.к. она может вызывать любой код на Python.
Давайте создадим файл config.yml и сохраним туда наш конфиг.
Также создадим файл yaml_parser.py, где и будем реализовывать логику работы с YAML.
Напишем функцию для чтения конфига:
Что здесь происходит:
- С помощью библиотеки pathlib определяем текущую директорию (CWD) и путь до конфига (CONFIG_PATH)
- Внутри функции def open_config() проверяем, что объект по указанному в аргументе config_path пути существует и является файлом. Если какое-то из этих условий не выполняется, вызываем исключение raise FileNotFoundError , страхуя себя от лишних ошибок
- Далее открываем файл с помощью контекстного менеджера в режиме чтения, преобразуем наш конфиг в словарь и возвращаем его.
Посмотрим, что получится при вызове этой функции:
Итак, все работает! Теперь наш конфиг преобразован в словарь и с ним можно работать в коде.
Согласитесь, если описывать конфиги именно в таком формате, то читать их будет посложнее, чем YAML.
Запись YAML-файлов
Ну и финальное. Попробуем обратную процедуру, преобразуем и запишем словарь в YAML-файл.
Попробуем обратно преобразовать тот же конфиг из dict в YAML, затем сравним файлы и убедимся, что данные идентичны. Поехали!
Напишем функцию для записи словаря в YAML-файл:
Что здесь происходит:
- Функция принимает имя нового файла, включая расширение (filename) и словарь, который будем преобразовывать и сохранять (data)
- С помощью контекстного менеджера открываем (и создаем, если файла нет) файл в режиме записи
- с помощью функции safe_dump() преобразовываем и сохраняем данные без сортировки по ключам
Вызовем функцию и посмотрим, что происходит:
После выполнения кода, в той же директории рядом с исходным файлом будет создан файл new_config.yml с таким содержимым:
Структура и данные абсолютно идентичны, мы справились с задачей!
Есть лишь маленькое “но”, здесь не будет якорей или явного указания строк в кавычках, могут немного скорректироваться отступы.
Давайте убедимся, что наши файлы идентичны на все 100%. Откроем оба сохраненных конфига и проверим их на равенство с помощью assert :
В случае неравенства будет вызвано исключение AssertionError с указанным текстом ошибки.
Этого не произошло, т.к. наши данные идентичны.
***
Присоединяйтесь ко мне в Telegram: https://t.me/python3_with_love. Там есть все, и читать код намного удобнее.