My first comfortable bike

Objective of the project

At work, I often came across environment variables. On the one hand, they are convenient and safe and there are no questions about them. But on the other hand, working with them in code seemed difficult to me in terms of the recording itself.

To make it easier for me to explain, let’s look at an example:

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')

Here are several variables that will later be specified in the project.
But the fact is that if more variables are needed, then there will be more such lines themselves. And thus it is possible to read such code, but it will take time and care.

I also encountered the problem of my inattention regarding the translation to another data type, because with environment variables Python works immediately as with a string, which is not always convenient and understandable.

My goal was to make my code easier to read and to increase development speed, as well as for simple peace of mind about reading this code. And that’s why I was interested in writing this library.

Implementation

I took the entire implementation concept from the library Pydantic, which interested me at one time and became my assistant in my work. So I started building a beautiful diagram in my head and in the editor.

What she looked like (Below is the pseudocode):

class МойКласс(Библиотека):
  __подключаемфайл__ = 'файл'
  
  Переменная1:тип
  Переменная2:тип
  

Having settled on this, I began to implement the project. But after a while I realized that I also needed an instance of the class, because this would improve the work with these classes and the implementation would be faster.

Ready project

After execution the following happened:

# .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'>

Having admired my project, I was ready to leave it, because I didn’t see any innovation for it. But, having shown the finished project, my colleagues asked to make some changes to it, for even more convenient work – aliases And variable changes.

Initially aliases were needed only to replace variable names, because in Python It is prohibited to use keywords to write them as variables (pass, def, async, etc.). But later I realized that they could be used in much more interesting ways. Therefore this implementation was included in the project.

Changing Variables There were a little more problems with their implementation, but this part is almost the most interesting.
Most often, environment variables are static data, and only in some cases does it make sense to change them to other values. But after implementing the change, I began to think that something cannot be changed at all for the operation of the entire project. And if there is an error in this understanding, the same production can be broken.
Therefore, at the same time, a ban on changing the variable was added to this implementation.

Result of additional implementations:

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

Results

The advantages of this implementation:

  • The code is easy to read

  • The variable type is automatically substituted

  • The type of the variable can be anything, including user-defined (Translation is done via: type(value))

  • Automatic connection of variables (If the variables are not in a file, but for example in docker-compose, then the variable envfie no need to indicate)

  • Easy assignment of new values

  • Assigning aliases and prohibiting changes

But what if there are no downsides:

  • You cannot delete environment variables in code – the integrity of the class will be violated

  • Implementation of the last plus via cache (beta version)

Afterword

I hope this library will help not only my projects, but also yours.

Link to the library in PyPI: https://pypi.org/project/envserv/

Also leave your feedback on this article and if you are interested in it, you can suggest your implementations, we will definitely think about them together 🙂

Happy work everyone and fewer bugs!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *