Skip to main content

Advanced .env loader with imports, conditionals, typing, validation, and compilation to standard format

Project description

dotenv-fusion logo

dotenv-fusion

An advanced .env loader for Python with variable expansion, imports, conditions, typing and validation.

Compatible with other dotenv loaders: all advanced directives start with #@ and are seen as comments by standard tools.

Installation

The software is currently in preview mode; only the UVX version from GitLab is available at this time.

# run directly with uvx from Gitlab (no install needed)
uvx git+https://gitlab.com/pytgaen-group/dotenv-fusion

# Install from PyPI (when published)
pip install dotenv-fusion

# Or with uv
uv pip install dotenv-fusion

# Or run directly with uvx (no install needed)
uvx dotenv-fusion load

# Or install from source
pip install .

Usage

Shell integration (primary usage)

# Bash / Zsh
eval "$(dotenv-fusion load)"
eval "$(dotenv-fusion load -f .env.production)"

# Fish
eval (dotenv-fusion load -o fish)

# Example workflow
eval "$(dotenv-fusion load -f .env.production)"
echo $DATABASE_URL

Convenient aliases

Add to your ~/.bashrc, ~/.zshrc, or ~/.config/fish/config.fish:

# Bash/Zsh
alias dof='eval "$(dotenv-fusion load)"'
alias dofo='eval "$(dotenv-fusion load --override)"'

# Fish
alias dof='eval (dotenv-fusion load -o fish)'
alias dofo='eval (dotenv-fusion load -o fish --override)'

Then simply run dof to load your .env file, or dofo to load with override.

🚀 Meet Fuse: Universal .env Compatibility

The Problem: Tools like VS Code, Docker, and most editors only understand standard .env format. They can't parse advanced directives like #@import, #@if, etc.

The Solution: dotenv-fusion introduces a compilation system that transforms your advanced .env-fuse files into standard .env format, making them compatible with every tool while keeping all the power of advanced features.

# 1. Write advanced config with all features
# .env-fuse
#@import configs/database-${ENV}.env
#@if ${ENV} == production
LOG_LEVEL=error
#@endif

# 2. Compile to standard .env (one command!)
dotenv-fusion fuse --var ENV=production

# 3. VS Code, Docker, and all tools work perfectly!
# .env is now a flat file with resolved values

Why this is powerful

  • Universal Compatibility: Your .env files work with VS Code, Docker, Kubernetes, and every tool
  • 🎯 Keep Advanced Features: Use imports, conditions, and variables in .env-fuse
  • 🏗️ Build-Time Variables: Compile different environments with --var flags
  • Zero Configuration: No VS Code extensions, no Docker plugins needed
  • 🔄 Like TypeScript for .env: .env-fuse → compile → .env (just like .ts.js)

Compilation Workflows

Multi-Environment Builds (CI/CD):

# Development
dotenv-fusion fuse --var ENV=dev -o .env.dev

# Staging
dotenv-fusion fuse --var ENV=staging -o .env.staging

# Production EU
dotenv-fusion fuse --var ENV=prod --var REGION=eu -o .env.prod-eu

# Production US
dotenv-fusion fuse --var ENV=prod --var REGION=us -o .env.prod-us

Docker Integration:

# Compile during build
ARG BUILD_ENV=production
RUN dotenv-fusion fuse --var ENV=${BUILD_ENV}
# Now .env is available for all other tools!

One-Command Development:

# Compile and load in one shot
dotenv-fusion fuse --load --var ENV=dev
# Variables are in your environment, .env file created

Dynamic Imports:

# .env-fuse
#@import secrets/${TENANT_ID}/api-keys.env
#@import configs/features-${FEATURE_SET}.env

# Compile for different tenants/features
dotenv-fusion fuse --var TENANT_ID=customer-123 --var FEATURE_SET=premium
dotenv-fusion fuse --var TENANT_ID=customer-456 --var FEATURE_SET=basic

Fuse Command Reference

# Basic compilation
dotenv-fusion fuse                              # .env-fuse → .env

# With compilation variables (build-time variables)
dotenv-fusion fuse --var ENV=prod               # Single variable
dotenv-fusion fuse --var ENV=prod --var REGION=eu  # Multiple variables

# Custom source/target
dotenv-fusion fuse -f .env-fuse.prod           # Custom source
dotenv-fusion fuse -o .env.production          # Custom target

