Minimal, type-safe environment variable validation for Python.
Project description
envly
Minimal, type-safe environment variable validation for Python.
from envly import Environment, StringVar, IntVar, BoolVar
class MyEnv(Environment, prefix="APP_"):
HOST = StringVar()
PORT = IntVar(default=8080)
DEBUG = BoolVar(default=False)
TOKEN = StringVar(secret=True)
env = MyEnv()
print(env.PORT) # 8080 —> int, not str
print(env.TOKEN) # SecretStr('**redacted**')
Installation
pip install envly
Declaring fields
Every field is declared using a factory function. The type is baked into the function, so no annotations are required!
| Function | Returns | Notes |
|---|---|---|
StringVar() |
str |
Supports secret=True |
IntVar() |
int |
|
FloatVar() |
float |
|
BytesVar() |
bytes |
|
BoolVar() |
bool |
Accepts true/false/1/0/yes/no/on/off |
EnumVar(*choices) |
str |
Restricts to allowed values |
UrlVar() |
str |
Requires scheme and host |
EmailVar() |
str |
|
PathVar() |
Path |
Returns pathlib.Path |
RegexVar(pattern) |
str |
Must fully match |
ListVar(of=) |
list[T] |
Split from a delimited string |
JsonVar() |
Any |
Parsed with json.loads |
Options
All var functions share these keyword arguments:
PORT = IntVar(
default=8080, # used when the var is not set
validate=lambda x: x < 65536, # single validator
validate=( # or a tuple of validators
lambda x: x > 1024,
lambda x: x < 65536,
),
var_name="SERVICE_PORT", # override the env var key
)
ListVar (sub-coercion per element)
PORTS = ListVar(of=int) # "8080,9000" -> [8080, 9000]
TAGS = ListVar(of=str, sep=";") # "api;bot" -> ["api", "bot"]
ALLOWED = ListVar(of=int, validate=lambda xs: all(x < 65536 for x in xs))
Whitespace around each element is stripped automatically. Errors include the offending index: [PORTS[1]] expected an integer, got 'nope'.
JsonVar (optional type assertion)
SETTINGS = JsonVar() # any valid JSON
SETTINGS = JsonVar(type=dict) # asserts the root value is a dict
StringVar(secret=True) (redacted values)
TOKEN = StringVar(secret=True)
Returns a SecretStr, which is a str subclass that redacts itself in __repr__ and __str__, so secrets don't leak into logs. The raw value is accessible via .reveal().
print(env.TOKEN) # **redacted**
print(env.TOKEN.reveal()) # the-actual-secret
Prefix
Pass prefix on the class definition to prepend a namespace to every var name.
class MyEnv(Environment, prefix="APP_"):
PORT = IntVar(default=8080) # reads APP_PORT
var_name always takes precedence over the prefix, letting you opt individual fields out:
class MyEnv(Environment, prefix="APP_"):
DB_URL = StringVar(var_name="DATABASE_URL") # reads DATABASE_URL, not APP_DATABASE_URL
Schema
Environment.schema() returns a typed description of every declared field — useful for documentation, startup health checks, or generating .env templates.
for field in MyEnv.schema():
req = "required" if field["required"] else f"default={field.get('default')!r}"
print(f"{field['env_var']:<20} {field['type']:<12} {req}")
APP_HOST str required
APP_PORT int default=8080
APP_DEBUG bool default=False
APP_TOKEN SecretStr required
Inheritance
MyEnvs compose naturally through class inheritance.
class BaseMyEnv(Environment):
LOG_LEVEL = EnumVar("debug", "info", "warn", "error", default="info")
DEBUG = BoolVar(default=False)
class MyEnv(BaseMyEnv, prefix="APP_"):
HOST = StringVar()
PORT = IntVar(default=8080)
Testing
Pass _source to substitute os.environ with a plain dict.
env = MyEnv(_source={"APP_HOST": "localhost", "APP_PORT": "9000"})
Immutability
MyEnv instances are frozen after construction. Any attempt to set an attribute raises AttributeError.
env.PORT = 1 # AttributeError: MyEnv instances are immutable after construction.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file envly-0.1.2.tar.gz.
File metadata
- Download URL: envly-0.1.2.tar.gz
- Upload date:
- Size: 12.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c7b8ca5e75698ebdd14ed6a32bb3b531ff7d0b58aab9ef58efd177e72718e0d1
|
|
| MD5 |
72df566c05a9bb1b1f0e64542bdd4154
|
|
| BLAKE2b-256 |
8d43d82ad82ff052cf7ce9481bb61e71b4b3bf5a6a08f16b8b9cfa479f64cbf9
|
File details
Details for the file envly-0.1.2-py3-none-any.whl.
File metadata
- Download URL: envly-0.1.2-py3-none-any.whl
- Upload date:
- Size: 18.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bac4f1f171c5fbee80b3c53dce02b1fd0a61e53ee9176ec135c04cbfe5dac23e
|
|
| MD5 |
7303872f9615d8d2a3dbe45f93b693cb
|
|
| BLAKE2b-256 |
5a90317910f65cb939d51588c2e4c449aab72d4a3a711a676943155ea9834fe2
|