0
# Exception Handling
1
2
Comprehensive exception hierarchy covering database errors, ORM-specific errors, query errors, and transaction errors. Proper exception handling is crucial for building robust applications with Pony ORM.
3
4
## Capabilities
5
6
### ORM-Level Exceptions
7
8
Core ORM exceptions that handle entity operations, queries, and object lifecycle errors.
9
10
```python { .api }
11
class OrmError(Exception):
12
"""Base exception class for all ORM-related errors."""
13
14
class ObjectNotFound(OrmError):
15
"""Raised when get() or [] operator finds no matching entity."""
16
17
class MultipleObjectsFoundError(OrmError):
18
"""Raised when get() finds multiple entities when expecting one."""
19
20
class TooManyObjectsFoundError(MultipleObjectsFoundError):
21
"""Raised when query returns more objects than expected limit."""
22
23
class OperationWithDeletedObjectError(OrmError):
24
"""Raised when attempting operations on deleted entity objects."""
25
26
class CacheIndexError(OrmError):
27
"""Raised when entity cache index operations fail."""
28
29
class MappingError(OrmError):
30
"""Raised when entity mapping or schema definition is invalid."""
31
32
class ERDiagramError(OrmError):
33
"""Raised when entity relationship diagram generation fails."""
34
35
class BindingError(OrmError):
36
"""Raised when database binding or connection configuration fails."""
37
```
38
39
### Database-Level Exceptions
40
41
Standard database API exceptions that correspond to DB-API 2.0 specification errors.
42
43
```python { .api }
44
class DBException(Exception):
45
"""Base exception class for all database-related errors."""
46
47
class Warning(DBException):
48
"""Exception for important database warnings."""
49
50
class Error(DBException):
51
"""Base exception class for database errors."""
52
53
class InterfaceError(Error):
54
"""Exception for database interface errors."""
55
56
class DatabaseError(Error):
57
"""Exception for database-specific errors."""
58
59
class DataError(DatabaseError):
60
"""Exception for data processing errors (invalid data, numeric overflow, etc.)."""
61
62
class OperationalError(DatabaseError):
63
"""Exception for database operation errors (connection lost, memory allocation, etc.)."""
64
65
class IntegrityError(DatabaseError):
66
"""Exception for database constraint violations (foreign key, unique, etc.)."""
67
68
class InternalError(DatabaseError):
69
"""Exception for internal database errors."""
70
71
class ProgrammingError(DatabaseError):
72
"""Exception for programming errors (table not found, syntax error, etc.)."""
73
74
class NotSupportedError(DatabaseError):
75
"""Exception for unsupported database operations or methods."""
76
```
77
78
### Schema and Table Exceptions
79
80
Exceptions related to database schema operations and table management.
81
82
```python { .api }
83
class DBSchemaError(OrmError):
84
"""Raised when database schema operations fail."""
85
86
class TableDoesNotExist(DBSchemaError):
87
"""Raised when referencing non-existent database table."""
88
89
class TableIsNotEmpty(DBSchemaError):
90
"""Raised when attempting operations that require empty table."""
91
92
class ConstraintError(DBSchemaError):
93
"""Raised when database constraint operations fail."""
94
```
95
96
### Query and Result Exceptions
97
98
Exceptions specific to query operations and result processing.
99
100
```python { .api }
101
class RowNotFound(DBException):
102
"""Raised when database query returns no rows when expecting results."""
103
104
class MultipleRowsFound(DBException):
105
"""Raised when database query returns multiple rows when expecting one."""
106
107
class TooManyRowsFound(MultipleRowsFound):
108
"""Raised when query returns more rows than specified limit."""
109
```
110
111
### Transaction Exceptions
112
113
Exceptions related to transaction management and concurrency control.
114
115
```python { .api }
116
class TransactionError(OrmError):
117
"""Base exception class for transaction-related errors."""
118
119
class ConnectionClosedError(TransactionError):
120
"""Raised when database connection is unexpectedly closed."""
121
122
class TransactionIntegrityError(TransactionError):
123
"""Raised when transaction integrity is compromised."""
124
125
class IsolationError(TransactionError):
126
"""Raised when transaction isolation level conflicts occur."""
127
128
class CommitException(TransactionError):
129
"""Raised when transaction commit operation fails."""
130
131
class RollbackException(TransactionError):
132
"""Raised when transaction rollback operation fails."""
133
134
class UnrepeatableReadError(TransactionError):
135
"""Raised when repeatable read isolation is violated."""
136
137
class OptimisticCheckError(TransactionError):
138
"""Raised when optimistic concurrency control check fails."""
139
140
class UnresolvableCyclicDependency(TransactionError):
141
"""Raised when circular dependencies prevent transaction completion."""
142
143
class UnexpectedError(TransactionError):
144
"""Raised for unexpected transaction-related errors."""
145
146
class DatabaseSessionIsOver(TransactionError):
147
"""Raised when attempting operations outside active database session."""
148
```
149
150
### Translation and Runtime Exceptions
151
152
Exceptions related to query translation and runtime operations.
153
154
```python { .api }
155
class TranslationError(OrmError):
156
"""Raised when generator expression cannot be translated to SQL."""
157
158
class ExprEvalError(OrmError):
159
"""Raised when expression evaluation fails during query processing."""
160
161
class PonyRuntimeWarning(UserWarning):
162
"""Warning class for runtime issues that don't prevent execution."""
163
164
class DatabaseContainsIncorrectValue(PonyRuntimeWarning):
165
"""Warning when database contains values that don't match expected format."""
166
167
class DatabaseContainsIncorrectEmptyValue(PonyRuntimeWarning):
168
"""Warning when database contains empty values in unexpected contexts."""
169
```
170
171
### Security and Permission Exceptions
172
173
Exceptions related to security and access control operations.
174
175
```python { .api }
176
class PermissionError(OrmError):
177
"""Raised when security permission checks fail."""
178
```
179
180
## Usage Examples
181
182
### Basic Exception Handling
183
184
```python
185
from pony.orm import *
186
187
@db_session
188
def safe_user_lookup(email):
189
try:
190
user = User.get(email=email)
191
return user
192
except ObjectNotFound:
193
print(f"No user found with email: {email}")
194
return None
195
except MultipleObjectsFoundError:
196
print(f"Multiple users found with email: {email}")
197
# This shouldn't happen if email is unique
198
raise
199
200
@db_session
201
def handle_entity_operations():
202
try:
203
# Try to create user
204
user = User(name="Alice", email="alice@example.com")
205
commit()
206
207
except IntegrityError as e:
208
print(f"Database constraint violation: {e}")
209
rollback()
210
# Handle duplicate email, etc.
211
212
except DatabaseError as e:
213
print(f"Database error occurred: {e}")
214
rollback()
215
raise
216
```
217
218
### Transaction Exception Handling
219
220
```python
221
@db_session
222
def handle_transaction_errors():
223
try:
224
# Risky operations that might conflict
225
user = User.get(name="Alice")
226
user.balance += 100
227
228
# Force a flush to detect conflicts early
229
flush()
230
231
# More operations...
232
order = Order(user=user, total=50.0)
233
commit()
234
235
except OptimisticCheckError as e:
236
print(f"Concurrent modification detected: {e}")
237
rollback()
238
# Retry logic or user notification
239
240
except ConnectionClosedError as e:
241
print(f"Database connection lost: {e}")
242
# Reconnection logic
243
244
except TransactionError as e:
245
print(f"Transaction failed: {e}")
246
rollback()
247
raise
248
249
# Retry pattern for handling optimistic concurrency
250
def retry_on_conflict(func, max_retries=3):
251
for attempt in range(max_retries):
252
try:
253
return func()
254
except OptimisticCheckError:
255
if attempt == max_retries - 1:
256
raise
257
print(f"Retry attempt {attempt + 1}")
258
# Brief delay before retry
259
import time
260
time.sleep(0.1 * (attempt + 1))
261
```
262
263
### Query Exception Handling
264
265
```python
266
@db_session
267
def safe_query_operations():
268
try:
269
# Query that might return no results
270
admin = User.get(role="admin")
271
272
except ObjectNotFound:
273
print("No admin user found, creating default admin")
274
admin = User(name="Admin", role="admin", email="admin@example.com")
275
276
try:
277
# Query that might return multiple results unexpectedly
278
primary_contact = Contact.get(is_primary=True, company_id=123)
279
280
except MultipleObjectsFoundError:
281
print("Multiple primary contacts found, using first one")
282
primary_contact = Contact.select(
283
lambda c: c.is_primary and c.company_id == 123
284
).first()
285
286
except ObjectNotFound:
287
print("No primary contact found")
288
primary_contact = None
289
290
# Handle translation errors for complex queries
291
@db_session
292
def handle_query_translation():
293
try:
294
# Complex query that might not translate properly
295
result = select(u for u in User
296
if some_complex_python_function(u.data))
297
298
except TranslationError as e:
299
print(f"Query cannot be translated to SQL: {e}")
300
# Fall back to Python filtering
301
all_users = User.select()
302
result = [u for u in all_users
303
if some_complex_python_function(u.data)]
304
```
305
306
### Schema and Database Exception Handling
307
308
```python
309
def handle_database_setup():
310
try:
311
db.bind('postgresql', host='localhost', user='app', password='secret')
312
db.generate_mapping(create_tables=True)
313
314
except BindingError as e:
315
print(f"Failed to bind to database: {e}")
316
# Try fallback database
317
db.bind('sqlite', filename='fallback.db')
318
db.generate_mapping(create_tables=True)
319
320
except TableDoesNotExist as e:
321
print(f"Required table missing: {e}")
322
# Create missing tables
323
db.create_tables()
324
325
except DBSchemaError as e:
326
print(f"Schema error: {e}")
327
# Handle schema migration or recreation
328
329
except DatabaseError as e:
330
print(f"Database operation failed: {e}")
331
raise
332
333
@db_session
334
def handle_table_operations():
335
try:
336
# Operation that requires empty table
337
db.execute("TRUNCATE TABLE user_sessions")
338
339
except TableIsNotEmpty as e:
340
print(f"Cannot truncate non-empty table: {e}")
341
# Delete rows individually
342
delete(s for s in UserSession)
343
344
except ProgrammingError as e:
345
print(f"SQL programming error: {e}")
346
# Handle SQL syntax issues
347
```
348
349
### Comprehensive Error Handling Pattern
350
351
```python
352
from functools import wraps
353
354
def safe_db_operation(func):
355
"""Decorator for comprehensive database error handling."""
356
@wraps(func)
357
def wrapper(*args, **kwargs):
358
try:
359
return func(*args, **kwargs)
360
361
except ObjectNotFound as e:
362
print(f"Entity not found in {func.__name__}: {e}")
363
return None
364
365
except MultipleObjectsFoundError as e:
366
print(f"Multiple entities found in {func.__name__}: {e}")
367
raise
368
369
except IntegrityError as e:
370
print(f"Database constraint violation in {func.__name__}: {e}")
371
rollback()
372
raise
373
374
except OptimisticCheckError as e:
375
print(f"Concurrent modification in {func.__name__}: {e}")
376
rollback()
377
raise
378
379
except TransactionError as e:
380
print(f"Transaction error in {func.__name__}: {e}")
381
rollback()
382
raise
383
384
except DatabaseError as e:
385
print(f"Database error in {func.__name__}: {e}")
386
rollback()
387
raise
388
389
except Exception as e:
390
print(f"Unexpected error in {func.__name__}: {e}")
391
rollback()
392
raise
393
394
return wrapper
395
396
# Usage of the decorator
397
@safe_db_operation
398
@db_session
399
def update_user_profile(user_id, **updates):
400
user = User[user_id] # May raise ObjectNotFound
401
for key, value in updates.items():
402
setattr(user, key, value)
403
commit()
404
return user
405
```
406
407
### Warning and Runtime Error Handling
408
409
```python
410
import warnings
411
412
# Handle runtime warnings
413
def handle_warnings():
414
# Catch Pony runtime warnings
415
with warnings.catch_warnings(record=True) as w:
416
warnings.simplefilter("always")
417
418
with db_session:
419
# Operations that might generate warnings
420
problematic_data = select(u for u in User if u.data_field)
421
422
for warning in w:
423
if issubclass(warning.category, PonyRuntimeWarning):
424
print(f"Pony warning: {warning.message}")
425
# Handle data quality issues
426
427
@db_session
428
def handle_expression_errors():
429
try:
430
# Complex expression that might fail evaluation
431
result = select(u.calculate_score() for u in User)
432
433
except ExprEvalError as e:
434
print(f"Expression evaluation failed: {e}")
435
# Fall back to safer calculation
436
result = select(u for u in User)
437
scores = [u.calculate_score() for u in result]
438
```