velocity-python 0.0.239__tar.gz → 0.1.1__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 (178) hide show
  1. {velocity_python-0.0.239 → velocity_python-0.1.1}/PKG-INFO +26 -27
  2. {velocity_python-0.0.239 → velocity_python-0.1.1}/pyproject.toml +28 -29
  3. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/formbuilder/reshaper.py +28 -1
  5. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/base_handler.py +1 -1
  6. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/mixins/web_handler.py +1 -1
  7. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/sqs_handler.py +62 -30
  8. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/database.py +6 -6
  9. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/decorators.py +36 -3
  10. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/engine.py +299 -13
  11. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/row.py +59 -5
  12. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/sequence.py +1 -1
  13. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/table.py +90 -0
  14. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/transaction.py +166 -11
  15. velocity_python-0.1.1/src/velocity/db/servers/postgres/__init__.py +147 -0
  16. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/postgres/sql.py +174 -5
  17. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_engine.py +6 -6
  18. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +1 -1
  19. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_schema_locking_initializers.py +7 -7
  20. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity_python.egg-info/PKG-INFO +26 -27
  21. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity_python.egg-info/SOURCES.txt +9 -0
  22. velocity_python-0.1.1/src/velocity_python.egg-info/requires.txt +36 -0
  23. velocity_python-0.1.1/tests/test_batch_operations.py +394 -0
  24. velocity_python-0.1.1/tests/test_concurrency_safety.py +374 -0
  25. velocity_python-0.1.1/tests/test_connection_pool.py +288 -0
  26. velocity_python-0.1.1/tests/test_connection_resilience.py +380 -0
  27. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_formbuilder_reshaper.py +55 -2
  28. velocity_python-0.1.1/tests/test_prepared_statements.py +159 -0
  29. velocity_python-0.1.1/tests/test_psycopg3_upgrade.py +246 -0
  30. velocity_python-0.1.1/tests/test_query_cache.py +298 -0
  31. velocity_python-0.1.1/tests/test_row_batch_update.py +196 -0
  32. velocity_python-0.1.1/tests/test_sqs_per_record_transactions.py +593 -0
  33. velocity_python-0.0.239/src/velocity/db/servers/postgres/__init__.py +0 -85
  34. velocity_python-0.0.239/src/velocity_python.egg-info/requires.txt +0 -36
  35. {velocity_python-0.0.239 → velocity_python-0.1.1}/LICENSE +0 -0
  36. {velocity_python-0.0.239 → velocity_python-0.1.1}/README.md +0 -0
  37. {velocity_python-0.0.239 → velocity_python-0.1.1}/setup.cfg +0 -0
  38. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/__init__.py +0 -0
  39. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/formbuilder/__init__.py +0 -0
  40. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/invoices.py +0 -0
  41. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/orders.py +0 -0
  42. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/payments.py +0 -0
  43. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/purchase_orders.py +0 -0
  44. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/tests/__init__.py +0 -0
  45. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/tests/test_email_processing.py +0 -0
  46. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/tests/test_payment_profile_sorting.py +0 -0
  47. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/tests/test_spreadsheet_functions.py +0 -0
  48. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/validators/__init__.py +0 -0
  49. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/app/validators/formbuilder_template.py +0 -0
  50. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/__init__.py +0 -0
  51. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/amplify.py +0 -0
  52. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/amplify_build.py +0 -0
  53. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/__init__.py +0 -0
  54. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/context.py +0 -0
  55. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/context_factory.py +0 -0
  56. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/exceptions.py +0 -0
  57. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  58. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  59. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  60. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/perf.py +0 -0
  61. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/handlers/response.py +0 -0
  62. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/tests/__init__.py +0 -0
  63. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  64. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  65. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/aws/tests/test_response.py +0 -0
  66. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/__init__.py +0 -0
  67. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/__init__.py +0 -0
  68. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/column.py +0 -0
  69. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/result.py +0 -0
  70. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/core/view.py +0 -0
  71. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/exceptions.py +0 -0
  72. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/__init__.py +0 -0
  73. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/base/__init__.py +0 -0
  74. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/base/initializer.py +0 -0
  75. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/base/operators.py +0 -0
  76. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/base/sql.py +0 -0
  77. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/base/types.py +0 -0
  78. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/mysql/__init__.py +0 -0
  79. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/mysql/operators.py +0 -0
  80. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/mysql/reserved.py +0 -0
  81. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/mysql/sql.py +0 -0
  82. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/mysql/types.py +0 -0
  83. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/postgres/operators.py +0 -0
  84. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/postgres/reserved.py +0 -0
  85. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/postgres/types.py +0 -0
  86. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  87. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/sqlite/operators.py +0 -0
  88. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  89. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/sqlite/sql.py +0 -0
  90. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/sqlite/types.py +0 -0
  91. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  92. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  93. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  94. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  95. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/sqlserver/types.py +0 -0
  96. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/servers/tablehelper.py +0 -0
  97. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/__init__.py +0 -0
  98. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/common_db_test.py +0 -0
  99. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/__init__.py +0 -0
  100. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/common.py +0 -0
  101. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_column.py +0 -0
  102. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  103. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_database.py +0 -0
  104. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  105. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  106. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_result.py +0 -0
  107. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_row.py +0 -0
  108. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  109. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  110. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  111. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  112. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_table.py +0 -0
  113. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  114. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  115. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/sql/__init__.py +0 -0
  116. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/sql/common.py +0 -0
  117. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  118. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  119. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  120. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_db_utils.py +0 -0
  121. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_postgres.py +0 -0
  122. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  123. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  124. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_result_caching.py +0 -0
  125. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  126. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  127. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  128. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_sql_builder.py +0 -0
  129. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_tablehelper.py +0 -0
  130. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/tests/test_view_helper.py +0 -0
  131. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/db/utils.py +0 -0
  132. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/logging.py +0 -0
  133. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/__init__.py +0 -0
  134. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/conv/__init__.py +0 -0
  135. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/conv/iconv.py +0 -0
  136. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/conv/oconv.py +0 -0
  137. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/db.py +0 -0
  138. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/export.py +0 -0
  139. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/format.py +0 -0
  140. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/mail.py +0 -0
  141. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/merge.py +0 -0
  142. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/tests/__init__.py +0 -0
  143. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/tests/test_db.py +0 -0
  144. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/tests/test_fix.py +0 -0
  145. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/tests/test_format.py +0 -0
  146. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/tests/test_iconv.py +0 -0
  147. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/tests/test_merge.py +0 -0
  148. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/tests/test_oconv.py +0 -0
  149. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/tests/test_original_error.py +0 -0
  150. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/tests/test_timer.py +0 -0
  151. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/timer.py +0 -0
  152. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/misc/tools.py +0 -0
  153. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/payment/__init__.py +0 -0
  154. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/payment/authorizenet_adapter.py +0 -0
  155. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/payment/base_adapter.py +0 -0
  156. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/payment/braintree_adapter.py +0 -0
  157. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/payment/charge_rules.py +0 -0
  158. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/payment/demo_profiles.py +0 -0
  159. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/payment/profiles.py +0 -0
  160. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/payment/router.py +0 -0
  161. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity/payment/stripe_adapter.py +0 -0
  162. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  163. {velocity_python-0.0.239 → velocity_python-0.1.1}/src/velocity_python.egg-info/top_level.txt +0 -0
  164. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_amplify_build.py +0 -0
  165. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_decorators.py +0 -0
  166. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_formbuilder_template_validator.py +0 -0
  167. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_iconv_money_to_cents.py +0 -0
  168. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_lambda_handler.py +0 -0
  169. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_lambda_handler_auth.py +0 -0
  170. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_mixins_import.py +0 -0
  171. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_payment_braintree_adapter.py +0 -0
  172. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_payment_demo_profiles.py +0 -0
  173. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_payment_profiles.py +0 -0
  174. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_payment_router.py +0 -0
  175. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_payment_stripe_adapter.py +0 -0
  176. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  177. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_table_alter.py +0 -0
  178. {velocity_python-0.0.239 → velocity_python-0.1.1}/tests/test_where_clause_validation.py +0 -0
