velocity-python 0.1.124__tar.gz → 0.1.125__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 (310) hide show
  1. {velocity_python-0.1.124 → velocity_python-0.1.125}/PKG-INFO +1 -1
  2. {velocity_python-0.1.124 → velocity_python-0.1.125}/pyproject.toml +1 -1
  3. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/mixins/data_service.py +38 -0
  5. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/table.py +30 -0
  6. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/postgres/sql.py +22 -0
  7. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity_python.egg-info/PKG-INFO +1 -1
  8. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity_python.egg-info/SOURCES.txt +2 -1
  9. velocity_python-0.1.125/tests/test_write_hook_empty_strings.py +146 -0
  10. {velocity_python-0.1.124 → velocity_python-0.1.125}/LICENSE +0 -0
  11. {velocity_python-0.1.124 → velocity_python-0.1.125}/README.md +0 -0
  12. {velocity_python-0.1.124 → velocity_python-0.1.125}/setup.cfg +0 -0
  13. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/__init__.py +0 -0
  14. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/amplify.py +0 -0
  15. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/amplify_build.py +0 -0
  16. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/assets/__init__.py +0 -0
  17. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/assets/backfill.py +0 -0
  18. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/assets/indexing.py +0 -0
  19. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/assets/references.py +0 -0
  20. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/assets/service.py +0 -0
  21. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/assets/usage_index.py +0 -0
  22. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/blobs.py +0 -0
  23. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/capabilities/__init__.py +0 -0
  24. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/capabilities/base.py +0 -0
  25. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/capabilities/dispatch.py +0 -0
  26. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/capabilities/infra.py +0 -0
  27. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/capabilities/jobs.py +0 -0
  28. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/capabilities/provisioning.py +0 -0
  29. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/capabilities/retention.py +0 -0
  30. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/capabilities/security.py +0 -0
  31. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/capabilities/single_session.py +0 -0
  32. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/deploy/__init__.py +0 -0
  33. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/deploy/config.py +0 -0
  34. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/deploy/operations.py +0 -0
  35. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/deploy/policies.py +0 -0
  36. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/deploy/reconcile.py +0 -0
  37. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/deploy/targets.py +0 -0
  38. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/dirty_pipeline.py +0 -0
  39. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/email.py +0 -0
  40. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/facets.py +0 -0
  41. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/feature_flags.py +0 -0
  42. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/__init__.py +0 -0
  43. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/base_handler.py +0 -0
  44. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/context.py +0 -0
  45. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/context_factory.py +0 -0
  46. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/exceptions.py +0 -0
  47. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  48. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/masquerade.py +0 -0
  49. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  50. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/mixins/job_handler.py +0 -0
  51. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/mixins/session_control.py +0 -0
  52. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  53. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/perf.py +0 -0
  54. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/response.py +0 -0
  55. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  56. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/job_lifecycle.py +0 -0
  57. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/job_results.py +0 -0
  58. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/mirror.py +0 -0
  59. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/ops.py +0 -0
  60. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/s3.py +0 -0
  61. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/sms.py +0 -0
  62. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/ssm_config.py +0 -0
  63. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/tests/__init__.py +0 -0
  64. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/tests/test_amplify_build_policy_size.py +0 -0
  65. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/tests/test_amplify_build_shim.py +0 -0
  66. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  67. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/tests/test_deploy_policies.py +0 -0
  68. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/tests/test_deploy_reconcile.py +0 -0
  69. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/tests/test_email.py +0 -0
  70. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  71. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/tests/test_response.py +0 -0
  72. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/aws/tests/test_sms.py +0 -0
  73. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/cache.py +0 -0
  74. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/__init__.py +0 -0
  75. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/__init__.py +0 -0
  76. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/async_support.py +0 -0
  77. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/column.py +0 -0
  78. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/database.py +0 -0
  79. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/decorators.py +0 -0
  80. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/engine.py +0 -0
  81. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/jsonproxy.py +0 -0
  82. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/lazy_engine.py +0 -0
  83. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/result.py +0 -0
  84. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/row.py +0 -0
  85. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/sequence.py +0 -0
  86. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/transaction.py +0 -0
  87. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/core/view.py +0 -0
  88. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/exceptions.py +0 -0
  89. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/maintenance.py +0 -0
  90. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/migrations.py +0 -0
  91. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/__init__.py +0 -0
  92. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/base/__init__.py +0 -0
  93. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/base/initializer.py +0 -0
  94. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/base/operators.py +0 -0
  95. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/base/sql.py +0 -0
  96. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/base/types.py +0 -0
  97. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/mysql/__init__.py +0 -0
  98. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/mysql/operators.py +0 -0
  99. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/mysql/reserved.py +0 -0
  100. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/mysql/sql.py +0 -0
  101. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/mysql/types.py +0 -0
  102. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/postgres/__init__.py +0 -0
  103. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/postgres/operators.py +0 -0
  104. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/postgres/reserved.py +0 -0
  105. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/postgres/types.py +0 -0
  106. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  107. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/sqlite/operators.py +0 -0
  108. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  109. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/sqlite/sql.py +0 -0
  110. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/sqlite/types.py +0 -0
  111. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  112. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  113. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  114. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  115. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/sqlserver/types.py +0 -0
  116. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/servers/tablehelper.py +0 -0
  117. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/__init__.py +0 -0
  118. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/common_db_test.py +0 -0
  119. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/__init__.py +0 -0
  120. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/common.py +0 -0
  121. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/conftest.py +0 -0
  122. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_column.py +0 -0
  123. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  124. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_database.py +0 -0
  125. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  126. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  127. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  128. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_result.py +0 -0
  129. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_row.py +0 -0
  130. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  131. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  132. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  133. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  134. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  135. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_table.py +0 -0
  136. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  137. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  138. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/sql/__init__.py +0 -0
  139. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/sql/common.py +0 -0
  140. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  141. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  142. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  143. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_db_utils.py +0 -0
  144. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_postgres.py +0 -0
  145. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  146. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  147. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_result_caching.py +0 -0
  148. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  149. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  150. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  151. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  152. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_sql_builder.py +0 -0
  153. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_tablehelper.py +0 -0
  154. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/tests/test_view_helper.py +0 -0
  155. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/db/utils.py +0 -0
  156. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/logging.py +0 -0
  157. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/__init__.py +0 -0
  158. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/conv/__init__.py +0 -0
  159. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/conv/iconv.py +0 -0
  160. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/conv/oconv.py +0 -0
  161. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/db.py +0 -0
  162. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/export.py +0 -0
  163. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/format.py +0 -0
  164. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/mail.py +0 -0
  165. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/merge.py +0 -0
  166. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/pdf.py +0 -0
  167. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tests/__init__.py +0 -0
  168. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tests/test_db.py +0 -0
  169. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tests/test_fix.py +0 -0
  170. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tests/test_format.py +0 -0
  171. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tests/test_iconv.py +0 -0
  172. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tests/test_mail_send.py +0 -0
  173. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tests/test_merge.py +0 -0
  174. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tests/test_oconv.py +0 -0
  175. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tests/test_original_error.py +0 -0
  176. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tests/test_timer.py +0 -0
  177. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/timer.py +0 -0
  178. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/misc/tools.py +0 -0
  179. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/__init__.py +0 -0
  180. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/core/__init__.py +0 -0
  181. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/core/capabilities.py +0 -0
  182. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/core/store.py +0 -0
  183. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/core/where.py +0 -0
  184. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/exceptions.py +0 -0
  185. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/__init__.py +0 -0
  186. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/base/__init__.py +0 -0
  187. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/cassandra/__init__.py +0 -0
  188. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/cassandra/translation.py +0 -0
  189. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/couchbase/__init__.py +0 -0
  190. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/couchbase/translation.py +0 -0
  191. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/dynamodb/__init__.py +0 -0
  192. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/dynamodb/translation.py +0 -0
  193. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/elasticsearch/__init__.py +0 -0
  194. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/elasticsearch/translation.py +0 -0
  195. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/memory/__init__.py +0 -0
  196. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/mongodb/__init__.py +0 -0
  197. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/mongodb/translation.py +0 -0
  198. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/nosql/servers/redis/__init__.py +0 -0
  199. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/payment/__init__.py +0 -0
  200. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/payment/authorizenet_adapter.py +0 -0
  201. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/payment/authorizenet_mirror.py +0 -0
  202. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/payment/base_adapter.py +0 -0
  203. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/payment/braintree_adapter.py +0 -0
  204. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/payment/braintree_mirror.py +0 -0
  205. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/payment/charge_rules.py +0 -0
  206. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/payment/stripe_adapter.py +0 -0
  207. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity/payment/stripe_mirror.py +0 -0
  208. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  209. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity_python.egg-info/entry_points.txt +0 -0
  210. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity_python.egg-info/requires.txt +0 -0
  211. {velocity_python-0.1.124 → velocity_python-0.1.125}/src/velocity_python.egg-info/top_level.txt +0 -0
  212. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_access_error_403.py +0 -0
  213. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_activity_tracking_aborted_tx.py +0 -0
  214. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_amplify_build.py +0 -0
  215. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_asset_indexing.py +0 -0
  216. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_asset_references.py +0 -0
  217. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_assets_service.py +0 -0
  218. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_async_support.py +0 -0
  219. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_batch_operations.py +0 -0
  220. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_blob_store.py +0 -0
  221. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_capabilities.py +0 -0
  222. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_capability_catalog.py +0 -0
  223. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_cognito_user_cache.py +0 -0
  224. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_column_tx_arg.py +0 -0
  225. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_concurrency_safety.py +0 -0
  226. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_connection_pool.py +0 -0
  227. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_connection_resilience.py +0 -0
  228. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_context_flags_loader.py +0 -0
  229. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_context_job_descriptions.py +0 -0
  230. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_data_service_query_cap.py +0 -0
  231. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_data_service_renumber.py +0 -0
  232. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_data_service_save_as.py +0 -0
  233. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_db_credentials_ssm_cascade.py +0 -0
  234. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_db_maintenance.py +0 -0
  235. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_decorators.py +0 -0
  236. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_delete_hook_fk_blocked.py +0 -0
  237. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_dirty_pipeline_fast_path.py +0 -0
  238. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_email_processing.py +0 -0
  239. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_enqueue_metric_namespace.py +0 -0
  240. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_enqueue_send_failures.py +0 -0
  241. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_facets.py +0 -0
  242. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_feature_flags.py +0 -0
  243. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_get_cognito_user_provider.py +0 -0
  244. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_http_handler_rollback.py +0 -0
  245. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_iconv_money_to_cents.py +0 -0
  246. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_identifier_injection_guard.py +0 -0
  247. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_job_handler.py +0 -0
  248. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_job_lifecycle.py +0 -0
  249. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_job_results.py +0 -0
  250. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_json_columns.py +0 -0
  251. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_jsonb_dict_adapter.py +0 -0
  252. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_lambda_handler.py +0 -0
  253. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_lambda_handler_auth.py +0 -0
  254. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_lambda_handler_masquerade.py +0 -0
  255. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_lambda_handler_user_cache.py +0 -0
  256. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_lazy_engine.py +0 -0
  257. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_masquerade_grant.py +0 -0
  258. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_mirror.py +0 -0
  259. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_mixins_import.py +0 -0
  260. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_n_plus_one.py +0 -0
  261. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_nosql_dynamodb.py +0 -0
  262. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_nosql_memory.py +0 -0
  263. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_nosql_mongodb.py +0 -0
  264. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_nosql_redis.py +0 -0
  265. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_nosql_translation.py +0 -0
  266. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_nosql_where.py +0 -0
  267. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_observability.py +0 -0
  268. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_on_commit.py +0 -0
  269. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_ops.py +0 -0
  270. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_payment_authorizenet_adapter.py +0 -0
  271. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_payment_braintree_adapter.py +0 -0
  272. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_payment_braintree_mirror.py +0 -0
  273. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_payment_mirror_empty_table.py +0 -0
  274. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_payment_profile_sorting.py +0 -0
  275. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_payment_stripe_adapter.py +0 -0
  276. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_pdf.py +0 -0
  277. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_prepared_statements.py +0 -0
  278. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_psycopg3_upgrade.py +0 -0
  279. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_query_cache.py +0 -0
  280. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_query_missing_table_unlocked.py +0 -0
  281. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_read_cache.py +0 -0
  282. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_response.py +0 -0
  283. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_restricted_direct_tables.py +0 -0
  284. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_result_returning.py +0 -0
  285. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_retry_side_effect_guard.py +0 -0
  286. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_return_default_safety.py +0 -0
  287. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_row_batch_update.py +0 -0
  288. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_row_cache_staleness.py +0 -0
  289. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_s3_bucket_config.py +0 -0
  290. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_schema_migrations.py +0 -0
  291. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_security_capability.py +0 -0
  292. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_security_hardening.py +0 -0
  293. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_server_cursor.py +0 -0
  294. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_session_control.py +0 -0
  295. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_single_autocommit_safety.py +0 -0
  296. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_single_session_capability.py +0 -0
  297. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_spreadsheet_functions.py +0 -0
  298. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_sqlite_backend.py +0 -0
  299. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_sqs_per_record_transactions.py +0 -0
  300. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_ssm_config.py +0 -0
  301. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_store_user_data.py +0 -0
  302. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  303. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_table_alter.py +0 -0
  304. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_table_find_cache.py +0 -0
  305. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_table_purge.py +0 -0
  306. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_transaction_class_wrapping.py +0 -0
  307. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_transaction_commit_and_ownership.py +0 -0
  308. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_transaction_edge_cases.py +0 -0
  309. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_where_clause_validation.py +0 -0
  310. {velocity_python-0.1.124 → velocity_python-0.1.125}/tests/test_write_hook_create_flow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.124
3
+ Version: 0.1.125
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.124"
7
+ version = "0.1.125"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.1.124"
1
+ __version__ = version = "0.1.125"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -56,6 +56,39 @@ def _fk_blocked_delete_message(table, exc):
56
56
  _IDENTIFIER_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
57
57
 
58
58
 
59
+ TEXT_TYPES = frozenset(
60
+ {"text", "character varying", "character", "varchar", "char", "citext", "name"}
61
+ )
62
+
63
+
64
+ def coerce_empty_strings(column_types, incoming):
65
+ """The subset of ``incoming`` whose empty-string values must become
66
+ ``None`` because their column is not a text type — ``{}`` when nothing
67
+ needs changing or the types are unknown."""
68
+ if not column_types:
69
+ return {}
70
+ out = {}
71
+ for key, value in incoming.items():
72
+ if not isinstance(value, str) or value.strip() != "":
73
+ continue
74
+ dtype = column_types.get(key)
75
+ if dtype is None:
76
+ continue
77
+ if str(dtype).lower() in TEXT_TYPES:
78
+ continue
79
+ out[key] = None
80
+ return out
81
+
82
+
83
+ def _column_types(tx, table):
84
+ """The table's ``{column: data_type}`` map, ``{}`` when unavailable —
85
+ a lookup failure must never block a write."""
86
+ try:
87
+ return tx.table(table).column_types(cache_ttl=300) or {}
88
+ except Exception: # noqa: BLE001 — best effort, the write proceeds
89
+ return {}
90
+
91
+
59
92
  def _bump_sys_id_sequence(tx, table):
60
93
  """Keep the table's serial/identity sequence ahead of MAX(sys_id) so a
61
94
  future auto-generated id can never collide with an explicitly chosen one
@@ -338,6 +371,11 @@ class DataServiceMixin:
338
371
  def write_hook(self, tx, table, sys_id, incoming, context, save_as=False):
339
372
  row = {}
340
373
  incoming.pop("sys_id", None)
374
+ # A form posts every field it shows; untouched NULLs come back as ''.
375
+ # Postgres refuses '' for boolean/date/timestamp/numeric columns
376
+ # (found live: UPDATE studio SET inactive = '' — 2026-09-26), so
377
+ # empty strings become NULL wherever the column is not text.
378
+ incoming.update(coerce_empty_strings(_column_types(tx, table), incoming))
341
379
  if sys_id == "@new":
342
380
  self._call_rwx_hook(
343
381
  "before_new", "common", tx, table, sys_id, incoming, context
@@ -641,6 +641,36 @@ class Table:
641
641
  )
642
642
  return load()
643
643
 
644
+ def column_types(self, **kwds):
645
+ """``{column_name: data_type}`` (information_schema names, e.g.
646
+ ``boolean``, ``timestamp with time zone``, ``text``). ``{}`` when the
647
+ server module has no ``column_types`` SQL. ``cache_ttl=N`` as in
648
+ ``sys_columns``."""
649
+ builder = getattr(self.sql, "column_types", None)
650
+ if builder is None:
651
+ return {}
652
+ sql, vals = builder(self.name)
653
+ if kwds.get("sql_only", False):
654
+ return sql, vals
655
+
656
+ def load():
657
+ result = self.tx.execute(sql, vals, cursor=self.cursor())
658
+ return {name: dtype for name, dtype in result.as_tuple()}
659
+
660
+ cache_ttl = kwds.get("cache_ttl")
661
+ if cache_ttl:
662
+ from velocity.cache import SCHEMA_TAG, read_cache
663
+
664
+ return dict(
665
+ read_cache.memo(
666
+ ("column_types", self._cache_scope(), self.name),
667
+ load,
668
+ cache_ttl,
669
+ tables=(self.name, SCHEMA_TAG),
670
+ )
671
+ )
672
+ return load()
673
+
644
674
  def columns(self):
645
675
  """
646
676
  Returns non-system column names.
@@ -1392,6 +1392,28 @@ class SQL(BaseSQLDialect):
1392
1392
  ]
1393
1393
  )
1394
1394
 
1395
+ @classmethod
1396
+ def column_types(cls, name):
1397
+ """``(column_name, data_type)`` rows for a table — the type map the
1398
+ write path uses to turn a form's empty strings into NULL for
1399
+ non-text columns."""
1400
+ if "." in name:
1401
+ return """
1402
+ select column_name, data_type
1403
+ from information_schema.columns
1404
+ where UPPER(table_schema) = UPPER(%s)
1405
+ and UPPER(table_name) = UPPER(%s)
1406
+ """, tuple(
1407
+ name.split(".")
1408
+ )
1409
+ return """
1410
+ select column_name, data_type
1411
+ from information_schema.columns
1412
+ where UPPER(table_name) = UPPER(%s)
1413
+ """, (
1414
+ name,
1415
+ )
1416
+
1395
1417
  @classmethod
1396
1418
  def column_info(cls, table, name):
1397
1419
  params = table.split(".")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.124
3
+ Version: 0.1.125
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
@@ -304,4 +304,5 @@ tests/test_transaction_class_wrapping.py
304
304
  tests/test_transaction_commit_and_ownership.py
