Skip to main content

Fast JSON-backed persistent dictionary with autosave, TTL, file locking, and namespaced views.

Project description

aiodict

aiodict is a fast, practical, JSON-backed persistent dictionary for Python. It keeps data in memory for speed, flushes to disk atomically, supports TTL, autosave, filesystem locking, transactions, and namespaced views.

It is especially useful for:

  • Telegram bots and admin tools
  • local config storage
  • small to medium automation projects
  • prototypes and MVPs
  • lightweight caches and counters

It is not a replacement for PostgreSQL or Redis in high-scale distributed systems. It is a strong local persistence layer for one machine, with optional multi-process file coordination via a lock file.


Features

  • dict-like API
  • atomic JSON writes using a temporary file and os.replace
  • in-memory cache for fast reads and writes
  • debounced autosave
  • TTL per key
  • explicit flush() and reload()
  • namespaced views
  • transaction-style batching
  • backup file creation (.bak)
  • filesystem lock for safer multi-process access on one machine
  • custom serializer / deserializer hooks

Installation

pip install aiodict

Quick Start

from aiodict import aiodict

# Create or open a JSON-backed store
store = aiodict("db.json")

store["bot_name"] = "My Bot"
store["admin_id"] = 123456789

print(store["bot_name"])
print(store.get("missing", "default value"))

store.flush()
store.close()

Why use aiodict?

aiodict is designed for cases where you want something much more convenient than manually reading and writing JSON, but much simpler than deploying a full database.

Compared to a plain dict, it gives you persistence. Compared to raw JSON file handling, it gives you:

  • better structure
  • autosave
  • TTL
  • locking
  • atomic writes
  • transactions
  • reusable helper methods

Main Class

aiodict

aiodict(
    path="db.json",
    autosave=True,
    autosave_delay=0.35,
    create_backup=True,
    serializer=None,
    deserializer=None,
    indent=None,
    sort_keys=False,
    cleanup_expired_on_load=True,
    lock_timeout=10.0,
    encoding="utf-8",
)

Parameters

path

Path to the JSON file.

autosave

If True, mutations are automatically flushed after autosave_delay.

autosave_delay

Debounce delay in seconds before a pending write is saved.

create_backup

If True, a .bak copy of the previous JSON file is created before each successful write.

serializer

Optional function used to convert unsupported objects into JSON-serializable values.

deserializer

Optional function used to transform values back after loading from disk.

indent

JSON indentation. Use None for compact output and better speed.

sort_keys

If True, saved JSON keys are sorted.

cleanup_expired_on_load

If True, expired keys are removed when the store is loaded.

lock_timeout

How long to wait for the file lock before raising an error.

encoding

File encoding, usually utf-8.


Basic Usage

Set and read values

from aiodict import aiodict

store = aiodict("db.json")
store["name"] = "Abbosjon"
print(store["name"])

Safe reads

lang = store.get("lang", "en")

Delete a key

del store["name"]

Check if a key exists

if store.exists("name"):
    print("exists")

Batch Operations

Update many values

store.update({
    "theme": "dark",
    "timezone": "Asia/Tashkent",
})

Multi-set

store.mset({
    "a": 1,
    "b": 2,
    "c": 3,
})

Multi-get

values = store.mget(["a", "b", "missing"], default=None)
print(values)

Numeric Helpers

Increment a counter

store.incr("visits")
store.incr("visits", 5)

List Helpers

Append

store.append("admins", 123456789)

Append uniquely

store.append("admins", 123456789, unique=True)

Extend

store.extend("admins", [111, 222, 333])

Remove values from a list

store.remove("admins", 222)
store.remove("admins", 111, all_occurrences=True)

TTL Support

Set with TTL

store.set("otp:123", "918273", ttl=60)

Add or update expiration later

store.expire("otp:123", 120)

Read remaining TTL

remaining = store.ttl("otp:123")
print(remaining)

Remove expiration

store.persist("otp:123")

Remove expired keys manually

removed = store.cleanup_expired()
print(f"Removed {removed} expired keys")

Transactions

Use transaction() when you want several writes and one final flush.

with store.transaction():
    store["user:1"] = {"name": "Alice"}
    store["user:2"] = {"name": "Bob"}
    store.incr("user_count", 2)

This is useful when you want cleaner save behavior and fewer disk writes.


Namespaces

Namespaces help organize keys without managing prefixes manually.

users = store.namespace("users")
settings = store.namespace("settings")

