Конфигурационные файлы как инструмент управления приложениями на Python

Вводные замечания о форматах конфигурационных файлов

Обычно, когда речь заходит о конфигурационных файлах, то на память приходят статичные текстовые файлы, содержащие описание различных настроек системы и выступающие главным образом в роли малозначительного элемента инфраструктуры кода. Т акого рода файлы несложно превратить в динамический элемент инфраструктуры, который позволит управлять логикой работы приложения без необходимости вносить изменения в программный код.

  • INI – самый простой формат из рассмотренных. С одной стороны чем проще читать конфигурационный файл, тем лучше, но с другой – файлы *.INI могут оперировать только одномерными структурами, т.е. структурами с простой одноуровневой иерархией. В большинстве прикладных задач, когда приходится иметь дело с моделями объектов, допускающих представление в виде списков, ассоциативных массивов и т.п., возможностей INI оказывается недостаточно.
  • JSON-файл выглядит как обычный словарь Python и может включать сложные иерархические зависимости, однако с точки зрения читаемости проигрывает и YAML, и TOML. Кроме того, JSON не поддерживает комментарии, а они часто могут значительно упростить сопровождение кода.
  • В отличие от предыдущих, формат TOML обладает несоизмеримой гибкостью и широтой арсенала поддерживаемых типов данных. TOML поддерживает простые пары «ключ-значение», массивы, классические и встроенные таблицы, массивы таблиц, булевы значения, а также локальные временные метки и временные метки со смещением.

Для сравнения рассмотрим одну и ту же модель объекта, описанного с помощью TOML и JSON.

Вот TOML-представление модели объекта:

А вот JSON-представление:

Синтаксические особенности JSON – избыточные фигурные и квадратные скобки – делают сложноструктурные JSON-файлы «размазанными».

Формат YAML обладает схожими с форматом TOML возможностями (в смысле гибкости представления моделей объектов и разнообразия поддерживаемых типов данных), но на сложных структурах выглядит компактнее.

Python-библиотеки для работы с конфигурационными файлами

Из всего многообразия предназначенных для работы с конфигурационными файлами библиотек – ориентированных на какой-то конкретный формат или «всеядных» – можно выделить следующие:

    : это элемент стандартной библиотеки Python, предназначенный для работы с INI-файлами Microsoft Windows. В распоряжении пользователя есть класс ConfigParser , который реализует базовые возможности библиотеки. : элемент не входит в стандартную библиотеку, поэтому его нужно установить с помощью менеджера пакетов pip pip install pyyaml . В Python-сценарии обращение к библиотеке выглядит как import yaml .
    : этот элемент тоже представляет собой стороннее решение для работы с форматом TOML и требует установки с помощью pip install toml . : это очень гибкая библиотека, которая 1) позволяет работать со всеми популярными форматами конфигурационных файлов (*.toml, *.yaml, *.json, *.ini, *.py), 2) поддерживает мультипрофили, т.е. конфигурационный файл может содержать несколько заголовков, относящихся к различным стадиям готовности программного продукта (например, default, development, production и т.д.), а нужный набор настроек затем вызывается с указанием соответствующего заголовка, 3) умеет работать с переменными окружения («работает из коробки» с библиотекой dotenv), 4) предлагает утилиту командной строки для выполнения операций общего назначения (init, list, write, validate и т.д.). Устанавливается библиотека с помощью менеджера пакетов pip pip install dynaconf .
    : это, строго говоря, не просто библиотека, а полноценная платформа, предназначенная для решения широкого круга задач, связанных с конфигурацией сложных приложений. Установить hydra можно либо с помощью менеджера пакетов pip pip install hydra-core –upgrade , либо с помощью менеджера пакетов conda conda install -c conda-forge hydra-core .

