envdoctor (Python)
Native Python port of envdoctor — a local-first consistency checker for environment variables, distributed on PyPI so Python projects can use it without Node.
Install
pip install arun-envdoctor
The PyPI distribution is named
arun-envdoctor(PyPI blocksenvdoctoras too similar to an existing project), but the installed command and the importable package are both stillenvdoctor.
Quick start
envdoctor scan --dir . # audit; exit 1 on errors
envdoctor scan --strict # treat warnings as errors too
envdoctor scan --json # emit findings as a JSON array (no values)
What it detects
Reconciles the environment variables used in your Python source
(os.getenv("X"), os.environ.get("X"), os.environ["X"], and the
from os import environ forms) against those defined in your .env files,
then reports:
| Rule | Severity | Meaning |
|---|---|---|
undefined-in-source |
error | Referenced (source or infra files) but not defined in any .env file |
duplicates |
error | The same key is defined 2+ times within a single .env file |
public-prefix |
error | A secret-looking variable is exposed to client bundles via a public prefix (NEXT_PUBLIC_, VITE_, REACT_APP_, EXPO_PUBLIC_, GATSBY_, NUXT_PUBLIC_, VUE_APP_, PUBLIC_) |
type-mismatch |
error | A variable's value has incompatible inferred types across environments (e.g. PORT=3000 vs PORT=abc) |
unused |
warning | Defined in .env but never referenced in source |
environment-diff |
warning | Defined in some environment files but missing from others |
weak-secret |
warning | A secret-looking variable has a weak, empty, or placeholder value |
typo |
warning | A used-but-undefined name closely matches a defined one (likely a typo) |
In addition to Python source, envdoctor scans Docker Compose
(docker-compose.yml / compose.yaml), GitHub Actions workflows
(.github/workflows/*.yml), and Kubernetes manifests (any YAML with both
apiVersion: and kind:) for referenced variables. Detection is dependency-free
(regex only, no YAML parser): shell-style interpolation ${VAR} / $VAR
(including ${VAR:-default} forms) across all three, plus
${{ secrets.X }}, ${{ vars.X }}, and ${{ env.X }} references in Actions.
These references feed the same missing/undefined and unused detectors, so a
variable referenced only in infra files but never defined is flagged, and one
defined and referenced only in infra is not reported unused.
Comments and docstrings are stripped before scanning, so documented examples
don't cause false positives. Nothing is uploaded and variable values are
never printed — they are used only for detection and never appear in any output
(human or --json). envdoctor scan exits 1 when there are errors (or with
--strict, warnings), making it CI-friendly. Pass --json to emit a JSON array
of findings (each with rule, severity, name, message, file, line)
for machine consumption.
Library use
from pathlib import Path
from envdoctor import scan
result = scan(Path("."))
for finding in result.errors:
print(finding.name, finding.message)
Development
pip install -e ".[dev]" pytest
pytest
Subcommands
Alongside scan, every port shares these environment subcommands:
envdoctor diff <envA> <envB> # compare two environments (add --json)
envdoctor sync <from> <to> # copy missing keys (add --dry-run)
envdoctor init [--force] # generate .env.example + ENVIRONMENT.md
envdoctor fix # (re)generate both docs
diff reports which variable names are only in one environment; sync appends
the missing keys to the target .env file as empty KEY= placeholders — values
are never copied.
init / fix
Both commands generate two files at the project root from the union of every
variable name (defined in any .env* file ∪ referenced in source/infra), sorted
ascending. Values are never written.
.env.example— a header comment followed by oneNAME=line per variable.ENVIRONMENT.md— a Markdown table of each variable withDefined/Usedcolumns.
init writes each file only if it does not already exist (--force overwrites);
fix always regenerates both. Every port produces byte-identical files.
Schema validation
Add an envdoctor.schema.json at your project root to validate .env values:
{
"PORT": { "type": "integer", "min": 1, "max": 65535 },
"LEVEL": { "enum": ["debug", "info", "warn", "error"] },
"TOKEN": { "type": "string", "optional": true }
}
Supported rule fields: type (string/integer/float/boolean/url/json), enum,
regex, min, max, optional. Values that fail are reported as
schema-validation errors (values are never printed).
Other languages
envdoctor ships as a standalone native port for each ecosystem:
- Node (reference) · Go · Ruby · PHP · Java · Perl
- 📖 Docs: arun-skg.github.io/envdoctor
- Main repository: github.com/arun-skg/envdoctor
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 arun_envdoctor-0.1.2.tar.gz.
File metadata
- Download URL: arun_envdoctor-0.1.2.tar.gz
- Upload date:
- Size: 15.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e420941c4b1c2d8e6a1abc302295582a589ad25f3a5d0212150006ff48ea5d0e
|
|
| MD5 |
0713b8c60f30f3c5d19fa2b842e3f3aa
|
|
| BLAKE2b-256 |
46ca738845fa0ca91046063a8e22723fdc82f9f822ef9f52258598abd8af51a0
|
Provenance
The following attestation bundles were made for arun_envdoctor-0.1.2.tar.gz:
Publisher:
python-release.yml on arun-skg/envdoctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
arun_envdoctor-0.1.2.tar.gz -
Subject digest:
e420941c4b1c2d8e6a1abc302295582a589ad25f3a5d0212150006ff48ea5d0e - Sigstore transparency entry: 2569428097
- Sigstore integration time:
-
Permalink:
arun-skg/envdoctor@3cf5dfb61e76cfa10b5bcd09124f227f08c74b07 -
Branch / Tag:
refs/tags/python-v0.1.2 - Owner: https://github.com/arun-skg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-release.yml@3cf5dfb61e76cfa10b5bcd09124f227f08c74b07 -
Trigger Event:
push
-
Statement type:
File details
Details for the file arun_envdoctor-0.1.2-py3-none-any.whl.
File metadata
- Download URL: arun_envdoctor-0.1.2-py3-none-any.whl
- Upload date:
- Size: 13.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2582558bada37a2a629d589eeaf831ae781b01b48697628e03525ff0c90815e3
|
|
| MD5 |
6b77de159a81b84e527e0feaac9405ab
|
|
| BLAKE2b-256 |
0a8d20190adf6ef6525f00fa480e3ccab0181a8e8f915e00f1db8e78fa6c52e0
|
Provenance
The following attestation bundles were made for arun_envdoctor-0.1.2-py3-none-any.whl:
Publisher:
python-release.yml on arun-skg/envdoctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
arun_envdoctor-0.1.2-py3-none-any.whl -
Subject digest:
2582558bada37a2a629d589eeaf831ae781b01b48697628e03525ff0c90815e3 - Sigstore transparency entry: 2569428147
- Sigstore integration time:
-
Permalink:
arun-skg/envdoctor@3cf5dfb61e76cfa10b5bcd09124f227f08c74b07 -
Branch / Tag:
refs/tags/python-v0.1.2 - Owner: https://github.com/arun-skg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-release.yml@3cf5dfb61e76cfa10b5bcd09124f227f08c74b07 -
Trigger Event:
push
-
Statement type: