velocity-python 0.1.124__tar.gz → 0.1.126__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.
- {velocity_python-0.1.124 → velocity_python-0.1.126}/PKG-INFO +1 -1
- {velocity_python-0.1.124 → velocity_python-0.1.126}/pyproject.toml +1 -1
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/__init__.py +1 -1
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/mixins/data_service.py +38 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/jsonproxy.py +32 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/table.py +30 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/postgres/sql.py +22 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity_python.egg-info/PKG-INFO +1 -1
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity_python.egg-info/SOURCES.txt +2 -1
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_json_columns.py +57 -0
- velocity_python-0.1.126/tests/test_write_hook_empty_strings.py +146 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/LICENSE +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/README.md +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/setup.cfg +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/amplify.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/amplify_build.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/assets/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/assets/backfill.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/assets/indexing.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/assets/references.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/assets/service.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/assets/usage_index.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/blobs.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/base.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/dispatch.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/infra.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/jobs.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/provisioning.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/retention.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/security.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/single_session.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/deploy/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/deploy/config.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/deploy/operations.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/deploy/policies.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/deploy/reconcile.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/deploy/targets.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/dirty_pipeline.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/email.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/facets.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/feature_flags.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/base_handler.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/context.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/context_factory.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/exceptions.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/lambda_handler.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/masquerade.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/mixins/job_handler.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/mixins/session_control.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/perf.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/response.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/sqs_handler.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/job_lifecycle.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/job_results.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/mirror.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/ops.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/s3.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/sms.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/ssm_config.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_amplify_build_policy_size.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_amplify_build_shim.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_deploy_policies.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_deploy_reconcile.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_email.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_response.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_sms.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/cache.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/async_support.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/column.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/database.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/decorators.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/engine.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/lazy_engine.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/result.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/row.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/sequence.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/transaction.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/core/view.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/exceptions.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/maintenance.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/migrations.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/base/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/base/initializer.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/base/operators.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/base/sql.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/base/types.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/mysql/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/mysql/operators.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/mysql/reserved.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/mysql/sql.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/mysql/types.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/postgres/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/postgres/operators.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/postgres/reserved.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/postgres/types.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/sqlite/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/sqlite/operators.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/sqlite/reserved.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/sqlite/sql.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/sqlite/types.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/sqlserver/operators.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/sqlserver/sql.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/sqlserver/types.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/tablehelper.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/common_db_test.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/common.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/conftest.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_column.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_connections.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_database.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_engine.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_imports.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_result.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_row.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_table.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/sql/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/sql/common.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_db_utils.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_postgres.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_result_caching.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_sql_builder.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_tablehelper.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/tests/test_view_helper.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/utils.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/logging.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/conv/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/conv/iconv.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/conv/oconv.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/db.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/export.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/format.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/mail.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/merge.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/pdf.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tests/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tests/test_db.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tests/test_fix.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tests/test_format.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tests/test_iconv.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tests/test_mail_send.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tests/test_merge.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tests/test_oconv.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tests/test_original_error.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tests/test_timer.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/timer.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/misc/tools.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/core/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/core/capabilities.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/core/store.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/core/where.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/exceptions.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/base/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/cassandra/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/cassandra/translation.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/couchbase/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/couchbase/translation.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/dynamodb/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/dynamodb/translation.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/elasticsearch/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/elasticsearch/translation.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/memory/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/mongodb/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/mongodb/translation.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/nosql/servers/redis/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/payment/__init__.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/payment/authorizenet_adapter.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/payment/authorizenet_mirror.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/payment/base_adapter.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/payment/braintree_adapter.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/payment/braintree_mirror.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/payment/charge_rules.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/payment/stripe_adapter.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/payment/stripe_mirror.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity_python.egg-info/dependency_links.txt +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity_python.egg-info/entry_points.txt +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity_python.egg-info/requires.txt +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity_python.egg-info/top_level.txt +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_access_error_403.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_activity_tracking_aborted_tx.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_amplify_build.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_asset_indexing.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_asset_references.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_assets_service.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_async_support.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_batch_operations.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_blob_store.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_capabilities.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_capability_catalog.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_cognito_user_cache.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_column_tx_arg.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_concurrency_safety.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_connection_pool.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_connection_resilience.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_context_flags_loader.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_context_job_descriptions.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_data_service_query_cap.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_data_service_renumber.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_data_service_save_as.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_db_credentials_ssm_cascade.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_db_maintenance.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_decorators.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_delete_hook_fk_blocked.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_dirty_pipeline_fast_path.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_email_processing.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_enqueue_metric_namespace.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_enqueue_send_failures.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_facets.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_feature_flags.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_get_cognito_user_provider.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_http_handler_rollback.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_iconv_money_to_cents.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_identifier_injection_guard.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_job_handler.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_job_lifecycle.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_job_results.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_jsonb_dict_adapter.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_lambda_handler.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_lambda_handler_auth.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_lambda_handler_masquerade.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_lambda_handler_user_cache.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_lazy_engine.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_masquerade_grant.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_mirror.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_mixins_import.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_n_plus_one.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_nosql_dynamodb.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_nosql_memory.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_nosql_mongodb.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_nosql_redis.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_nosql_translation.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_nosql_where.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_observability.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_on_commit.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_ops.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_payment_authorizenet_adapter.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_payment_braintree_adapter.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_payment_braintree_mirror.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_payment_mirror_empty_table.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_payment_profile_sorting.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_payment_stripe_adapter.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_pdf.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_prepared_statements.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_psycopg3_upgrade.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_query_cache.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_query_missing_table_unlocked.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_read_cache.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_response.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_restricted_direct_tables.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_result_returning.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_retry_side_effect_guard.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_return_default_safety.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_row_batch_update.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_row_cache_staleness.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_s3_bucket_config.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_schema_migrations.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_security_capability.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_security_hardening.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_server_cursor.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_session_control.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_single_autocommit_safety.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_single_session_capability.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_spreadsheet_functions.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_sqlite_backend.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_sqs_per_record_transactions.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_ssm_config.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_store_user_data.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_sys_modified_count_postgres_demo.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_table_alter.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_table_find_cache.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_table_purge.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_transaction_class_wrapping.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_transaction_commit_and_ownership.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_transaction_edge_cases.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_where_clause_validation.py +0 -0
- {velocity_python-0.1.124 → velocity_python-0.1.126}/tests/test_write_hook_create_flow.py +0 -0
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/mixins/data_service.py
RENAMED
|
@@ -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
|
|
@@ -68,6 +68,22 @@ class BoundList(list):
|
|
|
68
68
|
super().__init__(_wrap(v, ctrl) for v in iterable)
|
|
69
69
|
self._ctrl = ctrl
|
|
70
70
|
|
|
71
|
+
# -- copies are plain values ---------------------------------------
|
|
72
|
+
# A copy of a bound value is a plain list with no link to the row.
|
|
73
|
+
# Without these, copy.deepcopy rebuilt the proxy through the pickle
|
|
74
|
+
# path and refilled it with __setitem__ — one flush (an UPDATE) per
|
|
75
|
+
# nested container for a read-only copy, or an AttributeError when the
|
|
76
|
+
# rebuilt controller had no flush (2026-09-28, found by a dance forms
|
|
77
|
+
# migration deep-copying a definition read from a JSONB column).
|
|
78
|
+
def __copy__(self):
|
|
79
|
+
return unwrap(self)
|
|
80
|
+
|
|
81
|
+
def __deepcopy__(self, memo):
|
|
82
|
+
return unwrap(self)
|
|
83
|
+
|
|
84
|
+
def __reduce__(self):
|
|
85
|
+
return (list, (unwrap(self),))
|
|
86
|
+
|
|
71
87
|
# -- mutators ------------------------------------------------------
|
|
72
88
|
def __setitem__(self, index, value):
|
|
73
89
|
if isinstance(index, slice):
|
|
@@ -131,6 +147,22 @@ class BoundDict(dict):
|
|
|
131
147
|
super().__init__({k: _wrap(v, ctrl) for k, v in mapping.items()})
|
|
132
148
|
self._ctrl = ctrl
|
|
133
149
|
|
|
150
|
+
# -- copies are plain values ---------------------------------------
|
|
151
|
+
# A copy of a bound value is a plain dict with no link to the row.
|
|
152
|
+
# Without these, copy.deepcopy rebuilt the proxy through the pickle
|
|
153
|
+
# path and refilled it with __setitem__ — one flush (an UPDATE) per
|
|
154
|
+
# nested container for a read-only copy, or an AttributeError when the
|
|
155
|
+
# rebuilt controller had no flush (2026-09-28, found by a dance forms
|
|
156
|
+
# migration deep-copying a definition read from a JSONB column).
|
|
157
|
+
def __copy__(self):
|
|
158
|
+
return unwrap(self)
|
|
159
|
+
|
|
160
|
+
def __deepcopy__(self, memo):
|
|
161
|
+
return unwrap(self)
|
|
162
|
+
|
|
163
|
+
def __reduce__(self):
|
|
164
|
+
return (dict, (unwrap(self),))
|
|
165
|
+
|
|
134
166
|
# -- mutators ------------------------------------------------------
|
|
135
167
|
def __setitem__(self, key, value):
|
|
136
168
|
super().__setitem__(key, _wrap(value, self._ctrl))
|
|
@@ -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(".")
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity_python.egg-info/SOURCES.txt
RENAMED
|
@@ -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
|
|
@@ -423,3 +423,60 @@ class TestRowJsonTextDecode(unittest.TestCase):
|
|
|
423
423
|
|
|
424
424
|
if __name__ == "__main__":
|
|
425
425
|
unittest.main()
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
class TestProxyCopies(unittest.TestCase):
|
|
429
|
+
"""A copy of a bound JSON value is a plain value: copying never
|
|
430
|
+
touches the row (2026-09-28)."""
|
|
431
|
+
|
|
432
|
+
def _bound(self):
|
|
433
|
+
from velocity.db.core import jsonproxy
|
|
434
|
+
|
|
435
|
+
writes = []
|
|
436
|
+
value = {
|
|
437
|
+
"pages": [{"ui": [{"copy": "x"}], "skippable": {"label": "s"}}],
|
|
438
|
+
"tags": ["a", ["b"]],
|
|
439
|
+
}
|
|
440
|
+
return jsonproxy.bind(value, writes.append), value, writes
|
|
441
|
+
|
|
442
|
+
def _assert_plain(self, value):
|
|
443
|
+
self.assertIn(type(value), (dict, list))
|
|
444
|
+
items = value.values() if isinstance(value, dict) else value
|
|
445
|
+
for item in items:
|
|
446
|
+
if isinstance(item, (dict, list)):
|
|
447
|
+
self._assert_plain(item)
|
|
448
|
+
|
|
449
|
+
def test_deepcopy_and_copy_are_plain_and_write_nothing(self):
|
|
450
|
+
import copy
|
|
451
|
+
|
|
452
|
+
bound, original, writes = self._bound()
|
|
453
|
+
deep = copy.deepcopy(bound)
|
|
454
|
+
shallow = copy.copy(bound)
|
|
455
|
+
self.assertEqual(deep, original)
|
|
456
|
+
self.assertEqual(shallow, original)
|
|
457
|
+
self._assert_plain(deep)
|
|
458
|
+
self._assert_plain(shallow)
|
|
459
|
+
self.assertEqual(writes, [])
|
|
460
|
+
# the copy is independent of the row
|
|
461
|
+
deep["pages"][0]["skippable"]["label"] = "changed"
|
|
462
|
+
self.assertEqual(bound["pages"][0]["skippable"]["label"], "s")
|
|
463
|
+
self.assertEqual(writes, [])
|
|
464
|
+
# nested proxies copy plain too
|
|
465
|
+
self._assert_plain(copy.deepcopy(bound["pages"]))
|
|
466
|
+
self._assert_plain(copy.copy(bound["tags"]))
|
|
467
|
+
self.assertEqual(writes, [])
|
|
468
|
+
|
|
469
|
+
def test_pickle_round_trip_is_plain(self):
|
|
470
|
+
import pickle
|
|
471
|
+
|
|
472
|
+
bound, original, writes = self._bound()
|
|
473
|
+
back = pickle.loads(pickle.dumps(bound))
|
|
474
|
+
self.assertEqual(back, original)
|
|
475
|
+
self._assert_plain(back)
|
|
476
|
+
self.assertEqual(writes, [])
|
|
477
|
+
|
|
478
|
+
def test_mutation_still_flushes(self):
|
|
479
|
+
bound, _original, writes = self._bound()
|
|
480
|
+
bound["pages"][0]["skippable"]["label"] = "t"
|
|
481
|
+
self.assertEqual(len(writes), 1)
|
|
482
|
+
self.assertEqual(writes[0]["pages"][0]["skippable"]["label"], "t")
|
|
@@ -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()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/dispatch.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/provisioning.py
RENAMED
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/retention.py
RENAMED
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/security.py
RENAMED
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/capabilities/single_session.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/base_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/context_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/lambda_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/mixins/__init__.py
RENAMED
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/mixins/job_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/mixins/web_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/handlers/sqs_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_deploy_policies.py
RENAMED
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/aws/tests/test_deploy_reconcile.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/base/__init__.py
RENAMED
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/base/initializer.py
RENAMED
|
File without changes
|
{velocity_python-0.1.124 → velocity_python-0.1.126}/src/velocity/db/servers/base/operators.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|