Package: elara
ElaraElara DB is an easy to use, lightweight key-value 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.View theGithub repositoryand theofficial docs.$pipinstallelaraGo 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+.Key FeaturesOffers three modes of execution - normal, cache and secure - secure mode generates a key file and encrypts the database for additional security.Manipulate data structures such as strings, lists and dictionaries.Fast and flexible in-memory LRUcaching mechanism.Supports keys of any type, not just strings!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.InstallationFrompypi:$pipinstallelaraOR,Clone the repository and install the dependencies :$pipinstall-rrequirements.py$python-munittest-v# Run testsLicenseCopyright (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.FundamentalsBasic usage :importelaradb=elara.exe("new.db")db.set("name","Elara")print(db.get("name"))# ElaraYou can choose between normally transacting data from the database
or you can protect your database with a key.importelara# exe_secure() encrypts the db filedb=elara.exe_secure("new.db",True,"newdb.key")# OR# db = elara.exe_secure(path="path/new.db", commitdb=True, key_path="path/edb.key")db.set("name","Elara")print(db.get("name"))# Elaraexe_secure(path, commitdb=False,key_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.Usingexe_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.gitignoreto ensure they’re not
pushed into an upstream repository.commitdb- this argument defaults toFalseie. you will
have to manually call thecommit()method to write the in-memory
changes into the database. If set toTrue, changes will be
written into the file after every operation.importelaradb=elara.exe_secure(path="path/new.db",key_path="path/edb.key")# commit=Falsedb.set("num",20)print(db.get("num"))# 20db.commit()# Writes in-memory changes into the fileexe(path, commitdb=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.importelaradb=elara.exe("new.db",True)db.set("name","Elara")print(db.get("name"))# ElaraAll the following operations are methods that can be applied to the
instance returned fromexe()orexe_secure(). These operations
manipulate/analyse data in-memory after the Data is loaded from the
file. Set thecommitargument toTrueelse manually use thecommit()method to sync in-memory data with the database file.get(key)- returns the corresponding value from the db orNoneset(key, value)- returnsTrueor an Exception. Thekeycan be any data type that is supported by python dictionaries (int, float, string etc.).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.clear()- clears the database data currently stored in-memory.exists(key)- returnsTrueif 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 returnsFalse.getkeys()- returns the list of keys in the database with. The
list is ordered with themost recently accessedkeys starting
from index 0.getmatch(match)- Takes thematchargument and returns a
Dictionary of key-value pairs of which the keys containmatchas a sub string.numkeys()- returns the number of keys in the database.retkey()- returns the Key used to encrypt/decrypt the db file;
returnsNoneif the file is unprotected.retmem()- returns all the in-memory db contents.retdb()- returns all the db file contents.importelaradb=elara.exe("new.db")db.set("num1",20)# ("num1", 20) is written into the file dbdb.commit()db.set("num2",30)print(db.retmem())# {'num1': 20, 'num2': 30}print(db.retdb())# {'num1': 20}Note -retmem()andretdb()will return the same value ifcommitis set toTrueor if thecommit()method is used
before callingretdb()Elara adds some syntax sugar for get(), set() and rem() :importelaradb=elara.exe("new.db")db["key"]="value"print(db["key"])# valuedelself.db["key"]print(db.retmem())# {}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 incache_param.
Herecommitdefaults toFalseto allow for in-memory manipulation.cache_param- This argument is a dictionary that can define of 3optionalparameters.max_age- This is the default amount of time insecondsthat any key stored (eg. usingset()) into the cache will last for before being evicted.
Defaults toNonewhich 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 themax_sizelimit has been reached, an automaticcull()is called to evict some keys based oncull_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 itsmax_size. 0 <=cull_freq<=100.
Defaults to20ie. 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 - Inexe_cache, thepathparameter is a required argument in case you need to commit your cache contents into the database.set(key, value, max_age=None)- Theset()function takes another argument,max_age, that is set toNoneby default ie. the key-value pair will follow the defaultmax_ageset incache_paramOR they stay never get evicted ifcache_paramis not defined. Themax_ageparam inset()allows for more granular control over cache item expiry.max_ageshould be an integer greater than 0.max_age = "i"indicates the item will not be removed from memory (overrides defaultmax_ageormax_agedefined incache_param)Similarly,lnew(key, max_age=None),hnew(key, max_age=None)(read the API reference) andgetset(key, value, max_age=None), all accept the optionalmax_ageargument.importelaracache_param={"max_age":900,"max_size":4,"cull_freq":25}cache=elara.exe_cache(path="new.db",cache_param=cache_param)# OR# 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 evictedcache.set("key3","This one will be evicted in 100 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 expireprint(cache.getkeys())# ["key1", "key4", "key3", "key2"]print(cache.get("key1"))# 7cache.set("key5",20)# Automatic culling when max_size is reachedprint(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 theLeast recently accessedkeys being deleted first. Elara maintains a
simple LRU list to track key access.importelara"""
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)ifcache.exists("num1"):print(cache.get("num1"))# 10print(cache.retmem())# {'num1': 10, 'num2': 20, 'num3': 30, 'num4': 40}# most recently accessed keys come firstprint(cache.getkeys())# ['num1', 'num4', 'num3', 'num2']# delete 25% of the stale keys (follows LRU)cache.cull(25)# most recently accessed keys come firstprint(cache.getkeys())# ['num1', 'num4', 'num3']ttl(key)- returns the time to live of the key as adatetime.timedelta()object or returnsNoneif it does not have an expiration value.
ReturnsFalseif the key is missing.ttls(key)- returns the time to live of the key inseconds. ReturnsFalseif the key is missing.persist(key)- sets the expiry value of the key toNone, hence persisting it. ReturnsFalseif the key is missing.Serialization and Storage :Elara supports basic python datatypes (int,str,dict,listetc.).
However, objects (simple and complex) can be stored and retrieved usingget,setand other functions that apply to them
as long as they arein-memoryandnot persisted in the file, as that would lead to serialization errors.importelaracache=elara.exe("new.db")# commit = False by defaultclassMyObj():def__init__(self,num):self.num=numobj=MyObj(19)cache.set("obj",obj)print(cache.get("obj").num)# 19To persist a simple object as a dictionary, use the__dict__attribute.Elara uses checksums and a file version flag to verify database file integrity.All database writes are atomic (uses the safer library). Database writes are done in a separate thread along with a thread lock.API referenceStrings :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 theset(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 returnsTrue; returnsFalseotherwise.msetnx(dict)- takes a dictionary of key-value pairs as an
argument and calls thesetnx(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-1otherwise.append(key, data)- Append the data (String) to an existing
string value; returnsFalseif it fails.Lists :lnew(key)- Initialises an empty list for the given key and
returnsTrueor an Exception; key can be any data type that is supported by python dictionaries (int, float, string etc.).lpush(key, value)- Appends the given value to the list and
returnsTrue; returnsFalseif the key does not exist.lpop(key)- Pops and returns the last element of the list if it
exists; returnsFalseotherwise. Index of the element can be
passed to delete a specific element usinglpop(key, pos).posdefaults to-1(last element of the list).lrem(key, value)- remove a value from the list. ReturnsTrueon success andFalseotherwise.llen(key)- returns length of the list if the key exists; returns-1otherwise.lindex(key, index)- takes the index as an argument and returns
the value if the key and list exist; returnsFalseotherwise.lrange(key, start, end)- takesstartandendindex as
arguments and returns the list within the given range. Value atendnot included. Returns empty list if start/end are invalid.lextend(key, new_list)- Extend the list withnew_listif the
key exists. ReturnsTrueorFalseif the key does not exist.lexists(key, value)- returnsTrueif the value is present in
the list; returnsFalseotherwise.lappend(key, pos, value)- appendsvalueto the existing data
at indexposusing the+operator. ReturnsTrueorFalse.importelaradb=elara.exe(path='new.db',commitdb=True)db.lnew('newlist')db.lpush('newlist',3)db.lpush('newlist',4)db.lpush('newlist',5)print(db.lpop('newlist'))# 5print(db.lindex('newlist',0))# 3new_list=[6,7,8,9]db.lextend('newlist',new_list)print(db.get('newlist'))# [3, 4, 6, 7, 8, 9]Hashtable/Dictionary :hnew(key)- Initialises an empty dictionary for the given key and
returnsTrueor an Exception; key can be any data type that is supported by python dictionaries (int, float, string etc.).hadd(key, dict_key, value)- Assigns a value to a dictionary key
and returnsTrue; returnsFalseif the dictionary doesn’t
exist.haddt(key, tuple)- Add a new key-value tuple into the
dictionary. ReturnsTrueif the dictionary exists; returnsFalseotherwise.hget(key, dict_key)- Returns the value from the dictionary;
returnsFalseif the dictionary doesn’t exist.hpop(key, dict_key)- Deletes the given key-value pair from the
dictionary and returns the deleted value; returnsFalseif 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 dictionarydictpassed as an
argument.Update key and Secure DB :updatekey(key_path)- This method works for instances produced byexe_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.importelara# exe_secure() encrypts the db filedb=elara.exe_secure("new.db",True,"newdb.key")db.set("name","Elara")print(db.get("name"))# Elaradb.updatekey('newkeypath.key')# Regular program flow doesn't get affected by key updateprint(db.get("name"))# ElaraHowever, the next time you run the program, you have to pass the new
updated key (newkeypath.keyin this case) to avoid errors.securedb(key_path)- Callsupdatekey(key_path)for instances
which are already protected with a key. For an unprotected instance
ofexe(), it generates a new key in the given key_path and
encrypts the database file. This db file can henceforth only be used
with theexe_secure()function.Export data :exportdb(export_path, sort=True)- Copies the entire content of
the database file into the specified export file path usingjson.dump(). To prevent sorting of Keys, useexportdb(export_path, False)exportmem(export_path, sort=True)- Copies the current database
contents stored in-memory into the specified export file path usingjson.dump(). To prevent sorting of Keys, useexportmem(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.importelaradb=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
}
"""TestsRun this command inside the base directory to execute all tests inside
thetestfolder:$python-munittest-vDependenciescryptographymsgpacksaferReleases notesLatest -v0.5.xv0.5.4- No breaking changesv0.5.3v0.5.2v0.5.1v0.5.0v0.5.xcomes with an internal re-architecture that allows for much better caching and granular control on item expiry.
No breaking changes fromv0.4.xv0.4.xmoves 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.2.1and earlier used a mix ofasciiandbase64encoding.v0.3.0usesutf-8instead.To safeguard data, its better to export all existing data from any existing database file before upgrading Elara.
(usingexportdb(export_path))View Elara’s detailed release historyhere.ContributorsOriginal author and maintainer -Saurabh Pujari.Logo design -Jonah Eapen.Open source contributors :DarthUdp.AdityaKotwal100.