Universal Python binding for the LMDB 'Lightning' Database
—
ACID transaction handling for consistent database operations. Transactions provide atomicity, consistency, isolation, and durability guarantees for all database modifications. LMDB supports concurrent readers with exclusive writers using MVCC (Multi-Version Concurrency Control).
Create read-only or read-write transactions with optional parent transaction support and buffer management.
class Environment:
def begin(self, db=None, parent=None, write: bool = False,
buffers: bool = False) -> Transaction:
"""
Begin a new transaction.
Parameters:
- db: Default database handle for transaction operations
- parent: Parent transaction (for nested transactions)
- write: Enable write operations (default read-only)
- buffers: Return buffer objects instead of copying data
Returns:
Transaction instance
Note:
Write transactions are exclusive - only one write transaction per environment.
Read transactions are concurrent and don't block each other or writers.
"""Commit or abort transactions with proper resource cleanup and error handling.
class Transaction:
def commit(self) -> None:
"""
Commit all operations in transaction and close it.
Changes become visible to other transactions.
Raises:
Various errors if commit fails (disk full, corruption, etc.)
"""
def abort(self) -> None:
"""
Abandon all operations and close transaction.
All changes are discarded without affecting database.
Safe to call multiple times.
"""
def id(self) -> int:
"""
Get unique transaction identifier.
Returns:
Transaction ID (monotonically increasing)
"""Read operations for accessing stored data with flexible default handling and multi-database support.
class Transaction:
def get(self, key: bytes, default=None, db=None) -> bytes:
"""
Retrieve value for given key.
Parameters:
- key: Key to lookup (bytes object)
- default: Value returned if key not found
- db: Database handle (uses transaction default if None)
Returns:
Value bytes or default if key not found
"""Write operations for storing, updating, and removing data with extensive control options.
class Transaction:
def put(self, key: bytes, value: bytes, dupdata: bool = True,
overwrite: bool = True, append: bool = False, db=None) -> bool:
"""
Store key-value pair in database.
Parameters:
- key: Key bytes (must not be empty)
- value: Value bytes
- dupdata: Allow duplicate keys (ignored if database doesn't support duplicates)
- overwrite: Replace existing value (False raises KeyExistsError if key exists)
- append: Optimize for appending (key must be >= all existing keys)
- db: Database handle
Returns:
True if key was inserted, False if key existed and was updated
Raises:
KeyExistsError: If overwrite=False and key exists
MapFullError: If database is full
"""
def replace(self, key: bytes, value: bytes, db=None) -> bytes:
"""
Replace existing value and return old value.
Parameters:
- key: Key to replace
- value: New value
- db: Database handle
Returns:
Previous value
Raises:
NotFoundError: If key doesn't exist
"""
def pop(self, key: bytes, db=None) -> bytes:
"""
Get value and delete key in single operation.
Parameters:
- key: Key to retrieve and delete
- db: Database handle
Returns:
Value that was stored
Raises:
NotFoundError: If key doesn't exist
"""
def delete(self, key: bytes, value: bytes = b'', db=None) -> bool:
"""
Delete key-value pair from database.
Parameters:
- key: Key to delete
- value: Specific value to delete (for duplicate keys, empty for any value)
- db: Database handle
Returns:
True if key was found and deleted
Raises:
NotFoundError: If key doesn't exist (when value specified)
"""Transaction-scoped database management including statistics and database deletion.
class Transaction:
def stat(self, db) -> dict:
"""
Get database statistics within transaction context.
Parameters:
- db: Database handle
Returns:
Dictionary with statistics:
- psize: Page size
- depth: B-tree depth
- branch_pages: Internal pages
- leaf_pages: Leaf pages
- overflow_pages: Overflow pages
- entries: Data items
"""
def drop(self, db, delete: bool = True) -> None:
"""
Empty or delete database.
Parameters:
- db: Database handle
- delete: If True, delete database; if False, empty database
Note:
Cannot delete the default database, only empty it
"""Create cursors for iteration and positioned access within transaction context.
class Transaction:
def cursor(self, db=None) -> Cursor:
"""
Create cursor for iterating over database.
Parameters:
- db: Database handle (uses transaction default if None)
Returns:
Cursor instance bound to this transaction and database
"""import lmdb
env = lmdb.open('/path/to/database')
# Write transaction with context manager (recommended)
with env.begin(write=True) as txn:
# Store data
txn.put(b'user:1', b'{"name": "Alice", "age": 30}')
txn.put(b'user:2', b'{"name": "Bob", "age": 25}')
# Update existing data
txn.put(b'user:1', b'{"name": "Alice Smith", "age": 31}')
# Conditional insert (fails if key exists)
try:
txn.put(b'user:3', b'{"name": "Charlie"}', overwrite=False)
except lmdb.KeyExistsError:
print("User 3 already exists")
# Get and delete in one operation
old_value = txn.pop(b'user:2')
print(f"Removed user: {old_value}")
# Transaction auto-commits when leaving context successfully
# Read transaction
with env.begin() as txn:
user1 = txn.get(b'user:1')
if user1:
print(f"User 1: {user1}")
# Safe get with default
user99 = txn.get(b'user:99', default=b'Not found')
print(f"User 99: {user99}")
env.close()import lmdb
env = lmdb.open('/path/to/database')
# Manual transaction management (not recommended - use context managers)
txn = env.begin(write=True)
try:
txn.put(b'key1', b'value1')
txn.put(b'key2', b'value2')
# Simulate error condition
if some_error_condition:
txn.abort() # Discard changes
print("Transaction aborted")
else:
txn.commit() # Save changes
print("Transaction committed")
except Exception as e:
txn.abort() # Always abort on exception
print(f"Transaction failed: {e}")
env.close()import lmdb
env = lmdb.open('/path/to/database', max_dbs=3)
# Open multiple databases
users_db = env.open_db(b'users')
posts_db = env.open_db(b'posts')
comments_db = env.open_db(b'comments')
# Atomic operations across multiple databases
with env.begin(write=True) as txn:
# Create user
user_id = b'user:123'
txn.put(user_id, b'{"name": "Alice"}', db=users_db)
# Create post by user
post_id = b'post:456'
txn.put(post_id, b'{"user_id": "123", "title": "Hello World"}', db=posts_db)
# Add comment to post
comment_id = b'comment:789'
txn.put(comment_id, b'{"post_id": "456", "text": "Great post!"}', db=comments_db)
# All operations committed atomically
# Read from multiple databases
with env.begin() as txn:
user = txn.get(b'user:123', db=users_db)
post = txn.get(b'post:456', db=posts_db)
comment = txn.get(b'comment:789', db=comments_db)
print(f"User: {user}")
print(f"Post: {post}")
print(f"Comment: {comment}")
env.close()import lmdb
env = lmdb.open('/path/to/database')
try:
with env.begin(write=True) as txn:
txn.put(b'key1', b'value1')
# This might fail if database is full
large_value = b'x' * 1000000
txn.put(b'large_key', large_value)
except lmdb.MapFullError:
print("Database is full - increase map_size")
except lmdb.Error as e:
print(f"LMDB error: {e}")
finally:
env.close()Install with Tessl CLI
npx tessl i tessl/pypi-lmdb