One interface, multiple file formats — async nested storage with dot-path access and atomic writes
Project description
nestio
One interface, multiple file formats.
nestio gives you a single, consistent API for reading and writing nested data — regardless of file format. Switch between JSON, TOML, YAML, TOON, or MessagePack without changing any of your logic. Just swap the class.
Installation
pip install nestio
Supported formats
| Format | Class | File ext | Best for |
|---|---|---|---|
| JSON | JSON |
.json |
General purpose, APIs, configs |
| TOML | TOML |
.toml |
Configuration files |
| YAML | YAML |
.yaml |
Human-friendly configs |
| TOON | TOON |
.toon |
LLM input — compact, token-efficient |
| MessagePack | MSGPACK |
.msgpack |
Binary, fast, compact serialization |
The idea
Every class shares the exact same five methods. Your logic never changes — only the format does:
# Works identically for JSON, TOML, YAML, TOON, or MSGPACK
await storage.set("user.settings.theme", "dark")
await storage.get("user.settings.theme")
await storage.append("user.scores", 42)
await storage.update("user.settings", {"language": "en"})
await storage.delete("user.settings.theme")
Quickstart
JSON
import asyncio
from nestio.files import JSON
async def main():
db = JSON("data/config.json")
await db.set("user.name", "Alice")
await db.set("user.settings.theme", "dark")
await db.set("user.scores", [])
name = await db.get("user.name") # "Alice"
theme = await db.get("user.settings.theme") # "dark"
await db.append("user.scores", 42)
await db.update("user.settings", {"language": "en"})
await db.delete("user.settings.theme")
asyncio.run(main())
TOML
import asyncio
from nestio.files import TOML
async def main():
cfg = TOML("data/config.toml")
await cfg.set("server.host", "localhost")
await cfg.set("server.port", 8080)
await cfg.update("server", {"debug": True})
host = await cfg.get("server.host") # "localhost"
asyncio.run(main())
YAML
import asyncio
from nestio.files import YAML
async def main():
cfg = YAML("data/config.yaml")
await cfg.set("server.host", "localhost")
await cfg.set("tags", ["web", "api"])
await cfg.append("tags", "async")
tags = await cfg.get("tags") # ["web", "api", "async"]
asyncio.run(main())
TOON
TOON (Token-Oriented Object Notation) is a compact format designed for LLM input — YAML-style nesting, CSV-style rows for uniform arrays, up to 40% fewer tokens than JSON.
import asyncio
from nestio.files import TOON
async def main():
store = TOON("data/context.toon")
await store.set("context.task", "Our favorite hikes")
await store.set("friends", ["ana", "luis", "sam"])
await store.update("context", {"season": "spring_2025"})
task = await store.get("context.task") # "Our favorite hikes"
asyncio.run(main())
A .toon file produced by nestio looks like this:
context:
task: Our favorite hikes
season: spring_2025
friends[3]: ana,luis,sam
hikes[3]{id,name,distanceKm,wasSunny}:
1,Blue Lake Trail,7.5,true
2,Ridge Overlook,9.2,false
3,Wildflower Loop,5.1,true
MessagePack
MessagePack is a binary format — smaller and faster to read/write than text-based formats, great for local caches and high-frequency storage.
import asyncio
from nestio.files import MSGPACK
async def main():
cache = MSGPACK("data/cache.msgpack")
await cache.set("session.user_id", 1234)
await cache.set("session.token", "abc123")
await cache.set("session.permissions", ["read", "write"])
user_id = await cache.get("session.user_id") # 1234
await cache.append("session.permissions", "admin")
await cache.delete("session.token")
asyncio.run(main())
API
All five methods work the same across every format. All are async and must be awaited.
Import from the submodule or top level — both work:
from nestio.files import JSON # explicit
from nestio import JSON # shortcut
get(path, default=None)
Returns the value at the dot-path, or default if it doesn't exist.
value = await db.get("server.host", default="localhost")
set(path, value)
Sets the value at the dot-path. Creates intermediate dicts as needed.
await db.set("server.port", 8080)
delete(path)
Removes the key at the dot-path. Does nothing if the key doesn't exist.
await db.delete("server.port")
append(path, value)
Appends a value to a list at the dot-path. Creates the list if it doesn't exist yet.
await db.append("logs", {"level": "info", "msg": "started"})
update(path, new_data)
Deep-merges a dict into the value at the dot-path. Existing keys not in new_data are preserved.
await db.update("config", {"retries": 3, "timeout": 30})
How it works
- Dot-path access — keys like
"a.b.c"resolve through nested dicts automatically. - Atomic writes — every save writes to a temp file first, then uses
os.replace()to swap it in. Your file is never half-written. - Per-key locking — concurrent writes to the same path are serialized with
asyncio.Lock, while independent paths write in parallel. Locks clean up automatically after a TTL.
Requirements
- Python 3.9+
aiofilespyyaml(YAML)tomli(TOML, Python < 3.11 only)tomli-w(TOML)toons(TOON)msgpack(MessagePack)
License
MIT © MrBaconHat
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nestio-0.2.1.tar.gz.
File metadata
- Download URL: nestio-0.2.1.tar.gz
- Upload date:
- Size: 10.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0578ee1fb315cb9cbe9d3bd5d142045e67ed6d344ad7a0a3cc4f0d388dfbda79
|
|
| MD5 |
27f1ac82c1c2541ee3314df9ec476f8d
|
|
| BLAKE2b-256 |
4c0db93b517afc9455dfa0dbf86fc125cffdf064dc274718eb2132bf3a03200c
|
File details
Details for the file nestio-0.2.1-py3-none-any.whl.
File metadata
- Download URL: nestio-0.2.1-py3-none-any.whl
- Upload date:
- Size: 10.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d37ee8a762c2f0bbc5a5a24d1335216ede9b4b0b1bb59acc62c7c742ac65fcb
|
|
| MD5 |
f32c54d95a7e4622709006706d9e570f
|
|
| BLAKE2b-256 |
56321f8b6d2285a62b1199010663b6b15f04ec8566c73876d10167163815f0ec
|