305
305
  tests/test_transaction_edge_cases.py
306
306
  tests/test_where_clause_validation.py
307
- tests/test_write_hook_create_flow.py
307
+ tests/test_write_hook_create_flow.py
308
+ tests/test_write_hook_empty_strings.py
@@ -0,0 +1,146 @@
1
+ """write_hook turns a form's empty strings into NULL for non-text columns
2
+ (found live 2026-09-26: UPDATE studio SET inactive = '' — "invalid input
3
+ syntax for type boolean"). Text columns keep '' (a cleared note is a note
4
+ that is empty); unknown columns and unknown types are left alone.
5
+ """
6
+
7
+ import importlib.util
8
+ import unittest
9
+ from pathlib import Path
10
+
11
+ MODULE_PATH = (
12
+ Path(__file__).resolve().parents[1]
13
+ / "src"
14
+ / "velocity"
15
+ / "aws"
16
+ / "handlers"
17
+ / "mixins"
18
+ / "data_service.py"
19
+ )
20
+ _spec = importlib.util.spec_from_file_location("test_write_hook_empty", MODULE_PATH)
21
+ _mod = importlib.util.module_from_spec(_spec)
22
+ _spec.loader.exec_module(_mod)
23
+
24
+ TYPES = {
25
+ "description": "text",
26
+ "notes": "character varying",
27
+ "inactive": "boolean",
28
+ "inactivated_on": "timestamp with time zone",
29
+ "start_date": "date",
30
+ "amount": "numeric",
31
+ "payload": "jsonb",
32
+ }
33
+
34
+
35
+ class CoerceTests(unittest.TestCase):
36
+ def test_empty_strings_become_null_for_non_text_columns(self):
37
+ incoming = {
38
+ "description": "Magnify 1",
39
+ "notes": "",
40
+ "inactive": "",
41
+ "inactivated_on": " ",
42
+ "start_date": "",
43
+ "amount": "",
44
+ "payload": "",
45
+ "unknown_col": "",
46
+ }
47
+ self.assertEqual(
48
+ _mod.coerce_empty_strings(TYPES, incoming),
49
+ {
50
+ "inactive": None,
51
+ "inactivated_on": None,
52
+ "start_date": None,
53
+ "amount": None,
54
+ "payload": None,
55
+ },
56
+ )
57
+
58
+ def test_real_values_and_unknown_types_untouched(self):
59
+ self.assertEqual(
60
+ _mod.coerce_empty_strings(TYPES, {"inactive": False, "amount": "0"}), {}
61
+ )
62
+ self.assertEqual(_mod.coerce_empty_strings({}, {"inactive": ""}), {})
63
+ self.assertEqual(_mod.coerce_empty_strings(None, {"inactive": ""}), {})
64
+
65
+
66
+ class FakeRow:
67
+ def __init__(self, data):
68
+ self.data = dict(data)
69
+ self.updates = []
70
+
71
+ def update(self, payload):
72
+ self.updates.append(dict(payload))
73
+ self.data.update(payload)
74
+
75
+ def to_dict(self):
76
+ return dict(self.data)
77
+
78
+ def __getitem__(self, k):
79
+ return self.data[k]
80
+
81
+
82
+ class FakeTable:
83
+ def __init__(self, row, types):
84
+ self.row = row
85
+ self.types = types
86
+
87
+ def find(self, sys_id):
88
+ return self.row
89
+
90
+ def column_types(self, **kwds):
91
+ return self.types
92
+
93
+
94
+ class FakeTx:
95
+ def __init__(self, table):
96
+ self._table = table
97
+
98
+ def table(self, name):
99
+ return self._table
100
+
101
+
102
+ class FakeContext:
103
+ def get_feature_flags(self, tx):
104
+ return {}
105
+
106
+ def enqueue_dirty(self, table, sys_id):
107
+ pass
108
+
109
+
110
+ class Service(_mod.DataServiceMixin):
111
+ def _call_rwx_hook(self, *args, **kwargs):
112
+ pass
113
+
114
+ def _get_dirty_pipeline_excluded_tables(self, tx, context):
115
+ return set()
116
+
117
+ def _dirty_pipeline_enabled(self, *a, **k):
118
+ return False
119
+
120
+
121
+ class WriteHookTests(unittest.TestCase):
122
+ def test_update_writes_null_not_empty_string(self):
123
+ row = FakeRow({"sys_id": 6, "description": "Magnify", "inactive": None})
124
+ tx = FakeTx(FakeTable(row, TYPES))
125
+ incoming = {
126
+ "sys_id": 6,
127
+ "description": "Magnify 1",
128
+ "inactive": "",
129
+ "inactivated_on": "",
130
+ "inactivated_by": "",
131
+ }
132
+ try:
133
+ Service().write_hook(tx, "studio", 6, incoming, FakeContext())
134
+ except AttributeError:
135
+ # the tail of write_hook touches dirty-pipeline plumbing the fake
136
+ # does not model; the update itself has already happened
137
+ pass
138
+ self.assertEqual(row.updates[0]["inactive"], None)
139
+ self.assertEqual(row.updates[0]["inactivated_on"], None)
140
+ # a text column keeps its empty string
141
+ self.assertEqual(row.updates[0]["inactivated_by"], "")
142
+ self.assertEqual(row.updates[0]["description"], "Magnify 1")
143
+
144
+
145
+ if __name__ == "__main__":
146
+ unittest.main()