Strict separation of configuration from code.
decouple helps you organize your settings so that you can change parameters
without having to redeploy your app. It cleanly separates configuration values
(secrets, hostnames, feature flags, toggles) from your source code, so the same
codebase runs unchanged across local development, staging, and production —
only the environment changes.
This repository is a fork of HBNetwork/python-decouple. See Attribution below.
Hard-coding configuration in your source is a liability: secrets leak into
version control, environment-specific values force code edits, and every deploy
becomes risky. decouple follows the Twelve-Factor App
principle of storing config in the environment, while still giving you a
convenient fallback to local config files during development.
It lets you:
- Read settings from environment variables first, then fall back to a config file.
- Keep secrets out of source control (commit a
.env.example, not your.env). - Cast string values into the types your code actually needs (
int,bool, lists, etc.). - Provide sensible defaults so missing optional settings don't crash your app.
pip install python-decoupleTo install from this fork directly:
pip install git+https://github.com/tritathadore/decouple.gitdecouple supports both .env and .ini formats. The simplest option is a
.env file in your repository root:
# .env
DEBUG=True
SECRET_KEY=your-secret-key-here
DATABASE_URL=postgres://user:password@localhost:5432/mydb
EMAIL_PORT=587
ALLOWED_HOSTS=.localhost,.example.comAlternatively, a settings.ini:
# settings.ini
[settings]
DEBUG=True
SECRET_KEY=your-secret-key-here
DATABASE_URL=postgres://user:password@localhost:5432/mydb
EMAIL_PORT=587Tip: Add
.envto your.gitignoreand commit a.env.examplewith dummy values so collaborators know which variables to set.
from decouple import config
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int)The config object is a lazy, auto-detecting factory: it searches up your module
path for a settings.ini or .env file and picks the right reader automatically.
Everything returned from the environment or a config file is a string by
default. Use cast to coerce values into the type you need.
DEBUG = config('DEBUG', default=False, cast=bool)Recognized truthy strings: 1, true, yes, y, on, t.
Recognized falsy strings: 0, false, no, n, off, f, "".
EMAIL_PORT = config('EMAIL_PORT', default=587, cast=int)
TIMEOUT = config('TIMEOUT', default=30.0, cast=float)from decouple import config, Csv
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv())
# 'localhost,127.0.0.1' -> ['localhost', '127.0.0.1']
NUMBERS = config('NUMBERS', cast=Csv(int))
# '1,2,3' -> [1, 2, 3]cast accepts any callable, so you can pass your own function:
SECURE_PROXY_SSL_HEADER = config(
'SECURE_PROXY_SSL_HEADER',
default='HTTP_X_FORWARDED_PROTO,https',
cast=Csv(post_process=tuple),
)# Optional setting with a fallback
CACHE_TIMEOUT = config('CACHE_TIMEOUT', default=300, cast=int)
# Required setting — raises UndefinedValueError if missing and no default given
SECRET_KEY = config('SECRET_KEY')If a variable is not found in the environment or any config file, and no
default is provided, decouple raises an error early — failing fast instead
of silently running with bad config.
By default, config auto-detects the file. To point at a specific source:
from decouple import Config, RepositoryEnv
config = Config(RepositoryEnv('/path/to/.env'))from decouple import Config, RepositoryIni
config = Config(RepositoryIni('/path/to/settings.ini'))decouple resolves values in the following order, stopping at the first hit:
- Environment variables (
os.environ) - Repository — your
.envorsettings.inifile - The
defaultargument passed toconfig()
This Unix-style precedence means a value set in the actual environment always wins over the config file, which is what you want in production.
# settings.py
from decouple import config, Csv
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv())
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': config('DB_NAME'),
'USER': config('DB_USER'),
'PASSWORD': config('DB_PASSWORD'),
'HOST': config('DB_HOST', default='localhost'),
'PORT': config('DB_PORT', default=5432, cast=int),
}
}- Python 3.x
- No third-party runtime dependencies
Contributions are welcome. To set up a development environment:
git clone https://github.com/tritathadore/decouple.git
cd decouple
python -m venv venv
source venv/bin/activate
pip install -e .Please open an issue to discuss substantial changes before submitting a pull request.
This project is a fork of python-decouple by Henrique Bastos and contributors. All credit for the original design and implementation belongs to the upstream authors. This fork is maintained separately and any modifications are documented in the commit history.
python-decouple is released under the MIT License. This fork retains the
original license. See the LICENSE file for details.