velocity-python 0.0.205__tar.gz → 0.0.206__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.205 → velocity_python-0.0.206}/PKG-INFO +1 -1
  2. {velocity_python-0.0.205 → velocity_python-0.0.206}/pyproject.toml +1 -1
  3. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/base/sql.py +3 -23
  5. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/mysql/sql.py +39 -0
  6. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/postgres/sql.py +31 -0
  7. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/sqlite/sql.py +18 -0
  8. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/sqlserver/sql.py +34 -0
  9. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity_python.egg-info/PKG-INFO +1 -1
  10. {velocity_python-0.0.205 → velocity_python-0.0.206}/LICENSE +0 -0
  11. {velocity_python-0.0.205 → velocity_python-0.0.206}/README.md +0 -0
  12. {velocity_python-0.0.205 → velocity_python-0.0.206}/setup.cfg +0 -0
  13. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/app/__init__.py +0 -0
  14. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/app/invoices.py +0 -0
  15. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/app/orders.py +0 -0
  16. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/app/payments.py +0 -0
  17. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/app/purchase_orders.py +0 -0
  18. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/app/tests/__init__.py +0 -0
  19. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/app/tests/test_email_processing.py +0 -0
  20. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/app/tests/test_payment_profile_sorting.py +0 -0
  21. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/app/tests/test_spreadsheet_functions.py +0 -0
  22. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/__init__.py +0 -0
  23. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/amplify.py +0 -0
  24. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/__init__.py +0 -0
  25. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/base_handler.py +0 -0
  26. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/context.py +0 -0
  27. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/context_factory.py +0 -0
  28. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/exceptions.py +0 -0
  29. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  30. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  31. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  32. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  33. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/perf.py +0 -0
  34. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/response.py +0 -0
  35. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  36. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/tests/__init__.py +0 -0
  37. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  38. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/aws/tests/test_response.py +0 -0
  39. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/__init__.py +0 -0
  40. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/__init__.py +0 -0
  41. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/column.py +0 -0
  42. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/database.py +0 -0
  43. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/decorators.py +0 -0
  44. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/engine.py +0 -0
  45. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/result.py +0 -0
  46. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/row.py +0 -0
  47. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/sequence.py +0 -0
  48. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/table.py +0 -0
  49. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/transaction.py +0 -0
  50. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/core/view.py +0 -0
  51. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/exceptions.py +0 -0
  52. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/__init__.py +0 -0
  53. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/base/__init__.py +0 -0
  54. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/base/initializer.py +0 -0
  55. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/base/operators.py +0 -0
  56. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/base/types.py +0 -0
  57. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/mysql/__init__.py +0 -0
  58. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/mysql/operators.py +0 -0
  59. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/mysql/reserved.py +0 -0
  60. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/mysql/types.py +0 -0
  61. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/postgres/__init__.py +0 -0
  62. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/postgres/operators.py +0 -0
  63. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/postgres/reserved.py +0 -0
  64. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/postgres/types.py +0 -0
  65. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  66. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/sqlite/operators.py +0 -0
  67. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  68. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/sqlite/types.py +0 -0
  69. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  70. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  71. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  72. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/sqlserver/types.py +0 -0
  73. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/servers/tablehelper.py +0 -0
  74. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/__init__.py +0 -0
  75. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/common_db_test.py +0 -0
  76. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/__init__.py +0 -0
  77. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/common.py +0 -0
  78. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_column.py +0 -0
  79. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  80. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_database.py +0 -0
  81. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  82. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  83. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  84. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_result.py +0 -0
  85. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_row.py +0 -0
  86. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  87. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  88. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  89. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  90. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  91. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_table.py +0 -0
  92. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  93. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  94. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/sql/__init__.py +0 -0
  95. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/sql/common.py +0 -0
  96. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  97. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  98. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  99. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_db_utils.py +0 -0
  100. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_postgres.py +0 -0
  101. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  102. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  103. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_result_caching.py +0 -0
  104. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  105. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  106. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  107. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  108. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_sql_builder.py +0 -0
  109. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_tablehelper.py +0 -0
  110. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/tests/test_view_helper.py +0 -0
  111. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/db/utils.py +0 -0
  112. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/logging.py +0 -0
  113. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/__init__.py +0 -0
  114. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/conv/__init__.py +0 -0
  115. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/conv/iconv.py +0 -0
  116. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/conv/oconv.py +0 -0
  117. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/db.py +0 -0
  118. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/export.py +0 -0
  119. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/format.py +0 -0
  120. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/mail.py +0 -0
  121. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/merge.py +0 -0
  122. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/tests/__init__.py +0 -0
  123. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/tests/test_db.py +0 -0
  124. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/tests/test_fix.py +0 -0
  125. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/tests/test_format.py +0 -0
  126. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/tests/test_iconv.py +0 -0
  127. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/tests/test_merge.py +0 -0
  128. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/tests/test_oconv.py +0 -0
  129. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/tests/test_original_error.py +0 -0
  130. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/tests/test_timer.py +0 -0
  131. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/timer.py +0 -0
  132. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/misc/tools.py +0 -0
  133. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/payment/__init__.py +0 -0
  134. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/payment/base_adapter.py +0 -0
  135. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/payment/braintree_adapter.py +0 -0
  136. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/payment/router.py +0 -0
  137. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity/payment/stripe_adapter.py +0 -0
  138. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity_python.egg-info/SOURCES.txt +0 -0
  139. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  140. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity_python.egg-info/requires.txt +0 -0
  141. {velocity_python-0.0.205 → velocity_python-0.0.206}/src/velocity_python.egg-info/top_level.txt +0 -0
  142. {velocity_python-0.0.205 → velocity_python-0.0.206}/tests/test_decorators.py +0 -0
  143. {velocity_python-0.0.205 → velocity_python-0.0.206}/tests/test_iconv_money_to_cents.py +0 -0
  144. {velocity_python-0.0.205 → velocity_python-0.0.206}/tests/test_lambda_handler.py +0 -0
  145. {velocity_python-0.0.205 → velocity_python-0.0.206}/tests/test_lambda_handler_auth.py +0 -0
  146. {velocity_python-0.0.205 → velocity_python-0.0.206}/tests/test_mixins_import.py +0 -0
  147. {velocity_python-0.0.205 → velocity_python-0.0.206}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  148. {velocity_python-0.0.205 → velocity_python-0.0.206}/tests/test_table_alter.py +0 -0
  149. {velocity_python-0.0.205 → velocity_python-0.0.206}/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.205
