Skip to main content

specenv

Zero-dependency typed environment variable loader for Python

PyPI version Python versions License MIT CI


The Problem

Reading environment variables in plain Python is verbose, fragile, and produces cryptic errors at runtime:

import os

PORT    = int(os.environ.get("PORT", "8080"))
DEBUG   = os.environ.get("DEBUG", "false").lower() in ("1", "true", "yes")
TIMEOUT = float(os.environ.get("TIMEOUT", "30.0"))
HOSTS   = [h.strip() for h in os.environ.get("ALLOWED_HOSTS", "").split(",") if h.strip()]

# If PORT is "abc" you get:
# ValueError: invalid literal for int() with base 10: 'abc'
# — no variable name, no hint on how to fix it.

The Solution

import specenv

PORT    = specenv.get("PORT",           default=8080,  cast=int)
DEBUG   = specenv.get("DEBUG",          default=False, cast=bool)
TIMEOUT = specenv.get("TIMEOUT",        default=30.0,  cast=float)
HOSTS   = specenv.get("ALLOWED_HOSTS",  default=[],    cast=list)

# If PORT is "abc" you get:
# EnvCastError: Cannot cast PORT="abc" to int.
#     → Set PORT to a valid integer (e.g. PORT=8080)

Install

pip install specenv

Python 3.10+ required. No runtime dependencies.


Usage

Simple get

import specenv

PORT    = specenv.get("PORT",    default=8080,  cast=int)
DEBUG   = specenv.get("DEBUG",   default=False, cast=bool)
TIMEOUT = specenv.get("TIMEOUT", default=30.0,  cast=float)
HOSTS   = specenv.get("ALLOWED_HOSTS", default=[], cast=list)        # list[str]
PORTS   = specenv.get("PORTS",   default=[8080],  cast=list[int])    # list[int]

When default is omitted and the variable is not set, EnvCastError is raised immediately.

Bool casting

Environment value Result
1, true, yes, on True
0, false, no, off False
anything else EnvCastError

Matching is case-insensitive (TRUE, Yes, ON all work).

Schema-based loading

Group all variables into a single class for easier testing and injection:

from specenv import Schema, Var

class AppConfig(Schema):
    PORT        = Var(int,      default=8080)
    DEBUG       = Var(bool,     default=False)
    DB_URL      = Var(str,      required=True)
    TIMEOUT     = Var(float,    default=30.0)
    ALLOWED_IPS = Var(list[str], default=[])

config = AppConfig.load()
print(config.PORT)   # 8080 (or whatever PORT is set to)

Pass a plain dict to load() in tests to avoid touching the real environment:

cfg = AppConfig.load(env={"DB_URL": "sqlite:///test.db"})

Validation

PORT = specenv.get(
    "PORT",
    cast=int,
    validate=lambda v: 1 <= v <= 65535,
)

Or inside a Schema:

class Config(Schema):
    PORT = Var(int, default=8080, validate=lambda v: 1 <= v <= 65535)

Namespace / prefix

db    = specenv.namespace("DB_")
HOST  = db.get("HOST", default="localhost")      # reads DB_HOST
PORT  = db.get("PORT", default=5432, cast=int)   # reads DB_PORT

cache = specenv.namespace("CACHE_")
URL   = cache.get("URL", default="redis://localhost:6379")  # reads CACHE_URL

Error Messages

Every error is designed to tell you what went wrong and how to fix it.

EnvCastError: Cannot cast PORT="abc" to int.
    → Set PORT to a valid integer (e.g. PORT=8080)

EnvCastError: Required variable DB_URL is not set.
    → Add DB_URL to your environment or .env file.

EnvValidationError: PORT=99999 failed validation (must satisfy the provided lambda).

API Reference

Symbol Description
specenv.get(name, *, default, cast, validate) Read and optionally cast one variable
specenv.namespace(prefix) Return a Namespace that prepends prefix to every key
specenv.reset() Reset internal state (no-op in 0.1.0; safe to call in teardowns)
Schema Base class for declarative config
Var(cast_type, *, default, required, validate) Field descriptor used inside a Schema
Namespace Prefix-scoped view returned by namespace()
EnvCastError Raised on missing required variable or bad cast
EnvValidationError Raised when a validate callable returns False

Supported cast types: int, float, bool, str, list, list[int], list[float], list[str], pathlib.Path, and any list[T] where T is one of the above.


Contributing

Bug reports and pull requests are welcome on the GitHub issues page. Please open an issue before starting large changes so we can discuss the approach first.


License

MIT — see LICENSE.

Metadata

Release files for specenv 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for specenv 0.3.0
File Size Uploaded
specenv-0.3.0.tar.gz 10.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for specenv 0.3.0
File Interpreter ABI Platform
specenv-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 19.6 kB

Release files / specenv-0.3.0.tar.gz

Download URL specenv-0.3.0.tar.gz
Size 10.2 kB
Tags Source
SHA-256 checksum
How to use checksums
cde88cb8cbd103563f9ab4204a3d6ee00f6a252e3efca31096e59471eccc3b3e
BLAKE2b-256 checksum
How to use checksums
fce230938af683b3f7ce2db4b5af947acc1e137b247b9cbd671d87c41bbfdd3b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 4, 2026.

Transparency log

Release files / specenv-0.3.0-py3-none-any.whl

Download URL specenv-0.3.0-py3-none-any.whl
Size 9.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2b05cfa0e59b543af785b4f148f18924b5127a9ec5fada6469826428ef1c808c
BLAKE2b-256 checksum
How to use checksums
f68519dfeac538ac0b02e4c4f81deaf7cdd20d1935f8989f19db5f5da04b9f00
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 4, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page