velocity-python 0.1.22__tar.gz → 0.1.23__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 (172) hide show
  1. {velocity_python-0.1.22/src/velocity_python.egg-info → velocity_python-0.1.23}/PKG-INFO +1 -1
  2. {velocity_python-0.1.22 → velocity_python-0.1.23}/pyproject.toml +1 -1
  3. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/amplify_build.py +62 -31
  5. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/async_support.py +10 -2
  6. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/transaction.py +16 -2
  7. {velocity_python-0.1.22 → velocity_python-0.1.23/src/velocity_python.egg-info}/PKG-INFO +1 -1
  8. velocity_python-0.1.23/tests/test_amplify_build.py +150 -0
  9. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_async_support.py +5 -0
  10. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_observability.py +3 -0
  11. velocity_python-0.1.22/tests/test_amplify_build.py +0 -70
  12. {velocity_python-0.1.22 → velocity_python-0.1.23}/LICENSE +0 -0
  13. {velocity_python-0.1.22 → velocity_python-0.1.23}/README.md +0 -0
  14. {velocity_python-0.1.22 → velocity_python-0.1.23}/setup.cfg +0 -0
  15. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/__init__.py +0 -0
  16. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/amplify.py +0 -0
  17. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/__init__.py +0 -0
  18. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/base_handler.py +0 -0
  19. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/context.py +0 -0
  20. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/context_factory.py +0 -0
  21. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/exceptions.py +0 -0
  22. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  23. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  24. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  25. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  26. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/perf.py +0 -0
  27. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/response.py +0 -0
  28. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  29. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/s3.py +0 -0
  30. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/tests/__init__.py +0 -0
  31. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  32. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  33. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/aws/tests/test_response.py +0 -0
  34. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/__init__.py +0 -0
  35. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/__init__.py +0 -0
  36. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/column.py +0 -0
  37. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/database.py +0 -0
  38. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/decorators.py +0 -0
  39. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/engine.py +0 -0
  40. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/result.py +0 -0
  41. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/row.py +0 -0
  42. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/sequence.py +0 -0
  43. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/table.py +0 -0
  44. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/core/view.py +0 -0
  45. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/exceptions.py +0 -0
  46. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/migrations.py +0 -0
  47. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/__init__.py +0 -0
  48. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/base/__init__.py +0 -0
  49. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/base/initializer.py +0 -0
  50. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/base/operators.py +0 -0
  51. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/base/sql.py +0 -0
  52. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/base/types.py +0 -0
  53. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/mysql/__init__.py +0 -0
  54. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/mysql/operators.py +0 -0
  55. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/mysql/reserved.py +0 -0
  56. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/mysql/sql.py +0 -0
  57. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/mysql/types.py +0 -0
  58. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/postgres/__init__.py +0 -0
  59. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/postgres/operators.py +0 -0
  60. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/postgres/reserved.py +0 -0
  61. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/postgres/sql.py +0 -0
  62. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/postgres/types.py +0 -0
  63. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  64. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/sqlite/operators.py +0 -0
  65. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  66. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/sqlite/sql.py +0 -0
  67. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/sqlite/types.py +0 -0
  68. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  69. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  70. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  71. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  72. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/sqlserver/types.py +0 -0
  73. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/servers/tablehelper.py +0 -0
  74. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/__init__.py +0 -0
  75. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/common_db_test.py +0 -0
  76. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/__init__.py +0 -0
  77. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/common.py +0 -0
  78. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_column.py +0 -0
  79. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  80. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_database.py +0 -0
  81. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  82. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  83. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  84. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_result.py +0 -0
  85. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_row.py +0 -0
  86. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  87. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  88. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  89. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  90. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  91. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_table.py +0 -0
  92. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  93. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  94. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/sql/__init__.py +0 -0
  95. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/sql/common.py +0 -0
  96. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  97. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  98. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  99. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_db_utils.py +0 -0
  100. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_postgres.py +0 -0
  101. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  102. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  103. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_result_caching.py +0 -0
  104. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  105. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  106. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  107. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  108. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_sql_builder.py +0 -0
  109. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_tablehelper.py +0 -0
  110. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/tests/test_view_helper.py +0 -0
  111. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/db/utils.py +0 -0
  112. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/logging.py +0 -0
  113. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/__init__.py +0 -0
  114. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/conv/__init__.py +0 -0
  115. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/conv/iconv.py +0 -0
  116. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/conv/oconv.py +0 -0
  117. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/db.py +0 -0
  118. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/export.py +0 -0
  119. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/format.py +0 -0
  120. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/mail.py +0 -0
  121. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/merge.py +0 -0
  122. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/pdf.py +0 -0
  123. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/tests/__init__.py +0 -0
  124. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/tests/test_db.py +0 -0
  125. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/tests/test_fix.py +0 -0
  126. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/tests/test_format.py +0 -0
  127. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/tests/test_iconv.py +0 -0
  128. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/tests/test_merge.py +0 -0
  129. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/tests/test_oconv.py +0 -0
  130. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/tests/test_original_error.py +0 -0
  131. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/tests/test_timer.py +0 -0
  132. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/timer.py +0 -0
  133. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/misc/tools.py +0 -0
  134. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/payment/__init__.py +0 -0
  135. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/payment/authorizenet_adapter.py +0 -0
  136. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/payment/base_adapter.py +0 -0
  137. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/payment/braintree_adapter.py +0 -0
  138. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/payment/charge_rules.py +0 -0
  139. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity/payment/stripe_adapter.py +0 -0
  140. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity_python.egg-info/SOURCES.txt +0 -0
  141. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  142. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity_python.egg-info/entry_points.txt +0 -0
  143. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity_python.egg-info/requires.txt +0 -0
  144. {velocity_python-0.1.22 → velocity_python-0.1.23}/src/velocity_python.egg-info/top_level.txt +0 -0
  145. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_batch_operations.py +0 -0
  146. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_concurrency_safety.py +0 -0
  147. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_connection_pool.py +0 -0
  148. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_connection_resilience.py +0 -0
  149. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_decorators.py +0 -0
  150. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_email_processing.py +0 -0
  151. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_iconv_money_to_cents.py +0 -0
  152. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_lambda_handler.py +0 -0
  153. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_lambda_handler_auth.py +0 -0
  154. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_mixins_import.py +0 -0
  155. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_n_plus_one.py +0 -0
  156. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_payment_braintree_adapter.py +0 -0
  157. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_payment_profile_sorting.py +0 -0
  158. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_payment_stripe_adapter.py +0 -0
  159. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_pdf.py +0 -0
  160. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_prepared_statements.py +0 -0
  161. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_psycopg3_upgrade.py +0 -0
  162. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_query_cache.py +0 -0
  163. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_row_batch_update.py +0 -0
  164. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_row_cache_staleness.py +0 -0
  165. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_row_dirty_tracking.py +0 -0
  166. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_schema_migrations.py +0 -0
  167. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_security_hardening.py +0 -0
  168. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_spreadsheet_functions.py +0 -0
  169. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_sqs_per_record_transactions.py +0 -0
  170. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  171. {velocity_python-0.1.22 → velocity_python-0.1.23}/tests/test_table_alter.py +0 -0
  172. {velocity_python-0.1.22 → velocity_python-0.1.23}/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.22
