Skip to main content

msgspec-conf

A tiny settings loader inspired by pydantic_settings, but implemented with msgspec. It allows loading structured configuration from environment variables, .env files, YAML and TOML config files without depending on Pydantic.

Features

  • Define settings as msgspec.Struct classes
  • Load values from the current environment, optional .env files, YAML or TOML config files, and defaults
  • Automatic type coercion for scalar values and common collection formats
  • Optional prefixes and case-insensitive matching
  • File-backed secret fallback via *_FILE variables
  • Declarative loading for top-level settings composed from prefixed blocks

Usage

from typing import Optional
import msgspec

from msgspec_conf import BaseSettings

class AppSettings(BaseSettings):
    debug: bool = False
    database_url: str
    api_key: Optional[str] = None

settings = AppSettings.load(env_file=".env", prefix="APP_")
print(settings.database_url)

.env values override defaults, while real environment variables take precedence over the file.

.env lines may start with export . A # preceded by whitespace starts a comment in unquoted values (HOST=example.com # prod), while COLOR=#fff, URLs with fragments and quoted values (NOTE="a # b") are kept as written.

YAML and TOML config files

A config file can supply structured, non-secret configuration — the kind you commit to git — while .env keeps private values out of the repository. The two sources are independent: each has its own path and either can be used alone or together. The file is parsed as TOML when it has a .toml suffix and as YAML otherwise.

settings = AppSettings.load(config_file="config.yaml", env_file=".env")
# config.yaml — safe to commit
debug: false
host: api.example.com
tags:
  - landing
  - monitoring

Values resolve with the precedence real env vars > .env file > config file > defaults. So if HOST appears both in config.yaml and the environment, the environment wins; fields only present in the config file are still applied.

The same applies to TOML. To read settings straight out of pyproject.toml, point config_file at it and use config_table to select the table (a dotted path) that holds your settings:

settings = AppSettings.load(
    config_file="pyproject.toml",
    config_table="tool.myservice",
)
# pyproject.toml
[tool.myservice]
debug = false
host = "api.example.com"
tags = ["landing", "monitoring"]

config_table works with any config file format; without it the whole document is used. load_composed_settings accepts both config_file and config_table as well, so each prefixed block can be configured from a nested table.

Because YAML is already structured, native types are used directly — no string parsing is needed for lists, mappings, or nested records:

limits:
  checkout: 20
  login: 60
rules:
  - { id: 1, score: 95 }
  - { id: 2, score: 90 }

Top-level YAML keys are matched against field names case-insensitively.

List values can be written as CSV, semicolon/newline-delimited text, or JSON:

APP_ALLOWED_ORIGINS=https://example.com,https://api.example.com
APP_ALLOWED_LOCALES=en;uk;fr
APP_ALLOWED_TOPICS=["landing.live_stats","monitoring.live_status"]

Dict values can be written as JSON or delimited key-value pairs:

APP_LIMITS={"checkout":20,"login":60}
APP_LIMITS=checkout=20,login:60;refresh=120

list[dict[...]] values can be written as JSON or as records separated by semicolon/newline. Within each record, comma separates key-value pairs:

APP_RULES=[{"id":"policy.example","score":95,"evidence_urls":["https://example.com"]}]
APP_RULES=id=1,score=95;id=2,score=90

Prefer JSON when values are deeply nested or may contain delimiters.

Boolean values are parsed strictly. Accepted values are 1/0, true/false, yes/no, on/off, and y/n.

Literal[...], enum.Enum/IntEnum and Annotated[...] fields are coerced from their string form, and nested msgspec.Struct fields accept JSON:

APP_LOG_LEVEL=2
APP_ENDPOINT={"host":"api.example.com","port":443}

File-backed values

If TOKEN is empty or unset, TOKEN_FILE can point to a file containing the value:

class SecretSettings(BaseSettings):
    token: str = ""
    token_file: str | None = None

settings = SecretSettings.load()
TOKEN_FILE=/run/secrets/api_token

The token_file field is optional. TOKEN_FILE also works when only token is defined.

Composed settings

Services with a top-level msgspec.Struct can load nested settings blocks declaratively:

from msgspec_conf import BaseSettings, ServiceDefaultsBase, load_composed_settings
import msgspec

class DatabaseSettings(BaseSettings):
    host: str = "localhost"
    port: int = 5432

class ServiceDefaults(ServiceDefaultsBase):
    service_name: str = "example-service"

class Settings(msgspec.Struct, kw_only=True):
    debug: bool = False
    service_name: str = "example-service"
    database: DatabaseSettings = msgspec.field(default_factory=DatabaseSettings)

settings = load_composed_settings(
    Settings,
    env_file=".env",
    defaults_cls=ServiceDefaults,
    prefixes={"database": "POSTGRES_"},
)

Default factories on nested fields are preserved and then overridden by matching environment values.

A YAML config file works here too. Top-level keys map to shared fields, while each nested mapping (keyed by the block's field name) configures that block:

settings = load_composed_settings(
    Settings,
    config_file="config.yaml",
    env_file=".env",
    defaults_cls=ServiceDefaults,
    prefixes={"database": "POSTGRES_"},
)
debug: false
service_name: example-service
database:
  host: db.internal
  port: 5432

Environment variables (e.g. POSTGRES_HOST) still override the matching YAML values.

Shared fields covered by defaults_cls also read environment variables; other top-level fields are taken from the config file as-is.

Installation

pip install msgspec-conf
# or
uv add msgspec-conf

Development

uv sync --dev
uv run pytest

Metadata

Release files for msgspec-conf 1.4.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 msgspec-conf 1.4.0
File Size Uploaded Provenance
msgspec_conf-1.4.0.tar.gz 10.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for msgspec-conf 1.4.0
File Interpreter ABI Platform
msgspec_conf-1.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 21.0 kB

Release files / msgspec_conf-1.4.0.tar.gz

Download URL msgspec_conf-1.4.0.tar.gz
Size 10.2 kB
Tags Source
SHA-256 checksum
How to use checksums
5ebd88ab6f96fef4374557499fdb2ece6550571488e792d151b00698dcb91182
BLAKE2b-256 checksum
How to use checksums
25e298e923697dcbf3a21934fa27b3a82311e6084768ad9a2af98f3480530f86
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.0

Release files / msgspec_conf-1.4.0-py3-none-any.whl

Download URL msgspec_conf-1.4.0-py3-none-any.whl
Size 10.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cbffc3dde6bf35702abc1002376597bf443f11d768dae876e710b64f6cdf704f
BLAKE2b-256 checksum
How to use checksums
ca51e3eeba2b224a33870ebb511f97b05fb903dff3dc5e8d944b61303ace737c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.15.0

Security for msgspec-conf 1.4.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.4.0 This release

2 release files No provenance

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