velocity-python 0.1.79__tar.gz → 0.1.80__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 (217) hide show
  1. {velocity_python-0.1.79 → velocity_python-0.1.80}/PKG-INFO +1 -1
  2. {velocity_python-0.1.79 → velocity_python-0.1.80}/pyproject.toml +1 -1
  3. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/mixins/web_handler.py +34 -2
  5. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/transaction.py +34 -0
  6. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity_python.egg-info/PKG-INFO +1 -1
  7. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity_python.egg-info/SOURCES.txt +1 -0
  8. velocity_python-0.1.80/tests/test_activity_tracking_aborted_tx.py +116 -0
  9. {velocity_python-0.1.79 → velocity_python-0.1.80}/LICENSE +0 -0
  10. {velocity_python-0.1.79 → velocity_python-0.1.80}/README.md +0 -0
  11. {velocity_python-0.1.79 → velocity_python-0.1.80}/setup.cfg +0 -0
  12. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/__init__.py +0 -0
  13. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/amplify.py +0 -0
  14. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/amplify_build.py +0 -0
  15. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/assets/__init__.py +0 -0
  16. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/assets/backfill.py +0 -0
  17. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/assets/indexing.py +0 -0
  18. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/assets/references.py +0 -0
  19. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/assets/service.py +0 -0
  20. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/assets/usage_index.py +0 -0
  21. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/dirty_pipeline.py +0 -0
  22. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/__init__.py +0 -0
  23. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/base_handler.py +0 -0
  24. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/context.py +0 -0
  25. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/context_factory.py +0 -0
  26. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/exceptions.py +0 -0
  27. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  28. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/masquerade.py +0 -0
  29. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  30. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  31. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/perf.py +0 -0
  32. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/response.py +0 -0
  33. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  34. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/s3.py +0 -0
  35. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/ssm_config.py +0 -0
  36. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/tests/__init__.py +0 -0
  37. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  38. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  39. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/aws/tests/test_response.py +0 -0
  40. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/__init__.py +0 -0
  41. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/__init__.py +0 -0
  42. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/async_support.py +0 -0
  43. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/column.py +0 -0
  44. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/database.py +0 -0
  45. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/decorators.py +0 -0
  46. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/engine.py +0 -0
  47. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/jsonproxy.py +0 -0
  48. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/result.py +0 -0
  49. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/row.py +0 -0
  50. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/sequence.py +0 -0
  51. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/table.py +0 -0
  52. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/core/view.py +0 -0
  53. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/exceptions.py +0 -0
  54. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/maintenance.py +0 -0
  55. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/migrations.py +0 -0
  56. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/__init__.py +0 -0
  57. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/base/__init__.py +0 -0
  58. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/base/initializer.py +0 -0
  59. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/base/operators.py +0 -0
  60. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/base/sql.py +0 -0
  61. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/base/types.py +0 -0
  62. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/mysql/__init__.py +0 -0
  63. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/mysql/operators.py +0 -0
  64. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/mysql/reserved.py +0 -0
  65. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/mysql/sql.py +0 -0
  66. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/mysql/types.py +0 -0
  67. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/postgres/__init__.py +0 -0
  68. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/postgres/operators.py +0 -0
  69. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/postgres/reserved.py +0 -0
  70. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/postgres/sql.py +0 -0
  71. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/postgres/types.py +0 -0
  72. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  73. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/sqlite/operators.py +0 -0
  74. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  75. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/sqlite/sql.py +0 -0
  76. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/sqlite/types.py +0 -0
  77. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  78. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  79. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  80. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  81. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/sqlserver/types.py +0 -0
  82. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/servers/tablehelper.py +0 -0
  83. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/__init__.py +0 -0
  84. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/common_db_test.py +0 -0
  85. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/__init__.py +0 -0
  86. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/common.py +0 -0
  87. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/conftest.py +0 -0
  88. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_column.py +0 -0
  89. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  90. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_database.py +0 -0
  91. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  92. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  93. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  94. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_result.py +0 -0
  95. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_row.py +0 -0
  96. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  97. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  98. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  99. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  100. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  101. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_table.py +0 -0
  102. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  103. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  104. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/sql/__init__.py +0 -0
  105. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/sql/common.py +0 -0
  106. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  107. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  108. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  109. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_db_utils.py +0 -0
  110. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_postgres.py +0 -0
  111. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  112. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  113. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_result_caching.py +0 -0
  114. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  115. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  116. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  117. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  118. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_sql_builder.py +0 -0
  119. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_tablehelper.py +0 -0
  120. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/tests/test_view_helper.py +0 -0
  121. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/db/utils.py +0 -0
  122. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/logging.py +0 -0
  123. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/__init__.py +0 -0
  124. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/conv/__init__.py +0 -0
  125. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/conv/iconv.py +0 -0
  126. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/conv/oconv.py +0 -0
  127. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/db.py +0 -0
  128. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/export.py +0 -0
  129. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/format.py +0 -0
  130. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/mail.py +0 -0
  131. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/merge.py +0 -0
  132. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/pdf.py +0 -0
  133. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/tests/__init__.py +0 -0
  134. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/tests/test_db.py +0 -0
  135. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/tests/test_fix.py +0 -0
  136. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/tests/test_format.py +0 -0
  137. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/tests/test_iconv.py +0 -0
  138. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/tests/test_merge.py +0 -0
  139. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/tests/test_oconv.py +0 -0
  140. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/tests/test_original_error.py +0 -0
  141. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/tests/test_timer.py +0 -0
  142. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/timer.py +0 -0
  143. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/misc/tools.py +0 -0
  144. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/payment/__init__.py +0 -0
  145. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/payment/authorizenet_adapter.py +0 -0
  146. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/payment/authorizenet_mirror.py +0 -0
  147. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/payment/base_adapter.py +0 -0
  148. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/payment/braintree_adapter.py +0 -0
  149. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/payment/braintree_mirror.py +0 -0
  150. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/payment/charge_rules.py +0 -0
  151. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/payment/stripe_adapter.py +0 -0
  152. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity/payment/stripe_mirror.py +0 -0
  153. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  154. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity_python.egg-info/entry_points.txt +0 -0
  155. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity_python.egg-info/requires.txt +0 -0
  156. {velocity_python-0.1.79 → velocity_python-0.1.80}/src/velocity_python.egg-info/top_level.txt +0 -0
  157. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_amplify_build.py +0 -0
  158. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_asset_indexing.py +0 -0
  159. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_asset_references.py +0 -0
  160. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_assets_service.py +0 -0
  161. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_async_support.py +0 -0
  162. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_batch_operations.py +0 -0
  163. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_column_tx_arg.py +0 -0
  164. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_concurrency_safety.py +0 -0
  165. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_connection_pool.py +0 -0
  166. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_connection_resilience.py +0 -0
  167. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_context_job_descriptions.py +0 -0
  168. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_db_credentials_ssm_cascade.py +0 -0
  169. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_db_maintenance.py +0 -0
  170. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_decorators.py +0 -0
  171. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_dirty_pipeline_fast_path.py +0 -0
  172. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_email_processing.py +0 -0
  173. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_enqueue_send_failures.py +0 -0
  174. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_get_cognito_user_provider.py +0 -0
  175. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_http_handler_rollback.py +0 -0
  176. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_iconv_money_to_cents.py +0 -0
  177. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_identifier_injection_guard.py +0 -0
  178. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_json_columns.py +0 -0
  179. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_jsonb_dict_adapter.py +0 -0
  180. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_lambda_handler.py +0 -0
  181. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_lambda_handler_auth.py +0 -0
  182. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_lambda_handler_masquerade.py +0 -0
  183. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_masquerade_grant.py +0 -0
  184. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_mixins_import.py +0 -0
  185. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_n_plus_one.py +0 -0
  186. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_observability.py +0 -0
  187. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_payment_authorizenet_adapter.py +0 -0
  188. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_payment_braintree_adapter.py +0 -0
  189. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_payment_braintree_mirror.py +0 -0
  190. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_payment_mirror_empty_table.py +0 -0
  191. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_payment_profile_sorting.py +0 -0
  192. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_payment_stripe_adapter.py +0 -0
  193. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_pdf.py +0 -0
  194. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_prepared_statements.py +0 -0
  195. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_psycopg3_upgrade.py +0 -0
  196. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_query_cache.py +0 -0
  197. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_restricted_direct_tables.py +0 -0
  198. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_retry_side_effect_guard.py +0 -0
  199. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_return_default_safety.py +0 -0
  200. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_row_batch_update.py +0 -0
  201. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_row_cache_staleness.py +0 -0
  202. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_schema_migrations.py +0 -0
  203. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_security_hardening.py +0 -0
  204. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_server_cursor.py +0 -0
  205. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_single_autocommit_safety.py +0 -0
  206. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_spreadsheet_functions.py +0 -0
  207. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_sqlite_backend.py +0 -0
  208. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_sqs_per_record_transactions.py +0 -0
  209. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_ssm_config.py +0 -0
  210. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_store_user_data.py +0 -0
  211. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  212. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_table_alter.py +0 -0
  213. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_transaction_class_wrapping.py +0 -0
  214. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_transaction_commit_and_ownership.py +0 -0
  215. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_transaction_edge_cases.py +0 -0
  216. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_where_clause_validation.py +0 -0
  217. {velocity_python-0.1.79 → velocity_python-0.1.80}/tests/test_write_hook_create_flow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.79
