В статье показывается взаимодействие с несколькими конфигами на базе TOML-разметки.
Технологии
Язык: Python 3.12 (на других версиях часть кода не будет работать)
Библиотеки:
- pydantic (для построения объектной модели, в рамках которой можно удобно валидировать данные)
- pydantic_settings (помогает извлекать данные из toml файлов)
- typing (для сокрытия чувствительных данных)
План работ
- Подготовка toml файлов
- Создаем простую сборку
- Рефакторинг программы
Подготовка toml файлов
Для начала создадим 2 файла с конфигами
файл: db_con_p.toml
[SQL_SERVER_DED]
ded_host = "MSK_DEDUCTOR01RM"
ded_name = "DEDUCTOR_P"
[SQL_SERVER_LOG]
lom_host = "MSK_LOGINOM02RM"
lom_name = "LOGINOM_P"
файл: web_con_p.toml
[CRE]
cre_host = "localhost"
cre_pass = "123"
cre_methods = "1,8,22"
[CRONOS]
crs_host = "localhost"
crs_pass = "567"
Теперь создадим питоновский файл
Предполагаю, что библиотеки pydantic и pydantic_settings уже установлены.
Пока пишем общую структуру классов
файл: app.py
Класс ConfigBase - это базовый конфиг для всех конфигов с расширением. Он наследуется от BaseSettings, а не от BaseModel, чтобы в рамках нашего конфига мы могли не только использовать объектную модель библиотеки pydantic, но производить дополнительные настройки для работы с TOML файлами.
Классы SQL_SERVER_DED и SQL_SERVER_LOG - это классы с данными из файлов. Пока в них нет данных, но мы скоро исправим. Они наследуются от ConfigBase, т.е. этим классам нужен будет объект settings_cls, который лежит в ConfigBase, а не в BaseSettings.
Класс Config - это финальный конфиг. В интернете можно встретить примеры, что данный класс наследуется от BaseSettings. В моем примере он наследуется от ConfigBase, чтобы также использовать settings_cls, который лежит в ConfigBase, а не в BaseSettings.
Создаем простую сборку
Укажем классу ConfigBase расположение файла TOML
В классах SQL_SERVER_DED и SQL_SERVER_LOG создадим поля с типизацией.
ВАЖНО:
- Названия полей должно совпадать с теми, что лежат в файле db_con_p.toml
- Название класса должно совпадать с названием структуры (в файле db_con_p.toml), в которую вложены поля.
- В классе нужно указать все поля из структуры.
- Необходимо создать классы под все структуры из файла.
В классе Config создаем переменные
ВАЖНО:
- Наименование переменных должно совпадать с названием классов
- Переменным присваивается тип (типизация) конкретного класса
И так... если сейчас запустим класс Config, то ничего не заработает. Т.к. Config не умеет обрабатывать классы SQL_SERVER_DED и SQL_SERVER_LOG
Чтобы проблему решить, необходимо импортировать новый класс TomlConfigSettingsSource. И дополнительно импортируем для работы с типизацией Self и PydanticBaseSettingsSource
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic_settings import TomlConfigSettingsSource, PydanticBaseSettingsSource
from typing import Self
И обновить новую логику в классе Config
ВАЖНО:
- Были добавлены классовые методы load и settings_customise_sources
- Метод load можно назвать как угодно, хоть main
- Метод settings_customise_sources мы переопределяем у класса BaseSettings
- Метод load грузит наш конфиг в память программы из файла TOML
- Метод load возвращает тип Config, но т.к. Config не существует внутри Config, мы можем через библиотеку typing вернуть тип Self. Т.е. себя же вернет, а если точнее, экземпляр класса. Это не то же самое, что вернуть класс, т.е. что мы хотели, но в рамках этой реализации мы не обращаем внимание на этот нюанс.
- Метод settings_customise_sources мы переопределяем, чтобы наш класс Config научился понимать, как обрабатывать TOML файлы. Именно для этого метода мы наследуемся от ConfigBase, чтобы именно его settings_cls передавался в TomlConfigSettingsSource.
Можем запустить. Все работает.
Результат:
Полный код выглядит так:
Рефакторинг программы
В рамках рефакторинга:
- добавим обработку не одного, а двух файлов TOML
- красиво оформим логики в разные файлы и папки
Первым делом давайте файлы TOML уберем в папку connections
В файле app.py в классе ConfigBase заменим
SettingsConfigDict(toml_file="db_con_p.toml")
на
SettingsConfigDict(toml_file=("connections/db_con_p.toml","connections/web_con_p.toml"))
Сам класс перенесем в папку config\base в файл main.py
файл: main.py
рядом создадим файл: __init__.py
from .main import ConfigBase
ВАЖНО:
В main.py прописываем
if __name__ == "__main__":
pass
т.к. намекаем, что данный файл не запускается как самостоятельный
Теперь из файла app.py перенесем классы SQL_SERVER_DED и SQL_SERVER_LOG в папку config\db в файл main.py
файл: main.py
рядом создадим файл: __init__.py
from .main import SQL_SERVER_DED, SQL_SERVER_LOG
Теперь на примере классов SQL_SERVER_DED и SQL_SERVER_LOG напишем код для файла web_con_p.toml и расположим его в папке config\web в файле main.py.
Создадим классы CRE и CRONOS. В этих классах есть поля с паролями. Пароли необходимо скрыть для безопасности.
файл: main.py
рядом создадим файл: __init__.py
from .main import CRE, CRONOS
Теперь из файла app.py перенесем класс Config в папку config в файл main.py.
И добавим в код новые поля для классов CRE и CRONOS
файл: main.py
рядом создадим файл: __init__.py
from .main import Config
Финальное расположение файлов
Осталось поправить код в корневом файле app.py
На этом всё. Запускаем программу. Получаем результат:
Особенности:
- Класс Config не знает расположение TOML файлов, но умеет их обрабатывать
- Чувствительные данные скрыты от юзеров, которые будут использоваться класс Config
- Для каждого сервиса, БД и пр. можно по аналогии с примерами создавать свои TOML файлы с конфигурациями. Т.е. программа может работать с множеством конфигов
GitLab
Подписывайтесь на Дзен, а также приглашаю в мой телеграмм канал, там публикую другой, но не менее интересный контент.