3
+ Version: 0.1.23
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.22"
7
+ version = "0.1.23"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.1.22"
1
+ __version__ = version = "0.1.23"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -235,6 +235,36 @@ def retryable_call(
235
235
  raise
236
236
 
237
237
 
238
+ def resolve_latest_layer_arns(
239
+ lambda_client,
240
+ current_layers: Sequence[Mapping[str, Any]],
241
+ ) -> Sequence[str]:
242
+ updated_layer_arns = []
243
+
244
+ for layer in current_layers or ():
245
+ layer_arn = layer["Arn"]
246
+ layer_name = layer_arn.split(":")[-2]
247
+ current_version = layer_arn.split(":")[-1]
248
+
249
+ latest_response = retryable_call(
250
+ lambda_client.list_layer_versions,
251
+ LayerName=layer_name,
252
+ MaxItems=1,
253
+ )
254
+
255
+ if latest_response and latest_response.get("LayerVersions"):
256
+ latest_version_info = latest_response["LayerVersions"][0]
257
+ latest_version = latest_version_info["Version"]
258
+ latest_arn = latest_version_info["LayerVersionArn"]
259
+ updated_layer_arns.append(
260
+ latest_arn if str(latest_version) != str(current_version) else layer_arn
261
+ )
262
+ else:
263
+ updated_layer_arns.append(layer_arn)
264
+
265
+ return updated_layer_arns
266
+
267
+
238
268
  def _serialize_policy_document(policy_document: Any) -> str:
239
269
  if isinstance(policy_document, str):
240
270
  return policy_document
@@ -443,6 +473,16 @@ def run_backend_deployment(
443
473
 
444
474
  timeout_value = config.resolve_timeout(function_name)
445
475
  memory_value = config.resolve_memory_size(function_name)
476
+ layers_value = None
477
+ current_layers = function.get("Layers") or []
478
+ if current_layers:
479
+ try:
480
+ layers_value = list(resolve_latest_layer_arns(lambda_client, current_layers))
481
+ except Exception as exc:
482
+ print(
483
+ f"[WARN] Unable to resolve latest layers for {function_name}: {exc}. "
484
+ "Keeping current layer attachments."
485
+ )
446
486
  print(
447
487
  f"[INFO] Updating Lambda function {function_name} with timeout={timeout_value}"
448
488
  + (
@@ -456,6 +496,7 @@ def run_backend_deployment(
456
496
  memory_size=memory_value,
457
497
  subnet_ids=subnet_ids,
458
498
  security_group_ids=security_group_ids,
499
+ layers=layers_value,
459
500
  )
460
501
 
461
502
  if not set_cloudwatch_logging(
@@ -563,44 +604,34 @@ def update_lambda_layers_to_latest(
563
604
  layer_arn = layer["Arn"]
564
605
  layer_name = layer_arn.split(":")[-2]
565
606
  current_version = layer_arn.split(":")[-1]
566
-
567
607
  print(
568
608
  f"[INFO] Processing layer: {layer_name} (current version: {current_version})"
569
609
  )
570
610
 
571
- try:
572
- latest_response = retryable_call(
573
- lambda_client.list_layer_versions,
574
- LayerName=layer_name,
575
- MaxItems=1,
611
+ try:
612
+ updated_layer_arns = list(
613
+ resolve_latest_layer_arns(lambda_client, current_layers)
614
+ )
615
+ except Exception as exc:
616
+ print(f"[ERROR] Failed to resolve latest layers: {exc}")
617
+ print("[INFO] Keeping current layer versions")
618
+ updated_layer_arns = [layer["Arn"] for layer in current_layers]
619
+
620
+ for original_arn, updated_arn in zip(
621
+ [layer["Arn"] for layer in current_layers],
622
+ updated_layer_arns,
623
+ ):
624
+ layer_name = original_arn.split(":")[-2]
625
+ current_version = original_arn.split(":")[-1]
626
+ latest_version = updated_arn.split(":")[-1]
627
+ if updated_arn != original_arn:
628
+ print(
629
+ f"[INFO] Updating {layer_name}: v{current_version} -> v{latest_version}"
576
630
  )
577
-
578
- if latest_response and latest_response.get("LayerVersions"):
579
- latest_version_info = latest_response["LayerVersions"][0]
580
- latest_version = latest_version_info["Version"]
581
- latest_arn = latest_version_info["LayerVersionArn"]
582
-
583
- if str(latest_version) != str(current_version):
584
- print(
585
- f"[INFO] Updating {layer_name}: v{current_version} -> v{latest_version}"
586
- )
587
- updated_layer_arns.append(latest_arn)
588
- else:
589
- print(
590
- f"[INFO] {layer_name} is already at latest version (v{latest_version})"
591
- )
592
- updated_layer_arns.append(layer_arn)
593
- else:
594
- print(
595
- f"[WARN] No versions found for layer {layer_name}, keeping current version"
596
- )
597
- updated_layer_arns.append(layer_arn)
598
- except Exception as exc:
631
+ else:
599
632
  print(
600
- f"[ERROR] Failed to get latest version for layer {layer_name}: {exc}"
633
+ f"[INFO] {layer_name} is already at latest version (v{latest_version})"
601
634
  )
602
- print(f"[INFO] Keeping current version for {layer_name}")
603
- updated_layer_arns.append(layer_arn)
604
635
 
605
636
  current_layer_arns = [layer["Arn"] for layer in current_layers]
606
637
  if updated_layer_arns != current_layer_arns:
@@ -493,10 +493,18 @@ class AsyncTransaction:
493
493
 
494
494
  # R12 — Slow query logging
495
495
  if _SLOW_QUERY_MS and elapsed_ms > _SLOW_QUERY_MS:
496
+ from velocity.db.core.transaction import _summarize_sql
497
+
498
+ sql_preview = _summarize_sql(sql)
496
499
  _logger.warning(
497
500
  "Slow async query: %.1f ms sql=%s",
498
- elapsed_ms, sql[:200],
499
- extra={"query_duration_ms": round(elapsed_ms, 1)},
501
+ elapsed_ms,
502
+ sql_preview,
503
+ extra={
504
+ "query_duration_ms": round(elapsed_ms, 1),
505
+ "sql_preview": sql_preview,
506
+ },
507
+ stack_info=True,
500
508
  )
501
509
 
502
510
  # R14 — N+1 detection
@@ -23,6 +23,7 @@ _DEFAULT_QUERY_CACHE_SIZE = int(os.environ.get("VELOCITY_QUERY_CACHE_SIZE", "100
23
23
 
24
24
  # Slow-query threshold in milliseconds (0 = disabled).
25
25
  _SLOW_QUERY_MS = int(os.environ.get("VELOCITY_SLOW_QUERY_MS", "500"))
26
+ _SLOW_QUERY_SQL_CHARS = int(os.environ.get("VELOCITY_SLOW_QUERY_SQL_CHARS", "4000"))
26
27
 
27
28
  # N+1 detection: warn when the same table is SELECTed more than this many
28
29
  # times within a single transaction. 0 = disabled. Only active when the
@@ -76,6 +77,16 @@ def _extract_table_name(sql):
76
77
  return None
77
78
 
78
79
 
80
+ def _summarize_sql(sql, limit=None):
81
+ if not sql:
82
+ return None
83
+ compact = " ".join(str(sql).split())
84
+ max_chars = _SLOW_QUERY_SQL_CHARS if limit is None else limit
85
+ if max_chars and len(compact) > max_chars:
86
+ return compact[:max_chars] + "... [truncated]"
87
+ return compact
88
+
89
+
79
90
  class Transaction:
80
91
  """
81
92
  Encapsulates a single transaction in the database (connection + commit/rollback).
@@ -210,14 +221,17 @@ class Transaction:
210
221
  if _SLOW_QUERY_MS and elapsed_ms > _SLOW_QUERY_MS:
211
222
  op = _classify_sql(sql)
212
223
  tbl = _extract_table_name(sql)
224
+ sql_preview = _summarize_sql(sql)
213
225
  _logger.warning(
214
- "Slow query (%s): %.1f ms table=%s",
215
- op, elapsed_ms, tbl,
226
+ "Slow query (%s): %.1f ms table=%s sql=%s",
227
+ op, elapsed_ms, tbl, sql_preview,
216
228
  extra={
217
229
  "query_duration_ms": round(elapsed_ms, 1),
218
230
  "table_name": tbl,
219
231
  "operation": op,
232
+ "sql_preview": sql_preview,
220
233
  },
234
+ stack_info=True,
221
235
  )
222
236
 
223
237
  # R14 — N+1 detection (only when debug=True).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.22
3
+ Version: 0.1.23
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
@@ -0,0 +1,150 @@
1
+ from velocity.aws.amplify_build import (
2
+ BackendDeploymentConfig,
3
+ build_environment_variables,
4
+ get_amplify_app_id_and_branch,
5
+ run_backend_deployment,
6
+ )
7
+
8
+
9
+ class FakeAmplifyProject:
10
+ def get_merged_env_vars(self, branch):
11
+ return {"EXISTING": branch}
12
+
13
+ def get_region(self):
14
+ return "us-east-1"
15
+
16
+
17
+ class FakeDeployProject(FakeAmplifyProject):
18
+ def __init__(self):
19
+ self.updated_functions = []
20
+
21
+ def list_lambda_functions_filtered(self, branch):
22
+ return [
23
+ {
24
+ "FunctionName": f"ClientEvents-{branch}",
25
+ "Layers": [
26
+ {
27
+ "Arn": "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-support:225"
28
+ },
29
+ {
30
+ "Arn": "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-accounting:34"
31
+ },
32
+ ],
33
+ }
34
+ ]
35
+
36
+ def update_lambda_function(self, **kwargs):
37
+ self.updated_functions.append(kwargs)
38
+
39
+
40
+ class FakeLambdaClient:
41
+ def list_layer_versions(self, LayerName, MaxItems):
42
+ versions = {
43
+ "py-lib-support": [
44
+ {
45
+ "Version": 240,
46
+ "LayerVersionArn": "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-support:240",
47
+ }
48
+ ],
49
+ "py-lib-accounting": [
50
+ {
51
+ "Version": 34,
52
+ "LayerVersionArn": "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-accounting:34",
53
+ }
54
+ ],
55
+ }
56
+ return {"LayerVersions": versions[LayerName][:MaxItems]}
57
+
58
+
59
+ def test_backend_deployment_config_resolves_queue_timeout_and_memory_size():
60
+ config = BackendDeploymentConfig(
61
+ queue_name_template="clients-queue-{branch}",
62
+ short_timeout_tokens=("Events", "Data"),
63
+ memory_size_by_token={"QueueHandler": 1024, "Events": 512},
64
+ )
65
+
66
+ assert config.resolve_queue_name("Demo") == "clients-queue-demo"
67
+ assert config.resolve_timeout("ClientEvents-demo") == 60
68
+ assert config.resolve_timeout("ClientUtility-demo") == 900
69
+ assert config.resolve_memory_size("ClientQueueHandler-demo") == 1024
70
+ assert config.resolve_memory_size("ClientEvents-demo") == 512
71
+ assert config.resolve_memory_size("ClientUtility-demo") is None
72
+
73
+
74
+ def test_build_environment_variables_reads_project_metadata(tmp_path, monkeypatch):
75
+ amplify_dir = tmp_path / "amplify" / "backend"
76
+ amplify_dir.mkdir(parents=True)
77
+ (amplify_dir / "amplify-meta.json").write_text('{"UserPoolId": "us-east-1_demoPool",}')
78
+
79
+ monkeypatch.setenv("AWS_JOB_ID", "job-123")
80
+ app = FakeAmplifyProject()
81
+
82
+ env_vars = build_environment_variables(
83
+ app,
84
+ "demo",
85
+ "clients-queue-demo",
86
+ project_root=tmp_path,
87
+ )
88
+
89
+ assert env_vars["EXISTING"] == "demo"
90
+ assert env_vars["SqsWorkQueue"] == "clients-queue-demo"
91
+ assert env_vars["AWS_JOB_ID"] == "job-123"
92
+ assert env_vars["AWS_USER_POOL_ID"] == "us-east-1_demoPool"
93
+ assert env_vars["USER_BRANCH"] == "demo"
94
+ assert env_vars["REACT_APP_USER_BRANCH"] == "demo"
95
+ assert env_vars["USER_REGION"] == "us-east-1"
96
+ assert env_vars["LOGLEVEL"] == "INFO"
97
+ assert "BUILD_DATETIME" in env_vars
98
+
99
+
100
+ def test_get_amplify_app_id_and_branch_uses_team_provider_info(tmp_path, monkeypatch):
101
+ team_provider_path = tmp_path / "amplify"
102
+ team_provider_path.mkdir(parents=True)
103
+ (team_provider_path / "team-provider-info.json").write_text(
104
+ '{"demo": {"awscloudformation": {"AmplifyAppId": "app-123"}}}'
105
+ )
106
+
107
+ monkeypatch.setenv("AWS_BRANCH", "demo")
108
+ monkeypatch.delenv("AWS_APP_ID", raising=False)
109
+
110
+ app_id, branch = get_amplify_app_id_and_branch(tmp_path)
111
+
112
+ assert app_id == "app-123"
113
+ assert branch == "demo"
114
+
115
+
116
+ def test_run_backend_deployment_refreshes_lambda_layers(monkeypatch, tmp_path):
117
+ monkeypatch.setattr(
118
+ "velocity.aws.amplify_build.ensure_lambda_policies_and_attach",
119
+ lambda *args, **kwargs: None,
120
+ )
121
+ monkeypatch.setattr(
122
+ "velocity.aws.amplify_build.set_cloudwatch_logging",
123
+ lambda *args, **kwargs: True,
124
+ )
125
+
126
+ app = FakeDeployProject()
127
+ config = BackendDeploymentConfig(
128
+ queue_name_template="clients-queue-{branch}",
129
+ short_timeout_tokens=("Events",),
130
+ memory_size_by_token={"Events": 512},
131
+ )
132
+
133
+ run_backend_deployment(
134
+ "app-123",
135
+ "demo",
136
+ config,
137
+ project_root=tmp_path,
138
+ app=app,
139
+ lambda_client=FakeLambdaClient(),
140
+ logs_client=object(),
141
+ )
142
+
143
+ assert len(app.updated_functions) == 1
144
+ assert app.updated_functions[0]["function_name"] == "ClientEvents-demo"
145
+ assert app.updated_functions[0]["timeout"] == 60
146
+ assert app.updated_functions[0]["memory_size"] == 512
147
+ assert app.updated_functions[0]["layers"] == [
148
+ "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-support:240",
149
+ "arn:aws:lambda:us-east-1:741671896925:layer:py-lib-accounting:34",
150
+ ]
@@ -687,3 +687,8 @@ class TestAsyncSlowQueryLogging:
687
687
  if "Slow" in str(c)
688
688
  ]
689
689
  assert len(slow_calls) >= 1
690
+ args = slow_calls[0].args
691
+ kwargs = slow_calls[0].kwargs
692
+ assert args[2] == "SELECT * FROM large_table"
693
+ assert kwargs["extra"]["sql_preview"] == "SELECT * FROM large_table"
694
+ assert kwargs["stack_info"] is True
@@ -177,6 +177,7 @@ class TestSlowQueryLogging:
177
177
 
178
178
  assert any("Slow query" in r.message for r in caplog.records)
179
179
  assert any("big_table" in r.message for r in caplog.records)
180
+ assert any("sql=SELECT * FROM big_table" in r.message for r in caplog.records)
180
181
 
181
182
  def test_fast_query_no_warning(self, caplog):
182
183
  tx = _make_tx()
@@ -230,6 +231,8 @@ class TestSlowQueryLogging:
230
231
  assert rec.query_duration_ms > 0
231
232
  assert rec.table_name == "orders"
232
233
  assert rec.operation == "SELECT"
234
+ assert rec.sql_preview == "SELECT * FROM orders WHERE id = 1"
235
+ assert rec.stack_info
233
236
 
234
237
 
235
238
  # ──────────────────────────────────────────────────────────────────────
@@ -1,70 +0,0 @@
1
- from velocity.aws.amplify_build import (
2
- BackendDeploymentConfig,
3
- build_environment_variables,
4
- get_amplify_app_id_and_branch,
5
- )
6
-
7
-
8
- class FakeAmplifyProject:
9
- def get_merged_env_vars(self, branch):
10
- return {"EXISTING": branch}
11
-
12
- def get_region(self):
13
- return "us-east-1"
14
-
15
-
16
- def test_backend_deployment_config_resolves_queue_timeout_and_memory_size():
17
- config = BackendDeploymentConfig(
18
- queue_name_template="clients-queue-{branch}",
19
- short_timeout_tokens=("Events", "Data"),
20
- memory_size_by_token={"QueueHandler": 1024, "Events": 512},
21
- )
22
-
23
- assert config.resolve_queue_name("Demo") == "clients-queue-demo"
24
- assert config.resolve_timeout("ClientEvents-demo") == 60
25
- assert config.resolve_timeout("ClientUtility-demo") == 900
26
- assert config.resolve_memory_size("ClientQueueHandler-demo") == 1024
27
- assert config.resolve_memory_size("ClientEvents-demo") == 512
28
- assert config.resolve_memory_size("ClientUtility-demo") is None
29
-
30
-
31
- def test_build_environment_variables_reads_project_metadata(tmp_path, monkeypatch):
32
- amplify_dir = tmp_path / "amplify" / "backend"
33
- amplify_dir.mkdir(parents=True)
34
- (amplify_dir / "amplify-meta.json").write_text('{"UserPoolId": "us-east-1_demoPool",}')
35
-
36
- monkeypatch.setenv("AWS_JOB_ID", "job-123")
37
- app = FakeAmplifyProject()
38
-
39
- env_vars = build_environment_variables(
40
- app,
41
- "demo",
42
- "clients-queue-demo",
43
- project_root=tmp_path,
44
- )
45
-
46
- assert env_vars["EXISTING"] == "demo"
47
- assert env_vars["SqsWorkQueue"] == "clients-queue-demo"
48
- assert env_vars["AWS_JOB_ID"] == "job-123"
49
- assert env_vars["AWS_USER_POOL_ID"] == "us-east-1_demoPool"
50
- assert env_vars["USER_BRANCH"] == "demo"
51
- assert env_vars["REACT_APP_USER_BRANCH"] == "demo"
52
- assert env_vars["USER_REGION"] == "us-east-1"
53
- assert env_vars["LOGLEVEL"] == "INFO"
54
- assert "BUILD_DATETIME" in env_vars
55
-
56
-
57
- def test_get_amplify_app_id_and_branch_uses_team_provider_info(tmp_path, monkeypatch):
58
- team_provider_path = tmp_path / "amplify"
59
- team_provider_path.mkdir(parents=True)
60
- (team_provider_path / "team-provider-info.json").write_text(
61
- '{"demo": {"awscloudformation": {"AmplifyAppId": "app-123"}}}'
62
- )
63
-
64
- monkeypatch.setenv("AWS_BRANCH", "demo")
65
- monkeypatch.delenv("AWS_APP_ID", raising=False)
66
-
67
- app_id, branch = get_amplify_app_id_and_branch(tmp_path)
68
-
69
- assert app_id == "app-123"
70
- assert branch == "demo"