Skip to main content

One interface, multiple file formats — async nested storage with dot-path access and atomic writes

Project description

nestio

Async local storage for Python. One interface, multiple file formats.

Stop writing the same boilerplate every time you need local storage.

nestio provides a consistent async API for JSON, TOML, YAML, TOON, and MessagePack — plus a typed .env loader — with:

  • Dot-path access"user.settings.theme"
  • Atomic writes — never leave files half-written
  • Automatic locking — safe concurrent writes out of the box
  • One API across every supported format

Supported formats: JSON • TOML • YAML • TOON • MessagePack

Same API. Same code. Different format.


Why nestio?

Without nestio:

import json

with open("config.json", "r") as f:
    data = json.load(f)

data["user"]["settings"]["theme"] = "dark"

with open("config.json", "w") as f:
    json.dump(data, f, indent=4)

With nestio:

from nestio.files import JSON

db = JSON("config.json")
await db.set("user.settings.theme", "dark")

Same logic. Less boilerplate.


Installation

pip install nestio

PyPI Version Python 3.9+ License MIT


Features

Feature Supported
Async API
Dot-path access
Atomic writes
Automatic locking
JSON
TOML
YAML
MessagePack
TOON
.env loader

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

Quickstart

File storage

import asyncio
from nestio.files import JSON  # swap for TOML, YAML, TOON, or MSGPACK — API is identical

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

from nestio.files import TOML

cfg = TOML("data/config.toml")
await cfg.set("server.host", "localhost")
await cfg.set("server.port", 8080)
await cfg.update("server", {"debug": True})

YAML

from nestio.files import YAML

cfg = YAML("data/config.yaml")
await cfg.set("server.host", "localhost")
await cfg.set("tags", ["web", "api"])
await cfg.append("tags", "async")

MessagePack

from nestio.files import MSGPACK

cache = MSGPACK("data/cache.msgpack")
await cache.set("session.user_id", 1234)
await cache.set("session.permissions", ["read", "write"])
await cache.append("session.permissions", "admin")

TOON

from nestio.files import TOON

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"})

Environment variables

nestio.env gives you a typed wrapper around your .env file — no more scattered os.getenv() calls.

from nestio.env import Env

env = Env(".env")  # raises FileNotFoundError if missing

# Basic access
debug = env.get("DEBUG", default="false")
host  = env["HOST"]          # same as os.getenv("HOST")

# Typed getters
port    = env.get_int("PORT")        # int
ratio   = env.get_float("RATIO")     # float
verbose = env.get_bool("VERBOSE")    # True for "true", "1", "yes", "y", "t"
tags    = env.get_list("TAGS")       # splits on "," by default
tags    = env.get_list("TAGS", sep=" ")  # custom separator

# Required variables — raises KeyError if missing or empty
secret  = env.require("SECRET_KEY")

Given a .env file like:

HOST=localhost
PORT=8080
DEBUG=true
TAGS=web,api,async
SECRET_KEY=supersecret

File storage API

All five methods work the same across every format. All are async and must be awaited.

Import from the submodule or the 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.

Format-specific notes

TOON

TOON (Token-Oriented Object Notation) is a compact, human-readable format designed for LLM input. It combines YAML-style indentation for nested objects with CSV-style rows for uniform arrays — achieving up to 40% fewer tokens than JSON while maintaining full round-trip fidelity.

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 — files are not human-readable, but they're smaller and faster to parse than any text-based format. Best for local caches and high-frequency storage where you don't need to inspect files manually.


Roadmap

  • JSON support
  • TOML support
  • YAML support
  • MessagePack support
  • TOON support
  • Atomic writes
  • Async locking
  • .env loader
  • Benchmarks
  • Automatic format detection
  • More examples

Why I built nestio

I found myself repeatedly writing the same file handling code in async projects:

  • Load a file
  • Navigate nested dictionaries
  • Modify values
  • Save safely
  • Handle concurrent writes

nestio was created to remove that boilerplate and provide a consistent interface across multiple storage formats.


Requirements


License

MIT © MrBaconHat

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

nestio-0.3.1.tar.gz (13.4 kB view details)

Uploaded Source

Built Distribution

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

nestio-0.3.1-py3-none-any.whl (12.2 kB view details)

Uploaded Python 3

File details

Details for the file nestio-0.3.1.tar.gz.

File metadata

  • Download URL: nestio-0.3.1.tar.gz
  • Upload date:
  • Size: 13.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for nestio-0.3.1.tar.gz
Algorithm Hash digest
SHA256 c965845ea18191f385eed4fa7853fca78f85bcd8e8a1492e9b92e1f9f07d8067
MD5 fe19e2531d006d688cb27a80f6ddd2dc
BLAKE2b-256 a2fc3751ec2d4a05060ab493cbf8d5dd0e6be351413a57693f6f954f48ad6c63

See more details on using hashes here.

File details

Details for the file nestio-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: nestio-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 12.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for nestio-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a2cab5a71631336d0060efc92848e3e6c705c4c68a56215b79b98be163742330
MD5 4b7800c37bae50d27196e3d8bd0e0b78
BLAKE2b-256 a40351411417fd8ba9b1dcd2140b9cc5291db82d5c087f23d9a9f615a3406daa

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