ProvenPin Install

platformdirs config path + tomlkit TOML config store: working versions (tested 2026-10-15)

Resolve a per-user config file with platformdirs, then read and write it with tomlkit while preserving comments, key order and native types.

passed6 passed · 0 failed2026-10-15 18:10 UTC
Status
passed
Last passed
2026-10-15 18:10 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions3
platformdirs@4.13.0tomlkit@0.15.1pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38783276983

The code

Exactly the files that passed. Your agent can pull them with get_recipe("platformdirs-toml-config-store") (install), or see them on GitHub.

requirements.txt
platformdirs==4.13.0
tomlkit==0.15.1
pytest==9.1.1
config_store.py
from __future__ import annotations

from pathlib import Path

import platformdirs
import tomlkit


class ConfigStore:
    """A TOML config that lives in the platform's per-user config directory."""

    def __init__(
        self,
        appname: str = "provenpin",
        filename: str = "config.toml",
        path: Path | None = None,
    ):
        self.path = (
            Path(path) if path is not None else platformdirs.user_config_path(appname) / filename
        )
        self.path.parent.mkdir(parents=True, exist_ok=True)

    def load(self) -> tomlkit.TOMLDocument:
        if not self.path.exists():
            return tomlkit.document()
        return tomlkit.parse(self.path.read_text(encoding="utf-8"))

    def save(self, doc: tomlkit.TOMLDocument) -> None:
        # Write to a sibling first so a crash never leaves a half-written config.
        tmp = self.path.with_name(f"{self.path.name}.tmp")
        tmp.write_text(tomlkit.dumps(doc), encoding="utf-8")
        tmp.replace(self.path)

    def get(self, key: str, default=None):
        node = self.load()
        for part in key.split("."):
            try:
                node = node[part]
            except (KeyError, TypeError):
                return default
        return node

    def set(self, key: str, value) -> tomlkit.TOMLDocument:
        doc = self.load()
        *parents, leaf = key.split(".")
        node = doc
        for part in parents:
            node = node.setdefault(part, tomlkit.table())
        node[leaf] = value
        self.save(doc)
        return doc
tests/test_config_store.py
import sys

import pytest

from config_store import ConfigStore


def test_missing_file_loads_an_empty_document(tmp_path):
    store = ConfigStore(path=tmp_path / "config.toml")
    doc = store.load()
    assert dict(doc) == {}
    assert store.get("ui.theme", "system") == "system"


def test_set_then_get_round_trips_through_disk(tmp_path):
    store = ConfigStore(path=tmp_path / "config.toml")
    store.set("server.host", "localhost")
    store.set("server.port", 8080)

    reloaded = ConfigStore(path=tmp_path / "config.toml")
    assert reloaded.get("server.host") == "localhost"
    assert reloaded.get("server.port") == 8080


def test_set_preserves_comments_and_key_order(tmp_path):
    path = tmp_path / "config.toml"
    path.write_text(
        "# hand edited, keep this comment\n[ui]\ntheme = 'light' # was dark\n",
        encoding="utf-8",
    )
    store = ConfigStore(path=path)
    store.set("ui.font_size", 13)

    out = path.read_text(encoding="utf-8")
    assert "# hand edited, keep this comment" in out
    assert "theme = 'light' # was dark" in out
    assert out.index("theme") < out.index("font_size")


def test_save_creates_missing_parent_directories(tmp_path):
    path = tmp_path / "does" / "not" / "exist" / "config.toml"
    assert not path.parent.exists()

    ConfigStore(path=path).set("first_run", True)
    assert path.read_text(encoding="utf-8").strip() == "first_run = true"


def test_non_string_values_round_trip(tmp_path):
    store = ConfigStore(path=tmp_path / "config.toml")
    store.set("limits.retries", 3)
    store.set("limits.ratio", 0.5)
    store.set("limits.enabled", True)
    store.set("limits.hosts", ["alpha", "beta"])

    fresh = ConfigStore(path=tmp_path / "config.toml")
    assert fresh.get("limits.retries") == 3
    assert fresh.get("limits.ratio") == 0.5
    assert fresh.get("limits.enabled") is True
    assert fresh.get("limits.hosts") == ["alpha", "beta"]


@pytest.mark.skipif(sys.platform != "linux", reason="XDG is a Linux convention")
def test_xdg_config_home_controls_the_default_location(monkeypatch, tmp_path):
    monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path))
    store = ConfigStore("provenpin")
    assert store.path == tmp_path / "provenpin" / "config.toml"
pytest.ini
[pytest]
pythonpath = .

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.