@@ -1,54 +1,53 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.0.239
3
+ Version: 0.1.1
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
7
7
  Project-URL: Homepage, https://codeclubs.org/projects/velocity
8
8
  Keywords: database,orm,sql,rapid-development,data-storage
9
- Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Development Status :: 4 - Beta
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Topic :: Database
12
12
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
13
  Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.7
15
- Classifier: Programming Language :: Python :: 3.8
16
14
  Classifier: Programming Language :: Python :: 3.9
17
15
  Classifier: Programming Language :: Python :: 3.10
18
16
  Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
19
18
  Classifier: Operating System :: OS Independent
20
- Requires-Python: >=3.7
19
+ Requires-Python: >=3.9
21
20
  Description-Content-Type: text/markdown
22
21
  License-File: LICENSE
23
- Requires-Dist: boto3>=1.26.0
24
- Requires-Dist: requests>=2.25.0
25
- Requires-Dist: jinja2>=3.0.0
26
- Requires-Dist: xlrd>=2.0.0
27
- Requires-Dist: openpyxl>=3.0.0
28
- Requires-Dist: sqlparse>=0.4.0
22
+ Requires-Dist: boto3>=1.35.0
23
+ Requires-Dist: requests>=2.32.0
24
+ Requires-Dist: jinja2>=3.1.0
25
+ Requires-Dist: xlrd>=2.0.1
26
+ Requires-Dist: openpyxl>=3.1.0
27
+ Requires-Dist: sqlparse>=0.5.0
29
28
  Provides-Extra: mysql
