velocity-python 0.1.1__tar.gz → 0.1.3__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 (179) hide show
  1. {velocity_python-0.1.1 → velocity_python-0.1.3}/PKG-INFO +12 -6
  2. {velocity_python-0.1.1 → velocity_python-0.1.3}/pyproject.toml +17 -6
  3. velocity_python-0.1.3/src/velocity/__init__.py +17 -0
  4. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/__init__.py +21 -11
  5. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/column.py +2 -1
  6. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/engine.py +5 -1
  7. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/row.py +43 -5
  8. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/table.py +28 -7
  9. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/base/sql.py +10 -0
  10. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/mysql/sql.py +5 -0
  11. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/sqlserver/sql.py +5 -0
  12. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/export.py +11 -4
  13. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity_python.egg-info/PKG-INFO +12 -6
  14. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity_python.egg-info/SOURCES.txt +2 -0
  15. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity_python.egg-info/requires.txt +15 -4
  16. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_row_batch_update.py +3 -0
  17. velocity_python-0.1.3/tests/test_row_cache_staleness.py +296 -0
  18. velocity_python-0.1.3/tests/test_security_hardening.py +256 -0
  19. velocity_python-0.1.1/src/velocity/__init__.py +0 -8
  20. {velocity_python-0.1.1 → velocity_python-0.1.3}/LICENSE +0 -0
  21. {velocity_python-0.1.1 → velocity_python-0.1.3}/README.md +0 -0
  22. {velocity_python-0.1.1 → velocity_python-0.1.3}/setup.cfg +0 -0
  23. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/__init__.py +0 -0
  24. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/formbuilder/__init__.py +0 -0
  25. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/formbuilder/reshaper.py +0 -0
  26. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/invoices.py +0 -0
  27. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/orders.py +0 -0
  28. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/payments.py +0 -0
  29. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/purchase_orders.py +0 -0
  30. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/tests/__init__.py +0 -0
  31. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/tests/test_email_processing.py +0 -0
  32. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/tests/test_payment_profile_sorting.py +0 -0
  33. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/tests/test_spreadsheet_functions.py +0 -0
  34. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/validators/__init__.py +0 -0
  35. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/app/validators/formbuilder_template.py +0 -0
  36. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/amplify.py +0 -0
  37. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/amplify_build.py +0 -0
  38. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/__init__.py +0 -0
  39. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/base_handler.py +0 -0
  40. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/context.py +0 -0
  41. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/context_factory.py +0 -0
  42. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/exceptions.py +0 -0
  43. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  44. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  45. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  46. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  47. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/perf.py +0 -0
  48. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/response.py +0 -0
  49. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  50. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/tests/__init__.py +0 -0
  51. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  52. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  53. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/aws/tests/test_response.py +0 -0
  54. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/__init__.py +0 -0
  55. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/__init__.py +0 -0
  56. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/database.py +0 -0
  57. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/decorators.py +0 -0
  58. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/result.py +0 -0
  59. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/sequence.py +0 -0
  60. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/transaction.py +0 -0
  61. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/core/view.py +0 -0
  62. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/exceptions.py +0 -0
  63. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/__init__.py +0 -0
  64. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/base/__init__.py +0 -0
  65. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/base/initializer.py +0 -0
  66. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/base/operators.py +0 -0
  67. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/base/types.py +0 -0
  68. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/mysql/__init__.py +0 -0
  69. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/mysql/operators.py +0 -0
  70. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/mysql/reserved.py +0 -0
  71. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/mysql/types.py +0 -0
  72. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/postgres/__init__.py +0 -0
  73. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/postgres/operators.py +0 -0
  74. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/postgres/reserved.py +0 -0
  75. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/postgres/sql.py +0 -0
  76. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/postgres/types.py +0 -0
  77. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  78. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/sqlite/operators.py +0 -0
  79. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  80. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/sqlite/sql.py +0 -0
  81. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/sqlite/types.py +0 -0
  82. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  83. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  84. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  85. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/sqlserver/types.py +0 -0
  86. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/servers/tablehelper.py +0 -0
  87. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/__init__.py +0 -0
  88. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/common_db_test.py +0 -0
  89. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/__init__.py +0 -0
  90. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/common.py +0 -0
  91. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_column.py +0 -0
  92. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  93. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_database.py +0 -0
  94. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  95. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  96. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  97. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_result.py +0 -0
  98. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_row.py +0 -0
  99. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  100. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  101. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  102. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  103. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  104. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_table.py +0 -0
  105. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  106. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  107. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/sql/__init__.py +0 -0
  108. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/sql/common.py +0 -0
  109. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  110. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  111. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  112. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_db_utils.py +0 -0
  113. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_postgres.py +0 -0
  114. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  115. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  116. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_result_caching.py +0 -0
  117. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  118. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  119. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  120. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  121. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_sql_builder.py +0 -0
  122. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_tablehelper.py +0 -0
  123. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/tests/test_view_helper.py +0 -0
  124. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/db/utils.py +0 -0
  125. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/logging.py +0 -0
  126. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/__init__.py +0 -0
  127. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/conv/__init__.py +0 -0
  128. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/conv/iconv.py +0 -0
  129. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/conv/oconv.py +0 -0
  130. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/db.py +0 -0
  131. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/format.py +0 -0
  132. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/mail.py +0 -0
  133. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/merge.py +0 -0
  134. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/tests/__init__.py +0 -0
  135. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/tests/test_db.py +0 -0
  136. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/tests/test_fix.py +0 -0
  137. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/tests/test_format.py +0 -0
  138. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/tests/test_iconv.py +0 -0
  139. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/tests/test_merge.py +0 -0
  140. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/tests/test_oconv.py +0 -0
  141. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/tests/test_original_error.py +0 -0
  142. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/tests/test_timer.py +0 -0
  143. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/timer.py +0 -0
  144. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/misc/tools.py +0 -0
  145. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/payment/__init__.py +0 -0
  146. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/payment/authorizenet_adapter.py +0 -0
  147. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/payment/base_adapter.py +0 -0
  148. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/payment/braintree_adapter.py +0 -0
  149. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/payment/charge_rules.py +0 -0
  150. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/payment/demo_profiles.py +0 -0
  151. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/payment/profiles.py +0 -0
  152. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/payment/router.py +0 -0
  153. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity/payment/stripe_adapter.py +0 -0
  154. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  155. {velocity_python-0.1.1 → velocity_python-0.1.3}/src/velocity_python.egg-info/top_level.txt +0 -0
  156. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_amplify_build.py +0 -0
  157. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_batch_operations.py +0 -0
  158. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_concurrency_safety.py +0 -0
  159. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_connection_pool.py +0 -0
  160. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_connection_resilience.py +0 -0
  161. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_decorators.py +0 -0
  162. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_formbuilder_reshaper.py +0 -0
  163. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_formbuilder_template_validator.py +0 -0
  164. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_iconv_money_to_cents.py +0 -0
  165. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_lambda_handler.py +0 -0
  166. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_lambda_handler_auth.py +0 -0
  167. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_mixins_import.py +0 -0
  168. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_payment_braintree_adapter.py +0 -0
  169. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_payment_demo_profiles.py +0 -0
  170. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_payment_profiles.py +0 -0
  171. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_payment_router.py +0 -0
  172. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_payment_stripe_adapter.py +0 -0
  173. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_prepared_statements.py +0 -0
  174. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_psycopg3_upgrade.py +0 -0
  175. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_query_cache.py +0 -0
  176. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_sqs_per_record_transactions.py +0 -0
  177. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  178. {velocity_python-0.1.1 → velocity_python-0.1.3}/tests/test_table_alter.py +0 -0
  179. {velocity_python-0.1.1 → velocity_python-0.1.3}/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.1
3
+ Version: 0.1.3
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
@@ -19,12 +19,16 @@ Classifier: Operating System :: OS Independent
19
19
  Requires-Python: >=3.9
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
- Requires-Dist: boto3>=1.35.0
23
- Requires-Dist: requests>=2.32.0
24
- Requires-Dist: jinja2>=3.1.0
25
- Requires-Dist: xlrd>=2.0.1
26
- Requires-Dist: openpyxl>=3.1.0
27
22
  Requires-Dist: sqlparse>=0.5.0
23
+ Provides-Extra: aws
24
+ Requires-Dist: boto3>=1.35.0; extra == "aws"
25
+ Requires-Dist: requests>=2.32.0; extra == "aws"
26
+ Provides-Extra: excel
27
+ Requires-Dist: openpyxl>=3.1.0; extra == "excel"
28
+ Provides-Extra: templates
29
+ Requires-Dist: jinja2>=3.1.0; extra == "templates"
30
+ Provides-Extra: http
31
+ Requires-Dist: requests>=2.32.0; extra == "http"
28
32
  Provides-Extra: mysql
29
33
  Requires-Dist: mysql-connector-python>=9.0.0; extra == "mysql"
30
34
  Provides-Extra: sqlserver
@@ -34,6 +38,8 @@ Requires-Dist: psycopg[binary]>=3.2.0; extra == "postgres"
34
38
  Provides-Extra: payment
35
39
  Requires-Dist: stripe>=12.0.0; extra == "payment"
36
40
  Requires-Dist: braintree>=4.30.0; extra == "payment"
41
+ Provides-Extra: all
42
+ Requires-Dist: velocity-python[aws,excel,http,payment,postgres,templates]; extra == "all"
37
43
  Provides-Extra: dev
38
44
  Requires-Dist: pytest>=8.0.0; extra == "dev"
39
45
  Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "velocity-python"
7
- version = "0.1.1"
7
+ version = "0.1.3"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -26,11 +26,6 @@ classifiers = [
26
26
  "Operating System :: OS Independent",
27
27
  ]
28
28
  dependencies = [
29
- "boto3>=1.35.0",
30
- "requests>=2.32.0",
31
- "jinja2>=3.1.0",
32
- "xlrd>=2.0.1",
33
- "openpyxl>=3.1.0",
34
29
  "sqlparse>=0.5.0"
35
30
  ]
36
31
 
@@ -38,6 +33,19 @@ dependencies = [
38
33
  Homepage = "https://codeclubs.org/projects/velocity"
39
34
 
40
35
  [project.optional-dependencies]
36
+ aws = [
37
+ "boto3>=1.35.0",
38
+ "requests>=2.32.0",
39
+ ]
40
+ excel = [
41
+ "openpyxl>=3.1.0",
42
+ ]
43
+ templates = [
44
+ "jinja2>=3.1.0",
45
+ ]
46
+ http = [
47
+ "requests>=2.32.0",
48
+ ]
41
49
  mysql = [
42
50
  "mysql-connector-python>=9.0.0",
43
51
  ]
@@ -51,6 +59,9 @@ payment = [
51
59
  "stripe>=12.0.0",
52
60
  "braintree>=4.30.0",
53
61
  ]
62
+ all = [
63
+ "velocity-python[postgres,aws,excel,templates,http,payment]",
64
+ ]
54
65
  dev = [
55
66
  "pytest>=8.0.0",
56
67
  "pytest-cov>=6.0.0",
@@ -0,0 +1,17 @@
1
+ __version__ = version = "0.1.3"
2
+
3
+ import importlib as _importlib
4
+
5
+ from . import db
6
+ from . import misc
7
+ from . import app
8
+
9
+ __all__ = ["aws", "db", "misc", "app"]
10
+
11
+ _LAZY_SUBMODULES = {"aws"}
12
+
13
+
14
+ def __getattr__(name: str):
15
+ if name in _LAZY_SUBMODULES:
16
+ return _importlib.import_module(f".{name}", __name__)
17
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -1,15 +1,5 @@
1
+ import importlib as _importlib
1
2
  import os
2
- import requests
3
-
4
- from velocity.aws.amplify import AmplifyProject
5
- from velocity.aws.amplify_build import (
6
- BackendDeploymentConfig,
7
- build_environment_variables,
8
- get_amplify_app_id_and_branch,
9
- initialize_build_environment,
10
- run_backend_deployment,
11
- update_lambda_layers_to_latest,
12
- )
13
3
 
14
4
  DEBUG = (os.environ.get("ENV") != "production") or (os.environ.get("DEBUG") == "Y")
15
5
 
@@ -23,11 +13,31 @@ class AWS(object):
23
13
  # Get AWS EC2 Instance ID. Must run this from the EC2 instance itself to get the ID
24
14
  @staticmethod
25
15
  def instance_id(cls):
16
+ import requests
26
17
  response = requests.get("http://169.254.169.254/latest/meta-data/instance-id")
27
18
  instance_id = response.text
28
19
  return instance_id
29
20
 
30
21
 
22
+ # Lazy-load heavy modules (boto3-dependent) on first access
23
+ _LAZY_ATTRS = {
24
+ "AmplifyProject": "velocity.aws.amplify",
25
+ "BackendDeploymentConfig": "velocity.aws.amplify_build",
26
+ "build_environment_variables": "velocity.aws.amplify_build",
27
+ "get_amplify_app_id_and_branch": "velocity.aws.amplify_build",
28
+ "initialize_build_environment": "velocity.aws.amplify_build",
29
+ "run_backend_deployment": "velocity.aws.amplify_build",
30
+ "update_lambda_layers_to_latest": "velocity.aws.amplify_build",
31
+ }
32
+
33
+
34
+ def __getattr__(name: str):
35
+ if name in _LAZY_ATTRS:
36
+ module = _importlib.import_module(_LAZY_ATTRS[name])
37
+ return getattr(module, name)
38
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
39
+
40
+
31
41
  __all__ = [
32
42
  "AmplifyProject",
33
43
  "AWS",
@@ -125,8 +125,9 @@ class Column:
125
125
  Returns the MAX() of this column, or 0 if table/column is missing.
126
126
  """
127
127
  try:
128
+ qcol = self.sql.quote_identifier(self.name)
128
129
  sql, vals = self.sql.select(
129
- columns=f"max({self.name})", table=self.table.name, where=where
130
+ columns=f"max({qcol})", table=self.table.name, where=where
130
131
  )
131
132
  return self.tx.execute(sql, vals).scalar()
132
133
  except (exceptions.DbTableMissingError, exceptions.DbColumnMissingError):
@@ -7,7 +7,7 @@ from contextlib import contextmanager
7
7
  from functools import wraps
8
8
  from velocity.db import exceptions
9
9
  from velocity.db.core.transaction import Transaction
10
- from velocity.db.utils import mask_config_for_display
10
+ from velocity.db.utils import mask_config_for_display, mask_sensitive_in_string
11
11
 
12
12
  import logging
13
13
 
@@ -714,6 +714,10 @@ class Engine:
714
714
  if relevant_frames:
715
715
  enhanced_message += "\n\nCall Context:\n" + "".join(relevant_frames)
716
716
 
717
+ # Mask any credentials that may have leaked into driver error messages
718
+ # (e.g. connection strings containing password=...).
719
+ enhanced_message = mask_sensitive_in_string(enhanced_message)
720
+
717
721
  # Note: SQL formatting for logging is available via _format_sql_with_params,
718
722
  # but we intentionally avoid eager logging here.
719
723
 
@@ -1,4 +1,5 @@
1
1
  import pprint
2
+ import time as _time
2
3
  import warnings
3
4
  from collections.abc import MutableMapping
4
5
  from velocity.db.exceptions import DbColumnMissingError
@@ -8,6 +9,7 @@ from velocity.db.exceptions import DbColumnMissingError
8
9
  # intercepted by __getattr__ / __setattr__.
9
10
  _INTERNAL_ATTRS = frozenset({
10
11
  "table", "pk", "_cache", "_column_set", "_batching", "_pending",
12
+ "_cache_ttl", "_cache_time", "_no_cache",
11
13
  })
12
14
 
13
15
 
@@ -23,7 +25,7 @@ class Row(MutableMapping):
23
25
  write-through (immediate UPDATE) and also update the local cache.
24
26
  """
25
27
 
26
- def __init__(self, table, key, lock=None):
28
+ def __init__(self, table, key, lock=None, cache_ttl=None, no_cache=False):
27
29
  if isinstance(table, str):
28
30
  raise Exception("Table parameter must be a `table` instance.")
29
31
  object.__setattr__(self, "table", table)
@@ -43,6 +45,9 @@ class Row(MutableMapping):
43
45
  object.__setattr__(self, "_column_set", None)
44
46
  object.__setattr__(self, "_batching", False)
45
47
  object.__setattr__(self, "_pending", {})
48
+ object.__setattr__(self, "_cache_ttl", cache_ttl)
49
+ object.__setattr__(self, "_cache_time", None)
50
+ object.__setattr__(self, "_no_cache", no_cache)
46
51
  if lock:
47
52
  self.lock()
48
53
 
@@ -51,10 +56,20 @@ class Row(MutableMapping):
51
56
  # ------------------------------------------------------------------
52
57
 
53
58
  def _ensure_cache(self):
54
- """Populate the local cache from the database if not yet loaded."""
59
+ """Populate the local cache from the database if not yet loaded or expired."""
60
+ if self._no_cache:
61
+ # Always fetch fresh — never store in cache
62
+ data = self.table.select(where=self.pk).as_dict().one()
63
+ return data or {}
64
+ if self._cache is not None and self._cache_ttl is not None:
65
+ elapsed = _time.monotonic() - (self._cache_time or 0)
66
+ if elapsed > self._cache_ttl:
67
+ object.__setattr__(self, "_cache", None)
68
+ object.__setattr__(self, "_column_set", None)
55
69
  if self._cache is None:
56
70
  data = self.table.select(where=self.pk).as_dict().one()
57
71
  object.__setattr__(self, "_cache", data or {})
72
+ object.__setattr__(self, "_cache_time", _time.monotonic())
58
73
  return self._cache
59
74
 
60
75
  def _column_names_lower(self):
@@ -232,9 +247,9 @@ class Row(MutableMapping):
232
247
  data.update(kwds)
233
248
  if data:
234
249
  self.table.update_or_insert(data, pk=self.pk)
235
- if self._cache is not None:
236
- self._cache.update(data)
237
- object.__setattr__(self, "_column_set", None)
250
+ # Invalidate cache so trigger-computed columns are re-fetched
251
+ object.__setattr__(self, "_cache", None)
252
+ object.__setattr__(self, "_column_set", None)
238
253
  return self
239
254
 
240
255
  def copy(self, lock=None):
@@ -366,6 +381,29 @@ class Row(MutableMapping):
366
381
  self.table.select(where=self.pk, lock=True)
367
382
  return self
368
383
 
384
+ def with_lock(self):
385
+ """SELECT ... FOR UPDATE and clear the cache so the next read is fresh.
386
+
387
+ Returns self for chaining::
388
+
389
+ row = row.with_lock()
390
+ balance = row["balance"] # guaranteed fresh, locked
391
+ """
392
+ self.table.select(where=self.pk, lock=True)
393
+ object.__setattr__(self, "_cache", None)
394
+ object.__setattr__(self, "_column_set", None)
395
+ return self
396
+
397
+ def no_cache(self):
398
+ """Return a new Row that never caches — every read hits the database.
399
+
400
+ Useful for critical-path reads where staleness is unacceptable::
401
+
402
+ fresh_row = row.no_cache()
403
+ balance = fresh_row["balance"] # always from DB
404
+ """
405
+ return Row(self.table, self.pk, no_cache=True)
406
+
369
407
  def notBlank(self, key, failobj=None):
370
408
  """
371
409
  Returns the value if it is not blank, else failobj.
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import re
2
3
  import warnings
3
4
  import sqlparse
@@ -12,6 +13,8 @@ from velocity.db.core.decorators import (
12
13
  reset_id_on_dup_key,
13
14
  )
14
15
 
16
+ _ddl_logger = logging.getLogger(__name__)
17
+
15
18
 
16
19
  class Query:
17
20
  """
@@ -252,6 +255,7 @@ class Table:
252
255
  )
253
256
  if kwds.get("sql_only", False):
254
257
  return sql, vals
258
+ _ddl_logger.warning("DDL CREATE INDEX on %s columns=%s unique=%s", self.name, columns, unique)
255
259
  self.tx.execute(sql, vals, cursor=self.cursor())
256
260
 
257
261
  def create_indexes(self, indexes, **kwds):
@@ -317,6 +321,7 @@ class Table:
317
321
  sql, vals = self.sql.drop_index(self.name, columns)
318
322
  if kwds.get("sql_only", False):
319
323
  return sql, vals
324
+ _ddl_logger.warning("DDL DROP INDEX on %s columns=%s", self.name, columns)
320
325
  self.tx.execute(sql, vals, cursor=self.cursor())
321
326
 
322
327
  @return_default(None)
@@ -325,6 +330,7 @@ class Table:
325
330
  Drops a column from this table.
326
331
  """
327
332
  sql, vals = self.sql.drop_column(self.name, column)
333
+ _ddl_logger.warning("DDL DROP COLUMN %s on %s", column, self.name)
328
334
  self.tx.execute(sql, vals, cursor=self.cursor())
329
335
 
330
336
  def create(self, columns=None, drop=False):
@@ -334,6 +340,7 @@ class Table:
334
340
  """
335
341
  columns = columns or {}
336
342
  sql, vals = self.sql.create_table(self.name, columns, drop)
343
+ _ddl_logger.warning("DDL CREATE TABLE %s columns=%s drop=%s", self.name, list(columns.keys()), drop)
337
344
  self.tx.execute(sql, vals, cursor=self.cursor())
338
345
 
339
346
  def drop(self):
@@ -341,6 +348,7 @@ class Table:
341
348
  Drops this table if it exists.
342
349
  """
343
350
  sql, vals = self.sql.drop_table(self.name)
351
+ _ddl_logger.warning("DDL DROP TABLE %s", self.name)
344
352
  self.tx.execute(sql, vals, cursor=self.cursor())
345
353
 
346
354
  def exists(self):
@@ -400,13 +408,13 @@ class Table:
400
408
  """
401
409
  return Column(self, name)
402
410
 
403
- def row(self, key=None, lock=None):
411
+ def row(self, key=None, lock=None, cache_ttl=None, no_cache=False):
404
412
  """
405
413
  Retrieves a Row instance for the given primary key or conditions dict. If `key` is None, returns a new row.
406
414
  """
407
415
  if key is None:
408
416
  return self.new(lock=lock)
409
- return Row(self, key, lock=lock)
417
+ return Row(self, key, lock=lock, cache_ttl=cache_ttl, no_cache=no_cache)
410
418
 
411
419
  def dict(self, key):
412
420
  """
@@ -415,14 +423,14 @@ class Table:
415
423
  r = self.find(key)
416
424
  return r.to_dict() if r else {}
417
425
 
418
- def rows(self, where=None, orderby=None, qty=None, lock=None, skip_locked=None):
426
+ def rows(self, where=None, orderby=None, qty=None, lock=None, skip_locked=None, cache_ttl=None, no_cache=False):
419
427
  """
420
428
  Generator that yields Row objects matching `where`, up to `qty`.
421
429
  """
422
430
  for key in self.ids(
423
431
  where=where, orderby=orderby, qty=qty, lock=lock, skip_locked=skip_locked
424
432
  ):
425
- yield Row(self, key, lock=lock)
433
+ yield Row(self, key, lock=lock, cache_ttl=cache_ttl, no_cache=no_cache)
426
434
 
427
435
  def ids(
428
436
  self,
@@ -625,6 +633,7 @@ class Table:
625
633
  )
626
634
  if kwds.get("sql_only", False):
627
635
  return sql, vals
636
+ _ddl_logger.warning("DDL CREATE FOREIGN KEY on %s columns=%s ref=%s(%s)", self.name, columns, key_to_table, key_to_columns)
628
637
  return self.tx.execute(sql, vals, cursor=self.cursor())
629
638
 
630
639
  def drop_foreign_key(self, columns, key_to_table, key_to_columns="sys_id", **kwds):
@@ -636,6 +645,7 @@ class Table:
636
645
  )
637
646
  if kwds.get("sql_only", False):
638
647
  return sql, vals
648
+ _ddl_logger.warning("DDL DROP FOREIGN KEY on %s columns=%s ref=%s(%s)", self.name, columns, key_to_table, key_to_columns)
639
649
  return self.tx.execute(sql, vals, cursor=self.cursor())
640
650
 
641
651
  def rename(self, name, **kwds):
@@ -645,6 +655,7 @@ class Table:
645
655
  sql, vals = self.sql.rename_table(self.name, name)
646
656
  if kwds.get("sql_only", False):
647
657
  return sql, vals
658
+ _ddl_logger.warning("DDL RENAME TABLE %s to %s", self.name, name)
648
659
  self.tx.execute(sql, vals, cursor=self.cursor())
649
660
  self.name = name
650
661
 
@@ -784,6 +795,7 @@ class Table:
784
795
  return statements[0]
785
796
  return statements
786
797
 
798
+ _ddl_logger.warning("DDL ALTER TABLE %s columns=%s mode=%s", self.name, list(columns.keys()), mode)
787
799
  for sql, vals in statements:
788
800
  if not sql:
789
801
  continue
@@ -808,6 +820,7 @@ class Table:
808
820
  )
809
821
  if kwds.get("sql_only", False):
810
822
  return sql, vals
823
+ _ddl_logger.warning("DDL ALTER COLUMN TYPE on %s column=%s", self.name, column)
811
824
  self.tx.execute(sql, vals, cursor=self.cursor())
812
825
 
813
826
  @create_missing
@@ -1069,9 +1082,10 @@ class Table:
1069
1082
  """
1070
1083
  Returns the sum of the given column across rows matching `where`.
1071
1084
  """
1085
+ qcol = self.sql.quote_identifier(column)
1072
1086
  sql, vals = self.sql.select(
1073
1087
  self.tx,
1074
- columns=f"coalesce(sum(coalesce({column},0)),0)",
1088
+ columns=f"coalesce(sum(coalesce({qcol},0)),0)",
1075
1089
  table=self.name,
1076
1090
  where=where,
1077
1091
  )
@@ -1322,6 +1336,7 @@ class Table:
1322
1336
  )
1323
1337
  if kwds.get("sql_only", False):
1324
1338
  return sql, vals
1339
+ _ddl_logger.warning("DDL CREATE VIEW %s temp=%s", name, temp)
1325
1340
  return self.tx.execute(sql, vals)
1326
1341
 
1327
1342
  def drop_view(self, name, silent=True, **kwds):
@@ -1331,6 +1346,7 @@ class Table:
1331
1346
  sql, vals = self.sql.drop_view(name=name, silent=silent)
1332
1347
  if kwds.get("sql_only", False):
1333
1348
  return sql, vals
1349
+ _ddl_logger.warning("DDL DROP VIEW %s", name)
1334
1350
  return self.tx.execute(sql, vals)
1335
1351
 
1336
1352
  def alter_trigger(self, name="USER", state="ENABLE", **kwds):
@@ -1340,6 +1356,7 @@ class Table:
1340
1356
  sql, vals = self.sql.alter_trigger(table=self.name, state=state, name=name)
1341
1357
  if kwds.get("sql_only", False):
1342
1358
  return sql, vals
1359
+ _ddl_logger.warning("DDL ALTER TRIGGER %s on %s state=%s", name, self.name, state)
1343
1360
  return self.tx.execute(sql, vals)
1344
1361
 
1345
1362
  def rename_column(self, orig, new, **kwds):
@@ -1349,6 +1366,7 @@ class Table:
1349
1366
  sql, vals = self.sql.rename_column(table=self.name, orig=orig, new=new)
1350
1367
  if kwds.get("sql_only", False):
1351
1368
  return sql, vals
1369
+ _ddl_logger.warning("DDL RENAME COLUMN on %s %s -> %s", self.name, orig, new)
1352
1370
  return self.tx.execute(sql, vals)
1353
1371
 
1354
1372
  def set_sequence(self, next_value=1000, **kwds):
@@ -1358,6 +1376,7 @@ class Table:
1358
1376
  sql, vals = self.sql.set_sequence(table=self.name, next_value=next_value)
1359
1377
  if kwds.get("sql_only", False):
1360
1378
  return sql, vals
1379
+ _ddl_logger.warning("DDL SET SEQUENCE on %s next_value=%s", self.name, next_value)
1361
1380
  return self.tx.execute(sql, vals).scalar()
1362
1381
 
1363
1382
  def get_sequence(self, **kwds):
@@ -1396,8 +1415,9 @@ class Table:
1396
1415
  """
1397
1416
  Returns the MAX() of the specified column.
1398
1417
  """
1418
+ qcol = self.sql.quote_identifier(column)
1399
1419
  sql, vals = self.sql.select(
1400
- self.tx, columns=f"max({column})", table=self.name, where=where
1420
+ self.tx, columns=f"max({qcol})", table=self.name, where=where
1401
1421
  )
1402
1422
  if kwds.get("sql_only", False):
1403
1423
  return sql, vals
@@ -1408,8 +1428,9 @@ class Table:
1408
1428
  """
1409
1429
  Returns the MIN() of the specified column.
1410
1430
  """
1431
+ qcol = self.sql.quote_identifier(column)
1411
1432
  sql, vals = self.sql.select(
1412
- self.tx, columns=f"min({column})", table=self.name, where=where
1433
+ self.tx, columns=f"min({qcol})", table=self.name, where=where
1413
1434
  )
1414
1435
  if kwds.get("sql_only", False):
1415
1436
  return sql, vals
@@ -38,6 +38,16 @@ class BaseSQLDialect(ABC):
38
38
  DatabaseObjectExistsErrorCodes: List[str] = []
39
39
  DataIntegrityErrorCodes: List[str] = []
40
40
 
41
+ @classmethod
42
+ def quote_identifier(cls, name: str) -> str:
43
+ """Always-quote a single SQL identifier to prevent injection.
44
+
45
+ Uses standard SQL double-quoting. Dialect subclasses override for
46
+ MySQL (backticks) and SQL Server (brackets).
47
+ """
48
+ escaped = name.replace('"', '""')
49
+ return f'"{escaped}"'
50
+
41
51
  @classmethod
42
52
  @abstractmethod
43
53
  def get_error(cls, e: Exception) -> Optional[str]:
@@ -48,6 +48,11 @@ class SQL(BaseSQLDialect):
48
48
  type_column_identifier = "DATA_TYPE"
49
49
  is_nullable = "IS_NULLABLE"
50
50
 
51
+ @classmethod
52
+ def quote_identifier(cls, name: str) -> str:
53
+ escaped = name.replace('`', '``')
54
+ return f'`{escaped}`'
55
+
51
56
  default_schema = ""
52
57
 
53
58
  ApplicationErrorCodes = []
@@ -48,6 +48,11 @@ class SQL(BaseSQLDialect):
48
48
  type_column_identifier = "DATA_TYPE"
49
49
  is_nullable = "IS_NULLABLE"
50
50
 
51
+ @classmethod
52
+ def quote_identifier(cls, name: str) -> str:
53
+ escaped = name.replace(']', ']]')
54
+ return f'[{escaped}]'
55
+
51
56
  default_schema = "dbo"
52
57
 
53
58
  # SQL Server error numbers
@@ -1,13 +1,15 @@
1
+ from __future__ import annotations
2
+
1
3
  from datetime import date, datetime, time, timedelta
2
4
  from decimal import Decimal
3
5
  from io import BytesIO
4
- from typing import Dict, List
6
+ from typing import TYPE_CHECKING, Dict, List
5
7
  import base64
6
8
  import re
7
9
 
8
- import openpyxl
9
- from openpyxl.styles import Alignment, Border, Font, NamedStyle, Side
10
- from openpyxl.utils import get_column_letter
10
+ if TYPE_CHECKING: # pragma: no cover
11
+ import openpyxl
12
+ from openpyxl.styles import NamedStyle
11
13
 
12
14
 
13
15
  NUMBER_FORMAT_RE = re.compile(r"[#0?][#0?,]*(?:\.[#0?]+)?")
@@ -107,6 +109,8 @@ def _display_value(cell) -> str:
107
109
 
108
110
  def autosize_columns(ws, fixed: Dict[str, float] | None = None):
109
111
  """Autosize columns in the worksheet based on content length."""
112
+ from openpyxl.utils import get_column_letter
113
+
110
114
  fixed = fixed or {}
111
115
  for col in ws.columns:
112
116
  max_length = 0
@@ -135,6 +139,9 @@ def create_spreadsheet(
135
139
  auto_size: bool = True,
136
140
  ):
137
141
  """Create an Excel spreadsheet with specified headers, rows, and styles."""
142
+ import openpyxl
143
+ from openpyxl.styles import Alignment, Border, Font, NamedStyle, Side
144
+
138
145
  styles = styles or {}
139
146
  merge = merge or []
140
147
  formats = formats or {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.1
3
+ Version: 0.1.3
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
@@ -19,12 +19,16 @@ Classifier: Operating System :: OS Independent
19
19
  Requires-Python: >=3.9
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
- Requires-Dist: boto3>=1.35.0
23
- Requires-Dist: requests>=2.32.0
24
- Requires-Dist: jinja2>=3.1.0
25
- Requires-Dist: xlrd>=2.0.1
26
- Requires-Dist: openpyxl>=3.1.0
27
22
  Requires-Dist: sqlparse>=0.5.0
23
+ Provides-Extra: aws
24
+ Requires-Dist: boto3>=1.35.0; extra == "aws"
25
+ Requires-Dist: requests>=2.32.0; extra == "aws"
26
+ Provides-Extra: excel
27
+ Requires-Dist: openpyxl>=3.1.0; extra == "excel"
28
+ Provides-Extra: templates
29
+ Requires-Dist: jinja2>=3.1.0; extra == "templates"
30
+ Provides-Extra: http
31
+ Requires-Dist: requests>=2.32.0; extra == "http"
28
32
  Provides-Extra: mysql
29
33
  Requires-Dist: mysql-connector-python>=9.0.0; extra == "mysql"
30
34
  Provides-Extra: sqlserver
@@ -34,6 +38,8 @@ Requires-Dist: psycopg[binary]>=3.2.0; extra == "postgres"
34
38
  Provides-Extra: payment
35
39
  Requires-Dist: stripe>=12.0.0; extra == "payment"
36
40
  Requires-Dist: braintree>=4.30.0; extra == "payment"
41
+ Provides-Extra: all
42
+ Requires-Dist: velocity-python[aws,excel,http,payment,postgres,templates]; extra == "all"
37
43
  Provides-Extra: dev
38
44
  Requires-Dist: pytest>=8.0.0; extra == "dev"
39
45
  Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
@@ -168,6 +168,8 @@ tests/test_prepared_statements.py
168
168
  tests/test_psycopg3_upgrade.py
169
169
  tests/test_query_cache.py
170
170
  tests/test_row_batch_update.py
171
+ tests/test_row_cache_staleness.py
172
+ tests/test_security_hardening.py
171
173
  tests/test_sqs_per_record_transactions.py
172
174
  tests/test_sys_modified_count_postgres_demo.py
173
175
  tests/test_table_alter.py
@@ -1,9 +1,11 @@
1
+ sqlparse>=0.5.0
2
+
3
+ [all]
4
+ velocity-python[aws,excel,http,payment,postgres,templates]
5
+
6
+ [aws]
1
7
  boto3>=1.35.0
2
8
  requests>=2.32.0
3
- jinja2>=3.1.0
4
- xlrd>=2.0.1
5
- openpyxl>=3.1.0
6
- sqlparse>=0.5.0
7
9
 
8
10
  [dev]
9
11
  pytest>=8.0.0
@@ -17,6 +19,12 @@ pre-commit>=4.0.0
17
19
  sphinx>=8.0.0
18
20
  sphinx-rtd-theme>=3.0.0
19
21
 
22
+ [excel]
23
+ openpyxl>=3.1.0
24
+
25
+ [http]
26
+ requests>=2.32.0
27
+
20
28
  [mysql]
21
29
  mysql-connector-python>=9.0.0
22
30
 
@@ -30,6 +38,9 @@ psycopg[binary]>=3.2.0
30
38
  [sqlserver]
31
39
  python-tds>=1.15.0
32
40
 
41
+ [templates]
42
+ jinja2>=3.1.0
43
+
33
44
  [test]
34
45
  pytest>=8.0.0
35
46
  pytest-cov>=6.0.0
@@ -26,6 +26,9 @@ def _make_row(cache=None, pk=None):
26
26
  object.__setattr__(row, "_column_set", None)
27
27
  object.__setattr__(row, "_batching", False)
28
28
  object.__setattr__(row, "_pending", {})
29
+ object.__setattr__(row, "_cache_ttl", None)
30
+ object.__setattr__(row, "_cache_time", None)
31
+ object.__setattr__(row, "_no_cache", False)
29
32
  return row
30
33
 
31
34