Skip to main content

Minimal, type-safe environment variable validation for Python.

Project description

envly

Minimal, type-safe environment variable validation for Python.

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
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

envly-0.1.0.tar.gz (3.7 kB view details)

Uploaded Source

Built Distribution

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

envly-0.1.0-py3-none-any.whl (3.7 kB view details)

Uploaded Python 3

File details

Details for the file envly-0.1.0.tar.gz.

File metadata

  • Download URL: envly-0.1.0.tar.gz
  • Upload date:
  • Size: 3.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for envly-0.1.0.tar.gz
Algorithm Hash digest
SHA256 4559890d2f217f1e1281fe898f60b1ac880b82c76f19a8bdaf1cf3a92cdb4392
MD5 714d43f56d172cb9cf6f4a16741f8e0c
BLAKE2b-256 9444a4219a313bc371f51feac5da776ba2238b51e82138eba9c46b408ffb19f5

See more details on using hashes here.

File details

Details for the file envly-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: envly-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 3.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for envly-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 74ab5a2137ad4158ea8d14ad137ba2b6abf61d22e4f0e24a2aa835492ffc580b
MD5 6b3f2f0b0ac5b1c65a5fc51278c086f5
BLAKE2b-256 11c98dd4e2fddaf159e21e11f59e3a083a77658cf291e62640208a046b033e6c

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