Skip to main content

No project description provided

Project description

runenv

Manage application settings with ease using runenv, a lightweight tool inspired by The Twelve-Factor App methodology for configuration through environment variables.

runenv provides:

  • A CLI for language-agnostic .env profile execution
  • A Python API for programmatic .env loading

“Store config in the environment” — 12factor.net/config

Section Status
CI/CD CI - Test
PyPI PyPI - Version Downloads
Python Python Versions
Style Black Ruff Mypy
License License - MIT
Docs CHANGELOG.md

Table of Contents


Key Features

  • 🚀 CLI-First: Use .env files across any language or platform.
  • 🐍 Python-native API: Load and transform environment settings inside Python.
  • ⚙️ Multiple Profiles: Switch easily between .env.dev, .env.prod, etc.
  • ⚙️ Multiple Formats: Use plain .env, .env.json, .env.toml, or .env.yaml
  • ⚙️ Autodetect Env File: Looking for .env, .env.json, .env.toml, and .env.yaml
  • 🔧 Parameter Expansion: Bash-style ${VAR:-default}, ${VAR:?msg}, ${VAR:+alt} operators.
  • ✏️ Escape Sequences: \n, \t, \\, \" in double-quoted values; $$ for a literal $.
  • 📄 Multi-line Values: Triple-quoted heredoc syntax ("""...""" / '''...''').
  • 🔒 Required Variables: # @required VAR declarations fail fast on missing config.
  • 📎 File Includes: # @include path merges another env file inline for layered config.
  • 🧩 Framework-Friendly: Works well with Django, Flask, FastAPI, and more.

Quick Start

Installation

pip install runenv
pip install runenv[toml] # if you want to use .env.toml in python < 3.11
pip install runenv[yaml] # if you want to use .env.yaml

CLI Usage

Run any command with a specified environment:

runenv run --env-file .env.dev -- python manage.py runserver
runenv run --env-file .env.prod -- uvicorn app:app --host 0.0.0.0
runenv list [--env-file .env]           # view parsed variables
runenv lint [--env-file .env]           # check for errors in env file
runenv lint --strict [--env-file .env]  # also warn on ambient/undefined refs

Python API

Load .env into os.environ

Note: The load_env will not parse env_file if the runenv CLI was used, unless you force=True it.

from runenv import load_env

load_env() # loads .env
load_env(
    env_file=".env.dev",    # file to load - will be autodetected if not passed
    prefix='APP_',          # load only APP_.* variables from file
    strip_prefix=True,      # strip ^ prefix when loading variables
    force=True,             # load env_file even if the `runvenv` CLI was used
    search_parent=1,        # look for env_file in current dir and its 1 parent dirs
    require_env_file=False  # raise error if env file is missing, otherwise just ignore
)

Read .env as a dictionary

from runenv import create_env

config = create_env() # parse .env content into dictionary
config = create_env(
    env_file=".env.dev",    # file to load - will be autodetected if not passed
    prefix='APP_',          # parse only APP_.* variables from file
    strip_prefix=True,      # strip ^ prefix when parsing variables
    search_parent=1,        # look for env_file in current dir and its 1 parent dirs
)
print(config)

Options include:

  • Filtering by prefix
  • Automatic prefix stripping
  • Searching parent directories

Multiple Profiles

Use separate .env files per environment:

runenv .env.dev flask run
runenv .env.staging python main.py
runenv .env.production uvicorn app.main:app

Recommended structure:

.env.dev
.env.test
.env.staging
.env.production

Framework Integrations

Note: If you're using runenv .env [./manage.py, ...] CLI then you do not need change your code. Use these integrations only if you're using Python API.

Django

# manage.py or wsgi.py
from runenv import load_env
load_env(".env")

Flask

from flask import Flask
from runenv import load_env

load_env(".env")
app = Flask(__name__)

FastAPI

from fastapi import FastAPI
from runenv import load_env

load_env(".env")
app = FastAPI()

Parsing Behaviour

Situation Behaviour
Duplicate key Last definition wins; a warning is emitted by lint
Key exactly equal to --prefix Skipped (stripping would produce an empty name)
Key without matching prefix Skipped and reported as info by lint

Duplicate keys are not an error — the last value in the file takes effect, matching the behaviour of most shell .env loaders. Use runenv lint to surface duplicates as warnings before they reach production.

Variable Expansion

${VAR} references resolve against variables defined in the same file and then fall back to the calling shell's os.environ. The following bash-style parameter expansion operators are supported:

Syntax Behaviour
${VAR} Value of VAR; empty string if unset
${VAR:-default} Value of VAR if set and non-empty, otherwise default
${VAR-default} Value of VAR if set (even if empty), otherwise default
${VAR:?msg} Value of VAR if set and non-empty; fatal error with msg otherwise
${VAR:+alt} alt if VAR is set and non-empty, otherwise empty string

The :? operator causes runenv run / runenv list to exit non-zero and runenv lint to report an error-level message with the line number where the variable is declared.

Quoting and Escape Sequences

Style Escape processing Variable expansion
Unquoted VAR=value None Yes
Single-quoted VAR='value' None Yes
Double-quoted VAR="value" \n \t \r \\ \" Yes
Triple double-quoted VAR="""...""" Same as double-quoted Yes
Triple single-quoted VAR='''...''' None Yes

Double-quoted values process the standard escape sequences:

GREETING="Hello\tWorld\n"   # tab + newline
PATH_VAL="C:\\Users\\name"  # literal backslashes
QUOTED="say \"hi\""         # embedded double quote

