vgi-python 0.23.0__tar.gz → 0.25.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.23.0 → vgi_python-0.25.0}/.github/workflows/ci.yml +5 -5
- {vgi_python-0.23.0 → vgi_python-0.25.0}/.github/workflows/docs.yml +1 -1
- {vgi_python-0.23.0 → vgi_python-0.25.0}/.github/workflows/integration.yml +4 -4
- {vgi_python-0.23.0 → vgi_python-0.25.0}/CLAUDE.md +96 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/PKG-INFO +6 -3
- vgi_python-0.25.0/docs/global-functions.md +88 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/pyproject.toml +13 -3
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_declarative.py +148 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_serialization.py +3 -1
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_directory_parity.py +13 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_protocol_version.py +3 -3
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/landing/describe.expected.json +39 -1
- vgi_python-0.25.0/tests/table/test_wide_columns.py +84 -0
- vgi_python-0.25.0/tests/test_call_state_attach.py +346 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_protocol_classes.py +90 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_serve.py +197 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_setting_secret_annotations.py +47 -2
- vgi_python-0.25.0/tests/test_signing_key.py +96 -0
- vgi_python-0.25.0/tests/test_worker_cli.py +200 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/uv.lock +164 -32
- vgi_python-0.25.0/vgi/_test_fixtures/global_functions.py +316 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/http_server.py +9 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/_common.py +46 -3
- vgi_python-0.25.0/vgi/_test_fixtures/table/wide.py +113 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/worker.py +27 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/auth.py +13 -1
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/catalog/catalog_interface.py +39 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/catalog/descriptors.py +79 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/function.py +54 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/function_storage.py +71 -1
- vgi_python-0.25.0/vgi/logging_config.py +319 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/meta_worker.py +31 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/protocol.py +261 -110
- vgi_python-0.25.0/vgi/protocol_version.txt +1 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/serve.py +562 -18
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/table_function.py +147 -53
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/worker.py +221 -15
- vgi_python-0.23.0/tests/test_worker_cli.py +0 -112
- vgi_python-0.23.0/vgi/logging_config.py +0 -161
- vgi_python-0.23.0/vgi/protocol_version.txt +0 -1
- {vgi_python-0.23.0 → vgi_python-0.25.0}/.gitattributes +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/.github/dependabot.yml +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/.github/workflows/release.yml +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/.gitignore +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/.python-version +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/.vale.ini +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/LICENSE +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/README.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/SECURITY.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/ci/README.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/ci/run-integration.sh +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/arguments.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/auth.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/catalogs.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/client.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/exceptions.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/filters.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/functions.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/http.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/index.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/metadata.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/observability.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/storage.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/transactor.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/api/worker.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/argument-serialization.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/logo.png +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/assets/social-card.png +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/authentication.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/catalog-interface.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/cli.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/column-statistics.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/concepts/index.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/contributing-docs.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/generator-api.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/how-to/index.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/index.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/lifecycle.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/metadata.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/overrides/main.html +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/robots.txt +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/shared-storage.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/tutorial/index.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/tutorial/table.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/docs/vgi-logo.png +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/examples/calc_worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/examples/filter_worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/examples/row_count_worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/examples/sum_worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/mkdocs.yml +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/packages/vgi-fixtures/pyproject.toml +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/scripts/measure_startup.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/test-data/generate.sh +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_example_worker_catalog.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_required_filters.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/client/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/client/test_cli.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_const_param_enforcement.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_container.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_copy_from.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_copy_to.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_github.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_secret.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/conftest.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/landing/_checker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/landing/describe.schema.json +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/test_blended_metadata.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_aggregate_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_argument_spec.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_auth.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_cache_control.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_copy_to_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_docstrings.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_exceptions.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_function_storage.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_landing_conformance.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_metadata.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_otel.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_schema_scoped_functions.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_tcp_transport.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_union_argument.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/test_worker_page.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_duckdb.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/same_name.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/companion.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/copy_from.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/copy_to.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/formatting.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/same_name.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/cache.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/filters.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/same_name_cached.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table_in_out.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table_in_out_same_name.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/twin_catalogs.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/aggregate_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/argument_spec.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/arguments.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/attach_header.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/cache_control.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/catalog/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/client/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/client/catalog_mixin.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/client/cli.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/client/client.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/_common.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/cpp_request_builders.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/copy_from_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/copy_to_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/exceptions.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/http/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/http/describe_json.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/http/landing.html +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/http/landing_page.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/invocation.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/metadata.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/otel.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/profiling.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/py.typed +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/scalar_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/schema_utils.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/secret_service.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/table_filter_pushdown.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/transactor/client.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/vgi/transactor/server.py +0 -0
- {vgi_python-0.23.0 → vgi_python-0.25.0}/wrangler.jsonc +0 -0
|
@@ -83,8 +83,8 @@ jobs:
|
|
|
83
83
|
- name: Install uv
|
|
84
84
|
uses: astral-sh/setup-uv@v8.2.0
|
|
85
85
|
|
|
86
|
-
- name: Set up Python 3.
|
|
87
|
-
run: uv python install 3.
|
|
86
|
+
- name: Set up Python 3.14
|
|
87
|
+
run: uv python install 3.14
|
|
88
88
|
|
|
89
89
|
- name: Install dependencies
|
|
90
90
|
run: uv sync --all-extras --group docs
|
|
@@ -141,8 +141,8 @@ jobs:
|
|
|
141
141
|
- name: Install uv
|
|
142
142
|
uses: astral-sh/setup-uv@v8.2.0
|
|
143
143
|
|
|
144
|
-
- name: Set up Python 3.
|
|
145
|
-
run: uv python install 3.
|
|
144
|
+
- name: Set up Python 3.14
|
|
145
|
+
run: uv python install 3.14
|
|
146
146
|
|
|
147
147
|
- name: Install test dependencies
|
|
148
148
|
run: |
|
|
@@ -150,7 +150,7 @@ jobs:
|
|
|
150
150
|
# local vgi-rpc checkout with the s3/external extras plus boto3.
|
|
151
151
|
# Subsequent uv run calls must pass --no-sync or uv re-syncs the
|
|
152
152
|
# env to the lockfile and drops the overlay.
|
|
153
|
-
uv sync --all-extras --python 3.
|
|
153
|
+
uv sync --all-extras --python 3.14
|
|
154
154
|
uv pip install './vgi-rpc[http,s3,external]' boto3
|
|
155
155
|
|
|
156
156
|
- name: Create test bucket in LocalStack
|
|
@@ -32,7 +32,7 @@ env:
|
|
|
32
32
|
VGI_REF: main
|
|
33
33
|
# The Haybarn release providing the prebuilt haybarn-unittest binary. Must be
|
|
34
34
|
# ABI-compatible with the community-published vgi extension.
|
|
35
|
-
HAYBARN_RELEASE: haybarn-v1.5.
|
|
35
|
+
HAYBARN_RELEASE: haybarn-v1.5.5-rc1
|
|
36
36
|
# CI is not a user: its ATTACHes should not land in Query.Farm's adoption data.
|
|
37
37
|
# The vgi extension fires a telemetry event per successful ATTACH, and this
|
|
38
38
|
# suite performs several hundred of them per lane, so opting out keeps the
|
|
@@ -78,11 +78,11 @@ jobs:
|
|
|
78
78
|
- name: Install uv
|
|
79
79
|
uses: astral-sh/setup-uv@v8.2.0
|
|
80
80
|
|
|
81
|
-
- name: Set up Python 3.
|
|
82
|
-
run: uv python install 3.
|
|
81
|
+
- name: Set up Python 3.14
|
|
82
|
+
run: uv python install 3.14
|
|
83
83
|
|
|
84
84
|
- name: Install dependencies (vgi + fixtures + all extras)
|
|
85
|
-
run: uv sync --all-extras --python 3.
|
|
85
|
+
run: uv sync --all-extras --python 3.14
|
|
86
86
|
|
|
87
87
|
- name: Download haybarn-unittest
|
|
88
88
|
run: |
|
|
@@ -297,6 +297,11 @@ vgi-client --input data.parquet --function sum_all_columns --worker vgi-fixture-
|
|
|
297
297
|
| `VGI_OAUTH_DEVICE_CODE_CLIENT_ID` | Client ID for device-code flow (optional, URL-safe chars only) |
|
|
298
298
|
| `VGI_OAUTH_DEVICE_CODE_CLIENT_SECRET` | Client secret for device-code flow (optional, URL-safe chars only) |
|
|
299
299
|
| `VGI_OAUTH_USE_ID_TOKEN` | When `1`/`true`/`yes`, clients use OIDC `id_token` as Bearer instead of `access_token` |
|
|
300
|
+
| `VGI_INTROSPECT_PRINCIPALS` | Comma-separated principals permitted to call `__introspect_token__`. Required — no permissive default — whenever the worker implements `resolve_token()` (see below) |
|
|
301
|
+
| `VGI_INTROSPECT_RATE_LIMIT` | Introspection requests allowed per caller per second (default 20) |
|
|
302
|
+
| `VGI_WORKER_ACCESS_LOG_SAMPLE` | Fraction of *successful* calls to keep in the access log, `0.0`–`1.0`. Errors are always kept; the decision is per call, so every record of one stream shares a fate |
|
|
303
|
+
| `VGI_WORKER_ACCESS_LOG_ASYNC` | When `1`/`true`/`yes`, emit access-log records from a listener thread. Bounded queue; full means drop, and a crash loses whatever is queued |
|
|
304
|
+
| `VGI_WORKER_ACCESS_LOG_QUEUE_SIZE` | Bound on the async access-log queue (default 10000) |
|
|
300
305
|
| `VGI_OTEL_ENABLED` | Enable OpenTelemetry instrumentation (`1`/`true`/`yes`) |
|
|
301
306
|
| `VGI_OTEL_CUSTOM_ATTRIBUTES` | Comma-separated `key=value` pairs for custom span/metric attributes |
|
|
302
307
|
| `VGI_OTEL_CLAIM_ATTRIBUTES` | Comma-separated `claim_key=span_attr_name` pairs for claim extraction |
|
|
@@ -392,6 +397,96 @@ Set `SENTRY_DSN` (and install `vgi[sentry]`) to forward unhandled exceptions to
|
|
|
392
397
|
|
|
393
398
|
The same enrichment applies to OTel spans when `VGI_OTEL_ENABLED=1` — both backends read from the same `VgiTracer.set_current_span_attributes()` call sites in `vgi/otel.py`. Either, neither, or both can be active in a process.
|
|
394
399
|
|
|
400
|
+
### Serving Behind a Load Balancer
|
|
401
|
+
|
|
402
|
+
`--max-externalized-response-bytes` caps a single *externalized* response — the
|
|
403
|
+
payload uploaded to blob storage and replaced on the wire by a pointer. Set it
|
|
404
|
+
to whatever the load balancer, API gateway, or object-store policy in front of
|
|
405
|
+
the worker will actually carry.
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
vgi-serve my.worker:MyWorker --http --max-externalized-response-bytes 67108864
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Unlike `--max-stream-response-bytes`, which governs the wire and is *soft* for
|
|
412
|
+
producer streams (a continuation token carries the overshoot to the next turn),
|
|
413
|
+
this cap is **hard on every method type with no continuation escape** — bytes
|
|
414
|
+
already uploaded cannot be un-uploaded. Default is no cap. When set, the value
|
|
415
|
+
is advertised as `VGI-Max-Externalized-Response-Bytes` so clients can size their
|
|
416
|
+
own expectations.
|
|
417
|
+
|
|
418
|
+
Also available on `Worker.main --http` and the fixture server. Under
|
|
419
|
+
`--server granian` it travels to worker processes through `export_serve_config`.
|
|
420
|
+
|
|
421
|
+
### Token Introspection (`resolve_token`)
|
|
422
|
+
|
|
423
|
+
A worker may optionally expose `POST {prefix}/__introspect_token__`, which
|
|
424
|
+
resolves an opaque bearer credential to a principal — for a reverse proxy that
|
|
425
|
+
terminates the only public listener and must know the caller's identity before
|
|
426
|
+
it can authorize anything.
|
|
427
|
+
|
|
428
|
+
```python
|
|
429
|
+
from vgi.auth import AuthUnavailableError, TokenIdentity
|
|
430
|
+
from vgi.worker import Worker
|
|
431
|
+
|
|
432
|
+
class MyWorker(Worker):
|
|
433
|
+
functions = [...]
|
|
434
|
+
|
|
435
|
+
@classmethod
|
|
436
|
+
def resolve_token(cls, token: str) -> TokenIdentity | None:
|
|
437
|
+
try:
|
|
438
|
+
row = api_keys.lookup(token) # your own store
|
|
439
|
+
except ConnectionError as exc:
|
|
440
|
+
# "I could not find out" — 503 + Retry-After, not 401.
|
|
441
|
+
raise AuthUnavailableError(str(exc)) from exc
|
|
442
|
+
if row is None:
|
|
443
|
+
return None # "the credential is unknown"
|
|
444
|
+
return TokenIdentity(principal=row.principal, token_name=row.label)
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**The route does not exist until `resolve_token` is overridden** — absent, not
|
|
448
|
+
routed-and-refusing. That is what keeps a dependency upgrade from growing a
|
|
449
|
+
credential-to-identity oracle on every existing worker.
|
|
450
|
+
|
|
451
|
+
Enabling it also requires an allowlist of principals permitted to ask, via
|
|
452
|
+
`--introspect-principals` or `VGI_INTROSPECT_PRINCIPALS`. There is no permissive
|
|
453
|
+
default and a worker that overrides the hook without one **refuses to start**:
|
|
454
|
+
authenticating and introspecting are different capabilities, and "any
|
|
455
|
+
authenticated caller" lets any user resolve any other user's credential to its
|
|
456
|
+
owner. `--introspect-rate-limit` (default 20/caller/second) bounds, rather than
|
|
457
|
+
closes, the oracle an allowlisted-but-compromised caller still has.
|
|
458
|
+
|
|
459
|
+
Return `None` for "the store answered and this credential is unknown"; raise
|
|
460
|
+
`AuthUnavailableError` for "the answer is not knowable". A caller that
|
|
461
|
+
negative-caches the first must not cache the second. Never return claims — a
|
|
462
|
+
pass-through claims field would let a worker choose its caller's tenant routing
|
|
463
|
+
and policy branch.
|
|
464
|
+
|
|
465
|
+
`AuthUnavailableError` is worth reaching for in any custom `authenticate`
|
|
466
|
+
callback too, not just here: it is deliberately **not** a `ValueError`, because
|
|
467
|
+
`chain_authenticate` advances to the next authenticator on `ValueError` — so a
|
|
468
|
+
sidecar outage raised as one is read as "not my credential, try the next" and
|
|
469
|
+
ends up a 401 from the end of the chain, restarting every session in the fleet
|
|
470
|
+
over a thirty-second blip.
|
|
471
|
+
|
|
472
|
+
### Access Log Sampling and Async Emission
|
|
473
|
+
|
|
474
|
+
`--access-log-sample 0.05` keeps 5% of *successful* calls. Errors are always
|
|
475
|
+
kept, and the decision is made per call so every record belonging to one stream
|
|
476
|
+
shares a fate. `--access-log-async` moves formatting and writing to a listener
|
|
477
|
+
thread so disk latency stays off the request path; the queue is bounded
|
|
478
|
+
(`--access-log-queue-size`, default 10000), full means drop, and the next record
|
|
479
|
+
through carries `dropped_records` so a gap is never silent. A crash loses
|
|
480
|
+
whatever is still queued — that is the trade.
|
|
481
|
+
|
|
482
|
+
Both apply only to `vgi_rpc.access`, which gets its own handler for the purpose.
|
|
483
|
+
They are deliberately not applied to the diagnostic loggers: dropping half of a
|
|
484
|
+
traceback is not a saving.
|
|
485
|
+
|
|
486
|
+
**Trace correlation needs no configuration.** vgi-rpc reads whatever span is
|
|
487
|
+
current when it emits a record, so `trace_id` / `span_id` appear on every access
|
|
488
|
+
record as soon as `VGI_OTEL_ENABLED=1`.
|
|
489
|
+
|
|
395
490
|
### Key Constraints for Scalar Functions:
|
|
396
491
|
- **1:1 row mapping**: Output must have exactly the same number of rows as input
|
|
397
492
|
- **Single column output**: Output schema has exactly one column named "result"
|
|
@@ -508,4 +603,5 @@ The table schema is automatically derived from the function's `output_schema`:
|
|
|
508
603
|
- **Function lifecycle**: `docs/lifecycle.md`
|
|
509
604
|
- **Generator API (advanced)**: `docs/generator-api.md`
|
|
510
605
|
- **Catalog interface**: `docs/catalog-interface.md`
|
|
606
|
+
- **Global functions (system.main)**: `docs/global-functions.md`
|
|
511
607
|
- **Shared storage backends**: `docs/shared-storage.md`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.25.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
|
|
@@ -162,7 +162,7 @@ Requires-Dist: httpx>=0.24
|
|
|
162
162
|
Requires-Dist: platformdirs
|
|
163
163
|
Requires-Dist: pyarrow
|
|
164
164
|
Requires-Dist: typer>=0.9
|
|
165
|
-
Requires-Dist: vgi-rpc>=0.
|
|
165
|
+
Requires-Dist: vgi-rpc>=0.39.1
|
|
166
166
|
Provides-Extra: azure
|
|
167
167
|
Requires-Dist: azure-identity>=1.16.0; extra == 'azure'
|
|
168
168
|
Requires-Dist: pymssql>=2.3.0; extra == 'azure'
|
|
@@ -190,8 +190,11 @@ Provides-Extra: duckdb
|
|
|
190
190
|
Requires-Dist: duckdb>=1.5.3; extra == 'duckdb'
|
|
191
191
|
Provides-Extra: fixtures
|
|
192
192
|
Requires-Dist: vgi-fixtures; extra == 'fixtures'
|
|
193
|
+
Provides-Extra: granian
|
|
194
|
+
Requires-Dist: granian>=2.7; extra == 'granian'
|
|
195
|
+
Requires-Dist: vgi-rpc[http]; extra == 'granian'
|
|
193
196
|
Provides-Extra: haybarn
|
|
194
|
-
Requires-Dist: haybarn>=1.5.
|
|
197
|
+
Requires-Dist: haybarn>=1.5.5rc1; extra == 'haybarn'
|
|
195
198
|
Provides-Extra: http
|
|
196
199
|
Requires-Dist: vgi-rpc[http]; extra == 'http'
|
|
197
200
|
Provides-Extra: oauth
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Global functions
|
|
2
|
+
|
|
3
|
+
A VGI catalog normally exposes its functions under its own name — `mycat.main.my_func()`.
|
|
4
|
+
Some functions aren't *about* the attached catalog at all: diagnostics, converters, format
|
|
5
|
+
helpers. For those, a worker can ask the client to also publish them into the **global**
|
|
6
|
+
function namespace (DuckDB's `system.main`), so they can be called unqualified:
|
|
7
|
+
|
|
8
|
+
```sql
|
|
9
|
+
ATTACH 'acme' AS acme (TYPE vgi, LOCATION 'acme-worker');
|
|
10
|
+
SELECT * FROM acme_table_info('acme'); -- no catalog qualifier
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This mirrors how `ducklake_table_info` is reachable after `LOAD ducklake`, except that a VGI
|
|
14
|
+
worker's globals appear at `ATTACH` time rather than at extension load.
|
|
15
|
+
|
|
16
|
+
## Declaring globals
|
|
17
|
+
|
|
18
|
+
Add the function classes to `Catalog.global_functions` and pick a prefix:
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from vgi.catalog import Catalog, Schema
|
|
22
|
+
|
|
23
|
+
CATALOG = Catalog(
|
|
24
|
+
name="acme",
|
|
25
|
+
global_function_prefix="acme",
|
|
26
|
+
global_functions=[TableInfoFunction, ChecksumFunction],
|
|
27
|
+
schemas=[
|
|
28
|
+
Schema(name="main", functions=[TableInfoFunction, ChecksumFunction, ...]),
|
|
29
|
+
],
|
|
30
|
+
)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
All four function types are supported: scalar, aggregate, table, and table-buffering.
|
|
34
|
+
|
|
35
|
+
**Every global must also appear in exactly one `Schema.functions`.** This is enforced in
|
|
36
|
+
`Catalog.__post_init__`. Bind dispatch is keyed on `(schema_name, name)`, so a function listed
|
|
37
|
+
only in `global_functions` would be registered by the client but never dispatchable. Keeping it
|
|
38
|
+
schema-resident also means the qualified name (`acme.main.table_info()`) keeps working, which is
|
|
39
|
+
the unambiguous fallback if the global name is unavailable.
|
|
40
|
+
|
|
41
|
+
### The prefix
|
|
42
|
+
|
|
43
|
+
`global_function_prefix` is applied by the client to form the globally visible name:
|
|
44
|
+
|
|
45
|
+
| prefix | function name | published as |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `"acme"` | `table_info` | `acme_table_info` |
|
|
48
|
+
| `None` | `table_info` | `table_info` |
|
|
49
|
+
|
|
50
|
+
The prefix must match `[a-z_][a-z0-9_]*` — it is concatenated into a SQL identifier.
|
|
51
|
+
|
|
52
|
+
**Prefer a prefix.** `system.main` is shared by every extension and every attached VGI worker in
|
|
53
|
+
the process. An unprefixed name like `table_info` is very likely to be claimed by someone else,
|
|
54
|
+
in which case yours is silently skipped (see below). A prefix that identifies your worker makes
|
|
55
|
+
collisions essentially impossible.
|
|
56
|
+
|
|
57
|
+
## What the client does with them
|
|
58
|
+
|
|
59
|
+
Registration is **best-effort and advisory**. Do not build anything that requires a global name
|
|
60
|
+
to resolve — treat it as an ergonomic alias for the schema-qualified name, which is the one with
|
|
61
|
+
guarantees.
|
|
62
|
+
|
|
63
|
+
Specifically, on the DuckDB side:
|
|
64
|
+
|
|
65
|
+
- **First attach wins.** If the name is already owned by a different worker, yours is skipped and
|
|
66
|
+
logged; your `ATTACH` still succeeds and the function stays reachable at its qualified path.
|
|
67
|
+
- **Re-`ATTACH` is idempotent.** Attaching the same worker again reuses the existing registration
|
|
68
|
+
and refreshes its connection state.
|
|
69
|
+
- **The user can turn it off.** `ATTACH ... (TYPE vgi, global_functions false)` suppresses
|
|
70
|
+
publishing entirely for that attach.
|
|
71
|
+
- **Registration outlives `DETACH`.** DuckDB has no API to unregister a function, so the entry
|
|
72
|
+
persists for the life of the process. Calling it after `DETACH` raises an error telling the user
|
|
73
|
+
to re-`ATTACH` rather than silently reconnecting to a detached catalog.
|
|
74
|
+
|
|
75
|
+
Use `vgi_global_functions()` to see what is currently published, who owns it, and whether it is
|
|
76
|
+
still live.
|
|
77
|
+
|
|
78
|
+
## Protocol
|
|
79
|
+
|
|
80
|
+
Globals ride on the existing attach response — there is no extra round trip and no separate RPC.
|
|
81
|
+
`CatalogAttachResult` carries:
|
|
82
|
+
|
|
83
|
+
- `global_functions: list[bytes]` — IPC-serialized `FunctionInfo` records. `name` and
|
|
84
|
+
`schema_name` are the real dispatch coordinates; the prefix is *not* baked into `name`.
|
|
85
|
+
- `global_function_prefix: str` — empty string means publish bare names.
|
|
86
|
+
|
|
87
|
+
Both fields are additive and default to empty, so a worker that doesn't set them advertises
|
|
88
|
+
nothing. Introduced in protocol version 1.3.0.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "vgi-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.25.0"
|
|
4
4
|
description = "Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
keywords = [
|
|
@@ -40,7 +40,7 @@ dependencies = [
|
|
|
40
40
|
"pyarrow",
|
|
41
41
|
"typer>=0.9",
|
|
42
42
|
"platformdirs",
|
|
43
|
-
"vgi-rpc>=0.
|
|
43
|
+
"vgi-rpc>=0.39.1",
|
|
44
44
|
"httpx>=0.24",
|
|
45
45
|
]
|
|
46
46
|
|
|
@@ -49,9 +49,13 @@ dependencies = [
|
|
|
49
49
|
# evaluation, the transactor, and the statistics fixtures. It is resolved
|
|
50
50
|
# lazily in vgi/_duckdb.py: haybarn is preferred, stock duckdb is the
|
|
51
51
|
# fallback. Neither is a hard dependency — install one via these extras.
|
|
52
|
-
haybarn = ["haybarn>=1.5.
|
|
52
|
+
haybarn = ["haybarn>=1.5.5rc1"]
|
|
53
53
|
duckdb = ["duckdb>=1.5.3"]
|
|
54
54
|
http = ["vgi-rpc[http]"]
|
|
55
|
+
# Alternative HTTP server: granian moves socket I/O and HTTP parsing into Rust,
|
|
56
|
+
# off the GIL. Optional because it is a compiled extension where waitress is
|
|
57
|
+
# pure Python; select it with `vgi-serve --http --server granian`.
|
|
58
|
+
granian = ["vgi-rpc[http]", "granian>=2.7"]
|
|
55
59
|
oauth = ["vgi-rpc[oauth]"]
|
|
56
60
|
otel = ["vgi-rpc[otel]"]
|
|
57
61
|
sentry = ["vgi-rpc[sentry]"]
|
|
@@ -187,6 +191,12 @@ warn_unused_ignores = true
|
|
|
187
191
|
module = "typer.*"
|
|
188
192
|
ignore_missing_imports = true
|
|
189
193
|
|
|
194
|
+
[[tool.mypy.overrides]]
|
|
195
|
+
# granian ships no stubs, and is an optional extra used only behind
|
|
196
|
+
# `--server granian`.
|
|
197
|
+
module = "granian.*"
|
|
198
|
+
ignore_missing_imports = true
|
|
199
|
+
|
|
190
200
|
[[tool.mypy.overrides]]
|
|
191
201
|
# These files have type: ignore comments for ty that mypy doesn't need
|
|
192
202
|
module = ["vgi._test_fixtures.scalar.null_handling", "vgi.table_function"]
|
|
@@ -26,6 +26,7 @@ from vgi.catalog import (
|
|
|
26
26
|
Catalog,
|
|
27
27
|
CatalogAttachResult,
|
|
28
28
|
ForeignKeyDef,
|
|
29
|
+
FunctionInfo,
|
|
29
30
|
Macro,
|
|
30
31
|
MacroInfo,
|
|
31
32
|
MacroType,
|
|
@@ -41,6 +42,7 @@ from vgi.catalog import (
|
|
|
41
42
|
View,
|
|
42
43
|
ViewInfo,
|
|
43
44
|
)
|
|
45
|
+
from vgi.catalog.catalog_interface import FunctionType as CatalogInterfaceFunctionType
|
|
44
46
|
from vgi.invocation import BindResponse
|
|
45
47
|
from vgi.schema_utils import schema
|
|
46
48
|
from vgi.table_function import (
|
|
@@ -1394,6 +1396,152 @@ class TestCatalogValidation:
|
|
|
1394
1396
|
Catalog(name="myapp", schemas=[s1, s2])
|
|
1395
1397
|
|
|
1396
1398
|
|
|
1399
|
+
class TestGlobalFunctionsValidation:
|
|
1400
|
+
"""Validation for ``Catalog.global_functions`` / ``global_function_prefix``."""
|
|
1401
|
+
|
|
1402
|
+
def test_defaults_are_empty(self) -> None:
|
|
1403
|
+
"""A catalog publishes nothing globally unless it asks to."""
|
|
1404
|
+
catalog = Catalog(name="myapp", schemas=[Schema(name="main")])
|
|
1405
|
+
assert catalog.global_functions == ()
|
|
1406
|
+
assert catalog.global_function_prefix is None
|
|
1407
|
+
|
|
1408
|
+
def test_accepts_schema_resident_function(self) -> None:
|
|
1409
|
+
"""The happy path: listed once globally, resident in exactly one schema."""
|
|
1410
|
+
catalog = Catalog(
|
|
1411
|
+
name="myapp",
|
|
1412
|
+
schemas=[Schema(name="main", functions=[UsersFunction])],
|
|
1413
|
+
global_functions=[UsersFunction],
|
|
1414
|
+
global_function_prefix="myapp",
|
|
1415
|
+
)
|
|
1416
|
+
assert list(catalog.global_functions) == [UsersFunction]
|
|
1417
|
+
assert catalog.global_function_prefix == "myapp"
|
|
1418
|
+
|
|
1419
|
+
def test_rejects_function_absent_from_every_schema(self) -> None:
|
|
1420
|
+
"""A global function that isn't schema-resident would never dispatch."""
|
|
1421
|
+
with pytest.raises(ValueError, match="does not appear in any Schema.functions"):
|
|
1422
|
+
Catalog(
|
|
1423
|
+
name="myapp",
|
|
1424
|
+
schemas=[Schema(name="main")],
|
|
1425
|
+
global_functions=[UsersFunction],
|
|
1426
|
+
)
|
|
1427
|
+
|
|
1428
|
+
def test_rejects_function_resident_in_multiple_schemas(self) -> None:
|
|
1429
|
+
"""Two homes make the global registration ambiguous about dispatch."""
|
|
1430
|
+
with pytest.raises(ValueError, match="appears in multiple schemas"):
|
|
1431
|
+
Catalog(
|
|
1432
|
+
name="myapp",
|
|
1433
|
+
schemas=[
|
|
1434
|
+
Schema(name="main", functions=[UsersFunction]),
|
|
1435
|
+
Schema(name="other", functions=[UsersFunction]),
|
|
1436
|
+
],
|
|
1437
|
+
global_functions=[UsersFunction],
|
|
1438
|
+
)
|
|
1439
|
+
|
|
1440
|
+
def test_rejects_duplicate_global_entries(self) -> None:
|
|
1441
|
+
"""Listing a class twice would publish the same name twice."""
|
|
1442
|
+
with pytest.raises(ValueError, match="duplicate entry UsersFunction"):
|
|
1443
|
+
Catalog(
|
|
1444
|
+
name="myapp",
|
|
1445
|
+
schemas=[Schema(name="main", functions=[UsersFunction])],
|
|
1446
|
+
global_functions=[UsersFunction, UsersFunction],
|
|
1447
|
+
)
|
|
1448
|
+
|
|
1449
|
+
@pytest.mark.parametrize("prefix", ["9bad", "Bad", "has-dash", "has space", "", "trailing;"])
|
|
1450
|
+
def test_rejects_non_identifier_prefix(self, prefix: str) -> None:
|
|
1451
|
+
"""The prefix is concatenated into a SQL identifier, so it must be safe."""
|
|
1452
|
+
with pytest.raises(ValueError, match="not a valid SQL identifier prefix"):
|
|
1453
|
+
Catalog(
|
|
1454
|
+
name="myapp",
|
|
1455
|
+
schemas=[Schema(name="main", functions=[UsersFunction])],
|
|
1456
|
+
global_functions=[UsersFunction],
|
|
1457
|
+
global_function_prefix=prefix,
|
|
1458
|
+
)
|
|
1459
|
+
|
|
1460
|
+
def test_prefix_validated_even_without_global_functions(self) -> None:
|
|
1461
|
+
"""A bad prefix is a bug worth reporting even if nothing uses it yet."""
|
|
1462
|
+
with pytest.raises(ValueError, match="not a valid SQL identifier prefix"):
|
|
1463
|
+
Catalog(name="myapp", schemas=[Schema(name="main")], global_function_prefix="Bad")
|
|
1464
|
+
|
|
1465
|
+
|
|
1466
|
+
class TestGlobalFunctionsAttachResult:
|
|
1467
|
+
"""``catalog_attach`` advertises globals so the client can publish them."""
|
|
1468
|
+
|
|
1469
|
+
@staticmethod
|
|
1470
|
+
def _attach(catalog_obj: Catalog) -> CatalogAttachResult:
|
|
1471
|
+
class _TestCatalog(ReadOnlyCatalogInterface):
|
|
1472
|
+
catalog = catalog_obj
|
|
1473
|
+
|
|
1474
|
+
return _TestCatalog().catalog_attach(
|
|
1475
|
+
name=catalog_obj.name,
|
|
1476
|
+
options={},
|
|
1477
|
+
data_version_spec=None,
|
|
1478
|
+
implementation_version=None,
|
|
1479
|
+
)
|
|
1480
|
+
|
|
1481
|
+
def test_absent_when_not_declared(self) -> None:
|
|
1482
|
+
"""Catalogs that declare nothing advertise an empty list and no prefix."""
|
|
1483
|
+
result = self._attach(Catalog(name="myapp", schemas=[Schema(name="main", functions=[UsersFunction])]))
|
|
1484
|
+
assert result.global_functions == []
|
|
1485
|
+
assert result.global_function_prefix == ""
|
|
1486
|
+
|
|
1487
|
+
def test_carries_serialized_function_info(self) -> None:
|
|
1488
|
+
"""Each global arrives as a FunctionInfo with real dispatch coordinates.
|
|
1489
|
+
|
|
1490
|
+
``name``/``schema_name`` must stay the values bind dispatches on — the
|
|
1491
|
+
prefix is applied client-side, not baked into the name here.
|
|
1492
|
+
"""
|
|
1493
|
+
result = self._attach(
|
|
1494
|
+
Catalog(
|
|
1495
|
+
name="myapp",
|
|
1496
|
+
schemas=[Schema(name="analytics", functions=[UsersFunction])],
|
|
1497
|
+
default_schema="analytics",
|
|
1498
|
+
global_functions=[UsersFunction],
|
|
1499
|
+
global_function_prefix="myapp",
|
|
1500
|
+
)
|
|
1501
|
+
)
|
|
1502
|
+
assert result.global_function_prefix == "myapp"
|
|
1503
|
+
assert len(result.global_functions) == 1
|
|
1504
|
+
|
|
1505
|
+
info = FunctionInfo.deserialize_from_bytes(result.global_functions[0])
|
|
1506
|
+
assert info.name == "users"
|
|
1507
|
+
assert info.schema_name == "analytics"
|
|
1508
|
+
assert info.function_type == CatalogInterfaceFunctionType.TABLE
|
|
1509
|
+
|
|
1510
|
+
def test_prefix_empty_string_when_unset(self) -> None:
|
|
1511
|
+
"""``None`` prefix serializes as empty string — the wire type is str."""
|
|
1512
|
+
result = self._attach(
|
|
1513
|
+
Catalog(
|
|
1514
|
+
name="myapp",
|
|
1515
|
+
schemas=[Schema(name="main", functions=[UsersFunction])],
|
|
1516
|
+
global_functions=[UsersFunction],
|
|
1517
|
+
)
|
|
1518
|
+
)
|
|
1519
|
+
assert result.global_function_prefix == ""
|
|
1520
|
+
assert len(result.global_functions) == 1
|
|
1521
|
+
|
|
1522
|
+
def test_example_fixture_publishes_one_of_each_function_type(self) -> None:
|
|
1523
|
+
"""The shipped fixture covers all four types for the C++ integration suite."""
|
|
1524
|
+
from vgi._test_fixtures.worker import ExampleCatalog
|
|
1525
|
+
|
|
1526
|
+
result = ExampleCatalog().catalog_attach(
|
|
1527
|
+
name="example",
|
|
1528
|
+
options={},
|
|
1529
|
+
data_version_spec=None,
|
|
1530
|
+
implementation_version=None,
|
|
1531
|
+
)
|
|
1532
|
+
assert result.global_function_prefix == "vgi_example"
|
|
1533
|
+
infos = [FunctionInfo.deserialize_from_bytes(b) for b in result.global_functions]
|
|
1534
|
+
assert {i.function_type for i in infos} == {
|
|
1535
|
+
CatalogInterfaceFunctionType.SCALAR,
|
|
1536
|
+
CatalogInterfaceFunctionType.TABLE,
|
|
1537
|
+
CatalogInterfaceFunctionType.AGGREGATE,
|
|
1538
|
+
CatalogInterfaceFunctionType.TABLE_BUFFERING,
|
|
1539
|
+
}
|
|
1540
|
+
# Every advertised global must be schema-resident, or it could never
|
|
1541
|
+
# be dispatched after the client registers it.
|
|
1542
|
+
assert all(i.schema_name == "main" for i in infos)
|
|
1543
|
+
|
|
1544
|
+
|
|
1397
1545
|
# =============================================================================
|
|
1398
1546
|
# ReadOnlyCatalogInterface with Catalog Object Tests
|
|
1399
1547
|
# =============================================================================
|
|
@@ -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) == 17
|
|
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_()
|
|
@@ -1121,6 +1121,8 @@ class TestArrowSchemaCorrectness:
|
|
|
1121
1121
|
assert schema.field("settings").type == pa.list_(pa.binary())
|
|
1122
1122
|
assert schema.field("secret_types").type == pa.list_(pa.binary())
|
|
1123
1123
|
assert schema.field("attach_catalogs").type == pa.list_(pa.binary())
|
|
1124
|
+
assert schema.field("global_functions").type == pa.list_(pa.binary())
|
|
1125
|
+
assert schema.field("global_function_prefix").type == pa.string()
|
|
1124
1126
|
assert schema.field("resolved_data_version").type == pa.string()
|
|
1125
1127
|
assert schema.field("resolved_implementation_version").type == pa.string()
|
|
1126
1128
|
|
|
@@ -42,6 +42,19 @@ _EXEMPTIONS: dict[str, str] = {
|
|
|
42
42
|
"path, ProcessParams.if_none_match threading, and the revalidatable "
|
|
43
43
|
"fixture's 304 behavior)."
|
|
44
44
|
),
|
|
45
|
+
"global_functions": (
|
|
46
|
+
"Publishing worker-declared functions into DuckDB's system.main is "
|
|
47
|
+
"entirely a C++-extension concern: the registry, prefix application, "
|
|
48
|
+
"first-attach-wins collision policy, post-DETACH liveness, and the "
|
|
49
|
+
"vgi_global_functions() introspection surface all live in the "
|
|
50
|
+
"extension. The Python worker's only role is advertising "
|
|
51
|
+
"global_functions/global_function_prefix on the attach response, which "
|
|
52
|
+
"tests/catalog/test_declarative.py covers directly (descriptor "
|
|
53
|
+
"validation + the serialized FunctionInfo payload). The observable "
|
|
54
|
+
"behaviour is cross-language by construction and is covered by "
|
|
55
|
+
"vgi/test/sql/integration/global_functions/, which any worker "
|
|
56
|
+
"implementation runs via VGI_TEST_WORKER."
|
|
57
|
+
),
|
|
45
58
|
"filter_pushdown": (
|
|
46
59
|
"Per-type filter-pushdown coverage is mirrored by tests/test_filter_pushdown.py "
|
|
47
60
|
"and tests/test_filter_pushdown_extension.py, which drive the same predicate "
|
|
@@ -32,14 +32,14 @@ CLIENT_PROTOCOL_VERSION = VgiProtocol.protocol_version
|
|
|
32
32
|
|
|
33
33
|
def test_client_protocol_version_is_canonical() -> None:
|
|
34
34
|
"""Sanity-check the constants this test reasons about haven't drifted apart."""
|
|
35
|
-
assert CLIENT_PROTOCOL_VERSION == "1.
|
|
35
|
+
assert CLIENT_PROTOCOL_VERSION == "1.3.0"
|
|
36
36
|
# The fixture must be a genuine mismatch in the major/minor that the
|
|
37
37
|
# framework compares; a patch-only difference would (correctly) pass.
|
|
38
38
|
assert BAD_PROTOCOL_VERSION.split(".")[:2] != CLIENT_PROTOCOL_VERSION.split(".")[:2]
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def test_mismatched_worker_protocol_version_raises() -> None:
|
|
42
|
-
"""A worker enforcing 99.0.0 must reject the 1.
|
|
42
|
+
"""A worker enforcing 99.0.0 must reject the 1.3.0 client at dispatch."""
|
|
43
43
|
with Client("vgi-fixture-bad-protocol-worker") as client, pytest.raises(ClientError) as exc_info:
|
|
44
44
|
list(
|
|
45
45
|
client.table_function(
|
|
@@ -54,5 +54,5 @@ def test_mismatched_worker_protocol_version_raises() -> None:
|
|
|
54
54
|
assert "protocol_version mismatch" in message
|
|
55
55
|
assert CLIENT_PROTOCOL_VERSION in message
|
|
56
56
|
assert BAD_PROTOCOL_VERSION in message
|
|
57
|
-
# Directional hint: the client (1.
|
|
57
|
+
# Directional hint: the client (1.3.0) is older than the worker (99.0.0).
|
|
58
58
|
assert "upgrade the VGI extension/client" in message
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
{
|
|
4
4
|
"attach_options": [],
|
|
5
5
|
"counts": {
|
|
6
|
-
"functions":
|
|
6
|
+
"functions": 221,
|
|
7
7
|
"schemas": 2,
|
|
8
8
|
"tables": 59,
|
|
9
9
|
"views": 3
|
|
@@ -371,6 +371,19 @@
|
|
|
371
371
|
{
|
|
372
372
|
"doc": "Example functions for testing VGI",
|
|
373
373
|
"functions": [
|
|
374
|
+
{
|
|
375
|
+
"args": [
|
|
376
|
+
{
|
|
377
|
+
"desc": "Column to sum",
|
|
378
|
+
"name": "value",
|
|
379
|
+
"type": "int64"
|
|
380
|
+
}
|
|
381
|
+
],
|
|
382
|
+
"doc": "Global-registration probe (aggregate)",
|
|
383
|
+
"name": "global_agg",
|
|
384
|
+
"returns": "int64",
|
|
385
|
+
"type": "aggregate"
|
|
386
|
+
},
|
|
374
387
|
{
|
|
375
388
|
"args": [
|
|
376
389
|
{
|
|
@@ -979,6 +992,19 @@
|
|
|
979
992
|
"returns": "double",
|
|
980
993
|
"type": "scalar"
|
|
981
994
|
},
|
|
995
|
+
{
|
|
996
|
+
"args": [
|
|
997
|
+
{
|
|
998
|
+
"desc": "Value to label",
|
|
999
|
+
"name": "value",
|
|
1000
|
+
"type": "int64"
|
|
1001
|
+
}
|
|
1002
|
+
],
|
|
1003
|
+
"doc": "Global-registration probe (scalar)",
|
|
1004
|
+
"name": "global_scalar",
|
|
1005
|
+
"returns": "string",
|
|
1006
|
+
"type": "scalar"
|
|
1007
|
+
},
|
|
982
1008
|
{
|
|
983
1009
|
"args": [
|
|
984
1010
|
{
|
|
@@ -2146,6 +2172,12 @@
|
|
|
2146
2172
|
"name": "geo_encode",
|
|
2147
2173
|
"type": "table"
|
|
2148
2174
|
},
|
|
2175
|
+
{
|
|
2176
|
+
"args": [],
|
|
2177
|
+
"doc": "Global-registration probe (table)",
|
|
2178
|
+
"name": "global_table",
|
|
2179
|
+
"type": "table"
|
|
2180
|
+
},
|
|
2149
2181
|
{
|
|
2150
2182
|
"args": [
|
|
2151
2183
|
{
|
|
@@ -3281,6 +3313,12 @@
|
|
|
3281
3313
|
"name": "exception_process",
|
|
3282
3314
|
"type": "table_in_out"
|
|
3283
3315
|
},
|
|
3316
|
+
{
|
|
3317
|
+
"args": [],
|
|
3318
|
+
"doc": "Global-registration probe (table-buffering)",
|
|
3319
|
+
"name": "global_buffered",
|
|
3320
|
+
"type": "table_in_out"
|
|
3321
|
+
},
|
|
3284
3322
|
{
|
|
3285
3323
|
"args": [],
|
|
3286
3324
|
"doc": "Worker sleeps in process (manual cancel test)",
|