В основном, выбор библиотеки определяется следующими аспектами:

  • сложностью задачи и ее особенностями. К примеру, конфигурация маршрута подготовки моделей машинного обучения для развертывания на облачной платформе может быть выполнена и с помощью PyYAML/toml, а вот управление сложным web-проектом скорее всего потребует продвинутых возможностей dynaconf или hydra;
  • требованиями к показателю переиспользования кода. С этой точки зрения преимущества на стороне библиотеки hydra;
  • гибкостью решения и одновременно простотой сопровождения кода. Здесь чаще используются библиотеки PyYAML и toml.

Несколько слов о синтаксисе YAML

Синтаксис YAML прост и лаконичен, но есть несколько особенностей. Практически каждый YAML-файл строится на базе списка. Каждый элемент списка это список пар «ключ-значение», который обычно называют словарем. То есть представление модели объекта с помощью YAML сводится к тому, чтобы описать эту модель в терминах списков и словарей.

Иногда YAML-файлы начинаются с тройного тире — и заканчиваются троеточием . , а иногда эти последовательности символов в начале и в конце файла опускают. PyYAML корректно работает в обоих случаях.

Все элементы списка располагаются на одном и том же уровне и начинаются с – (тире и пробел):

Словари YAML представляют собой, как в и Python, наборы пар «ключ-значение» (за двоеточием должен следовать пробел):

YAML поддерживает стандартные типы данных: целочисленный (int), вещественный (float), булев (boolean), строковый (string) и null:

При необходимости можно явно указывать тип данных значения с помощью конструкции !![тип данных] , например:

  • pi: !!float 3.14159 ,
  • flag: !!bool false и т.д.

Булевы значения, могут иметь разные варианты записи, но рекомендуется использовать true и false , чтобы YAML-файл был совместим с настройками по умолчанию большинства YAML-линтеров:

В YAML необязательно заключать строковые константы в кавычки, но в ситуациях, когда требуется явно подчеркнуть строковую природу значения, кавычки не помешают. Допускается использовать как одинарные, так и двойные кавычки. Единственное отличие заключается в том, что двойные кавычки разрешают использовать управляющие коды строковых констант – их еще называют экранированными последовательностями – \t (символ горизонтальной табуляции), \r (символ возврата каретки), \n (символ перехода на новую строку) и т.д.

Для работы с длинными строками используют символы | и >

Бывает, что отдельные фрагменты YAML-файла требуется повторить несколько раз. Для решения такого рода задач используются якоря & и псевдонимы * . Пример:

Псевдонимы можно задавать и для блоков:

С помощью ключа слияния <<: можно «наследовать» и переопределять разделы:

Для редактирования YAML-файлов подойдет любой текстовый редактор, но важен следующий момент. Не всегда есть возможность использовать современный редактор с красивым графическим интерфейсом пользователя, а вот текстовый редактор Vim предустановлен на большинстве UNIX-подобных операционных систем.

Проверка типов управляющих параметров конфигурационного файла

В простых сценариях использования, когда можно ограничиться контролем типа данных на уровне конфигурационного файла, будет достаточно использовать явное указание типа с помощью !![тип данных] .

В случае сложной структуры модели объекта и в случае сценария, когда приложение должно уметь работать не только с конфигурационными файлами, подготовленными самим разработчиком, но и со сторонними конфигурационными файлами, с точки зрения устойчивости имеет смысл перенести проверку типов на уровень логики приложения.

Проверку типов можно организовать с помощью стандартной библиотеки Python dataclasses и сторонней библиотеки marshmallow_dataclass .

Рассмотрим простой пример использования marshmallow_dataclass, заимствованный со страницы проекта:

Здесь класс Building содержит всего два атрибута: вещественный height и строковый name . С помощью метода marshmallow.validate выполняется проверка на минимальное значение атрибута height , а с помощью field атрибут name получает значение по умолчанию.

Класс City содержит опциональный атрибут name , который ожидает получить либо объект строкового типа, либо None . Атрибут buildings ожидает принять список объектов типа Building .

Далее создаем экземпляр схемы city_schema на базе объекта класса City , а затем загружаем словарь. Схема ожидает получить строку для атрибута name и список объектов Building , то есть список объектов, которые описываются строковым атрибутом name и вещественными атрибутом height .