# Compile and load
dotenv-fusion fuse --load                       # Compile then load
dotenv-fusion fuse --load --var ENV=prod       # With variables

# Output options
dotenv-fusion fuse --stdout                     # Print to stdout
dotenv-fusion fuse --strip-comments             # No header comments
dotenv-fusion fuse -v                           # Verbose mode

# Search parent directories
dotenv-fusion fuse --walk                       # Search 1 parent (default)
dotenv-fusion fuse --walk-up 3                  # Search up to 3 parents

Auto-Fallback in Load

The load command automatically falls back to .env-fuse if .env doesn't exist:

# Smart fallback
eval "$(dotenv-fusion load)"
# 1. Looks for .env
# 2. If not found → loads .env-fuse (with compilation)
# 3. If not found → loads .env-fuse.local

# With compilation variables
eval "$(dotenv-fusion load --var ENV=prod --var REGION=eu)"

# Search parent directories (e.g., from a subdirectory)
eval "$(dotenv-fusion load --walk)"           # Search 1 parent (default)
eval "$(dotenv-fusion load --walk-up 3)"      # Search up to 3 parents
DOTENV_FUSION_WALK_DEPTH=5 dotenv-fusion load --walk  # Custom default depth

File Naming Convention

Like .env files, .env-fuse follows the same patterns:

  • .env-fuse - Base configuration (source file)
  • .env-fuse.local - Local developer overrides (gitignored)
  • .env-fuse.production - Production config
  • .env-fuse.development - Development config

Compiled files remove the -fuse suffix:

  • .env-fuse.env
  • .env-fuse.production.env.production

CLI commands

# Load and export variables for shell integration
dotenv-fusion load [options]

# List all variable names
dotenv-fusion list [-f FILE]

# Show variable documentation
dotenv-fusion docs [-f FILE]

# Generate template .env file
dotenv-fusion template [-f FILE] > .env.example

# Load command options:
  -f, --file FILE      File to load (default: .env)
  -o, --format FORMAT  Output format: bash, zsh, fish, json (default: bash)
  --override           Override existing environment variables
  --var, -V KEY=VALUE  Compilation variable (e.g., --var ENV=prod)
  --walk               Search parent directories if not found locally (default: 1 level, configure with DOTENV_FUSION_WALK_DEPTH)
  --walk-up N          Search up to N parent directories (implies --walk)
  -v, --verbose        Verbose mode: -v (logic and names), -vv (values with secrets masked)
  -d, --debug          Show resolved values
  -t, --typed         Show values with types
  -h, --help          Help

Examples:
  dotenv-fusion docs                    # Show variable documentation
  dotenv-fusion template > .env.example # Generate template
  dotenv-fusion load -f .env.local -d   # Debug mode
  dotenv-fusion load -o json            # Output as JSON
  dotenv-fusion load -v                 # Verbose: show logic and variable names
  dotenv-fusion load -vv                # Very verbose: show values (secrets masked)

Python API (optional)

For Python projects that need programmatic access:

from dotenv_fusion import load_dotenv, get_env

# Load .env file
values = load_dotenv()  # or load_dotenv(".env.production")

# Get a variable
port = get_env("PORT", "3000")

# Variables are also available via os.environ
import os
print(os.environ["APP_NAME"])

Single-script usage

The core dotenvfusion.py can be used standalone without installation:

# Direct usage
python -m dotenv_fusion.dotenvfusion -f .env --debug

# Or copy the file for standalone usage
cp src/dotenv_fusion/dotenvfusion.py /usr/local/bin/
python /usr/local/bin/dotenvfusion.py --help

Features

Variable expansion

# Internal variables
BASE_URL=http://localhost
API_URL=${BASE_URL}/api/v1

# System variables
DATA_DIR=$HOME/data
WORK_DIR=$PWD/workspace

# Default value
CACHE_DIR=${CACHE_PATH:-/tmp/cache}
TIMEOUT=${REQUEST_TIMEOUT:-30}

Quotes

# Double quotes: variable expansion + escape sequences
MESSAGE="Welcome to ${APP_NAME}\nVersion: ${VERSION}"

# Single quotes: literal value (no expansion)
PATTERN='${NOT_EXPANDED}'

# No quotes: expansion + inline comments
DEBUG=true  # This is a comment

Escaping

# Literal dollar
PRICE="\$99.99"
REGEX='\$[0-9]+'

# Backslash
PATH="C:\\Users\\name"

