Перевод статьи «Use Python to parse configuration files».
Сегодня мы поговорим о том, как использовать Python для парсинга файлов конфигурации. Иногда программе требуется достаточно много параметров, поэтому помещать их все в качестве аргументов командной строки или переменных среды неприятно и нецелесообразно. В таких случаях вам нужно будет использовать файл конфигурации.
Существует несколько популярных форматов файлов конфигурации. Среди них –
- почтенный (хотя иногда недостаточно определенный) формат INI,
- популярный, но иногда сложный для написания вручную формат JSON,
- обширный, но иногда удивительный в деталях формат YAML
- и новейшее дополнение TOML, о котором многие люди не слышали. Пока что.
Ваша первая задача – выбрать формат, а затем задокументировать этот выбор. Разобравшись с этой простой частью, можно заняться парсингом конфигурации.
Иногда рекомендуется иметь класс, соответствующий «абстрактным» данным в конфигурации. Поскольку этот код ничего не делает с конфигурацией, это самый простой способ показать логику парсинга с синтаксической точки зрения.
Представьте себе конфигурацию файлового процессора: он включает в себя входной каталог, выходной каталог и файлы, которые нужно выбрать.
Абстрактное определение класса конфигурации может выглядеть примерно так:
from __future__ import annotations import attr @attr.frozen class Configuration: @attr.frozen class Files: input_dir: str output_dir: str files: Files @attr.frozen class Parameters: patterns: List[str] parameters: Parameters
Чтобы упростить код, зависящий от формата, можно написать функцию, которая парсит этот класс из словаря. Обратите внимание: здесь предполагается, что в конфигурации будут использоваться дефисы, а не символы подчеркивания. Такое несоответствие не редкость.
def configuration_from_dict(details): files = Configuration.Files( input_dir=details["files"]["input-dir"], output_dir=details["files"]["output-dir"], ) parameters = Configuration.Paraneters( patterns=details["parameters"]["patterns"] ) return Configuration( files=files, parameters=parameters, )
JSON
JSON (JavaScript Object Notation) – это формат, подобный JavaScript.
Вот пример конфигурации в формате JSON:
json_config = """ { "files": { "input-dir": "inputs", "output-dir": "outputs" }, "parameters": { "patterns": [ "*.txt", "*.md" ] } } """
Логика парсинга «встраивает» JSON во встроенные структуры данных Python (словари, списки, строки) с помощью модуля json
, а затем создает класс из словаря:
import json def configuration_from_json(data): parsed = json.loads(data) return configuration_from_dict(parsed)
INI
Формат INI, изначально популярный в операционной системе Windows, стал де-факто стандартом конфигурации.
Вот такая конфигурация у нас получится на INI:
ini_config=""" [files] input-dir = inputs output-dir = outputs [parameters] patterns = ['*.txt', '*.md'] """
Python может парсить его с помощью встроенного модуля configparser
. Парсер ведет себя как dict-подобный объект, поэтому его можно передать напрямую в configuration_from_dict
:
import configparser def configuration_from_ini(data): parser = configparser.ConfigParser() parser.read_string(data) return configuration_from_dict(parser)
YAML
YAML (Yet Another Markup Language) – это расширение JSON, которое упрощает написание кода вручную. Отчасти это достигается за счет длинной спецификации.
Вот такая конфигурация будет в YAML:
yaml_config = """ files: input-dir: inputs output-dir: outputs parameters: patterns: - '*.txt' - '*.md' """
Чтобы Python запарсил это, вам необходимо установить сторонний модуль. Самый популярный — PyYAML (pip install pyyaml
). Парсер YAML также возвращает встроенные типы данных Python, которые можно передать в configuration_from_dict
. Однако YAML-парсер ожидает поток, поэтому вам нужно преобразовать строку в поток.
import io import yaml def configuration_from_yaml(data): fp = io.StringIO(data) parsed = yaml.safe_load(fp) return configuration_from_dict(parsed)
TOML
TOML (Tom’s Own Markup Language) разработан как легкая альтернатива YAML. Спецификация здесь короче. И такой формат уже кое-где популярен (например, менеджер пакетов Rust, Cargo, использует ее для конфигурации своих пакетов).
А вот так будет выглядеть наша конфигурация на TOML:
toml_config = """ [files] input-dir = "inputs" output-dir = "outputs" [parameters] patterns = [ "*.txt", "*.md",] """
Чтобы запарсить TOML, вам необходимо установить сторонний пакет. Самый популярный из них называется просто toml
. Он возвращает базовые типы данных Python.
import toml def configuration_from_toml(data): parsed = toml.loads(data) return configuration_from_dict(parsed)
Заключение
В этой статье мы поговорили про использование Python для парсинга файлов конфигурации. Выбор формата конфигурации – непростое решение и своего рода тонкий компромисс. Однако, как только вы примете решение, Python сможет справиться с парсингом большинства популярных форматов, при этом используя всего лишь несколько строк кода.