Python config file path

Конфигурационные файлы в Python

Конфиги. Все хранят их по разному. Кто-то в .yaml , кто-то в .ini , а кто-то вообще в исходном коде, подумав, что «Путь Django» с его settings.py действительно хорош.

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

Попытка №1

А что насчёт того чтобы хранить конфигурацию в коде? Ну, а что, вроде удобно, да и новых языков не придётся изучать. Существует множество проектов, в которых данный способ используется, и хочу сказать, вполне успешно.

Типичный конфиг в этом стиле выглядит так:

# settings.py TWITTER_USERNAME="johndoe" TWITTER_PASSWORD="johndoespassword" TWITTER_TOKEN=". "

Выглядит неплохо. Только одно настораживает, почему секьюрные данные хранятся в коде? Как мы это коммитить будем? Загадка. Разве что вносить наш файл в .gitignore , но это, конечно, вообще не решение.

Да и вообще, почему хоть какие-то данные хранятся в коде? Как мне кажется код, он на то и код, что должен выполнять какую-то логику, а не хранить данные.

Данный подход, на самом деле используется много где. В том же Django. Все думают, что раз это самый популярный фреймворк, который используется в самом Инстаграме, то они то уж плохое советовать не будут. Жаль, что это не так.

Попытка №2

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

Но мы начнём с того, что нам предлагает сам Python — .ini . В стандартной библиотеке имеется библиотека configparser .

Наш конфиг, который мы уже писали ранее:

# settings.ini [Twitter] username="johndoe" password="johndoespassword" token=". "

А теперь прочитаем в Python:

import configparser # импортируем библиотеку config = configparser.ConfigParser() # создаём объекта парсера config.read("settings.ini") # читаем конфиг print(config["Twitter"]["username"]) # обращаемся как к обычному словарю! # 'johndoe'

Все проблемы решены. Данные хранятся не в коде, доступ прост. Но… а если нам нужно читать другие конфиги, ну там json или yaml например, или все сразу. Конечно, есть json в стандартной библиотеке и pyyaml , но придётся написать кучу (ну, или не совсем) кода для этого.

Попытка №3

А сейчас, я хотел бы показать Вам свою библиотеку, которая призвана решить все эти проблемы (ну, или хотя бы уменьшить ваши страдания :)).

Называется она betterconf и доступна на PyPi.

Установка так же проста, как и любой другой библиотеки:

Изначально, наш конфиг представлен в виде класса с полями:

# settings.py from betterconf import Config, field class TwitterConfig(Config): # объявляем класс, который наследуется от `Config` username = field("TWITTER_USERNAME", default="johndoe") # объявляем поле `username`, если оно не найдено, выставляем стандартное password = field("TWITTER_PASSWORD", default="johndoespassword") # аналогично token = field("TWITTER_TOKEN", default=lambda: raise RuntimeError("Account's token must be defined!") # делаем тоже самое, но при отсутствии токенавозбуждаем ошибку cfg = TwitterConfig() print(cfg.username) # 'johndoe'

По умолчанию, библиотека пытается взять значения из переменных окружения, но мы также можем настроить и это:

from betterconf import Config, field from betterconf.config import AbstractProvider import json class JSONProvider(AbstractProvider): # наследуемся от абстрактного класса SETTINGS_JSON_FILE = "settings.json" # путь до файла с настройками def __init__(self): with open(self.SETTINGS_JSON_FILE, "r") as f: self._settings = json.load(f) # открываем и читаем def get(self, name): return self._settings.get(name) # если значение есть - возвращаем его, иначе - None. Библиотека будет выбрасывать свою исключением, если получит None. provider = JSONProvider() class TwitterConfig(Config): username = field("twitter_username", provider=provider) # используем наш способ получения данных # . cfg = TwitterConfig() # . 

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

Хорошо, а что если у нас в конфигах есть булевые значения, или числа, они же в итоге будут все равно приходить в строках. И для этого есть решение:

from betterconf import Config, field # из коробки доступно всего 2 кастера from betterconf.caster import to_bool, to_int class TwitterConfig(Config): # . post_tweets = field("TWITTER_POST_TWEETS", caster=to_bool) # . 

Таким образом, все похожие на булевые типы значения (а именно true и false будут преобразованы в питоновский bool . Регистр не учитывается.

Свой кастер написать также легко:

from betterconf.caster import AbstractCaster class DashToDotCaster(AbstractCaster): def cast(self, val): return val.replace("-", ".") # заменяет тире на точки to_dot = DashToDotCaster() # . 

Итоги

Таким образом, мы пришли к выводу, что хранить настройки в исходных кодах — не есть хорошо. Для этого уже придуманы различные форматы. Ну, а вы познакомились с ещё одной полезной (как я считаю :)) библиотекой.

P.S

Да, также можно было включить и Pydantic , но я считаю, что он слишком НЕлегковесный для таких задач.

Источник

Читайте также:  Perl python ruby lua
Оцените статью