Directives

All directives start with #@ to remain compatible with other loaders.

File imports

# Simple import (error if file doesn't exist)
#@import database.env

# With prefix (HOST becomes DB_HOST)
#@import database.env prefix=DB_

# Optional import (ignored if file doesn't exist)
#@import local.env mode=ifexist

# Override (overwrites existing variables)
#@import overrides.env override=true

# Combined
#@import secrets.env prefix=SECRET_ mode=ifexist override=true

# Dynamic path
#@import configs/${ENV}.env

Options:

  • prefix=PREFIX_: prefix added to imported variables
  • override=true: overwrite already defined variables (default: first-wins)
  • mode=ifexist: silently ignore if file doesn't exist

Variable definitions

The #@def directive defines type, validation, required status, default value and documentation for a variable:

#@def PORT type=int default=3000 validate=^\d+$ doc="HTTP listening port"
#@def DEBUG type=bool default=false doc="Enable debug mode"
#@def API_KEY required=true doc="API key (required)"
#@def EMAIL validate=^[\w.-]+@[\w.-]+\.\w+$ doc="Contact email"
#@def TAGS type=list default=web,api doc="Comma-separated tags"
#@def CONFIG type=json doc="JSON configuration"

PORT=8080
API_KEY=secret-key
EMAIL=contact@example.com

Options:

  • type=: int, float, bool, str, list, json (default: str)
  • required=true: error if variable is not defined (default: false)
  • default=: default value if not defined
  • validate=: regex pattern to validate the value
  • secret=true: mark variable as secret (masked in verbose mode)
  • lazy=true: preserve ${VAR} references in fuse output instead of resolving (default: false)
  • doc=: variable description

Show documentation:

dotenv-fusion docs
# PORT (int) (default: 3000): HTTP listening port
# DEBUG (bool) (default: false): Enable debug mode
# API_KEY (str) [required]: API key (required)

Verbose mode and secrets

Verbose mode outputs diagnostic information to stderr while loading:

# Level 1: Show logic and variable names
dotenv-fusion load -v

# Output (stderr):
# [LOAD] .env
# [VAR] APP_NAME
# [VAR] DATABASE_PASSWORD (secret)
# [VAR] API_KEY (secret)
# [SUCCESS] Loaded 3 variables from 1 file(s)

# Level 2: Show values with secrets masked
dotenv-fusion load -vv

# Output (stderr):
# [LOAD] .env
# [VAR] APP_NAME = MyApp
# [VAR] DATABASE_PASSWORD = ***REDACTED*** (secret)
# [VAR] API_KEY = ***REDACTED*** (secret)
# [SUCCESS] Loaded 3 variables from 1 file(s)

Secret detection: Variables are automatically marked as secrets if their name contains common patterns (password, secret, token, key, api_key, credential, etc.). You can also explicitly mark variables as secrets:

#@def MY_TOKEN type=str secret=true doc="Explicitly marked as secret"
MY_TOKEN=my-secret-value

#@def PUBLIC_VAR type=str doc="Not a secret"
PUBLIC_VAR=public-value

Note: Secrets are only masked in verbose output (stderr). The actual export commands and environment variables contain the real values for proper shell integration.

# Verbose output goes to stderr (visible but doesn't affect eval)
eval "$(dotenv-fusion load -vv)"

# Environment variables contain real values
echo $API_KEY  # Shows the actual secret value, not ***REDACTED***

Generate template:

dotenv-fusion template > .env.example

Conditions

ENV=production

#@if ${ENV} == production
LOG_LEVEL=error
DEBUG=false
#@else
LOG_LEVEL=debug
DEBUG=true
#@endif

# Existence test
#@ifdef CI
CI_MODE=true
#@endif

#@ifndef LOCAL
REMOTE=true
#@endif

Supported operators: ==, !=, >, <, >=, <=

Processing modes

The #@mode directive changes how subsequent lines are processed:

# Enable override mode: later definitions override earlier ones
#@mode override=true
API_URL=https://prod.api.com  # This will override any previous API_URL

# Enable strict mode: error if variable is undefined
#@mode strict=true
VALUE=${MUST_EXIST}  # Error if MUST_EXIST doesn't exist

# Combine multiple modes on one line
#@mode override=true strict=true

# Disable modes
#@mode override=false strict=false

Available modes:

  • override=true/false - When true, variable definitions override existing values (default: no, first-wins)
  • strict=true/false - When true, undefined variables raise an error instead of becoming empty (default: no)

