Skip to main content

msgspec-based settings loader with env var, .env, YAML and TOML support

Project description

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

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

msgspec_conf-1.3.0.tar.gz (9.4 kB view details)

Uploaded Source

Built Distribution

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

msgspec_conf-1.3.0-py3-none-any.whl (10.0 kB view details)

Uploaded Python 3

File details

Details for the file msgspec_conf-1.3.0.tar.gz.

File metadata

  • Download URL: msgspec_conf-1.3.0.tar.gz
  • Upload date:
  • Size: 9.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for msgspec_conf-1.3.0.tar.gz
Algorithm Hash digest
SHA256 f2070043d2bf8e6a4a9d5b4cc45aa1e15ad7e740ce3bbf89030eb77b287afa5e
MD5 05748c92bb2e6181646e8e48508193f7
BLAKE2b-256 0e58d3bcc1a549b1a8d75eca7cd44590ca973a964c4b720a35edf2bc64a79fdb

See more details on using hashes here.

File details

Details for the file msgspec_conf-1.3.0-py3-none-any.whl.

File metadata

  • Download URL: msgspec_conf-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 10.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for msgspec_conf-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6d630dd76364993b0d34c20b99a8b8553f43717127f2dc82b97f4d6ab8d213fa
MD5 ccd694b644c49ed29f2a8a6676f09b05
BLAKE2b-256 5225f523296949f88f73df2ad8880fcd20a341d27119a5c8aaf2d7941ee569fd

See more details on using hashes here.

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