Предисловие
Это мой первый пост на Хабре и мой первый проект для PyPI. Заранее прошу прощения, если где-то допустил оплошность.
Приятного чтения!
Цель проекта
На работе я часто сталкивался с переменными окружения. С одной стороны они удобные и безопасные и к ним вопросов нет. Но с другой стороны, работа с ними в коде мне показалась сложной в плане самой записи.
Чтобы мне было легче объяснить, рассмотрим пример:
import os, dotenv
dotenv.load_dotenv()
projectid = int(os.environ.get("PROJECT_ID"))
switch = bool(os.environ.get("SWITCH"))
sql_user = os.environ.get('SQL_USER')
sql_pass = os.environ.get('SQL_PASSWORD')
Здесь указаны несколько переменных, которые потом будут указаны в проекте.
Но дело в том, что если переменных потребуется больше, то самих таких строк будет больше. И тем самым читать такой код можно, но потребуется время и внимательность.
Также сталкивался с проблемой своей невнимательности по поводу перевода в другой тип данных, ведь с переменными окружения Python работает сразу как со строкой, что не всегда удобно и понятно.
Моей целью было упрощение чтения своего кода и увеличение скорости разработки, а также для простого душевного спокойствия насчёт чтение этого кода. И поэтому меня заинтересовало написать данную библиотеку.
Реализация
Весь концепт реализации я взял из библиотеки Pydantic, которая меня в своё время заинтересовала и стала моим помощником в работе. Поэтому я начал строить у себя в голове и в редакторе красивую схему.
Как она выглядела(Ниже псевокод):
class МойКласс(Библиотека):
__подключаемфайл__ = 'файл'
Переменная1:тип
Переменная2:тип
Остановившись на этом, принялся к реализации проекта. Но через некоторое время понял, что также нужен экземпляр класса, ведь это улучшит работу с данными классами и реализация будет быстрее.
Готовый проект
После выполнения получилось следующее:
# .env file
String = Hello, world!
Integer = 23
Float = 45.0
Boolean = True
from envserv import EnvBase
class MyEnv(EnvBase):
__envfile__ = '.env' # Путь к файлу
String:str
Integer:int
Float:float
Boolean:bool
env = MyEnv() # Создаём экземпляр класса
print(env)
print(env.String, env.Integer, env.Float, env.Boolean)
print(type(env.String), type(env.Integer), type(env.Float), type(env.Boolean))
# Вывод:
# EnvServ(String:<class 'str'> = Hello, world!, Integer:<class 'int'> = 23, Float:<class 'float'> = 45.0, Boolean:<class 'bool'> = True)
# Hello, world! 23 45.0 True
# <class 'str'> <class 'int'> <class 'float'> <class 'bool'>
Восхитившись своим проектом, уже был готов его оставить, ведь не видел для него нововведения. Но, показав готовый проект, коллеги попросили внести в него некоторые изменения, для ещё более удобной работы - алиасы и изменения переменных.
Первоначально алиасы были нужны только для замены имён переменных, ведь в Python запрещено использовать ключевые слова для записи их как переменные (pass, def, async и т.п.). Но в дальнейшем осознал, что их можно использовать куда интереснее. Поэтому эта реализация была включена в проект.
Изменение переменных составило чуть больше проблем с их реализацией, но именно эта часть является чуть ли не самой интересной.
Чаще всего переменные окружения являются статичными данными, и лишь в некоторых случаях их есть смысл менять на другие значения. Но после реализации изменения начал задумываться, что ведь что-то вообще нельзя менять для работы всего проекта. И при ошибке в этом понимании, можно сломать тот же продакшн.
Поэтому одновременно к этой реализации был добавлен запрет на изменение переменной.
Результат дополнительных реализаций:
# .env file
user = test
pass = passtesting
from envserv import EnvBase, variable
class MyEnv(EnvBase):
__envfile__ = '.env' # Путь к файлу
user:str = variable(overwrite=False) # Здесь запрещаем изменять переменную
password:str = variable(alias="pass") # Здесь указываем, что переменная окружения называется pass
env = MyEnv()
print(env) # Вывод: EnvServ(user:<class 'str'> = test, password:<class 'str'> = passtesting)
env.password = "newpass"
print(env.user, env.password) # Вывод: test newpass
env.user = 'newuser' # Исключение: envserv.errors.EnvVariableError: Error overwriting variable user: It cannot be overwritten
Итоги
Плюсы данной реализации:
Код легко читается
Тип переменной автоматически подставляется
Типом переменной может быть что угодно, включая пользовательский (Перевод осуществляется через: тип(значение))
Автоматическое подключение переменных (Если переменные не в файле, а к примеру в docker-compose, то переменную envfie указывать не надо)
Лёгкое присвоение новых значений
Назначение алиасов и запреты на изменения
Но как же без минусов:
Удалять переменные окружения в коде нельзя - будет нарушено целостность класса
Реализация последнего плюса через кэш (бета-версия)
Послесловие
Надеюсь данная библиотека будет помогать не только моим проектам, но и вашим.
Ссылка на библиотеку в PyPI: https://pypi.org/project/envserv/
Также оставляйте свои отзывы по данной статье и если вас она заинтересовала - можете предложить свои реализации, обязательно вместе их обдумаем :)
Всем приятной работы и поменьше багов!