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

МЕНЮ


Искусственный интеллект
Поиск
Регистрация на сайте
Помощь проекту

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


2020-01-26 07:15

разработка по

Конфиги. Все хранят их по разному. Кто-то в .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.

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

pip install betterconf

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

# 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()  # ...

Репозиторий на Github с более подробной документацией.

Итоги

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

P.S

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


Источник: habr.com

Комментарии: