envgate
A minimal Python library to validate environment variables at startup. Zero dependencies.
Why?
Instead of your app crashing at runtime because DATABASE_URL is missing,
envgate validates everything at startup and tells you exactly what's wrong.
Installation
pip install envgate
Quick Start
from envgate import get_env, validate
# Get a single variable with type coercion
port = get_env("PORT", type="int", default=8000)
debug = get_env("DEBUG", type="bool", default=False)
# Explicitly mark a variable as required
api_key = get_env("API_KEY", required=True)
# Parse comma-separated lists (or use a custom separator)
hosts = get_env("ALLOWED_HOSTS", type="list") # ["a", "b", "c"]
ports = get_env("PORTS", type="list[int]", sep=":") # [8000, 8001]
# Or validate multiple variables at once
config = validate({
"DATABASE_URL": {"type": "str"},
"REDIS_URL": {"type": "str"},
"PORT": {"type": "int", "default": 8000},
"DEBUG": {"type": "bool", "default": False},
})
If DATABASE_URL and REDIS_URL are missing and PORT is invalid, you get all errors at once:
envgate.exceptions.ValidationError: Environment validation failed:
- Environment variable 'DATABASE_URL' is not set.
- Environment variable 'REDIS_URL' is not set.
- Environment variable 'PORT' has invalid value 'abc' (expected int).
Custom validators
Type coercion checks that PORT is an integer. A validator checks that
the value also makes sense — e.g. that the port is in a usable range, or
that a log level is one of a fixed set:
def in_port_range(p):
if not (1024 <= p <= 65535):
raise ValueError("must be in [1024, 65535]")
def is_known_level(level):
if level not in {"debug", "info", "warning", "error"}:
raise ValueError("must be one of debug|info|warning|error")
config = validate({
"PORT": {"type": "int", "validator": in_port_range},
"LOG_LEVEL": {"type": "str", "default": "info", "validator": is_known_level},
})
A validator signals failure by raising any exception — its message is
captured and joined into the same collective ValidationError as missing
and invalid-type errors:
envgate.exceptions.ValidationError: Environment validation failed:
- Environment variable 'PORT' has invalid value '80': must be in [1024, 65535]
- Environment variable 'LOG_LEVEL' has invalid value 'verbose': must be one of debug|info|warning|error
Loading a .env file
For local development, load variables from a .env file before validating.
load_env() copies the file's entries into os.environ, so validate()
picks them up with no extra wiring:
from envgate import load_env, validate
load_env() # reads ./.env into os.environ (defaults to ".env")
config = validate({
"DATABASE_URL": {"type": "str"},
"PORT": {"type": "int", "default": 8000},
})
Given a .env like:
# database
DATABASE_URL=postgres://localhost/app
PORT=5432
export DEBUG="true"
- Real environment variables win by default. A key already set in the
environment (CI, containers, systemd) is not overwritten by the file.
Pass
load_env(override=True)to flip this and let the file's values replace what's already there — handy for test fixtures. - A missing file is a silent no-op — handy in production, where you
rely on real environment variables and ship no
.env. A file that exists but has a broken line raisesEnvFileError. load_env()returns a dict of everything it parsed from the file, so you can log or inspect it without touching global state.
Parsing is stdlib-only and deliberately simple: blank lines and full-line
# comments are skipped, a leading export is tolerated, surrounding
quotes are stripped, and there's no shell-style interpolation.
Supported Types
| Type | Example values |
|---|---|
str |
Any string (default) |
int |
"42", "-7", "0" |
float |
"3.14", "42", "-2.5" |
bool |
"true", "1", "yes", "on" / "false", "0", "no", "off" |
list, list[str], list[int], list[float], list[bool] |
Comma-separated values — e.g. "a,b,c" → ["a", "b", "c"]. Pass sep=":" (or any character) to override the separator. |
Contributing
Contributions are welcome! Check out the CONTRIBUTING.md for guidelines.
License
MIT
Release files for envgate 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| envgate-1.0.0.tar.gz | 11.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| envgate-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.9 kB
Release files / envgate-1.0.0.tar.gz
| Download URL | envgate-1.0.0.tar.gz |
|---|---|
| Size | 11.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6d6abd3e5bb089b479c428ff3e9902b650b05da28cdf9176d10ac84d05465f2b
|
|
BLAKE2b-256 checksum How to use checksums |
47b14b30527c6497fbd47b34d2b795cd7b8b986253dd2228afbec30a6a33a03d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / envgate-1.0.0-py3-none-any.whl
| Download URL | envgate-1.0.0-py3-none-any.whl |
|---|---|
| Size | 13.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
524ba39ab75d469af8067e7919c392ad93749243458207af451db052991dccf1
|
|
BLAKE2b-256 checksum How to use checksums |
d598a0c1141c7545465020def3c3e481ae3c403f8d99951b80708cb225d63a1e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|