Python library to interact with keepass databases (supports KDBX3 and KDBX4)
—
File attachment handling including adding, retrieving, and managing binary data associated with database entries.
Manage binary data storage in the database for file attachments.
def add_binary(data, compressed=True, protected=True):
"""
Add binary data to the database.
Parameters:
- data (bytes): Binary data to store
- compressed (bool): Whether to compress the data (default True)
- protected (bool): Whether to protect the data in memory (default True)
Returns:
int: Binary ID that can be used to reference this data
"""
def delete_binary(id):
"""
Delete binary data and all references to it.
Parameters:
- id (int): Binary ID to delete
Note: This will remove the binary data and all attachment references to it
"""Usage Examples:
from pykeepass import PyKeePass
kp = PyKeePass('database.kdbx', password='secret')
# Read file and add as binary data
with open('document.pdf', 'rb') as f:
binary_data = f.read()
# Add binary data to database
binary_id = kp.add_binary(binary_data, compressed=True, protected=True)
print(f"Binary stored with ID: {binary_id}")
# Delete binary data when no longer needed
kp.delete_binary(binary_id)
kp.save()Find attachments across the database with flexible search criteria.
def find_attachments(**kwargs):
"""
Find attachments with flexible criteria.
Parameters:
- filename (str, optional): Match filename
- id (int, optional): Match binary ID
- element (Element, optional): Match parent element
- regex (bool): Use regex matching for filename
- flags (int, optional): Regex flags
- first (bool): Return only first match
Returns:
list or Attachment: List of matching attachments, or single attachment if first=True
"""Usage Examples:
import re
# Find all attachments
all_attachments = kp.find_attachments()
# Find by filename
pdf_attachments = kp.find_attachments(filename='document.pdf')
image_attachments = kp.find_attachments(filename=r'.*\.(jpg|png|gif)$', regex=True)
# Find by binary ID
attachment = kp.find_attachments(id=123, first=True)
# Case-insensitive filename search
docs = kp.find_attachments(filename='readme', regex=True, flags=re.IGNORECASE)
# Get first attachment matching criteria
first_pdf = kp.find_attachments(filename=r'.*\.pdf$', regex=True, first=True)Manage attachments associated with specific entries.
# Entry methods for attachment management
def add_attachment(id, filename):
"""
Add an attachment to this entry.
Parameters:
- id (int): Binary ID of stored data
- filename (str): Display filename for the attachment
Returns:
Attachment: The created attachment object
"""
def delete_attachment(attachment):
"""
Remove an attachment from this entry.
Parameters:
- attachment (Attachment): Attachment object to remove
Note: This removes the attachment reference but not the binary data
"""Usage Examples:
# Find entry to attach file to
entry = kp.find_entries_by_title('Important Document', first=True)
# Read and store binary data
with open('contract.pdf', 'rb') as f:
binary_data = f.read()
binary_id = kp.add_binary(binary_data)
# Attach to entry
attachment = entry.add_attachment(binary_id, 'contract.pdf')
print(f"Attached {attachment.filename} to {entry.title}")
# Remove attachment from entry
entry.delete_attachment(attachment)
kp.save()The Attachment class represents file attachments linked to entries.
class Attachment:
def __init__(element=None, kp=None, id=None, filename=None):
"""Create a new Attachment object."""
@property
def id() -> int:
"""Binary reference ID"""
@property
def filename() -> str:
"""Attachment filename"""
@property
def entry() -> Entry:
"""Parent entry that owns this attachment"""
@property
def binary() -> bytes:
"""Binary data content"""
@property
def data() -> bytes:
"""Alias for binary property - binary data content"""
def delete():
"""Remove this attachment from its parent entry."""Usage Examples:
# Access attachment properties
entry = kp.find_entries_by_title('Document Entry', first=True)
for attachment in entry.attachments:
print(f"Filename: {attachment.filename}")
print(f"Binary ID: {attachment.id}")
print(f"Data size: {len(attachment.binary)} bytes")
print(f"Parent entry: {attachment.entry.title}")
# Save attachment to file
with open(f"exported_{attachment.filename}", 'wb') as f:
f.write(attachment.data) # or attachment.binaryExamples showing complete workflows for working with attachments.
Adding Files as Attachments:
from pykeepass import PyKeePass
import os
kp = PyKeePass('database.kdbx', password='secret')
# Find or create entry
entry = kp.find_entries_by_title('Project Files', first=True)
if not entry:
group = kp.find_groups_by_name('Work', first=True)
entry = kp.add_entry(group, 'Project Files', 'username', 'password')
# Add multiple files
files_to_attach = ['spec.pdf', 'diagram.png', 'notes.txt']
for filepath in files_to_attach:
if os.path.exists(filepath):
# Read file
with open(filepath, 'rb') as f:
binary_data = f.read()
# Store in database
binary_id = kp.add_binary(binary_data, compressed=True, protected=True)
# Attach to entry
filename = os.path.basename(filepath)
attachment = entry.add_attachment(binary_id, filename)
print(f"Attached: {filename} ({len(binary_data)} bytes)")
kp.save()Extracting Attachments:
# Extract all attachments from database
output_dir = 'extracted_attachments'
os.makedirs(output_dir, exist_ok=True)
all_attachments = kp.find_attachments()
for attachment in all_attachments:
# Create safe filename
safe_filename = attachment.filename.replace('/', '_').replace('\\', '_')
output_path = os.path.join(output_dir, safe_filename)
# Handle filename conflicts
counter = 1
base_path = output_path
while os.path.exists(output_path):
name, ext = os.path.splitext(base_path)
output_path = f"{name}_{counter}{ext}"
counter += 1
# Write attachment data
with open(output_path, 'wb') as f:
f.write(attachment.binary)
print(f"Extracted: {attachment.filename} -> {output_path}")
print(f" From entry: {attachment.entry.title}")
print(f" Size: {len(attachment.binary)} bytes")Managing Attachment Storage:
# Clean up unused binary data
used_binary_ids = set()
# Collect all binary IDs in use
for attachment in kp.find_attachments():
used_binary_ids.add(attachment.id)
# Find unused binaries (this requires access to internal binary storage)
all_binary_ids = set(range(len(kp.binaries)))
unused_binary_ids = all_binary_ids - used_binary_ids
# Remove unused binaries
for binary_id in unused_binary_ids:
try:
kp.delete_binary(binary_id)
print(f"Deleted unused binary ID: {binary_id}")
except Exception as e:
print(f"Could not delete binary ID {binary_id}: {e}")
kp.save()
# Report attachment statistics
total_attachments = len(kp.find_attachments())
total_size = sum(len(attachment.binary) for attachment in kp.find_attachments())
print(f"Database contains {total_attachments} attachments")
print(f"Total attachment size: {total_size / 1024:.1f} KB")Attachment Validation:
# Validate all attachments
invalid_attachments = []
for attachment in kp.find_attachments():
try:
# Test access to binary data
data = attachment.binary
if not data or len(data) == 0:
invalid_attachments.append((attachment, "Empty data"))
# Validate filename
if not attachment.filename or attachment.filename.strip() == '':
invalid_attachments.append((attachment, "Invalid filename"))
# Check parent entry exists
if not attachment.entry:
invalid_attachments.append((attachment, "No parent entry"))
except Exception as e:
invalid_attachments.append((attachment, f"Access error: {e}"))
# Report issues
if invalid_attachments:
print("Invalid attachments found:")
for attachment, issue in invalid_attachments:
print(f" {attachment.filename}: {issue}")
else:
print("All attachments are valid")Install with Tessl CLI
npx tessl i tessl/pypi-pykeepass