Validate Your Settings
How do I validate my own spoc.toml tables? SPOC claims exactly one
top-level table — [spoc] — and hands every other table back untouched on
framework.config.tables, already parsed
(The Settings File). Validating what's
in yours is your job, with any schema tool you like: the table is a plain
dict, so any validator that accepts one fits the seam.
The worked example uses pydantic
(pip install pydantic) — a plain model over the already-parsed table. Not
pydantic-settings: SPOC has already done the file reading.
[spoc]
mode = "development"
[myapp] # yours: any keys, any shapes
api_url = "https://api.example.com"
retries = 3
Put the model and its check next to the declaration, not in an entry point.
on_ready fires inside every start(), so a bad table refuses the boot
itself — whichever process booted, HTTP server, worker, or one-off script —
rather than surfacing at the first request that reads the value:
"""The declaration, and the validation that guards every boot of it."""
import spoc
from pydantic import BaseModel, HttpUrl
class MyAppSettings(BaseModel):
api_url: HttpUrl
retries: int = 3
framework = spoc.Framework()
@framework.on_ready
def _settings_are_valid(registry):
"""Runs inside start(), after settings load and before the boot returns."""
MyAppSettings.model_validate(framework.config.tables["myapp"])
from pathlib import Path
from framework import MyAppSettings, framework
BASE_DIR = Path(__file__).resolve().parent
framework.start(BASE_DIR) # a bad [myapp] table would have refused right here
settings = MyAppSettings.model_validate(framework.config.tables["myapp"])
print(settings.retries) # 3 — typed, defaulted, and validated at the boundary
What the kernel already checks
A typo inside [spoc] refuses to boot, loudly, before your code runs — and
spoc check reports the same refusal in CI without booting anything
(The Command Line). Your own tables are outside both on
purpose: the kernel neither validates nor reads them, and the model above is
what makes them fail just as loudly.
The same seam covers the environment. framework.config.environment is the
active mode's env table as a plain dict[str, Any], so the
model-at-the-boundary pattern fits it unchanged — one more
model_validate(framework.config.environment) in the same callback.
Next: test your app.