python-dotenv - Documentation

Introduction

What is python-dotenv?

python-dotenv is a Python library that allows you to access environment variables from a .env file. This file, typically located in the root directory of your project, contains key-value pairs representing your environment variables. Using python-dotenv helps you manage sensitive information like API keys and database credentials securely, outside your version control system.

Why use python-dotenv?

Using python-dotenv offers several advantages:

Installation

The simplest way to install python-dotenv is using pip:

pip install python-dotenv

Basic Usage Example

First, create a .env file in your project’s root directory. Populate it with your environment variables:

DATABASE_URL=postgres://user:password@host:port/database
API_KEY=your_api_key_here
DEBUG=True

Then, in your Python code, load the .env file and access the variables:

import os
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

# Access environment variables
database_url = os.getenv("DATABASE_URL")
api_key = os.getenv("API_KEY")
debug_mode = os.getenv("DEBUG", False) # Provide a default value if variable is not found

print(f"Database URL: {database_url}")
print(f"API Key: {api_key}")
print(f"Debug Mode: {debug_mode}") 

Remember to never commit your .env file to version control (e.g., Git). Add it to your .gitignore file.

Core Functionality

Loading .env files

The primary function of python-dotenv is loading environment variables from a .env file. The load_dotenv() function handles this:

from dotenv import load_dotenv
load_dotenv() # Loads from .env in the current directory by default

This loads variables from .env in the current working directory. For more control, see “Using with different file paths” below. The function returns None and modifies the environment directly. It’s generally sufficient to call load_dotenv() only once at the beginning of your application.

Accessing environment variables

Once loaded, environment variables are accessed using the standard os.getenv() function:

import os
value = os.getenv("MY_VARIABLE")
print(value)

If the variable “MY_VARIABLE” is not found, os.getenv() will return None.

Overriding environment variables

Environment variables set in your system’s environment will take precedence over those in the .env file. System environment variables are loaded first, so if a variable exists in both locations, the system value will be used.

Handling errors

python-dotenv provides robust error handling. If the .env file is not found or has parsing errors (e.g., malformed syntax), it will typically raise an IOError or a ValueError. It’s good practice to wrap load_dotenv() in a try...except block:

from dotenv import load_dotenv, load_dotenv_override
try:
    load_dotenv()
except IOError:
    print("Could not load .env file")

Setting default values

You can provide default values when accessing environment variables using the second argument of os.getenv():

database_url = os.getenv("DATABASE_URL", "sqlite:///mydatabase.db") # Uses sqlite if DATABASE_URL isn't found.

This prevents your application from crashing if an expected environment variable is missing.

Using with different file paths

To load from a .env file in a different location, specify the dotenv_path argument:

from dotenv import load_dotenv
load_dotenv(dotenv_path="/path/to/.env") # Path is relative to your script's location unless absolute

You can also pass a list of paths; load_dotenv will use the first .env it finds:

from dotenv import load_dotenv
load_dotenv(dotenv_path=['./.env', '/etc/.env', '/opt/.env'])

Working with different file formats (e.g., .ini)

python-dotenv is primarily designed for .env files (key-value pairs). For other formats like .ini, you should consider using dedicated configuration libraries like configparser. python-dotenv does not directly support these alternative formats.

Advanced Usage

Using dotenv with frameworks (e.g., Flask, Django)

Integrating python-dotenv with web frameworks like Flask and Django is straightforward. Typically, you’ll load the .env file early in your application’s initialization:

Flask:

from flask import Flask
from dotenv import load_dotenv
import os

app = Flask(__name__)
load_dotenv()

DATABASE_URL = os.getenv("DATABASE_URL")
# ... rest of your Flask app ...

Django:

You would typically load the .env file in your settings.py before other settings are loaded:

import os
from dotenv import load_dotenv

# ... other Django imports ...

# Load environment variables before other settings
load_dotenv()

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': os.getenv('DB_NAME'),
        'USER': os.getenv('DB_USER'),
        'PASSWORD': os.getenv('DB_PASSWORD'),
        'HOST': os.getenv('DB_HOST'),
        'PORT': os.getenv('DB_PORT'),
    }
}

# ... rest of your Django settings ...

