velocity-python 0.2.9__tar.gz → 0.3.1__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.2.9 → velocity_python-0.3.1}/PKG-INFO +5 -2
- {velocity_python-0.2.9 → velocity_python-0.3.1}/pyproject.toml +10 -2
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/__init__.py +1 -1
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/capabilities/__init__.py +8 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/capabilities/base.py +62 -0
- velocity_python-0.3.1/src/velocity/aws/capabilities/dispatch.py +124 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/capabilities/jobs.py +41 -16
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/capabilities/provisioning.py +72 -5
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/capabilities/security.py +118 -43
- velocity_python-0.3.1/src/velocity/aws/dirty_pipeline.py +624 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/context.py +315 -16
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/lambda_handler.py +12 -4
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/__init__.py +8 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/audit_trail.py +288 -83
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/job_handler.py +2 -16
- velocity_python-0.3.1/src/velocity/aws/job_lifecycle.py +486 -0
- velocity_python-0.3.1/src/velocity/aws/scheduler.py +567 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/schema.py +7 -0
- velocity_python-0.3.1/src/velocity/db/_pgddl.py +162 -0
- velocity_python-0.3.1/src/velocity/db/audit.py +793 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/engine.py +35 -9
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/transaction.py +16 -2
- velocity_python-0.3.1/src/velocity/db/effective.py +408 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/exceptions.py +28 -0
- velocity_python-0.3.1/src/velocity/db/integrity.py +782 -0
- velocity_python-0.3.1/src/velocity/db/tests/postgres/test_integrity_triggers.py +514 -0
- velocity_python-0.3.1/src/velocity/hooks.py +526 -0
- velocity_python-0.3.1/src/velocity/rules/__init__.py +111 -0
- velocity_python-0.3.1/src/velocity/rules/engine.py +857 -0
- velocity_python-0.3.1/src/velocity/rules/expressions.py +451 -0
- velocity_python-0.3.1/src/velocity/rules/facts.py +331 -0
- velocity_python-0.3.1/src/velocity/rules/storage.py +391 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity_python.egg-info/PKG-INFO +5 -2
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity_python.egg-info/SOURCES.txt +28 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity_python.egg-info/requires.txt +5 -1
- velocity_python-0.3.1/tests/test_audit_log_confidential.py +41 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_audit_mixin.py +4 -1
- velocity_python-0.3.1/tests/test_audit_registry_table_gate.py +79 -0
- velocity_python-0.3.1/tests/test_capability_dependencies.py +319 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_connection_pool.py +75 -0
- velocity_python-0.3.1/tests/test_context_enqueue_idempotency.py +574 -0
- velocity_python-0.3.1/tests/test_db_audit_required.py +982 -0
- velocity_python-0.3.1/tests/test_db_effective.py +390 -0
- velocity_python-0.3.1/tests/test_db_integrity.py +724 -0
- velocity_python-0.3.1/tests/test_dirty_pipeline_consumer.py +645 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_ensure_infrastructure.py +14 -1
- velocity_python-0.3.1/tests/test_hooks.py +360 -0
- velocity_python-0.3.1/tests/test_job_lifecycle.py +182 -0
- velocity_python-0.3.1/tests/test_job_record_guard.py +443 -0
- velocity_python-0.3.1/tests/test_job_record_guard_postgres.py +198 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_lambda_handler_masquerade.py +88 -1
- velocity_python-0.3.1/tests/test_record_event_postgres.py +245 -0
- velocity_python-0.3.1/tests/test_record_event_transaction.py +409 -0
- velocity_python-0.3.1/tests/test_rules.py +868 -0
- velocity_python-0.3.1/tests/test_rules_storage.py +353 -0
- velocity_python-0.3.1/tests/test_scheduler.py +594 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_security_capability.py +20 -4
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_user_not_provisioned.py +2 -1
- velocity_python-0.2.9/src/velocity/aws/capabilities/dispatch.py +0 -61
- velocity_python-0.2.9/src/velocity/aws/dirty_pipeline.py +0 -121
- velocity_python-0.2.9/src/velocity/aws/job_lifecycle.py +0 -71
- velocity_python-0.2.9/tests/test_job_lifecycle.py +0 -119
- {velocity_python-0.2.9 → velocity_python-0.3.1}/LICENSE +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/README.md +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/setup.cfg +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/amplify.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/assets/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/assets/backfill.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/assets/indexing.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/assets/references.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/assets/service.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/assets/usage_index.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/authz.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/blobs.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/capabilities/infra.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/capabilities/retention.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/capabilities/single_session.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/deploy/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/deploy/ci_gate.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/deploy/config.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/deploy/frontend_checks.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/deploy/operations.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/deploy/policies.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/deploy/reconcile.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/deploy/targets.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/email.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/facets.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/failures.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/feature_flags.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/base_handler.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/context_factory.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/exceptions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/masquerade.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/action_names.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/client_errors.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/client_input.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/permissions_actions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/row_scoped.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/session_control.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/tiles_actions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/perf.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/response.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/handlers/sqs_handler.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/identity.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/job_results.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/mirror.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/ops.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/permissions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/queue_defaults.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/s3.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/sms.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/ssm_config.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tests/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tests/test_deploy_least_privilege.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tests/test_deploy_policies.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tests/test_deploy_policy_size.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tests/test_deploy_reconcile.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tests/test_email.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tests/test_response.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tests/test_sms.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/tiles.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/user_data.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/cache.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/async_support.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/column.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/database.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/decorators.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/jsonproxy.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/lazy_engine.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/result.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/row.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/sequence.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/table.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/core/view.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/fragments.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/maintenance.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/migrations.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/base/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/base/initializer.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/base/operators.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/base/sql.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/base/types.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/mysql/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/mysql/operators.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/mysql/reserved.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/mysql/sql.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/mysql/types.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/postgres/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/postgres/operators.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/postgres/reserved.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/postgres/sql.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/postgres/types.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/sqlite/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/sqlite/operators.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/sqlite/reserved.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/sqlite/sql.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/sqlite/types.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/sqlserver/operators.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/sqlserver/sql.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/sqlserver/types.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/servers/tablehelper.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/common_db_test.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/common.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/conftest.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_column.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_connections.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_database.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_engine.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_imports.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_result.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_row.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_table.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/sql/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/sql/common.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_db_utils.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_postgres.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_result_caching.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_sql_builder.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_tablehelper.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tests/test_view_helper.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/tunnel.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/db/utils.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/logging.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/conv/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/conv/iconv.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/conv/oconv.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/db.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/export.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/format.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/mail.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/merge.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/pdf.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tests/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tests/test_db.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tests/test_fix.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tests/test_format.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tests/test_iconv.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tests/test_mail_send.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tests/test_merge.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tests/test_oconv.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tests/test_original_error.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tests/test_timer.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/timer.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/misc/tools.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/core/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/core/capabilities.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/core/store.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/core/where.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/exceptions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/base/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/cassandra/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/cassandra/translation.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/couchbase/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/couchbase/translation.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/dynamodb/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/dynamodb/translation.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/elasticsearch/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/elasticsearch/translation.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/memory/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/mongodb/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/mongodb/translation.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/nosql/servers/redis/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/_mirror_core.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/authorizenet_adapter.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/authorizenet_mirror.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/base_adapter.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/braintree_adapter.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/braintree_mirror.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/charge_rules.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/money.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/plaid_api.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/plaid_webhook.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/redact.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/schema.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/stripe_adapter.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/payment/stripe_mirror.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/testing/__init__.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/testing/fake_db.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity_python.egg-info/dependency_links.txt +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity_python.egg-info/entry_points.txt +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity_python.egg-info/top_level.txt +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_access_error_403.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_action_names.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_activity_tracking_aborted_tx.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_amplify_redirects.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_asset_indexing.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_asset_references.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_assets_service.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_async_support.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_authz.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_aws_package_hygiene.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_batch_operations.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_blob_store.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_business_objects.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_capabilities.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_capability_catalog.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_check_changelog.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_ci_gate.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_client_errors_mixin.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_client_input_contract_export.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_client_sql_boundary.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_client_table_gate_case_and_pointers.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_cognito_client_lazy.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_cognito_user_cache.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_column_tx_arg.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_concurrency_safety.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_connection_resilience.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_context_flags_loader.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_context_job_descriptions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_context_queue_job.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_conv_combine.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_data_service_owner_scope.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_data_service_query_cap.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_data_service_renumber.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_data_service_row_number.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_data_service_save_as.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_data_service_security_gates.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_db_credentials_ssm_cascade.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_db_maintenance.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_db_tunnel.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_declared_column_types.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_decorators.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_delete_hook_fk_blocked.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_deploy_config_fields.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_deploy_engine.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_deploy_frontend_checks.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_deploy_operations_queue.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_deploy_policies.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_dirty_pipeline_fast_path.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_dlq_constant.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_docs_snippets.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_email_processing.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_enqueue_metric_namespace.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_enqueue_send_failures.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_export_workbook.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_facets.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_facets_and_where.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_failures.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_feature_flags.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_get_cognito_user_provider.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_http_handler_rollback.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_iconv_money_to_cents.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_identifier_injection_guard.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_identity.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_job_handler.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_job_results.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_json_columns.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_jsonb_dict_adapter.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_lambda_handler.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_lambda_handler_auth.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_lambda_handler_user_cache.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_lazy_engine.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_masquerade_grant.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_merge_on_conflict.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_mirror.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_mirror_upsert_public.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_mixins_import.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_n_plus_one.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_nosql_dynamodb.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_nosql_memory.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_nosql_mongodb.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_nosql_redis.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_nosql_translation.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_nosql_where.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_observability.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_on_action_default.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_on_commit.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_ops.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_audit_fixes.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_authorizenet_adapter.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_braintree_adapter.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_braintree_mirror.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_business_agnostic.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_charge_reference.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_extra_bounds.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_lazy_imports.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_mirror_empty_table.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_profile_sorting.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_payment_stripe_adapter.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pdf.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_permissions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_permissions_actions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_permissions_notices.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_permissions_review.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pgserver_bulk_cache.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pgserver_capability_types.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pgserver_ensure_infrastructure.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pgserver_get_tables.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pgserver_permissions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pgserver_pointer_joins.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pgserver_row_scope.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pgserver_search_export.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pgserver_tiles.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pgserver_user_data.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_plaid_api.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_plaid_webhook.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_pointer_join_sql.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_postgres_initializer_config.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_postgres_py_type.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_prepared_statements.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_psycopg3_upgrade.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_query_cache.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_query_missing_table_unlocked.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_raw_sql_prefix_policy.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_read_cache.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_readme_imports.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_response.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_restricted_direct_tables.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_result_returning.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_retry_side_effect_guard.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_return_default_safety.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_row_batch_update.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_row_cache_staleness.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_row_scoped_data_service.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_s3_bucket_config.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_s3_list_client_files.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_s3_upload.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_schema_lock_config_other_backends.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_schema_migrations.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_search_paging_export.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_security_events_table_gate.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_security_hardening.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_server_cursor.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_session_control.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_single_autocommit_safety.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_single_session_capability.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_sms_twilio_signature.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_spreadsheet_functions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_sqlite_backend.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_sqs_per_record_transactions.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_ssm_config.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_ssm_config_require.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_store_user_data.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_sys_modified_count_postgres_demo.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_table_alter.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_table_claim.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_table_find_cache.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_table_purge.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_testing_fake_db.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_tiles.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_transaction_class_wrapping.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_transaction_commit_and_ownership.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_transaction_edge_cases.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_tx_lifecycle_retry.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_tx_read_cache_and_bulk.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_user_data.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_where_clause_validation.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/tests/test_write_hook_create_flow.py +0 -0
- {velocity_python-0.2.9 → velocity_python-0.3.1}/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.
|
|
3
|
+
Version: 0.3.1
|
|
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
|
|
@@ -57,8 +57,10 @@ Requires-Dist: braintree<5,>=4.30.0; extra == "payment"
|
|
|
57
57
|
Requires-Dist: xmltodict<2,>=0.13.0; extra == "payment"
|
|
58
58
|
Provides-Extra: plaid
|
|
59
59
|
Requires-Dist: ecdsa>=0.19.0; extra == "plaid"
|
|
60
|
+
Provides-Extra: rules
|
|
61
|
+
Requires-Dist: cel-python<0.6,>=0.5.0; extra == "rules"
|
|
60
62
|
Provides-Extra: all
|
|
61
|
-
Requires-Dist: velocity-python[aws,excel,http,payment,pdf,plaid,postgres,templates]; extra == "all"
|
|
63
|
+
Requires-Dist: velocity-python[aws,excel,http,payment,pdf,plaid,postgres,rules,templates]; extra == "all"
|
|
62
64
|
Provides-Extra: dev
|
|
63
65
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
64
66
|
Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
|
|
@@ -81,6 +83,7 @@ Requires-Dist: xmltodict<2,>=0.13.0; extra == "test"
|
|
|
81
83
|
Requires-Dist: authorizenet>=1.1.3; extra == "test"
|
|
82
84
|
Requires-Dist: ecdsa>=0.19.0; extra == "test"
|
|
83
85
|
Requires-Dist: openpyxl>=3.1.0; extra == "test"
|
|
86
|
+
Requires-Dist: cel-python<0.6,>=0.5.0; extra == "test"
|
|
84
87
|
Requires-Dist: redis>=5.0.0; extra == "test"
|
|
85
88
|
Requires-Dist: fakeredis>=2.23.0; extra == "test"
|
|
86
89
|
Requires-Dist: pymongo>=4.6.0; extra == "test"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "velocity-python"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.1"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Velocity Team", email="info@codeclubs.org" },
|
|
10
10
|
]
|
|
@@ -101,8 +101,15 @@ payment = [
|
|
|
101
101
|
plaid = [
|
|
102
102
|
"ecdsa>=0.19.0",
|
|
103
103
|
]
|
|
104
|
+
# Expression conditions for velocity.rules (CEL, pure Python; pulls lark,
|
|
105
|
+
# pendulum and google-re2). Decision tables need nothing extra. Tested on
|
|
106
|
+
# cel-python 0.5.x; raise the bound only after `make test-unit` passes on the
|
|
107
|
+
# next minor (pre-1.0, so a minor may change the API).
|
|
108
|
+
rules = [
|
|
109
|
+
"cel-python>=0.5.0,<0.6",
|
|
110
|
+
]
|
|
104
111
|
all = [
|
|
105
|
-
"velocity-python[postgres,aws,excel,templates,http,payment,plaid,pdf]",
|
|
112
|
+
"velocity-python[postgres,aws,excel,templates,http,payment,plaid,pdf,rules]",
|
|
106
113
|
]
|
|
107
114
|
dev = [
|
|
108
115
|
"pytest>=8.0.0",
|
|
@@ -130,6 +137,7 @@ test = [
|
|
|
130
137
|
"authorizenet>=1.1.3",
|
|
131
138
|
"ecdsa>=0.19.0",
|
|
132
139
|
"openpyxl>=3.1.0",
|
|
140
|
+
"cel-python>=0.5.0,<0.6",
|
|
133
141
|
# NoSQL adapter unit tests run against in-process fakes (real clients for
|
|
134
142
|
# the wire formats, fakes for the servers). ES/Cassandra/Couchbase have
|
|
135
143
|
# translation-only unit tests and need no driver here.
|
|
@@ -13,10 +13,13 @@ from .base import ( # noqa: F401
|
|
|
13
13
|
POLICY_KINDS,
|
|
14
14
|
REGISTRY,
|
|
15
15
|
Capability,
|
|
16
|
+
CapabilityDependencyError,
|
|
16
17
|
capability,
|
|
17
18
|
catalog,
|
|
19
|
+
dependency_order,
|
|
18
20
|
describe,
|
|
19
21
|
get,
|
|
22
|
+
missing_dependencies,
|
|
20
23
|
load_config,
|
|
21
24
|
policy_field,
|
|
22
25
|
resolve_config,
|
|
@@ -30,6 +33,7 @@ from .dispatch import ( # noqa: F401
|
|
|
30
33
|
)
|
|
31
34
|
from .provisioning import ( # noqa: F401
|
|
32
35
|
as_bool,
|
|
36
|
+
check_dependencies,
|
|
33
37
|
provision,
|
|
34
38
|
row_enabled,
|
|
35
39
|
trigger_key,
|
|
@@ -47,15 +51,19 @@ from .security import SecurityMixin # noqa: E402,F401
|
|
|
47
51
|
__all__ = [
|
|
48
52
|
"Capability",
|
|
49
53
|
"CapabilityCatalogMixin",
|
|
54
|
+
"CapabilityDependencyError",
|
|
50
55
|
"CapabilityDispatcher",
|
|
51
56
|
"POLICY_KINDS",
|
|
52
57
|
"REGISTRY",
|
|
53
58
|
"as_bool",
|
|
54
59
|
"capability",
|
|
55
60
|
"catalog",
|
|
61
|
+
"check_dependencies",
|
|
62
|
+
"dependency_order",
|
|
56
63
|
"describe",
|
|
57
64
|
"get",
|
|
58
65
|
"load_config",
|
|
66
|
+
"missing_dependencies",
|
|
59
67
|
"policy_field",
|
|
60
68
|
"provision",
|
|
61
69
|
"resolve_config",
|
|
@@ -72,6 +72,11 @@ COUNTRY_CODE = re.compile(r"^[A-Z]{2}$")
|
|
|
72
72
|
REGISTRY: dict = {}
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
class CapabilityDependencyError(ValueError):
|
|
76
|
+
"""A capability is enabled without one it requires, requires an unknown
|
|
77
|
+
one, or the requirements form a cycle."""
|
|
78
|
+
|
|
79
|
+
|
|
75
80
|
def policy_field(name, kind, label, help="", **extra) -> dict:
|
|
76
81
|
"""One entry of a capability's ``policy`` schema. ``extra`` carries
|
|
77
82
|
kind-specific hints an editor may use (``options`` for a choice,
|
|
@@ -92,6 +97,7 @@ class Capability:
|
|
|
92
97
|
mixins: tuple = () # handler mixins the site attaches at runtime
|
|
93
98
|
applies_to: tuple = ("demo", "production") # default stages; registry can force
|
|
94
99
|
policy: tuple = () # (policy_field(...), ...) — the per-site config shape
|
|
100
|
+
requires_capabilities: tuple = () # names that must be enabled alongside
|
|
95
101
|
|
|
96
102
|
def handle(self, action, tx, context, config):
|
|
97
103
|
"""Do the work for one scheduled/queued ``action``. Return a JSON-able
|
|
@@ -119,6 +125,61 @@ def get(name):
|
|
|
119
125
|
raise KeyError(f"unknown capability {name!r}; registered: {known}") from None
|
|
120
126
|
|
|
121
127
|
|
|
128
|
+
def requirements(name) -> tuple:
|
|
129
|
+
"""The capabilities ``name`` declares it requires (unknown name: KeyError)."""
|
|
130
|
+
return tuple(getattr(get(name), "requires_capabilities", ()) or ())
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def dependency_order(names) -> list:
|
|
134
|
+
"""``names`` plus everything they require (transitively), each after the
|
|
135
|
+
capabilities it requires; otherwise in the order given.
|
|
136
|
+
|
|
137
|
+
Raises :class:`CapabilityDependencyError` for a cycle (naming it) or a
|
|
138
|
+
requirement that is not a registered capability.
|
|
139
|
+
"""
|
|
140
|
+
ordered: list = []
|
|
141
|
+
done: set = set()
|
|
142
|
+
visiting: list = []
|
|
143
|
+
|
|
144
|
+
def visit(name, needed_by=None):
|
|
145
|
+
if name in done:
|
|
146
|
+
return
|
|
147
|
+
if name in visiting:
|
|
148
|
+
cycle = visiting[visiting.index(name) :] + [name]
|
|
149
|
+
raise CapabilityDependencyError(
|
|
150
|
+
"capability requirements form a cycle: " + " -> ".join(cycle)
|
|
151
|
+
)
|
|
152
|
+
if name not in REGISTRY:
|
|
153
|
+
if needed_by is None:
|
|
154
|
+
get(name) # the usual "unknown capability" KeyError
|
|
155
|
+
raise CapabilityDependencyError(
|
|
156
|
+
f"capability {needed_by!r} requires {name!r}, which is not registered"
|
|
157
|
+
)
|
|
158
|
+
visiting.append(name)
|
|
159
|
+
for required in requirements(name):
|
|
160
|
+
visit(required, name)
|
|
161
|
+
visiting.pop()
|
|
162
|
+
done.add(name)
|
|
163
|
+
ordered.append(name)
|
|
164
|
+
|
|
165
|
+
for name in names:
|
|
166
|
+
visit(name)
|
|
167
|
+
return ordered
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def missing_dependencies(enabled) -> dict:
|
|
171
|
+
"""``{name: [required names not in enabled]}`` for each name in
|
|
172
|
+
``enabled`` that lacks one; empty when every requirement is met."""
|
|
173
|
+
enabled = list(enabled)
|
|
174
|
+
present = set(enabled)
|
|
175
|
+
missing = {}
|
|
176
|
+
for name in enabled:
|
|
177
|
+
absent = [req for req in requirements(name) if req not in present]
|
|
178
|
+
if absent:
|
|
179
|
+
missing[name] = absent
|
|
180
|
+
return missing
|
|
181
|
+
|
|
182
|
+
|
|
122
183
|
def resolve_config(cap, flags=None, row_config=None) -> dict:
|
|
123
184
|
"""defaults <- feature flags (declared knobs only) <- site registry config."""
|
|
124
185
|
config = dict(cap.defaults)
|
|
@@ -148,6 +209,7 @@ def describe(cap) -> dict:
|
|
|
148
209
|
],
|
|
149
210
|
"flags": list(cap.defaults),
|
|
150
211
|
"policy": [dict(field) for field in cap.policy],
|
|
212
|
+
"requires": list(cap.requires_capabilities),
|
|
151
213
|
}
|
|
152
214
|
|
|
153
215
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Runtime dispatch: route the ``run-capability`` queue action to a capability.
|
|
2
|
+
|
|
3
|
+
A site's queue handler mixes in :class:`CapabilityDispatcher`; the scheduler
|
|
4
|
+
stubs seeded by :mod:`provision` enqueue ``run-capability`` with
|
|
5
|
+
``{"capability": name, "action": action}`` and this routes it. Config is
|
|
6
|
+
resolved locally — the site's own ``sys_capabilities`` row plus its feature
|
|
7
|
+
flags — so the runtime never reaches into the control plane. A capability
|
|
8
|
+
runs only with an enabled row of its own and enabled rows for every
|
|
9
|
+
capability it requires (``requires_capabilities``, transitively); otherwise
|
|
10
|
+
it is skipped and the skip is reported (:mod:`velocity.aws.failures`).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
|
|
17
|
+
from velocity.aws import failures, feature_flags
|
|
18
|
+
|
|
19
|
+
from .base import (
|
|
20
|
+
REGISTRY,
|
|
21
|
+
CapabilityDependencyError,
|
|
22
|
+
catalog,
|
|
23
|
+
dependency_order,
|
|
24
|
+
resolve_config,
|
|
25
|
+
)
|
|
26
|
+
from .provisioning import SITE_TABLE, row_enabled
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _skip(tx, name, action, reason, *, report=True):
|
|
30
|
+
"""The skipped response; with ``report``, a failure event (scrubbed,
|
|
31
|
+
never raising) once the transaction commits — a skip that should not
|
|
32
|
+
happen must reach an operator, not just a queue response."""
|
|
33
|
+
if report:
|
|
34
|
+
|
|
35
|
+
def send():
|
|
36
|
+
failures.report(
|
|
37
|
+
None,
|
|
38
|
+
None,
|
|
39
|
+
source="queue",
|
|
40
|
+
action="run-capability",
|
|
41
|
+
extra={
|
|
42
|
+
"capability": name,
|
|
43
|
+
"capability_action": action,
|
|
44
|
+
"skipped": reason,
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
on_commit = getattr(tx, "on_commit", None)
|
|
49
|
+
if callable(on_commit):
|
|
50
|
+
on_commit(send)
|
|
51
|
+
else:
|
|
52
|
+
send()
|
|
53
|
+
return {"capability": name, "action": action, "skipped": reason}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def run_capability(tx, context, name, action):
|
|
57
|
+
"""Resolve config and run one capability action. Returns the response body.
|
|
58
|
+
|
|
59
|
+
Runs only when the site has an enabled ``sys_capabilities`` row for the
|
|
60
|
+
capability (provisioning wrote it) and an enabled row for every
|
|
61
|
+
capability it requires, directly or through another requirement
|
|
62
|
+
(:func:`dependency_order`). Otherwise the action is skipped, and — except
|
|
63
|
+
for a plain "disabled for this site" — reported through
|
|
64
|
+
:mod:`velocity.aws.failures`: a scheduled run for a capability that was
|
|
65
|
+
never provisioned, or whose requirement is off, is a misconfiguration.
|
|
66
|
+
"""
|
|
67
|
+
cap = REGISTRY.get(name)
|
|
68
|
+
if cap is None:
|
|
69
|
+
return {"error": f"unknown capability {name!r}", "capability": name}
|
|
70
|
+
|
|
71
|
+
site = tx.table(SITE_TABLE)
|
|
72
|
+
row = site.find({"name": name})
|
|
73
|
+
if not row:
|
|
74
|
+
return _skip(tx, name, action, "not provisioned for this site")
|
|
75
|
+
if not row_enabled(row):
|
|
76
|
+
return _skip(tx, name, action, "disabled for this site", report=False)
|
|
77
|
+
try:
|
|
78
|
+
# every requirement, transitively (a -> b -> c needs c on too)
|
|
79
|
+
chain = [n for n in dependency_order([name]) if n != name]
|
|
80
|
+
except (CapabilityDependencyError, KeyError) as exc:
|
|
81
|
+
return _skip(tx, name, action, f"dependency error: {exc}")
|
|
82
|
+
missing = []
|
|
83
|
+
for required in chain:
|
|
84
|
+
required_row = site.find({"name": required})
|
|
85
|
+
if not required_row or not row_enabled(required_row):
|
|
86
|
+
missing.append(required)
|
|
87
|
+
if missing:
|
|
88
|
+
return _skip(
|
|
89
|
+
tx, name, action, f"requires {', '.join(missing)}, not enabled here"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
raw = row.get("config")
|
|
93
|
+
row_config = json.loads(raw) if isinstance(raw, str) and raw else (raw or {})
|
|
94
|
+
config = resolve_config(cap, flags=feature_flags.get(tx), row_config=row_config)
|
|
95
|
+
|
|
96
|
+
result = cap.handle(action, tx, context, config)
|
|
97
|
+
return {"capability": name, "action": action, "result": result}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class CapabilityDispatcher:
|
|
101
|
+
"""Queue-handler mixin: ``run-capability`` -> the registered capability."""
|
|
102
|
+
|
|
103
|
+
def OnActionRunCapability(self, tx, context):
|
|
104
|
+
# Queue records and scheduled ticks only: a handler that mixes this in
|
|
105
|
+
# must not let an HTTP caller run a capability's actions.
|
|
106
|
+
from velocity.aws.dirty_pipeline import refuse_http
|
|
107
|
+
|
|
108
|
+
refuse_http(self, "run-capability")
|
|
109
|
+
payload = context.payload() or {}
|
|
110
|
+
body = run_capability(
|
|
111
|
+
tx, context, payload.get("capability"), payload.get("action")
|
|
112
|
+
)
|
|
113
|
+
context.response().set_body(body)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class CapabilityCatalogMixin:
|
|
117
|
+
"""Web-handler mixin for the control plane: ``capability-catalog`` answers
|
|
118
|
+
with every registered capability described (``{"rows": [...]}`` — the
|
|
119
|
+
shape velocity-react's ApiSelect and option hooks consume), so the admin
|
|
120
|
+
UI's pick-list and policy editors come from the code, not a copy."""
|
|
121
|
+
|
|
122
|
+
def OnActionCapabilityCatalog(self, tx, context):
|
|
123
|
+
del tx
|
|
124
|
+
context.response().set_body({"rows": catalog()})
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
``JobHandler`` only discovers a stale job (worker died: Lambda timeout / OOM)
|
|
4
4
|
when someone polls its status — an abandoned job nobody polls stays "Running"
|
|
5
5
|
forever, and ``aws_job_activity`` is never pruned. This capability sweeps on
|
|
6
|
-
a schedule with the
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
a schedule with the same rule the web tier uses (``Running``/``Initialized``
|
|
7
|
+
with no progress for the window), judged by the database clock
|
|
8
|
+
(:func:`stale_jobs`): stale jobs are persisted as Failed with the standard
|
|
9
|
+
message plus the status they expired from (``expired_from``), and old
|
|
10
|
+
activity rows are purged.
|
|
9
11
|
"""
|
|
10
12
|
|
|
11
13
|
from __future__ import annotations
|
|
@@ -17,7 +19,6 @@ from velocity.aws.job_lifecycle import (
|
|
|
17
19
|
JOB_ACTIVITY_TABLE,
|
|
18
20
|
STALE_FAILURE,
|
|
19
21
|
STALE_JOB_STATUSES,
|
|
20
|
-
job_is_stale,
|
|
21
22
|
)
|
|
22
23
|
|
|
23
24
|
from .base import EVERY_15_MINUTES, Capability, capability
|
|
@@ -39,27 +40,51 @@ class Jobs(Capability):
|
|
|
39
40
|
if action != "expire_stale":
|
|
40
41
|
raise ValueError(f"jobs: unknown action {action!r}")
|
|
41
42
|
now = datetime.datetime.now(datetime.timezone.utc)
|
|
42
|
-
|
|
43
|
-
minutes=int(config.get("job_stale_minutes") or 16)
|
|
44
|
-
)
|
|
43
|
+
minutes = int(config.get("job_stale_minutes") or 16)
|
|
45
44
|
jobs = tx.table(JOB_ACTIVITY_TABLE)
|
|
46
45
|
|
|
47
46
|
expired = 0
|
|
48
|
-
for
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
for job in stale_jobs(tx, minutes):
|
|
48
|
+
status = job.get("status")
|
|
49
|
+
# the status in the where: a job that progressed since the select
|
|
50
|
+
# (or that another sweep expired) is left alone
|
|
51
|
+
changed = jobs.update(
|
|
52
|
+
{
|
|
53
|
+
"status": "Failed",
|
|
54
|
+
"message": json.dumps({**STALE_FAILURE, "expired_from": status}),
|
|
55
|
+
},
|
|
56
|
+
where={"sys_id": job["sys_id"], "status": status},
|
|
57
|
+
)
|
|
58
|
+
if changed:
|
|
59
|
+
expired += 1
|
|
60
|
+
_report_after_commit(tx, job, status)
|
|
57
61
|
|
|
58
62
|
days = int(config.get("job_retention_days") or 30)
|
|
59
63
|
purged = jobs.purge({"<sys_created": now - datetime.timedelta(days=days)})
|
|
60
64
|
return {"expired": expired, "purged": purged}
|
|
61
65
|
|
|
62
66
|
|
|
67
|
+
def stale_jobs(tx, minutes):
|
|
68
|
+
"""``Running``/``Initialized`` jobs with no progress for ``minutes``,
|
|
69
|
+
judged by the database clock: ``sys_modified`` is written by the row
|
|
70
|
+
trigger in the session's time zone, so it is compared with
|
|
71
|
+
``LOCALTIMESTAMP`` there, never with a Python clock (which disagrees with
|
|
72
|
+
it whenever the database does not run in UTC)."""
|
|
73
|
+
statuses = sorted(STALE_JOB_STATUSES)
|
|
74
|
+
placeholders = ", ".join(["%s"] * len(statuses))
|
|
75
|
+
return (
|
|
76
|
+
tx.execute(
|
|
77
|
+
f"SELECT sys_id, job_id, action, status FROM {JOB_ACTIVITY_TABLE} "
|
|
78
|
+
f"WHERE status IN ({placeholders}) "
|
|
79
|
+
"AND sys_modified < LOCALTIMESTAMP - %s::interval "
|
|
80
|
+
"ORDER BY sys_id",
|
|
81
|
+
[*statuses, f"{int(minutes)} minutes"],
|
|
82
|
+
)
|
|
83
|
+
.as_dict()
|
|
84
|
+
.all()
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
63
88
|
def _report_after_commit(tx, job, status):
|
|
64
89
|
"""A job whose worker died never reached onError, so nothing reported it:
|
|
65
90
|
publish a ``source="job"`` failure event once the sweep commits."""
|
{velocity_python-0.2.9 → velocity_python-0.3.1}/src/velocity/aws/capabilities/provisioning.py
RENAMED
|
@@ -12,6 +12,12 @@ table — the runtime reads config from there, never from the control plane —
|
|
|
12
12
|
only what is unset, never clobbering an edit), and (4) upserts one
|
|
13
13
|
``sys_task_triggers`` row + one ``program_objects`` stub per schedule.
|
|
14
14
|
|
|
15
|
+
Before anything is written, every capability this run leaves enabled must
|
|
16
|
+
have the capabilities it requires (``requires_capabilities``) enabled too —
|
|
17
|
+
in ``entries``, or (when ``entries`` does not name them) already enabled in
|
|
18
|
+
the site's ``sys_capabilities`` — else :class:`CapabilityDependencyError`.
|
|
19
|
+
Entries are then provisioned in dependency order (required ones first).
|
|
20
|
+
|
|
15
21
|
Everything is keyed deterministically (``cap:<name>:<action>``), so running it
|
|
16
22
|
on every deploy is idempotent — and that is the point: a demo refresh wipes
|
|
17
23
|
the triggers, the next deploy puts them back. A disabled entry disables its
|
|
@@ -25,7 +31,13 @@ import json
|
|
|
25
31
|
|
|
26
32
|
from velocity.aws import feature_flags
|
|
27
33
|
|
|
28
|
-
from .base import
|
|
34
|
+
from .base import (
|
|
35
|
+
REGISTRY,
|
|
36
|
+
CapabilityDependencyError,
|
|
37
|
+
dependency_order,
|
|
38
|
+
get,
|
|
39
|
+
requirements,
|
|
40
|
+
)
|
|
29
41
|
|
|
30
42
|
SITE_TABLE = "sys_capabilities" # per-site materialised registry
|
|
31
43
|
TRIGGERS_TABLE = "sys_task_triggers"
|
|
@@ -202,17 +214,72 @@ def program_source(cap_name: str, action: str) -> str:
|
|
|
202
214
|
)
|
|
203
215
|
|
|
204
216
|
|
|
217
|
+
def _entry_active(entry, stage) -> bool:
|
|
218
|
+
cap = get(entry["name"])
|
|
219
|
+
enabled = as_bool(entry.get("enabled", True), default=False)
|
|
220
|
+
return enabled and (stage in cap.applies_to or bool(entry.get("force")))
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def check_dependencies(tx, stage: str, entries) -> list:
|
|
224
|
+
"""The entries in dependency order, after checking that every entry this
|
|
225
|
+
run leaves active has what it requires active too. A requirement
|
|
226
|
+
``entries`` does not mention is looked up in the site's own
|
|
227
|
+
``sys_capabilities`` (an earlier provision); one ``entries`` disables or
|
|
228
|
+
keeps out of ``stage`` is missing. Raises
|
|
229
|
+
:class:`CapabilityDependencyError` listing every gap, before any write."""
|
|
230
|
+
entries = list(entries or [])
|
|
231
|
+
by_name = {entry["name"]: entry for entry in entries}
|
|
232
|
+
active = {name for name, entry in by_name.items() if _entry_active(entry, stage)}
|
|
233
|
+
order = dependency_order(list(by_name)) # unknown names / cycles raise here
|
|
234
|
+
site = tx.table(SITE_TABLE)
|
|
235
|
+
problems = []
|
|
236
|
+
for name in order:
|
|
237
|
+
if name not in active:
|
|
238
|
+
continue
|
|
239
|
+
for required in requirements(name):
|
|
240
|
+
if required in active:
|
|
241
|
+
continue
|
|
242
|
+
if required in by_name:
|
|
243
|
+
entry = by_name[required]
|
|
244
|
+
on = as_bool(entry.get("enabled", True), default=False)
|
|
245
|
+
why = f"not for {stage}" if on else "disabled"
|
|
246
|
+
else:
|
|
247
|
+
row = site.find({"name": required})
|
|
248
|
+
if row and row_enabled(row):
|
|
249
|
+
continue
|
|
250
|
+
why = "not enabled for this site"
|
|
251
|
+
problems.append(f"{name} requires {required} ({why})")
|
|
252
|
+
# turning one off must not strand a capability left on from before
|
|
253
|
+
for name in by_name:
|
|
254
|
+
if name in active:
|
|
255
|
+
continue
|
|
256
|
+
for other, cap in sorted(REGISTRY.items()):
|
|
257
|
+
if other in by_name or name not in (cap.requires_capabilities or ()):
|
|
258
|
+
continue
|
|
259
|
+
row = site.find({"name": other})
|
|
260
|
+
if row and row_enabled(row):
|
|
261
|
+
problems.append(
|
|
262
|
+
f"{other} requires {name}, which this run turns off "
|
|
263
|
+
f"(disable {other} too, or keep {name})"
|
|
264
|
+
)
|
|
265
|
+
if problems:
|
|
266
|
+
raise CapabilityDependencyError(
|
|
267
|
+
"capability dependencies not met: " + "; ".join(problems)
|
|
268
|
+
)
|
|
269
|
+
return [by_name[name] for name in order if name in by_name]
|
|
270
|
+
|
|
271
|
+
|
|
205
272
|
def provision(tx, stage: str, entries, *, now=None) -> list:
|
|
206
|
-
"""Materialise ``entries`` for ``stage``. Returns [(name, outcome), ...]
|
|
273
|
+
"""Materialise ``entries`` for ``stage``. Returns [(name, outcome), ...]
|
|
274
|
+
in dependency order (required capabilities first)."""
|
|
207
275
|
now = now or datetime.datetime.utcnow()
|
|
208
276
|
tx.table(SITE_TABLE).ensure_schema(**SCHEMA[SITE_TABLE])
|
|
209
277
|
report = []
|
|
210
|
-
for entry in
|
|
278
|
+
for entry in check_dependencies(tx, stage, entries):
|
|
211
279
|
name = entry["name"]
|
|
212
280
|
cap = get(name)
|
|
281
|
+
active = _entry_active(entry, stage)
|
|
213
282
|
enabled = as_bool(entry.get("enabled", True), default=False)
|
|
214
|
-
applies = stage in cap.applies_to or bool(entry.get("force"))
|
|
215
|
-
active = enabled and applies
|
|
216
283
|
|
|
217
284
|
tx.table(SITE_TABLE).merge(
|
|
218
285
|
{
|