Используя точечную нотацию и нотацию списков можно добраться до значений атрибутов.

Шаблон применения конфигурационных файлов как инструмента управления Python-приложениями

Разобравшись с основными понятиями и концепциями, перейдем к рассмотрению связки «конфигурационный файл + Python-приложение».

В самом простом случае связка состоит из одного конфигурационного файла и одного модуля Python. В общем случае для управления Python-приложением может использоваться несколько файлов конфигурации (различных форматов), а базовая логика приложения опирается на наборов модулей и пакетов.

Сильная сторона такого представления задачи заключается в возможности явным образом декомпозировать приложение на:

  • изменяющийся блок управляющих параметров, или другими словами на динамическую часть инфраструктуры кода, с которой разработчик будет взаимодействовать в будущем,
  • неизменяющийся блок базовой логики – статичную часть кода, которая не требует внесения изменений в программный код напрямую.

Упрощенно, шаблон использования конфигурационных файлов как текстового интерфейса к Python-приложению выглядит так: управляющие параметры выносятся в конфигурационный файл, а базовая логика описывается в Python-модуле.

Этот шаблон распространяется на приложения произвольной сложности, но у него есть естественные ограничения.

Если конфигурационный файл разрастается до размеров исключающих быстрый поиск нужных управляющих параметров или затрудняет реализацию сложной логики управления, то стоит задуматься о более эффективных приемах организации работы.

В этом вопросе могут помочь:

    : библиотека для разработки настольных приложений с графическим интерфейсом пользователя; : мощная библиотека для прототипирования браузерных решений с графическим интерфейсом.

Пример связки «конфигурационный YAML-файл + Python-приложение»

В качестве примера возьмем задачу обнаружения выбросов в дискретной реализации стационарного гауссовского случайного процесса с различными типами корреляционных функций и ограничимся рассмотрением следующих их типов: экспоненциального, экспоненциально-косинусного, экспоненциально-косинусно-синусного (с плюсом) и экспоненциально-косинусно-синусного (с минусом).

Для простоты гипотеза о выбросах проверяется с помощью классической стандартизованной Z-оценки и модифицированной робастной Z-оценки (англ.) на медиане. Для моделирования псевдослучайных процессов с гауссовским распределением ординат и корреляционными функциями заданного типа использовались алгоритмы, заимствованные из книги Быкова, В.В. «Цифровое моделирование в статистической радиотехнике» .

Полный код примера с пояснениями и деталями реализации доступен в github-репозитории .

Вот его структура:

В каталоге configs располагается шаблон конфигурационного файла, который можно использовать в качестве отправной точки для разработки своих собственных более сложных конфигураций приложения.

В figure_examples хранятся демонстрационные примеры работы сценария python_scripts/main.py , а прямые результаты его работы в – каталоге figures .

Управлять типом корреляционной функции процесса можно с помощью параметра kind_acf . Смысл остальных управляющих параметров ( w_star , w0 , alpha и т.д.) должен быть понятен из комментариев.

Для запуска приложения следует перейти в корневой каталог проекта и выполнить:

Функция cmd_line_parser из модуля helper_funcs_and_class_schema.py прочитает значения флагов ( –config-path , –output-fig-path ), а затем вернет путь до конфигурационного файла и путь до файла с результатами анализа псевдослучайного процесса.

Затем, функция read_yaml_file прочитает конфигурационный файл и проверит типы управляющих параметров. Если типы управляющих параметров корректны и ошибок нет, то функция вернет объект типа Params , к атрибутам которого можно будет обратиться с помощью точечной нотации. Остается только построить сводку с использованием функции draw_graph .

К примеру, изменить значение управляющего параметра, даже не открывая конфигурационный файл, а затем создать на базе этого конфигурационного файла новый можно так

Здесь sed ищет строку « w0: !!float 3.0 », заменяет ее строкой « w0: !!float 3.15 » и записывает результат ( > ) в новый конфигурационный файл.

