velocity-python 0.0.204__tar.gz → 0.0.205__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.
Files changed (149) hide show
  1. {velocity_python-0.0.204 → velocity_python-0.0.205}/PKG-INFO +1 -1
  2. {velocity_python-0.0.204 → velocity_python-0.0.205}/pyproject.toml +1 -1
  3. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/base_handler.py +50 -6
  5. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/engine.py +5 -51
  6. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/base/sql.py +37 -0
  7. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity_python.egg-info/PKG-INFO +1 -1
  8. {velocity_python-0.0.204 → velocity_python-0.0.205}/LICENSE +0 -0
  9. {velocity_python-0.0.204 → velocity_python-0.0.205}/README.md +0 -0
  10. {velocity_python-0.0.204 → velocity_python-0.0.205}/setup.cfg +0 -0
  11. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/app/__init__.py +0 -0
  12. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/app/invoices.py +0 -0
  13. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/app/orders.py +0 -0
  14. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/app/payments.py +0 -0
  15. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/app/purchase_orders.py +0 -0
  16. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/app/tests/__init__.py +0 -0
  17. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/app/tests/test_email_processing.py +0 -0
  18. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/app/tests/test_payment_profile_sorting.py +0 -0
  19. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/app/tests/test_spreadsheet_functions.py +0 -0
  20. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/__init__.py +0 -0
  21. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/amplify.py +0 -0
  22. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/__init__.py +0 -0
  23. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/context.py +0 -0
  24. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/context_factory.py +0 -0
  25. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/exceptions.py +0 -0
  26. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  27. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  28. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  29. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  30. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/perf.py +0 -0
  31. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/response.py +0 -0
  32. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  33. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/tests/__init__.py +0 -0
  34. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  35. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/aws/tests/test_response.py +0 -0
  36. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/__init__.py +0 -0
  37. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/__init__.py +0 -0
  38. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/column.py +0 -0
  39. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/database.py +0 -0
  40. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/decorators.py +0 -0
  41. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/result.py +0 -0
  42. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/row.py +0 -0
  43. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/sequence.py +0 -0
  44. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/table.py +0 -0
  45. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/transaction.py +0 -0
  46. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/core/view.py +0 -0
  47. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/exceptions.py +0 -0
  48. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/__init__.py +0 -0
  49. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/base/__init__.py +0 -0
  50. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/base/initializer.py +0 -0
  51. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/base/operators.py +0 -0
  52. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/base/types.py +0 -0
  53. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/mysql/__init__.py +0 -0
  54. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/mysql/operators.py +0 -0
  55. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/mysql/reserved.py +0 -0
  56. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/mysql/sql.py +0 -0
  57. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/mysql/types.py +0 -0
  58. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/postgres/__init__.py +0 -0
  59. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/postgres/operators.py +0 -0
  60. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/postgres/reserved.py +0 -0
  61. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/postgres/sql.py +0 -0
  62. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/postgres/types.py +0 -0
  63. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  64. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/sqlite/operators.py +0 -0
  65. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  66. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/sqlite/sql.py +0 -0
  67. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/sqlite/types.py +0 -0
  68. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  69. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  70. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  71. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  72. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/sqlserver/types.py +0 -0
  73. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/servers/tablehelper.py +0 -0
  74. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/__init__.py +0 -0
  75. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/common_db_test.py +0 -0
  76. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/__init__.py +0 -0
  77. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/common.py +0 -0
  78. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_column.py +0 -0
  79. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  80. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_database.py +0 -0
  81. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  82. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  83. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  84. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_result.py +0 -0
  85. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_row.py +0 -0
  86. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  87. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  88. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  89. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  90. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  91. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_table.py +0 -0
  92. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  93. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  94. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/sql/__init__.py +0 -0
  95. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/sql/common.py +0 -0
  96. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  97. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  98. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  99. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_db_utils.py +0 -0
  100. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_postgres.py +0 -0
  101. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  102. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  103. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_result_caching.py +0 -0
  104. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  105. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  106. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  107. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  108. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_sql_builder.py +0 -0
  109. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_tablehelper.py +0 -0
  110. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/tests/test_view_helper.py +0 -0
  111. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/db/utils.py +0 -0
  112. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/logging.py +0 -0
  113. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/__init__.py +0 -0
  114. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/conv/__init__.py +0 -0
  115. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/conv/iconv.py +0 -0
  116. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/conv/oconv.py +0 -0
  117. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/db.py +0 -0
  118. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/export.py +0 -0
  119. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/format.py +0 -0
  120. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/mail.py +0 -0
  121. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/merge.py +0 -0
  122. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/tests/__init__.py +0 -0
  123. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/tests/test_db.py +0 -0
  124. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/tests/test_fix.py +0 -0
  125. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/tests/test_format.py +0 -0
  126. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/tests/test_iconv.py +0 -0
  127. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/tests/test_merge.py +0 -0
  128. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/tests/test_oconv.py +0 -0
  129. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/tests/test_original_error.py +0 -0
  130. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/tests/test_timer.py +0 -0
  131. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/timer.py +0 -0
  132. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/misc/tools.py +0 -0
  133. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/payment/__init__.py +0 -0
  134. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/payment/base_adapter.py +0 -0
  135. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/payment/braintree_adapter.py +0 -0
  136. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/payment/router.py +0 -0
  137. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity/payment/stripe_adapter.py +0 -0
  138. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity_python.egg-info/SOURCES.txt +0 -0
  139. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  140. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity_python.egg-info/requires.txt +0 -0
  141. {velocity_python-0.0.204 → velocity_python-0.0.205}/src/velocity_python.egg-info/top_level.txt +0 -0
  142. {velocity_python-0.0.204 → velocity_python-0.0.205}/tests/test_decorators.py +0 -0
  143. {velocity_python-0.0.204 → velocity_python-0.0.205}/tests/test_iconv_money_to_cents.py +0 -0
  144. {velocity_python-0.0.204 → velocity_python-0.0.205}/tests/test_lambda_handler.py +0 -0
  145. {velocity_python-0.0.204 → velocity_python-0.0.205}/tests/test_lambda_handler_auth.py +0 -0
  146. {velocity_python-0.0.204 → velocity_python-0.0.205}/tests/test_mixins_import.py +0 -0
  147. {velocity_python-0.0.204 → velocity_python-0.0.205}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  148. {velocity_python-0.0.204 → velocity_python-0.0.205}/tests/test_table_alter.py +0 -0
  149. {velocity_python-0.0.204 → velocity_python-0.0.205}/tests/test_where_clause_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.204
