Fail-fast validation of environment-based config for Python apps.
Project description
🦉 owl · check
Your Python service must not start with a broken environment. owlcheck enforces that.
One call at the top of your application's entrypoint validates that every environment variable your code declares it needs is present and well-typed. The process either starts with a complete configuration or it does not start at all.
This eliminates a specific class of production failure: the service that boots cleanly, accepts traffic for ten minutes, then crashes the first time a code path reaches for os.environ.get("STRIPE_SECRET"). With owlcheck, that variable is checked before the first request — or the process exits before any request can arrive.
Status
Early development. API is not yet stable. Requires pydantic>=2, pydantic-settings>=2, Python 3.10+.
Installation
pip install owlcheck
The canonical pattern
There is exactly one way to use owlcheck. This is by design.
# main.py
from owlcheck import Settings
from pydantic import Field
class AppSettings(Settings):
database_url: str = Field(..., alias="DATABASE_URL", description="Postgres DSN")
stripe_secret: str = Field(..., alias="STRIPE_SECRET", description="Stripe API secret")
debug: bool = Field(False, alias="DEBUG")
settings = AppSettings.load_or_exit()
That's the entire integration. After this line returns, settings is a validated, immutable record of your application's configuration. Import it anywhere:
from main import settings
connect(settings.database_url)
If any required environment variable is missing or invalid, the process exits with code 1 and a log message that names the violated contract — before any handler, worker, or background task can run.
What this enforces
- No partial-config startup. Validation runs before your application accepts any work. If something required is missing, the process fails immediately with a readable report; it does not run for ten minutes and crash at request time.
- One source of truth. Your
Settingssubclass is the schema. Scatteredos.environ.get(...)calls in handlers, workers, and helpers are explicitly the failure modeowlcheckexists to prevent. - Aliased error messages. Validation errors name the environment variable (the field's alias), not the Python attribute. Operators read
DATABASE_URL: missing (Postgres DSN), notdatabase_url: field required. - Typo detection in
.env. Unknown variables in your.envfile are surfaced as a warning at startup, then dropped so downstream code cannot accidentally depend on them. - Per-field status log. Each declared field is reported on startup as
provided(from env or override) ordefault. You always know what configuration the process actually loaded. - Immutable after load. Once
load_or_exit()returns, the contract is frozen. Attempts to mutatesettings.x = ...raiseValidationError. The configuration of a running process does not change under the code that depends on it.
Why "one call"
The alternative — scattered os.getenv("X") checks across handlers, workers, and helpers — is a tax that compounds:
- A contributor adds a feature that reads
os.getenv("NEW_FLAG"). Code review does not catch it; nothing connectsNEW_FLAGto the declared schema. - The feature ships. In production,
NEW_FLAGis unset. The feature degrades silently in some flows, loudly in others. Nobody notices until a customer complains. - You spend an afternoon
greping for everyos.getenvandos.environ.getin the repo to figure out what the actual configuration surface of the service is.
owlcheck makes one canonical pattern strictly easier than every alternative. Declare every environment variable your service needs as a Field on your Settings subclass. Call load_or_exit() once in main.py. Use settings.x everywhere else. The schema is the contract; the call enforces it.
Public API
| Symbol | Purpose |
|---|---|
Settings |
Base class. Subclass it; declare fields with Field(alias=...). |
Settings.load_or_exit(**kw) |
The canonical entrypoint. Loads, validates, prints diagnostics; calls sys.exit(1) if anything is wrong. |
Settings.load(**kw) |
Same as above, but raises ValidationError instead of exiting. For callers that must handle the error themselves (rare). |
load(cls, **kw) / load_or_exit(cls, **kw) |
Functional forms for when you cannot subclass Settings (e.g., a third-party BaseSettings type passed in). |
clear_cache() |
Test helper. Drops cached load() results. Not for application code. |
Design choices
- Honest typing only. If a field can be
Noneat runtime, declare itT | None.owlcheckdoes not provide "soft optional" markers that lie to the type checker. - Library, not framework.
owlcheckdoes not configure logging for you. Configure your application's logging once;owlcheckwrites tologging.getLogger("owlcheck")like any well-behaved library. - Immutable after load. Once
load()returns, the contract is frozen. See above.
License
Project details
Release history Release notifications | RSS feed
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 owlcheck-0.2.0.tar.gz.
File metadata
- Download URL: owlcheck-0.2.0.tar.gz
- Upload date:
- Size: 9.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b5fb6bb8f500199a2e5bc2165060ba292a0f98b17d5589d531dd0e9d12b9527f
|
|
| MD5 |
dc43f3bea4d224dbc73e1c5634e3f946
|
|
| BLAKE2b-256 |
cb6f9732a6d6dc71a80abfaffb976548dbbb48e614e37a0a61830ea64a2859d6
|
Provenance
The following attestation bundles were made for owlcheck-0.2.0.tar.gz:
Publisher:
publish.yml on OwlsDontTalk/owlcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
owlcheck-0.2.0.tar.gz -
Subject digest:
b5fb6bb8f500199a2e5bc2165060ba292a0f98b17d5589d531dd0e9d12b9527f - Sigstore transparency entry: 1593546436
- Sigstore integration time:
-
Permalink:
OwlsDontTalk/owlcheck@a6aaa0e3b1c076769a99f88284e271c6045f13ca -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/OwlsDontTalk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a6aaa0e3b1c076769a99f88284e271c6045f13ca -
Trigger Event:
push
-
Statement type:
File details
Details for the file owlcheck-0.2.0-py3-none-any.whl.
File metadata
- Download URL: owlcheck-0.2.0-py3-none-any.whl
- Upload date:
- Size: 9.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
abe790b2cc05b7499608006ed966aecc4d9d1346b319afcf0faddd27602ac66e
|
|
| MD5 |
bd8760feb93ddfd4a71a5f4549c7a815
|
|
| BLAKE2b-256 |
edb1b7de74603087d1813ac2704bb2391964fdad07af7d164ab96964b1e28d92
|
Provenance
The following attestation bundles were made for owlcheck-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on OwlsDontTalk/owlcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
owlcheck-0.2.0-py3-none-any.whl -
Subject digest:
abe790b2cc05b7499608006ed966aecc4d9d1346b319afcf0faddd27602ac66e - Sigstore transparency entry: 1593546504
- Sigstore integration time:
-
Permalink:
OwlsDontTalk/owlcheck@a6aaa0e3b1c076769a99f88284e271c6045f13ca -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/OwlsDontTalk
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a6aaa0e3b1c076769a99f88284e271c6045f13ca -
Trigger Event:
push
-
Statement type: