Elara DB is an easy to use, lightweight NoSQL database that can also be used as a fast in-memory cache.

Overview

PyPIbadge Releasebadge Licensebadge GitHub Repo stars

Elara DB is an easy to use, lightweight NoSQL database written for python that can also be used as a fast in-memory cache for JSON-serializable data. Includes various methods and features to manipulate data structures in-memory, protect database files and export data.

$ pip install elara
  • Latest - v0.5.1

Go through the release notes for details on upgrades as breaking changes might happen between version upgrades while Elara is in beta.

Elara DB has official support for python 3.6, 3.7, 3.8 and 3.9.


Key Features :

  • Offers three modes of execution - normal, cache and secure - secure mode generates a key file and encrypts the database for additional security.
  • Manipulate data structures in-memory.
  • Fast and flexible in-memory caching mechanism.
  • Choose between manual commits after performing operations in-memory or automatically commit every change into the storage.
  • Includes methods to export certain keys from the database or the entire storage.
  • Incorporates checksums to verify database file integrity.

Table of Contents :


Installation

From pypi :

$ pip install elara

OR,

Clone the repository and install the dependencies :

$ pip install -r requirements.py
$ python -m unittest -v     # Run tests

License

Copyright (c) 2021, Saurabh Pujari
All rights reserved.

This source code is licensed under the BSD-style license found in the LICENSE file in the root directory of this source tree.

Go back to the table of contents

Fundamentals

Basic usage :

You can choose between normally transacting data from the database or you can protect your database with a key.

import elara

# exe_secure() encrypts the db file
db = elara.exe_secure("new.db", True, "newdb.key")

db.set("name", "Elara")

print(db.get("name"))
# Elara
  • exe_secure(db_file_path, commit=False, key_file_path="edb.key") - Loads the contents of the encrypted database (using the key file) into the program memory or generates a new key file (default - edb.key) if it doesn't exist in the given path and it encrypts/decrypts the database file.

Using exe_secure() without a key file or without the correct key file corresponding to the database will result in errors. Database files are verified with checksums to maintain integrity. Key files and DB files can be included inside the .gitignore to ensure they're not pushed into an upstream repository.

  • commit - this argument defaults to False ie. you will have to manually call the commit() method to write the in-memory changes into the database. If set to True, changes will be written into the file after every operation.
import elara

db = elara.exe("new.db", "newdb.key") # commit=False  

db.set("num", 20)

print(db.get("num"))
# 20

db.commit() # Writes in-memory changes into the file
  • exe(db_file_path, commit=False) - Loads the contents of the database into the program memory or generates a new database file if it doesn't exist in the given path. The database file is NOT protected and can be accessed without a key.
import elara as elara

db = elara.exe("new.db", True)

db.set("name", "Elara")

print(db.get("name"))
# Elara

All the following operations are methods that can be applied to the instance returned from exe() or exe_secure() (or exe_cache(), as shown in the Cache section). These operations manipulate/analyse data in-memory after the Data is loaded from the file. Set the commit argument to True else manually use the commit() method to sync in-memory data with the database file.

  • get(key) - returns the corresponding value from the db or None
  • set(key, value) - returns True or an Exception. The key has to be a String.
  • rem(key) - deletes the key-value pair if it exists.
  • remkeys(keys=[]) - deletes all the key-value pairs from the list of keys given, if the key exists.
  • incr(key, val=1) - increments the value (has to be an int or a float) present at the given key with the val parameter (default 1, int or a float). For float operations it rounds the result to 3 decimal points.
  • decr(key, val=1) - decrements the value (has to be an int or a float) present at the given key with the val parameter (default 1, int or a float). For float operations it rounds the result to 3 decimal points.
  • clear() - clears the database data currently stored in-memory.
  • exists(key) - returns True if the key exists.
  • commit() - write in-memory changes into the database file.
  • getset(key, value) - Sets the new value and returns the old value for that key or returns False.
  • getkeys() - returns the list of keys in the database with. The list is ordered with the most recently accessed keys starting from index 0.
  • numkeys() - returns the number of keys in the database.
  • retkey() - returns the Key used to encrypt/decrypt the db file; returns None if the file is unprotected.
  • retmem() - returns all the in-memory db contents.
  • retdb() - returns all the db file contents.
import elara

db = elara.exe("new.db")

db.set("num1", 20)

# ("num1", 20) is written into the file db
db.commit()

db.set("num2", 30)

print(db.retmem())
# {'num1': 20, 'num2': 30}

print(db.retdb())
# {'num1': 20}

Note - retmem() and retdb() will return the same value if commit is set to True or if the commit() method is used before calling retdb()

Go back to the table of contents

Cache:

Elara can also be used as a fast in-memory cache.

  • exe_cache(path, cache_param=None, commit=False) - This function creates an instance with the settings defined in cache_param. Here commit defaults to False to allow for in-memory manipulation.
    • cache_param - This argument is a dictionary that can define of 3 optional parameters.
      • max_age - This is the default amount of time in seconds that any key stored (eg. using set()) into the cache will last for before being evicted. Defaults to None which indicates it will stay in memory for as long as the instance is running.
      • max_size - This is the maximum number of keys that will be stored in the cache. For every key addition request after the max_size limit has been reached, an automatic cull() is called to evict some keys based on cull_freq. Defaults to positive infinity as limited by the device.
      • cull_freq - This is the default amount of keys, in percentage, that will be evicted based on the LRU eviction strategy when the cache reaches its max_size. 0 <= cull_freq <=100. Defaults to 20 ie. 20% of all keys will be deleted based on the LRU eviction strategy.

The LRU eviction searches for, and deletes, expired keys lazily after every function call.

Note - In exe_cache, the path parameter is a required argument in case you need to commit your cache contents into the database.

  • set(key, value, max_age=None) - The set() function takes another argument, max_age, that is set to None by default ie. the key-value pair will follow the default max_age set in cache_param OR they stay never get evicted if cache_param is not defined. The max_age param in set() allows for more granular control over cache item expiry. max_age should be an integer greater than 0. max_age = "i" indicates the item will not be removed from memory (overrides default max_age or max_age defined in cache_param)

Similarly, lnew(key, max_age=None), hnew(key, max_age=None) (read the API reference) and getset(key, value, max_age=None), all accept the optional max_age argument.

import elara
import time

cache_param = {
    "max_age": 900,
    "max_size": 4,
    "cull_freq": 25
}

cache = elara.exe_cache("new.db", cache_param)

cache.set("key1", "This one will be evicted in 900 seconds")
cache.set("key2", "This one will not be evicted", "i") # 'i' signifies it will never be evicted 
cache.set("key3", "This one will be evicted in 50 seconds", 50)

print(cache.getkeys())
# ["key3", "key2", "key1"]

time.sleep(50)

print(cache.getkeys())
# ["key2", "key1"]

cache.set("key3", 5)
cache.set("key4", 10)

print(cache.getkeys())
# ["key4", "key3", "key2", "key1"]

cache.set("key1", 7, "i")    # overwrite "key1" to never expire

print(cache.getkeys())
# ["key1", "key4", "key3", "key2"]

print(cache.get("key1"))
# 7

cache.set("key5", 20)   # Automatic culling when max_size is reached

print(cache.getkeys())
# ["key5", "key1", "key4", "key3"]

Elara also allows for manual culling of cached items :

  • cull(percentage) - percentage (0 <= percentage <= 100) defines the percentage of Key-Value pairs to be deleted, with the Least recently accessed keys being deleted first. Elara maintains a simple LRU cache to track key access.
import elara

"""
Without the cache_param argument, all defauls will be set

Passing any one of the values is also valid as mentioned above 
cache = elara.exe_cache("new.db", {"max_size": 100}))

"""

cache = elara.exe_cache("new.db")

cache.set("num1", 10)
cache.set("num2", 20)
cache.set("num3", 30)
cache.set("num4", 40)

if cache.exists("num1"):
    print(cache.get("num1"))
    # 10 
    
print(cache.retmem())
# {'num1': 10, 'num2': 20, 'num3': 30, 'num4': 40}

# most recently accessed keys come first
print(cache.getkeys())
# ['num1', 'num4', 'num3', 'num2']

# delete 25% of the stale keys (follows LRU)
cache.cull(25) 

# most recently accessed keys come first
print(cache.getkeys())
# ['num1', 'num4', 'num3']

Go back to the table of contents

Serialization and Storage:

Elara supports basic python datatypes (int, str, dict, list etc.). However, objects (simple and complex) can be stored and retrieved using get, set and other functions that apply to them as long as they are in-memory and not persisted in the file, as that would lead to serialization errors.

import elara

cache = elara.exe("new.db") # commit = False by default

class MyObj():
    def __init__(self, num):
        self.num = num

obj = MyObj(19)

cache.set("obj", obj)

print(cache.get("obj").num)
# 19 
  • To persist a simple object as a dictionary, use the __dict__ attribute.

  • Elara uses checksums and a file version flag to verify database file integrity.

Go back to the table of contents


API reference

Strings :

  • mget(keys) - takes a list of keys as an argument and returns a list with all the corresponding values IF they exist; returns an empty list otherwise.
  • mset(dict) - takes a dictionary of key-value pairs as an argument and calls the set(key, value) method for each pair. Keys have to be a String.
  • setnx(key, value) - Sets the key-value if the key does not exist and returns True; returns False otherwise.
  • msetnx(dict) - takes a dictionary of key-value pairs as an argument and calls the setnx(key, value) method for each pair. Keys have to be a string.
  • slen(key) - returns the length of the string value if the key exists; returns -1 otherwise.
  • append(key, data) - Append the data (String) to an existing string value; returns False if it fails.

Go back to the table of contents

Lists :

  • lnew(key) - Initialises an empty list for the given key and returns True or an Exception; key has to be a string.
  • lpush(key, value) - Appends the given value to the list and returns True; returns False if the key does not exist.
  • lpop(key) - Pops and returns the last element of the list if it exists; returns False otherwise. Index of the element can be passed to delete a specific element using lpop(key, pos). pos defaults to -1 (last element of the list).
  • lrem(key, value) - remove a value from the list. Returns True on success and False otherwise.
  • llen(key) - returns length of the list if the key exists; returns -1 otherwise.
  • lindex(key, index) - takes the index as an argument and returns the value if the key and list exist; returns False otherwise.
  • lrange(key, start, end) - takes start and end index as arguments and returns the list within the given range. Value at end not included. Returns empty list if start/end are invalid.
  • lextend(key, new_list) - Extend the list with new_list if the key exists. Returns True or False if the key does not exist.
  • lexists(key, value) - returns True if the value is present in the list; returns False otherwise.
  • lappend(key, pos, value) - appends value to the existing data at index pos using the + operator. Returns True or False.
import elara

db = elara.exe('new.db', True)

db.lnew('newlist')
db.lpush('newlist', 3)
db.lpush('newlist', 4)
db.lpush('newlist', 5)

print(db.lpop('newlist'))
# 5

print(db.lindex('newlist', 0))
# 3

new_list = [6, 7, 8, 9]
db.lextend('newlist', new_list)
print(db.get('newlist'))
# [3, 4, 6, 7, 8, 9]

Go back to the table of contents

Hashtable/Dictionary :

  • hnew(key) - Initialises an empty dictionary for the given key and returns True or an Exception; key has to be a string.
  • hadd(key, dict_key, value) - Assigns a value to a dictionary key and returns True; returns False if the dictionary doesn't exist.
  • haddt(key, tuple) - Add a new key-value tuple into the dictionary. Returns True if the dictionary exists; returns False otherwise.
  • hget(key, dict_key) - Returns the value from the dictionary; returns False if the dictionary doesn't exist.
  • hpop(key, dict_key) - Deletes the given key-value pair from the dictionary and returns the deleted value; returns False if the dictionary doesn't exist.
  • hkeys(key) - returns all the Keys present in the dictionary.
  • hvals(key) - returns all the values present in the dictionary.
  • hmerge(key, dict) - updates (dict.update()) the dictionary pointed by the key with the new dictionary dict passed as an argument.

Go back to the table of contents

Update key and Secure DB :

  • updatekey(key_path) - This method works for instances produced by exe_secure(). It updates the key in the key file path and re-encyrpts the database with the new key. If the file doesn't exist, the method generates a new file with a key and uses that to encrypt the database file.
import elara 

# exe_secure() encrypts the db file
db = elara.exe_secure("new.db", True, "newdb.key")
db.set("name", "Elara")

print(db.get("name"))
# Elara

db.updatekey('newkeypath.key')

# Regular program flow doesn't get affected by key update
print(db.get("name"))   
# Elara

However, the next time you run the program, you have to pass the new updated key (newkeypath.key in this case) to avoid errors.

  • securedb(key_path) - Calls updatekey(key_path) for instances which are already protected with a key. For an unprotected instance of exe(), it generates a new key in the given key_path and encrypts the database file. This db file can henceforth only be used with the exe_secure() function.

Go back to the table of contents

