velocity-python 0.1.48__tar.gz → 0.1.50__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.48/src/velocity_python.egg-info → velocity_python-0.1.50}/PKG-INFO +1 -1
  2. {velocity_python-0.1.48 → velocity_python-0.1.50}/pyproject.toml +1 -1
  3. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/amplify_build.py +19 -2
  5. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/context.py +19 -17
  6. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/ssm_config.py +28 -6
  7. {velocity_python-0.1.48 → velocity_python-0.1.50/src/velocity_python.egg-info}/PKG-INFO +1 -1
  8. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_amplify_build.py +30 -0
  9. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_context_job_descriptions.py +61 -0
  10. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_ssm_config.py +55 -0
  11. {velocity_python-0.1.48 → velocity_python-0.1.50}/LICENSE +0 -0
  12. {velocity_python-0.1.48 → velocity_python-0.1.50}/README.md +0 -0
  13. {velocity_python-0.1.48 → velocity_python-0.1.50}/setup.cfg +0 -0
  14. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/__init__.py +0 -0
  15. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/amplify.py +0 -0
  16. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/assets/__init__.py +0 -0
  17. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/assets/backfill.py +0 -0
  18. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/assets/indexing.py +0 -0
  19. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/assets/references.py +0 -0
  20. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/assets/service.py +0 -0
  21. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/assets/usage_index.py +0 -0
  22. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/dirty_pipeline.py +0 -0
  23. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/__init__.py +0 -0
  24. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/base_handler.py +0 -0
  25. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/context_factory.py +0 -0
  26. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/exceptions.py +0 -0
  27. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  28. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  29. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  30. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  31. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/perf.py +0 -0
  32. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/response.py +0 -0
  33. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  34. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/s3.py +0 -0
  35. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/tests/__init__.py +0 -0
  36. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  37. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  38. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/aws/tests/test_response.py +0 -0
  39. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/__init__.py +0 -0
  40. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/__init__.py +0 -0
  41. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/async_support.py +0 -0
  42. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/column.py +0 -0
  43. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/database.py +0 -0
  44. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/decorators.py +0 -0
  45. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/engine.py +0 -0
  46. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/result.py +0 -0
  47. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/row.py +0 -0
  48. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/sequence.py +0 -0
  49. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/table.py +0 -0
  50. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/transaction.py +0 -0
  51. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/core/view.py +0 -0
  52. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/exceptions.py +0 -0
  53. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/migrations.py +0 -0
  54. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/__init__.py +0 -0
  55. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/base/__init__.py +0 -0
  56. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/base/initializer.py +0 -0
  57. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/base/operators.py +0 -0
  58. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/base/sql.py +0 -0
  59. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/base/types.py +0 -0
  60. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/mysql/__init__.py +0 -0
  61. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/mysql/operators.py +0 -0
  62. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/mysql/reserved.py +0 -0
  63. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/mysql/sql.py +0 -0
  64. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/mysql/types.py +0 -0
  65. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/postgres/__init__.py +0 -0
  66. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/postgres/operators.py +0 -0
  67. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/postgres/reserved.py +0 -0
  68. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/postgres/sql.py +0 -0
  69. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/postgres/types.py +0 -0
  70. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  71. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/sqlite/operators.py +0 -0
  72. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  73. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/sqlite/sql.py +0 -0
  74. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/sqlite/types.py +0 -0
  75. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  76. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  77. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  78. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  79. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/sqlserver/types.py +0 -0
  80. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/servers/tablehelper.py +0 -0
  81. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/__init__.py +0 -0
  82. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/common_db_test.py +0 -0
  83. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/__init__.py +0 -0
  84. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/common.py +0 -0
  85. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_column.py +0 -0
  86. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  87. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_database.py +0 -0
  88. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  89. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  90. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  91. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_result.py +0 -0
  92. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_row.py +0 -0
  93. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  94. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  95. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  96. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  97. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  98. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_table.py +0 -0
  99. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  100. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  101. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/sql/__init__.py +0 -0
  102. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/sql/common.py +0 -0
  103. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  104. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  105. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  106. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_db_utils.py +0 -0
  107. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_postgres.py +0 -0
  108. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  109. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  110. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_result_caching.py +0 -0
  111. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  112. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  113. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  114. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  115. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_sql_builder.py +0 -0
  116. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_tablehelper.py +0 -0
  117. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/tests/test_view_helper.py +0 -0
  118. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/db/utils.py +0 -0
  119. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/logging.py +0 -0
  120. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/__init__.py +0 -0
  121. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/conv/__init__.py +0 -0
  122. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/conv/iconv.py +0 -0
  123. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/conv/oconv.py +0 -0
  124. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/db.py +0 -0
  125. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/export.py +0 -0
  126. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/format.py +0 -0
  127. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/mail.py +0 -0
  128. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/merge.py +0 -0
  129. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/pdf.py +0 -0
  130. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/tests/__init__.py +0 -0
  131. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/tests/test_db.py +0 -0
  132. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/tests/test_fix.py +0 -0
  133. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/tests/test_format.py +0 -0
  134. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/tests/test_iconv.py +0 -0
  135. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/tests/test_merge.py +0 -0
  136. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/tests/test_oconv.py +0 -0
  137. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/tests/test_original_error.py +0 -0
  138. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/tests/test_timer.py +0 -0
  139. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/timer.py +0 -0
  140. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/misc/tools.py +0 -0
  141. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/payment/__init__.py +0 -0
  142. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/payment/authorizenet_adapter.py +0 -0
  143. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/payment/authorizenet_mirror.py +0 -0
  144. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/payment/base_adapter.py +0 -0
  145. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/payment/braintree_adapter.py +0 -0
  146. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/payment/braintree_mirror.py +0 -0
  147. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/payment/charge_rules.py +0 -0
  148. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/payment/stripe_adapter.py +0 -0
  149. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity/payment/stripe_mirror.py +0 -0
  150. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity_python.egg-info/SOURCES.txt +0 -0
  151. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  152. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity_python.egg-info/entry_points.txt +0 -0
  153. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity_python.egg-info/requires.txt +0 -0
  154. {velocity_python-0.1.48 → velocity_python-0.1.50}/src/velocity_python.egg-info/top_level.txt +0 -0
  155. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_asset_indexing.py +0 -0
  156. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_asset_references.py +0 -0
  157. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_assets_service.py +0 -0
  158. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_async_support.py +0 -0
  159. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_batch_operations.py +0 -0
  160. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_concurrency_safety.py +0 -0
  161. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_connection_pool.py +0 -0
  162. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_connection_resilience.py +0 -0
  163. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_decorators.py +0 -0
  164. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_dirty_pipeline_fast_path.py +0 -0
  165. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_email_processing.py +0 -0
  166. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_iconv_money_to_cents.py +0 -0
  167. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_lambda_handler.py +0 -0
  168. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_lambda_handler_auth.py +0 -0
  169. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_mixins_import.py +0 -0
  170. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_n_plus_one.py +0 -0
  171. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_observability.py +0 -0
  172. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_payment_authorizenet_adapter.py +0 -0
  173. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_payment_braintree_adapter.py +0 -0
  174. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_payment_braintree_mirror.py +0 -0
  175. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_payment_profile_sorting.py +0 -0
  176. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_payment_stripe_adapter.py +0 -0
  177. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_pdf.py +0 -0
  178. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_prepared_statements.py +0 -0
  179. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_psycopg3_upgrade.py +0 -0
  180. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_query_cache.py +0 -0
  181. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_row_batch_update.py +0 -0
  182. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_row_cache_staleness.py +0 -0
  183. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_row_dirty_tracking.py +0 -0
  184. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_schema_migrations.py +0 -0
  185. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_security_hardening.py +0 -0
  186. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_server_cursor.py +0 -0
  187. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_spreadsheet_functions.py +0 -0
  188. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_sqs_per_record_transactions.py +0 -0
  189. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  190. {velocity_python-0.1.48 → velocity_python-0.1.50}/tests/test_table_alter.py +0 -0
  191. {velocity_python-0.1.48 → velocity_python-0.1.50}/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.48