3
+ Version: 0.0.205
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.204"
7
+ version = "0.0.205"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.0.204"
1
+ __version__ = version = "0.0.205"
2
2
 
3
3
  from . import aws
4
4
  from . import db
@@ -221,17 +221,61 @@ class BaseHandler:
221
221
  local_context: The context object
222
222
  exception: The exception that occurred
223
223
  """
224
+ # Always log the original exception so it isn't lost if the error-path
225
+ # (e.g. job-status updates) fails due to a transient DB disconnect.
226
+ logger.exception(
227
+ "Unhandled exception during action execution",
228
+ extra={
229
+ "handler": self.__class__.__name__,
230
+ "action": getattr(local_context, "action", lambda: None)(),
231
+ "tx_present": tx is not None,
232
+ },
233
+ )
234
+
224
235
  if hasattr(self, "onError"):
225
- self.onError(
226
- tx,
227
- local_context,
228
- exc=exception.__class__.__name__,
229
- tb=traceback.format_exc(),
230
- )
236
+ try:
237
+ self.onError(
238
+ tx,
239
+ local_context,
240
+ exc=exception.__class__.__name__,
241
+ tb=traceback.format_exc(),
242
+ )
243
+ except Exception as on_error_exc:
244
+ if self._is_transient_db_disconnect(on_error_exc):
245
+ logger.warning(
246
+ "onError failed due to transient DB disconnect; continuing",
247
+ exc_info=True,
248
+ extra={
249
+ "handler": self.__class__.__name__,
250
+ "action": getattr(local_context, "action", lambda: None)(),
251
+ "original_exc": exception.__class__.__name__,
252
+ "onerror_exc": on_error_exc.__class__.__name__,
253
+ },
254
+ )
255
+ return
256
+ raise
231
257
  else:
232
258
  # Re-raise if no error handler is defined
233
259
  raise exception
234
260
 
261
+ @staticmethod
262
+ def _is_transient_db_disconnect(exc: Exception) -> bool:
263
+ """Return True if an exception looks like a transient DB disconnect.
264
+
265
+ This is intentionally message-based so it works even if the originating
266
+ exception type comes from psycopg2 / SQLAlchemy / wrapped Velocity errors.
267
+ """
268
+ try:
269
+ from velocity.db import exceptions as db_exceptions
270
+
271
+ if isinstance(exc, db_exceptions.DbConnectionError):
272
+ return True
273
+ except Exception:
274
+ # Avoid import-time coupling; fall back to message matching.
275
+ pass
276
+
277
+ return False
278
+
235
279
  def log(
236
280
  self, tx, message: str, function: Optional[str] = None, level: str = "info"
237
281
  ):
@@ -198,7 +198,9 @@ class Engine:
198
198
  # Transient disconnects can happen during maintenance / restarts.
199
199
  # Retrying the entire top-level function is the safest option.
200
200
  msg = str(e).strip().lower()
201
- if not self._is_transient_connection_error_message(msg):
201
+ if not getattr(
202
+ self.sql, "is_transient_connection_error_message", lambda _m: False
203
+ )(msg):
202
204
  raise
203
205
 
204
206
  connection_retry_count += 1
@@ -464,36 +466,8 @@ class Engine:
464
466
  raise exceptions.DbDatabaseMissingError(enhanced_message) from exception
465
467
  if re.findall(r"already exists", msg, re.M):
466
468
  raise exceptions.DbObjectExistsError(enhanced_message) from exception
467
- if re.findall(r"server closed the connection unexpectedly", msg, re.M):
468
- raise exceptions.DbConnectionError(enhanced_message) from exception
469
- if re.findall(r"ssl syscall error: eof detected", msg, re.M):
470
- raise exceptions.DbConnectionError(enhanced_message) from exception
471
- if re.findall(r"ssl syscall error: connection reset by peer", msg, re.M):
472
- raise exceptions.DbConnectionError(enhanced_message) from exception
473
- if re.findall(r"eof detected", msg, re.M):
474
- # Be conservative: EOFs typically indicate a transient disconnect.
475
- raise exceptions.DbConnectionError(enhanced_message) from exception
476
- if re.findall(r"no connection to the server", msg, re.M):
477
- raise exceptions.DbConnectionError(enhanced_message) from exception
478
- if re.findall(r"connection timed out", msg, re.M):
479
- raise exceptions.DbConnectionError(enhanced_message) from exception
480
- if re.findall(r"could not connect to server", msg, re.M):
481
- raise exceptions.DbConnectionError(enhanced_message) from exception
482
- if re.findall(r"cannot connect to server", msg, re.M):
483
- raise exceptions.DbConnectionError(enhanced_message) from exception
484
- if re.findall(r"terminating connection due to administrator command", msg, re.M):
485
- raise exceptions.DbConnectionError(enhanced_message) from exception
486
- if re.findall(r"connection reset by peer", msg, re.M):
487
- raise exceptions.DbConnectionError(enhanced_message) from exception
488
- if re.findall(r"broken pipe", msg, re.M):
489
- raise exceptions.DbConnectionError(enhanced_message) from exception
490
- if re.findall(r"could not receive data from server", msg, re.M):
491
- raise exceptions.DbConnectionError(enhanced_message) from exception
492
- if re.findall(r"could not send data to server", msg, re.M):
493
- raise exceptions.DbConnectionError(enhanced_message) from exception
494
- if re.findall(r"connection already closed", msg, re.M):
495
- raise exceptions.DbConnectionError(enhanced_message) from exception
496
- if re.findall(r"cursor already closed", msg, re.M):
469
+ # Dialect-specific connection error message classification (fallback when no/unknown code).
470
+ if getattr(self.sql, "is_connection_error_message", lambda _m: False)(msg):
497
471
  raise exceptions.DbConnectionError(enhanced_message) from exception
498
472
  if "no such table:" in msg:
499
473
  raise exceptions.DbTableMissingError(enhanced_message) from exception
@@ -512,26 +486,6 @@ class Engine:
512
486
  # If we can't classify it, re-raise with enhanced message
513
487
  raise type(exception)(enhanced_message) from exception
514
488
 
515
- def _is_transient_connection_error_message(self, msg: str) -> bool:
516
- """Return True if this looks like a retryable, transient connection drop.
517
-
518
- Keep this intentionally conservative: authentication/config issues should not be retried.
519
- """
520
- needles = (
521
- "ssl syscall error",
522
- "eof detected",
523
- "server closed the connection unexpectedly",
524
- "connection reset by peer",
525
- "broken pipe",
526
- "could not receive data from server",
527
- "could not send data to server",
528
- "terminating connection due to administrator command",
529
- "connection already closed",
530
- "cursor already closed",
531
- "no connection to the server",
532
- )
533
- return any(n in msg for n in needles)
534
-
535
489
  def _format_sql_with_params(self, sql, parameters):
536
490
  """
