Skip to main content

Environment variable loading utility with project root detection

Project description

envo

Environment variable loading utility.

envo provides a powerful, type-safe way to manage environment variables in Python projects. It automatically discovers .env files, coerces values to appropriate types, validates against specifications, and offers both programmatic and CLI interfaces.

Features

  • Load From Many Sources - .env.sample + .env.base + .other.yaml + c.json + d.toml + .env + os.environ
  • Refs - ENV_VAR_B=$ENV_VAR_A
  • Math - ITEM_COUNT=($MODE=="prod")?5:10
  • Chaining - ENVO_EXTENDS=.env.base ENVO_EXTENDED_BY=.env.overrides
  • Auto-Coerced Types - env.MY_VAL # 5 <int> automatically converts strings to int, float, bool, Path, dict, list, and more
  • Validation - using @min/@max/@type/etc declarations in sample.env
  • Self-Documenting - comments in the sample.env get loaded into the Env object and used/shown in commandline tools
  • CLI Tools - for validating, configuring, etc.
  • Path Substitution - % references the project root, and can be used for filepath env variables
  • Path Fallbacks - /file-a.txt|/file-b.txt will use the firs path that exists
  • Multiple file formats — Load from .env, .json, .yaml, and .toml files
  • Variable grouping — Auto-detected from sample.env, helps with grabbing chunks of env variables
  • Customizable — Override behavior with ENVO_* environment variables
  • Supports .py env specs - Allow using .py files to specify default values

How to use?

Requirements

  • Python 3.10+

Install

pip install modularizer-envo

Your job is simple:

  • write a sample.env in your project root
  • let the user overwrite variables using .env or by running envo config
  • load in parsed variables with from envo import Env and env = Env()

Quick Start

Programmatic Usage

from envo import Env

# Auto-discover and load environment
env = Env()

# Access with automatic type coercion
debug = env.DEBUG          # bool
port = env.PORT            # int
db_url = env.DATABASE_URL  # str

# Or use the pre-built global instance
from envo import env
print(env.DEBUG)

Using a Spec File

Create a sample.env file to define your environment schema:

# sample.env - Environment Variable Specification

# =============================================================================
# database - Database Configuration
# =============================================================================
DB_HOST=localhost          # Database hostname @required
DB_PORT=5432               # Port number @type:int @range:1-65535
DB_NAME=myapp              # Database name

# =============================================================================
# server - Server Configuration  
# =============================================================================
DEBUG=false                # Enable debug mode @type:bool
PORT=8080                  # Server port @type:int
LOG_LEVEL=info             # Log level @choices:debug,info,warn,error

Then in your code:

from envo import Env

env = Env()  # Automatically uses sample.env as spec

# Values are validated and coerced according to spec
port = env.DB_PORT    # int: 5432
debug = env.DEBUG     # bool: False

CLI Commands

envo show - Display environment variables with colored output.

envo validate - Validate a .env file against a spec.

envo config - Interactive TUI for browsing and editing environment variables.

Spec File Format

Basic .env Spec

# =============================================================================
# group_name - Group Description
# =============================================================================

VARIABLE_NAME=default_value  # Documentation text @directive:value

# Supported directives:
#   @type:int|float|bool|str|path|json|list|glob
#   @min:N          - Minimum value
#   @max:N          - Maximum value
#   @range:N-M      - Range constraint
#   @choices:a,b,c  - Valid choices
#   @pattern:regex  - Regex pattern
#   @required       - Value must be non-empty

JSON/YAML Spec

# config.yaml
database:
  DB_HOST:
    default: localhost
    help: Database hostname
    required: true
  DB_PORT:
    default: 5432
    type: int
    min: 1
    max: 65535

server:
  DEBUG:
    default: false
    type: bool
  PORT:
    default: 8080
    type: int

Type Coercion

envo automatically coerces values based on content or explicit type hints:

Type Examples Notes
bool true, false, 1, 0, yes, no, on, off Case-insensitive
int 42, 1_000_000 Underscores allowed
float 3.14, 1_000.5 Underscores allowed
path /home/user, ~/data, %/config % = project root
dict/list {"key": "value"}, ["a", "b"] JSON format
null null, none Becomes Python None
# Explicit type override
env.get("PORT", type=int)
env.get("HOSTS", type=list)
env.get_as("CONFIG_PATH", Path)

File Chaining

Chain multiple configuration files with priority control:

# base.env - Load another file with LOWER priority (we override it)
ENVO_EXTENDS=defaults.env
DB_HOST=production-db

# .env - Load another file with HIGHER priority (it overrides us)
ENVO_EXTENDED_BY=.env.local

Priority order (lowest to highest):

  1. Spec file defaults (sample.env)
  2. Extended files (ENVO_EXTENDS)
  3. Main .env file
  4. Extending files (ENVO_EXTENDED_BY)
  5. System environment variables (os.environ)

Variable References

Reference other variables with $VAR_NAME:

BASE_DIR=/app
DATA_DIR=$BASE_DIR/data
LOG_DIR=$BASE_DIR/logs
FULL_URL=http://$HOST:$PORT/api

Use % to reference the project root:

CONFIG_PATH=%/config
DATA_PATH=%/data

Expression Operators

Envo supports logical and arithmetic expressions for dynamic configuration:

