How to Safely Add Environment Variables to your Python App

3 min read.

Picture this. You have some awesome code that you want to share with the world. However, you don’t want to or can’t share keys, credentials, and other sensitive information. Sure we can add these items to our bash_profile, but what if we accidentally use a set of keys for the wrong project? This is where the dotenv family saves the day. The dotenv module loads credentials from a .env file, allowing you to customize the settings for each project individually. For this sample console project, we’ll be using python-dotenv. There are also versions for Node and Ruby.

What we’re Building

This commit has the starter code to create an app that logs the weather to the console. The code is 99% done, and the only thing that’s left is to add the necessary API key to get it running. We’ll be using Dark Sky to retrieve weather data. So grab a key and head on over to your favorite text editor.

Let’s Code!

As you can see in the starter files, the api key is listed in plain site.

import requests

DARK_SKY_API_KEY = "my-super-secret-api-key"

def get_weather(latitude, longitude):
    url = "https://api.darksky.net/forecast/{}/{},{}".format(DARK_SKY_API_KEY, latitude, longitude)

    response = requests.get(url)
    data = response.json()

    current_weather = data['currently']
    summary = current_weather['summary'].lower()
    temperature = current_weather['temperature']

    message = "Currently, it's {} degrees outside. There's also {}".format(temperature, summary)
    print(message)


if __name__ == "__main__":
    get_weather(latitude = 37.8267, longitude = -122.4233)

Funny gif

The first step is to launch the virtual environment, install current dependencies, and install the python-dotenv package.

virtualenv env
source env/bin/activate
pip install -r requirements.txt
pip install python-dotenv
pip freeze > requirements.txt

Now that the package is installed, let’s create a .env file with the following text.

DARK_SKY_API_KEY=ENTER-YOUR-DARKSKY-API-KEY-HERE

Any variables set in the .env file will be accessible in the app via os. Note that variables set here will override any variables set in your bash_profile. Meaning, if I have one value for my api key in my bash_profile and another set here for the same variable in the .env file, the value in the .env file will be used in the app.

Now that the .env file is set up, I can update the weather_app.py file to look like the following

from dotenv import load_dotenv
import os
import requests

load_dotenv()

DARK_SKY_API_KEY = os.environ['DARK_SKY_API_KEY']

def get_weather(latitude, longitude):
    url = "https://api.darksky.net/forecast/{}/{},{}".format(DARK_SKY_API_KEY, latitude, longitude)

    response = requests.get(url)
    data = response.json()

    current_weather = data['currently']
    summary = current_weather['summary'].lower()
    temperature = current_weather['temperature']

    message = "Currently, it's {} degrees outside. There's also {}.".format(temperature, summary)
    print(message)


if __name__ == "__main__":
    get_weather(latitude = 37.8267, longitude = -122.4233)

Make sure you add your .env file to the project’s .gitignore file so that it doesn’t get tracked in git history and sent to Github/ Bitbucket/ anywhere else.

That’s it! Running python weather_app.py shows that the app works! You can share your code with anyone without having to worry about them using your credentials. Here’s what’s logged out to my console.

Currently, it's 56.7 degrees outside. There's also overcast.

Link to commit

Cleanup

Although the app works perfectly fine, I like to move all of my environment variable imports to a config.py file. This way, if multiple files need to reference the same value, I don’t have to use dotenv across my entire app.

Move the following code to a config.py file.

from dotenv import load_dotenv
import os

load_dotenv()

DARK_SKY_API_KEY = os.environ['DARK_SKY_API_KEY']

Now, the top of the weather_app.py file only needs to contain the following lines of code:

import requests

from config import DARK_SKY_API_KEY

def get_weather(latitude, longitude):
...

The app runs the same, but now it’s a little cleaner and easier to expand on in the future.

Link to repo