537
491
  Format SQL query with parameters merged for easy copy-paste debugging.
@@ -52,6 +52,43 @@ class BaseSQLDialect(ABC):
52
52
  """
53
53
  pass
54
54
 
55
+ @classmethod
56
+ def is_connection_error_message(cls, msg: str) -> bool:
57
+ """Return True if an error message indicates a connection-level failure.
58
+
59
+ Dialects should override/extend this if they can do better than message matching.
60
+ Engine uses this only as a fallback when no/unknown error code is available.
61
+ """
62
+ if not msg:
63
+ return False
64
+ m = str(msg).strip().lower()
65
+ needles = (
66
+ "server closed the connection unexpectedly",
67
+ "no connection to the server",
68
+ "connection timed out",
69
+ "could not connect to server",
70
+ "cannot connect to server",
71
+ "connection already closed",
72
+ "cursor already closed",
73
+ "ssl syscall error",
74
+ "eof detected",
75
+ "connection reset by peer",
76
+ "broken pipe",
77
+ "terminating connection due to administrator command",
78
+ "could not receive data from server",
79
+ "could not send data to server",
80
+ )
81
+ return any(n in m for n in needles)
82
+
83
+ @classmethod
84
+ def is_transient_connection_error_message(cls, msg: str) -> bool:
85
+ """Return True if a connection error looks transient/retryable.
86
+
87
+ Default implementation treats most low-level disconnects as transient.
88
+ Dialects may override to be stricter.
89
+ """
90
+ return cls.is_connection_error_message(msg)
91
+
55
92
  # Core CRUD Operations
56
93
  @classmethod
57
94
  @abstractmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.204
3
+ Version: 0.0.205
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