velocity-python 0.1.126__tar.gz → 0.1.127__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 (312) hide show
  1. {velocity_python-0.1.126 → velocity_python-0.1.127}/PKG-INFO +1 -1
  2. {velocity_python-0.1.126 → velocity_python-0.1.127}/pyproject.toml +1 -1
  3. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/maintenance.py +11 -6
  5. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/postgres/__init__.py +5 -0
  6. velocity_python-0.1.127/src/velocity/db/tunnel.py +49 -0
  7. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity_python.egg-info/PKG-INFO +1 -1
  8. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity_python.egg-info/SOURCES.txt +2 -0
  9. velocity_python-0.1.127/tests/test_db_tunnel.py +127 -0
  10. {velocity_python-0.1.126 → velocity_python-0.1.127}/LICENSE +0 -0
  11. {velocity_python-0.1.126 → velocity_python-0.1.127}/README.md +0 -0
  12. {velocity_python-0.1.126 → velocity_python-0.1.127}/setup.cfg +0 -0
  13. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/__init__.py +0 -0
  14. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/amplify.py +0 -0
  15. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/amplify_build.py +0 -0
  16. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/assets/__init__.py +0 -0
  17. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/assets/backfill.py +0 -0
  18. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/assets/indexing.py +0 -0
  19. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/assets/references.py +0 -0
  20. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/assets/service.py +0 -0
  21. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/assets/usage_index.py +0 -0
  22. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/blobs.py +0 -0
  23. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/capabilities/__init__.py +0 -0
  24. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/capabilities/base.py +0 -0
  25. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/capabilities/dispatch.py +0 -0
  26. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/capabilities/infra.py +0 -0
  27. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/capabilities/jobs.py +0 -0
  28. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/capabilities/provisioning.py +0 -0
  29. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/capabilities/retention.py +0 -0
  30. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/capabilities/security.py +0 -0
  31. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/capabilities/single_session.py +0 -0
  32. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/deploy/__init__.py +0 -0
  33. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/deploy/config.py +0 -0
  34. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/deploy/operations.py +0 -0
  35. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/deploy/policies.py +0 -0
  36. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/deploy/reconcile.py +0 -0
  37. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/deploy/targets.py +0 -0
  38. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/dirty_pipeline.py +0 -0
  39. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/email.py +0 -0
  40. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/facets.py +0 -0
  41. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/feature_flags.py +0 -0
  42. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/__init__.py +0 -0
  43. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/base_handler.py +0 -0
  44. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/context.py +0 -0
  45. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/context_factory.py +0 -0
  46. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/exceptions.py +0 -0
  47. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  48. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/masquerade.py +0 -0
  49. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  50. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  51. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/mixins/job_handler.py +0 -0
  52. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/mixins/session_control.py +0 -0
  53. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  54. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/perf.py +0 -0
  55. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/response.py +0 -0
  56. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  57. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/job_lifecycle.py +0 -0
  58. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/job_results.py +0 -0
  59. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/mirror.py +0 -0
  60. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/ops.py +0 -0
  61. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/s3.py +0 -0
  62. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/sms.py +0 -0
  63. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/ssm_config.py +0 -0
  64. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/tests/__init__.py +0 -0
  65. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/tests/test_amplify_build_policy_size.py +0 -0
  66. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/tests/test_amplify_build_shim.py +0 -0
  67. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  68. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/tests/test_deploy_policies.py +0 -0
  69. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/tests/test_deploy_reconcile.py +0 -0
  70. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/tests/test_email.py +0 -0
  71. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  72. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/tests/test_response.py +0 -0
  73. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/aws/tests/test_sms.py +0 -0
  74. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/cache.py +0 -0
  75. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/__init__.py +0 -0
  76. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/__init__.py +0 -0
  77. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/async_support.py +0 -0
  78. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/column.py +0 -0
  79. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/database.py +0 -0
  80. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/decorators.py +0 -0
  81. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/engine.py +0 -0
  82. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/jsonproxy.py +0 -0
  83. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/lazy_engine.py +0 -0
  84. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/result.py +0 -0
  85. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/row.py +0 -0
  86. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/sequence.py +0 -0
  87. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/table.py +0 -0
  88. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/transaction.py +0 -0
  89. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/core/view.py +0 -0
  90. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/exceptions.py +0 -0
  91. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/migrations.py +0 -0
  92. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/__init__.py +0 -0
  93. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/base/__init__.py +0 -0
  94. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/base/initializer.py +0 -0
  95. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/base/operators.py +0 -0
  96. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/base/sql.py +0 -0
  97. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/base/types.py +0 -0
  98. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/mysql/__init__.py +0 -0
  99. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/mysql/operators.py +0 -0
  100. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/mysql/reserved.py +0 -0
  101. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/mysql/sql.py +0 -0
  102. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/mysql/types.py +0 -0
  103. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/postgres/operators.py +0 -0
  104. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/postgres/reserved.py +0 -0
  105. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/postgres/sql.py +0 -0
  106. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/postgres/types.py +0 -0
  107. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  108. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/sqlite/operators.py +0 -0
  109. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  110. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/sqlite/sql.py +0 -0
  111. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/sqlite/types.py +0 -0
  112. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  113. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  114. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  115. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  116. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/sqlserver/types.py +0 -0
  117. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/servers/tablehelper.py +0 -0
  118. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/__init__.py +0 -0
  119. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/common_db_test.py +0 -0
  120. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/__init__.py +0 -0
  121. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/common.py +0 -0
  122. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/conftest.py +0 -0
  123. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_column.py +0 -0
  124. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  125. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_database.py +0 -0
  126. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  127. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  128. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  129. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_result.py +0 -0
  130. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_row.py +0 -0
  131. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  132. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  133. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  134. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  135. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  136. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_table.py +0 -0
  137. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  138. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  139. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/sql/__init__.py +0 -0
  140. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/sql/common.py +0 -0
  141. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  142. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  143. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  144. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_db_utils.py +0 -0
  145. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_postgres.py +0 -0
  146. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  147. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  148. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_result_caching.py +0 -0
  149. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  150. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  151. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  152. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  153. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_sql_builder.py +0 -0
  154. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_tablehelper.py +0 -0
  155. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/tests/test_view_helper.py +0 -0
  156. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/db/utils.py +0 -0
  157. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/logging.py +0 -0
  158. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/__init__.py +0 -0
  159. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/conv/__init__.py +0 -0
  160. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/conv/iconv.py +0 -0
  161. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/conv/oconv.py +0 -0
  162. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/db.py +0 -0
  163. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/export.py +0 -0
  164. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/format.py +0 -0
  165. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/mail.py +0 -0
  166. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/merge.py +0 -0
  167. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/pdf.py +0 -0
  168. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tests/__init__.py +0 -0
  169. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tests/test_db.py +0 -0
  170. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tests/test_fix.py +0 -0
  171. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tests/test_format.py +0 -0
  172. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tests/test_iconv.py +0 -0
  173. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tests/test_mail_send.py +0 -0
  174. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tests/test_merge.py +0 -0
  175. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tests/test_oconv.py +0 -0
  176. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tests/test_original_error.py +0 -0
  177. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tests/test_timer.py +0 -0
  178. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/timer.py +0 -0
  179. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/misc/tools.py +0 -0
  180. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/__init__.py +0 -0
  181. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/core/__init__.py +0 -0
  182. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/core/capabilities.py +0 -0
  183. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/core/store.py +0 -0
  184. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/core/where.py +0 -0
  185. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/exceptions.py +0 -0
  186. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/__init__.py +0 -0
  187. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/base/__init__.py +0 -0
  188. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/cassandra/__init__.py +0 -0
  189. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/cassandra/translation.py +0 -0
  190. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/couchbase/__init__.py +0 -0
  191. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/couchbase/translation.py +0 -0
  192. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/dynamodb/__init__.py +0 -0
  193. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/dynamodb/translation.py +0 -0
  194. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/elasticsearch/__init__.py +0 -0
  195. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/elasticsearch/translation.py +0 -0
  196. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/memory/__init__.py +0 -0
  197. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/mongodb/__init__.py +0 -0
  198. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/mongodb/translation.py +0 -0
  199. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/nosql/servers/redis/__init__.py +0 -0
  200. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/payment/__init__.py +0 -0
  201. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/payment/authorizenet_adapter.py +0 -0
  202. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/payment/authorizenet_mirror.py +0 -0
  203. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/payment/base_adapter.py +0 -0
  204. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/payment/braintree_adapter.py +0 -0
  205. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/payment/braintree_mirror.py +0 -0
  206. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/payment/charge_rules.py +0 -0
  207. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/payment/stripe_adapter.py +0 -0
  208. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity/payment/stripe_mirror.py +0 -0
  209. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  210. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity_python.egg-info/entry_points.txt +0 -0
  211. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity_python.egg-info/requires.txt +0 -0
  212. {velocity_python-0.1.126 → velocity_python-0.1.127}/src/velocity_python.egg-info/top_level.txt +0 -0
  213. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_access_error_403.py +0 -0
  214. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_activity_tracking_aborted_tx.py +0 -0
  215. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_amplify_build.py +0 -0
  216. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_asset_indexing.py +0 -0
  217. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_asset_references.py +0 -0
  218. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_assets_service.py +0 -0
  219. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_async_support.py +0 -0
  220. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_batch_operations.py +0 -0
  221. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_blob_store.py +0 -0
  222. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_capabilities.py +0 -0
  223. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_capability_catalog.py +0 -0
  224. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_cognito_user_cache.py +0 -0
  225. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_column_tx_arg.py +0 -0
  226. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_concurrency_safety.py +0 -0
  227. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_connection_pool.py +0 -0
  228. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_connection_resilience.py +0 -0
  229. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_context_flags_loader.py +0 -0
  230. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_context_job_descriptions.py +0 -0
  231. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_data_service_query_cap.py +0 -0
  232. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_data_service_renumber.py +0 -0
  233. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_data_service_save_as.py +0 -0
  234. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_db_credentials_ssm_cascade.py +0 -0
  235. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_db_maintenance.py +0 -0
  236. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_decorators.py +0 -0
  237. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_delete_hook_fk_blocked.py +0 -0
  238. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_dirty_pipeline_fast_path.py +0 -0
  239. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_email_processing.py +0 -0
  240. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_enqueue_metric_namespace.py +0 -0
  241. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_enqueue_send_failures.py +0 -0
  242. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_facets.py +0 -0
  243. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_feature_flags.py +0 -0
  244. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_get_cognito_user_provider.py +0 -0
  245. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_http_handler_rollback.py +0 -0
  246. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_iconv_money_to_cents.py +0 -0
  247. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_identifier_injection_guard.py +0 -0
  248. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_job_handler.py +0 -0
  249. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_job_lifecycle.py +0 -0
  250. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_job_results.py +0 -0
  251. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_json_columns.py +0 -0
  252. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_jsonb_dict_adapter.py +0 -0
  253. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_lambda_handler.py +0 -0
  254. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_lambda_handler_auth.py +0 -0
  255. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_lambda_handler_masquerade.py +0 -0
  256. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_lambda_handler_user_cache.py +0 -0
  257. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_lazy_engine.py +0 -0
  258. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_masquerade_grant.py +0 -0
  259. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_mirror.py +0 -0
  260. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_mixins_import.py +0 -0
  261. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_n_plus_one.py +0 -0
  262. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_nosql_dynamodb.py +0 -0
  263. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_nosql_memory.py +0 -0
  264. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_nosql_mongodb.py +0 -0
  265. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_nosql_redis.py +0 -0
  266. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_nosql_translation.py +0 -0
  267. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_nosql_where.py +0 -0
  268. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_observability.py +0 -0
  269. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_on_commit.py +0 -0
  270. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_ops.py +0 -0
  271. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_payment_authorizenet_adapter.py +0 -0
  272. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_payment_braintree_adapter.py +0 -0
  273. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_payment_braintree_mirror.py +0 -0
  274. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_payment_mirror_empty_table.py +0 -0
  275. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_payment_profile_sorting.py +0 -0
  276. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_payment_stripe_adapter.py +0 -0
  277. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_pdf.py +0 -0
  278. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_prepared_statements.py +0 -0
  279. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_psycopg3_upgrade.py +0 -0
  280. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_query_cache.py +0 -0
  281. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_query_missing_table_unlocked.py +0 -0
  282. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_read_cache.py +0 -0
  283. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_response.py +0 -0
  284. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_restricted_direct_tables.py +0 -0
  285. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_result_returning.py +0 -0
  286. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_retry_side_effect_guard.py +0 -0
  287. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_return_default_safety.py +0 -0
  288. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_row_batch_update.py +0 -0
  289. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_row_cache_staleness.py +0 -0
  290. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_s3_bucket_config.py +0 -0
  291. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_schema_migrations.py +0 -0
  292. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_security_capability.py +0 -0
  293. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_security_hardening.py +0 -0
  294. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_server_cursor.py +0 -0
  295. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_session_control.py +0 -0
  296. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_single_autocommit_safety.py +0 -0
  297. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_single_session_capability.py +0 -0
  298. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_spreadsheet_functions.py +0 -0
  299. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_sqlite_backend.py +0 -0
  300. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_sqs_per_record_transactions.py +0 -0
  301. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_ssm_config.py +0 -0
  302. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_store_user_data.py +0 -0
  303. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  304. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_table_alter.py +0 -0
  305. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_table_find_cache.py +0 -0
  306. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_table_purge.py +0 -0
  307. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_transaction_class_wrapping.py +0 -0
  308. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_transaction_commit_and_ownership.py +0 -0
  309. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_transaction_edge_cases.py +0 -0
  310. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_where_clause_validation.py +0 -0
  311. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_write_hook_create_flow.py +0 -0
  312. {velocity_python-0.1.126 → velocity_python-0.1.127}/tests/test_write_hook_empty_strings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.126