На рисунках ниже приведены результаты работы Python-приложения с различными комбинациями управляющих параметров.

<i>Сводка по анализу выбросов стационарного гауссовского псевдослучайного процесса с корреляционной функцией экспоненциального типа</i>Сводка по анализу выбросов стационарного гауссовского псевдослучайного процесса с корреляционной функцией экспоненциального типа <i>Сводка по анализу выбросов стационарного гауссовского псевдослучайного процесса с корреляционной функцией экспоненциально-косинусного типа</i>Сводка по анализу выбросов стационарного гауссовского псевдослучайного процесса с корреляционной функцией экспоненциально-косинусного типа <i>Сводка по анализу выбросов стационарного гауссовского псевдослучайного процесса с корреляционной функцией экспоненциально-косинусно-синусного типа (минус)</i>Сводка по анализу выбросов стационарного гауссовского псевдослучайного процесса с корреляционной функцией экспоненциально-косинусно-синусного типа (минус) <i>Сводка по анализу выбросов стационарного гауссовского псевдослучайного процесса с корреляционной функцией экспоненциально-косинусно-синусного типа (плюс)</i>Сводка по анализу выбросов стационарного гауссовского псевдослучайного процесса с корреляционной функцией экспоненциально-косинусно-синусного типа (плюс)

Вы пропустили

AEGIS Algorithms Android Angular Apache Airflow Apache Druid Apache Flink Apache Spark API API Canvas AppSec Architecture Artificial Intelligence Astro Authentication Authorization AutoGPT AWS AWS Aurora AWS Boto3 AWS EC2 AWS Lambda Azure Babylon.js Backend bash Beautiful Soup Bento UI Big Data Binary Tree Browser API Bun Career Cassandra Charts ChatGPT Chrome Extension Clean Code CLI ClickHouse Coding Codux Combine Compose Computer Context Fusion Copilot Cosmo Route CProgramming cron Cryptography CSS CTF Cypress DALL-E Data Analysis Data science Database dbt dbt Cloud deno Design Design Patterns Detekt Development Distributed Systems Django Docker Docker Hub Drizzle DRY DuckDB Express FastAPI Flask Flutter For Beginners Front End Development Game Development GCN GCP Geospatial Git GitHub Actions GitHub Pages Gitlab GMS GoFr Golang Google Google Sheets Google Wire GPT-3 GPT3 Gradio Gradle Grafana Graphic Design GraphQL gRPC Guidance HMS Hotwire HTML Huawei HuggingFace IndexedDB InfoSec Interview iOS Jackknife Java JavaScript Jetpack Compose JSON Kafka Kotlin Kubernetes LangChain Laravel Linux LlaMA LLM localStorage Logging Machine Learning Magento Math Mermaid Micro Frontends Mobile Mobile App Development mondayDB MongoDB Mongoose MySQL Naming NestJS NET NetMock Networks NextJS NLP Node.js Nodejs NoSQL NPM OOP OpenAI OTP Pandas PDF PHP Playwright Plotly Polars PostgreSQL Prefect Productivity Programming Prometheus Puppeteer Pushover Python Pytorch Quarkus Rabbitmq RAG Ramda Raspberry Pi React React Native Reactor Redis REST API Revolut Riverpod RProgramming Ruby Ruby on Rails Rust Scalene SCDB ScyllaDB Selenium Servers Sklearn SLO SnowFlake Snowkase Software Architecture Software Development Solara Solid Spring Boot SQL SQLite Streamlit SudoLang Supabase Swift SwiftUI Tailwind CSS Taipy Terraform Testing Transformers TURN TypeScript Ubuntu UI Design Unix UX UX Design Vim Vite VSCode Vue Web Architecture Web Components Web Development Web Frameworks Web Scraping Web-разработка Webassembly Websocket Whisper Widgets WordPress YAML YouTube Zed Наука о данных Разное Тренды

Современный подход к разработке с использованием Next.js