3
+ Version: 0.0.206
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.205"
7
+ version = "0.0.206"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.0.205"
1
+ __version__ = version = "0.0.206"
2
2
 
3
3
  from . import aws
4
4
  from . import db
@@ -59,35 +59,15 @@ class BaseSQLDialect(ABC):
59
59
  Dialects should override/extend this if they can do better than message matching.
60
60
  Engine uses this only as a fallback when no/unknown error code is available.
61
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)
62
+ return False
82
63
 
83
64
  @classmethod
84
65
  def is_transient_connection_error_message(cls, msg: str) -> bool:
85
66
  """Return True if a connection error looks transient/retryable.
86
67
 
87
- Default implementation treats most low-level disconnects as transient.
88
- Dialects may override to be stricter.
68
+ Default implementation is disabled; dialects must implement this.
89
69
  """
90
- return cls.is_connection_error_message(msg)
70
+ return False
91
71
 
92
72
  # Core CRUD Operations
93
73
  @classmethod
@@ -72,6 +72,45 @@ class SQL(BaseSQLDialect):
72
72
  error_msg = getattr(e, "msg", None)
73
73
  return error_code, error_msg
74
74
 
75
+ @classmethod
76
+ def is_connection_error_message(cls, msg: str) -> bool:
77
+ if not msg:
78
+ return False
79
+ m = str(msg).strip().lower()
80
+
81
+ # Common MySQL connector / server disconnects.
82
+ needles = (
83
+ "mysql server has gone away",
84
+ "lost connection to mysql server",
85
+ "can't connect to mysql server",
86
+ "connection refused",
87
+ "connection reset by peer",
88
+ "broken pipe",
89
+ "connection timed out",
90
+ "read timed out",
91
+ "write timed out",
92
+ "server shutdown",
93
+ "too many connections",
94
+ "is dead or not enabled",
95
+ )
96
+ return any(n in m for n in needles)
97
+
98
+ @classmethod
99
+ def is_transient_connection_error_message(cls, msg: str) -> bool:
100
+ if not cls.is_connection_error_message(msg):
101
+ return False
102
+
103
+ # Do not treat auth/config problems as transient.
104
+ m = str(msg).strip().lower()
105
+ non_transient = (
106
+ "access denied",
107
+ "authentication",
108
+ "unknown database",
109
+ "unknown mysql server host",
110
+ "bad handshake",
111
+ )
112
+ return not any(n in m for n in non_transient)
113
+
75
114
  @classmethod
