velocity-python 0.1.44__tar.gz → 0.1.46__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 (191) hide show
  1. {velocity_python-0.1.44/src/velocity_python.egg-info → velocity_python-0.1.46}/PKG-INFO +1 -1
  2. {velocity_python-0.1.44 → velocity_python-0.1.46}/pyproject.toml +1 -1
  3. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/amplify.py +4 -12
  5. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/amplify_build.py +0 -8
  6. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/ssm_config.py +47 -10
  7. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/table.py +75 -11
  8. {velocity_python-0.1.44 → velocity_python-0.1.46/src/velocity_python.egg-info}/PKG-INFO +1 -1
  9. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity_python.egg-info/SOURCES.txt +1 -0
  10. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_amplify_build.py +13 -1
  11. velocity_python-0.1.46/tests/test_ssm_config.py +102 -0
  12. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_table_alter.py +48 -0
  13. {velocity_python-0.1.44 → velocity_python-0.1.46}/LICENSE +0 -0
  14. {velocity_python-0.1.44 → velocity_python-0.1.46}/README.md +0 -0
  15. {velocity_python-0.1.44 → velocity_python-0.1.46}/setup.cfg +0 -0
  16. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/__init__.py +0 -0
  17. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/assets/__init__.py +0 -0
  18. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/assets/backfill.py +0 -0
  19. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/assets/indexing.py +0 -0
  20. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/assets/references.py +0 -0
  21. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/assets/service.py +0 -0
  22. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/assets/usage_index.py +0 -0
  23. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/dirty_pipeline.py +0 -0
  24. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/__init__.py +0 -0
  25. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/base_handler.py +0 -0
  26. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/context.py +0 -0
  27. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/context_factory.py +0 -0
  28. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/exceptions.py +0 -0
  29. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  30. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  31. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  32. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  33. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/perf.py +0 -0
  34. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/response.py +0 -0
  35. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  36. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/s3.py +0 -0
  37. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/tests/__init__.py +0 -0
  38. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  39. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  40. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/aws/tests/test_response.py +0 -0
  41. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/__init__.py +0 -0
  42. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/__init__.py +0 -0
  43. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/async_support.py +0 -0
  44. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/column.py +0 -0
  45. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/database.py +0 -0
  46. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/decorators.py +0 -0
  47. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/engine.py +0 -0
  48. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/result.py +0 -0
  49. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/row.py +0 -0
  50. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/sequence.py +0 -0
  51. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/transaction.py +0 -0
  52. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/core/view.py +0 -0
  53. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/exceptions.py +0 -0
  54. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/migrations.py +0 -0
  55. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/__init__.py +0 -0
  56. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/base/__init__.py +0 -0
  57. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/base/initializer.py +0 -0
  58. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/base/operators.py +0 -0
  59. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/base/sql.py +0 -0
  60. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/base/types.py +0 -0
  61. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/mysql/__init__.py +0 -0
  62. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/mysql/operators.py +0 -0
  63. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/mysql/reserved.py +0 -0
  64. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/mysql/sql.py +0 -0
  65. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/mysql/types.py +0 -0
  66. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/postgres/__init__.py +0 -0
  67. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/postgres/operators.py +0 -0
  68. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/postgres/reserved.py +0 -0
  69. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/postgres/sql.py +0 -0
  70. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/postgres/types.py +0 -0
  71. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  72. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/sqlite/operators.py +0 -0
  73. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  74. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/sqlite/sql.py +0 -0
  75. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/sqlite/types.py +0 -0
  76. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  77. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  78. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  79. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  80. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/sqlserver/types.py +0 -0
  81. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/servers/tablehelper.py +0 -0
  82. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/__init__.py +0 -0
  83. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/common_db_test.py +0 -0
  84. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/__init__.py +0 -0
  85. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/common.py +0 -0
  86. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_column.py +0 -0
  87. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  88. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_database.py +0 -0
  89. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  90. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  91. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  92. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_result.py +0 -0
  93. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_row.py +0 -0
  94. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  95. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  96. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  97. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  98. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  99. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_table.py +0 -0
  100. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  101. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  102. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/sql/__init__.py +0 -0
  103. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/sql/common.py +0 -0
  104. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  105. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  106. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  107. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_db_utils.py +0 -0
  108. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_postgres.py +0 -0
  109. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  110. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  111. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_result_caching.py +0 -0
  112. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  113. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  114. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  115. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  116. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_sql_builder.py +0 -0
  117. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_tablehelper.py +0 -0
  118. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/tests/test_view_helper.py +0 -0
  119. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/db/utils.py +0 -0
  120. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/logging.py +0 -0
  121. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/__init__.py +0 -0
  122. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/conv/__init__.py +0 -0
  123. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/conv/iconv.py +0 -0
  124. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/conv/oconv.py +0 -0
  125. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/db.py +0 -0
  126. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/export.py +0 -0
  127. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/format.py +0 -0
  128. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/mail.py +0 -0
  129. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/merge.py +0 -0
  130. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/pdf.py +0 -0
  131. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/tests/__init__.py +0 -0
  132. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/tests/test_db.py +0 -0
  133. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/tests/test_fix.py +0 -0
  134. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/tests/test_format.py +0 -0
  135. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/tests/test_iconv.py +0 -0
  136. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/tests/test_merge.py +0 -0
  137. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/tests/test_oconv.py +0 -0
  138. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/tests/test_original_error.py +0 -0
  139. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/tests/test_timer.py +0 -0
  140. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/timer.py +0 -0
  141. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/misc/tools.py +0 -0
  142. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/payment/__init__.py +0 -0
  143. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/payment/authorizenet_adapter.py +0 -0
  144. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/payment/authorizenet_mirror.py +0 -0
  145. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/payment/base_adapter.py +0 -0
  146. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/payment/braintree_adapter.py +0 -0
  147. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/payment/braintree_mirror.py +0 -0
  148. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/payment/charge_rules.py +0 -0
  149. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/payment/stripe_adapter.py +0 -0
  150. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity/payment/stripe_mirror.py +0 -0
  151. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  152. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity_python.egg-info/entry_points.txt +0 -0
  153. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity_python.egg-info/requires.txt +0 -0
  154. {velocity_python-0.1.44 → velocity_python-0.1.46}/src/velocity_python.egg-info/top_level.txt +0 -0
  155. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_asset_indexing.py +0 -0
  156. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_asset_references.py +0 -0
  157. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_assets_service.py +0 -0
  158. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_async_support.py +0 -0
  159. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_batch_operations.py +0 -0
  160. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_concurrency_safety.py +0 -0
  161. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_connection_pool.py +0 -0
  162. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_connection_resilience.py +0 -0
  163. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_context_job_descriptions.py +0 -0
  164. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_decorators.py +0 -0
  165. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_dirty_pipeline_fast_path.py +0 -0
  166. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_email_processing.py +0 -0
  167. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_iconv_money_to_cents.py +0 -0
  168. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_lambda_handler.py +0 -0
  169. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_lambda_handler_auth.py +0 -0
  170. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_mixins_import.py +0 -0
  171. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_n_plus_one.py +0 -0
  172. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_observability.py +0 -0
  173. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_payment_authorizenet_adapter.py +0 -0
  174. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_payment_braintree_adapter.py +0 -0
  175. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_payment_braintree_mirror.py +0 -0
  176. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_payment_profile_sorting.py +0 -0
  177. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_payment_stripe_adapter.py +0 -0
  178. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_pdf.py +0 -0
  179. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_prepared_statements.py +0 -0
  180. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_psycopg3_upgrade.py +0 -0
  181. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_query_cache.py +0 -0
  182. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_row_batch_update.py +0 -0
  183. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_row_cache_staleness.py +0 -0
  184. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_row_dirty_tracking.py +0 -0
  185. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_schema_migrations.py +0 -0
  186. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_security_hardening.py +0 -0
  187. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_server_cursor.py +0 -0
  188. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_spreadsheet_functions.py +0 -0
  189. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_sqs_per_record_transactions.py +0 -0
  190. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  191. {velocity_python-0.1.44 → velocity_python-0.1.46}/tests/test_where_clause_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.44
