velocity-python 0.0.108__py3-none-any.whl → 0.0.110__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.

Potentially problematic release.


This version of velocity-python might be problematic. Click here for more details.

velocity/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.0.108"
1
+ __version__ = version = "0.0.110"
2
2
 
3
3
  from . import aws
4
4
  from . import db
@@ -327,146 +327,93 @@ class Engine:
327
327
 
328
328
  def process_error(self, exception, sql=None, parameters=None):
329
329
  """
330
- Process database errors and raise appropriate velocity exceptions.
331
- Enhanced for robustness with exception chaining and comprehensive error handling.
332
-
333
- Args:
334
- exception: The original exception from the database driver
335
- sql: The SQL statement that caused the error (optional)
336
- parameters: The parameters passed to the SQL statement (optional)
337
-
338
- Raises:
339
- The appropriate velocity exception with proper chaining
330
+ Central method to parse driver exceptions and re-raise them as our custom exceptions.
340
331
  """
341
332
  logger = logging.getLogger(__name__)
342
333
 
343
- # Enhanced logging with context - more readable format
344
- sql_preview = sql[:100] + "..." if sql and len(sql) > 100 else sql or "None"
345
-
346
- logger.error(
347
- f"🔴 Database Error Detected\n"
348
- f" Exception Type: {type(exception).__name__}\n"
349
- f" SQL Statement: {sql_preview}\n"
350
- f" Processing error for classification..."
351
- )
334
+ # If it's already a velocity exception, just re-raise it
335
+ if isinstance(exception, exceptions.DbException):
336
+ raise exception
352
337
 
353
- # Safely get error code and message with fallbacks
338
+ # Get error code and message from the SQL driver
354
339
  try:
355
- error_code = getattr(exception, 'pgcode', None) or self.get_error(exception)
356
- except Exception as e:
357
- logger.warning(f"⚠️ Unable to extract database error code: {e}")
358
- error_code = None
359
-
360
- try:
361
- error_message = str(exception)
362
- except Exception as e:
363
- logger.warning(f"⚠️ Unable to convert exception to string: {e}")
364
- error_message = f"<Error converting exception: {type(exception).__name__}>"
365
-
366
- # Primary error classification by error code
367
- if error_code and hasattr(self, 'error_codes'):
368
- for error_class, codes in self.error_codes.items():
369
- if error_code in codes:
370
- logger.info(f"✅ Successfully classified error: {error_code} → {error_class}")
371
- try:
372
- raise self._create_exception_with_chaining(
373
- error_class, error_message, exception, sql, parameters
374
- )
375
- except Exception as creation_error:
376
- logger.error(f"❌ Failed to create {error_class} exception: {creation_error}")
377
- # Fall through to regex classification
378
- break
379
-
380
- # Secondary error classification by message patterns (regex fallback)
381
- error_message_lower = error_message.lower()
382
-
383
- # Enhanced connection error patterns
384
- connection_patterns = [
385
- r'connection.*refused|could not connect',
386
- r'network.*unreachable|network.*down',
387
- r'broken pipe|connection.*broken',
388
- r'timeout.*connection|connection.*timeout',
389
- r'server.*closed.*connection|connection.*lost',
390
- r'no route to host|host.*unreachable',
391
- r'connection.*reset|reset.*connection'
392
- ]
393
-
394
- # Enhanced duplicate key patterns
395
- duplicate_patterns = [
396
- r'duplicate.*key.*value|unique.*constraint.*violated',
397
- r'duplicate.*entry|key.*already.*exists',
398
- r'violates.*unique.*constraint',
399
- r'unique.*violation|constraint.*unique'
400
- ]
401
-
402
- # Enhanced permission/authorization patterns
403
- permission_patterns = [
404
- r'permission.*denied|access.*denied|authorization.*failed',
405
- r'insufficient.*privileges|privilege.*denied',
406
- r'not.*authorized|unauthorized.*access',
407
- r'authentication.*failed|login.*failed'
408
- ]
409
-
410
- # Enhanced database/table not found patterns
411
- not_found_patterns = [
412
- r'database.*does.*not.*exist|unknown.*database',
413
- r'table.*does.*not.*exist|relation.*does.*not.*exist',
414
- r'no.*such.*database|database.*not.*found',
415
- r'schema.*does.*not.*exist|unknown.*table'
416
- ]
417
-
418
- # Enhanced syntax error patterns
419
- syntax_patterns = [
420
- r'syntax.*error|invalid.*syntax',
421
- r'malformed.*query|bad.*sql.*grammar',
422
- r'unexpected.*token|parse.*error'
423
- ]
424
-
425
- # Enhanced deadlock/timeout patterns
426
- deadlock_patterns = [
427
- r'deadlock.*detected|lock.*timeout',
428
- r'timeout.*waiting.*for.*lock|query.*timeout',
429
- r'lock.*wait.*timeout|deadlock.*found'
430
- ]
431
-
432
- # Comprehensive pattern matching with error class mapping
433
- pattern_mappings = [
434
- (connection_patterns, 'ConnectionError'),
435
- (duplicate_patterns, 'DuplicateError'),
436
- (permission_patterns, 'PermissionError'),
437
- (not_found_patterns, 'NotFoundError'),
438
- (syntax_patterns, 'SyntaxError'),
439
- (deadlock_patterns, 'DeadlockError')
440
- ]
340
+ error_code, error_message = self.sql.get_error(exception)
341
+ except Exception:
342
+ error_code, error_message = None, str(exception)
441
343
 
442
- # Apply pattern matching
443
- for patterns, error_class in pattern_mappings:
444
- for pattern in patterns:
445
- try:
446
- if re.search(pattern, error_message_lower):
447
- logger.info(f"✅ Classified error by pattern match: '{pattern}' → {error_class}")
448
- raise self._create_exception_with_chaining(
449
- error_class, error_message, exception, sql, parameters
450
- )
451
- except re.error as regex_error:
452
- logger.warning(f"⚠️ Regex pattern error for '{pattern}': {regex_error}")
453
- continue
454
- except Exception as pattern_error:
455
- logger.error(f"❌ Error applying pattern '{pattern}': {pattern_error}")
456
- continue
344
+ msg = str(exception).strip().lower()
457
345
 
458
- # Fallback: return generic database error with full context
459
- available_codes = list(getattr(self, 'error_codes', {}).keys()) if hasattr(self, 'error_codes') else []
460
346
  logger.warning(
461
- f"⚠️ Unable to classify database error automatically\n"
462
- f" → Falling back to generic DatabaseError\n"
463
- f" → Error Code: {error_code or 'Unknown'}\n"
464
- f" → Available Classifications: {available_codes or 'None configured'}"
347
+ "Database error caught. Attempting to transform: code=%s message=%s",
348
+ error_code,
349
+ error_message,
465
350
  )
466
351
 
