or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

attachments.mddatabase-operations.mdentry-management.mdgroup-management.mdindex.md

attachments.mddocs/

0

# Attachments

1

2

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

3

4

## Capabilities

5

6

### Binary Data Management

7

8

Manage binary data storage in the database for file attachments.

9

10

```python { .api }

11

def add_binary(data, compressed=True, protected=True):

12

"""

13

Add binary data to the database.

14

15

Parameters:

16

- data (bytes): Binary data to store

17

- compressed (bool): Whether to compress the data (default True)

18

- protected (bool): Whether to protect the data in memory (default True)

19

20

Returns:

21

int: Binary ID that can be used to reference this data

22

"""

23

24

def delete_binary(id):

25

"""

26

Delete binary data and all references to it.

27

28

Parameters:

29

- id (int): Binary ID to delete

30

31

Note: This will remove the binary data and all attachment references to it

32

"""

33

```

34

35

**Usage Examples:**

36

37

```python

38

from pykeepass import PyKeePass

39

40

kp = PyKeePass('database.kdbx', password='secret')

41

42

# Read file and add as binary data

43

with open('document.pdf', 'rb') as f:

44

binary_data = f.read()

45

46

# Add binary data to database

47

binary_id = kp.add_binary(binary_data, compressed=True, protected=True)

48

print(f"Binary stored with ID: {binary_id}")

49

50

# Delete binary data when no longer needed

51

kp.delete_binary(binary_id)

52

53

kp.save()

54

```

55

56

### Attachment Search and Retrieval

57

58

Find attachments across the database with flexible search criteria.

59

60

```python { .api }

61

def find_attachments(**kwargs):

62

"""

63

Find attachments with flexible criteria.

64

65

Parameters:

66

- filename (str, optional): Match filename

67

- id (int, optional): Match binary ID

68

- element (Element, optional): Match parent element

69

- regex (bool): Use regex matching for filename

70

- flags (int, optional): Regex flags

71

- first (bool): Return only first match

72

73

Returns:

74

list or Attachment: List of matching attachments, or single attachment if first=True

75

"""

76

```

77

78

**Usage Examples:**

79

80

```python

81

import re

82

83

# Find all attachments

84

all_attachments = kp.find_attachments()

85

86

# Find by filename

87

pdf_attachments = kp.find_attachments(filename='document.pdf')

88

image_attachments = kp.find_attachments(filename=r'.*\.(jpg|png|gif)$', regex=True)

89

90

# Find by binary ID

91

attachment = kp.find_attachments(id=123, first=True)

92

93

# Case-insensitive filename search

94

docs = kp.find_attachments(filename='readme', regex=True, flags=re.IGNORECASE)

95

96

# Get first attachment matching criteria

97

first_pdf = kp.find_attachments(filename=r'.*\.pdf$', regex=True, first=True)

98

```

99

100

### Entry Attachment Management

101

102

Manage attachments associated with specific entries.

103

104

```python { .api }

105

# Entry methods for attachment management

106

def add_attachment(id, filename):

107

"""

108

Add an attachment to this entry.

109

110

Parameters:

111

- id (int): Binary ID of stored data

112

- filename (str): Display filename for the attachment

113

114

Returns:

115

Attachment: The created attachment object

116

"""

117

118

def delete_attachment(attachment):

119

"""

120

Remove an attachment from this entry.

121

122

Parameters:

123

- attachment (Attachment): Attachment object to remove

124

125

Note: This removes the attachment reference but not the binary data

126

"""

127

```

128

129

**Usage Examples:**

130

131

```python

132

# Find entry to attach file to

133

entry = kp.find_entries_by_title('Important Document', first=True)

134

135

# Read and store binary data

136

with open('contract.pdf', 'rb') as f:

137

binary_data = f.read()

138

139

binary_id = kp.add_binary(binary_data)

140

141

# Attach to entry

142

attachment = entry.add_attachment(binary_id, 'contract.pdf')

143

print(f"Attached {attachment.filename} to {entry.title}")

144

145

# Remove attachment from entry

146

entry.delete_attachment(attachment)

147

148

kp.save()

149

```

150

151

### Attachment Class and Properties

152

153

The Attachment class represents file attachments linked to entries.

154

155

```python { .api }

156

class Attachment:

157

def __init__(element=None, kp=None, id=None, filename=None):

158

"""Create a new Attachment object."""

159

160

@property

161

def id() -> int:

162

"""Binary reference ID"""

163

164

@property

165

def filename() -> str:

166

"""Attachment filename"""

167

168

@property

169

def entry() -> Entry:

170

"""Parent entry that owns this attachment"""

171

172

@property

173

def binary() -> bytes:

174

"""Binary data content"""

175

176

@property

177

def data() -> bytes:

178

"""Alias for binary property - binary data content"""

179

180

def delete():

181

"""Remove this attachment from its parent entry."""

182

```

183

184

**Usage Examples:**

185

186

```python

187

# Access attachment properties

188

entry = kp.find_entries_by_title('Document Entry', first=True)

189

190

for attachment in entry.attachments:

191

print(f"Filename: {attachment.filename}")

192

print(f"Binary ID: {attachment.id}")

193

print(f"Data size: {len(attachment.binary)} bytes")

194

print(f"Parent entry: {attachment.entry.title}")

195

196

# Save attachment to file

197

with open(f"exported_{attachment.filename}", 'wb') as f:

198

f.write(attachment.data) # or attachment.binary

199

```

200

201

### Complete Attachment Workflow

202

203

Examples showing complete workflows for working with attachments.

204

205

**Adding Files as Attachments:**

206

207

```python

208

from pykeepass import PyKeePass

209

import os

210

211

kp = PyKeePass('database.kdbx', password='secret')

212

213

# Find or create entry

214

entry = kp.find_entries_by_title('Project Files', first=True)

215

if not entry:

216

group = kp.find_groups_by_name('Work', first=True)

217

entry = kp.add_entry(group, 'Project Files', 'username', 'password')

218

219

# Add multiple files

220

files_to_attach = ['spec.pdf', 'diagram.png', 'notes.txt']

221

222

for filepath in files_to_attach:

223

if os.path.exists(filepath):

224

# Read file

225

with open(filepath, 'rb') as f:

226

binary_data = f.read()

227

228

# Store in database

229

binary_id = kp.add_binary(binary_data, compressed=True, protected=True)

230

231

# Attach to entry

232

filename = os.path.basename(filepath)

233

attachment = entry.add_attachment(binary_id, filename)

234

print(f"Attached: {filename} ({len(binary_data)} bytes)")

235

236

kp.save()

237

```

238

239

**Extracting Attachments:**

240

241

```python

242

# Extract all attachments from database

243

output_dir = 'extracted_attachments'

244

os.makedirs(output_dir, exist_ok=True)

245

246

all_attachments = kp.find_attachments()

247

248

for attachment in all_attachments:

249

# Create safe filename

250

safe_filename = attachment.filename.replace('/', '_').replace('\\', '_')

251

output_path = os.path.join(output_dir, safe_filename)

252

253

# Handle filename conflicts

254

counter = 1

255

base_path = output_path

256

while os.path.exists(output_path):

257

name, ext = os.path.splitext(base_path)

258

output_path = f"{name}_{counter}{ext}"

259

counter += 1

260

261

# Write attachment data

262

with open(output_path, 'wb') as f:

263

f.write(attachment.binary)

264

265

print(f"Extracted: {attachment.filename} -> {output_path}")

266

print(f" From entry: {attachment.entry.title}")

267

print(f" Size: {len(attachment.binary)} bytes")

268

```

269

270

**Managing Attachment Storage:**

271

272

```python

273

# Clean up unused binary data

274

used_binary_ids = set()

275

276

# Collect all binary IDs in use

277

for attachment in kp.find_attachments():

278

used_binary_ids.add(attachment.id)

279

280

# Find unused binaries (this requires access to internal binary storage)

281

all_binary_ids = set(range(len(kp.binaries)))

282

unused_binary_ids = all_binary_ids - used_binary_ids

283

284

# Remove unused binaries

285

for binary_id in unused_binary_ids:

286

try:

287

kp.delete_binary(binary_id)

288

print(f"Deleted unused binary ID: {binary_id}")

289

except Exception as e:

290

print(f"Could not delete binary ID {binary_id}: {e}")

291

292

kp.save()

293

294

# Report attachment statistics

295

total_attachments = len(kp.find_attachments())

296

total_size = sum(len(attachment.binary) for attachment in kp.find_attachments())

297

298

print(f"Database contains {total_attachments} attachments")

299

print(f"Total attachment size: {total_size / 1024:.1f} KB")

300

```

301

302

**Attachment Validation:**

303

304

```python

305

# Validate all attachments

306

invalid_attachments = []

307

308

for attachment in kp.find_attachments():

309

try:

310

# Test access to binary data

311

data = attachment.binary

312

if not data or len(data) == 0:

313

invalid_attachments.append((attachment, "Empty data"))

314

315

# Validate filename

316

if not attachment.filename or attachment.filename.strip() == '':

317

invalid_attachments.append((attachment, "Invalid filename"))

318

319

# Check parent entry exists

320

if not attachment.entry:

321

invalid_attachments.append((attachment, "No parent entry"))

322

323

except Exception as e:

324

invalid_attachments.append((attachment, f"Access error: {e}"))

325

326

# Report issues

327

if invalid_attachments:

328

print("Invalid attachments found:")

329

for attachment, issue in invalid_attachments:

330

print(f" {attachment.filename}: {issue}")

331

else:

332

print("All attachments are valid")

333

```