Skip to main content

Fail-fast validation of environment-based config for Python apps.

Project description

🦉 owl · check

CI Python Pydantic pydantic-settings License PyPI

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 Settings subclass is the schema. Scattered os.environ.get(...) calls in handlers, workers, and helpers are explicitly the failure mode owlcheck exists 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), not database_url: field required.
  • Typo detection in .env. Unknown variables in your .env file 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) or default. You always know what configuration the process actually loaded.
  • Immutable after load. Once load_or_exit() returns, the contract is frozen. Attempts to mutate settings.x = ... raise ValidationError. 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:

  1. A contributor adds a feature that reads os.getenv("NEW_FLAG"). Code review does not catch it; nothing connects NEW_FLAG to the declared schema.
  2. The feature ships. In production, NEW_FLAG is unset. The feature degrades silently in some flows, loudly in others. Nobody notices until a customer complains.
  3. You spend an afternoon greping for every os.getenv and os.environ.get in 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 None at runtime, declare it T | None. owlcheck does not provide "soft optional" markers that lie to the type checker.
  • Library, not framework. owlcheck does not configure logging for you. Configure your application's logging once; owlcheck writes to logging.getLogger("owlcheck") like any well-behaved library.
  • Immutable after load. Once load() returns, the contract is frozen. See above.

License

MIT

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

owlcheck-0.2.0.tar.gz (9.2 kB view details)

Uploaded Source

Built Distribution

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

owlcheck-0.2.0-py3-none-any.whl (9.3 kB view details)

Uploaded Python 3

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

Hashes for owlcheck-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b5fb6bb8f500199a2e5bc2165060ba292a0f98b17d5589d531dd0e9d12b9527f
MD5 dc43f3bea4d224dbc73e1c5634e3f946
BLAKE2b-256 cb6f9732a6d6dc71a80abfaffb976548dbbb48e614e37a0a61830ea64a2859d6

See more details on using hashes here.

Provenance

The following attestation bundles were made for owlcheck-0.2.0.tar.gz:

Publisher: publish.yml on OwlsDontTalk/owlcheck

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for owlcheck-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 abe790b2cc05b7499608006ed966aecc4d9d1346b319afcf0faddd27602ac66e
MD5 bd8760feb93ddfd4a71a5f4549c7a815
BLAKE2b-256 edb1b7de74603087d1813ac2704bb2391964fdad07af7d164ab96964b1e28d92

See more details on using hashes here.

Provenance

The following attestation bundles were made for owlcheck-0.2.0-py3-none-any.whl:

Publisher: publish.yml on OwlsDontTalk/owlcheck

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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