Use $$ anywhere to emit a literal $ without triggering variable expansion:

PGSERVICE=$$HOME/.pgservice  # value: $HOME/.pgservice
TEMPLATE=price: $$${AMOUNT}  # literal $ followed by expanded AMOUNT

Triple-quoted values span multiple lines — useful for certificates, JSON blobs, or any multi-line secret:

PRIVATE_KEY="""
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----
"""

RAW_TEXT='''
no \n escape processing here
$$HOME is literal too
'''

Inline Comments

Comments start with #. The rules depend on quoting:

Style # treatment
Unquoted VAR=value # comment # ends the value; trailing spaces before # are stripped
Double-quoted VAR="value # hash" # inside quotes is a literal character
Single-quoted VAR='value # hash' # inside quotes is a literal character
DEBUG=1         # this comment is stripped → value is "1"
MSG=hello world # this too → value is "hello world"
TAG="v1.0 # rc" # hash is part of the value → "v1.0 # rc"

To include a literal # in an unquoted value, quote the value instead.

Including Other Files

Use # @include path to load another env file at that point in the current file. Paths are relative to the file containing the directive.

# @include .env.base
# @include ../shared/secrets.env

PORT=8080  # overrides anything in included files above

Merge order: variables are processed in the order they appear — included files are expanded inline at the directive's position. A variable defined after the @include line overrides a same-named variable from the included file; a variable defined before is overridden by the included file.

Error cases reported by runenv lint:

  • Included file not found — error-level message, parsing continues
  • Circular include (A includes B includes A) — error-level message, the cycle is broken

# @required directives inside included files are honoured.

Required Variables

Declare variables that must be present and non-empty with # @required:

# @required DATABASE_URL, SECRET_KEY
# @required PORT

DATABASE_URL=postgresql://localhost/mydb
SECRET_KEY=${APP_SECRET:?APP_SECRET must be set}
PORT=${PORT:-8000}

If any declared variable is missing or empty after full expansion, runenv lint reports an error at the directive's line number and runenv run exits non-zero. Multiple names can appear on one line (comma-separated) or across multiple directives.

# @required and ${VAR:?msg} are complementary, not duplicates. Use # @required to declare top-level contracts on the keys your application needs. Use ${SOURCE:?msg} when building a value from another variable and you want a specific error message that names the source. Don't combine both on the same variable.


Sample .env File

# Pull in shared base configuration
# @include .env.base

# Declare required variables — runenv fails fast if any are missing
# @required DATABASE_URL, SECRET_KEY

# export keyword accepted for shell-source compatibility
export HOST=localhost
PORT=${PORT:-8000}
URL=http://${HOST}:${PORT}

# Parameter expansion
CACHE_URL=${REDIS_URL:-redis://localhost:6379}   # default if unset/empty
LOG_LEVEL=${LOG_LEVEL-info}                      # default only if unset
FEATURE_HEADER=${FEATURE_FLAG:+X-Feature: on}   # set only when flag is on

# :? is for inline interpolation guards (different from # @required):
# it fails with a custom message pointing at the *source* variable
DATABASE_URL=${DATABASE_URL:?DATABASE_URL must be set}
SECRET_KEY=${SECRET_KEY:?SECRET_KEY must be set}

# Escape sequences in double-quoted strings
GREETING="Hello\tWorld"
WINDOWS_PATH="C:\\Users\\deploy"

# Literal $ with $$ — no variable expansion triggered
PGSERVICE=$$HOME/.pgservice

# Multi-line heredoc value (triple-quoted)
BANNER="""
Welcome to MyApp
Running on ${HOST}:${PORT}
"""

# Quotes and inline comments
EMAIL="admin@example.com" # Inline comment
TOKEN='s3cr3t'
DEBUG=1

Similar Tools


With runenv, you get portable, scalable, and explicit configuration management that aligns with modern deployment standards. Ideal for CLI usage, Python projects, and multi-environment pipelines.

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

runenv-1.4.4.tar.gz (220.9 kB view details)

Uploaded Source

Built Distribution

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

runenv-1.4.4-py3-none-any.whl (18.8 kB view details)

Uploaded Python 3

File details

Details for the file runenv-1.4.4.tar.gz.

File metadata

  • Download URL: runenv-1.4.4.tar.gz
  • Upload date:
  • Size: 220.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.11

File hashes

Hashes for runenv-1.4.4.tar.gz
Algorithm Hash digest
SHA256 4904f22a40f6823114427a51e66423ab0785a85a5c854fe998ac1f87d333c1ef
MD5 d620c0e82731c01a32d50348fec5a5d0
BLAKE2b-256 3b8c4b9f359bc9d9d14846efa4f0c40241db8c650e2084672832f8ef8dce6a23

See more details on using hashes here.

File details

Details for the file runenv-1.4.4-py3-none-any.whl.

File metadata

  • Download URL: runenv-1.4.4-py3-none-any.whl
  • Upload date:
  • Size: 18.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.11

File hashes

Hashes for runenv-1.4.4-py3-none-any.whl
Algorithm Hash digest
SHA256 69412305edaaef793082200ee3e0490995a948106a182d4de849f6fb086db6c7
MD5 071911cd20d872bde3d8252be3417af7
BLAKE2b-256 68b63897a9c9ebd6b0b913ee2e13b611591f156b975bbc0ff0f7ffc98d71a98a

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