3
+ Version: 0.1.127
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.126"
7
+ version = "0.1.127"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.1.126"
1
+ __version__ = version = "0.1.127"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -33,6 +33,7 @@ import os
33
33
  from typing import Iterable, List, Optional
34
34
 
35
35
  from velocity.db.servers import postgres
36
+ from velocity.db.tunnel import apply_db_tunnel
36
37
 
37
38
  SNAPSHOT_INFIX = "-snapshot-"
38
39
  TS_FORMAT = "%Y%m%d-%H%M%S" # sortable: lexical order == chronological order
@@ -136,12 +137,16 @@ class DatabaseMaintenance:
136
137
  return _run()
137
138
 
138
139
  def _conn_params(self) -> dict:
139
- return {
140
- "host": self._config.get("host") or os.environ.get("DBHost"),
141
- "port": self._config.get("port") or os.environ.get("DBPort"),
142
- "user": self._config.get("user") or os.environ.get("DBUser"),
143
- "password": self._config.get("password") or os.environ.get("DBPassword"),
144
- }
140
+ # the pg_dump/pg_restore/psql runs follow DB_TUNNEL like the engine does
141
+ return apply_db_tunnel(
142
+ {
143
+ "host": self._config.get("host") or os.environ.get("DBHost"),
144
+ "port": self._config.get("port") or os.environ.get("DBPort"),
145
+ "user": self._config.get("user") or os.environ.get("DBUser"),
146
+ "password": self._config.get("password")
147
+ or os.environ.get("DBPassword"),
148
+ }
149
+ )
145
150
 