3
+ Version: 0.1.80
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.1.79"
7
+ version = "0.1.80"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.1.79"
1
+ __version__ = version = "0.1.80"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -522,8 +522,40 @@ Request Details:
522
522
  self.track_activity_start(tx, context)
523
523
 
524
524
  def _enhanced_after_action(self, tx, context):
525
- """Enhanced afterAction that adds activity tracking"""
526
- self.track_activity_success(tx, context)
525
+ """Enhanced afterAction that adds activity tracking.
526
+
527
+ Activity tracking is bookkeeping — it must never convert a completed
528
+ action into a 500. Two guards:
529
+
530
+ * If the transaction is already aborted, the action caught a DB error and
531
+ returned a "success" response WITHOUT rolling back to a savepoint. The
532
+ tracking UPDATE would then fail at ``SAVEPOINT`` with "current
533
+ transaction is aborted", masking the real error (prod incident
534
+ 2026-07-01). Skip the write and log loudly — the swallow-without-
535
+ savepoint is the actual bug; wrap the fallible statement in
536
+ ``with tx.savepoint():``.
537
+ * Any other failure of the tracking write is caught and logged rather than
538
+ propagated, so bookkeeping can't fail an otherwise-successful request.
539
+ """
540
+ try:
541
+ if getattr(tx, "is_aborted", None) and tx.is_aborted():
542
+ logger.error(
543
+ "Skipping success activity tracking: transaction is aborted "
544
+ "after a 'successful' action (action=%s handler=%s). An earlier "
545
+ "DB error was caught without rolling back to a savepoint — wrap "
546
+ "the fallible statement in `with tx.savepoint():`.",
547
+ context.action(),
548
+ self.__class__.__name__,
549
+ )
550
+ else:
551
+ self.track_activity_success(tx, context)
552
+ except Exception:
553
+ logger.exception(
554
+ "Success activity tracking failed (action=%s handler=%s); "
555
+ "continuing without it.",
556
+ context.action(),
557
+ self.__class__.__name__,
558
+ )
527
559
  if self._perf_timing_enabled:
