sqlobjects 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlobjects/__init__.py +38 -0
- sqlobjects/config.py +519 -0
- sqlobjects/database.py +586 -0
- sqlobjects/exceptions.py +538 -0
- sqlobjects/expressions.py +1054 -0
- sqlobjects/fields.py +1866 -0
- sqlobjects/history.py +101 -0
- sqlobjects/metadata.py +1130 -0
- sqlobjects/model.py +1009 -0
- sqlobjects/objects.py +812 -0
- sqlobjects/queries.py +1059 -0
- sqlobjects/relations.py +843 -0
- sqlobjects/session.py +389 -0
- sqlobjects/signals.py +464 -0
- sqlobjects/utils/__init__.py +5 -0
- sqlobjects/utils/naming.py +53 -0
- sqlobjects/utils/pattern.py +644 -0
- sqlobjects/validators.py +294 -0
- sqlobjects-0.1.0.dist-info/METADATA +29 -0
- sqlobjects-0.1.0.dist-info/RECORD +23 -0
- sqlobjects-0.1.0.dist-info/WHEEL +5 -0
- sqlobjects-0.1.0.dist-info/licenses/LICENSE +21 -0
- sqlobjects-0.1.0.dist-info/top_level.txt +1 -0
sqlobjects/exceptions.py
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
"""SQLObjects Exception System
|
|
2
|
+
|
|
3
|
+
This module provides a comprehensive exception hierarchy for SQLObjects.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from sqlalchemy.exc import DataError as SQLADataError
|
|
9
|
+
from sqlalchemy.exc import DisconnectionError as SQLADisconnectionError
|
|
10
|
+
from sqlalchemy.exc import IntegrityError as SQLAIntegrityError
|
|
11
|
+
from sqlalchemy.exc import OperationalError as SQLAOperationalError
|
|
12
|
+
from sqlalchemy.exc import ProgrammingError as SQLAProgrammingError
|
|
13
|
+
from sqlalchemy.exc import SQLAlchemyError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"SQLObjectsError",
|
|
18
|
+
"DoesNotExist",
|
|
19
|
+
"MultipleObjectsReturned",
|
|
20
|
+
"ValidationError",
|
|
21
|
+
"ValidationErrorCollector",
|
|
22
|
+
"DatabaseError",
|
|
23
|
+
"IntegrityError",
|
|
24
|
+
"TransactionError",
|
|
25
|
+
"ConfigurationError",
|
|
26
|
+
"DeferredFieldError",
|
|
27
|
+
"PrimaryKeyError",
|
|
28
|
+
"SQLError",
|
|
29
|
+
"OperationalError",
|
|
30
|
+
"DataError",
|
|
31
|
+
"ProgrammingError",
|
|
32
|
+
"create_validation_error",
|
|
33
|
+
"convert_sqlalchemy_error",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SQLObjectsError(Exception):
|
|
38
|
+
"""Base exception class for all SQLObjects-related errors.
|
|
39
|
+
|
|
40
|
+
This is the root exception class that all other SQLObjects exceptions
|
|
41
|
+
inherit from. It can be used to catch any SQLObjects-specific error.
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
>>> try:
|
|
45
|
+
... # SQLObjects operations
|
|
46
|
+
... pass
|
|
47
|
+
... except SQLObjectsError as e:
|
|
48
|
+
... # Handle any SQLObjects error
|
|
49
|
+
... print(f"SQLObjects error: {e}")
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class DoesNotExist(SQLObjectsError):
|
|
56
|
+
"""Raised when a database query returns no results when one was expected.
|
|
57
|
+
|
|
58
|
+
This exception is typically raised by get() methods when no object
|
|
59
|
+
matches the specified criteria.
|
|
60
|
+
|
|
61
|
+
Examples:
|
|
62
|
+
>>> try:
|
|
63
|
+
... user = await User.objects.get(id=999)
|
|
64
|
+
... except DoesNotExist:
|
|
65
|
+
... print("User not found")
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class MultipleObjectsReturned(SQLObjectsError):
|
|
72
|
+
"""Raised when a query returns multiple objects when only one was expected.
|
|
73
|
+
|
|
74
|
+
This exception is typically raised by get() methods when multiple objects
|
|
75
|
+
match the specified criteria.
|
|
76
|
+
|
|
77
|
+
Examples:
|
|
78
|
+
>>> try:
|
|
79
|
+
... user = await User.objects.get(name="John")
|
|
80
|
+
... except MultipleObjectsReturned:
|
|
81
|
+
... print("Multiple users found with name 'John'")
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ValidationError(SQLObjectsError):
|
|
88
|
+
"""Raised when data validation fails during model operations.
|
|
89
|
+
|
|
90
|
+
This exception supports both single field validation errors and multiple
|
|
91
|
+
field validation errors. It provides detailed information about what
|
|
92
|
+
validation failed and why.
|
|
93
|
+
|
|
94
|
+
Attributes:
|
|
95
|
+
field: Name of the field that failed validation (for single errors)
|
|
96
|
+
message: Human-readable error message
|
|
97
|
+
code: Error code for programmatic handling
|
|
98
|
+
params: Parameters used in the error message
|
|
99
|
+
field_errors: Dictionary of field names to error lists (for multiple errors)
|
|
100
|
+
model_class: Name of the model class where validation failed
|
|
101
|
+
operation: Operation that triggered the validation (e.g., 'create', 'update')
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(
|
|
105
|
+
self,
|
|
106
|
+
message: str,
|
|
107
|
+
field: str | None = None,
|
|
108
|
+
code: str | None = None,
|
|
109
|
+
params: dict[str, Any] | None = None,
|
|
110
|
+
field_errors: dict[str, list[str]] | None = None,
|
|
111
|
+
) -> None:
|
|
112
|
+
"""Initialize a validation error.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
message: Human-readable error message
|
|
116
|
+
field: Name of the field that failed validation (for single errors)
|
|
117
|
+
code: Error code for programmatic handling
|
|
118
|
+
params: Parameters used in the error message
|
|
119
|
+
field_errors: Dictionary of field names to error lists (for multiple errors)
|
|
120
|
+
|
|
121
|
+
Examples:
|
|
122
|
+
>>> # Single field error
|
|
123
|
+
>>> raise ValidationError("Email is required", field="email", code="required")
|
|
124
|
+
>>> # Multiple field errors
|
|
125
|
+
>>> errors = {"email": ["Email is required"], "age": ["Age must be positive"]}
|
|
126
|
+
>>> raise ValidationError("Validation failed", field_errors=errors)
|
|
127
|
+
"""
|
|
128
|
+
super().__init__(message)
|
|
129
|
+
self.field = field
|
|
130
|
+
self.message = message
|
|
131
|
+
self.code = code or "invalid"
|
|
132
|
+
self.params = params or {}
|
|
133
|
+
self.field_errors = field_errors or {}
|
|
134
|
+
self.model_class: str | None = None
|
|
135
|
+
self.operation: str | None = None
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def is_multiple(self) -> bool:
|
|
139
|
+
"""Check if this validation error contains multiple field errors.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
True if this error contains multiple field errors, False otherwise
|
|
143
|
+
|
|
144
|
+
Examples:
|
|
145
|
+
>>> error = ValidationError("Email is required", field="email")
|
|
146
|
+
>>> error.is_multiple # False
|
|
147
|
+
>>> errors = {"email": ["Required"], "age": ["Invalid"]}
|
|
148
|
+
>>> error = ValidationError("Multiple errors", field_errors=errors)
|
|
149
|
+
>>> error.is_multiple # True
|
|
150
|
+
"""
|
|
151
|
+
return bool(self.field_errors)
|
|
152
|
+
|
|
153
|
+
def add_field_error(self, field: str, message: str) -> None:
|
|
154
|
+
"""Add a validation error for a specific field.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
field: Name of the field
|
|
158
|
+
message: Error message for the field
|
|
159
|
+
|
|
160
|
+
Examples:
|
|
161
|
+
>>> error = ValidationError("Validation failed")
|
|
162
|
+
>>> error.add_field_error("email", "Email is required")
|
|
163
|
+
>>> error.add_field_error("email", "Email format is invalid")
|
|
164
|
+
"""
|
|
165
|
+
if field not in self.field_errors:
|
|
166
|
+
self.field_errors[field] = []
|
|
167
|
+
self.field_errors[field].append(message)
|
|
168
|
+
|
|
169
|
+
def get_field_errors(self, field: str) -> list[str]:
|
|
170
|
+
"""Get all validation errors for a specific field.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
field: Name of the field
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
List of error messages for the specified field
|
|
177
|
+
|
|
178
|
+
Examples:
|
|
179
|
+
>>> errors = {"email": ["Required", "Invalid format"]}
|
|
180
|
+
>>> error = ValidationError("Multiple errors", field_errors=errors)
|
|
181
|
+
>>> error.get_field_errors("email") # ["Required", "Invalid format"]
|
|
182
|
+
>>> error.get_field_errors("name") # []
|
|
183
|
+
"""
|
|
184
|
+
return self.field_errors.get(field, [])
|
|
185
|
+
|
|
186
|
+
def to_dict(self) -> dict[str, Any]:
|
|
187
|
+
"""Convert the validation error to a dictionary format suitable for APIs.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Dictionary representation of the validation error
|
|
191
|
+
|
|
192
|
+
Examples:
|
|
193
|
+
>>> # Single field error
|
|
194
|
+
>>> error = ValidationError("Email is required", field="email", code="required")
|
|
195
|
+
>>> error.to_dict()
|
|
196
|
+
{'message': 'Email is required', 'code': 'required', 'field': 'email'}
|
|
197
|
+
|
|
198
|
+
>>> # Multiple field errors
|
|
199
|
+
>>> errors = {"email": ["Required"], "age": ["Invalid"]}
|
|
200
|
+
>>> error = ValidationError("Validation failed", field_errors=errors)
|
|
201
|
+
>>> error.to_dict()
|
|
202
|
+
{'message': 'Validation failed', 'field_errors': {...}, 'error_count': 2}
|
|
203
|
+
"""
|
|
204
|
+
if self.is_multiple:
|
|
205
|
+
# Multiple field errors format
|
|
206
|
+
return {
|
|
207
|
+
"message": self.message,
|
|
208
|
+
"field_errors": self.field_errors,
|
|
209
|
+
"error_count": sum(len(errors) for errors in self.field_errors.values()),
|
|
210
|
+
}
|
|
211
|
+
else:
|
|
212
|
+
# Single field error format
|
|
213
|
+
result: dict = {"message": self.message, "code": self.code}
|
|
214
|
+
if self.field:
|
|
215
|
+
result["field"] = self.field
|
|
216
|
+
if self.params:
|
|
217
|
+
result["params"] = self.params
|
|
218
|
+
return result
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class ValidationErrorCollector:
|
|
222
|
+
"""Helper class for collecting multiple validation errors before raising.
|
|
223
|
+
|
|
224
|
+
This class provides a convenient way to collect validation errors from
|
|
225
|
+
multiple fields and then raise a single ValidationError with all the
|
|
226
|
+
collected errors.
|
|
227
|
+
|
|
228
|
+
Examples:
|
|
229
|
+
>>> collector = ValidationErrorCollector()
|
|
230
|
+
>>> if not user.email:
|
|
231
|
+
... collector.add_error("email", "Email is required")
|
|
232
|
+
>>> if user.age < 0:
|
|
233
|
+
... collector.add_error("age", "Age must be positive")
|
|
234
|
+
>>> collector.raise_if_errors() # Raises ValidationError if any errors
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
def __init__(self) -> None:
|
|
238
|
+
"""Initialize an empty error collector."""
|
|
239
|
+
self._errors: dict[str, list[str]] = {}
|
|
240
|
+
|
|
241
|
+
def add_error(self, field: str, message: str) -> None:
|
|
242
|
+
"""Add a validation error for a specific field.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
field: Name of the field that has the error
|
|
246
|
+
message: Error message describing the validation failure
|
|
247
|
+
|
|
248
|
+
Examples:
|
|
249
|
+
>>> collector = ValidationErrorCollector()
|
|
250
|
+
>>> collector.add_error("email", "Email is required")
|
|
251
|
+
>>> collector.add_error("email", "Email format is invalid")
|
|
252
|
+
"""
|
|
253
|
+
if field not in self._errors:
|
|
254
|
+
self._errors[field] = []
|
|
255
|
+
self._errors[field].append(message)
|
|
256
|
+
|
|
257
|
+
def has_errors(self) -> bool:
|
|
258
|
+
"""Check if any validation errors have been collected.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
True if there are any errors, False otherwise
|
|
262
|
+
|
|
263
|
+
Examples:
|
|
264
|
+
>>> collector = ValidationErrorCollector()
|
|
265
|
+
>>> collector.has_errors() # False
|
|
266
|
+
>>> collector.add_error("email", "Required")
|
|
267
|
+
>>> collector.has_errors() # True
|
|
268
|
+
"""
|
|
269
|
+
return bool(self._errors)
|
|
270
|
+
|
|
271
|
+
def raise_if_errors(self) -> None:
|
|
272
|
+
"""Raise a ValidationError if any errors have been collected.
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
ValidationError: If there are any collected errors
|
|
276
|
+
|
|
277
|
+
Examples:
|
|
278
|
+
>>> collector = ValidationErrorCollector()
|
|
279
|
+
>>> collector.add_error("email", "Required")
|
|
280
|
+
>>> collector.raise_if_errors() # Raises ValidationError
|
|
281
|
+
"""
|
|
282
|
+
if self.has_errors():
|
|
283
|
+
field_count = len(self._errors)
|
|
284
|
+
total_errors = sum(len(errors) for errors in self._errors.values())
|
|
285
|
+
message = f"Validation failed for {field_count} field(s) with {total_errors} error(s)"
|
|
286
|
+
raise ValidationError(message, field_errors=self._errors)
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def errors(self) -> dict[str, list[str]]:
|
|
290
|
+
"""Get a copy of all collected errors.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Dictionary mapping field names to lists of error messages
|
|
294
|
+
|
|
295
|
+
Examples:
|
|
296
|
+
>>> collector = ValidationErrorCollector()
|
|
297
|
+
>>> collector.add_error("email", "Required")
|
|
298
|
+
>>> collector.errors # {"email": ["Required"]}
|
|
299
|
+
"""
|
|
300
|
+
return self._errors.copy()
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class DatabaseError(SQLObjectsError):
|
|
304
|
+
"""Raised when a database operation fails.
|
|
305
|
+
|
|
306
|
+
This is a general exception for database-related errors that don't
|
|
307
|
+
fall into more specific categories like IntegrityError or TransactionError.
|
|
308
|
+
|
|
309
|
+
Examples:
|
|
310
|
+
>>> try:
|
|
311
|
+
... await db.execute("INVALID SQL")
|
|
312
|
+
... except DatabaseError as e:
|
|
313
|
+
... print(f"Database error: {e}")
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
pass
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class IntegrityError(DatabaseError):
|
|
320
|
+
"""Raised when a database integrity constraint is violated.
|
|
321
|
+
|
|
322
|
+
This exception is typically raised when operations violate database
|
|
323
|
+
constraints such as unique constraints, foreign key constraints, or
|
|
324
|
+
check constraints.
|
|
325
|
+
|
|
326
|
+
Examples:
|
|
327
|
+
>>> try:
|
|
328
|
+
... await User.objects.create(email="existing@example.com")
|
|
329
|
+
... except IntegrityError:
|
|
330
|
+
... print("Email already exists")
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
def __init__(self, message: str, original_error: Exception | None = None):
|
|
334
|
+
self.original_error = original_error
|
|
335
|
+
super().__init__(message)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class TransactionError(DatabaseError):
|
|
339
|
+
"""Raised when a database transaction operation fails.
|
|
340
|
+
|
|
341
|
+
This exception is raised for transaction-specific errors such as
|
|
342
|
+
deadlocks, transaction rollbacks, or commit failures.
|
|
343
|
+
|
|
344
|
+
Examples:
|
|
345
|
+
>>> try:
|
|
346
|
+
... async with session.begin():
|
|
347
|
+
... # Database operations that might cause deadlock
|
|
348
|
+
... pass
|
|
349
|
+
... except TransactionError as e:
|
|
350
|
+
... print(f"Transaction failed: {e}")
|
|
351
|
+
"""
|
|
352
|
+
|
|
353
|
+
pass
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class ConfigurationError(SQLObjectsError):
|
|
357
|
+
"""Raised when there is an error in SQLObjects configuration.
|
|
358
|
+
|
|
359
|
+
This exception is raised when invalid configuration is detected,
|
|
360
|
+
such as invalid database URLs, missing required settings, or
|
|
361
|
+
conflicting configuration options.
|
|
362
|
+
|
|
363
|
+
Examples:
|
|
364
|
+
>>> try:
|
|
365
|
+
... init_db("invalid://database/url")
|
|
366
|
+
... except ConfigurationError as e:
|
|
367
|
+
... print(f"Configuration error: {e}")
|
|
368
|
+
"""
|
|
369
|
+
|
|
370
|
+
pass
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
class DeferredFieldError(SQLObjectsError):
|
|
374
|
+
"""Raised when accessing a deferred field that hasn't been loaded.
|
|
375
|
+
|
|
376
|
+
This exception is raised when trying to access a field that was
|
|
377
|
+
deferred during the query and hasn't been explicitly loaded.
|
|
378
|
+
|
|
379
|
+
Examples:
|
|
380
|
+
>>> try:
|
|
381
|
+
... content = article.content # deferred field
|
|
382
|
+
... except DeferredFieldError:
|
|
383
|
+
... await article.load_deferred_field("content")
|
|
384
|
+
"""
|
|
385
|
+
|
|
386
|
+
def __init__(self, field_name: str):
|
|
387
|
+
self.field_name = field_name
|
|
388
|
+
super().__init__(f"Deferred field '{field_name}' not loaded")
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class PrimaryKeyError(SQLObjectsError):
|
|
392
|
+
"""Raised when there's an issue with primary key operations.
|
|
393
|
+
|
|
394
|
+
This exception is raised for primary key related errors such as
|
|
395
|
+
missing primary key values for update/delete operations.
|
|
396
|
+
|
|
397
|
+
Examples:
|
|
398
|
+
>>> try:
|
|
399
|
+
... await user.delete() # user has no primary key
|
|
400
|
+
... except PrimaryKeyError:
|
|
401
|
+
... print("Cannot delete without primary key")
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
def __init__(self, message: str, operation: str | None = None):
|
|
405
|
+
self.operation = operation
|
|
406
|
+
super().__init__(message)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class SQLError(SQLObjectsError):
|
|
410
|
+
"""Base class for SQLAlchemy operation errors.
|
|
411
|
+
|
|
412
|
+
This exception wraps SQLAlchemy errors to provide a consistent
|
|
413
|
+
exception interface for SQLObjects users.
|
|
414
|
+
"""
|
|
415
|
+
|
|
416
|
+
def __init__(self, message: str, original_error: Exception | None = None):
|
|
417
|
+
self.original_error = original_error
|
|
418
|
+
super().__init__(message)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
class OperationalError(SQLError):
|
|
422
|
+
"""Database operational errors (connection, timeout, etc.).
|
|
423
|
+
|
|
424
|
+
This exception is raised for operational database errors such as
|
|
425
|
+
connection failures, timeouts, or server unavailability.
|
|
426
|
+
"""
|
|
427
|
+
|
|
428
|
+
pass
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
class DataError(SQLError):
|
|
432
|
+
"""Data-related errors (invalid values, type conversion).
|
|
433
|
+
|
|
434
|
+
This exception is raised for data-related errors such as invalid
|
|
435
|
+
data types, constraint violations, or conversion failures.
|
|
436
|
+
"""
|
|
437
|
+
|
|
438
|
+
pass
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
class ProgrammingError(SQLError):
|
|
442
|
+
"""SQL programming errors (syntax, missing tables).
|
|
443
|
+
|
|
444
|
+
This exception is raised for programming errors such as SQL syntax
|
|
445
|
+
errors, missing tables, or invalid column references.
|
|
446
|
+
"""
|
|
447
|
+
|
|
448
|
+
pass
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
# Private global error message mapping
|
|
452
|
+
_ERROR_MESSAGES = {
|
|
453
|
+
"required": "This field is required",
|
|
454
|
+
"invalid": "Invalid value",
|
|
455
|
+
"min_length": "Ensure this value has at least {min_length} characters",
|
|
456
|
+
"max_length": "Ensure this value has at most {max_length} characters",
|
|
457
|
+
"min_value": "Ensure this value is greater than or equal to {min_value}",
|
|
458
|
+
"max_value": "Ensure this value is less than or equal to {max_value}",
|
|
459
|
+
"invalid_email": "Enter a valid email address",
|
|
460
|
+
"invalid_url": "Enter a valid URL",
|
|
461
|
+
"invalid_choice": "'{value}' is not a valid choice",
|
|
462
|
+
"invalid_date": "Enter a valid date",
|
|
463
|
+
"invalid_time": "Enter a valid time",
|
|
464
|
+
"invalid_decimal": "Enter a valid decimal number",
|
|
465
|
+
"invalid_json": "Enter valid JSON",
|
|
466
|
+
"file_not_found": "File not found: {path}",
|
|
467
|
+
"file_too_large": "File size {size} exceeds maximum allowed size {max_size}",
|
|
468
|
+
"file_too_small": "File size {size} is below minimum required size {min_size}",
|
|
469
|
+
"invalid_file_type": "File type '{file_type}' is not allowed. Allowed types: {allowed_types}",
|
|
470
|
+
"file_access_error": "Cannot access file: {error}",
|
|
471
|
+
"invalid_file": "Invalid file",
|
|
472
|
+
"not_an_image": "File is not a valid image",
|
|
473
|
+
"not_implemented": "This method must be implemented by subclasses",
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def create_validation_error(
|
|
478
|
+
code: str,
|
|
479
|
+
field: str | None = None,
|
|
480
|
+
params: dict[str, Any] | None = None,
|
|
481
|
+
) -> ValidationError:
|
|
482
|
+
"""Create a ValidationError with English message.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
code: Error code for message lookup
|
|
486
|
+
field: Field name for the error
|
|
487
|
+
params: Parameters for message formatting
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
ValidationError instance with English message
|
|
491
|
+
|
|
492
|
+
Examples:
|
|
493
|
+
>>> error = create_validation_error("required", field="email")
|
|
494
|
+
>>> error = create_validation_error("min_length", params={"min_length": 3})
|
|
495
|
+
"""
|
|
496
|
+
message = _ERROR_MESSAGES.get(code, code)
|
|
497
|
+
if params:
|
|
498
|
+
try:
|
|
499
|
+
message = message.format(**params)
|
|
500
|
+
except (KeyError, ValueError):
|
|
501
|
+
pass
|
|
502
|
+
|
|
503
|
+
return ValidationError(message, field=field, code=code, params=params)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def convert_sqlalchemy_error(error: Exception) -> SQLObjectsError:
|
|
507
|
+
"""Convert SQLAlchemy exceptions to SQLObjects exceptions.
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
error: SQLAlchemy exception to convert
|
|
511
|
+
|
|
512
|
+
Returns:
|
|
513
|
+
Corresponding SQLObjects exception
|
|
514
|
+
|
|
515
|
+
Examples:
|
|
516
|
+
>>> try:
|
|
517
|
+
... # SQLAlchemy operation
|
|
518
|
+
... pass
|
|
519
|
+
... except SQLAlchemyError as e:
|
|
520
|
+
... raise convert_sqlalchemy_error(e)
|
|
521
|
+
"""
|
|
522
|
+
error_msg = str(error)
|
|
523
|
+
|
|
524
|
+
if isinstance(error, SQLAIntegrityError):
|
|
525
|
+
return IntegrityError(error_msg, original_error=error)
|
|
526
|
+
elif isinstance(error, SQLAOperationalError):
|
|
527
|
+
return OperationalError(error_msg, original_error=error)
|
|
528
|
+
elif isinstance(error, SQLADataError):
|
|
529
|
+
return DataError(error_msg, original_error=error)
|
|
530
|
+
elif isinstance(error, SQLAProgrammingError):
|
|
531
|
+
return ProgrammingError(error_msg, original_error=error)
|
|
532
|
+
elif isinstance(error, SQLADisconnectionError):
|
|
533
|
+
return OperationalError(error_msg, original_error=error)
|
|
534
|
+
elif isinstance(error, SQLAlchemyError):
|
|
535
|
+
return SQLError(error_msg, original_error=error)
|
|
536
|
+
else:
|
|
537
|
+
# Not a SQLAlchemy error, return as-is or wrap in generic SQLError
|
|
538
|
+
return SQLError(error_msg, original_error=error)
|