76
115
  def select(
77
116
  cls,
@@ -77,6 +77,37 @@ class SQL(BaseSQLDialect):
77
77
  error_mesg = getattr(e, "pgerror", None)
78
78
  return error_code, error_mesg
79
79
 
80
+ @classmethod
81
+ def is_connection_error_message(cls, msg: str) -> bool:
82
+ if not msg:
83
+ return False
84
+ m = str(msg).strip().lower()
85
+ needles = (
86
+ "server closed the connection unexpectedly",
87
+ "no connection to the server",
88
+ "connection timed out",
89
+ "could not connect to server",
90
+ "cannot connect to server",
91
+ "could not translate host name",
92
+ "connection already closed",
93
+ "cursor already closed",
94
+ "ssl syscall error",
95
+ "eof detected",
96
+ "connection reset by peer",
97
+ "broken pipe",
98
+ "terminating connection due to administrator command",
99
+ "could not receive data from server",
100
+ "could not send data to server",
101
+ "the database system is starting up",
102
+ "the database system is shutting down",
103
+ )
104
+ return any(n in m for n in needles)
105
+
106
+ @classmethod
107
+ def is_transient_connection_error_message(cls, msg: str) -> bool:
108
+ # For Postgres, low-level disconnects/restarts are typically transient.
109
+ return cls.is_connection_error_message(msg)
110
+
80
111
  @staticmethod
81
112
  def _validate_where_string(where):
82
113
  """
@@ -72,6 +72,24 @@ class SQL(BaseSQLDialect):
72
72
  # SQLite exceptions don't have error codes like other databases
73
73
  return None, str(e)
74
74
 
75
+ @classmethod
76
+ def is_connection_error_message(cls, msg: str) -> bool:
77
+ if not msg:
78
+ return False
79
+ m = str(msg).strip().lower()
80
+ needles = (
81
+ "unable to open database file",
82
+ "disk i/o error",
83
+ "readonly database",
84
+ "file is not a database",
85
+ )
86
+ return any(n in m for n in needles)
87
+
88
+ @classmethod
89
+ def is_transient_connection_error_message(cls, msg: str) -> bool:
90
+ # SQLite connection/file errors are typically not transient in-process.
91
+ return False
92
+
75
93
  @classmethod
76
94
  def select(
77
95
  cls,
@@ -74,6 +74,40 @@ class SQL(BaseSQLDialect):
74
74
  error_message = getattr(e, "message", None) or str(e)
75
75
  return error_number, error_message
76
76
 
77
+ @classmethod
78
+ def is_connection_error_message(cls, msg: str) -> bool:
79
+ if not msg:
80
+ return False
81
+ m = str(msg).strip().lower()
82
+
83
+ # pyodbc/pymssql/pytds typically surface these in message text.
84
+ needles = (
85
+ "communication link failure",
86
+ "transport-level error",
87
+ "tcp provider",
88
+ "a connection was successfully established with the server, but then an error occurred during the login process",
89
+ "connection is broken",
90
+ "connection was forcibly closed",
91
+ "connection reset",
92
+ "login timeout expired",
93
+ "server is not found or not accessible",
94
+ "could not open a connection",
95
+ "network-related",
96
+ "connection timed out",
97
+ "broken pipe",
98
+ )
99
+ return any(n in m for n in needles)
100
+
101
+ @classmethod
102
+ def is_transient_connection_error_message(cls, msg: str) -> bool:
103
+ if not cls.is_connection_error_message(msg):
104
+ return False
105
+ m = str(msg).strip().lower()
106
+ # "login failed" is usually credentials/permissions, not transient.
107
+ if "login failed" in m:
108
+ return False
109
+ return True
110
+
77
111
  @classmethod
78
112
  def select(
79
113
  cls,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.205
3
+ Version: 0.0.206
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