velocity-python 0.0.109__tar.gz → 0.0.110__tar.gz

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.

Files changed (82) hide show
  1. {velocity_python-0.0.109 → velocity_python-0.0.110}/PKG-INFO +1 -1
  2. {velocity_python-0.0.109 → velocity_python-0.0.110}/pyproject.toml +1 -1
  3. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/engine.py +76 -176
  5. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity_python.egg-info/PKG-INFO +1 -1
  6. {velocity_python-0.0.109 → velocity_python-0.0.110}/LICENSE +0 -0
  7. {velocity_python-0.0.109 → velocity_python-0.0.110}/README.md +0 -0
  8. {velocity_python-0.0.109 → velocity_python-0.0.110}/setup.cfg +0 -0
  9. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/app/__init__.py +0 -0
  10. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/app/invoices.py +0 -0
  11. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/app/orders.py +0 -0
  12. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/app/payments.py +0 -0
  13. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/app/purchase_orders.py +0 -0
  14. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/aws/__init__.py +0 -0
  15. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/aws/amplify.py +0 -0
  16. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/aws/handlers/__init__.py +0 -0
  17. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/aws/handlers/context.py +0 -0
  18. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  19. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/aws/handlers/response.py +0 -0
  20. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  21. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/__init__.py +0 -0
  22. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/__init__.py +0 -0
  23. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/column.py +0 -0
  24. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/database.py +0 -0
  25. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/decorators.py +0 -0
  26. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/exceptions.py +0 -0
  27. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/result.py +0 -0
  28. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/row.py +0 -0
  29. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/sequence.py +0 -0
  30. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/table.py +0 -0
  31. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/core/transaction.py +0 -0
  32. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/exceptions.py +0 -0
  33. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/__init__.py +0 -0
  34. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/mysql.py +0 -0
  35. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/mysql_reserved.py +0 -0
  36. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/postgres/__init__.py +0 -0
  37. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/postgres/operators.py +0 -0
  38. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/postgres/reserved.py +0 -0
  39. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/postgres/sql.py +0 -0
  40. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/postgres/types.py +0 -0
  41. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/sqlite.py +0 -0
  42. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/sqlite_reserved.py +0 -0
  43. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/sqlserver.py +0 -0
  44. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/sqlserver_reserved.py +0 -0
  45. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/servers/tablehelper.py +0 -0
  46. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/db/utils.py +0 -0
  47. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/__init__.py +0 -0
  48. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/conv/__init__.py +0 -0
  49. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/conv/iconv.py +0 -0
  50. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/conv/oconv.py +0 -0
  51. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/db.py +0 -0
  52. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/export.py +0 -0
  53. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/format.py +0 -0
  54. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/mail.py +0 -0
  55. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/merge.py +0 -0
  56. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/timer.py +0 -0
  57. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity/misc/tools.py +0 -0
  58. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity_python.egg-info/SOURCES.txt +0 -0
  59. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  60. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity_python.egg-info/requires.txt +0 -0
  61. {velocity_python-0.0.109 → velocity_python-0.0.110}/src/velocity_python.egg-info/top_level.txt +0 -0
  62. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_cursor_rowcount_fix.py +0 -0
  63. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_db.py +0 -0
  64. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_db_utils.py +0 -0
  65. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_email_processing.py +0 -0
  66. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_fix.py +0 -0
  67. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_format.py +0 -0
  68. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_iconv.py +0 -0
  69. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_merge.py +0 -0
  70. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_oconv.py +0 -0
  71. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_original_error.py +0 -0
  72. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_payment_profile_sorting.py +0 -0
  73. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_postgres.py +0 -0
  74. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_process_error_robustness.py +0 -0
  75. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_response.py +0 -0
  76. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_result_caching.py +0 -0
  77. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_result_sql_aware.py +0 -0
  78. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_row_get_missing_column.py +0 -0
  79. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_spreadsheet_functions.py +0 -0
  80. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_sql_builder.py +0 -0
  81. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_tablehelper.py +0 -0
  82. {velocity_python-0.0.109 → velocity_python-0.0.110}/tests/test_timer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.109
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "velocity-python"
7
- version = "0.0.109"
7
+ version = "0.0.110"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.0.109"
1
+ __version__ = version = "0.0.110"
2
2
 
3
3
  from . import aws
4
4
  from . import db
@@ -24,38 +24,6 @@ class Engine:
24
24
  self.__sql = sql
25
25
  self.__driver = driver
26
26
  self.__connect_timeout = connect_timeout