3
+ Version: 0.1.50
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.48"
7
+ version = "0.1.50"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.1.48"
1
+ __version__ = version = "0.1.50"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -60,6 +60,9 @@ class BackendDeploymentConfig:
60
60
  "WebHooks",
61
61
  )
62
62
  memory_size_by_token: Mapping[str, int] = field(default_factory=dict)
63
+ extra_lambda_policy_statements: Tuple[Mapping[str, Any], ...] = field(
64
+ default_factory=tuple
65
+ )
63
66
  use_reserved_concurrency: bool = True
64
67
 
65
68
  def resolve_queue_name(self, branch: str) -> str:
@@ -76,6 +79,15 @@ class BackendDeploymentConfig:
76
79
  return memory_size
77
80
  return None
78
81
 
82
+ def build_lambda_policy_document(
83
+ self, base_policy_document: Mapping[str, Any]
84
+ ) -> Dict[str, Any]:
85
+ policy_document = dict(base_policy_document)
86
+ statements = list(policy_document.get("Statement", []))
87
+ statements.extend(dict(statement) for statement in self.extra_lambda_policy_statements)
88
+ policy_document["Statement"] = statements
89
+ return policy_document
90
+
79
91
 
80
92
  def _normalize_project_root(project_root: Union[str, Path]) -> Path:
81
93
  return Path(project_root).resolve()
@@ -319,6 +331,7 @@ def _sync_managed_policy_document(iam_client, policy_arn: str, policy_document:
319
331
  def ensure_lambda_policies_and_attach(
320
332
  app: AmplifyProject,
321
333
  function: Mapping[str, str],
334
+ config: Optional[BackendDeploymentConfig] = None,
322
335
  iam_client=None,
323
336
  ) -> None:
324
337
  if iam_client is None:
@@ -336,11 +349,15 @@ def ensure_lambda_policies_and_attach(
336
349
  ],
337
350
  }
338
351
 
352
+ lambda_vpc_policy = app.get_lambda_vpc_policy_template()
353
+ if config is not None:
354
+ lambda_vpc_policy = config.build_lambda_policy_document(lambda_vpc_policy)
355
+
339
356
  required_policies = [
340
357
  (
341
358
  "lambda-vpc-execution",
342
359
  f"arn:aws:iam::{account_id}:policy/lambda-vpc-execution",
343
- app.get_lambda_vpc_policy_template(),
360
+ lambda_vpc_policy,
344
361
  ),
345
362
  (
346
363
  "lambda-layer-access",
@@ -509,7 +526,7 @@ def run_backend_deployment(
509
526
  else:
510
527
  queue_producers.append(function)
511
528
 
512
- ensure_lambda_policies_and_attach(app, function)
529
+ ensure_lambda_policies_and_attach(app, function, config=config)
513
530
 
514
531
  timeout_value = config.resolve_timeout(function_name)
515
532
  memory_value = config.resolve_memory_size(function_name)
@@ -13,6 +13,9 @@ import hashlib
13
13
  import velocity
14
14
  import velocity.db
15
15
  from velocity.aws.ssm_config import getenv as config_getenv
16
+ from velocity.aws.ssm_config import get_project_name as config_get_project_name
17
+ from velocity.aws.ssm_config import get_region as config_get_region
18
+ from velocity.aws.ssm_config import get_stage as config_get_stage
16
19
  from velocity.logging import get_logger
17
20
 
18
21
  engine = velocity.db.postgres.initialize()
@@ -29,6 +32,18 @@ def _get_work_queue_name() -> str:
29
32
  return queue_name
30
33
 
31
34
 
35
+ def _get_controls_vars() -> dict:
36
+ return {
37
+ "USER_BRANCH": config_get_stage(),
38
+ "AWS_JOB_ID": os.environ.get("AWS_JOB_ID"),
39
+ "LOGLEVEL": config_getenv("LOGLEVEL"),
40
+ "USER_REGION": config_get_region(),
41
+ "ProjectName": config_get_project_name(),
42
+ "DBDatabase": config_getenv("DBDatabase"),
43
+ "BUILD_DATETIME": config_getenv("BUILD_DATETIME"),
44
+ }
45
+
46
+
32
47
  @engine.transaction
33
48
  class Context:
34
49
 
@@ -431,11 +446,11 @@ class Context:
431
446
 
432
447
  def _is_production(self):
433
448
  """Return True when running in production environment."""
434
- return os.environ.get("USER_BRANCH", "").lower() == "production"
449
+ return str(config_get_stage("") or "").lower() == "production"
435
450
 
436
451
  def get_environment(self):
437
452
  """Return the current USER_BRANCH value (defaults to demo)."""
438
- return os.environ.get("USER_BRANCH", "demo")
453
+ return config_get_stage("demo")
439
454
 
440
455
  def get_current_lambda_layers(self):
441
456
  """Return details about the Lambda layers attached to the running function."""
@@ -467,21 +482,8 @@ class Context:
467
482
  return []
468
483
 
469
484
  def get_pass_through_vars(self, tx):
470
- """Return environment and layer information for controls.vars."""
471
- vars = dict(
472
- [
473
- (x, os.environ.get(x))
474
- for x in [
475
- "USER_BRANCH",
476
- "AWS_JOB_ID",
477
- "LOGLEVEL",
478
- "USER_REGION",
479
- "ProjectName",
480
- "DBDatabase",
481
- "BUILD_DATETIME",
482
- ]
483
- ]
484
- )
485
+ """Return runtime and config information for controls.vars."""
486
+ vars = _get_controls_vars()
485
487
 
486
488
  temp = {"controls": {"vars": vars}}
487
489
 
@@ -5,6 +5,10 @@ SSM-backed configuration with environment-variable fallback.
5
5
 
6
6
  /{ProjectName}/{stage}/{key}
7
7
 
8
+ and, on a project-scoped miss, then tries::
9
+
10
+ /Global/{stage}/{key}
11
+
8
12
  when SSM loading is enabled. An explicit ``VELOCITY_SSM_ENABLED`` environment
9
13
  variable still overrides the behavior, but Lambda runtimes now auto-enable SSM
10
14
  when they can resolve both project and stage from the runtime environment.
@@ -118,19 +122,37 @@ def _ssm_prefix() -> str | None:
118
122
  return None
119
123
 
120
124
 
121
- def _fetch(key: str) -> str | None:
122
- """Single SSM GetParameter call; returns value or None on any failure."""
125
+ def _ssm_parameter_names(key: str) -> list[str]:
123
126
  prefix = _ssm_prefix()
124
127
  if prefix is None:
128
+ return []
129
+
130
+ parameter_names = [f'{prefix}/{key}']
131
+ project = get_project_name()
132
+ stage = get_stage()
133
+ if project and stage and project != 'Global':
134
+ parameter_names.append(f'/Global/{stage}/{key}')
135
+ return parameter_names
136
+
137
+
138
+ def _fetch(key: str) -> str | None:
139
+ """Resolve one key from project SSM first, then Global fallback."""
140
+ parameter_names = _ssm_parameter_names(key)
141
+ if not parameter_names:
125
142
  return None
126
- param_name = f'{prefix}/{key}'
143
+
127
144
  try:
128
145
  import boto3
129
146
  ssm = boto3.client('ssm', region_name=get_region('us-east-1'))
130
- resp = ssm.get_parameter(Name=param_name, WithDecryption=True)
131
- return resp['Parameter']['Value']
147
+ for param_name in parameter_names:
148
+ try:
149
+ resp = ssm.get_parameter(Name=param_name, WithDecryption=True)
150
+ return resp['Parameter']['Value']
151
+ except Exception as exc:
152
+ logger.debug('SSM miss for %s: %s', param_name, exc)
153
+ return None
132
154
  except Exception as exc:
133
- logger.debug('SSM miss for %s: %s', param_name, exc)
155
+ logger.debug('SSM client failure while resolving %s: %s', key, exc)
134
156
  return None
135
157
 
136
158
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.48
3
+ Version: 0.1.50
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
@@ -74,6 +74,36 @@ def test_backend_deployment_config_resolves_queue_timeout_and_memory_size():
74
74
  assert config.resolve_memory_size("ClientUtility-demo") is None
75
75
 
76
76
 
77
+ def test_backend_deployment_config_merges_extra_lambda_policy_statements():
78
+ config = BackendDeploymentConfig(
79
+ queue_name_template="clients-queue-{branch}",
80
+ extra_lambda_policy_statements=(
81
+ {
82
+ "Effect": "Allow",
83
+ "Action": ["ssm:PutParameter"],
84
+ "Resource": "*",
85
+ },
86
+ ),
87
+ )
88
+
89
+ base_policy = {
90
+ "Version": "2012-10-17",
91
+ "Statement": [
92
+ {
93
+ "Effect": "Allow",
94
+ "Action": ["ssm:GetParameter"],
95
+ "Resource": "*",
96
+ }
97
+ ],
98
+ }
99
+
100
+ merged = config.build_lambda_policy_document(base_policy)
101
+
102
+ assert len(merged["Statement"]) == 2
103
+ assert merged["Statement"][0]["Action"] == ["ssm:GetParameter"]
104
+ assert merged["Statement"][1]["Action"] == ["ssm:PutParameter"]
105
+
106
+
77
107
  def test_build_environment_variables_reads_project_metadata(tmp_path, monkeypatch):
78
108
  amplify_dir = tmp_path / "amplify" / "backend"
79
109
  amplify_dir.mkdir(parents=True)
@@ -87,6 +87,67 @@ class TestQueueJobDescriptions(unittest.TestCase):
87
87
  self.assertEqual(1, len(fake_context.jobs))
88
88
  self.assertIsNone(fake_context.jobs[0]["description"])
89
89
 
90
+ def test_get_pass_through_vars_uses_ssm_aware_runtime_helpers(self):
91
+ fake_context = _FakeContext()
92
+
93
+ def _fake_config_getenv(key, default=None, **kwargs):
94
+ values = {
95
+ "LOGLEVEL": "INFO",
96
+ "DBDatabase": "cc_demo",
97
+ "BUILD_DATETIME": "05/28/2026 11:00PM",
98
+ }
99
+ return values.get(key, default)
100
+
101
+ with patch.dict(
102
+ handler_context.os.environ,
103
+ {"AWS_JOB_ID": "job-123"},
104
+ clear=False,
105
+ ), patch.object(
106
+ handler_context,
107
+ "config_get_stage",
108
+ return_value="demo",
109
+ ), patch.object(
110
+ handler_context,
111
+ "config_get_region",
112
+ return_value="us-east-1",
113
+ ), patch.object(
114
+ handler_context,
115
+ "config_get_project_name",
116
+ return_value="BackOffice",
117
+ ), patch.object(
118
+ handler_context,
119
+ "config_getenv",
120
+ side_effect=_fake_config_getenv,
121
+ ):
122
+ result = handler_context.Context.get_pass_through_vars.__wrapped__(
123
+ fake_context,
124
+ tx=None,
125
+ )
126
+
127
+ self.assertEqual(
128
+ {
129
+ "USER_BRANCH": "demo",
130
+ "AWS_JOB_ID": "job-123",
131
+ "LOGLEVEL": "INFO",
132
+ "USER_REGION": "us-east-1",
133
+ "ProjectName": "BackOffice",
134
+ "DBDatabase": "cc_demo",
135
+ "BUILD_DATETIME": "05/28/2026 11:00PM",
136
+ },
137
+ result["controls"]["vars"],
138
+ )
139
+
140
+ def test_environment_helpers_use_stage_alias_resolution(self):
141
+ fake_context = _FakeContext()
142
+
143
+ with patch.object(
144
+ handler_context,
145
+ "config_get_stage",
146
+ side_effect=["production", "demo"],
147
+ ):
148
+ self.assertTrue(handler_context.Context._is_production(fake_context))
149
+ self.assertEqual("demo", handler_context.Context.get_environment(fake_context))
150
+
90
151
 
91
152
  if __name__ == "__main__":
92
153
  unittest.main()
@@ -83,6 +83,61 @@ def test_getenv_auto_enables_ssm_in_lambda_with_inferred_project(monkeypatch):
83
83
  client_factory.assert_called_once_with("ssm", region_name="us-west-2")
84
84
 
85
85
 
86
+ def test_getenv_falls_back_to_global_when_project_key_missing(monkeypatch):
87
+ ssm_config.clear_cache()
88
+ monkeypatch.setenv("VELOCITY_SSM_ENABLED", "true")
89
+ monkeypatch.setenv("ProjectName", "BackOffice")
90
+ monkeypatch.setenv("ENV", "demo")
91
+
92
+ class FakeSSM:
93
+ def __init__(self):
94
+ self.calls = []
95
+
96
+ def get_parameter(self, Name, WithDecryption):
97
+ self.calls.append((Name, WithDecryption))
98
+ if Name == "/BackOffice/demo/DBHost":
99
+ raise Exception("missing local")
100
+ if Name == "/Global/demo/DBHost":
101
+ return {"Parameter": {"Value": "global-host"}}
102
+ raise AssertionError(Name)
103
+
104
+ fake_ssm = FakeSSM()
105
+
106
+ with patch("boto3.client", return_value=fake_ssm):
107
+ assert ssm_config.getenv("DBHost") == "global-host"
108
+
109
+ assert fake_ssm.calls == [
110
+ ("/BackOffice/demo/DBHost", True),
111
+ ("/Global/demo/DBHost", True),
112
+ ]
113
+
114
+
115
+ def test_getenv_prefers_local_before_global(monkeypatch):
116
+ ssm_config.clear_cache()
117
+ monkeypatch.setenv("VELOCITY_SSM_ENABLED", "true")
118
+ monkeypatch.setenv("ProjectName", "BackOffice")
119
+ monkeypatch.setenv("ENV", "demo")
120
+
121
+ class FakeSSM:
122
+ def __init__(self):
123
+ self.calls = []
124
+
125
+ def get_parameter(self, Name, WithDecryption):
126
+ self.calls.append((Name, WithDecryption))
127
+ if Name == "/BackOffice/demo/DBHost":
128
+ return {"Parameter": {"Value": "local-host"}}
129
+ raise AssertionError(Name)
130
+
131
+ fake_ssm = FakeSSM()
132
+
133
+ with patch("boto3.client", return_value=fake_ssm):
134
+ assert ssm_config.getenv("DBHost") == "local-host"
135
+
136
+ assert fake_ssm.calls == [
137
+ ("/BackOffice/demo/DBHost", True),
138
+ ]
139
+
140
+
86
141
  def test_getenv_raises_on_missing_when_disabled(monkeypatch):
87
142
  ssm_config.clear_cache()
88
143
  monkeypatch.delenv("VELOCITY_SSM_ENABLED", raising=False)