vgi-python 0.29.6__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.6 → vgi_python-0.30.0}/PKG-INFO +2 -2
- {vgi_python-0.29.6 → vgi_python-0.30.0}/pyproject.toml +2 -2
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_batch_metadata_callback.py +1 -1
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_scan_branches_client.py +8 -5
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_time_travel_client.py +1 -1
- vgi_python-0.30.0/tests/test_client_oauth.py +204 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/uv.lock +5 -5
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/client.py +112 -11
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/cpp_request_builders.py +1 -1
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/worker.py +44 -15
- {vgi_python-0.29.6 → vgi_python-0.30.0}/.gitattributes +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/.github/dependabot.yml +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/.github/workflows/integration.yml +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/.github/workflows/release.yml +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/.gitignore +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/.python-version +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/.vale.ini +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/CLAUDE.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/LICENSE +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/README.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/RELEASE-RUNBOOK.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/SECURITY.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/ci/README.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/ci/run-integration.sh +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/arguments.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/auth.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/catalogs.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/client.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/exceptions.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/filters.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/functions.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/http.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/index.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/metadata.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/observability.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/storage.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/transactor.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/api/worker.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/argument-serialization.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/logo.png +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/assets/social-card.png +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/authentication.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/catalog-interface.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/cli.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/column-statistics.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/concepts/index.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/contributing-docs.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/generator-api.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/global-functions.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/how-to/index.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/index.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/lifecycle.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/metadata.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/overrides/main.html +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/robots.txt +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/shared-storage.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/tutorial/index.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/tutorial/table.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/docs/vgi-logo.png +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/examples/batch_index_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/examples/cache_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/examples/calc_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/examples/copy_format_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/examples/filter_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/examples/row_count_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/examples/sum_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/mkdocs.yml +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/packages/vgi-fixtures/pyproject.toml +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/scripts/measure_startup.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/test-data/generate.sh +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_attach_option.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_declarative.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_example_worker_catalog.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_required_filters.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_serialization.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_aggregate_client.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_cli.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_copy_client.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_split_client.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_stop_force.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_table_column_statistics.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_const_param_enforcement.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_container.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_copy_from.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_copy_to.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_directory_parity.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_github.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_protocol_version.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_secret.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_splits.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/conftest.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/anchor_len_overrun.bin +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/bad_fingerprint.bin +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/bad_flags_unsealed_but_key_present.bin +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/bad_version.bin +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/generate.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/manifest.json +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/reserved_flag_bit.bin +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/stale_anchor.bin +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/truncated.bin +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/valid_sealed.bin +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/data/split_tokens/valid_unsealed.bin +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table/test_wide_columns.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/test_blended_exchange.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/test_blended_metadata.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_aggregate_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_argument_spec.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_auth.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_cache_control.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_call_state_attach.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_copy_to_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_docstrings.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_exceptions.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_fixture_catalog_parity.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_function_storage.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_java_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_rust_request_builders.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_rust_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_launch_transport.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_metadata.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_otel.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_protocol_classes.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_schema_scoped_functions.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_serve.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_setting_secret_annotations.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_signing_key.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_split_token.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_tcp_transport.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_union_argument.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_wire_record_schema_parity.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/test_worker_page.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_duckdb.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/same_name.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/companion.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/copy_from.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/copy_to.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/global_functions.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/http_server.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/formatting.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/same_name.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/_common.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/cache.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/filters.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/same_name_cached.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/splits.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table/wide.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table_in_out.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/table_in_out_same_name.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/twin_catalogs.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/aggregate_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/argument_spec.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/arguments.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/attach_header.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/auth.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/cache_control.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/catalog/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/catalog/catalog_interface.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/catalog/descriptors.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/aggregate.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/catalog_mixin.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/cli.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/client/errors.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/_common.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/java_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/rust_request_builders.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/rust_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/copy_from_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/copy_to_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/exceptions.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/function_storage.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/http/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/http/landing.html +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/http/landing_page.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/http/vgi-client.js +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/invocation.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/logging_config.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/meta_worker.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/metadata.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/otel.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/profiling.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/protocol.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/protocol_version.txt +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/py.typed +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/scalar_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/schema_utils.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/secret_service.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/serve.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/split_token.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/table_filter_pushdown.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/table_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/transactor/client.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.29.6 → vgi_python-0.30.0}/vgi/transactor/server.py +0 -0
- {vgi_python-0.29.6 → 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
|
|
@@ -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
|
|
|
@@ -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."
|
|
@@ -1613,10 +1613,23 @@ class Worker:
|
|
|
1613
1613
|
max_externalized_response_bytes=max_externalized_response_bytes,
|
|
1614
1614
|
)
|
|
1615
1615
|
elif unix is not None:
|
|
1616
|
-
# AF_UNIX launcher path
|
|
1617
|
-
#
|
|
1618
|
-
#
|
|
1619
|
-
|
|
1616
|
+
# AF_UNIX launcher path on POSIX; Windows has no
|
|
1617
|
+
# socket.AF_UNIX in this code path, so --unix there
|
|
1618
|
+
# carries a named-pipe name (\\.\pipe\...) instead - the
|
|
1619
|
+
# launcher constructs whichever one is appropriate for the
|
|
1620
|
+
# platform it's running on. Mirrors vgi_rpc.rpc.run_server's
|
|
1621
|
+
# own platform dispatch (vgi_rpc/rpc/__init__.py) exactly -
|
|
1622
|
+
# this classmethod has its own, separate serving path (not
|
|
1623
|
+
# a call to run_server) because it also handles Sentry/OTel
|
|
1624
|
+
# setup and cls-specific worker construction, but the
|
|
1625
|
+
# transport-level platform choice must stay identical
|
|
1626
|
+
# between the two, or a worker built against this
|
|
1627
|
+
# classmethod (e.g. any vgi.worker.Worker subclass) crashes
|
|
1628
|
+
# on Windows with "AttributeError: module 'socket' has no
|
|
1629
|
+
# attribute 'AF_UNIX'" while one built directly against
|
|
1630
|
+
# run_server works fine - confirmed as a real, live bug
|
|
1631
|
+
# this way, not theoretical.
|
|
1632
|
+
from vgi_rpc.rpc import serve_named_pipe, serve_unix
|
|
1620
1633
|
|
|
1621
1634
|
from vgi.serve import _maybe_init_sentry, _resolve_otel_config
|
|
1622
1635
|
|
|
@@ -1629,20 +1642,36 @@ class Worker:
|
|
|
1629
1642
|
|
|
1630
1643
|
instrument_server(server, otel_config)
|
|
1631
1644
|
worker._vgi_tracer = VgiTracer.create(otel_config)
|
|
1632
|
-
abs_path = os.path.abspath(unix)
|
|
1633
1645
|
effective_idle = idle_timeout if idle_timeout > 0 else None
|
|
1634
1646
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1647
|
+
if sys.platform == "win32":
|
|
1648
|
+
pipe_name = unix
|
|
1637
1649
|
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1650
|
+
def _emit(bound: str) -> None:
|
|
1651
|
+
print(f"PIPE:{bound}", flush=True)
|
|
1652
|
+
|
|
1653
|
+
serve_named_pipe(
|
|
1654
|
+
server,
|
|
1655
|
+
pipe_name,
|
|
1656
|
+
threaded=True,
|
|
1657
|
+
max_connections=max_connections,
|
|
1658
|
+
idle_timeout=effective_idle,
|
|
1659
|
+
on_bound=_emit,
|
|
1660
|
+
)
|
|
1661
|
+
else:
|
|
1662
|
+
abs_path = os.path.abspath(unix)
|
|
1663
|
+
|
|
1664
|
+
def _emit(bound: str) -> None:
|
|
1665
|
+
print(f"UNIX:{bound}", flush=True)
|
|
1666
|
+
|
|
1667
|
+
serve_unix(
|
|
1668
|
+
server,
|
|
1669
|
+
abs_path,
|
|
1670
|
+
threaded=True,
|
|
1671
|
+
max_connections=max_connections,
|
|
1672
|
+
idle_timeout=effective_idle,
|
|
1673
|
+
on_bound=_emit,
|
|
1674
|
+
)
|
|
1646
1675
|
elif tcp is not None:
|
|
1647
1676
|
# TCP launcher path. Bind to [HOST:]PORT, print
|
|
1648
1677
|
# TCP:<host>:<port> on stdout (mirrors run_server's
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|