158

Need an example and please explain me the purpose of python-dotenv.
I am kind of confused with the documentation.

ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
Dev Jalla
  • 1,910
  • 2
  • 13
  • 21

5 Answers5

250

From the Github page:

Reads the key,value pair from .env and adds them to environment variable. It is great of managing app settings during development and in production using 12-factor principles.

Assuming you have created the .env file along-side your settings module.

.
├── .env
└── settings.py

Add the following code to your settings.py:

# settings.py
import os
from os.path import join, dirname
from dotenv import load_dotenv

dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)

SECRET_KEY = os.environ.get("SECRET_KEY")
DATABASE_PASSWORD = os.environ.get("DATABASE_PASSWORD")

.env is a simple text file with each environment variable listed one per line, in the format of KEY="Value". The lines starting with # are ignored.

SOME_VAR=someval
# I am a comment and that is OK
FOO="BAR"
AnhellO
  • 855
  • 1
  • 14
  • 16
Will
  • 4,942
  • 2
  • 22
  • 47
  • 2
    Wish i found the same on github please elobarate – Dev Jalla Jan 10 '17 at 05:48
  • 39
    just want to add, to have to be careful if key/value pair looks like `GIRLFRIEND=None` as `print` shows `None` so in this case you have to check for `type` of the variable to distinguish. – daparic Mar 27 '18 at 15:59
  • 6
    Can anyone explain the difference / pros and cons of dotenv vs autoenv? – aherzfeld Nov 01 '18 at 08:40
  • you need to `import os` to get `os.environ.get("SECRET_KEY")` – carkod May 04 '19 at 14:17
  • Just wanna share. I am on Mac OS. Used `VAR1=var1`, `VAR2="var2"`, `VAR3=None` and all result as string. The last one prints out `None` as typelogic mention but it is string-typed `None`. – addicted Jun 29 '19 at 23:58
  • @user9074332 the `DATABASE_PASSWORD` is set in the .env file along with SOME_VAR and FOO. It could be DATABASE_PASSWORD="InsecurePassword" – Liquidgenius Dec 17 '19 at 15:16
  • Does doetenv know where the file is stored even if you move it afterwards? I moved my .env file to a different location after reading from it succesfully, and for some reason my Python script is able to read from it. – Tartaglia May 11 '20 at 18:32
  • is the recommendation to have as many env files as there are app environments, e.g. `.env-dev`, `.env-prod`, etc or just different 'sections' within the single file? Also, what if I want some of the config items to be version-controlled in git without the passwords? Is it recommended then to create separate `.env.password` files or something to that effect? – user9074332 Oct 28 '20 at 03:05
  • 3
    @user9074332 The `.env` file convention includes a leading `.` which makes it hidden on mac & linux systems. You would also add `.env` to your `.gitignore` file so it can't be committed to version control. If it is only local and gitignored then it is fine to have a password in it. It should only be called `.env` not many of them. If you want to use dotenv in CI/CD on different environments then something like https://stackoverflow.com/a/55581164/1335793 can work but depends on your deployment platform and process. – Davos Jan 20 '21 at 13:09
  • 13
    `in production using 12-factor principles` refers to the principle https://12factor.net/config of storing config in env vars. Storing them in a local gitignored `.env` file is just an implicit helper to export env vars, so you don't forget to export or to run a shell script. When deploying to prod you abandon the `.env` file. The `load_dotenv()` and the `os.environ.get` calls are independent; `dotenv` works for local development and you use some other mechanism such as your CI/CD system's env vars secrets manager in prod, either way the `os.environ.get` part always works. – Davos Jan 20 '21 at 13:37
  • @Tartaglia as it is specified in the documentation of the `load_dotenv` method, it first searches for a file in the current directory named .env and if not found moves to a parent directory so if you moved it to a parent directory it should still get detected. So, dotenv does not keep track of where the files are. – vinkomlacic Dec 18 '21 at 20:13
80

In addition to @Will's answer, the python-dotenv module comes with a find_dotenv() that will try to find the .env file.

# settings.py
import os
from dotenv import load_dotenv, find_dotenv

load_dotenv(find_dotenv())

SECRET_KEY = os.environ.get("SECRET_KEY")
DATABASE_PASSWORD = os.environ.get("DATABASE_PASSWORD")
cannin
  • 2,735
  • 2
  • 25
  • 32
16

You could set the env variables like this:

 export PRIVATE_KEY=0X32323

and then read it with os module.

import os

private_key=os.getenv("PRIVATE_KEY")

But this way, environment variable works only for the duration that shell is live. If you close the shell and restart it, you have to set environmental variable again. python-dotenv prevents us from doing this repetitive work.For this create .env file and add variables in this format

 PRIVATE_KEY=fb6b05d6e75a93e30e22334443379292ccd29f5d815ad93a86ee23e749227

then in the file u want to access anv variables

import os
from dotenv import load_dotenv 

#default directory for .env file is the current directory
#if you set .env in different directory, put the directory address load_dotenv("directory_of_.env)
load_dotenv()

load_dotenv() will set the environment variables from .env and we access with os module

   private_key=os.getenv("PRIVATE_KEY")
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
5

Just to add to @cannin, if you want to specify the which file you want to find:

from dotenv import find_dotenv
from dotenv import load_dotenv

env_file = find_dotenv(".env.dev")
load_dotenv(env_file)
yasbars
  • 171
  • 3
  • 7
3

If you're starting your app from a shell such as bash or zsh, then the point of .env management utilities like (npm) dotenv or python-dotenv becomes moot.

Here's an example of how to manage .env with bash that simply, directly, and safely addresses configuration as recommended by the 12-Factor App. It also requires no additional dependencies.

Given a project hosted under ~/projects/foobar/, create an environment file in a safe location outside your project's space (e.g. ~/.envs/foobar/dev). Its content may look something like this:

set -a

PROJECT=foobar
DB_NAME=foobar_dev
DB_PASSWORD=5ecret
CACHE_ENABLED=
DEBUG=yes
LOG=/tmp/foobar.log
...

set +a

Then create a symlink to that file from your project's space:

$ ln -s ~/.envs/foobar/dev ~/projects/foobar/.env

The project now has a .env file symlinking to the actual file. When you source the symlink, all variables between set -a and set +a are exported to the environment.

$ source ~/projects/foobar/.env

And voila! If you run python from the same shell instance you sourced the environment file, you can retrieve the latter and update your config with it:

import os
config.update(os.environ)

The point of making .env a symlink to ~/.envs/foobar/dev is an added precaution to listing it in .gititgnore. If for whatever reasons the file were to be checked into version control, its contents would just show that it's a link to another file.

Michael Ekoka
  • 19,050
  • 12
  • 78
  • 79
  • 1
    Interesting edge case: I was using Prefect to orchestrate python code, and the default upload behavior is to follow symlinks and upload the file (to local storage or S3 for example.) **Therefore, protecting a local file from a repo by symlinking can fail**, where using an `env` variable to referece a file location will be OK. – Merlin Jan 31 '23 at 03:01
  • 1
    @Merlin My answer presumes common knowledge that .env is a private, user specific file and thus should be included in .gitignore. No one wants to deal with teammates's .env in the repo, symlinks or not. That's why I say that making it a symlink is an *added precaution to listing it in .gitignore*. It's not a replacement. But people can forget. If I accidentally check it in, I'd prefer to have the added likelihood that it's checked in as a symlink, even if that's not an absolute certainty (with Prefect and such). – Michael Ekoka Jan 31 '23 at 14:18