CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pykeepass

Python library to interact with keepass databases (supports KDBX3 and KDBX4)

Pending
Overview
Eval results
Files

attachments.mddocs/

Attachments

File attachment handling including adding, retrieving, and managing binary data associated with database entries.

Capabilities

Binary Data Management

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()

Attachment Search and Retrieval

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)

Entry Attachment Management

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()

Attachment Class and Properties

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.binary

Complete Attachment Workflow

Examples 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

docs

attachments.md

database-operations.md

entry-management.md

group-management.md

index.md

tile.json