146
151
  # ------------------------------------------------------------------ guards
147
152
  def _guard(self, db: str, action: str) -> None:
@@ -2,6 +2,7 @@ import os
2
2
  from ..base.initializer import BaseInitializer
3
3
  from velocity.db.core import engine
4
4
  from velocity.aws.ssm_config import getenv as _getenv
5
+ from velocity.db.tunnel import apply_db_tunnel
5
6
 
6
7
 
7
8
  # Default TCP keepalive settings for PostgreSQL connections.
@@ -60,6 +61,10 @@ class PostgreSQLInitializer(BaseInitializer):
60
61
  base_config, config, **kwargs
61
62
  )
62
63
 
64
+ # DB_TUNNEL (operator laptops): every connection goes to the local end
65
+ # of a tunnel to a private server. See velocity.db.tunnel.
66
+ apply_db_tunnel(final_config)
67
+
63
68
  # Apply TCP keepalive defaults (user config takes precedence).
64
69
  for key, default_val in _DEFAULT_KEEPALIVE.items():
65
70
  final_config.setdefault(key, default_val)
@@ -0,0 +1,49 @@
1
+ """``DB_TUNNEL``: reach a private database through a local tunnel end.
2
+
3
+ An operator on a laptop cannot reach a database that has no public address;
4
+ a tunnel (SSH ``-L``, an SSM port forward, ...) exposes it on a local port.
5
+ Setting ``DB_TUNNEL=127.0.0.1:15433`` points every velocity PostgreSQL
6
+ connection in the process -- engines from ``postgres.initialize()`` and the
7
+ ``pg_dump``/``pg_restore``/``psql`` runs of ``DatabaseMaintenance`` -- at that
8
+ local end instead of the configured host. Host and port only: the database
9
+ name and credentials still come from wherever they were configured.
10
+
11
+ A tunnel forwards to ONE server, so the override applies to every connection,
12
+ including an explicit ``config={"host": ...}``: under a tunnel, no other host
13
+ is reachable anyway. ``sslmode=require`` still encrypts end to end;
14
+ ``verify-full`` would reject the local name, so don't combine the two.
15
+
16
+ Read from ``os.environ`` only (never SSM): it describes this process's
17
+ network path, not the application's configuration.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import os
23
+ from typing import Mapping, Optional, Tuple
24
+
25
+ DB_TUNNEL_VAR = "DB_TUNNEL"
26
+
27
+
28
+ def db_tunnel(environ: Optional[Mapping[str, str]] = None) -> Optional[Tuple[str, str]]:
29
+ """``(host, port)`` from ``DB_TUNNEL``, or ``None`` when it is unset/blank.
30
+
31
+ Raises ``ValueError`` for anything that is not ``host:port`` -- a typo must
32
+ not silently fall back to the unreachable real host."""
33
+ value = (os.environ if environ is None else environ).get(DB_TUNNEL_VAR, "")
34
+ value = (value or "").strip()
35
+ if not value:
36
+ return None
37
+ host, _, port = value.rpartition(":")
38
+ if not host or not port.isdigit():
39
+ raise ValueError(f"{DB_TUNNEL_VAR} must be host:port, got {value!r}")
40
+ return host, port
41
+
42
+
43
+ def apply_db_tunnel(config: dict, environ: Optional[Mapping[str, str]] = None) -> dict:
44
+ """Replace ``config['host']``/``config['port']`` with the tunnel's local end
45
+ when ``DB_TUNNEL`` is set. Mutates and returns ``config``."""
46
+ tunnel = db_tunnel(environ)
47
+ if tunnel:
48
+ config["host"], config["port"] = tunnel
49
+ return config
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.126
3
+ Version: 0.1.127
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
@@ -69,6 +69,7 @@ src/velocity/db/__init__.py
69
69
  src/velocity/db/exceptions.py
70
70
  src/velocity/db/maintenance.py
71
71
  src/velocity/db/migrations.py
72
+ src/velocity/db/tunnel.py
72
73
  src/velocity/db/utils.py
73
74
  src/velocity/db/core/__init__.py
74
75
  src/velocity/db/core/async_support.py
@@ -229,6 +230,7 @@ tests/test_data_service_renumber.py
229
230
  tests/test_data_service_save_as.py
230
231
  tests/test_db_credentials_ssm_cascade.py
231
232
  tests/test_db_maintenance.py
233
+ tests/test_db_tunnel.py
232
234
  tests/test_decorators.py
233
235
  tests/test_delete_hook_fk_blocked.py
234
236
  tests/test_dirty_pipeline_fast_path.py
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env python3
2
+ """``DB_TUNNEL=host:port`` points every PostgreSQL connection in the process at
3
+ a local tunnel end: engines from ``postgres.initialize()`` and the
4
+ ``pg_dump``/``pg_restore`` runs of ``DatabaseMaintenance``. Host and port only;
5
+ a malformed value fails loudly instead of falling back to the real host."""
6
+
7
+ import os
8
+ import unittest
9
+ from unittest.mock import MagicMock, patch
10
+
11
+ from velocity.db import maintenance
12
+ from velocity.db.servers import postgres as postgres_server
13
+ from velocity.db.tunnel import DB_TUNNEL_VAR, apply_db_tunnel, db_tunnel
14
+
15
+ FAKE = {
16
+ "DBDatabase": "app_db",
17
+ "DBHost": "private.rds.example.com",
18
+ "DBPort": "5433",
19
+ "DBUser": "app_user",
20
+ "DBPassword": "app_pass",
21
+ }
22
+
23
+
24
+ def _no_tunnel():
25
+ env = dict(os.environ)
26
+ env.pop(DB_TUNNEL_VAR, None)
27
+ return patch.dict(os.environ, env, clear=True)
28
+
29
+
30
+ class TestParse(unittest.TestCase):
31
+ def test_unset_or_blank_is_none(self):
32
+ self.assertIsNone(db_tunnel({}))
33
+ self.assertIsNone(db_tunnel({DB_TUNNEL_VAR: " "}))
34
+
35
+ def test_host_and_port(self):
36
+ self.assertEqual(
37
+ db_tunnel({DB_TUNNEL_VAR: " 127.0.0.1:15433 "}), ("127.0.0.1", "15433")
38
+ )
39
+
40
+ def test_malformed_raises(self):
41
+ for bad in ("localhost", ":15433", "127.0.0.1:", "127.0.0.1:abc"):
42
+ with self.subTest(bad=bad), self.assertRaises(ValueError):
43
+ db_tunnel({DB_TUNNEL_VAR: bad})
44
+
45
+ def test_apply_replaces_host_and_port_only(self):
46
+ config = {"host": "h", "port": "1", "user": "u", "dbname": "d"}
47
+ apply_db_tunnel(config, {DB_TUNNEL_VAR: "127.0.0.1:15433"})
48
+ self.assertEqual(
49
+ config,
50
+ {"host": "127.0.0.1", "port": "15433", "user": "u", "dbname": "d"},
51
+ )
52
+
53
+ def test_apply_without_tunnel_leaves_config(self):
54
+ config = {"host": "h", "port": "1"}
55
+ self.assertEqual(apply_db_tunnel(config, {}), {"host": "h", "port": "1"})
56
+
57
+
58
+ class TestPostgresInitialize(unittest.TestCase):
59
+ def _initialize(self, config=None):
60
+ captured = {}
61
+
62
+ def fake_engine(driver, cfg, sql, **kwargs):
63
+ captured.update(cfg)
64
+ return MagicMock()
65
+
66
+ with (
67
+ patch.object(postgres_server, "_getenv", lambda key: FAKE.get(key)),
68
+ patch.object(postgres_server.engine, "Engine", fake_engine),
69
+ ):
70
+ postgres_server.PostgreSQLInitializer.initialize(config)
71
+ return captured
72
+
73
+ def test_without_tunnel_uses_configured_host(self):
74
+ with _no_tunnel():
75
+ cfg = self._initialize()
76
+ self.assertEqual(
77
+ (cfg["host"], cfg["port"]), ("private.rds.example.com", "5433")
78
+ )
79
+
80
+ def test_tunnel_overrides_host_and_port_keeps_credentials(self):
81
+ with patch.dict(os.environ, {DB_TUNNEL_VAR: "127.0.0.1:15433"}):
82
+ cfg = self._initialize()
83
+ self.assertEqual((cfg["host"], cfg["port"]), ("127.0.0.1", "15433"))
84
+ self.assertEqual(cfg["dbname"], "app_db")
85
+ self.assertEqual(cfg["user"], "app_user")
86
+ self.assertEqual(cfg["password"], "app_pass")
87
+
88
+ def test_tunnel_overrides_an_explicit_config_host(self):
89
+ # a tunnel reaches one server; any other host is unreachable anyway
90
+ with patch.dict(os.environ, {DB_TUNNEL_VAR: "127.0.0.1:15433"}):
91
+ cfg = self._initialize({"host": "other.example.com", "port": "5432"})
92
+ self.assertEqual((cfg["host"], cfg["port"]), ("127.0.0.1", "15433"))
93
+
94
+ def test_malformed_tunnel_fails_before_connecting(self):
95
+ with patch.dict(os.environ, {DB_TUNNEL_VAR: "localhost"}):
96
+ with self.assertRaises(ValueError):
97
+ self._initialize()
98
+
99
+
100
+ class TestMaintenanceTools(unittest.TestCase):
101
+ def test_pg_dump_follows_the_tunnel(self):
102
+ m = maintenance.DatabaseMaintenance(
103
+ config={"host": "private.rds.example.com", "port": "5433", "user": "u"},
104
+ engine=MagicMock(),
105
+ )
106
+ with (
107
+ patch.dict(os.environ, {DB_TUNNEL_VAR: "127.0.0.1:15433"}),
108
+ patch.object(m, "_run_tool") as run,
109
+ ):
110
+ m.backup_to_file("app_db", "/tmp/x.dump")
111
+ args = run.call_args[0][0]
112
+ self.assertEqual(args[args.index("-h") + 1], "127.0.0.1")
113
+ self.assertEqual(args[args.index("-p") + 1], "15433")
114
+
115
+ def test_pg_dump_without_tunnel_uses_configured_host(self):
116
+ m = maintenance.DatabaseMaintenance(
117
+ config={"host": "private.rds.example.com", "port": "5433", "user": "u"},
118
+ engine=MagicMock(),
119
+ )
120
+ with _no_tunnel(), patch.object(m, "_run_tool") as run:
121
+ m.backup_to_file("app_db", "/tmp/x.dump")
122
+ args = run.call_args[0][0]
123
+ self.assertEqual(args[args.index("-h") + 1], "private.rds.example.com")
124
+
125
+
126
+ if __name__ == "__main__":
127
+ unittest.main()