528
560
  elapsed_ms = context.perf.log("web_handler action total")
529
561
  if elapsed_ms is not None:
@@ -577,6 +577,40 @@ class Transaction:
577
577
  # Transaction is clean again — no uncommitted work pending.
578
578
  self._in_progress = False
579
579
 
580
+ def is_aborted(self):
581
+ """Return True if the connection is in a failed/aborted transaction.
582
+
583
+ After a statement errors on PostgreSQL, the whole transaction is poisoned:
584
+ every subsequent statement — *including* ``SAVEPOINT`` — fails with
585
+ "current transaction is aborted, commands ignored until end of transaction
586
+ block" until a ``ROLLBACK`` (or rollback to a savepoint taken *before* the
587
+ error). Because even ``SAVEPOINT`` fails in this state, a ``tx.savepoint()``
588
+ wrapper cannot rescue an already-poisoned transaction — callers that want to
589
+ run bookkeeping after a fallible step should check this first and skip (and
590
+ fix the swallow-without-savepoint upstream).
591
+
592
+ Best-effort and driver-specific: returns False when it cannot be determined
593
+ (no open connection, or a driver without a transaction-status concept). For
594
+ psycopg (v2/v3) this maps to libpq ``PQTRANS_INERROR`` (numeric 3).
595
+ """
596
+ conn = self.connection
597
+ if conn is None:
598
+ return False
599
+ try:
600
+ status = conn.info.transaction_status
601
+ except Exception:
602
+ return False
603
+ if status is None:
604
+ return False
605
+ # psycopg3: pq.TransactionStatus.INERROR (IntEnum, name 'INERROR', value 3).
606
+ # Compare by name and by the stable libpq numeric to stay driver-robust.
607
+ try:
608
+ if getattr(status, "name", None) == "INERROR":
609
+ return True
610
+ return int(status) == 3
611
+ except Exception:
612
+ return False
613
+
580
614
  def create_savepoint(self, sp=None, cursor=None):