Remember to add .env to your .gitignore file in both cases.

Programmatic loading and manipulation of .env

While load_dotenv() is convenient, you can also programmatically load and manipulate .env data:

from dotenv import dotenv_values

# Load values from .env file into a dictionary
env_vars = dotenv_values()
print(env_vars) # Access as a dictionary

#Modify dictionary
env_vars['NEW_VAR'] = 'new_value'

#Write back to .env file (careful with sensitive data)
with open('.env', 'w') as f:
    for key, value in env_vars.items():
        f.write(f"{key}={value}\n")

Caution: Writing back to .env directly is generally discouraged for production environments due to potential security risks. It’s recommended for development or specific testing purposes only.

Creating custom loaders and parsers

For highly specialized needs, you can create custom loaders and parsers. This involves using the lower-level functions within python-dotenv to handle files differently or to parse files with a non-standard format:

from dotenv import parse_dotenv, set_key

# Parse your custom file
data = parse_dotenv(path='/path/to/my/custom_file.txt')


# modify the parsed data

set_key(data, 'MY_CUSTOM_KEY', 'MY_CUSTOM_VALUE')

# load modified data into environment
os.environ.update(data)

Security considerations

API Reference

load_dotenv() function

Loads environment variables from a .env file into os.environ.

Signature:

load_dotenv(dotenv_path=None, stream=None, verbose=False, override=False, encoding='utf-8')

Parameters:

Returns:

None. Modifies os.environ directly.

Raises:

find_dotenv() function

Finds the first .env file in the search path. Useful for determining the path to your .env file before loading it.

Signature:

find_dotenv(filename=None, raise_error_if_not_found=True, usecwd=True)

Parameters:

Returns:

(str) The path to the found .env file.

Raises:

set_key() function

Sets a key-value pair in a dictionary.

Signature:

set_key(d, key, value)

Parameters:

Returns:

None. Modifies the dictionary in place.

unset_key() function

Removes a key from a dictionary.

Signature:

unset_key(d, key)

Parameters:

Returns:

None. Modifies the dictionary in place.

get_key() function

Gets a value from a dictionary, handling missing keys.

Signature:

get_key(d, key, default=None)

Parameters:

Returns:

The value associated with the key, or the default value.

get() function

Gets a value from the environment variables or a dictionary, handling missing keys.

Signature:

get(d, key, default=None)

Parameters:

Returns:

The value associated with the key, or the default value.

dotenv.main module

This module contains a command-line interface for python-dotenv. It’s used primarily for development and debugging purposes. It’s not typically used directly within applications. The main function can be used for parsing .env files, showing content and allowing the manipulation of its content.

Contributing

We welcome contributions to python-dotenv! Whether it’s bug fixes, new features, or improved documentation, your help is appreciated.

Setting up the development environment

  1. Clone the repository:

    git clone https://github.com/theskumar/python-dotenv.git
    cd python-dotenv
  2. Create a virtual environment (recommended):

    python3 -m venv .venv
    source .venv/bin/activate  # On Windows: .venv\Scripts\activate
  3. Install dependencies:

    pip install -r requirements-dev.txt
  4. Install the package in editable mode:

    pip install -e .

Running tests

We use pytest for testing. To run the tests:

pytest

You can also run specific tests or test files as needed. Refer to the pytest documentation for more advanced usage.

Coding style guide

We follow PEP 8 as closely as possible. Consistent formatting is important for readability and maintainability. The flake8 linter should be used to check your code before submitting a pull request:

flake8

Submitting pull requests

  1. Fork the repository on GitHub.
  2. Create a new branch for your feature or bug fix. Use descriptive branch names (e.g., fix/bug-in-load_dotenv, feature/support-ini-files).
  3. Make your changes, committing them with clear and concise commit messages.
  4. Run the tests to ensure everything works correctly (pytest). Address any failures before proceeding.
  5. Run the linter to ensure code style compliance (flake8).
  6. Push your branch to your forked repository.
  7. Create a pull request on the main python-dotenv repository. Include a clear description of your changes and address any comments from reviewers.

Remember to keep your pull requests focused on a single change. Small, well-tested pull requests are easier to review and merge.

Troubleshooting

Common errors and solutions

Debugging tips

FAQ