3
+ Version: 0.1.46
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.44"
7
+ version = "0.1.46"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.1.44"
1
+ __version__ = version = "0.1.46"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -446,18 +446,10 @@ class AmplifyProject:
446
446
  )
447
447
 
448
448
  def sync(self, branch):
449
- print(
450
- f"\n🚀 Syncing environment variables for app '{self.app_id}' and environment '{branch}'..."
451
- )
452
- env_vars = self.get_merged_env_vars(branch)
453
-
454
- print(f"\n🔧 Applying {len(env_vars)} environment variables...")
455
- for function_name in self.list_lambda_functions_filtered(branch):
456
- print(f"➡️ Updating Lambda function: {function_name}")
457
- self.update_lambda_function(function_name, env_vars)
458
-
459
- print(
460
- "✅ Environment variables successfully applied to matching Lambda functions.\n"
449
+ raise RuntimeError(
450
+ "AmplifyProject.sync() is retired. Lambda environment variables are no "
451
+ "longer copied from Amplify configuration. Use SSM-backed runtime "
452
+ "configuration instead."
461
453
  )
462
454
 
463
455
 
@@ -450,13 +450,6 @@ def run_backend_deployment(
450
450
  queue_handler = None
451
451
  queue_producers = []
452
452
  pending_logging = []
453
-
454
- env_vars = build_environment_variables(
455
- app,
456
- branch,
457
- queue_name,
458
- project_root=project_root,
459
- )
460
453
  subnet_ids = list(config.subnet_ids) if config.subnet_ids else None
461
454
  security_group_ids = (
462
455
  list(config.security_group_ids) if config.security_group_ids else None
@@ -491,7 +484,6 @@ def run_backend_deployment(
491
484
  )
492
485
  app.update_lambda_function(
493
486
  function_name=function_name,
494
- merge_env_vars=env_vars,
495
487
  timeout=timeout_value,
496
488
  memory_size=memory_value,
497
489
  subnet_ids=subnet_ids,
@@ -6,7 +6,7 @@ Opt-in: set ``VELOCITY_SSM_ENABLED=true`` in your Amplify environment variables
6
6
 
7
7
  When enabled, ``getenv(key)`` reads from SSM at::
8
8
 
9
- /{ProjectName}/{ENV}/{key}
9
+ /{ProjectName}/{stage}/{key}
10
10
 
11
11
  and falls back to ``os.environ`` on a miss or any error. Results are cached
12
12
  for the process lifetime so SSM is called at most once per key per Lambda
@@ -40,12 +40,35 @@ def _is_enabled() -> bool:
40
40
  return _SSM_ENABLED
41
41
 
42
42
 
43
+ def _first_nonempty_env(*keys: str) -> str | None:
44
+ for key in keys:
45
+ value = os.environ.get(key)
46
+ if value is None:
47
+ continue
48
+ value = value.strip()
49
+ if value:
50
+ return value
51
+ return None
52
+
53
+
54
+ def get_project_name(default: Optional[str] = None) -> Optional[str]:
55
+ return _first_nonempty_env('ProjectName') or default
56
+
57
+
58
+ def get_stage(default: Optional[str] = None) -> Optional[str]:
59
+ return _first_nonempty_env('AppStage', 'ENV', 'USER_BRANCH', 'AWS_BRANCH') or default
60
+
61
+
62
+ def get_region(default: Optional[str] = 'us-east-1') -> Optional[str]:
63
+ return _first_nonempty_env('AWS_REGION', 'REGION', 'USER_REGION') or default
64
+
65
+
43
66
  def _ssm_prefix() -> str | None:
44
- """Return '/{ProjectName}/{ENV}' or None if either var is missing."""
45
- project = os.environ.get('ProjectName')
46
- env = os.environ.get('ENV')
47
- if project and env:
48
- return f'/{project}/{env}'
67
+ """Return '/{ProjectName}/{stage}' or None if either component is missing."""
68
+ project = get_project_name()
69
+ stage = get_stage()
70
+ if project and stage:
71
+ return f'/{project}/{stage}'
49
72
  return None
50
73
 
51
74
 
@@ -57,7 +80,7 @@ def _fetch(key: str) -> str | None:
57
80
  param_name = f'{prefix}/{key}'
58
81
  try:
59
82
  import boto3
60
- ssm = boto3.client('ssm', region_name=os.environ.get('REGION', 'us-east-1'))
83
+ ssm = boto3.client('ssm', region_name=get_region('us-east-1'))
61
84
  resp = ssm.get_parameter(Name=param_name, WithDecryption=True)
62
85
  return resp['Parameter']['Value']
63
86
  except Exception as exc:
@@ -65,16 +88,25 @@ def _fetch(key: str) -> str | None:
65
88
  return None
66
89
 
67
90
 
68
- def getenv(key: str, default: Optional[str] = None) -> Optional[str]:
91
+ def getenv(
92
+ key: str,
93
+ default: Optional[str] = None,
94
+ *,
95
+ raise_on_missing: bool = False,
96
+ ) -> Optional[str]:
69
97
  """
70
98
  Read a config value, checking SSM before ``os.environ``.
71
99
 
72
100
  SSM is only consulted when ``VELOCITY_SSM_ENABLED=true`` is set in the
73
- Lambda environment *and* both ``ProjectName`` and ``ENV`` are present.
101
+ Lambda environment *and* both ``ProjectName`` and a resolved stage are present.
74
102
  All other cases fall straight through to ``os.environ``.
75
103
 
76
104
  Results are cached per process so SSM is queried at most once per key
77
105
  per Lambda cold start.
106
+
107
+ When ``raise_on_missing`` is true, this raises ``KeyError`` if neither
108
+ SSM nor ``os.environ`` contains a non-empty value for the requested key.
109
+ In that mode, the provided default is ignored.
78
110
  """
79
111
  if _is_enabled():
80
112
  if key not in _cache:
@@ -82,9 +114,14 @@ def getenv(key: str, default: Optional[str] = None) -> Optional[str]:
82
114
  _cache[key] = value if value is not None else _MISS
83
115
  cached = _cache[key]
84
116
  if cached is not _MISS:
117
+ if raise_on_missing and cached == "":
118
+ raise KeyError(f'{key} not found in SSM or environment')
85
119
  return cached # type: ignore[return-value]
86
120
 
87
- return os.environ.get(key, default)
121
+ value = os.environ.get(key, default)
122
+ if raise_on_missing and value in (None, ""):
123
+ raise KeyError(f'{key} not found in SSM or environment')
124
+ return value
88
125
 
89
126
 
90
127
  def clear_cache() -> None:
@@ -234,6 +234,63 @@ def _normalize_index_expression(expression):
234
234
  return normalized.strip().lower()
235
235
 
236
236
 
237
+ def _split_top_level_boolean_terms(expression):
238
+ parts = []
239
+ operators = []
240
+ start = 0
241
+ depth = 0
242
+ position = 0
243
+ while position < len(expression):
244
+ char = expression[position]
245
+ if char == "(":
246
+ depth += 1
247
+ position += 1
248
+ continue
249
+ if char == ")":
250
+ depth = max(0, depth - 1)
251
+ position += 1
252
+ continue
253
+ if depth == 0:
254
+ lowered = expression[position:].lower()
255
+ for operator in (" and ", " or "):
256
+ if lowered.startswith(operator):
257
+ parts.append(expression[start:position].strip())
258
+ operators.append(operator.strip())
259
+ position += len(operator)
260
+ start = position
261
+ break
262
+ else:
263
+ position += 1
264
+ continue
265
+ position += 1
266
+
267
+ if operators:
268
+ parts.append(expression[start:].strip())
269
+
270
+ return parts, operators
271
+
272
+
273
+ def _normalize_index_where_clause(expression):
274
+ normalized = _normalize_index_expression(expression)
275
+ parts, operators = _split_top_level_boolean_terms(normalized)
276
+ if not operators:
277
+ return normalized
278
+
279
+ simplified_parts = []
280
+ for part in parts:
281
+ stripped = _strip_redundant_outer_parens(part)
282
+ nested_parts, nested_operators = _split_top_level_boolean_terms(stripped)
283
+ if nested_operators:
284
+ simplified_parts.append(part)
285
+ else:
286
+ simplified_parts.append(stripped)
287
+
288
+ combined = [simplified_parts[0]]
289
+ for operator, part in zip(operators, simplified_parts[1:]):
290
+ combined.extend([operator, part])
291
+ return " ".join(combined).strip()
292
+
293
+
237
294
  def _parse_index_signature(index_sql):
238
295
  normalized_sql = " ".join(str(index_sql or "").split())
239
296
  upper_sql = normalized_sql.upper()
@@ -257,7 +314,7 @@ def _parse_index_signature(index_sql):
257
314
  where_clause = None
258
315
  where_pos = upper_sql.find(" WHERE ", closing_index)
259
316
  if where_pos != -1:
260
- where_clause = _normalize_index_expression(normalized_sql[where_pos + 7 :])
317
+ where_clause = _normalize_index_where_clause(normalized_sql[where_pos + 7 :])
261
318
 
262
319
  return {
263
320
  "name": index_name,
@@ -686,16 +743,23 @@ class Table:
686
743
  return signatures
687
744
 
688
745
  def _desired_index_signature(self, spec):
689
- sql, _vals = self.create_index(
690
- spec["columns"],
691
- unique=spec["unique"],
692
- direction=spec["direction"],
693
- where=spec["where"],
694
- lower=spec["lower"],
695
- name=spec["name"],
696
- sql_only=True,
697
- )
698
- return _parse_index_signature(sql)
746
+ columns = []
747
+ for column_name in spec["columns"]:
748
+ normalized_column = _normalize_identifier(column_name)
749
+ if spec.get("lower"):
750
+ normalized_column = f"lower({normalized_column})"
751
+ columns.append(normalized_column)
752
+
753
+ return {
754
+ "name": _normalize_identifier(spec.get("name")) if spec.get("name") else None,
755
+ "unique": bool(spec.get("unique")),
756
+ "columns": tuple(columns),
757
+ "where": (
758
+ _normalize_index_where_clause(spec.get("where"))
759
+ if spec.get("where")
760
+ else None
761
+ ),
762
+ }
699
763
 
700
764
  def _existing_foreign_key_signatures(self):
701
765
  sql, vals = self.sql.foreign_key_info(table=self.name, column=None)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.44
3
+ Version: 0.1.46
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
@@ -183,6 +183,7 @@ tests/test_security_hardening.py
183
183
  tests/test_server_cursor.py
184
184
  tests/test_spreadsheet_functions.py
185
185
  tests/test_sqs_per_record_transactions.py
186
+ tests/test_ssm_config.py
186
187
  tests/test_sys_modified_count_postgres_demo.py
187
188
  tests/test_table_alter.py
188
189
  tests/test_where_clause_validation.py
@@ -1,3 +1,6 @@
1
+ import pytest
2
+
3
+ from velocity.aws.amplify import AmplifyProject
1
4
  from velocity.aws.amplify_build import (
2
5
  BackendDeploymentConfig,
3
6
  build_environment_variables,
@@ -144,7 +147,16 @@ def test_run_backend_deployment_refreshes_lambda_layers(monkeypatch, tmp_path):
144
147
  assert app.updated_functions[0]["function_name"] == "ClientEvents-demo"
145
148
  assert app.updated_functions[0]["timeout"] == 60
146
149
  assert app.updated_functions[0]["memory_size"] == 512
150
+ assert "merge_env_vars" not in app.updated_functions[0]
147
151
  assert app.updated_functions[0]["layers"] == [
148
152
  "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-support:240",
149
153
  "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-accounting:34",
150
- ]
154
+ ]
155
+
156
+
157
+ def test_amplify_project_sync_is_retired():
158
+ app = AmplifyProject.__new__(AmplifyProject)
159
+ app.app_id = "app-123"
160
+
161
+ with pytest.raises(RuntimeError, match=r"AmplifyProject\.sync\(\) is retired"):
162
+ app.sync("demo")
@@ -0,0 +1,102 @@
1
+ from unittest.mock import patch
2
+
3
+ import pytest
4
+
5
+ from velocity.aws import ssm_config
6
+
7
+
8
+ def test_get_stage_prefers_app_stage_then_aliases(monkeypatch):
9
+ ssm_config.clear_cache()
10
+ monkeypatch.setenv("AWS_BRANCH", "production")
11
+ monkeypatch.setenv("USER_BRANCH", "demo")
12
+ monkeypatch.setenv("ENV", "sandbox")
13
+ monkeypatch.setenv("AppStage", "stage")
14
+
15
+ assert ssm_config.get_stage() == "stage"
16
+
17
+
18
+ def test_ssm_prefix_uses_aws_branch_when_env_missing(monkeypatch):
19
+ ssm_config.clear_cache()
20
+ monkeypatch.setenv("ProjectName", "Client")
21
+ monkeypatch.delenv("ENV", raising=False)
22
+ monkeypatch.delenv("USER_BRANCH", raising=False)
23
+ monkeypatch.setenv("AWS_BRANCH", "production")
24
+
25
+ assert ssm_config._ssm_prefix() == "/Client/production"
26
+
27
+
28
+ def test_getenv_falls_back_to_environment_when_disabled(monkeypatch):
29
+ ssm_config.clear_cache()
30
+ monkeypatch.delenv("VELOCITY_SSM_ENABLED", raising=False)
31
+ monkeypatch.setenv("StripeSecretKey", "env-secret")
32
+
33
+ assert ssm_config.getenv("StripeSecretKey") == "env-secret"
34
+
35
+
36
+ def test_getenv_reads_ssm_with_region_aliases(monkeypatch):
37
+ ssm_config.clear_cache()
38
+ monkeypatch.setenv("VELOCITY_SSM_ENABLED", "true")
39
+ monkeypatch.setenv("ProjectName", "Client")
40
+ monkeypatch.setenv("AWS_BRANCH", "production")
41
+ monkeypatch.setenv("AWS_REGION", "us-west-2")
42
+ monkeypatch.setenv("StripeSecretKey", "env-secret")
43
+
44
+ class FakeSSM:
45
+ def get_parameter(self, Name, WithDecryption):
46
+ assert Name == "/Client/production/StripeSecretKey"
47
+ assert WithDecryption is True
48
+ return {"Parameter": {"Value": "ssm-secret"}}
49
+
50
+ with patch("boto3.client", return_value=FakeSSM()) as client_factory:
51
+ assert ssm_config.getenv("StripeSecretKey") == "ssm-secret"
52
+
53
+ client_factory.assert_called_once_with("ssm", region_name="us-west-2")
54
+
55
+
56
+ def test_getenv_raises_on_missing_when_disabled(monkeypatch):
57
+ ssm_config.clear_cache()
58
+ monkeypatch.delenv("VELOCITY_SSM_ENABLED", raising=False)
59
+ monkeypatch.delenv("MissingKey", raising=False)
60
+
61
+ with pytest.raises(KeyError, match="MissingKey"):
62
+ ssm_config.getenv("MissingKey", raise_on_missing=True)
63
+
64
+
65
+ def test_getenv_raises_on_missing_after_ssm_miss(monkeypatch):
66
+ ssm_config.clear_cache()
67
+ monkeypatch.setenv("VELOCITY_SSM_ENABLED", "true")
68
+ monkeypatch.setenv("ProjectName", "Client")
69
+ monkeypatch.setenv("AWS_BRANCH", "production")
70
+ monkeypatch.delenv("MissingKey", raising=False)
71
+
72
+ class FakeSSM:
73
+ def get_parameter(self, Name, WithDecryption):
74
+ raise Exception("missing")
75
+
76
+ with patch("boto3.client", return_value=FakeSSM()):
77
+ with pytest.raises(KeyError, match="MissingKey"):
78
+ ssm_config.getenv("MissingKey", raise_on_missing=True)
79
+
80
+
81
+ def test_getenv_raises_on_empty_environment_value(monkeypatch):
82
+ ssm_config.clear_cache()
83
+ monkeypatch.delenv("VELOCITY_SSM_ENABLED", raising=False)
84
+ monkeypatch.setenv("MissingKey", "")
85
+
86
+ with pytest.raises(KeyError, match="MissingKey"):
87
+ ssm_config.getenv("MissingKey", raise_on_missing=True)
88
+
89
+
90
+ def test_getenv_raises_on_empty_ssm_value(monkeypatch):
91
+ ssm_config.clear_cache()
92
+ monkeypatch.setenv("VELOCITY_SSM_ENABLED", "true")
93
+ monkeypatch.setenv("ProjectName", "Client")
94
+ monkeypatch.setenv("AWS_BRANCH", "production")
95
+
96
+ class FakeSSM:
97
+ def get_parameter(self, Name, WithDecryption):
98
+ return {"Parameter": {"Value": ""}}
99
+
100
+ with patch("boto3.client", return_value=FakeSSM()):
101
+ with pytest.raises(KeyError, match="MissingKey"):
102
+ ssm_config.getenv("MissingKey", raise_on_missing=True)
@@ -558,6 +558,54 @@ class TableAlterTests(unittest.TestCase):
558
558
  _parse_index_signature(desired),
559
559
  )
560
560
 
561
+ def test_parse_index_signature_ignores_redundant_boolean_term_parentheses(self):
562
+ existing = (
563
+ "CREATE INDEX idx_pending_transactions_scheduled_capture_at "
564
+ "ON public.pending_transactions USING btree (scheduled_capture_at) "
565
+ "WHERE ((status = 'authorized'::text) AND (captured_at IS NULL) AND "
566
+ "(canceled_at IS NULL))"
567
+ )
568
+ desired = (
569
+ "CREATE INDEX idx_pending_transactions_scheduled_capture_at "
570
+ "ON pending_transactions (scheduled_capture_at) "
571
+ "WHERE status = 'authorized' AND captured_at IS NULL AND canceled_at IS NULL"
572
+ )
573
+
574
+ self.assertEqual(
575
+ _parse_index_signature(existing),
576
+ _parse_index_signature(desired),
577
+ )
578
+
579
+ def test_desired_index_signature_allows_system_columns_not_in_declared_spec(self):
580
+ table = FakeTable(
581
+ PostgresSQL,
582
+ {
583
+ "product_sys_id": {"type": "BIGINT", "nullable": False},
584
+ },
585
+ )
586
+
587
+ signature = Table._desired_index_signature(
588
+ table,
589
+ {
590
+ "name": "idx_inventory_ledger_product_sys_id_sys_id",
591
+ "columns": ["product_sys_id", "sys_id"],
592
+ "unique": False,
593
+ "direction": None,
594
+ "where": None,
595
+ "lower": None,
596
+ },
597
+ )
598
+
599
+ self.assertEqual(
600
+ signature,
601
+ {
602
+ "name": "idx_inventory_ledger_product_sys_id_sys_id",
603
+ "unique": False,
604
+ "columns": ("product_sys_id", "sys_id"),
605
+ "where": None,
606
+ },
607
+ )
608
+
561
609
 
562
610
  if __name__ == "__main__":
563
611
  unittest.main()