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.Structclasses - Load values from the current environment, optional
.envfiles, 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
*_FILEvariables - 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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| msgspec_conf-1.4.0.tar.gz | 10.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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.