Operator Syntax Description Result
Ternary $VAR?yes:no If VAR is truthy, use "yes", else "no" yes or no
NOT !$VAR Logical negation true or false
AND $A&&$B Logical AND true or false
OR $A||$B Logical OR true or false
Equals $A==$B Equality check true or false
Not Equals $A!=$B Inequality check true or false
Add $A+$B Addition (numbers) or concatenation (strings) number or string
Subtract $A-$B Subtraction number
Multiply $A*$B Multiplication number

Note: Single | and / are NOT operators to avoid conflicts with path separators.

# Ternary - conditional values
DEBUG_MSG=$DEBUG?Debugging enabled:Production mode
DB_HOST=$USE_DOCKER?db:localhost

# Logical operators (use && and ||)
IS_READY=$DB_CONFIGURED&&$CACHE_CONFIGURED
USE_FALLBACK=$PRIMARY_DOWN||$FORCE_FALLBACK
FEATURE_OFF=!$FEATURE_ENABLED

# Comparisons
IS_PROD=$ENV==production
NOT_LOCAL=$ENV!=local

# Arithmetic
TOTAL_WORKERS=$CPU_COUNT*2
ADJUSTED_PORT=$BASE_PORT+$INSTANCE_ID

# String concatenation (when values aren't both numeric)
GREETING=$HELLO+$WORLD

# Combine with parentheses
SHOW_DEBUG=($DEBUG&&$VERBOSE)||$FORCE_DEBUG
COMPLEX_CALC=($A+$B)*$C

Truthiness: Values are truthy unless they are empty, false, 0, no, off, null, or none.

Variable Groups

Filter variables by group for organized access:

# Get all database variables
db_env = env.get_group("database")
print(db_env.DB_HOST)

# List all available groups
print(env.list_groups())

# Access all groups as a dict
for name, group_env in env.groups.items():
    print(f"{name}: {list(group_env.keys())}")

Configuration

Customize envo behavior with environment variables:

Variable Default Description
ENVO_DEFAULT_ENV_FILE .env Default env file name
ENVO_DEFAULT_SPEC_FILES sample.env,.env.sample Spec file search order
ENVO_DEFAULT_GROUP unknown Default group for ungrouped vars
ENVO_USE_SPEC_DEFAULT <default> Special value to use spec default
ENVO_BOOL_TRUE_VALUES true,1,yes,on,y,enable,enabled Values coerced to True
ENVO_BOOL_FALSE_VALUES false,0,no,off,n,disable,disabled Values coerced to False
ENVO_PROJECT_ROOT_CHAR % Character for project root substitution
ENVO_REF_CHAR $ Character for variable references

See sample.env in the repository for the complete list of configuration options, including color scheme customization.

Advanced Usage

Custom Spec

from envo import Env

env = Env(
    ".env",
    spec={
        "PORT": int,
        "DEBUG": bool,
        "HOSTS": list,
        "DB_*": {"groups": ("database",), "type": str},
        "API_*": {"groups": ("api",), "required": True},
    }
)

Multiple Files with Priority

# Load with explicit priority order
env = Env(
    "defaults.env",      # Lowest priority
    ".env",              # Medium priority
    ".env.local",        # Highest priority
)

# Control system environment handling
env = Env(
    ".env",
    existing_env_priority="lowest",  # or "highest" (default), "none"
)

Validation Pipeline

from envo import Env
from envo.spec_type import VariableSpec

env = Env(
    spec={
        "PORT": VariableSpec(
            type=int,
            default=8080,
            validator=lambda x: x if 1 <= x <= 65535 else ValueError("Invalid port"),
        ),
        "EMAIL": VariableSpec(
            type=str,
            pattern=r"^[^@]+@[^@]+\.[^@]+$",
            required=True,
        ),
    }
)

Access Raw Values

# Get the raw string value before coercion
raw_port = env.raw["PORT"]  # "8080" (string)

# Get the parsed/coerced value
port = env.PORT  # 8080 (int)

License

Unlicense — Public Domain

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

modularizer_envo-0.1.1.tar.gz (58.0 kB view details)

Uploaded Source

Built Distribution

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

modularizer_envo-0.1.1-py3-none-any.whl (59.7 kB view details)

Uploaded Python 3

File details

Details for the file modularizer_envo-0.1.1.tar.gz.

File metadata

  • Download URL: modularizer_envo-0.1.1.tar.gz
  • Upload date:
  • Size: 58.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for modularizer_envo-0.1.1.tar.gz
Algorithm Hash digest
SHA256 ce89d355e04e4d27b8fc85310c866ad34e08f1e28c0623cac277359120ef64e4
MD5 05db4e2572e5e40d8904f2200f6a8b62
BLAKE2b-256 b237a63fdea51d71520f85a5a99d6692c60b03e4603c9a6cc7b2ccff225d36e3

See more details on using hashes here.

Provenance

The following attestation bundles were made for modularizer_envo-0.1.1.tar.gz:

Publisher: publish.yml on modularizer/envo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file modularizer_envo-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for modularizer_envo-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2c52813bbba1f03c8210d510fa8591af3c113ba7af7ecf604189e1ef75a75b88
MD5 9d09a3b989c5b308ac3af571e142aa10
BLAKE2b-256 5f4270a618e9cb2fd6fe23a70455fa2181b83869d99c642273496ab4e52cf1d7

See more details on using hashes here.

Provenance

The following attestation bundles were made for modularizer_envo-0.1.1-py3-none-any.whl:

Publisher: publish.yml on modularizer/envo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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