467
- raise self._create_exception_with_chaining(
468
- 'DatabaseError', error_message, exception, sql, parameters
352
+ # Direct error code mapping
353
+ if error_code in self.sql.ApplicationErrorCodes:
354
+ raise exceptions.DbApplicationError(str(exception)) from exception
355
+ if error_code in self.sql.ColumnMissingErrorCodes:
356
+ raise exceptions.DbColumnMissingError(str(exception)) from exception
357
+ if error_code in self.sql.TableMissingErrorCodes:
358
+ raise exceptions.DbTableMissingError(str(exception)) from exception
359
+ if error_code in self.sql.DatabaseMissingErrorCodes:
360
+ raise exceptions.DbDatabaseMissingError(str(exception)) from exception
361
+ if error_code in self.sql.ForeignKeyMissingErrorCodes:
362
+ raise exceptions.DbForeignKeyMissingError(str(exception)) from exception
363
+ if error_code in self.sql.TruncationErrorCodes:
364
+ raise exceptions.DbTruncationError(str(exception)) from exception
365
+ if error_code in self.sql.DataIntegrityErrorCodes:
366
+ raise exceptions.DbDataIntegrityError(str(exception)) from exception
367
+ if error_code in self.sql.ConnectionErrorCodes:
368
+ raise exceptions.DbConnectionError(str(exception)) from exception
369
+ if error_code in self.sql.DuplicateKeyErrorCodes:
370
+ raise exceptions.DbDuplicateKeyError(str(exception)) from exception
371
+ if error_code in self.sql.DatabaseObjectExistsErrorCodes:
372
+ raise exceptions.DbObjectExistsError(str(exception)) from exception
373
+ if error_code in self.sql.LockTimeoutErrorCodes:
374
+ raise exceptions.DbLockTimeoutError(str(exception)) from exception
375
+ if error_code in self.sql.RetryTransactionCodes:
376
+ raise exceptions.DbRetryTransaction(str(exception)) from exception
377
+
378
+ # Regex-based fallback patterns
379
+ if re.search(r"key \(sys_id\)=\(\d+\) already exists.", msg, re.M):
380
+ raise exceptions.DbDuplicateKeyError(str(exception)) from exception
381
+ if re.findall(r"database.*does not exist", msg, re.M):
382
+ raise exceptions.DbDatabaseMissingError(str(exception)) from exception
383
+ if re.findall(r"no such database", msg, re.M):
384
+ raise exceptions.DbDatabaseMissingError(str(exception)) from exception
385
+ if re.findall(r"already exists", msg, re.M):
386
+ raise exceptions.DbObjectExistsError(str(exception)) from exception
387
+ if re.findall(r"server closed the connection unexpectedly", msg, re.M):
388
+ raise exceptions.DbConnectionError(str(exception)) from exception
389
+ if re.findall(r"no connection to the server", msg, re.M):
390
+ raise exceptions.DbConnectionError(str(exception)) from exception
391
+ if re.findall(r"connection timed out", msg, re.M):
392
+ raise exceptions.DbConnectionError(str(exception)) from exception
393
+ if re.findall(r"could not connect to server", msg, re.M):
394
+ raise exceptions.DbConnectionError(str(exception)) from exception
395
+ if re.findall(r"cannot connect to server", msg, re.M):
396
+ raise exceptions.DbConnectionError(str(exception)) from exception
397
+ if re.findall(r"connection already closed", msg, re.M):
398
+ raise exceptions.DbConnectionError(str(exception)) from exception
399
+ if re.findall(r"cursor already closed", msg, re.M):
400
+ raise exceptions.DbConnectionError(str(exception)) from exception
401
+ if "no such table:" in msg:
402
+ raise exceptions.DbTableMissingError(str(exception)) from exception
403
+
404
+ logger.error(
405
+ "Unhandled/Unknown Error in engine.process_error",
406
+ exc_info=True,
407
+ extra={
408
+ "error_code": error_code,
409
+ "error_msg": error_message,
410
+ "sql_stmt": sql,
411
+ "sql_params": parameters,
412
+ },
469
413
  )
414
+
415
+ # If we can't classify it, re-raise the original exception
416
+ raise exception
470
417
 
471
418
  def _format_human_readable_error(self, error_class, message, original_exception, sql=None, parameters=None, format_type='console'):
472
419
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.108
3
+ Version: 0.0.110
4
4
  Summary: A rapid application development library for interfacing with data storage
5
5
  Author-email: Velocity Team <info@codeclubs.org>
6
6
  License-Expression: MIT
@@ -1,4 +1,4 @@
1
- velocity/__init__.py,sha256=--SXChkiTr8nQ6NCjSI78kO3i4IrqNpzMSXnF_CTEl4,107
1
+ velocity/__init__.py,sha256=5Na4DTc1X43viY7Pm3xBOpXkedLfKb2sDq7Y3pK6Z8M,107
2
2
  velocity/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  velocity/app/invoices.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  velocity/app/orders.py,sha256=W-HAXEwY8-IFXbKh82HnMeRVZM7P-TWGEQOWtkLIzI4,6298
@@ -18,7 +18,7 @@ velocity/db/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
18
18
  velocity/db/core/column.py,sha256=tAr8tL3a2nyaYpNHhGl508FrY_pGZTzyYgjAV5CEBv4,4092
19
19
  velocity/db/core/database.py,sha256=3zNGItklu9tZCKsbx2T2vCcU1so8AL9PPL0DLjvaz6s,3554
20
20
  velocity/db/core/decorators.py,sha256=76Jkr9XptXt8cvcgp1zbHfuL8uHzWy8lwfR29u-DVu4,4574
21
- velocity/db/core/engine.py,sha256=iLQSr9hvn4w78EGiCbS2WsiojQgPYRBW_EGjTS2074g,49225
21
+ velocity/db/core/engine.py,sha256=ykF-UjYlDqDgD_KDLp7kiot-TEOrh5BifnPLyQEewgo,47549
22
22
  velocity/db/core/exceptions.py,sha256=tuDniRqTX8Opc2d033LPJOI3Ux4NSwUcHqW729n-HXA,1027
23
23
  velocity/db/core/result.py,sha256=dgiOXH-iJXuDH4PbSTWVkn-heAkJQcXCC-gs0ZuqF94,12814
24
24
  velocity/db/core/row.py,sha256=zZ3zZbWjZkZfYAYuZJLHFJ8jdXc7dYv8Iyv9Ut8W8tE,7261
@@ -49,8 +49,8 @@ velocity/misc/tools.py,sha256=_bGneHHA_BV-kUonzw5H3hdJ5AOJRCKfzhgpkFbGqIo,1502
49
49
  velocity/misc/conv/__init__.py,sha256=MLYF58QHjzfDSxb1rdnmLnuEQCa3gnhzzZ30CwZVvQo,40
50
50
  velocity/misc/conv/iconv.py,sha256=d4_BucW8HTIkGNurJ7GWrtuptqUf-9t79ObzjJ5N76U,10603
51
51
  velocity/misc/conv/oconv.py,sha256=h5Lo05DqOQnxoD3y6Px_MQP_V-pBbWf8Hkgkb9Xp1jk,6032
52
- velocity_python-0.0.108.dist-info/licenses/LICENSE,sha256=aoN245GG8s9oRUU89KNiGTU4_4OtnNmVi4hQeChg6rM,1076
53
- velocity_python-0.0.108.dist-info/METADATA,sha256=gBHRoiRyaXBQHlbmh9tYPxR0DTV5ZU_8n2F3GEvjIW0,34262
54
- velocity_python-0.0.108.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
55
- velocity_python-0.0.108.dist-info/top_level.txt,sha256=JW2vJPmodgdgSz7H6yoZvnxF8S3fTMIv-YJWCT1sNW0,9
56
- velocity_python-0.0.108.dist-info/RECORD,,
52
+ velocity_python-0.0.110.dist-info/licenses/LICENSE,sha256=aoN245GG8s9oRUU89KNiGTU4_4OtnNmVi4hQeChg6rM,1076
53
+ velocity_python-0.0.110.dist-info/METADATA,sha256=KSop21_ZqWqh1EZNPMcOge75C9POxKMVG5ZQhg6NrrM,34262
54
+ velocity_python-0.0.110.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
55
+ velocity_python-0.0.110.dist-info/top_level.txt,sha256=JW2vJPmodgdgSz7H6yoZvnxF8S3fTMIv-YJWCT1sNW0,9
56
+ velocity_python-0.0.110.dist-info/RECORD,,