python -m envless_env types reads the names, types and visibility of your variables, never their values, and writes envless_env_types.py: the same env objects, typed by name. A misspelt name, a server variable read through the client view, or a bool used as a str then fails your type check instead of your deploy.
python -m envless_env typesfrom envless_env_types import client_env, env, optional
database_url: str = env.DATABASE_URL
port: float = env.PORT
public_url: str = client_env.PUBLIC_API_URL
sentry_dsn: str | None = optional.SENTRY_DSNIt is the Python counterpart of envless types, which writes envless-env.d.ts for TypeScript. The module costs nothing at runtime: each name is the matching view from envless_env.server or envless_env.client, cast to a generated Protocol. Commit it, it holds no secrets.
What it writes
| Name | View | Types |
|---|---|---|
env | envless_env.server.env | Every variable |
optional | envless_env.server.optional | Every variable, each | None |
client_env | envless_env.client.env | Client variables only |
client_optional | envless_env.client.optional | Client variables only, each | None |
A string variable is typed str, a boolean is bool, and a number is float. That last one is deliberate: a number reads back as an int when it is whole and a float when it is not, and float accepts both, so a fractional sample rate is typed as honestly as a port. Wrap it in int() where an API wants an integer, or validate with pydantic-settings.
Names that are Python keywords, class for instance, are listed in a comment at the top of the file and stay readable by subscription, env['class']. Names that start and end with two underscores are reserved by Python, listed in a second comment, and readable only from the dict load() returns.
Where the names come from
Under envless run, the command reads the manifest the CLI injected and makes no request, so your envless login session is all it needs:
envless run -- python -m envless_env typesOutside it, the command fetches the metadata with the same machine credentials the loader uses: ENVLESS_TOKEN, with the project and environments from the flags, from ENVLESS_PROJECT and ENVLESS_ENV, or from .envless. No decryption key is needed. Under envless run the flags below are ignored, and the command says so, because the CLI has already chosen the environments: pass --env to envless run instead. Every environment in .envless is read and merged in order, so a name that only exists in staging is still typed, with the type of the first environment that declares it.
| Flag | Purpose |
|---|---|
--output PATH | The file to write, envless_env_types.py by default |
--project SLUG | The project, instead of ENVLESS_PROJECT or .envless |
--product SLUG | The product, when the project slug exists in more than one |
--env SLUGS | Comma separated environments, merged in order |
--check | Exit 1 when the file is missing or stale, and write nothing |
Keeping it current
Run it again whenever you add, rename or retype a variable. In CI, --check fails the job when the committed file no longer matches Envless:
envless run --env production -- python -m envless_env types --checkThe file is formatted the way Black and Ruff format code by default, so running your formatter over it changes nothing and --check stays green. Leave it out of hand edits: the next run replaces it.