Export data :

  • exportdb(export_path, sort=True) - Copies the entire content of the database file into the specified export file path using json.dump(). To prevent sorting of Keys, use exportdb(export_path, False)

  • exportmem(export_path, sort=True) - Copies the current database contents stored in-memory into the specified export file path using json.dump(). To prevent sorting of Keys, use exportmem(export_path, False).

  • exportkeys(export_path, keys = [], sort=True) - Takes a list of keys as an argument and exports those specific keys from the in-memory data to the given export file path.

import elara

db = elara.exe('new.db', False)
db.set("one", 100)
db.set("two", 200)
db.commit()
db.set("three", 300)

db.exportdb('exportdb.txt')

db.exportmem('exportmem.txt')
db.exportkeys('exportkeys.txt', keys = ['one', 'three'])

"""
# exportdb.txt
{
    "one": 100,
    "two": 200
}

# exportmem.txt
{
    "one": 100,
    "three": 300,
    "two": 200
    
}

# exportkeys.txt
{
    "one": 100,
    "three": 300
}
"""

Go back to the table of contents


Tests

Run this command inside the base directory to execute all tests inside the test folder:

$ python -m unittest -v

Dependencies

  • cryptography
  • msgpack

Release notes

  • Latest - v0.5.x
    • v0.5.1 - No breaking changes
    • v0.5.0

v0.5.x comes with an internal re-architecture that allows for much better caching and granular control on item expiry. No breaking changes from v0.4.x.

v0.4.x moves away from the json-based (dump, load) storage approach used in earlier versions, instead storing it as bytes and has support for checksums and database file version flags for added security.

v0.3.x uses utf-8 encoding while storing data.

v0.2.x and earlier used a mix of ascii and base64 encoding.

To safeguard data, its better to export all existing data from any existing database file before upgrading Elara. (use exportdb(export_path)).

View Elara's release history.

Go back to the table of contents


Contributors :

Original author and maintainer - Saurabh Pujari
Logo design - Jonah Eapen

Go back to the table of contents