27
-
28
- # Set up error code mappings from the SQL class
29
- self._setup_error_mappings()
30
-
31
- def _setup_error_mappings(self):
32
- """
33
- Set up error code to exception class mappings from the SQL driver.
34
- """
35
- self.error_codes = {}
36
-
37
- # Map error codes to exception class names
38
- sql_attrs = [
39
- ('ApplicationErrorCodes', 'DbApplicationError'),
40
- ('DatabaseMissingErrorCodes', 'DbDatabaseMissingError'),
41
- ('TableMissingErrorCodes', 'DbTableMissingError'),
42
- ('ColumnMissingErrorCodes', 'DbColumnMissingError'),
43
- ('ForeignKeyMissingErrorCodes', 'DbForeignKeyMissingError'),
44
- ('ConnectionErrorCodes', 'DbConnectionError'),
45
- ('DuplicateKeyErrorCodes', 'DbDuplicateKeyError'),
46
- ('RetryTransactionCodes', 'DbRetryTransaction'),
47
- ('TruncationErrorCodes', 'DbTruncationError'),
48
- ('LockTimeoutErrorCodes', 'DbLockTimeoutError'),
49
- ('DatabaseObjectExistsErrorCodes', 'DbObjectExistsError'),
50
- ('DataIntegrityErrorCodes', 'DbDataIntegrityError')
51
- ]
52
-
53
- for attr_name, exception_class in sql_attrs:
54
- if hasattr(self.sql, attr_name):
55
- codes = getattr(self.sql, attr_name)
56
- if codes: # Only add non-empty lists
57
- for code in codes:
58
- self.error_codes[str(code)] = exception_class
59
27
 
60
28
  def __str__(self):
61
29
  return f"[{self.sql.server}] engine({self.config})"
@@ -359,161 +327,93 @@ class Engine:
359
327
 
360
328
  def process_error(self, exception, sql=None, parameters=None):
361
329
  """
362
- Process database errors and raise appropriate velocity exceptions.
363
- Enhanced for robustness with exception chaining and comprehensive error handling.
364
-
365
- Args:
366
- exception: The original exception from the database driver
367
- sql: The SQL statement that caused the error (optional)
368
- parameters: The parameters passed to the SQL statement (optional)
369
-
370
- Raises:
371
- The appropriate velocity exception with proper chaining
330
+ Central method to parse driver exceptions and re-raise them as our custom exceptions.
372
331
  """
373
332
  logger = logging.getLogger(__name__)
374
333
 
375
- # Enhanced logging with context - more readable format
376
- sql_preview = sql[:100] + "..." if sql and len(sql) > 100 else sql or "None"
377
-
378
- logger.error(
379
- f"🔴 Database Error Detected\n"
380
- f" Exception Type: {type(exception).__name__}\n"
381
- f" SQL Statement: {sql_preview}\n"
382
- f" Processing error for classification..."
383
- )
334
+ # If it's already a velocity exception, just re-raise it
335
+ if isinstance(exception, exceptions.DbException):
336
+ raise exception
384
337
 
385
- # Safely get error code and message with fallbacks
386
- try:
387
- # Try PostgreSQL-specific error code first, then use SQL driver's get_error method
388
- error_code = getattr(exception, 'pgcode', None)
389
- if not error_code and hasattr(self.sql, 'get_error'):
390
- try:
391
- error_code, error_message_from_driver = self.sql.get_error(exception)
392
- if error_message_from_driver:
393
- error_message = error_message_from_driver
394
- except Exception as get_error_exception:
395
- logger.warning(f"⚠️ SQL driver get_error failed: {get_error_exception}")
396
- error_code = None
397
- except Exception as e:
398
- logger.warning(f"⚠️ Unable to extract database error code: {e}")
399
- error_code = None
400
-
338
+ # Get error code and message from the SQL driver
401
339
  try:
402
- error_message = str(exception)
403
- except Exception as e:
404
- logger.warning(f"⚠️ Unable to convert exception to string: {e}")
405
- error_message = f"<Error converting exception: {type(exception).__name__}>"
406
-
407
- # Primary error classification by error code
408
- if error_code and hasattr(self, 'error_codes') and str(error_code) in self.error_codes:
409
- error_class = self.error_codes[str(error_code)]
410
- logger.info(f"✅ Successfully classified error: {error_code} → {error_class}")
411
- try:
412
- raise self._create_exception_with_chaining(
413
- error_class, error_message, exception, sql, parameters
414
- )
415
- except Exception as creation_error:
416
- logger.error(f"❌ Failed to create {error_class} exception: {creation_error}")
417
- # Fall through to regex classification
418
-
419
- # Secondary error classification by message patterns (regex fallback)
420
- error_message_lower = error_message.lower()
421
-
422
- # Enhanced connection error patterns
423
- connection_patterns = [
424
- r'connection.*refused|could not connect',
425
- r'network.*unreachable|network.*down',
426
- r'broken pipe|connection.*broken',
427
- r'timeout.*connection|connection.*timeout',
428
- r'server.*closed.*connection|connection.*lost',
429
- r'no route to host|host.*unreachable',
430
- r'connection.*reset|reset.*connection'
431
- ]
432
-
433
- # Enhanced duplicate key patterns
434
- duplicate_patterns = [
435
- r'duplicate.*key.*value|unique.*constraint.*violated',
436
- r'duplicate.*entry|key.*already.*exists',
437
- r'violates.*unique.*constraint',
438
- r'unique.*violation|constraint.*unique'
439
- ]
440
-
441
- # Enhanced permission/authorization patterns
442
- permission_patterns = [
443
- r'permission.*denied|access.*denied|authorization.*failed',
444
- r'insufficient.*privileges|privilege.*denied',
445
- r'not.*authorized|unauthorized.*access',
446
- r'authentication.*failed|login.*failed'
447
- ]
448
-
449
- # Enhanced database/table not found patterns
450
- not_found_patterns = [
451
- r'database.*does.*not.*exist|unknown.*database',
452
- r'table.*does.*not.*exist|relation.*does.*not.*exist',
453
- r'no.*such.*database|database.*not.*found',
454
- r'schema.*does.*not.*exist|unknown.*table'
455
- ]
456
-
457
- # Enhanced column missing patterns
458
- column_missing_patterns = [
459
- r'column.*does.*not.*exist',
460
- r'unknown.*column|column.*not.*found',
461
- r'no.*such.*column|invalid.*column.*name'
462
- ]
463
-
464
- # Enhanced syntax error patterns
465
- syntax_patterns = [
466
- r'syntax.*error|invalid.*syntax',
467
- r'malformed.*query|bad.*sql.*grammar',
468
- r'unexpected.*token|parse.*error'
469
- ]
470
-
471
- # Enhanced deadlock/timeout patterns
472
- deadlock_patterns = [
473
- r'deadlock.*detected|lock.*timeout',
474
- r'timeout.*waiting.*for.*lock|query.*timeout',
475
- r'lock.*wait.*timeout|deadlock.*found'
476
- ]
340
+ error_code, error_message = self.sql.get_error(exception)
341
+ except Exception:
342
+ error_code, error_message = None, str(exception)
477
343
 
478
- # Comprehensive pattern matching with error class mapping
479
- pattern_mappings = [
480
- (connection_patterns, 'DbConnectionError'),
481
- (duplicate_patterns, 'DbDuplicateKeyError'),
482
- (permission_patterns, 'DbPermissionError'),
483
- (not_found_patterns, 'DbTableMissingError'),
484
- (column_missing_patterns, 'DbColumnMissingError'),
485
- (syntax_patterns, 'DbSyntaxError'),
486
- (deadlock_patterns, 'DbDeadlockError')
487
- ]
344
+ msg = str(exception).strip().lower()
488
345
 
489
- # Apply pattern matching
490
- for patterns, error_class in pattern_mappings:
491
- for pattern in patterns:
492
- try:
493
- if re.search(pattern, error_message_lower):
494
- logger.info(f"✅ Classified error by pattern match: '{pattern}' → {error_class}")
495
- raise self._create_exception_with_chaining(
496
- error_class, error_message, exception, sql, parameters
497
- )
498
- except re.error as regex_error:
499
- logger.warning(f"⚠️ Regex pattern error for '{pattern}': {regex_error}")
500
- continue
501
- except Exception as pattern_error:
502
- logger.error(f"❌ Error applying pattern '{pattern}': {pattern_error}")
503
- continue
504
-
505
- # Fallback: return generic database error with full context
506
- available_codes = list(getattr(self, 'error_codes', {}).keys()) if hasattr(self, 'error_codes') else []
507
346
  logger.warning(
508
- f"⚠️ Unable to classify database error automatically\n"
509
- f" → Falling back to generic DatabaseError\n"
510
- f" → Error Code: {error_code or 'Unknown'}\n"
511
- 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,
512
350
  )
513
351
 
514
- raise self._create_exception_with_chaining(
515
- '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
+ },
516
413
  )
414
+
415
+ # If we can't classify it, re-raise the original exception
416
+ raise exception
517
417
 
518
418
  def _format_human_readable_error(self, error_class, message, original_exception, sql=None, parameters=None, format_type='console'):
519
419
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.109
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