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.

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.

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.

Installation

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

Development

uv sync --dev
uv run pytest

Release files for msgspec-conf 1.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 msgspec-conf 1.3.0
File Size Uploaded
msgspec_conf-1.3.0.tar.gz 9.4 kB Details

Built distribution (wheel)

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

Total release size: 19.4 kB

Release files / msgspec_conf-1.3.0.tar.gz

Download URL msgspec_conf-1.3.0.tar.gz
Size 9.4 kB
Tags Source
SHA-256 checksum
How to use checksums
f2070043d2bf8e6a4a9d5b4cc45aa1e15ad7e740ce3bbf89030eb77b287afa5e
BLAKE2b-256 checksum
How to use checksums
0e58d3bcc1a549b1a8d75eca7cd44590ca973a964c4b720a35edf2bc64a79fdb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

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

Download URL msgspec_conf-1.3.0-py3-none-any.whl
Size 10.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6d630dd76364993b0d34c20b99a8b8553f43717127f2dc82b97f4d6ab8d213fa
BLAKE2b-256 checksum
How to use checksums
5225f523296949f88f73df2ad8880fcd20a341d27119a5c8aaf2d7941ee569fd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release history Release notifications | RSS feed

This release

1.3.0 This release

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