30
- Requires-Dist: mysql-connector-python>=8.0.0; extra == "mysql"
29
+ Requires-Dist: mysql-connector-python>=9.0.0; extra == "mysql"
31
30
  Provides-Extra: sqlserver
32
- Requires-Dist: python-tds>=1.10.0; extra == "sqlserver"
31
+ Requires-Dist: python-tds>=1.15.0; extra == "sqlserver"
33
32
  Provides-Extra: postgres
34
- Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgres"
33
+ Requires-Dist: psycopg[binary]>=3.2.0; extra == "postgres"
35
34
  Provides-Extra: payment
36
- Requires-Dist: stripe>=8.0.0; extra == "payment"
37
- Requires-Dist: braintree>=4.0.0; extra == "payment"
35
+ Requires-Dist: stripe>=12.0.0; extra == "payment"
36
+ Requires-Dist: braintree>=4.30.0; extra == "payment"
38
37
  Provides-Extra: dev
39
- Requires-Dist: pytest>=7.0.0; extra == "dev"
40
- Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
41
- Requires-Dist: black>=23.0.0; extra == "dev"
42
- Requires-Dist: flake8>=6.0.0; extra == "dev"
43
- Requires-Dist: mypy>=1.0.0; extra == "dev"
44
- Requires-Dist: pre-commit>=3.0.0; extra == "dev"
38
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
39
+ Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
40
+ Requires-Dist: black>=24.0.0; extra == "dev"
41
+ Requires-Dist: flake8>=7.0.0; extra == "dev"
42
+ Requires-Dist: mypy>=1.10.0; extra == "dev"
43
+ Requires-Dist: pre-commit>=4.0.0; extra == "dev"
45
44
  Provides-Extra: test
46
- Requires-Dist: pytest>=7.0.0; extra == "test"
47
- Requires-Dist: pytest-cov>=4.0.0; extra == "test"
48
- Requires-Dist: pytest-mock>=3.10.0; extra == "test"
45
+ Requires-Dist: pytest>=8.0.0; extra == "test"
46
+ Requires-Dist: pytest-cov>=6.0.0; extra == "test"
47
+ Requires-Dist: pytest-mock>=3.14.0; extra == "test"
49
48
  Provides-Extra: docs
50
- Requires-Dist: sphinx>=5.0.0; extra == "docs"
51
- Requires-Dist: sphinx-rtd-theme>=1.2.0; extra == "docs"
49
+ Requires-Dist: sphinx>=8.0.0; extra == "docs"
50
+ Requires-Dist: sphinx-rtd-theme>=3.0.0; extra == "docs"
52
51
  Dynamic: license-file
53
52
 
54
53
  # Velocity
@@ -4,35 +4,34 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "velocity-python"
7
- version = "0.0.239"
7
+ version = "0.1.1"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
11
11
  description = "A rapid application development library for interfacing with data storage"
12
12
  readme = "README.md"
13
13
  license = "MIT"
14
- requires-python = ">=3.7"
14
+ requires-python = ">=3.9"
15
15
  keywords = ["database", "orm", "sql", "rapid-development", "data-storage"]