By default, an undefined variable becomes an empty string and first definition wins.

Complete examples

Web application

# .env
#@def APP_NAME required=true doc="Application name"
#@def ENV required=true validate=^(dev|staging|production)$ doc="Environment"
#@def PORT type=int default=3000 doc="HTTP port"
#@def DEBUG type=bool default=false doc="Debug mode"

APP_NAME=MyApp
ENV=production

#@import configs/base.env
#@import configs/${ENV}.env
#@import .env.local mode=ifexist

#@if ${ENV} == production
#@import secrets/prod.env prefix=SECRET_
#@else
#@import secrets/dev.env prefix=SECRET_
#@endif

Microservices

# .env
#@import services/database.env prefix=DB_
#@import services/redis.env prefix=REDIS_
#@import services/rabbitmq.env prefix=MQ_

# Result:
# DB_HOST, DB_PORT, DB_USER, DB_PASS
# REDIS_HOST, REDIS_PORT
# MQ_HOST, MQ_PORT, MQ_VHOST

CI/CD

# .env
#@ifdef CI
#@import ci/test.env override=true
#@endif

#@ifdef DOCKER
#@import docker/container.env
#@endif

#@if ${DEPLOY_TARGET} == aws
#@import deploy/aws.env
#@endif

#@if ${DEPLOY_TARGET} == gcp
#@import deploy/gcp.env
#@endif

Behavior

Resolution order

  1. Compilation variables (--var) - highest priority
  2. Variables defined in the current file and imported files (in order)
  3. System environment variables
  4. Default values (${VAR:-default})

Precedence (first-wins)

By default, the first definition wins:

# .env
PORT=3000
#@import other.env  # If other.env contains PORT=8080, it's ignored

Use override=true to change this behavior.

Circular imports

Circular imports are detected and silently ignored.

Security

Shell escaping

When using eval "$(dotenv-fusion load)", all values are properly escaped to prevent command injection:

  • shlex.quote(): Uses Python's standard shell escaping function
  • Variable name validation: Only alphanumeric + underscore names are exported
  • Command injection prevention: Dangerous values like $(rm -rf /) are safely escaped
# Example: dangerous value is safely escaped
DANGEROUS=$(echo "pwned")  # Will be exported as '$(echo "pwned")' - NOT executed

# Safe to use with eval
eval "$(dotenv-fusion load)"
echo $DANGEROUS  # Shows: $(echo "pwned") - the literal string

Best practices

  • Always use eval "$(dotenv-fusion load)" pattern for shell integration
  • Review .env files before loading untrusted sources
  • Use --debug flag to inspect values before loading: dotenv-fusion load --debug

Python API

from dotenv_fusion import DotenvLoader, load_dotenv, get_env

# Simple function
values = load_dotenv(".env", override=False, apply_types=True)

# Class for more control
loader = DotenvLoader()
values = loader.load(".env")

# Access raw values (strings)
raw_values = loader.values

# Access typed values
typed_values = loader.typed_values

# Get documentation
docs = loader.get_docs()

# Generate template
template = loader.generate_template()

License

LGPL-3.0-or-later

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dotenv_fusion-0.3.0.tar.gz (19.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dotenv_fusion-0.3.0-py3-none-any.whl (20.6 kB view details)

Uploaded Python 3

File details

Details for the file dotenv_fusion-0.3.0.tar.gz.

File metadata

  • Download URL: dotenv_fusion-0.3.0.tar.gz
  • Upload date:
  • Size: 19.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for dotenv_fusion-0.3.0.tar.gz
Algorithm Hash digest
SHA256 7b73576985ce52561eedeb25736d21a0d00e55833d1289dac60a73d4020f2c9e
MD5 4e10b075da7fd43f32ec3a5f0545ac7a
BLAKE2b-256 bbe551f74c8b85ba3ddf7c165f23eb32cdee1d584b986a04fcade7e040ebb04b

See more details on using hashes here.

File details

Details for the file dotenv_fusion-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: dotenv_fusion-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 20.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for dotenv_fusion-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b445f83540d19983ce2d8b687d779dd766357a41324a5fbebd778e538ec19284
MD5 aa5fe382bee1273049ebcc5b16d68cb4
BLAKE2b-256 bf1040d959012014cb3edbf2ddcc944c7820196130146b08ce2ad02af72144ab

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page