Comments
  • Your encrypted storage can benefit from a binary encoding scheme

    Your encrypted storage can benefit from a binary encoding scheme

    I would like to suggest at elara.elarautil.Util.encryptAndStore you use a binary encoding scheme like msgpack or to dump as json and encode as utf-8 and that will give you bytes to encrypt and then store while reliably being able to get the data back, the way it is right now you encode as ASCII (not compatible with utf-8 that might exist in the db) causing possible data loss, and saving the encoding to base64 round trip.

    These are quite trivial changes, but I can help you implement them if you need help. edit: also a hash/checksum to both encrypted and not ways might be a good idea.

    enhancement 
    opened by neonimp 11
  • Move storage format over to binary, this change allows the addition of:

    Move storage format over to binary, this change allows the addition of:

    Checksums, File format versioning and feature flags i.e: encryption, this change also makes the db files way more compact.

    Other changes: tighten except clauses in Util class, change some names to snake case

    This is a more involved change so take the time to review it and consider it's impacts and ask away any doubts you may have about it

    closes #10

    enhancement 
    opened by neonimp 10
  • Keyfile Path only in root directory

    Keyfile Path only in root directory

    Hi,

    I've tried to create a new database in secure mode and wanted to move both files into a subdirectory db = elara.exe_secure("path/new.db", "path/newdb.key") While the db file was created in the subdirectory, the keyfile wasn't accepted as argument which resulted in 'edb.key' being created in the root directory.

    It would be great if this could be fixed. (If this is on purpose, please share the reasons for this decision. Curious!)

    Thank you for this great app!

    opened by schmurian 3
  • Atomic file read/writes

    Atomic file read/writes

    Description :

    • Ideally, files writes and reads should be fully atomic to avoid file corruption
    • The Safer library is handy for this usecase.
      • This library essentially directs all file writes to a memory stream (or temp file) before writing into the actual file to ensure atomicity.

    Comment any other suggestions or alternatives.

    enhancement 
    opened by saurabh0719 2
  • Add checksums to verify file integrity

    Add checksums to verify file integrity

    I think its better to use crc (as suggested by @DarthUdp) compared to sha256 as its more computationally lightweight which i feel is suitable for this purpose.

    enhancement 
    opened by saurabh0719 2
  • v0.5.4 release

    v0.5.4 release

    Description :

    • Elara can now hold keys of any type, not just strings!
    • Added Time to live functions : ttl and ttls
    • Added a persist function to remove expiry
    • Added a randomkey function to retrieve any random key from the DB
    • Added linsert

    Updated docs, bumped version to v0.5.4.


    opened by saurabh0719 0
  • V0.5.1 release

    V0.5.1 release

    Description :

    • InvalidCacheParams exception class added
    • Fixed bug in rem(key) to raise an Exception on Status.NOTFOUND
    • Updated tests to account for exceptions
    enhancement bug fix 
    opened by saurabh0719 0
  • Re-architect the cache mechanism to track max_size, max_age and cull_freq

    Re-architect the cache mechanism to track max_size, max_age and cull_freq

    Description :

    • Update the architecture to maintain an access list (using the LRU) to set default max_age of the cache and track max_age of each individual key-value pair.
    • Also track the max_size of the cache and allow the user to set the default cull_freq (cull frequency - percentage of key-value pairs to be deleted when cache reaches it's max_size)
    • Update cache (resolve max_age and LRU) after every operation. (set, get etc. etc.)
    • Allow set() to take individual max_age for key-value pairs.
    enhancement 
    opened by saurabh0719 0
  • v0.2.0

    v0.2.0

    Description -

    Updates for v0.2.0 beta

    • Added a simple LRU object to track the most recently accessed keys
    • Added a cull() function to delete stale keys
    • getkeys() returns keys in order of most recently accessed.
    • Test coverage for LRU
    • fixed some minor bugs
    • Updated README.md
    • Added README.rst and updated setup.py

    No breaking changes.

    documentation enhancement 
    opened by saurabh0719 0
Releases(v0.5.4)
  • v0.5.4(Aug 26, 2021)

    Description :

    • Elara can now support keys of any type, not just strings!

    • New ttl and ttls functions to retrieve time to live for all cache objects

    • A new persist function to persist a key that previously had an expiry.

    • Added linsert for lists and randomkey to fetch any random key from the database.

    v0.5.4 out now on pypi! No breaking changes.


    Source code(tar.gz)
    Source code(zip)
  • v0.5.3(May 28, 2021)

    Description :

    • fixed json dump bug for export functions
    • db writes are now done in a separate thread
    • getmatch(key) to retrieve all key-value pairs that contain the substr in their key
    • syntax sugar for get, set , rem and exists.

    No breaking changes.

    Source code(tar.gz)
    Source code(zip)
  • v0.5.2(May 25, 2021)

  • v0.5.1(May 18, 2021)

    Description :

    • InvalidCacheParams exception class added for better error messages.
    • Fixed internal bug in rem(key) (raises exception if lru.rem_key(key) returns Status.NOTFOUND)
    • Improved testing for exceptions

    No breaking changes were made from v0.5.0.

    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(May 16, 2021)

    Description :

    • Re-designed the core cache and LRU backend to allow for default max_age, max_size and cull_freq parameters
    • Updated set functions to take max_age param to allow for more granular control
    • Official support for python 3.6. (Elara now supports 3.6 to 3.9)
    • Updated tests and docs

    No breaking changes were made from v0.4.x.

    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(May 14, 2021)

    Description :

    This is a breaking change

    • v0.4.x moves away from utf-8 encoded json based storage and stores data as bytes (uses msgpack dependency)
    • Support for checksums to verify database file integrity
    • updated tests

    Every version after v0.4.0 will use the same storage method thus breaking changes w.r.t this may be unlikely. To safeguard data, its better to export all existing data from any existing database file before upgrading Elara. (use exportdb(export_path)).

    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(May 13, 2021)

    Description :

    • v0.2.1 and earlier used a mix of ascii and base64 encoding. v0.3.0 uses utf-8 encoding instead.
    • To safeguard data, its better to export all existing data from any encrypted file before upgrading. You can then use the securedb() method to re-encrypt it.

    This release is a breaking change from v0.2.x.

    Source code(tar.gz)
    Source code(zip)
  • v0.2.1(May 12, 2021)

  • v0.2.0(May 12, 2021)

    Description -

    • Improved support for in-memory caching :
      • Includes a cull() function that deletes stale keys. Key access is tracked using the LRU principle
      • getkeys() returns all the keys in the database ordered by most recently accessed.
    • Updated tests for LRU
    • Updated documentation for cache

    No breaking changes were made in this update.

    Source code(tar.gz)
    Source code(zip)
Owner
Saurabh Pujari
Software developer
Saurabh Pujari
Migrate data from SQL to NoSQL easily

Migrate data from SQL to NoSQL easily Installation ?? pip install sql2nosql --upgrade Dependencies ?? For the package to work, it first needs "clients

Facundo Padilla 43 Mar 26, 2022
TinyDB is a lightweight document oriented database optimized for your happiness :)

