vgi-python 0.29.5__tar.gz → 0.30.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.29.5 → vgi_python-0.30.0}/PKG-INFO +2 -2
- {vgi_python-0.29.5 → vgi_python-0.30.0}/pyproject.toml +2 -2
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_batch_metadata_callback.py +1 -1
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_scan_branches_client.py +8 -5
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_time_travel_client.py +1 -1
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/test_blended_exchange.py +42 -0
- vgi_python-0.30.0/tests/test_client_oauth.py +204 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/uv.lock +5 -5
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/client.py +135 -12
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/cpp_request_builders.py +1 -1
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/worker.py +44 -15
- {vgi_python-0.29.5 → vgi_python-0.30.0}/.gitattributes +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/.github/dependabot.yml +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/.github/workflows/integration.yml +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/.github/workflows/release.yml +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/.gitignore +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/.python-version +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/.vale.ini +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/CLAUDE.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/LICENSE +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/README.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/RELEASE-RUNBOOK.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/SECURITY.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/ci/README.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/ci/run-integration.sh +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/arguments.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/auth.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/catalogs.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/client.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/exceptions.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/filters.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/functions.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/http.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/index.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/metadata.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/observability.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/storage.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/transactor.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/api/worker.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/argument-serialization.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/logo.png +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/assets/social-card.png +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/authentication.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/catalog-interface.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/cli.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/column-statistics.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/concepts/index.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/contributing-docs.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/generator-api.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/global-functions.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/how-to/index.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/index.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/lifecycle.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/metadata.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/overrides/main.html +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/robots.txt +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/shared-storage.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/tutorial/index.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/tutorial/table.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/docs/vgi-logo.png +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/examples/batch_index_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/examples/cache_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/examples/calc_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/examples/copy_format_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/examples/filter_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/examples/row_count_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/examples/sum_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/mkdocs.yml +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/packages/vgi-fixtures/pyproject.toml +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/scripts/measure_startup.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/test-data/generate.sh +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_attach_option.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_declarative.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_example_worker_catalog.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_required_filters.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_serialization.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_aggregate_client.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_cli.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_copy_client.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_split_client.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_stop_force.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_table_column_statistics.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_const_param_enforcement.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_container.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_copy_from.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_copy_to.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_directory_parity.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_github.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_protocol_version.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_secret.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_splits.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/conftest.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/anchor_len_overrun.bin +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/bad_fingerprint.bin +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/bad_flags_unsealed_but_key_present.bin +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/bad_version.bin +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/generate.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/manifest.json +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/reserved_flag_bit.bin +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/stale_anchor.bin +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/truncated.bin +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/valid_sealed.bin +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/data/split_tokens/valid_unsealed.bin +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table/test_wide_columns.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/test_blended_metadata.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_aggregate_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_argument_spec.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_auth.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_cache_control.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_call_state_attach.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_copy_to_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_docstrings.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_exceptions.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_fixture_catalog_parity.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_function_storage.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_java_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_rust_request_builders.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_rust_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_launch_transport.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_metadata.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_otel.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_protocol_classes.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_schema_scoped_functions.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_serve.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_setting_secret_annotations.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_signing_key.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_split_token.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_tcp_transport.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_union_argument.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_wire_record_schema_parity.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/test_worker_page.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_duckdb.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/same_name.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/companion.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/copy_from.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/copy_to.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/global_functions.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/http_server.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/formatting.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/same_name.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/_common.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/cache.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/filters.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/same_name_cached.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/splits.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table/wide.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table_in_out.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/table_in_out_same_name.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/twin_catalogs.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/aggregate_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/argument_spec.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/arguments.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/attach_header.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/auth.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/cache_control.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/catalog/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/catalog/catalog_interface.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/catalog/descriptors.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/aggregate.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/catalog_mixin.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/cli.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/client/errors.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/_common.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/java_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/rust_request_builders.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/rust_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/copy_from_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/copy_to_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/exceptions.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/function_storage.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/http/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/http/landing.html +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/http/landing_page.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/http/vgi-client.js +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/invocation.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/logging_config.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/meta_worker.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/metadata.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/otel.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/profiling.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/protocol.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/protocol_version.txt +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/py.typed +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/scalar_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/schema_utils.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/secret_service.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/serve.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/split_token.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/table_filter_pushdown.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/table_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/transactor/client.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/vgi/transactor/server.py +0 -0
- {vgi_python-0.29.5 → vgi_python-0.30.0}/wrangler.jsonc +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.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: httpx2>=2.9.1
|
|
|
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.44.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'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "vgi-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.30.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.44.1",
|
|
44
44
|
"httpx2>=2.9.1",
|
|
45
45
|
]
|
|
46
46
|
|
|
@@ -69,7 +69,7 @@ def test_callback_sees_none_for_a_batch_without_custom_metadata(client: Any) ->
|
|
|
69
69
|
client.table_function(
|
|
70
70
|
function_name="sequence",
|
|
71
71
|
schema_name="main",
|
|
72
|
-
arguments=Arguments(positional=
|
|
72
|
+
arguments=Arguments(positional=(pa.scalar(4),)),
|
|
73
73
|
batch_metadata_callback=metas.append,
|
|
74
74
|
)
|
|
75
75
|
)
|
|
@@ -60,8 +60,11 @@ def test_single_branch_table_falls_back_to_legacy_rpc(client: Any) -> None:
|
|
|
60
60
|
name="numbers",
|
|
61
61
|
)
|
|
62
62
|
assert len(result.branches) == 1
|
|
63
|
-
assert
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
assert (
|
|
64
|
+
result.branches[0].function_name
|
|
65
|
+
== client.table_scan_function_get(
|
|
66
|
+
attach_opaque_data=_attach_opaque(client),
|
|
67
|
+
schema_name=DATA,
|
|
68
|
+
name="numbers",
|
|
69
|
+
).function_name
|
|
70
|
+
)
|
|
@@ -79,7 +79,7 @@ def test_table_function_with_no_at_clause_is_unaffected(client: Any) -> None:
|
|
|
79
79
|
"""The new `at_unit`/`at_value` params default to `None` — unchanged behavior without them."""
|
|
80
80
|
batches = list(
|
|
81
81
|
client.table_function(
|
|
82
|
-
function_name="sequence", schema_name="main", arguments=Arguments(positional=
|
|
82
|
+
function_name="sequence", schema_name="main", arguments=Arguments(positional=(pa.scalar(5),))
|
|
83
83
|
)
|
|
84
84
|
)
|
|
85
85
|
assert sum(b.num_rows for b in batches) == 5
|
|
@@ -238,3 +238,45 @@ class TestBlendedArityAndVarargs:
|
|
|
238
238
|
|
|
239
239
|
assert len(results) == 1
|
|
240
240
|
assert results[0].column("row_sum").to_pylist() == [3.0, 5.0]
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class TestHasFinalizeSkip:
|
|
244
|
+
"""`has_finalize=False` skips the FINALIZE `init()` entirely for a no-finalize function.
|
|
245
|
+
|
|
246
|
+
Real interop bug found live against a non-Python worker SDK: the client
|
|
247
|
+
unconditionally sent a FINALIZE-phase init() for every table-in-out call,
|
|
248
|
+
which the Python fixture worker happens to no-op gracefully but a
|
|
249
|
+
TypeScript-SDK worker rejects outright ("a blended row-transform function
|
|
250
|
+
has no FINALIZE phase"). `has_finalize=False` avoids sending that init()
|
|
251
|
+
at all rather than depending on every SDK tolerating an unexpected one.
|
|
252
|
+
Output must be identical either way for a function that has no finalize
|
|
253
|
+
stage (there is nothing to skip yielding) -- this is the equivalence this
|
|
254
|
+
suite can prove locally; the cross-SDK rejection itself is TypeScript-only.
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
def test_output_identical_with_and_without_has_finalize(self, client_transport: Any) -> None:
|
|
258
|
+
"""Same output whether the FINALIZE init() is sent or skipped, both transports."""
|
|
259
|
+
input_schema = make_schema([pa.field("n", pa.int64())])
|
|
260
|
+
batch = pa.RecordBatch.from_pydict({"n": [0, 1, 3, 2]}, schema=input_schema)
|
|
261
|
+
|
|
262
|
+
with client_transport() as client:
|
|
263
|
+
with_finalize = list(
|
|
264
|
+
client.table_in_out_function(
|
|
265
|
+
function_name="blended_explode",
|
|
266
|
+
schema_name="main",
|
|
267
|
+
input=iter([batch]),
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
with client_transport() as client:
|
|
271
|
+
without_finalize = list(
|
|
272
|
+
client.table_in_out_function(
|
|
273
|
+
function_name="blended_explode",
|
|
274
|
+
schema_name="main",
|
|
275
|
+
input=iter([batch]),
|
|
276
|
+
has_finalize=False,
|
|
277
|
+
)
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
assert len(with_finalize) == 1
|
|
281
|
+
assert len(without_finalize) == 1
|
|
282
|
+
assert with_finalize[0].column("i").to_pylist() == without_finalize[0].column("i").to_pylist()
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Tests for vgi.client.Client's OAuth wiring (oauth=/oauth_refresh_token=/oauth_flow=).
|
|
4
|
+
|
|
5
|
+
The device-code/PKCE flow logic itself is tested exhaustively in
|
|
6
|
+
vgi-rpc-python's tests/test_oauth_client.py (mock IdP, no network). These
|
|
7
|
+
tests cover the integration seam: does `Client` build a `VgiOAuthAuth` with
|
|
8
|
+
the right configuration, does construction-time validation reject bad
|
|
9
|
+
combinations, and does a full round trip actually work when driven through
|
|
10
|
+
`Client`'s real request path (via the `httpx_client=` escape hatch, wired to
|
|
11
|
+
a mock IdP the same way vgi-rpc-python's own tests are).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import httpx2
|
|
17
|
+
import pytest
|
|
18
|
+
from vgi_rpc.http import VgiOAuthAuth
|
|
19
|
+
|
|
20
|
+
from vgi.client.client import Client
|
|
21
|
+
|
|
22
|
+
RESOURCE_METADATA_URL = "https://api.example.com/.well-known/oauth-protected-resource"
|
|
23
|
+
DEVICE_AUTH_URL = "https://auth.example.com/device/code"
|
|
24
|
+
TOKEN_URL = "https://auth.example.com/token"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class _MockIdp:
|
|
28
|
+
"""Minimal mock IdP + protected resource for one full device-code round trip."""
|
|
29
|
+
|
|
30
|
+
def __init__(self) -> None:
|
|
31
|
+
self.device_auth_calls = 0
|
|
32
|
+
self.granted_token = "mock-access-token"
|
|
33
|
+
|
|
34
|
+
def handler(self, request: httpx2.Request) -> httpx2.Response:
|
|
35
|
+
"""Route a request to the matching mock endpoint by exact URL."""
|
|
36
|
+
url = str(request.url)
|
|
37
|
+
if url == RESOURCE_METADATA_URL:
|
|
38
|
+
return httpx2.Response(
|
|
39
|
+
200,
|
|
40
|
+
json={
|
|
41
|
+
"resource": "https://api.example.com",
|
|
42
|
+
"authorization_servers": ["https://auth.example.com"],
|
|
43
|
+
"device_code_client_id": "device-client",
|
|
44
|
+
"device_code_client_secret": "device-secret",
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
if url == "https://auth.example.com/.well-known/openid-configuration":
|
|
48
|
+
return httpx2.Response(
|
|
49
|
+
200,
|
|
50
|
+
json={
|
|
51
|
+
"token_endpoint": TOKEN_URL,
|
|
52
|
+
"device_authorization_endpoint": DEVICE_AUTH_URL,
|
|
53
|
+
"grant_types_supported": ["urn:ietf:params:oauth:grant-type:device_code"],
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
if url == DEVICE_AUTH_URL:
|
|
57
|
+
self.device_auth_calls += 1
|
|
58
|
+
return httpx2.Response(
|
|
59
|
+
200,
|
|
60
|
+
json={
|
|
61
|
+
"device_code": "devcode-123",
|
|
62
|
+
"user_code": "ABCD-EFGH",
|
|
63
|
+
"verification_uri": "https://auth.example.com/activate",
|
|
64
|
+
"expires_in": 600,
|
|
65
|
+
"interval": 0,
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
if url == TOKEN_URL:
|
|
69
|
+
return httpx2.Response(200, json={"access_token": self.granted_token, "token_type": "Bearer"})
|
|
70
|
+
if url.endswith("/vgi/catalogs"):
|
|
71
|
+
token = request.headers.get("authorization", "")
|
|
72
|
+
if token:
|
|
73
|
+
return httpx2.Response(200, json={"authorization": token})
|
|
74
|
+
return httpx2.Response(
|
|
75
|
+
401,
|
|
76
|
+
headers={"www-authenticate": f'Bearer resource_metadata="{RESOURCE_METADATA_URL}"'},
|
|
77
|
+
)
|
|
78
|
+
return httpx2.Response(404, json={"error": "not_found", "url": url}) # pragma: no cover
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TestConstructorValidation:
|
|
82
|
+
"""Tests for the mutually-exclusive oauth/bearer_token/httpx_client combinations."""
|
|
83
|
+
|
|
84
|
+
def test_oauth_and_bearer_token_raises(self) -> None:
|
|
85
|
+
"""Passing both oauth=True and bearer_token= raises ValueError."""
|
|
86
|
+
with pytest.raises(ValueError, match="mutually exclusive"):
|
|
87
|
+
Client(transport="http", base_url="https://example.com", oauth=True, bearer_token="tok")
|
|
88
|
+
|
|
89
|
+
def test_oauth_refresh_token_and_bearer_token_raises(self) -> None:
|
|
90
|
+
"""oauth_refresh_token= (which implies oauth=True) also conflicts with bearer_token=."""
|
|
91
|
+
with pytest.raises(ValueError, match="mutually exclusive"):
|
|
92
|
+
Client(
|
|
93
|
+
transport="http",
|
|
94
|
+
base_url="https://example.com",
|
|
95
|
+
oauth_refresh_token="seed",
|
|
96
|
+
bearer_token="tok",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def test_oauth_and_httpx_client_raises(self) -> None:
|
|
100
|
+
"""Passing both oauth=True and httpx_client= raises ValueError."""
|
|
101
|
+
with pytest.raises(ValueError, match="httpx_client"):
|
|
102
|
+
Client(
|
|
103
|
+
transport="http",
|
|
104
|
+
base_url="https://example.com",
|
|
105
|
+
oauth=True,
|
|
106
|
+
httpx_client=httpx2.Client(),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def test_invalid_oauth_flow_raises(self) -> None:
|
|
110
|
+
"""An unrecognized oauth_flow value raises ValueError, not a silent no-op."""
|
|
111
|
+
with pytest.raises(ValueError, match="oauth_flow"):
|
|
112
|
+
Client(transport="http", base_url="https://example.com", oauth=True, oauth_flow="carrier-pigeon") # type: ignore[arg-type]
|
|
113
|
+
|
|
114
|
+
def test_invalid_oauth_prompt_raises(self) -> None:
|
|
115
|
+
"""An unrecognized oauth_prompt value raises ValueError, not a silent no-op."""
|
|
116
|
+
with pytest.raises(ValueError, match="oauth_prompt"):
|
|
117
|
+
Client(transport="http", base_url="https://example.com", oauth=True, oauth_prompt="shout") # type: ignore[arg-type]
|
|
118
|
+
|
|
119
|
+
def test_bearer_token_alone_still_works(self) -> None:
|
|
120
|
+
"""The existing bearer_token= path is unaffected by the new oauth params."""
|
|
121
|
+
client = Client(transport="http", base_url="https://example.com", bearer_token="tok")
|
|
122
|
+
assert client._bearer_token == "tok"
|
|
123
|
+
assert not client._use_oauth
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TestOAuthWiring:
|
|
127
|
+
"""Tests that Client(oauth=...) builds a correctly-configured VgiOAuthAuth."""
|
|
128
|
+
|
|
129
|
+
def test_oauth_true_builds_vgi_oauth_auth(self) -> None:
|
|
130
|
+
"""oauth=True lazily builds a VgiOAuthAuth with the client's own oauth_* settings."""
|
|
131
|
+
client = Client(
|
|
132
|
+
transport="http",
|
|
133
|
+
base_url="https://example.com",
|
|
134
|
+
oauth=True,
|
|
135
|
+
oauth_flow="device_code",
|
|
136
|
+
oauth_timeout_seconds=42.0,
|
|
137
|
+
oauth_prompt="consent",
|
|
138
|
+
)
|
|
139
|
+
assert client.oauth_identity() is None # nothing has authenticated yet
|
|
140
|
+
client._get_or_create_httpx_client()
|
|
141
|
+
assert isinstance(client._oauth_auth, VgiOAuthAuth)
|
|
142
|
+
assert client._oauth_auth._flow == "device_code" # noqa: SLF001 -- white-box wiring check
|
|
143
|
+
assert client._oauth_auth._timeout_seconds == 42.0 # noqa: SLF001
|
|
144
|
+
assert client._oauth_auth._prompt == "consent" # noqa: SLF001
|
|
145
|
+
client._oauth_auth.close()
|
|
146
|
+
|
|
147
|
+
def test_oauth_refresh_token_seeds_the_auth_object(self) -> None:
|
|
148
|
+
"""oauth_refresh_token= implies oauth=True and seeds the refresh token."""
|
|
149
|
+
client = Client(
|
|
150
|
+
transport="http",
|
|
151
|
+
base_url="https://example.com",
|
|
152
|
+
oauth_refresh_token="pre-seeded-token",
|
|
153
|
+
)
|
|
154
|
+
assert client._use_oauth
|
|
155
|
+
client._get_or_create_httpx_client()
|
|
156
|
+
assert client._oauth_auth is not None
|
|
157
|
+
assert client._oauth_auth._token.refresh_token == "pre-seeded-token" # noqa: SLF001
|
|
158
|
+
client._oauth_auth.close()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class TestOAuthEndToEnd:
|
|
162
|
+
"""A full device-code login round trip through the exact client Client would use.
|
|
163
|
+
|
|
164
|
+
Uses the httpx_client= escape hatch to wire a VgiOAuthAuth against a mock
|
|
165
|
+
IdP (httpx2.MockTransport) -- the same technique vgi-rpc-python's own
|
|
166
|
+
tests use. The device-code/PKCE flow mechanics are covered exhaustively
|
|
167
|
+
there (mock IdP, no network); this test's job is narrower: prove
|
|
168
|
+
``Client`` actually issues its RPCs through the httpx2.Client it was
|
|
169
|
+
handed (so a real attach would carry the OAuth-obtained token), and that
|
|
170
|
+
a 401-triggered login on that client completes end to end.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
def test_client_issues_requests_through_the_oauth_authed_httpx_client(self) -> None:
|
|
174
|
+
"""A 401 on the client Client._get_or_create_httpx_client() returns triggers a real login."""
|
|
175
|
+
idp = _MockIdp()
|
|
176
|
+
auth = VgiOAuthAuth(
|
|
177
|
+
base_url="https://example.com",
|
|
178
|
+
flow="device_code",
|
|
179
|
+
timeout_seconds=5.0,
|
|
180
|
+
transport=httpx2.MockTransport(idp.handler),
|
|
181
|
+
)
|
|
182
|
+
httpx_client = httpx2.Client(
|
|
183
|
+
transport=httpx2.MockTransport(idp.handler), auth=auth, base_url="https://example.com"
|
|
184
|
+
)
|
|
185
|
+
# Not started (no worker spawned) -- this test only exercises the HTTP/OAuth
|
|
186
|
+
# plumbing, not a full RPC call, so client.stop() (which requires start())
|
|
187
|
+
# doesn't apply; the httpx_client/auth are closed directly instead.
|
|
188
|
+
client = Client(transport="http", base_url="https://example.com", httpx_client=httpx_client)
|
|
189
|
+
try:
|
|
190
|
+
# This is the exact client object Client's own RPC machinery sends
|
|
191
|
+
# every request through -- not a parallel one built just for the test.
|
|
192
|
+
assert client._get_or_create_httpx_client() is httpx_client
|
|
193
|
+
|
|
194
|
+
resp = httpx_client.get("/vgi/catalogs")
|
|
195
|
+
assert resp.status_code == 200
|
|
196
|
+
assert resp.json()["authorization"] == f"Bearer {idp.granted_token}"
|
|
197
|
+
assert idp.device_auth_calls == 1
|
|
198
|
+
|
|
199
|
+
# A second request reuses the cached token -- no second login.
|
|
200
|
+
httpx_client.get("/vgi/catalogs")
|
|
201
|
+
assert idp.device_auth_calls == 1
|
|
202
|
+
finally:
|
|
203
|
+
httpx_client.close()
|
|
204
|
+
auth.close()
|
|
@@ -2569,7 +2569,7 @@ requires-dist = [
|
|
|
2569
2569
|
|
|
2570
2570
|
[[package]]
|
|
2571
2571
|
name = "vgi-python"
|
|
2572
|
-
version = "0.
|
|
2572
|
+
version = "0.30.0"
|
|
2573
2573
|
source = { editable = "." }
|
|
2574
2574
|
dependencies = [
|
|
2575
2575
|
{ name = "click" },
|
|
@@ -2686,7 +2686,7 @@ requires-dist = [
|
|
|
2686
2686
|
{ name = "vgi-python", extras = ["test-fixtures"], marker = "extra == 'test-fixtures-writable'" },
|
|
2687
2687
|
{ name = "vgi-python", extras = ["test-fixtures", "test-fixtures-writable", "http", "launch", "oauth", "otel", "sentry", "azure"], marker = "extra == 'dev'" },
|
|
2688
2688
|
{ name = "vgi-python", extras = ["transactor"], marker = "extra == 'test-fixtures-writable'" },
|
|
2689
|
-
{ name = "vgi-rpc", specifier = ">=0.
|
|
2689
|
+
{ name = "vgi-rpc", specifier = ">=0.44.1" },
|
|
2690
2690
|
{ name = "vgi-rpc", extras = ["cli"], marker = "extra == 'launch'" },
|
|
2691
2691
|
{ name = "vgi-rpc", extras = ["cli", "conformance", "external", "http", "oauth", "otel", "sentry"], marker = "extra == 'dev'" },
|
|
2692
2692
|
{ name = "vgi-rpc", extras = ["http"], marker = "extra == 'granian'" },
|
|
@@ -2716,7 +2716,7 @@ docs = [
|
|
|
2716
2716
|
|
|
2717
2717
|
[[package]]
|
|
2718
2718
|
name = "vgi-rpc"
|
|
2719
|
-
version = "0.
|
|
2719
|
+
version = "0.44.1"
|
|
2720
2720
|
source = { registry = "https://pypi.org/simple" }
|
|
2721
2721
|
dependencies = [
|
|
2722
2722
|
{ name = "docstring-parser" },
|
|
@@ -2724,9 +2724,9 @@ dependencies = [
|
|
|
2724
2724
|
{ name = "pywin32", marker = "sys_platform == 'win32'" },
|
|
2725
2725
|
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
|
2726
2726
|
]
|
|
2727
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
2727
|
+
sdist = { url = "https://files.pythonhosted.org/packages/1a/16/5a78053080117cac00a8945b70298d636a63d57801a33528bb63b14cdd95/vgi_rpc-0.44.1.tar.gz", hash = "sha256:37a54ae2cb7e5a7a2603aad69ece92c3c99a96906c96d2196806202ee4b70825", size = 1170809, upload-time = "2026-08-28T15:03:07.221Z" }
|
|
2728
2728
|
wheels = [
|
|
2729
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
2729
|
+
{ url = "https://files.pythonhosted.org/packages/8f/d6/3bfff42bd723ffcd0c2550d932e766aa65d39a886017cb9c002174e68196/vgi_rpc-0.44.1-py3-none-any.whl", hash = "sha256:e9785753bdb30e3f07f2bdbeafcddd09533cc014baa310e093a94fdeda6ad517", size = 545257, upload-time = "2026-08-28T15:03:05.268Z" },
|
|
2730
2730
|
]
|
|
2731
2731
|
|
|
2732
2732
|
[package.optional-dependencies]
|
|
@@ -424,6 +424,11 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
424
424
|
tcp_host: str | None = None,
|
|
425
425
|
tcp_port: int | None = None,
|
|
426
426
|
bearer_token: str | None = None,
|
|
427
|
+
oauth: bool = False,
|
|
428
|
+
oauth_refresh_token: str | None = None,
|
|
429
|
+
oauth_flow: Literal["auto", "device_code", "pkce"] = "auto",
|
|
430
|
+
oauth_timeout_seconds: float = 120.0,
|
|
431
|
+
oauth_prompt: Literal["none", "login", "select_account", "consent"] = "none",
|
|
427
432
|
httpx_client: Any | None = None,
|
|
428
433
|
external_location: Any | None = None,
|
|
429
434
|
launch_argv: Sequence[str] | None = None,
|
|
@@ -472,12 +477,38 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
472
477
|
``"http://127.0.0.1:8765"``.
|
|
473
478
|
tcp_host: TCP-only. Hostname or IP of the running worker.
|
|
474
479
|
tcp_port: TCP-only. Port of the running worker.
|
|
475
|
-
bearer_token: HTTP-only. When set, every request carries
|
|
476
|
-
``Authorization: Bearer <token>`` header.
|
|
477
|
-
|
|
480
|
+
bearer_token: HTTP-only. When set, every request carries a
|
|
481
|
+
static ``Authorization: Bearer <token>`` header. Mutually
|
|
482
|
+
exclusive with ``oauth``/``oauth_refresh_token`` — for a
|
|
483
|
+
token that expires or needs a login flow, use those instead.
|
|
484
|
+
oauth: HTTP-only. When True, obtain and refresh bearer tokens
|
|
485
|
+
automatically via OAuth (device-code today; PKCE is not yet
|
|
486
|
+
implemented — see ``oauth_flow``) whenever the worker
|
|
487
|
+
answers 401 with an RFC 9728 challenge. Implied by passing
|
|
488
|
+
``oauth_refresh_token``. The first call blocks and prints a
|
|
489
|
+
"Visit: ... Enter code: ..." prompt until login completes;
|
|
490
|
+
later calls reuse the cached token and refresh it silently.
|
|
491
|
+
See ``vgi_rpc.http.VgiOAuthAuth`` for the full mechanism
|
|
492
|
+
(RFC 8628 device-code polling, RFC 8414/9728 discovery,
|
|
493
|
+
silent refresh, the secret-less-proxy ``token_endpoint``
|
|
494
|
+
override).
|
|
495
|
+
oauth_refresh_token: HTTP-only. Pre-obtained refresh token,
|
|
496
|
+
seeded so the first request can silently refresh instead of
|
|
497
|
+
running an interactive login. Implies ``oauth=True``.
|
|
498
|
+
Mutually exclusive with ``bearer_token``.
|
|
499
|
+
oauth_flow: HTTP-only, OAuth-only. ``"auto"`` (default) picks
|
|
500
|
+
device-code when the server offers it; ``"device_code"``
|
|
501
|
+
forces it; ``"pkce"`` is not yet implemented and raises
|
|
502
|
+
``NotImplementedError`` when actually needed.
|
|
503
|
+
oauth_timeout_seconds: HTTP-only, OAuth-only. How long an
|
|
504
|
+
interactive login may take before giving up (further capped
|
|
505
|
+
by the authorization server's own device-code expiry).
|
|
506
|
+
oauth_prompt: HTTP-only, OAuth-only. Reserved for the PKCE flow
|
|
507
|
+
(not yet implemented); has no effect on device-code logins.
|
|
478
508
|
httpx_client: HTTP-only escape hatch. When provided, overrides
|
|
479
|
-
``bearer_token`` and is
|
|
480
|
-
|
|
509
|
+
``bearer_token``/``oauth``/``oauth_refresh_token`` and is
|
|
510
|
+
used verbatim; supply this when you need mTLS or a custom
|
|
511
|
+
auth scheme. Not the canonical path.
|
|
481
512
|
external_location: HTTP-only. ``ExternalLocationConfig`` that
|
|
482
513
|
controls how the client fetches pointer batches (workers
|
|
483
514
|
that externalize large outputs via demo storage / S3 return
|
|
@@ -535,12 +566,36 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
535
566
|
else:
|
|
536
567
|
raise ValueError(f"unknown transport {transport!r}")
|
|
537
568
|
|
|
569
|
+
use_oauth = oauth or oauth_refresh_token is not None
|
|
570
|
+
if use_oauth and bearer_token is not None:
|
|
571
|
+
raise ValueError("bearer_token is mutually exclusive with oauth/oauth_refresh_token")
|
|
572
|
+
if use_oauth and httpx_client is not None:
|
|
573
|
+
raise ValueError(
|
|
574
|
+
"oauth/oauth_refresh_token is mutually exclusive with httpx_client "
|
|
575
|
+
"(the escape hatch is verbatim-or-nothing — build your own VgiOAuthAuth "
|
|
576
|
+
"and pass it as httpx_client=httpx2.Client(auth=...) instead)"
|
|
577
|
+
)
|
|
578
|
+
if oauth_flow not in ("auto", "device_code", "pkce"):
|
|
579
|
+
raise ValueError(f"oauth_flow must be 'auto', 'device_code', or 'pkce', got {oauth_flow!r}")
|
|
580
|
+
if oauth_prompt not in ("none", "login", "select_account", "consent"):
|
|
581
|
+
raise ValueError(
|
|
582
|
+
f"oauth_prompt must be 'none', 'login', 'select_account', or 'consent', got {oauth_prompt!r}"
|
|
583
|
+
)
|
|
584
|
+
|
|
538
585
|
self.server_path: str | Sequence[str] = server_path if server_path is not None else ""
|
|
539
586
|
self._transport = transport
|
|
540
587
|
self._base_url = base_url
|
|
541
588
|
self._tcp_host = tcp_host
|
|
542
589
|
self._tcp_port = tcp_port
|
|
543
590
|
self._bearer_token = bearer_token
|
|
591
|
+
self._use_oauth = use_oauth
|
|
592
|
+
self._oauth_refresh_token = oauth_refresh_token
|
|
593
|
+
self._oauth_flow = oauth_flow
|
|
594
|
+
self._oauth_timeout_seconds = oauth_timeout_seconds
|
|
595
|
+
self._oauth_prompt = oauth_prompt
|
|
596
|
+
# Built lazily by _get_or_create_httpx_client when self._use_oauth; None otherwise
|
|
597
|
+
# (including when the caller supplied httpx_client= directly, which owns its own auth).
|
|
598
|
+
self._oauth_auth: Any | None = None
|
|
544
599
|
self._httpx_client = httpx_client
|
|
545
600
|
self._launch_argv = tuple(launch_argv) if launch_argv is not None else None
|
|
546
601
|
self._launch_idle_timeout = launch_idle_timeout
|
|
@@ -581,6 +636,11 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
581
636
|
base_url: str,
|
|
582
637
|
*,
|
|
583
638
|
bearer_token: str | None = None,
|
|
639
|
+
oauth: bool = False,
|
|
640
|
+
oauth_refresh_token: str | None = None,
|
|
641
|
+
oauth_flow: Literal["auto", "device_code", "pkce"] = "auto",
|
|
642
|
+
oauth_timeout_seconds: float = 120.0,
|
|
643
|
+
oauth_prompt: Literal["none", "login", "select_account", "consent"] = "none",
|
|
584
644
|
httpx_client: Any | None = None,
|
|
585
645
|
external_location: Any | None = None,
|
|
586
646
|
worker_limit: int | None = None,
|
|
@@ -590,12 +650,18 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
590
650
|
|
|
591
651
|
Canonical entry point for non-DuckDB callers (e.g. a TypeScript port
|
|
592
652
|
browsing catalog contents). Subprocess-specific kwargs are not
|
|
593
|
-
accepted; pool/stderr semantics do not apply.
|
|
653
|
+
accepted; pool/stderr semantics do not apply. See `[`Client.__init__`][]`
|
|
654
|
+
for what ``oauth``/``oauth_refresh_token``/``oauth_flow`` do.
|
|
594
655
|
"""
|
|
595
656
|
return cls(
|
|
596
657
|
transport="http",
|
|
597
658
|
base_url=base_url,
|
|
598
659
|
bearer_token=bearer_token,
|
|
660
|
+
oauth=oauth,
|
|
661
|
+
oauth_refresh_token=oauth_refresh_token,
|
|
662
|
+
oauth_flow=oauth_flow,
|
|
663
|
+
oauth_timeout_seconds=oauth_timeout_seconds,
|
|
664
|
+
oauth_prompt=oauth_prompt,
|
|
599
665
|
httpx_client=httpx_client,
|
|
600
666
|
external_location=external_location,
|
|
601
667
|
worker_limit=worker_limit,
|
|
@@ -882,23 +948,39 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
882
948
|
"""Return the shared httpx2.Client for this Client's HTTP transport.
|
|
883
949
|
|
|
884
950
|
Lazily constructs one bound to ``self._base_url`` (so RPC requests
|
|
885
|
-
resolve against the remote worker) with
|
|
886
|
-
<token>`` header
|
|
887
|
-
|
|
888
|
-
|
|
951
|
+
resolve against the remote worker) with either a static
|
|
952
|
+
``Authorization: Bearer <token>`` header (``bearer_token``) or a
|
|
953
|
+
``VgiOAuthAuth`` (``oauth``/``oauth_refresh_token``) that obtains and
|
|
954
|
+
refreshes tokens on demand — never both, enforced at construction.
|
|
955
|
+
When the caller passes ``httpx_client=`` directly, they're
|
|
956
|
+
responsible for configuring ``base_url`` and auth on it — we use it
|
|
957
|
+
verbatim.
|
|
889
958
|
"""
|
|
890
959
|
if self._httpx_client is not None:
|
|
891
960
|
return self._httpx_client
|
|
892
961
|
|
|
893
962
|
import httpx2
|
|
894
963
|
|
|
964
|
+
auth = None
|
|
895
965
|
headers: dict[str, str] = {}
|
|
896
|
-
if self.
|
|
966
|
+
if self._use_oauth:
|
|
967
|
+
from vgi_rpc.http import VgiOAuthAuth
|
|
968
|
+
|
|
969
|
+
self._oauth_auth = VgiOAuthAuth(
|
|
970
|
+
base_url=self._base_url or "",
|
|
971
|
+
flow=self._oauth_flow,
|
|
972
|
+
refresh_token=self._oauth_refresh_token,
|
|
973
|
+
timeout_seconds=self._oauth_timeout_seconds,
|
|
974
|
+
prompt=self._oauth_prompt,
|
|
975
|
+
)
|
|
976
|
+
auth = self._oauth_auth
|
|
977
|
+
elif self._bearer_token is not None:
|
|
897
978
|
headers["Authorization"] = f"Bearer {self._bearer_token}"
|
|
898
979
|
self._httpx_client = httpx2.Client(
|
|
899
980
|
base_url=self._base_url or "",
|
|
900
981
|
follow_redirects=True,
|
|
901
982
|
headers=headers,
|
|
983
|
+
auth=auth,
|
|
902
984
|
# httpx2's 5s default read timeout is too aggressive for RPC
|
|
903
985
|
# calls that do real server-side work (scans, cold workers).
|
|
904
986
|
timeout=httpx2.Timeout(60.0, connect=10.0),
|
|
@@ -906,6 +988,19 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
906
988
|
self._httpx_client_owned = True
|
|
907
989
|
return self._httpx_client
|
|
908
990
|
|
|
991
|
+
def oauth_identity(self) -> Any | None:
|
|
992
|
+
"""Return the signed-in OAuth identity's parsed id_token claims.
|
|
993
|
+
|
|
994
|
+
Returns ``None`` when this client isn't using OAuth, or hasn't
|
|
995
|
+
completed a login yet (the identity is only known once a real
|
|
996
|
+
exchange has happened — calling this before the first request
|
|
997
|
+
never triggers one). See ``vgi_rpc.http.OAuthIdentity`` for the
|
|
998
|
+
fields (``sub``/``email``/``name``/``issuer``/``claims``).
|
|
999
|
+
"""
|
|
1000
|
+
if self._oauth_auth is None:
|
|
1001
|
+
return None
|
|
1002
|
+
return self._oauth_auth.identity()
|
|
1003
|
+
|
|
909
1004
|
def _spawn_subprocess_connection(self, worker_index: int) -> WorkerConnection:
|
|
910
1005
|
"""Spawn or borrow a subprocess worker and wrap it in an RPC proxy.
|
|
911
1006
|
|
|
@@ -1214,6 +1309,12 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1214
1309
|
finally:
|
|
1215
1310
|
self._httpx_client = None
|
|
1216
1311
|
self._httpx_client_owned = False
|
|
1312
|
+
# Close VgiOAuthAuth's own internal (unauthenticated) flow client, if one was built.
|
|
1313
|
+
if self._oauth_auth is not None:
|
|
1314
|
+
try:
|
|
1315
|
+
self._oauth_auth.close()
|
|
1316
|
+
finally:
|
|
1317
|
+
self._oauth_auth = None
|
|
1217
1318
|
|
|
1218
1319
|
return returncode
|
|
1219
1320
|
|
|
@@ -1915,6 +2016,7 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1915
2016
|
settings: dict[str, Any] | None = None,
|
|
1916
2017
|
transaction_opaque_data: bytes | None = None,
|
|
1917
2018
|
parent_row_callback: Callable[[list[int]], None] | None = None,
|
|
2019
|
+
has_finalize: bool = True,
|
|
1918
2020
|
) -> Generator[pa.RecordBatch]:
|
|
1919
2021
|
"""Invoke a table-in-out function on the worker and stream results.
|
|
1920
2022
|
|
|
@@ -1956,6 +2058,20 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1956
2058
|
`FunctionInfo.input_from_args`); leave unset for ordinary
|
|
1957
2059
|
table-in-out functions, which have no provenance concept and
|
|
1958
2060
|
may legitimately change row count.
|
|
2061
|
+
has_finalize: Whether this function declares a FINALIZE stage
|
|
2062
|
+
(`FunctionInfo.has_finalize`). Defaults to `True` — every
|
|
2063
|
+
caller before this parameter existed got a FINALIZE-phase
|
|
2064
|
+
`init()` unconditionally, so this preserves that exactly.
|
|
2065
|
+
Pass `False` for a function known to have no finalize (every
|
|
2066
|
+
blended `RowTransformFunction` — `has_finalize` is always
|
|
2067
|
+
false for those, enforced at `resolve_metadata()`) to skip
|
|
2068
|
+
the FINALIZE `init()` entirely, not just send one expecting
|
|
2069
|
+
an empty reply. Confirmed load-bearing, not cosmetic: some
|
|
2070
|
+
worker SDKs (e.g. the TypeScript one) actively reject an
|
|
2071
|
+
unexpected FINALIZE `init()` for a function that never
|
|
2072
|
+
advertised `has_finalize`, rather than silently no-op'ing
|
|
2073
|
+
it — the C++ DuckDB extension avoids this the same way, by
|
|
2074
|
+
conditionally registering `in_out_function_final` at all.
|
|
1959
2075
|
|
|
1960
2076
|
Yields:
|
|
1961
2077
|
Output `RecordBatch`es from the function. In single-worker mode, output
|
|
@@ -2019,7 +2135,14 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
2019
2135
|
# Close secondary workers
|
|
2020
2136
|
self._close_secondary_workers()
|
|
2021
2137
|
|
|
2022
|
-
# Finalize on primary worker
|
|
2138
|
+
# Finalize on primary worker — skipped entirely (not just
|
|
2139
|
+
# sent expecting an empty reply) when the function has no
|
|
2140
|
+
# finalize stage. See has_finalize's docstring: some worker
|
|
2141
|
+
# SDKs reject an unadvertised FINALIZE init() outright.
|
|
2142
|
+
if not has_finalize:
|
|
2143
|
+
_logger.debug("skipping_finalize_no_finalize_stage")
|
|
2144
|
+
return
|
|
2145
|
+
|
|
2023
2146
|
_logger.debug("finalizing_primary_worker")
|
|
2024
2147
|
yield from self._finalize_primary_worker(
|
|
2025
2148
|
bind_request,
|
|
@@ -513,7 +513,7 @@ def main() -> None:
|
|
|
513
513
|
"--exception-include",
|
|
514
514
|
default='"duckdb/common/exception.hpp"',
|
|
515
515
|
help=(
|
|
516
|
-
|
|
516
|
+
'#include directive (with its own <> or "") for the exception '
|
|
517
517
|
"type named by --exception-type (default: "
|
|
518
518
|
'"duckdb/common/exception.hpp"). E.g. <stdexcept> for '
|
|
519
519
|
"std::runtime_error."
|