vgi-python 0.8.11__tar.gz → 0.10.0__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.
- {vgi_python-0.8.11 → vgi_python-0.10.0}/.github/workflows/integration.yml +3 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/PKG-INFO +1 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/packages/vgi-fixtures/pyproject.toml +1 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/pyproject.toml +1 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_declarative.py +39 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_serialization.py +2 -1
- vgi_python-0.10.0/tests/conformance/test_const_param_enforcement.py +69 -0
- vgi_python-0.10.0/tests/conformance/test_container.py +17 -0
- vgi_python-0.10.0/tests/conformance/test_copy_from.py +16 -0
- vgi_python-0.10.0/tests/conformance/test_copy_to.py +21 -0
- vgi_python-0.10.0/tests/conformance/test_github.py +15 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_secret.py +3 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_aggregate_function.py +51 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_argument_spec.py +101 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_copy_to_function.py +82 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/uv.lock +58 -58
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/__init__.py +2 -0
- vgi_python-0.10.0/vgi/_test_fixtures/aggregate/secret_typed.py +115 -0
- vgi_python-0.10.0/vgi/_test_fixtures/companion.py +169 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/copy_from.py +58 -2
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/copy_to.py +78 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/formatting.py +2 -2
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table_in_out.py +46 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/worker.py +56 -2
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/argument_spec.py +146 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/arguments.py +71 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/catalog/__init__.py +2 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/catalog/catalog_interface.py +96 -2
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/catalog/descriptors.py +15 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/_common.py +2 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/copy_from_function.py +22 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/copy_to_function.py +32 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/scalar_function.py +33 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/worker.py +16 -1
- {vgi_python-0.8.11 → vgi_python-0.10.0}/.gitattributes +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/.github/dependabot.yml +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/.github/workflows/release.yml +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/.gitignore +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/.python-version +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/.vale.ini +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/CLAUDE.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/LICENSE +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/README.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/SECURITY.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/ci/README.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/ci/run-integration.sh +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/arguments.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/auth.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/catalogs.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/client.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/exceptions.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/filters.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/functions.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/http.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/index.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/metadata.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/observability.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/storage.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/transactor.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/api/worker.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/argument-serialization.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/logo.png +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/assets/social-card.png +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/authentication.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/catalog-interface.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/cli.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/column-statistics.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/concepts/index.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/contributing-docs.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/generator-api.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/how-to/index.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/index.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/lifecycle.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/metadata.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/overrides/main.html +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/robots.txt +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/shared-storage.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/tutorial/index.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/tutorial/table.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/docs/vgi-logo.png +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/examples/calc_worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/examples/filter_worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/examples/row_count_worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/examples/sum_worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/mkdocs.yml +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/scripts/measure_startup.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/test-data/generate.sh +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_example_worker_catalog.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_required_field_filter_paths.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/client/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/client/test_cli.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_directory_parity.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_protocol_version.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/conftest.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_auth.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_docstrings.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_exceptions.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_function_storage.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_metadata.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_otel.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_protocol_classes.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_serve.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_setting_secret_annotations.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_tcp_transport.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_union_argument.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/test_worker_page.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_duckdb.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/http_server.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/_common.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/filters.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/aggregate_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/auth.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/client/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/client/catalog_mixin.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/client/cli.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/client/client.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/cpp_request_builders.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/exceptions.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/function_storage.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/http/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/invocation.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/logging_config.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/meta_worker.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/metadata.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/otel.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/protocol.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/protocol_version.txt +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/py.typed +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/schema_utils.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/secret_service.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/serve.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/table_filter_pushdown.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/table_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/transactor/client.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/vgi/transactor/server.py +0 -0
- {vgi_python-0.8.11 → vgi_python-0.10.0}/wrangler.jsonc +0 -0
|
@@ -39,7 +39,9 @@ jobs:
|
|
|
39
39
|
runs-on: ubuntu-latest
|
|
40
40
|
timeout-minutes: 45
|
|
41
41
|
strategy:
|
|
42
|
-
|
|
42
|
+
# If any transport leg fails, cancel the rest — one broken transport means
|
|
43
|
+
# the build is broken; no value in burning ~45 min finishing the others.
|
|
44
|
+
fail-fast: true
|
|
43
45
|
matrix:
|
|
44
46
|
include:
|
|
45
47
|
# stdio: the subprocess transport (whole suite — the primary lane).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow
|
|
5
5
|
Project-URL: Homepage, https://query.farm
|
|
6
6
|
Project-URL: Repository, https://github.com/Query-farm/vgi-python
|
|
@@ -38,6 +38,7 @@ dependencies = ["vgi-python[test-fixtures-writable]", "vgi-python[duckdb]"]
|
|
|
38
38
|
|
|
39
39
|
[project.scripts]
|
|
40
40
|
vgi-fixture-worker = "vgi._test_fixtures.worker:main"
|
|
41
|
+
vgi-fixture-companion-worker = "vgi._test_fixtures.companion:main"
|
|
41
42
|
vgi-fixture-http = "vgi._test_fixtures.http_server:main"
|
|
42
43
|
vgi-fixture-versioned-worker = "vgi._test_fixtures.versioned:main"
|
|
43
44
|
vgi-fixture-versioned-tables-worker = "vgi._test_fixtures.versioned_tables:main"
|
|
@@ -41,8 +41,10 @@ from vgi.catalog import (
|
|
|
41
41
|
View,
|
|
42
42
|
ViewInfo,
|
|
43
43
|
)
|
|
44
|
+
from vgi.invocation import BindResponse
|
|
44
45
|
from vgi.schema_utils import schema
|
|
45
46
|
from vgi.table_function import (
|
|
47
|
+
BindParams,
|
|
46
48
|
ProcessParams,
|
|
47
49
|
TableFunctionGenerator,
|
|
48
50
|
bind_fixed_schema,
|
|
@@ -81,6 +83,31 @@ class UsersFunction(TableFunctionGenerator[EmptyArgs]):
|
|
|
81
83
|
out.finish()
|
|
82
84
|
|
|
83
85
|
|
|
86
|
+
@init_single_worker
|
|
87
|
+
class SecretLookupFunction(TableFunctionGenerator[EmptyArgs]):
|
|
88
|
+
"""Backing function whose ``on_bind`` performs a two-phase secret lookup.
|
|
89
|
+
|
|
90
|
+
This exercises the bug where ``Table(function=...)`` schema derivation took
|
|
91
|
+
the empty secret-scope-request schema instead of retrying the bind.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
class Meta: # noqa: D106
|
|
95
|
+
name = "secret_scan"
|
|
96
|
+
description = "Backing function that looks up a secret at bind time"
|
|
97
|
+
|
|
98
|
+
FIXED_SCHEMA: ClassVar[pa.Schema] = schema({"id": pa.int64(), "name": pa.string()})
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def on_bind(cls, params: BindParams[EmptyArgs]) -> BindResponse: # noqa: D102
|
|
102
|
+
params.secrets.get("some_secret") # unresolved -> registers a two-phase lookup
|
|
103
|
+
return BindResponse(output_schema=cls.FIXED_SCHEMA)
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def process(cls, params: ProcessParams[EmptyArgs], state: None, out: OutputCollector) -> None: # noqa: D102
|
|
107
|
+
out.emit(pa.RecordBatch.from_pydict({"id": [1], "name": ["x"]}, schema=params.output_schema))
|
|
108
|
+
out.finish()
|
|
109
|
+
|
|
110
|
+
|
|
84
111
|
@init_single_worker
|
|
85
112
|
@bind_fixed_schema
|
|
86
113
|
class EventsFunction(TableFunctionGenerator[EmptyArgs]):
|
|
@@ -186,6 +213,18 @@ class TestTableWithFunction:
|
|
|
186
213
|
assert resolved.field(0).name == "id"
|
|
187
214
|
assert resolved.field(1).name == "name"
|
|
188
215
|
|
|
216
|
+
def test_function_backed_table_derives_schema_despite_secret_lookup(self) -> None:
|
|
217
|
+
"""A backing function that calls secrets.get() in on_bind still yields its schema.
|
|
218
|
+
|
|
219
|
+
Regression: on_bind's two-phase secret lookup makes the first bind return an
|
|
220
|
+
empty secret-scope-request schema; schema derivation must retry with resolved
|
|
221
|
+
secrets rather than adopt the empty schema (which produced 0-column tables).
|
|
222
|
+
"""
|
|
223
|
+
table = Table(name="things", function=SecretLookupFunction)
|
|
224
|
+
resolved = table.resolved_columns
|
|
225
|
+
assert [f.name for f in resolved] == ["id", "name"]
|
|
226
|
+
assert len(resolved) == 2
|
|
227
|
+
|
|
189
228
|
def test_table_function_backed_with_constraints(self) -> None:
|
|
190
229
|
"""Function-backed table can have constraints validated."""
|
|
191
230
|
table = Table(
|
|
@@ -336,7 +375,6 @@ class TestTableWithFunction:
|
|
|
336
375
|
from typing import Any as _Any
|
|
337
376
|
|
|
338
377
|
from vgi.invocation import BindResponse
|
|
339
|
-
from vgi.table_function import BindParams
|
|
340
378
|
|
|
341
379
|
@init_single_worker
|
|
342
380
|
class _ManualBindFn(TableFunctionGenerator[EmptyArgs]):
|
|
@@ -1110,7 +1110,7 @@ class TestArrowSchemaCorrectness:
|
|
|
1110
1110
|
def test_catalog_attach_result_schema(self) -> None:
|
|
1111
1111
|
"""Verify CatalogAttachResult Arrow schema."""
|
|
1112
1112
|
schema = CatalogAttachResult.ARROW_SCHEMA
|
|
1113
|
-
assert len(schema) ==
|
|
1113
|
+
assert len(schema) == 15
|
|
1114
1114
|
assert schema.field("attach_opaque_data").type == pa.binary()
|
|
1115
1115
|
assert schema.field("supports_transactions").type == pa.bool_()
|
|
1116
1116
|
assert schema.field("supports_time_travel").type == pa.bool_()
|
|
@@ -1120,6 +1120,7 @@ class TestArrowSchemaCorrectness:
|
|
|
1120
1120
|
assert schema.field("default_schema").type == pa.string()
|
|
1121
1121
|
assert schema.field("settings").type == pa.list_(pa.binary())
|
|
1122
1122
|
assert schema.field("secret_types").type == pa.list_(pa.binary())
|
|
1123
|
+
assert schema.field("attach_catalogs").type == pa.list_(pa.binary())
|
|
1123
1124
|
assert schema.field("resolved_data_version").type == pa.string()
|
|
1124
1125
|
assert schema.field("resolved_implementation_version").type == pa.string()
|
|
1125
1126
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Conformance test: ConstParam value constraints are enforced at bind.
|
|
4
|
+
|
|
5
|
+
A ``ConstParam`` may declare ``choices``/``ge``/``le``/``gt``/``lt``/``pattern``.
|
|
6
|
+
Those are surfaced for agent discovery (via ``vgi_function_arguments()``) AND
|
|
7
|
+
must be enforced: a const value that violates a declared constraint fails at
|
|
8
|
+
bind with ``ArgumentValidationError``, rather than silently reaching
|
|
9
|
+
``compute()``. This mirrors the legacy ``Arg`` descriptor path.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Annotated
|
|
15
|
+
|
|
16
|
+
import pyarrow as pa
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
from vgi.arguments import Arguments, ArgumentValidationError, ConstParam, Param, Returns
|
|
20
|
+
from vgi.protocol import BindRequest, FunctionType
|
|
21
|
+
from vgi.scalar_function import BindResult, ScalarFunction
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class _Measure(ScalarFunction):
|
|
25
|
+
class Meta:
|
|
26
|
+
name = "measure"
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def on_bind(cls, params: object) -> BindResult:
|
|
30
|
+
return BindResult(pa.string())
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def compute(
|
|
34
|
+
cls,
|
|
35
|
+
unit: Annotated[str, ConstParam("Output unit", choices=["mm", "cm", "m"])],
|
|
36
|
+
precision: Annotated[int, ConstParam("Decimals", ge=0, le=10)],
|
|
37
|
+
value: Annotated[pa.DoubleArray, Param(doc="Value")],
|
|
38
|
+
) -> Annotated[pa.StringArray, Returns()]:
|
|
39
|
+
return pa.array([str(value)], type=pa.string())
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _bind(unit: str, precision: int) -> None:
|
|
43
|
+
_Measure.bind(
|
|
44
|
+
BindRequest(
|
|
45
|
+
function_name="measure",
|
|
46
|
+
arguments=Arguments(positional=(pa.scalar(unit), pa.scalar(precision))),
|
|
47
|
+
function_type=FunctionType.SCALAR,
|
|
48
|
+
input_schema=pa.schema([("value", pa.float64())]),
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_valid_const_values_bind() -> None:
|
|
54
|
+
"""A const value inside every declared constraint binds cleanly."""
|
|
55
|
+
_bind("cm", 2)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_choice_violation_raises_at_bind() -> None:
|
|
59
|
+
"""A const value outside the choice set fails at bind."""
|
|
60
|
+
with pytest.raises(ArgumentValidationError) as exc:
|
|
61
|
+
_bind("xx", 2)
|
|
62
|
+
assert "unit" in str(exc.value)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_range_violation_raises_at_bind() -> None:
|
|
66
|
+
"""A const value outside the numeric range fails at bind."""
|
|
67
|
+
with pytest.raises(ArgumentValidationError) as exc:
|
|
68
|
+
_bind("cm", 99)
|
|
69
|
+
assert "precision" in str(exc.value)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Conformance stub for ``vgi/test/sql/integration/container/``."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from tests.conformance._stub import skip_area
|
|
8
|
+
|
|
9
|
+
skip_area(
|
|
10
|
+
"container",
|
|
11
|
+
[
|
|
12
|
+
"errors.test",
|
|
13
|
+
"shared_http.test",
|
|
14
|
+
"shared_tcp.test",
|
|
15
|
+
"sklearn_smoke.test",
|
|
16
|
+
],
|
|
17
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Conformance stub for ``vgi/test/sql/integration/copy_from/``."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from tests.conformance._stub import skip_area
|
|
8
|
+
|
|
9
|
+
skip_area(
|
|
10
|
+
"copy_from",
|
|
11
|
+
[
|
|
12
|
+
"basic.test",
|
|
13
|
+
"options.test",
|
|
14
|
+
"secrets.test",
|
|
15
|
+
],
|
|
16
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Conformance stub for ``vgi/test/sql/integration/copy_to/``."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from tests.conformance._stub import skip_area
|
|
8
|
+
|
|
9
|
+
skip_area(
|
|
10
|
+
"copy_to",
|
|
11
|
+
[
|
|
12
|
+
"basic.test",
|
|
13
|
+
"failure.test",
|
|
14
|
+
"options.test",
|
|
15
|
+
"ordered.test",
|
|
16
|
+
"parallel.test",
|
|
17
|
+
"secrets.test",
|
|
18
|
+
"tmp_file.test",
|
|
19
|
+
"types.test",
|
|
20
|
+
],
|
|
21
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Conformance stub for ``vgi/test/sql/integration/github/``."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from tests.conformance._stub import skip_area
|
|
8
|
+
|
|
9
|
+
skip_area(
|
|
10
|
+
"github",
|
|
11
|
+
[
|
|
12
|
+
"download.test",
|
|
13
|
+
"errors.test",
|
|
14
|
+
],
|
|
15
|
+
)
|
|
@@ -9,10 +9,13 @@ from tests.conformance._stub import skip_area
|
|
|
9
9
|
skip_area(
|
|
10
10
|
"secret",
|
|
11
11
|
[
|
|
12
|
+
"secret_aggregate.test",
|
|
13
|
+
"secret_function_backed_table.test",
|
|
12
14
|
"secret_no_secret.test",
|
|
13
15
|
"secret_registration.test",
|
|
14
16
|
"secret_scalar.test",
|
|
15
17
|
"secret_scoped.test",
|
|
16
18
|
"secret_table_function.test",
|
|
19
|
+
"secret_table_in_out.test",
|
|
17
20
|
],
|
|
18
21
|
)
|
|
@@ -13,7 +13,14 @@ import pytest
|
|
|
13
13
|
from vgi_rpc import ArrowSerializableDataclass, ArrowType
|
|
14
14
|
|
|
15
15
|
from vgi.aggregate_function import AggregateFunction
|
|
16
|
-
from vgi.arguments import
|
|
16
|
+
from vgi.arguments import (
|
|
17
|
+
Arguments,
|
|
18
|
+
ArgumentValidationError,
|
|
19
|
+
ConstParam,
|
|
20
|
+
Param,
|
|
21
|
+
Returns,
|
|
22
|
+
validate_const_arg_constraints,
|
|
23
|
+
)
|
|
17
24
|
from vgi.table_function import ProcessParams
|
|
18
25
|
|
|
19
26
|
# ---------------------------------------------------------------------------
|
|
@@ -199,6 +206,49 @@ class TestInitSubclassParameterExtraction:
|
|
|
199
206
|
assert arg.arrow_type == pa.float64()
|
|
200
207
|
assert arg.const is True
|
|
201
208
|
|
|
209
|
+
def test_const_param_constraints_enforced(self) -> None:
|
|
210
|
+
"""A ConstParam's declared constraints are enforced at bind (same as scalar)."""
|
|
211
|
+
|
|
212
|
+
class BoundedAgg(AggregateFunction[SimpleState]):
|
|
213
|
+
class Meta:
|
|
214
|
+
name = "test_bounded_agg"
|
|
215
|
+
|
|
216
|
+
@classmethod
|
|
217
|
+
def initial_state(cls, params: ProcessParams[Any]) -> SimpleState:
|
|
218
|
+
return SimpleState()
|
|
219
|
+
|
|
220
|
+
@classmethod
|
|
221
|
+
def update(
|
|
222
|
+
cls,
|
|
223
|
+
states: dict[int, SimpleState],
|
|
224
|
+
group_ids: pa.Int64Array,
|
|
225
|
+
value: Annotated[pa.DoubleArray, Param(doc="Values")],
|
|
226
|
+
mode: Annotated[str, ConstParam("Mode", choices=["min", "max"])] = "min",
|
|
227
|
+
) -> None:
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
@classmethod
|
|
231
|
+
def combine(cls, source: SimpleState, target: SimpleState, params: ProcessParams[Any]) -> SimpleState:
|
|
232
|
+
return SimpleState()
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def finalize(
|
|
236
|
+
cls,
|
|
237
|
+
group_ids: pa.Int64Array,
|
|
238
|
+
states: dict[int, SimpleState],
|
|
239
|
+
params: ProcessParams[Any],
|
|
240
|
+
) -> Annotated[pa.RecordBatch, Returns(pa.float64())]:
|
|
241
|
+
return pa.record_batch({"result": pa.array([], type=pa.float64())})
|
|
242
|
+
|
|
243
|
+
# The ConstParam carries its choices (threaded via _const_param_to_arg)...
|
|
244
|
+
const_params = BoundedAgg._const_params
|
|
245
|
+
assert const_params["mode"].choices == ["min", "max"]
|
|
246
|
+
# ...and the shared bind-time validator (called by aggregate_bind) enforces it.
|
|
247
|
+
validate_const_arg_constraints(const_params, Arguments(positional=(pa.scalar("max"),)))
|
|
248
|
+
with pytest.raises(ArgumentValidationError) as exc:
|
|
249
|
+
validate_const_arg_constraints(const_params, Arguments(positional=(pa.scalar("median"),)))
|
|
250
|
+
assert "mode" in str(exc.value)
|
|
251
|
+
|
|
202
252
|
def test_const_param_phases(self) -> None:
|
|
203
253
|
"""ConstParam phase values are stored correctly."""
|
|
204
254
|
|
|
@@ -12,13 +12,18 @@ import pytest
|
|
|
12
12
|
from vgi.argument_spec import (
|
|
13
13
|
VGI_ARG_KEY,
|
|
14
14
|
VGI_ARG_NAMED,
|
|
15
|
+
VGI_CHOICES_KEY,
|
|
16
|
+
VGI_DEFAULT_KEY,
|
|
15
17
|
VGI_DOC_KEY,
|
|
18
|
+
VGI_PATTERN_KEY,
|
|
19
|
+
VGI_RANGE_KEY,
|
|
16
20
|
VGI_TYPE_ANY,
|
|
17
21
|
VGI_TYPE_KEY,
|
|
18
22
|
VGI_TYPE_TABLE,
|
|
19
23
|
VGI_VARARGS_KEY,
|
|
20
24
|
VGI_VARARGS_TRUE,
|
|
21
25
|
ArgumentSpec,
|
|
26
|
+
_format_range,
|
|
22
27
|
argument_specs_to_schema,
|
|
23
28
|
extract_argument_specs,
|
|
24
29
|
schema_to_argument_specs,
|
|
@@ -722,3 +727,99 @@ class TestArgumentDoc:
|
|
|
722
727
|
specs = {s.name: s for s in extract_argument_specs(FunctionWithDocs)}
|
|
723
728
|
assert specs["unit"].doc == "Unit string, e.g. 'mi'"
|
|
724
729
|
assert specs["scale"].doc == ""
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
class TestArgumentConstraints:
|
|
733
|
+
"""Per-argument constraint metadata (choices / range / pattern / default)."""
|
|
734
|
+
|
|
735
|
+
def test_format_range_notation(self) -> None:
|
|
736
|
+
"""Interval notation covers inclusive, exclusive, open, and empty bounds."""
|
|
737
|
+
assert _format_range(0, 100, None, None) == "[0, 100]"
|
|
738
|
+
assert _format_range(None, None, 0, None) == "(0, +inf)"
|
|
739
|
+
assert _format_range(1, None, None, 10) == "[1, 10)"
|
|
740
|
+
assert _format_range(None, 5, None, None) == "(-inf, 5]"
|
|
741
|
+
assert _format_range(None, None, None, None) is None
|
|
742
|
+
|
|
743
|
+
def test_writer_emits_constraint_keys(self) -> None:
|
|
744
|
+
"""A constrained spec writes value-encoded constraint metadata keys."""
|
|
745
|
+
spec = ArgumentSpec(
|
|
746
|
+
name="unit",
|
|
747
|
+
position="unit",
|
|
748
|
+
arrow_type=pa.utf8(),
|
|
749
|
+
default_json='"mm"',
|
|
750
|
+
choices_json='["mm", "cm", "m"]',
|
|
751
|
+
range_notation="[0, 100]",
|
|
752
|
+
pattern="^[A-Z]{2}$",
|
|
753
|
+
)
|
|
754
|
+
meta = argument_specs_to_schema([spec])[0].metadata
|
|
755
|
+
assert meta[VGI_DEFAULT_KEY] == b'"mm"'
|
|
756
|
+
assert meta[VGI_CHOICES_KEY] == b'["mm", "cm", "m"]'
|
|
757
|
+
assert meta[VGI_RANGE_KEY] == b"[0, 100]"
|
|
758
|
+
assert meta[VGI_PATTERN_KEY] == b"^[A-Z]{2}$"
|
|
759
|
+
|
|
760
|
+
def test_unconstrained_arg_omits_keys(self) -> None:
|
|
761
|
+
"""An arg without constraints yields none of the constraint keys."""
|
|
762
|
+
meta = argument_specs_to_schema([ArgumentSpec(name="v", position=0, arrow_type=pa.int64())])[0].metadata
|
|
763
|
+
for key in (VGI_DEFAULT_KEY, VGI_CHOICES_KEY, VGI_RANGE_KEY, VGI_PATTERN_KEY):
|
|
764
|
+
assert meta is None or key not in meta
|
|
765
|
+
|
|
766
|
+
def test_constraints_round_trip(self) -> None:
|
|
767
|
+
"""Constraint metadata survives the schema round-trip."""
|
|
768
|
+
spec = ArgumentSpec(
|
|
769
|
+
name="mode",
|
|
770
|
+
position="mode",
|
|
771
|
+
arrow_type=pa.utf8(),
|
|
772
|
+
default_json='"add"',
|
|
773
|
+
choices_json='["add", "mul"]',
|
|
774
|
+
range_notation="[0, 10)",
|
|
775
|
+
pattern="^x+$",
|
|
776
|
+
)
|
|
777
|
+
out = schema_to_argument_specs(argument_specs_to_schema([spec]))[0]
|
|
778
|
+
assert out.default_json == '"add"'
|
|
779
|
+
assert out.choices_json == '["add", "mul"]'
|
|
780
|
+
assert out.range_notation == "[0, 10)"
|
|
781
|
+
assert out.pattern == "^x+$"
|
|
782
|
+
|
|
783
|
+
def test_extract_constraints_from_arg(self) -> None:
|
|
784
|
+
"""extract_argument_specs reads choices/default/bounds/pattern off Arg."""
|
|
785
|
+
|
|
786
|
+
class FunctionWithConstraints(TableInOutFunction): # type: ignore[type-arg]
|
|
787
|
+
mode: str = Arg[str]( # type: ignore[assignment]
|
|
788
|
+
"mode", doc="Blend mode", choices=["add", "mul"], default="add"
|
|
789
|
+
)
|
|
790
|
+
level: int = Arg[int](0, ge=1, le=10) # type: ignore[assignment]
|
|
791
|
+
code: str = Arg[str](1, pattern="^[A-Z]{2}$") # type: ignore[assignment]
|
|
792
|
+
|
|
793
|
+
specs = {s.name: s for s in extract_argument_specs(FunctionWithConstraints)}
|
|
794
|
+
assert specs["mode"].choices_json == '["add", "mul"]'
|
|
795
|
+
assert specs["mode"].default_json == '"add"'
|
|
796
|
+
assert specs["level"].range_notation == "[1, 10]"
|
|
797
|
+
assert specs["code"].pattern == "^[A-Z]{2}$"
|
|
798
|
+
|
|
799
|
+
def test_const_param_constraints_surface(self) -> None:
|
|
800
|
+
"""The modern Param/ConstParam API surfaces choices / range / pattern."""
|
|
801
|
+
from vgi.arguments import ConstParam, Param, Returns
|
|
802
|
+
from vgi.scalar_function import ScalarFunction
|
|
803
|
+
|
|
804
|
+
class ConstrainedScalar(ScalarFunction):
|
|
805
|
+
class Meta:
|
|
806
|
+
name = "constrained_scalar"
|
|
807
|
+
description = "constraint demo"
|
|
808
|
+
|
|
809
|
+
@classmethod
|
|
810
|
+
def compute(
|
|
811
|
+
cls,
|
|
812
|
+
unit: Annotated[str, ConstParam("Output unit", choices=["mm", "cm", "m"])],
|
|
813
|
+
precision: Annotated[int, ConstParam("Decimals", ge=0, le=10)],
|
|
814
|
+
code: Annotated[str, ConstParam("Label code", pattern="^[A-Z]{2}$")],
|
|
815
|
+
value: Annotated[pa.DoubleArray, Param(doc="Value")],
|
|
816
|
+
) -> Annotated[pa.StringArray, Returns()]:
|
|
817
|
+
return value # never executed — the test only reads arg specs
|
|
818
|
+
|
|
819
|
+
specs = {s.name: s for s in extract_argument_specs(ConstrainedScalar)}
|
|
820
|
+
assert specs["unit"].choices_json == '["mm", "cm", "m"]'
|
|
821
|
+
assert specs["precision"].range_notation == "[0, 10]"
|
|
822
|
+
assert specs["code"].pattern == "^[A-Z]{2}$"
|
|
823
|
+
# The columnar Param carries no constraints.
|
|
824
|
+
assert specs["value"].choices_json is None
|
|
825
|
+
assert specs["value"].range_notation is None
|
|
@@ -9,8 +9,14 @@ import types
|
|
|
9
9
|
|
|
10
10
|
import pyarrow as pa
|
|
11
11
|
|
|
12
|
-
from vgi._test_fixtures.copy_to import
|
|
12
|
+
from vgi._test_fixtures.copy_to import (
|
|
13
|
+
ExampleLinesCopyToArgs,
|
|
14
|
+
ExampleLinesCopyToFunction,
|
|
15
|
+
SecretLinesCopyToArgs,
|
|
16
|
+
SecretLinesCopyToFunction,
|
|
17
|
+
)
|
|
13
18
|
from vgi._test_fixtures.worker import ExampleCatalog
|
|
19
|
+
from vgi.table_function import ResolvedSecrets, SecretsAccessor
|
|
14
20
|
|
|
15
21
|
SCHEMA = pa.schema([("a", pa.int64()), ("b", pa.string())])
|
|
16
22
|
|
|
@@ -100,6 +106,81 @@ def test_close_empty_input_with_header_writes_header_only() -> None:
|
|
|
100
106
|
assert _read(out_name) == "a,b\n"
|
|
101
107
|
|
|
102
108
|
|
|
109
|
+
# ---------------------------------------------------------------------------
|
|
110
|
+
# Secret forwarding (CopyToFunction.on_secrets hook)
|
|
111
|
+
# ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _secret_params(store: _Store, secrets: ResolvedSecrets) -> types.SimpleNamespace:
|
|
115
|
+
"""A TableBufferingParams-shaped stub carrying resolved secrets for write/close."""
|
|
116
|
+
bind_call = types.SimpleNamespace(input_schema=SCHEMA)
|
|
117
|
+
init_call = types.SimpleNamespace(bind_call=bind_call)
|
|
118
|
+
return types.SimpleNamespace(storage=store, init_call=init_call, execution_id=b"x", secrets=secrets)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_on_secrets_requests_destination_scoped_secret() -> None:
|
|
122
|
+
"""on_secrets() registers a pending lookup scoped to the COPY destination path."""
|
|
123
|
+
accessor = SecretsAccessor(None) # nothing resolved yet → first-call behavior
|
|
124
|
+
params = types.SimpleNamespace(
|
|
125
|
+
args=SecretLinesCopyToArgs(), # default secret_type='vgi_example'
|
|
126
|
+
bind_call=types.SimpleNamespace(copy_to=types.SimpleNamespace(file_path="s3://bucket/out.bin")),
|
|
127
|
+
secrets=accessor,
|
|
128
|
+
)
|
|
129
|
+
SecretLinesCopyToFunction.on_secrets(params)
|
|
130
|
+
# The hook must have asked the framework for a scoped two-phase resolution.
|
|
131
|
+
assert accessor.needs_resolution
|
|
132
|
+
pending = accessor.pending_lookups
|
|
133
|
+
assert len(pending) == 1
|
|
134
|
+
assert pending[0].secret_type == "vgi_example"
|
|
135
|
+
assert pending[0].scope == "s3://bucket/out.bin"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_close_forwards_resolved_secret_api_key() -> None:
|
|
139
|
+
"""close() writes the resolved (destination-scoped) secret's api_key + row count."""
|
|
140
|
+
store = _Store()
|
|
141
|
+
out_name = _tmp_path()
|
|
142
|
+
secrets = ResolvedSecrets(
|
|
143
|
+
{
|
|
144
|
+
"writer_creds": {
|
|
145
|
+
"type": pa.scalar("vgi_example"),
|
|
146
|
+
"scope": pa.scalar(out_name),
|
|
147
|
+
"api_key": pa.scalar("WRITER_KEY"),
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
params = _secret_params(store, secrets)
|
|
152
|
+
opts = SecretLinesCopyToArgs()
|
|
153
|
+
|
|
154
|
+
SecretLinesCopyToFunction.write(
|
|
155
|
+
batch=pa.record_batch({"a": [1, 2], "b": ["x", "y"]}, schema=SCHEMA),
|
|
156
|
+
options=opts,
|
|
157
|
+
file_path=out_name,
|
|
158
|
+
params=params,
|
|
159
|
+
)
|
|
160
|
+
n = SecretLinesCopyToFunction.close(options=opts, file_path=out_name, params=params)
|
|
161
|
+
assert n == 2
|
|
162
|
+
assert _read(out_name) == "api_key=WRITER_KEY\nrows=2\n"
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_close_writes_none_when_secret_absent() -> None:
|
|
166
|
+
"""A genuinely missing secret resolves to 'NONE' (silent miss, not an error)."""
|
|
167
|
+
store = _Store()
|
|
168
|
+
out_name = _tmp_path()
|
|
169
|
+
params = _secret_params(store, ResolvedSecrets())
|
|
170
|
+
opts = SecretLinesCopyToArgs()
|
|
171
|
+
n = SecretLinesCopyToFunction.close(options=opts, file_path=out_name, params=params)
|
|
172
|
+
assert n == 0
|
|
173
|
+
assert _read(out_name) == "api_key=NONE\nrows=0\n"
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def test_catalog_advertises_secret_lines_out_format() -> None:
|
|
177
|
+
"""The secret-forwarding writer is advertised like any other COPY TO format."""
|
|
178
|
+
formats = ExampleCatalog().copy_from_formats(attach_opaque_data=b"", transaction_opaque_data=None)
|
|
179
|
+
by = {(f.direction, f.format_name): f for f in formats}
|
|
180
|
+
assert ("to", "secret_lines_out") in by
|
|
181
|
+
assert by[("to", "secret_lines_out")].handler == "secret_lines_writer"
|
|
182
|
+
|
|
183
|
+
|
|
103
184
|
def test_catalog_advertises_copy_to_format() -> None:
|
|
104
185
|
"""The example catalog advertises example_lines_out with direction='to'."""
|
|
105
186
|
formats = ExampleCatalog().copy_from_formats(attach_opaque_data=b"", transaction_opaque_data=None)
|