16
16
  classifiers = [
17
- "Development Status :: 3 - Alpha",
17
+ "Development Status :: 4 - Beta",
18
18
  "Intended Audience :: Developers",
19
19
  "Topic :: Database",
20
20
  "Topic :: Software Development :: Libraries :: Python Modules",
21
21
  "Programming Language :: Python :: 3",
22
- "Programming Language :: Python :: 3.7",
23
- "Programming Language :: Python :: 3.8",
24
22
  "Programming Language :: Python :: 3.9",
25
23
  "Programming Language :: Python :: 3.10",
26
24
  "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
27
26
  "Operating System :: OS Independent",
28
27
  ]
29
28
  dependencies = [
30
- "boto3>=1.26.0",
31
- "requests>=2.25.0",
32
- "jinja2>=3.0.0",
33
- "xlrd>=2.0.0",
34
- "openpyxl>=3.0.0",
35
- "sqlparse>=0.4.0"
29
+ "boto3>=1.35.0",
30
+ "requests>=2.32.0",
31
+ "jinja2>=3.1.0",
32
+ "xlrd>=2.0.1",
33
+ "openpyxl>=3.1.0",
34
+ "sqlparse>=0.5.0"
36
35
  ]
37
36
 
38
37
  [project.urls]
@@ -40,34 +39,34 @@ Homepage = "https://codeclubs.org/projects/velocity"
40
39
 
41
40
  [project.optional-dependencies]
42
41
  mysql = [
43
- "mysql-connector-python>=8.0.0",
42
+ "mysql-connector-python>=9.0.0",
44
43
  ]
45
44
  sqlserver = [
46
- "python-tds>=1.10.0",
45
+ "python-tds>=1.15.0",
47
46
  ]
48
47
  postgres = [
49
- "psycopg2-binary>=2.9.0",
48
+ "psycopg[binary]>=3.2.0",
50
49
  ]
51
50
  payment = [
52
- "stripe>=8.0.0",
53
- "braintree>=4.0.0",
51
+ "stripe>=12.0.0",
52
+ "braintree>=4.30.0",
54
53
  ]
55
54
  dev = [
56
- "pytest>=7.0.0",
57
- "pytest-cov>=4.0.0",
58
- "black>=23.0.0",
59
- "flake8>=6.0.0",
60
- "mypy>=1.0.0",
61
- "pre-commit>=3.0.0",
55
+ "pytest>=8.0.0",
56
+ "pytest-cov>=6.0.0",
57
+ "black>=24.0.0",
58
+ "flake8>=7.0.0",
59
+ "mypy>=1.10.0",
60
+ "pre-commit>=4.0.0",
62
61
  ]
63
62
  test = [
64
- "pytest>=7.0.0",
65
- "pytest-cov>=4.0.0",
66
- "pytest-mock>=3.10.0",
63
+ "pytest>=8.0.0",
64
+ "pytest-cov>=6.0.0",
65
+ "pytest-mock>=3.14.0",
67
66
  ]
68
67
  docs = [
69
- "sphinx>=5.0.0",
70
- "sphinx-rtd-theme>=1.2.0",
68
+ "sphinx>=8.0.0",
69
+ "sphinx-rtd-theme>=3.0.0",
71
70
  ]
72
71
 
73
72
  [tool.setuptools.packages.find]
@@ -78,7 +77,7 @@ where = ["src"]
78
77
 
79
78
  [tool.black]
80
79
  line-length = 88
81
- target-version = ['py37', 'py38', 'py39', 'py310', 'py311']
80
+ target-version = ['py39', 'py310', 'py311', 'py312']
82
81
  include = '\.pyi?$'