users["1001"] = {"name": "Ali"}
settings["language"] = "uz"

print(users["1001"])
print(settings["language"])

Internally, those become:

  • users:1001
  • settings:language

Explicit Persistence

Save now

store.flush()

Reload from disk

store.reload()

Close cleanly

store.close()

Context manager usage

from aiodict import aiodict

with aiodict("db.json") as store:
    store["hello"] = "world"

Serialization Hooks

Custom serializer

from datetime import datetime
from aiodict import aiodict


def serializer(value):
    if isinstance(value, datetime):
        return {"__type__": "datetime", "value": value.isoformat()}
    raise TypeError


def deserializer(value):
    if isinstance(value, dict) and value.get("__type__") == "datetime":
        return datetime.fromisoformat(value["value"])
    return value


store = aiodict("db.json", serializer=serializer, deserializer=deserializer)
store["created_at"] = datetime.now()
store.flush()

Useful Methods

copy()

Returns a plain dict snapshot of the live data.

snapshot = store.copy()

stats()

Returns basic runtime information.

print(store.stats())

Example output:

{
    "keys": 12,
    "ttl_keys": 2,
    "autosave": True,
    "autosave_delay": 0.35,
    "file_path": "db.json",
    "file_size_bytes": 2048,
    "dirty": False,
}

Telegram Bot Example

from aiodict import aiodict

store = aiodict("bot_db.json")
users = store.namespace("users")
stats = store.namespace("stats")


def register_user(user_id: int, full_name: str) -> None:
    users[str(user_id)] = {
        "full_name": full_name,
        "joined_at": "2026-04-05",
    }
    stats.incr("total_users")


def is_registered(user_id: int) -> bool:
    return users.exists(str(user_id))

This works well for small and medium bots on a single machine.


Performance Notes

aiodict is fast because reads and writes primarily hit memory. Disk writes are delayed and batched.

That said, it still writes to one JSON file, so it is best suited for:

  • low to moderate write frequency
  • one machine
  • small to medium datasets

It is not ideal for:

  • massive datasets
  • very high write concurrency
  • distributed multi-server systems
  • analytics-heavy workloads

Recommended Use Cases

Good fit:

  • bot config
  • user settings
  • counters
  • drafts
  • feature flags
  • local admin data
  • temporary OTP or cooldown keys with TTL

Not a good fit:

  • payment ledgers
  • large e-commerce systems
  • multi-node web backends
  • big broadcast job queues at very high scale

Practical Scale Guidance

As a primary store, aiodict is a good fit for:

  • a few hundred to a few thousand actively changing keys
  • small to medium bots
  • local tools and internal systems

For large production systems with many active writes, use PostgreSQL or Redis.

A practical rule:

  • Great: small to medium single-machine projects
  • Acceptable: moderate workloads with controlled write volume
  • Wrong tool: large distributed systems or heavy concurrent write workloads

Development Tips

  • call flush() before shutdown in critical flows
  • use namespaces to keep keys organized
  • prefer compact JSON (indent=None) for best performance
  • use transaction() when doing many writes together
  • use TTL for temporary values like OTPs and cooldowns
  • keep stored values JSON-friendly whenever possible

License

MIT

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

aiodict-1.0.1.tar.gz (15.2 kB view details)

Uploaded Source

Built Distribution

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

aiodict-1.0.1-py3-none-any.whl (10.6 kB view details)

Uploaded Python 3

File details

Details for the file aiodict-1.0.1.tar.gz.

File metadata

  • Download URL: aiodict-1.0.1.tar.gz
  • Upload date:
  • Size: 15.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for aiodict-1.0.1.tar.gz
Algorithm Hash digest
SHA256 c623d09d38fbbf798af35e735d049e92c7374d2ad3443f0583e86c2833caca1d
MD5 9c576b3e473879f2fae3d3eb746eb271
BLAKE2b-256 305dc551c4d5654f06de07d6882cd856ebf5d492683ef670aafaa5066c63e8ff

See more details on using hashes here.

File details

Details for the file aiodict-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: aiodict-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 10.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for aiodict-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 313e5a81d21a9f2a932a948ee869e0542b0cdaa9f06924a9c344d082de9ec83f
MD5 b5ea787930a2cc89864d6c9eb64e8b44
BLAKE2b-256 a99fb42cc6745abb36e038951a72f8c6e41ca3e97ca508f10f60399c96de08c3

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