How does python-dotenv simplify Configuration Management?
Tags: python, environmentvariables, dotenv, programming
Published: 2023-07-21 14:08:50

Navigating through the python projects, we can quickly realize that handling configurations can be like exploring mazes with numerous paths to choose from. In this article, we’ll embark on a thrilling journey and delve into the world of environment variables in Python, with particular focus on the realm of `.env` files.

First, let’s understand what configuration management is. I just finished a Netflix series, related to space travel, so picture this. You are a captain of your spaceship, commanding a crew of space travelers. Your spaceship is equipped with a range of cool gadgets and futuristic technologies, each with its own set of settings and values. The settings might include the ship’s speed, destination coordinates, communication frequencies, and more.

Now as you venture through the vastness of the cosmos, you encounter different planets, each with its unique challenges and environments. On some planets, you need to fly at a slower speed to avoid debris, while on others you might need to crank up the communication frequencies to establish contact with alien civilization. 

In spaceship jargon, these settings are called configuration variables. And here’s where `python-dotenv` comes into play - it’s like having a magical map that stores all the settings separately, making your life as captain so much easier!

With `python-dotenv`, you can create a special treasure chest called `.env`, where you write down all the configuration variables for each planet. For example,
```
SPEED=10
DESTINATION_X=234
DESTINATION_Y=567
COMMUNICATION_FREQ=200
```
Now, whenever you approach a new planet, you consult your treasure chest and quickly update your spaceship’s settings without tinkering with the spaceship’s control panel directly. It’s like casting the spell to magically adjust the ship’s speed and communication frequencies for each destination!

The best part? Since the `.env` treasure chest is hidden safely in your spaceship’s storage room, you can keep all your top-secret settings secure from prying eyes - even from other spaceship crew who might be curious about your awesome adventures.

So with `python-dotenv`, you become the master of configuration management, effortlessly adapting to different planets and situations, all while keeping your spaceship’s secret safe and sound. 🙆‍♂️🚀✨ 

Okay Enough! Let’s jump into some technical stuff, understanding how to use python-dotenv and behind the scenes… 

---
## 🤔 What is python-dotenv?
In [docs](https://pypi.org/project/python-dotenv/), python-dotenv defines itself as 
> Python-dotenv reads key-value pairs from a .env file and can set them as environment variables. It helps in the development of applications following the 12-factor principles.

Let’s understand this first, `key-value` pairs are a way of organizing data, where each piece of data is associated with a unique key. `12-factor principles`, are a set of best practices for building modern scalable, maintainable software applications. One of these principles involves storing configuration in the environment, which means using environment variables to manage configuration instead of hardcoding them into the code.

### 🏃‍♂️💨 Getting Started with python-dotenv
Install python-dotenv using 
```pip install python-dotenv```
Make sure you activate your virtual environment, if not activated.
This will add python-dotenv to your application, to make it load the configuration from the `.env` file when it is present (eg., in development).

To configure the development environment, add a `.env` in the root directory of your project:
```
.
├── .env
└── main.py
```
The syntax for writing in the '.env' file for python-dotenv is similar to Bash.

```
# Spaceship Config
DOMAIN=dev.to
CAPTAIN_EMAIL=captain@${DOMAIN}
DESINATION_PLANET=${DOMAIN}/priplanus
SPEED=10
DESTINATION_X=234
DESTINATION_Y=567
COMMUNICATION_FREQ=200
```
Also make sure to add `.env` file to `.gitignore`, especially if it contains secrets like passwords.
How to load/use these environment variables in `main.py` file:

```
main.py

import os
from dotenv import load_dotenv

load_dotenv() # takes environment variables from .env

SPEED = os.getenv(“SPEED”) # or use os.environ(“SPEED”)
```

This is how you would be able to use environment variables in a Python Project. Easy isn’t it? Now let’s discuss what happens when you call `load_dotenv()`.

## 🔌📥 load_dotenv() 

Disclaimer: Source of all the data I am to provide is from [GitHub of python-dotenv](https://github.com/theskumar/python-dotenv).

When you call `load_dotenv()` from the `dotenv` package, it can optionally accept an argument called `dotenv_path: Optional[StrPath]` or `stream: Optional[IO[str]]`, which allows you to specify the path to a custom `.env` file for loading environment variables. If you don't provide the `dotenv_path` or `stream`, the function uses `find_dotenv()` to search for the `.env` file in increasingly higher folders within the project. Once it finds the file, it loads the environment variables from it.

The `find_dotenv()` function returns the path to the `.env` file if it is found, or an empty string if it doesn't find the file.

Now, we have the file path, so `load_dotenv()` calls the class `DotEnv`, which does the main part of loading all the variables found as environment variables.

So, `DotEnv` has a method called `set_as_environment_variables`, which loads the current dotenv as a system variable. Here is how it is implemented.

Once `find_dotenv(`) locates the .env file, `load_dotenv()` proceeds to call the `DotEnv` class to perform the crucial task of loading all the variables found in the `.env` file as environment variables.

The `DotEnv` class contains a method named `set_as_environment_variables`, which handles the process of setting the values from the `.env` file as system-level environment variables. This means that after invoking this method, the variables defined in the `.env` file become accessible as environment variables throughout the entire Python application.

Here is how it is implemented:
```
01 def set_as_environment_variables(self) -> bool:
02         if not self.dict():
03             return False
04 
05         for k, v in self.dict().items():
06             if k in os.environ and not self.override:
07                 continue
08             if v is not None:
09                 os.environ[k] = v
10 
11         return True

```
Here, `dict()` is a method in the `DotEnv` class, which returns dotenv as a dictionary. After checking all the conditions, in line 09 of the above listing, the environment variables are set.

Here, `self.overside: bool`, is by default set to `True`. If in `load_dotenv`, you add an argument as `load_dotenv(override=False)`, then, the already present environment variable will not be updated.

And this is how python-dotenv works, behind the scenes, for setting the environment variables. There are more features of python-dotenv like:
1. Load configuration without altering the environment
2. Parse configuration as a stream
3. Load .env files in IPython

Go through the [docs](https://pypi.org/project/python-dotenv/) and the [GitHub](https://github.com/theskumar/python-dotenv), to see how these features work. 

---

## ⌚👋👋 Outro

And now, armed with the power of `python-dotenv`, you, the spaceship captain, navigated the cosmos with ease, effortlessly adjusting to the unique challenges of each planet. 

Happy coding, see you soon in the next article. 🚀✨🌌

Follow me on [GitHub](https://github.com/jiisanda), [LinkedIn](https://www.linkedin.com/in/jiisanda/), and Subscribe my [YouTube](https://www.youtube.com/@codeakey).