83
82
  extend-exclude = '''
84
83
  /(
@@ -122,7 +121,7 @@ module = [
122
121
  "boto3.*",
123
122
  "env",
124
123
  "pytds.*",
125
- "psycopg2.*",
124
+ "psycopg.*",
126
125
  "requests.*",
127
126
  "sqlparse.*",
128
127
  "stripe.*",
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.0.239"
1
+ __version__ = version = "0.1.1"
2
2
 
3
3
  from . import aws
4
4
  from . import db
@@ -168,7 +168,34 @@ def reshape_formbuilder_template(raw_row: Dict[str, Any]) -> Dict[str, Any]:
168
168
  if key.startswith("sys_"):
169
169
  del fb[key]
170
170
 
171
- for key in ("url_slug", "url_slug_alternate"):
171
+ for key in (
172
+ # UI-only computed column (row counter) — never a real DB column
173
+ "row_number",
174
+ "url_slug",
175
+ "url_slug_alternate",
176
+ # Administrative / validation metadata — not read by DonateMain frontend
177
+ "is_published",
178
+ "datetime_last_validated",
179
+ "last_validation_result",
180
+ "last_validation_reason",
181
+ # Backend-only: security / fraud prevention
182
+ "whitelist_end_date",
183
+ "recaptcha_version",
184
+ "recaptcha_v3_score_threshold",
185
+ # Backend-only: routing / campaign metadata
186
+ "campaign",
187
+ "sub_campaign",
188
+ # Backend-only: email receipts (server-side sending only)
189
+ "receipt_from_address",
190
+ "receipt_subject",
191
+ "receipt_body_text",
192
+ "mail_bcc",
193
+ # Backend-only: rally out-of-band receipts
194
+ "rally_receipt_from_address",
195
+ "rally_receipt_subject",
196
+ "rally_receipt_body_text",
197
+ "rally_mail_bcc",
198
+ ):
172
199
  fb.pop(key, None)
173
200
 
174
201
  return fb
@@ -335,7 +335,7 @@ class BaseHandler:
335
335
  """Return True if an exception looks like a transient DB disconnect.
336
336
 
337
337
  This is intentionally message-based so it works even if the originating
338
- exception type comes from psycopg2 / SQLAlchemy / wrapped Velocity errors.
338
+ exception type comes from psycopg / SQLAlchemy / wrapped Velocity errors.
339
339
  """
340
340
  if exc is None and args:
341
341
  exc = args[0]
@@ -217,7 +217,7 @@ class WebHandler(ABC):
217
217
  return sanitized
218
218
 
219
219
  def _normalize_activity_value(self, value: Any) -> Any:
220
- """Convert values into types acceptable by psycopg2"""
220
+ """Convert values into types acceptable by the database driver."""
221
221
  if isinstance(value, (dict, list, tuple, set)):
222
222
  try:
223
223
  return json.dumps(value)
@@ -2,7 +2,8 @@
2
2
  SQS Handler Module
3
3
 
4
4
  This module provides a base class for handling AWS SQS events in Lambda functions.
5
- It includes logging capabilities, action routing, and error handling.
5
+ It includes per-record transaction isolation, partial batch failure reporting
6
+ via ``batchItemFailures``, and error handling hooks.
6
7
  """
7
8
 
8
9
  import json
@@ -22,8 +23,11 @@ class SqsHandler(BaseHandler):
22
23
  """
23
24
  Base class for handling SQS events in AWS Lambda functions.
24
25
 
25
- Provides structured processing of SQS records with automatic action routing,
26
- logging capabilities, and error handling hooks.
26
+ Each SQS record is processed in its own database transaction. If a record
27
+ fails, the transaction is rolled back and the record's ``messageId`` is
28
+ included in the ``batchItemFailures`` response so that SQS retries only
29
+ the failed records. Successful records are deleted from the queue
30
+ automatically.
27
31
  """
28
32
 
29
33
  def __init__(
@@ -33,14 +37,6 @@ class SqsHandler(BaseHandler):
33
37
  context_factory: Optional[ContextFactory] = None,
34
38
  context_class=VelocityContext.Context,
35
39
  ):
36
- """
37
- Initialize the SQS handler.
38
-
39
- Args:
40
- aws_event: The AWS Lambda event containing SQS records
41
- aws_context: The AWS Lambda context object
42
- context_class: The context class to use for processing
43
- """
44
40
  super().__init__(
45
41
  aws_event,
46
42
  aws_context,
@@ -50,28 +46,40 @@ class SqsHandler(BaseHandler):
50
46
 
51
47
  def serve(self, tx):
52
48
  """
53
- Process all SQS records in the event.
49
+ Process all SQS records, each in its own transaction.
54
50
 
55
- Args:
56
- tx: Database transaction object
51
+ Returns a ``batchItemFailures`` response so that only failed records
52
+ are retried by SQS. When all records succeed the return value is
53
+ ``None`` (no failures to report).
57
54
  """
58
55
  records = self.aws_event.get("Records", [])
56
+ if not records:
57
+ return None
58
+
59
+ engine = tx.engine
60
+ failures = []
59
61
 
60
62
  for record in records:
61
- self._process_record(tx, record)
63
+ message_id = record.get("messageId")
64
+ try:
65
+ with engine.transaction() as record_tx:
66
+ self._process_record(record_tx, record)
67
+ except Exception as exc:
68
+ self.on_record_error(record, exc)
69
+ if message_id:
70
+ failures.append({"itemIdentifier": message_id})
71
+
72
+ if failures:
73
+ return {"batchItemFailures": failures}
74
+ return None
62
75
 
63
76
  def _process_record(self, tx, record: Dict[str, Any]):
64
77
  """
65
78
  Process a single SQS record.
66
-
67
- Args:
68
- tx: Database transaction object
69
- record: Individual SQS record to process
70
79
  """
71
80
  attrs = record.get("attributes", {})
72
81
  postdata = {}
73
82
 
74
- # Parse message body if present
75
83
  body = record.get("body")
76
84
  if body:
77
85
  try:
@@ -80,7 +88,6 @@ class SqsHandler(BaseHandler):
80
88
  logger.warning("Failed to parse SQS message body as JSON: %s", e)
81
89
  postdata = {"raw_body": body}
82
90
 
83
- # Create local context for this record
84
91
  local_context = self.create_context(
85
92
  args=attrs,
86
93
  postdata=postdata,
@@ -90,26 +97,51 @@ class SqsHandler(BaseHandler):
90
97
  if hasattr(local_context, "configure_perf"):
91
98
  local_context.configure_perf(postdata=postdata)
92
99
 
93
- # Determine action from postdata
94
100
  action = postdata.get("action") if isinstance(postdata, dict) else None
95
-
96
- # Get the list of actions to execute
97
101
  actions = self.get_actions_to_execute(action)
98
102
 
99
- # Use BaseHandler's execute_actions method
100
103
  try:
101
104
  self.execute_actions(tx, local_context, actions)
102
105
  except Exception as e:
106
+ # Roll back failed DB state so handle_error/onError get a usable tx.
107
+ try:
108
+ tx.rollback()
109
+ except Exception:
110
+ pass
103
111
  self.handle_error(tx, local_context, e)
104
112
 
105
- def OnActionDefault(self, tx, context):
113
+ def on_record_error(self, record, exception):
114
+ """
115
+ Called when a record fails and will be reported in
116
+ ``batchItemFailures``. Override in subclasses for custom behaviour
117
+ (e.g. alerting, metrics).
106
118
  """
107
- Default action handler when no specific action is found.
119
+ message_id = record.get("messageId", "unknown")
120
+ receive_count = record.get("attributes", {}).get(
121
+ "ApproximateReceiveCount", "0"
122
+ )
123
+ logger.error(
124
+ "SQS record %s failed (receive count: %s): %s",
125
+ message_id,
126
+ receive_count,
127
+ exception,
128
+ )
108
129
 
109
- Args:
110
- tx: Database transaction object
111
- context: The context object for this record
130
+ @staticmethod
131
+ def is_dlq_candidate(record, max_receives=5):
112
132
  """
133
+ Return ``True`` if the record has been received *max_receives* or more
134
+ times, indicating it is likely headed for the dead-letter queue.
135
+
136
+ The actual DLQ routing is controlled by the SQS redrive policy;
137
+ this helper is for logging and metrics.
138
+ """
139
+ receive_count = int(
140
+ record.get("attributes", {}).get("ApproximateReceiveCount", "0")
141
+ )
142
+ return receive_count >= max_receives
143
+
144
+ def OnActionDefault(self, tx, context):
113
145
  action = context.action() if hasattr(context, "action") else "unknown"
114
146
  warning_message = (
115
147
  f"[Warn] Action handler not found. Calling default action "
@@ -93,12 +93,12 @@ class Database:
93
93
  Performs VACUUM on this database, optionally FULL, optionally ANALYZE,
94
94
  optionally REINDEX.
95
95
  """
96
- # Manually open a separate connection to run VACUUM in isolation_level=0
96
+ # Manually open a separate connection to run VACUUM outside a transaction.
97
97
  conn = self.tx.engine.connect()
98
- old_isolation_level = conn.isolation_level
98
+ old_autocommit = conn.autocommit
99
99
  try:
100
- # Postgres requires VACUUM to run outside a normal transaction block
101
- conn.set_isolation_level(0)
100
+ # Postgres requires VACUUM to run outside a normal transaction block.
101
+ conn.autocommit = True
102
102
 
103
103
  # Build up the VACUUM command
104
104
  parts = ["VACUUM"]
@@ -116,6 +116,6 @@ class Database:
116
116
  cur.execute(f"REINDEX DATABASE {self.name}")
117
117
 
118
118
  finally:
119
- # Restore isolation level and close the connection
120
- conn.set_isolation_level(old_isolation_level)
119
+ # Restore autocommit state and close the connection
120
+ conn.autocommit = old_autocommit
121
121
  conn.close()
@@ -60,6 +60,7 @@ def reset_id_on_dup_key(func):
60
60
 
61
61
  @wraps(func)
62
62
  def reset_decorator(self, *args, retries=0, **kwds):
63
+ max_retries = 10
63
64
  sp = self.tx.create_savepoint(cursor=self.cursor())
64
65
  try:
65
66
  result = func(self, *args, **kwds)
@@ -71,7 +72,7 @@ def reset_id_on_dup_key(func):
71
72
  raise
72
73
  if not _is_primary_key_duplicate(error):
73
74
  raise
74
- if retries < 3:
75
+ if retries < max_retries:
75
76
  backoff_time = (2**retries) * 0.01 + random.uniform(0, 0.02)
76
77
  time.sleep(backoff_time)
77
78
  self.set_sequence(self.max("sys_id") + 1)
@@ -145,6 +146,11 @@ def create_missing(func):
145
146
  """
146
147
  If the function call fails with DbColumnMissingError or DbTableMissingError,
147
148
  tries to create them and re-run (only if schema is not locked).
149
+
150
+ DDL operations use IF NOT EXISTS at the SQL level, but concurrent Lambdas
151
+ may still race. If the CREATE/ALTER raises ``DbObjectExistsError`` we
152
+ silently ignore it (the object was created by another process) and retry
153
+ the original operation.
148
154
  """
149
155
 
150
156
  @wraps(func)
@@ -172,7 +178,21 @@ def create_missing(func):
172
178
  for i, arg in enumerate(args):
173
179
  if isinstance(arg, dict):
174
180
  data.update(arg)
175
- self.alter(data, mode="add")
181
+
182
+ # ALTER TABLE ADD COLUMN IF NOT EXISTS — acquire an advisory
183
+ # lock to serialize DDL across concurrent Lambda containers,
184
+ # then guard against any remaining race with a savepoint.
185
+ try:
186
+ self.tx.advisory_lock(f"velocity_ddl_{self.name}")
187
+ except Exception:
188
+ pass # advisory lock is best-effort (e.g. non-PostgreSQL)
189
+ sp2 = self.tx.create_savepoint(cursor=self.cursor())
190
+ try:
191
+ self.alter(data, mode="add")
192
+ self.tx.release_savepoint(sp2, cursor=self.cursor())
193
+ except exceptions.DbObjectExistsError:
194
+ self.tx.rollback_savepoint(sp2, cursor=self.cursor())
195
+
176
196
  return func(self, *args, **kwds)
177
197
  except exceptions.DbTableMissingError as e:
178
198
  self.tx.rollback_savepoint(sp, cursor=self.cursor())
@@ -192,7 +212,20 @@ def create_missing(func):
192
212
  for i, arg in enumerate(args):
193
213
  if isinstance(arg, dict):
194
214
  data.update(arg)
195
- self.create(data)
215
+
216
+ # CREATE TABLE IF NOT EXISTS — acquire advisory lock then
217
+ # guard against concurrent creation with a savepoint.
218
+ try:
219
+ self.tx.advisory_lock(f"velocity_ddl_{self.name}")
220
+ except Exception:
221
+ pass # advisory lock is best-effort
222
+ sp2 = self.tx.create_savepoint(cursor=self.cursor())
223
+ try:
224
+ self.create(data)
225
+ self.tx.release_savepoint(sp2, cursor=self.cursor())
226
+ except exceptions.DbObjectExistsError:
227
+ self.tx.rollback_savepoint(sp2, cursor=self.cursor())
228
+
196
229
  return func(self, *args, **kwds)
197
230
 
198
231
  return wrapper