Quick Links Example Code Supported Python Versions Documentation Changelog Extensions Contributing Introduction TinyDB is a lightweight document orien

Markus Siemens 5.6k Dec 30, 2022
LightDB is a lightweight JSON Database for Python

LightDB What is this? LightDB is a lightweight JSON Database for Python that allows you to quickly and easily write data to a file Installing pip3 ins

Stanislaw 14 Oct 1, 2022
A Simple , ☁️ Lightweight , 💪 Efficent JSON based database for 🐍 Python.

A Simple, Lightweight, Efficent JSON based DataBase for Python The current stable version is v1.6.1 pip install pysondb==1.6.1 Support the project her

PysonDB 282 Jan 7, 2023
AWS Tags As A Database is a Python library using AWS Tags as a Key-Value database.

AWS Tags As A Database is a Python library using AWS Tags as a Key-Value database. This database is completely free* ??

Oren Leung 42 Nov 25, 2022
securedb is a fast and lightweight Python framework to easily interact with JSON-based encrypted databases.

securedb securedb is a Python framework that lets you work with encrypted JSON databases. Features: newkey() to generate an encryption key write(key,

Filippo Romani 2 Nov 23, 2022
Manage your sqlite database very easy (like django) ...

Manage your sqlite database very easy (like django) ...

aWolver 1 Feb 9, 2022
MyReplitDB - the most simplistic and easiest wrapper to use for replit's database system.

MyReplitDB is the most simplistic and easiest wrapper to use for replit's database system. Installing You can install it from the PyPI Or y

kayle 4 Jul 3, 2022
Python object-oriented database

ZODB, a Python object-oriented database ZODB provides an object-oriented database for Python that provides a high-degree of transparency. ZODB runs on

Zope 574 Dec 31, 2022
This is a simple graph database in SQLite, inspired by

This is a simple graph database in SQLite, inspired by "SQLite as a document database".

Denis Papathanasiou 1.2k Jan 3, 2023
Python function to extract all the rows from a SQLite database file while iterating over its bytes, such as while downloading it

Python function to extract all the rows from a SQLite database file while iterating over its bytes, such as while downloading it

Department for International Trade 16 Nov 9, 2022
A simple GUI that interacts with a database to keep track of a collection of US coins.

CoinCollectorGUI A simple gui designed to interact with a database. The goal of the database is to make keeping track of collected coins simple. The G

Builder212 1 Nov 9, 2021
Makes google's political ad database actually useful

Making Google's political ad transparency library suck less This is a series of scripts that takes Google's political ad transparency data and makes t

The Guardian 7 Apr 28, 2022
Decentralised graph database management system

Decentralised graph database management system To get started clone the repo, and run the command below. python3 database.py Now, create a new termina

Omkar Patil 2 Apr 18, 2022
A Persistent Embedded Graph Database for Python

Cog - Embedded Graph Database for Python cogdb.io New release: 2.0.5! Installing Cog pip install cogdb Cog is a persistent embedded graph database im

Arun Mahendra 214 Dec 30, 2022
A Painless Simple Way To Create Schema and Do Database Operations Quickly In Python

PainlessDB - Taking Your Pain away to the moon ?? Contribute · Community · Documentation ?? Introduction : PainlessDB is a Python-based free and open-

Aiden Ellis 3 Jul 15, 2022
HTTP graph database built in Python 3

KiwiDB HTTP graph database built in Python 3. Reference Format References are strings in the format: {refIDENTIFIER@GROUP} Authentication Currently, t

JanCraft 1 Dec 17, 2021
Tiny local JSON database for Python.

Pylowdb Simple to use local JSON database ?? # This is pure python, not specific to pylowdb ;) db.data['posts'] = ({ 'id': 1, 'title': 'pylowdb is awe

Hussein Sarea 3 Jan 26, 2022
PathfinderMonsterDatabase - A database of all monsters in Pathfinder 1e, created by parsing aonprd.com

PathfinderMonsterDatabase A database of all monsters in Pathfinder 1e, created by parsing aonprd.com Setup Run the following line to install all requi

Yoni Lerner 11 Jun 12, 2022