velocity-python 0.1.126__tar.gz → 0.1.129__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 (333) hide show
  1. {velocity_python-0.1.126 → velocity_python-0.1.129}/PKG-INFO +2 -4
  2. {velocity_python-0.1.126 → velocity_python-0.1.129}/pyproject.toml +2 -5
  3. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/amplify.py +16 -25
  5. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/amplify_build.py +13 -31
  6. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/capabilities/jobs.py +23 -0
  7. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/deploy/__init__.py +12 -0
  8. velocity_python-0.1.129/src/velocity/aws/deploy/config.py +115 -0
  9. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/deploy/operations.py +188 -39
  10. velocity_python-0.1.129/src/velocity/aws/deploy/policies.py +563 -0
  11. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/deploy/reconcile.py +125 -52
  12. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/facets.py +61 -13
  13. velocity_python-0.1.129/src/velocity/aws/failures.py +502 -0
  14. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/base_handler.py +59 -1
  15. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/context.py +148 -84
  16. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/exceptions.py +8 -1
  17. velocity_python-0.1.129/src/velocity/aws/handlers/mixins/client_input.py +481 -0
  18. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/mixins/data_service.py +88 -15
  19. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/mixins/job_handler.py +55 -5
  20. velocity_python-0.1.129/src/velocity/aws/handlers/sqs_handler.py +248 -0
  21. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/s3.py +55 -15
  22. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/tests/test_amplify_build_shim.py +9 -2
  23. velocity_python-0.1.129/src/velocity/aws/tests/test_deploy_least_privilege.py +449 -0
  24. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/tests/test_deploy_policies.py +58 -3
  25. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/tests/test_deploy_reconcile.py +30 -46
  26. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/cache.py +39 -2
  27. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/__init__.py +3 -0
  28. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/decorators.py +138 -23
  29. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/engine.py +149 -22
  30. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/result.py +4 -1
  31. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/row.py +45 -12
  32. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/table.py +105 -39
  33. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/transaction.py +401 -94
  34. velocity_python-0.1.129/src/velocity/db/fragments.py +225 -0
  35. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/maintenance.py +11 -6
  36. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/postgres/__init__.py +5 -0
  37. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/postgres/sql.py +397 -147
  38. velocity_python-0.1.129/src/velocity/db/servers/tablehelper.py +1213 -0
  39. velocity_python-0.1.129/src/velocity/db/tunnel.py +49 -0
  40. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/pdf.py +20 -148
  41. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/payment/__init__.py +8 -1
  42. velocity_python-0.1.129/src/velocity/payment/_mirror_core.py +107 -0
  43. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/payment/authorizenet_adapter.py +548 -406
  44. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/payment/authorizenet_mirror.py +20 -22
  45. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/payment/base_adapter.py +289 -6
  46. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/payment/braintree_adapter.py +408 -300
  47. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/payment/braintree_mirror.py +17 -37
  48. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/payment/charge_rules.py +29 -14
  49. velocity_python-0.1.129/src/velocity/payment/money.py +163 -0
  50. velocity_python-0.1.129/src/velocity/payment/redact.py +76 -0
  51. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/payment/stripe_adapter.py +710 -362
  52. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/payment/stripe_mirror.py +32 -43
  53. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity_python.egg-info/PKG-INFO +2 -4
  54. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity_python.egg-info/SOURCES.txt +19 -0
  55. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity_python.egg-info/requires.txt +0 -3
  56. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_amplify_build.py +2 -6
  57. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_batch_operations.py +11 -12
  58. velocity_python-0.1.129/tests/test_client_sql_boundary.py +1071 -0
  59. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_concurrency_safety.py +12 -3
  60. velocity_python-0.1.129/tests/test_db_tunnel.py +127 -0
  61. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_facets.py +23 -19
  62. velocity_python-0.1.129/tests/test_failures.py +295 -0
  63. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_job_handler.py +113 -13
  64. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_job_lifecycle.py +17 -0
  65. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_on_commit.py +84 -8
  66. velocity_python-0.1.129/tests/test_payment_audit_fixes.py +1048 -0
  67. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_payment_authorizenet_adapter.py +2 -2
  68. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_payment_braintree_mirror.py +11 -31
  69. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_payment_mirror_empty_table.py +26 -32
  70. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_payment_stripe_adapter.py +41 -27
  71. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_pdf.py +68 -175
  72. velocity_python-0.1.129/tests/test_pgserver_bulk_cache.py +147 -0
  73. velocity_python-0.1.129/tests/test_pgserver_pointer_joins.py +238 -0
  74. velocity_python-0.1.129/tests/test_pointer_join_sql.py +342 -0
  75. velocity_python-0.1.129/tests/test_raw_sql_prefix_policy.py +282 -0
  76. velocity_python-0.1.129/tests/test_s3_list_client_files.py +99 -0
  77. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_sqs_per_record_transactions.py +94 -22
  78. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_table_find_cache.py +26 -1
  79. velocity_python-0.1.129/tests/test_tx_lifecycle_retry.py +496 -0
  80. velocity_python-0.1.129/tests/test_tx_read_cache_and_bulk.py +358 -0
  81. velocity_python-0.1.126/src/velocity/aws/deploy/config.py +0 -65
  82. velocity_python-0.1.126/src/velocity/aws/deploy/policies.py +0 -311
  83. velocity_python-0.1.126/src/velocity/aws/handlers/sqs_handler.py +0 -151
  84. velocity_python-0.1.126/src/velocity/db/servers/tablehelper.py +0 -692
  85. {velocity_python-0.1.126 → velocity_python-0.1.129}/LICENSE +0 -0
  86. {velocity_python-0.1.126 → velocity_python-0.1.129}/README.md +0 -0
  87. {velocity_python-0.1.126 → velocity_python-0.1.129}/setup.cfg +0 -0
  88. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/__init__.py +0 -0
  89. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/assets/__init__.py +0 -0
  90. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/assets/backfill.py +0 -0
  91. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/assets/indexing.py +0 -0
  92. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/assets/references.py +0 -0
  93. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/assets/service.py +0 -0
  94. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/assets/usage_index.py +0 -0
  95. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/blobs.py +0 -0
  96. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/capabilities/__init__.py +0 -0
  97. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/capabilities/base.py +0 -0
  98. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/capabilities/dispatch.py +0 -0
  99. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/capabilities/infra.py +0 -0
  100. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/capabilities/provisioning.py +0 -0
  101. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/capabilities/retention.py +0 -0
  102. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/capabilities/security.py +0 -0
  103. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/capabilities/single_session.py +0 -0
  104. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/deploy/targets.py +0 -0
  105. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/dirty_pipeline.py +0 -0
  106. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/email.py +0 -0
  107. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/feature_flags.py +0 -0
  108. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/__init__.py +0 -0
  109. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/context_factory.py +0 -0
  110. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  111. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/masquerade.py +0 -0
  112. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  113. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/mixins/session_control.py +0 -0
  114. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  115. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/perf.py +0 -0
  116. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/handlers/response.py +0 -0
  117. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/job_lifecycle.py +0 -0
  118. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/job_results.py +0 -0
  119. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/mirror.py +0 -0
  120. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/ops.py +0 -0
  121. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/sms.py +0 -0
  122. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/ssm_config.py +0 -0
  123. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/tests/__init__.py +0 -0
  124. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/tests/test_amplify_build_policy_size.py +0 -0
  125. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  126. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/tests/test_email.py +0 -0
  127. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  128. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/tests/test_response.py +0 -0
  129. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/aws/tests/test_sms.py +0 -0
  130. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/__init__.py +0 -0
  131. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/async_support.py +0 -0
  132. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/column.py +0 -0
  133. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/database.py +0 -0
  134. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/jsonproxy.py +0 -0
  135. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/lazy_engine.py +0 -0
  136. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/sequence.py +0 -0
  137. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/core/view.py +0 -0
  138. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/exceptions.py +0 -0
  139. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/migrations.py +0 -0
  140. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/__init__.py +0 -0
  141. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/base/__init__.py +0 -0
  142. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/base/initializer.py +0 -0
  143. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/base/operators.py +0 -0
  144. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/base/sql.py +0 -0
  145. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/base/types.py +0 -0
  146. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/mysql/__init__.py +0 -0
  147. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/mysql/operators.py +0 -0
  148. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/mysql/reserved.py +0 -0
  149. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/mysql/sql.py +0 -0
  150. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/mysql/types.py +0 -0
  151. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/postgres/operators.py +0 -0
  152. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/postgres/reserved.py +0 -0
  153. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/postgres/types.py +0 -0
  154. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  155. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/sqlite/operators.py +0 -0
  156. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  157. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/sqlite/sql.py +0 -0
  158. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/sqlite/types.py +0 -0
  159. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  160. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  161. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  162. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  163. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/servers/sqlserver/types.py +0 -0
  164. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/__init__.py +0 -0
  165. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/common_db_test.py +0 -0
  166. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/__init__.py +0 -0
  167. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/common.py +0 -0
  168. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/conftest.py +0 -0
  169. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_column.py +0 -0
  170. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  171. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_database.py +0 -0
  172. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  173. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  174. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  175. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_result.py +0 -0
  176. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_row.py +0 -0
  177. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  178. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  179. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  180. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  181. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  182. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_table.py +0 -0
  183. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  184. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  185. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/sql/__init__.py +0 -0
  186. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/sql/common.py +0 -0
  187. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  188. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  189. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  190. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_db_utils.py +0 -0
  191. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_postgres.py +0 -0
  192. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  193. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  194. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_result_caching.py +0 -0
  195. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  196. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  197. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  198. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  199. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_sql_builder.py +0 -0
  200. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_tablehelper.py +0 -0
  201. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/tests/test_view_helper.py +0 -0
  202. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/db/utils.py +0 -0
  203. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/logging.py +0 -0
  204. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/__init__.py +0 -0
  205. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/conv/__init__.py +0 -0
  206. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/conv/iconv.py +0 -0
  207. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/conv/oconv.py +0 -0
  208. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/db.py +0 -0
  209. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/export.py +0 -0
  210. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/format.py +0 -0
  211. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/mail.py +0 -0
  212. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/merge.py +0 -0
  213. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tests/__init__.py +0 -0
  214. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tests/test_db.py +0 -0
  215. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tests/test_fix.py +0 -0
  216. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tests/test_format.py +0 -0
  217. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tests/test_iconv.py +0 -0
  218. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tests/test_mail_send.py +0 -0
  219. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tests/test_merge.py +0 -0
  220. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tests/test_oconv.py +0 -0
  221. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tests/test_original_error.py +0 -0
  222. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tests/test_timer.py +0 -0
  223. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/timer.py +0 -0
  224. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/misc/tools.py +0 -0
  225. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/__init__.py +0 -0
  226. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/core/__init__.py +0 -0
  227. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/core/capabilities.py +0 -0
  228. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/core/store.py +0 -0
  229. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/core/where.py +0 -0
  230. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/exceptions.py +0 -0
  231. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/__init__.py +0 -0
  232. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/base/__init__.py +0 -0
  233. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/cassandra/__init__.py +0 -0
  234. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/cassandra/translation.py +0 -0
  235. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/couchbase/__init__.py +0 -0
  236. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/couchbase/translation.py +0 -0
  237. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/dynamodb/__init__.py +0 -0
  238. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/dynamodb/translation.py +0 -0
  239. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/elasticsearch/__init__.py +0 -0
  240. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/elasticsearch/translation.py +0 -0
  241. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/memory/__init__.py +0 -0
  242. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/mongodb/__init__.py +0 -0
  243. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/mongodb/translation.py +0 -0
  244. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity/nosql/servers/redis/__init__.py +0 -0
  245. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  246. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity_python.egg-info/entry_points.txt +0 -0
  247. {velocity_python-0.1.126 → velocity_python-0.1.129}/src/velocity_python.egg-info/top_level.txt +0 -0
  248. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_access_error_403.py +0 -0
  249. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_activity_tracking_aborted_tx.py +0 -0
  250. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_asset_indexing.py +0 -0
  251. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_asset_references.py +0 -0
  252. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_assets_service.py +0 -0
  253. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_async_support.py +0 -0
  254. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_blob_store.py +0 -0
  255. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_capabilities.py +0 -0
  256. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_capability_catalog.py +0 -0
  257. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_cognito_user_cache.py +0 -0
  258. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_column_tx_arg.py +0 -0
  259. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_connection_pool.py +0 -0
  260. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_connection_resilience.py +0 -0
  261. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_context_flags_loader.py +0 -0
  262. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_context_job_descriptions.py +0 -0
  263. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_data_service_query_cap.py +0 -0
  264. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_data_service_renumber.py +0 -0
  265. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_data_service_save_as.py +0 -0
  266. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_db_credentials_ssm_cascade.py +0 -0
  267. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_db_maintenance.py +0 -0
  268. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_decorators.py +0 -0
  269. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_delete_hook_fk_blocked.py +0 -0
  270. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_dirty_pipeline_fast_path.py +0 -0
  271. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_email_processing.py +0 -0
  272. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_enqueue_metric_namespace.py +0 -0
  273. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_enqueue_send_failures.py +0 -0
  274. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_feature_flags.py +0 -0
  275. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_get_cognito_user_provider.py +0 -0
  276. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_http_handler_rollback.py +0 -0
  277. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_iconv_money_to_cents.py +0 -0
  278. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_identifier_injection_guard.py +0 -0
  279. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_job_results.py +0 -0
  280. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_json_columns.py +0 -0
  281. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_jsonb_dict_adapter.py +0 -0
  282. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_lambda_handler.py +0 -0
  283. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_lambda_handler_auth.py +0 -0
  284. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_lambda_handler_masquerade.py +0 -0
  285. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_lambda_handler_user_cache.py +0 -0
  286. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_lazy_engine.py +0 -0
  287. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_masquerade_grant.py +0 -0
  288. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_mirror.py +0 -0
  289. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_mixins_import.py +0 -0
  290. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_n_plus_one.py +0 -0
  291. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_nosql_dynamodb.py +0 -0
  292. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_nosql_memory.py +0 -0
  293. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_nosql_mongodb.py +0 -0
  294. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_nosql_redis.py +0 -0
  295. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_nosql_translation.py +0 -0
  296. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_nosql_where.py +0 -0
  297. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_observability.py +0 -0
  298. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_ops.py +0 -0
  299. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_payment_braintree_adapter.py +0 -0
  300. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_payment_profile_sorting.py +0 -0
  301. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_prepared_statements.py +0 -0
  302. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_psycopg3_upgrade.py +0 -0
  303. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_query_cache.py +0 -0
  304. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_query_missing_table_unlocked.py +0 -0
  305. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_read_cache.py +0 -0
  306. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_response.py +0 -0
  307. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_restricted_direct_tables.py +0 -0
  308. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_result_returning.py +0 -0
  309. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_retry_side_effect_guard.py +0 -0
  310. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_return_default_safety.py +0 -0
  311. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_row_batch_update.py +0 -0
  312. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_row_cache_staleness.py +0 -0
  313. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_s3_bucket_config.py +0 -0
  314. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_schema_migrations.py +0 -0
  315. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_security_capability.py +0 -0
  316. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_security_hardening.py +0 -0
  317. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_server_cursor.py +0 -0
  318. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_session_control.py +0 -0
  319. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_single_autocommit_safety.py +0 -0
  320. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_single_session_capability.py +0 -0
  321. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_spreadsheet_functions.py +0 -0
  322. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_sqlite_backend.py +0 -0
  323. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_ssm_config.py +0 -0
  324. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_store_user_data.py +0 -0
  325. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  326. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_table_alter.py +0 -0
  327. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_table_purge.py +0 -0
  328. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_transaction_class_wrapping.py +0 -0
  329. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_transaction_commit_and_ownership.py +0 -0
  330. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_transaction_edge_cases.py +0 -0
  331. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_where_clause_validation.py +0 -0
  332. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_write_hook_create_flow.py +0 -0
  333. {velocity_python-0.1.126 → velocity_python-0.1.129}/tests/test_write_hook_empty_strings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.126
3
+ Version: 0.1.129
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
@@ -34,9 +34,7 @@ Requires-Dist: odfpy>=1.4.0; extra == "ods"
34
34
  Provides-Extra: templates
35
35
  Requires-Dist: jinja2>=3.1.0; extra == "templates"
36
36
  Provides-Extra: pdf
37
- Requires-Dist: pdfkit>=1.0.0; extra == "pdf"
38
- Provides-Extra: pdf-weasyprint
39
- Requires-Dist: weasyprint>=60.0; extra == "pdf-weasyprint"
37
+ Requires-Dist: weasyprint>=60.0; extra == "pdf"
40
38
  Provides-Extra: http
41
39
  Requires-Dist: requests>=2.32.0; extra == "http"
42
40
  Provides-Extra: mysql
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "velocity-python"
7
- version = "0.1.126"
7
+ version = "0.1.129"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -54,11 +54,8 @@ ods = [
54
54
  templates = [
55
55
  "jinja2>=3.1.0",
56
56
  ]
57
+ # HTML→PDF via WeasyPrint; native libs (Pango/Cairo) come from the OS or a layer.
57
58
  pdf = [
58
- "pdfkit>=1.0.0",
59
- ]
60
- # Maintained HTML→PDF engine; native libs (Pango/Cairo) come from the OS or a layer.
61
- pdf-weasyprint = [
62
59
  "weasyprint>=60.0",
63
60
  ]
64
61
  http = [
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.1.126"
1
+ __version__ = version = "0.1.129"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -1,5 +1,4 @@
1
1
  import boto3
2
- import time
3
2
  from collections.abc import Mapping, Sequence
4
3
 
5
4
 
@@ -190,31 +189,23 @@ class AmplifyProject:
190
189
  )
191
190
 
192
191
  def check_policies(self, function):
193
- # Attach a role policy
194
- iam_client = boto3.client("iam")
195
- response = iam_client.list_attached_role_policies(
196
- RoleName=function["Role"].split("/")[1]
192
+ """Attach the shared ENI baseline (AWS-managed
193
+ AWSLambdaVPCAccessExecutionRole) to the function's role.
194
+
195
+ No longer attaches the account-wide ``lambda-vpc-execution`` policy or
196
+ AmazonSQSFullAccess (AWS-1); app grants come from
197
+ velocity.aws.deploy.run_deployment's per-app, per-stage policy.
198
+ """
199
+ from velocity.aws.deploy.policies import (
200
+ SHARED_BASELINE_POLICY_ARNS,
201
+ attach_role_policies,
202
+ )
203
+
204
+ attach_role_policies(
205
+ boto3.client("iam"),
206
+ function["Role"].split("/")[-1],
207
+ SHARED_BASELINE_POLICY_ARNS,
197
208
  )
198
- has_policy_attached = False
199
- for policy in response["AttachedPolicies"]:
200
- if policy["PolicyName"] == "lambda-vpc-execution":
201
- has_policy_attached = True
202
- if not has_policy_attached:
203
- account_id = self.get_account_id()
204
- iam_client.attach_role_policy(
205
- PolicyArn=f"arn:aws:iam::{account_id}:policy/lambda-vpc-execution",
206
- RoleName=function["Role"].split("/")[1],
207
- )
208
- time.sleep(15)
209
- has_policy_attached = False
210
- for policy in response["AttachedPolicies"]:
211
- if policy["PolicyName"] == "AmazonSQSFullAccess":
212
- has_policy_attached = True
213
- if not has_policy_attached:
214
- iam_client.attach_role_policy(
215
- PolicyArn="arn:aws:iam::aws:policy/AmazonSQSFullAccess",
216
- RoleName=function["Role"].split("/")[1],
217
- )
218
209
 
219
210
  def sync(self, branch):
220
211
  raise RuntimeError(
@@ -5,9 +5,10 @@ package (config / targets / policies / operations / reconcile). This module
5
5
  keeps every name and signature the existing project build.py files import,
6
6
  with pre-refactor behavior:
7
7
 
8
- - run_backend_deployment() runs in "legacy-union" policy mode (config extras
9
- unioned into the shared lambda-vpc-execution policy). Projects migrated to
10
- velocity.aws.deploy.run_deployment() get per-app reconciled policies.
8
+ - run_backend_deployment() delegates to velocity.aws.deploy.run_deployment()
9
+ (per-app, per-stage declarative policies). The former "legacy-union" mode,
10
+ which unioned every app's extras into the shared lambda-vpc-execution
11
+ policy, was removed (AWS-1): no consumer used it.
11
12
 
12
13
  New projects should import from velocity.aws.deploy directly.
13
14
  """
@@ -80,33 +81,15 @@ def ensure_lambda_policies_and_attach(
80
81
  config: Optional[DeploymentConfig] = None,
81
82
  iam_client=None,
82
83
  ) -> None:
83
- """Legacy per-function policy ensure: union document into the shared
84
- lambda-vpc-execution policy, then attach it + AmazonSQSFullAccess."""
85
- if iam_client is None:
86
- iam_client = boto3.client("iam")
87
-
88
- account_id = app.get_account_id()
89
- policy_arn = f"arn:aws:iam::{account_id}:policy/lambda-vpc-execution"
90
- policy_document = baseline_lambda_policy_document(account_id)
91
- if config is not None:
92
- policy_document = config.build_lambda_policy_document(policy_document)
93
-
94
- try:
95
- retryable_call(iam_client.get_policy, PolicyArn=policy_arn)
96
- except iam_client.exceptions.NoSuchEntityException:
97
- _ensure_policy_document_size("lambda-vpc-execution", policy_document)
98
- print("Creating policy lambda-vpc-execution")
99
- retryable_call(
100
- iam_client.create_policy,
101
- PolicyName="lambda-vpc-execution",
102
- PolicyDocument=_serialize_policy_document(policy_document),
103
- )
104
- time.sleep(10)
105
- else:
106
- sync_managed_policy_document(iam_client, policy_arn, policy_document)
107
-
108
- role_name = function["Role"].split("/")[-1]
109
- attach_role_policies(iam_client, role_name, [policy_arn, AWS_SQS_FULL_ACCESS_ARN])
84
+ """Removed: this unioned app grants into the account-wide
85
+ lambda-vpc-execution policy and attached AmazonSQSFullAccess (AWS-1).
86
+ Use velocity.aws.deploy.run_deployment(), which reconciles a per-app,
87
+ per-stage policy declaratively."""
88
+ del app, function, config, iam_client
89
+ raise RuntimeError(
90
+ "ensure_lambda_policies_and_attach was removed (it granted account-wide "
91
+ "SSM/Cognito/SQS access); use velocity.aws.deploy.run_deployment()"
92
+ )
110
93
 
111
94
 
112
95
  def set_cloudwatch_logging(
@@ -141,7 +124,6 @@ def run_backend_deployment(
141
124
  run_deployment(
142
125
  target,
143
126
  config,
144
- policy_mode="legacy-union",
145
127
  lambda_client=lambda_client,
146
128
  logs_client=logs_client,
147
129
  )
@@ -53,7 +53,30 @@ class Jobs(Capability):
53
53
  where={"sys_id": job["sys_id"]},
54
54
  )
55
55
  expired += 1
56
+ _report_after_commit(tx, job, status)
56
57
 
57
58
  days = int(config.get("job_retention_days") or 30)
58
59
  purged = jobs.purge({"<sys_created": now - datetime.timedelta(days=days)})
59
60
  return {"expired": expired, "purged": purged}
61
+
62
+
63
+ def _report_after_commit(tx, job, status):
64
+ """A job whose worker died never reached onError, so nothing reported it:
65
+ publish a ``source="job"`` failure event once the sweep commits."""
66
+ from velocity.aws import failures
67
+
68
+ job_id = job.get("job_id")
69
+ action = job.get("action")
70
+
71
+ def _send():
72
+ failures.report(
73
+ None,
74
+ failures.JobWorkerDied(
75
+ f"job {job_id} stuck in {status}: {STALE_FAILURE['error']}"
76
+ ),
77
+ source="job",
78
+ action=action,
79
+ job_id=job_id,
80
+ )
81
+
82
+ tx.on_commit(_send)
@@ -17,11 +17,15 @@ See docs/deploy.md in the velocity-python repo.
17
17
 
18
18
  from velocity.aws.deploy.config import BackendDeploymentConfig, DeploymentConfig
19
19
  from velocity.aws.deploy.policies import (
20
+ AWS_LAMBDA_VPC_ACCESS_ARN,
20
21
  IAM_MANAGED_POLICY_MAX_CHARS,
21
22
  ManagedPolicySizeError,
22
23
  app_policy_name,
24
+ app_runtime_statements,
23
25
  baseline_lambda_policy_document,
24
26
  ensure_app_policy,
27
+ reconcile_role_policies,
28
+ retire_policy,
25
29
  sync_managed_policy_document,
26
30
  )
27
31
  from velocity.aws.deploy.operations import (
@@ -29,7 +33,9 @@ from velocity.aws.deploy.operations import (
29
33
  ensure_cloudwatch_log_group,
30
34
  ensure_dlq,
31
35
  ensure_identity_pool_developer_provider,
36
+ ensure_queue_event_source_mapping,
32
37
  list_functions_by_tag,
38
+ queue_visibility_timeout,
33
39
  resolve_latest_layer_arns,
34
40
  retryable_call,
35
41
  set_cloudwatch_logging,
@@ -48,6 +54,7 @@ from velocity.aws.deploy.reconcile import (
48
54
  )
49
55
 
50
56
  __all__ = [
57
+ "AWS_LAMBDA_VPC_ACCESS_ARN",
51
58
  "AmplifyTarget",
52
59
  "BackendDeploymentConfig",
53
60
  "DeploymentConfig",
@@ -55,16 +62,21 @@ __all__ = [
55
62
  "ManagedPolicySizeError",
56
63
  "PlainTarget",
57
64
  "app_policy_name",
65
+ "app_runtime_statements",
58
66
  "baseline_lambda_policy_document",
59
67
  "ensure_api_cors_header",
60
68
  "ensure_app_policy",
61
69
  "ensure_cloudwatch_log_group",
62
70
  "ensure_dlq",
63
71
  "ensure_identity_pool_developer_provider",
72
+ "ensure_queue_event_source_mapping",
64
73
  "get_amplify_app_id_and_branch",
65
74
  "initialize_build_environment",
66
75
  "list_functions_by_tag",
76
+ "queue_visibility_timeout",
77
+ "reconcile_role_policies",
67
78
  "resolve_latest_layer_arns",
79
+ "retire_policy",
68
80
  "retryable_call",
69
81
  "run_deployment",
70
82
  "set_cloudwatch_logging",
@@ -0,0 +1,115 @@
1
+ """Declarative deployment configuration for velocity.aws.deploy.
2
+
3
+ DeploymentConfig describes everything a project's backend deployment needs:
4
+ queue naming, VPC placement, per-function timeout/memory overrides, and the
5
+ project's IAM policy statements. It is platform-neutral — nothing here assumes
6
+ Amplify.
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from typing import Any, Dict, Mapping, Optional, Sequence, Tuple
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class DeploymentConfig:
15
+ queue_name_template: str
16
+ subnet_ids: Tuple[str, ...] = field(default_factory=tuple)
17
+ security_group_ids: Tuple[str, ...] = field(default_factory=tuple)
18
+ short_timeout_seconds: int = 60
19
+ default_timeout_seconds: int = 900
20
+ short_timeout_tokens: Tuple[str, ...] = (
21
+ "Events",
22
+ "Data",
23
+ "Public",
24
+ "WebHooks",
25
+ )
26
+ memory_size_by_token: Mapping[str, int] = field(default_factory=dict)
27
+ extra_lambda_policy_statements: Tuple[Mapping[str, Any], ...] = field(
28
+ default_factory=tuple
29
+ )
30
+ use_reserved_concurrency: bool = True
31
+
32
+ # ── Per-app/stage generated IAM (see policies.app_runtime_statements) ──
33
+ # SSM read is scoped to /{ssm_project_name}/{stage}/* and /Global/{stage}/*.
34
+ # ssm_project_name is the runtime ProjectName (e.g. "Donor" for the donors
35
+ # app); None falls back to the deploy target's app name.
36
+ ssm_project_name: Optional[str] = None
37
+ ssm_read_global: bool = True
38
+ # Extra SSM read prefixes, "{stage}" substituted (e.g. "/Donor/{stage}").
39
+ extra_ssm_read_prefixes: Tuple[str, ...] = field(default_factory=tuple)
40
+ # Cognito user pools this app's functions call, per stage ("*" = every
41
+ # stage): pool ids ("us-east-1_AbC123") or user-pool ARNs.
42
+ cognito_user_pools: Mapping[str, Tuple[str, ...]] = field(default_factory=dict)
43
+ # Config keys resolved at deploy time to pool ids
44
+ # (/{ssm_project_name}/{stage}/{key}, then /Global/{stage}/{key}).
45
+ cognito_user_pool_config_keys: Tuple[str, ...] = field(default_factory=tuple)
46
+ # cognito-idp actions granted on those pools. The default is what
47
+ # velocity's own runtime needs (Context.get_cognito_user).
48
+ cognito_actions: Tuple[str, ...] = (
49
+ "cognito-idp:AdminGetUser",
50
+ "cognito-idp:ListUsers",
51
+ )
52
+ # Other queues this app sends to ("{branch}"/"{raw_branch}" substituted).
53
+ extra_send_queue_templates: Tuple[str, ...] = field(default_factory=tuple)
54
+ # SNS topic for velocity.aws.failures (the FailureTopicArn config key);
55
+ # "{stage}", "{region}", "{account}" substituted. Grants sns:Publish.
56
+ failure_topic_arn: Optional[str] = None
57
+
58
+ # ── Work queue (setup_sqs_queue_and_permissions) ──
59
+ # VisibilityTimeout = clamp(handler timeout x multiplier, 30 s, 12 h).
60
+ queue_visibility_timeout_multiplier: int = 6
61
+ queue_dlq_enabled: bool = True
62
+ queue_dlq_max_receive_count: int = 3
63
+ queue_batch_size: int = 1
64
+
65
+ def resolve_ssm_project_name(self, app_name: str) -> str:
66
+ return self.ssm_project_name or app_name
67
+
68
+ def resolve_cognito_user_pools(self, stage: str) -> Tuple[str, ...]:
69
+ pools = list(self.cognito_user_pools.get("*", ()))
70
+ for key in (stage, stage.lower()):
71
+ for pool in self.cognito_user_pools.get(key, ()):
72
+ if pool not in pools:
73
+ pools.append(pool)
74
+ return tuple(pools)
75
+
76
+ def resolve_send_queue_names(self, stage: str) -> Tuple[str, ...]:
77
+ return tuple(
78
+ template.format(branch=stage.lower(), raw_branch=stage)
79
+ for template in self.extra_send_queue_templates
80
+ )
81
+
82
+ def resolve_queue_name(self, stage: str) -> str:
83
+ return self.queue_name_template.format(branch=stage.lower(), raw_branch=stage)
84
+
85
+ def resolve_timeout(self, function_name: str) -> int:
86
+ if any(token in function_name for token in self.short_timeout_tokens):
87
+ return self.short_timeout_seconds
88
+ return self.default_timeout_seconds
89
+
90
+ def resolve_memory_size(self, function_name: str) -> Optional[int]:
91
+ for token, memory_size in self.memory_size_by_token.items():
92
+ if token in function_name:
93
+ return memory_size
94
+ return None
95
+
96
+ def build_lambda_policy_document(
97
+ self, base_policy_document: Mapping[str, Any]
98
+ ) -> Dict[str, Any]:
99
+ """Legacy helper: base document + this config's extra statements.
100
+
101
+ The shared-union policy mode it served was removed (no consumer used
102
+ it); kept for importers. Deployments read
103
+ extra_lambda_policy_statements directly into the per-app policy.
104
+ """
105
+ policy_document = dict(base_policy_document)
106
+ statements = list(policy_document.get("Statement", []))
107
+ statements.extend(
108
+ dict(statement) for statement in self.extra_lambda_policy_statements
109
+ )
110
+ policy_document["Statement"] = statements
111
+ return policy_document
112
+
113
+
114
+ # Backwards-compatible name used by existing build.py files.
115
+ BackendDeploymentConfig = DeploymentConfig
@@ -376,6 +376,113 @@ def build_sqs_queue_policy(
376
376
  )
377
377
 
378
378
 
379
+ MAX_VISIBILITY_TIMEOUT_SECONDS = 12 * 60 * 60
380
+ MIN_VISIBILITY_TIMEOUT_SECONDS = 30
381
+ REPORT_BATCH_ITEM_FAILURES = "ReportBatchItemFailures"
382
+
383
+
384
+ def queue_visibility_timeout(
385
+ function_timeout: Optional[int], multiplier: int = 6
386
+ ) -> int:
387
+ """AWS guidance for an SQS event source: visibility timeout of at least
388
+ six times the function timeout, clamped to [30 s, 12 h]."""
389
+ seconds = int(function_timeout or 0) * int(multiplier)
390
+ return max(
391
+ MIN_VISIBILITY_TIMEOUT_SECONDS, min(seconds, MAX_VISIBILITY_TIMEOUT_SECONDS)
392
+ )
393
+
394
+
395
+ def _queue_url_or_none(sqs_client, queue_name: str) -> Optional[str]:
396
+ try:
397
+ return sqs_client.get_queue_url(QueueName=queue_name)["QueueUrl"]
398
+ except sqs_client.exceptions.QueueDoesNotExist:
399
+ return None
400
+
401
+
402
+ def _ensure_dlq_queue(sqs_client, dlq_name: str, retention_seconds: int) -> str:
403
+ """Create ``dlq_name`` if missing; return its ARN."""
404
+ dlq_url = _queue_url_or_none(sqs_client, dlq_name)
405
+ if dlq_url is None:
406
+ dlq_url = sqs_client.create_queue(
407
+ QueueName=dlq_name,
408
+ Attributes={"MessageRetentionPeriod": str(retention_seconds)},
409
+ )["QueueUrl"]
410
+ print(f"[INFO] Created DLQ: {dlq_name}")
411
+ return sqs_client.get_queue_attributes(
412
+ QueueUrl=dlq_url, AttributeNames=["QueueArn"]
413
+ )["Attributes"]["QueueArn"]
414
+
415
+
416
+ def _normalized_attribute(name: str, value: Optional[str]) -> Any:
417
+ if value is None:
418
+ return None
419
+ if name in ("Policy", "RedrivePolicy"):
420
+ try:
421
+ return json.loads(value)
422
+ except (TypeError, ValueError):
423
+ return value
424
+ return str(value)
425
+
426
+
427
+ def ensure_queue_event_source_mapping(
428
+ lambda_client,
429
+ *,
430
+ queue_arn: str,
431
+ function_name: str,
432
+ batch_size: int = 1,
433
+ ) -> dict:
434
+ """Create or correct the SQS -> Lambda mapping.
435
+
436
+ The mapping must report partial batch failures
437
+ (``FunctionResponseTypes=["ReportBatchItemFailures"]``), otherwise the
438
+ handler's ``batchItemFailures`` response is ignored and failed records
439
+ are deleted as if they succeeded. Existing mappings are updated in place
440
+ (batch size and response types); ``Enabled`` is left as found. Prints and
441
+ returns the diff.
442
+ """
443
+ mappings = []
444
+ paginator = lambda_client.get_paginator("list_event_source_mappings")
445
+ for page in paginator.paginate(
446
+ EventSourceArn=queue_arn, FunctionName=function_name
447
+ ):
448
+ mappings.extend(page.get("EventSourceMappings", []))
449
+
450
+ if not mappings:
451
+ print(
452
+ f" + event source mapping {queue_arn.rsplit(':', 1)[-1]} -> {function_name} "
453
+ f"(BatchSize={batch_size}, FunctionResponseTypes=[{REPORT_BATCH_ITEM_FAILURES}])"
454
+ )
455
+ lambda_client.create_event_source_mapping(
456
+ EventSourceArn=queue_arn,
457
+ FunctionName=function_name,
458
+ Enabled=True,
459
+ BatchSize=batch_size,
460
+ FunctionResponseTypes=[REPORT_BATCH_ITEM_FAILURES],
461
+ )
462
+ return {"created": True, "updated": []}
463
+
464
+ updated = []
465
+ for mapping in mappings:
466
+ changes = {}
467
+ response_types = list(mapping.get("FunctionResponseTypes") or [])
468
+ if REPORT_BATCH_ITEM_FAILURES not in response_types:
469
+ changes["FunctionResponseTypes"] = response_types + [
470
+ REPORT_BATCH_ITEM_FAILURES
471
+ ]
472
+ if mapping.get("BatchSize") != batch_size:
473
+ changes["BatchSize"] = batch_size
474
+ if not changes:
475
+ continue
476
+ for key, value in changes.items():
477
+ print(
478
+ f" ~ event source mapping {mapping['UUID']}: {key} "
479
+ f"{mapping.get(key)!r} -> {value!r}"
480
+ )
481
+ lambda_client.update_event_source_mapping(UUID=mapping["UUID"], **changes)
482
+ updated.append(mapping["UUID"])
483
+ return {"created": False, "updated": updated}
484
+
485
+
379
486
  def setup_sqs_queue_and_permissions(
380
487
  *,
381
488
  account_id: str,
@@ -389,11 +496,28 @@ def setup_sqs_queue_and_permissions(
389
496
  sqs_resource=None,
390
497
  lambda_client=None,
391
498
  policy_json: Optional[str] = None,
392
- ) -> None:
499
+ function_timeout: Optional[int] = None,
500
+ visibility_timeout_multiplier: int = 6,
501
+ dlq_enabled: bool = True,
502
+ dlq_max_receive_count: int = 3,
503
+ dlq_retention_seconds: int = 1209600,
504
+ batch_size: int = 1,
505
+ ) -> dict:
506
+ """Reconcile the work queue, its DLQ and the handler's mapping.
507
+
508
+ - DLQ ``{queue_name}-dlq`` created by default, redrive maxReceiveCount 3;
509
+ - VisibilityTimeout = clamp(handler timeout x 6, 30 s, 12 h) — the
510
+ handler's ``Timeout`` unless ``function_timeout`` is given;
511
+ - queue policy scoped to the producer and handler roles;
512
+ - mapping with ``ReportBatchItemFailures`` (created or corrected).
513
+
514
+ The attribute diff is printed. ``sqs_resource`` is accepted for
515
+ backwards compatibility and unused. Returns
516
+ ``{"queue_arn", "dlq_arn", "attributes_changed", "mapping"}``.
517
+ """
518
+ del sqs_resource
393
519
  if sqs_client is None:
394
520
  sqs_client = boto3.client("sqs")
395
- if sqs_resource is None:
396
- sqs_resource = boto3.resource("sqs")
397
521
  if lambda_client is None:
398
522
  lambda_client = boto3.client("lambda")
399
523
 
@@ -401,28 +525,58 @@ def setup_sqs_queue_and_permissions(
401
525
  policy_json = build_sqs_queue_policy(
402
526
  account_id, region, queue_name, queue_producers, queue_handler
403
527
  )
528
+ if function_timeout is None:
529
+ function_timeout = queue_handler.get("Timeout") or 900
530
+ visibility = queue_visibility_timeout(
531
+ function_timeout, visibility_timeout_multiplier
532
+ )
404
533
 
405
- try:
406
- queue = sqs_resource.get_queue_by_name(QueueName=queue_name)
407
- sqs_client.set_queue_attributes(
408
- QueueUrl=queue.url,
409
- Attributes={"Policy": policy_json, "VisibilityTimeout": "900"},
534
+ desired = {"Policy": policy_json, "VisibilityTimeout": str(visibility)}
535
+ dlq_arn = None
536
+ if dlq_enabled:
537
+ dlq_arn = _ensure_dlq_queue(
538
+ sqs_client, f"{queue_name}-dlq", dlq_retention_seconds
410
539
  )
411
- except sqs_client.exceptions.QueueDoesNotExist:
412
- queue = sqs_resource.create_queue(
413
- QueueName=queue_name,
414
- Attributes={"Policy": policy_json, "VisibilityTimeout": "900"},
540
+ desired["RedrivePolicy"] = json.dumps(
541
+ {"maxReceiveCount": dlq_max_receive_count, "deadLetterTargetArn": dlq_arn}
415
542
  )
416
543
 
417
- try:
418
- lambda_client.create_event_source_mapping(
419
- EventSourceArn=f"arn:aws:sqs:{region}:{account_id}:{queue_name}",
420
- FunctionName=queue_handler["FunctionName"],
421
- Enabled=True,
422
- BatchSize=1,
423
- )
424
- except lambda_client.exceptions.ResourceConflictException:
425
- pass
544
+ queue_url = _queue_url_or_none(sqs_client, queue_name)
545
+ changed = []
546
+ if queue_url is None:
547
+ print(f" + queue {queue_name}: {sorted(desired)}")
548
+ queue_url = sqs_client.create_queue(QueueName=queue_name, Attributes=desired)[
549
+ "QueueUrl"
550
+ ]
551
+ changed = sorted(desired)
552
+ else:
553
+ current = sqs_client.get_queue_attributes(
554
+ QueueUrl=queue_url, AttributeNames=["All"]
555
+ ).get("Attributes", {})
556
+ delta = {
557
+ name: value
558
+ for name, value in desired.items()
559
+ if _normalized_attribute(name, current.get(name))
560
+ != _normalized_attribute(name, value)
561
+ }
562
+ for name in sorted(delta):
563
+ if name == "Policy":
564
+ print(f" ~ queue {queue_name}: Policy updated")
565
+ else:
566
+ print(
567
+ f" ~ queue {queue_name}: {name} {current.get(name)!r} -> {delta[name]!r}"
568
+ )
569
+ if delta:
570
+ sqs_client.set_queue_attributes(QueueUrl=queue_url, Attributes=delta)
571
+ changed = sorted(delta)
572
+
573
+ queue_arn = f"arn:aws:sqs:{region}:{account_id}:{queue_name}"
574
+ mapping = ensure_queue_event_source_mapping(
575
+ lambda_client,
576
+ queue_arn=queue_arn,
577
+ function_name=queue_handler["FunctionName"],
578
+ batch_size=batch_size,
579
+ )
426
580
 
427
581
  if use_reserved_concurrency:
428
582
  reserved = 10 if stage == "production" else 3
@@ -443,6 +597,13 @@ def setup_sqs_queue_and_permissions(
443
597
  except Exception:
444
598
  pass
445
599
 
600
+ return {
601
+ "queue_arn": queue_arn,
602
+ "dlq_arn": dlq_arn,
603
+ "attributes_changed": changed,
604
+ "mapping": mapping,
605
+ }
606
+
446
607
 
447
608
  def ensure_dlq(
448
609
  queue_name: str,
@@ -453,30 +614,18 @@ def ensure_dlq(
453
614
  ) -> Optional[str]:
454
615
  """Idempotently create `{queue_name}-dlq` and attach a redrive policy to
455
616
  the work queue. Returns the DLQ ARN, or None when the main queue is absent.
617
+
618
+ ``setup_sqs_queue_and_permissions`` now does this itself by default; this
619
+ stays for build scripts that call it directly.
456
620
  """
457
621
  if sqs_client is None:
458
622
  sqs_client = boto3.client("sqs")
459
623
 
460
624
  dlq_name = f"{queue_name}-dlq"
625
+ dlq_arn = _ensure_dlq_queue(sqs_client, dlq_name, retention_seconds)
461
626
 
462
- try:
463
- dlq_url = sqs_client.get_queue_url(QueueName=dlq_name)["QueueUrl"]
464
- print(f"[INFO] DLQ already exists: {dlq_name}")
465
- except sqs_client.exceptions.QueueDoesNotExist:
466
- resp = sqs_client.create_queue(
467
- QueueName=dlq_name,
468
- Attributes={"MessageRetentionPeriod": str(retention_seconds)},
469
- )
470
- dlq_url = resp["QueueUrl"]
471
- print(f"[INFO] Created DLQ: {dlq_name}")
472
-
473
- dlq_arn = sqs_client.get_queue_attributes(
474
- QueueUrl=dlq_url, AttributeNames=["QueueArn"]
475
- )["Attributes"]["QueueArn"]
476
-
477
- try:
478
- main_url = sqs_client.get_queue_url(QueueName=queue_name)["QueueUrl"]
479
- except sqs_client.exceptions.QueueDoesNotExist:
627
+ main_url = _queue_url_or_none(sqs_client, queue_name)
628
+ if main_url is None:
480
629
  print(f"[WARN] Main queue {queue_name!r} not found; skipping redrive policy.")
481
630
  return None
482
631