581
615
  """
582
616
  Creates a savepoint named `sp`. If none given, uses a random name.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.79
3
+ Version: 0.1.80
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
@@ -151,6 +151,7 @@ src/velocity_python.egg-info/dependency_links.txt
151
151
  src/velocity_python.egg-info/entry_points.txt
152
152
  src/velocity_python.egg-info/requires.txt
153
153
  src/velocity_python.egg-info/top_level.txt
154
+ tests/test_activity_tracking_aborted_tx.py
154
155
  tests/test_amplify_build.py
155
156
  tests/test_asset_indexing.py
156
157
  tests/test_asset_references.py
@@ -0,0 +1,116 @@
1
+ """Bookkeeping must never turn a completed action into a 500.
2
+
3
+ If an action catches a DB error and returns a "success" response WITHOUT rolling
4
+ back to a savepoint, the transaction stays aborted. velocity's afterAction
5
+ activity-tracking UPDATE would then fail at SAVEPOINT with "current transaction is
6
+ aborted", masking the real error and producing an unhandled 500 (prod incident
7
+ 2026-07-01, BackOfficeData BillingDashboard). These tests pin:
8
+
9
+ * Transaction.is_aborted() detects the poisoned state.
10
+ * _enhanced_after_action skips the tracking write when aborted (and never
11
+ raises), and still tracks on a healthy transaction.
12
+ """
13
+
14
+ import unittest
15
+ from unittest.mock import MagicMock
16
+
17
+ from velocity.db.core.transaction import Transaction
18
+ from velocity.aws.handlers.mixins.web_handler import WebHandler
19
+
20
+
21
+ class IsAbortedTest(unittest.TestCase):
22
+ def _tx_with_status(self, status):
23
+ tx = Transaction.__new__(Transaction)
24
+ conn = MagicMock()
25
+ conn.info.transaction_status = status
26
+ tx.connection = conn
27
+ return tx
28
+
29
+ def test_no_connection_is_not_aborted(self):
30
+ tx = Transaction.__new__(Transaction)
31
+ tx.connection = None
32
+ self.assertFalse(tx.is_aborted())
33
+
34
+ def test_inerror_by_name_is_aborted(self):
35
+ status = MagicMock()
36
+ status.name = "INERROR"
37
+ self.assertTrue(self._tx_with_status(status).is_aborted())
38
+
39
+ def test_inerror_by_numeric_is_aborted(self):
40
+ # A driver enum member whose .name isn't 'INERROR' but int() == 3.
41
+ class _Status:
42
+ name = "PQTRANS_INERROR"
43
+
44
+ def __int__(self):
45
+ return 3
46
+
47
+ self.assertTrue(self._tx_with_status(_Status()).is_aborted())
48
+
49
+ def test_intrans_is_not_aborted(self):
50
+ status = MagicMock()
51
+ status.name = "INTRANS"
52
+ status.__int__ = lambda self: 2
53
+ self.assertFalse(self._tx_with_status(status).is_aborted())
54
+
55
+ def test_status_probe_failure_is_not_aborted(self):
56
+ tx = Transaction.__new__(Transaction)
57
+ conn = MagicMock()
58
+ type(conn).info = property(lambda self: (_ for _ in ()).throw(RuntimeError()))
59
+ tx.connection = conn
60
+ self.assertFalse(tx.is_aborted())
61
+
62
+
63
+ class _Handler(WebHandler):
64
+ """Minimal harness exercising the real _enhanced_after_action."""
65
+
66
+ def __init__(self):
67
+ self._perf_timing_enabled = False
68
+ self.track_activity_success = MagicMock()
69
+
70
+
71
+ def _context():
72
+ ctx = MagicMock()
73
+ ctx.action.return_value = "query"
74
+ return ctx
75
+
76
+
77
+ class EnhancedAfterActionGuardTest(unittest.TestCase):
78
+ def test_skips_tracking_when_tx_aborted(self):
79
+ handler = _Handler()
80
+ tx = MagicMock()
81
+ tx.is_aborted.return_value = True
82
+
83
+ handler._enhanced_after_action(tx, _context()) # must not raise
84
+
85
+ handler.track_activity_success.assert_not_called()
86
+
87
+ def test_tracks_when_tx_healthy(self):
88
+ handler = _Handler()
89
+ tx = MagicMock()
90
+ tx.is_aborted.return_value = False
91
+
92
+ handler._enhanced_after_action(tx, _context())
93
+
94
+ handler.track_activity_success.assert_called_once()
95
+
96
+ def test_tracking_failure_is_swallowed(self):
97
+ handler = _Handler()
98
+ handler.track_activity_success.side_effect = RuntimeError("boom")
99
+ tx = MagicMock()
100
+ tx.is_aborted.return_value = False
101
+
102
+ # A failing bookkeeping write must not propagate out of afterAction.
103
+ handler._enhanced_after_action(tx, _context())
104
+
105
+ def test_missing_is_aborted_falls_back_to_tracking(self):
106
+ # Older Transaction without is_aborted() (getattr guard) still tracks.
107
+ handler = _Handler()
108
+ tx = MagicMock(spec=[]) # no is_aborted attribute
109
+
110
+ handler._enhanced_after_action(tx, _context())
111
+
112
+ handler.track_activity_success.assert_called_once()
113
+
114
+
115
+ if __name__ == "__main__":
116
+ unittest.main()