vgi-python 0.31.1__tar.gz → 0.31.3__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.31.3/.dockerignore +15 -0
- vgi_python-0.31.3/.github/workflows/tailnet-integration.yml +49 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/PKG-INFO +4 -2
- {vgi_python-0.31.1 → vgi_python-0.31.3}/ci/run-integration.sh +15 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/how-to/http-auth.md +19 -0
- vgi_python-0.31.3/docs/how-to/iroh.md +91 -0
- vgi_python-0.31.3/docs/how-to/tailnet.md +195 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/mkdocs.yml +2 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/pyproject.toml +3 -2
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_example_worker_catalog.py +32 -0
- vgi_python-0.31.3/tests/client/test_bind_and_join_keys.py +134 -0
- vgi_python-0.31.3/tests/client/test_http_response_budget.py +109 -0
- vgi_python-0.31.3/tests/client/test_iroh_client.py +66 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_directory_parity.py +9 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/test_wide_columns.py +1 -1
- vgi_python-0.31.3/tests/tailnet/Dockerfile +13 -0
- vgi_python-0.31.3/tests/tailnet/__init__.py +1 -0
- vgi_python-0.31.3/tests/tailnet/compose.yaml +182 -0
- vgi_python-0.31.3/tests/tailnet/probe.py +113 -0
- vgi_python-0.31.3/tests/tailnet/run.sh +108 -0
- vgi_python-0.31.3/tests/tailnet/test_worker.py +64 -0
- vgi_python-0.31.3/tests/tailnet/worker.py +198 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_call_state_attach.py +2 -2
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_filter_pushdown.py +69 -1
- vgi_python-0.31.3/tests/test_function_shape_mismatch.py +84 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_launch_transport.py +3 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_serve.py +165 -1
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_setting_secret_annotations.py +62 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_tcp_transport.py +75 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/uv.lock +25 -6
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/catalog_mixin.py +64 -9
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/client.py +351 -25
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/serve.py +155 -2
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/table_filter_pushdown.py +20 -2
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/table_in_out_function.py +12 -1
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/worker.py +192 -10
- {vgi_python-0.31.1 → vgi_python-0.31.3}/.gitattributes +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/.github/dependabot.yml +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/.github/workflows/integration.yml +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/.github/workflows/release.yml +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/.gitignore +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/.python-version +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/.vale.ini +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/CLAUDE.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/LICENSE +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/README.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/RELEASE-RUNBOOK.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/SECURITY.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/ci/README.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/arguments.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/auth.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/catalogs.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/client.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/exceptions.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/filters.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/functions.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/http.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/index.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/metadata.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/observability.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/storage.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/transactor.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/api/worker.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/argument-serialization.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/logo.png +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/assets/social-card.png +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/authentication.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/catalog-interface.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/cli.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/column-statistics.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/concepts/index.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/contributing-docs.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/generator-api.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/global-functions.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/how-to/index.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/index.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/lifecycle.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/metadata.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/overrides/main.html +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/robots.txt +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/shared-storage.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/tutorial/index.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/tutorial/table.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/docs/vgi-logo.png +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/examples/batch_index_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/examples/cache_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/examples/calc_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/examples/copy_format_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/examples/filter_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/examples/row_count_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/examples/sum_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/packages/vgi-fixtures/pyproject.toml +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/scripts/measure_startup.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/test-data/generate.sh +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_attach_option.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_declarative.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_required_filters.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_serialization.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_aggregate_client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_batch_metadata_callback.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_cli.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_copy_client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_scan_branches_client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_split_client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_stop_force.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_table_column_statistics.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_time_travel_client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_const_param_enforcement.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_container.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_copy_from.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_copy_to.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_github.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_protocol_version.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_secret.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_splits.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/conftest.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/anchor_len_overrun.bin +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/bad_fingerprint.bin +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/bad_flags_unsealed_but_key_present.bin +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/bad_version.bin +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/generate.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/manifest.json +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/reserved_flag_bit.bin +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/stale_anchor.bin +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/truncated.bin +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/valid_sealed.bin +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/data/split_tokens/valid_unsealed.bin +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/test_blended_exchange.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/test_blended_metadata.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_aggregate_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_argument_spec.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_auth.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_cache_control.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_client_oauth.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_copy_to_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_docstrings.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_exceptions.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_fixture_catalog_parity.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_function_storage.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_java_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_rust_request_builders.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_rust_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_metadata.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_otel.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_protocol_classes.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_schema_scoped_functions.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_signing_key.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_split_token.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_union_argument.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_wire_record_schema_parity.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/test_worker_page.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_duckdb.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/same_name.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/companion.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/copy_from.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/copy_to.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/global_functions.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/http_server.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/formatting.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/same_name.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/_common.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/cache.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/filters.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/same_name_cached.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/same_name_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/splits.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table/wide.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table_in_out.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/table_in_out_same_name.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/twin_catalogs.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/aggregate_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/argument_spec.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/arguments.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/attach_header.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/auth.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/cache_control.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/catalog/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/catalog/catalog_interface.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/catalog/descriptors.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/aggregate.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/cli.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/client/errors.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/_common.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/cpp_request_builders.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/java_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/rust_request_builders.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/rust_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/copy_from_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/copy_to_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/exceptions.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/function_storage.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/http/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/http/landing.html +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/http/landing_page.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/http/vgi-client.js +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/invocation.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/logging_config.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/meta_worker.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/metadata.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/otel.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/profiling.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/protocol.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/protocol_version.txt +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/py.typed +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/scalar_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/schema_utils.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/secret_service.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/split_token.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/table_function.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/transactor/client.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/vgi/transactor/server.py +0 -0
- {vgi_python-0.31.1 → vgi_python-0.31.3}/wrangler.jsonc +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Real Tailnet authentication
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
tailscale_channel:
|
|
7
|
+
description: Official Tailscale container channel
|
|
8
|
+
required: true
|
|
9
|
+
default: stable
|
|
10
|
+
type: choice
|
|
11
|
+
options: [stable, unstable]
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: vgi-python-real-tailnet
|
|
18
|
+
cancel-in-progress: false
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
tailnet:
|
|
22
|
+
name: high-level worker / ${{ inputs.tailscale_channel }}
|
|
23
|
+
runs-on: ubuntu-24.04
|
|
24
|
+
timeout-minutes: 30
|
|
25
|
+
environment: tailnet-integration
|
|
26
|
+
env:
|
|
27
|
+
TAILSCALE_IMAGE: tailscale/tailscale:${{ inputs.tailscale_channel }}
|
|
28
|
+
TS_OAUTH_CLIENT_ID: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
|
29
|
+
TS_OAUTH_SECRET: ${{ secrets.TS_OAUTH_SECRET }}
|
|
30
|
+
TAILNET_ISSUER: ${{ vars.TAILNET_ISSUER }}
|
|
31
|
+
TAILNET_EXPECTED_CAPABILITY: ${{ vars.TAILNET_EXPECTED_CAPABILITY }}
|
|
32
|
+
TAILNET_EXPECTED_CLIENT_TAG: ${{ vars.TAILNET_EXPECTED_CLIENT_TAG }}
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v7
|
|
35
|
+
|
|
36
|
+
- name: Checkout unreleased vgi-rpc identity layer
|
|
37
|
+
uses: actions/checkout@v7
|
|
38
|
+
with:
|
|
39
|
+
repository: Query-farm/vgi-rpc-python
|
|
40
|
+
path: .tailnet/vgi-rpc
|
|
41
|
+
ref: 04f19147625b4f643b38a687151d8233daa8997c
|
|
42
|
+
|
|
43
|
+
- name: Check kernel networking support
|
|
44
|
+
run: |
|
|
45
|
+
test -c /dev/net/tun
|
|
46
|
+
docker compose version
|
|
47
|
+
|
|
48
|
+
- name: Run high-level Tailnet authentication matrix
|
|
49
|
+
run: tests/tailnet/run.sh
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.31.
|
|
3
|
+
Version: 0.31.3
|
|
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.45.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'
|
|
@@ -198,6 +198,8 @@ Provides-Extra: haybarn
|
|
|
198
198
|
Requires-Dist: haybarn>=1.5.5rc1; extra == 'haybarn'
|
|
199
199
|
Provides-Extra: http
|
|
200
200
|
Requires-Dist: vgi-rpc[http]; extra == 'http'
|
|
201
|
+
Provides-Extra: iroh
|
|
202
|
+
Requires-Dist: vgi-rpc[http,iroh]; extra == 'iroh'
|
|
201
203
|
Provides-Extra: launch
|
|
202
204
|
Requires-Dist: vgi-rpc[cli]; extra == 'launch'
|
|
203
205
|
Provides-Extra: oauth
|
|
@@ -86,6 +86,10 @@ if [ "$TRANSPORT" = "http" ]; then
|
|
|
86
86
|
# * buffer_input/sizes.test — input buffering semantics differ over HTTP
|
|
87
87
|
# (same known limitation). (scale.test_slow is a .test_slow file and is
|
|
88
88
|
# never staged below, which only finds *.test.)
|
|
89
|
+
# * database_worker/package.test — the packaged fixture is deliberately an
|
|
90
|
+
# executable wrapper around VGI_TEST_WORKER. An HTTP URL is not an
|
|
91
|
+
# executable artifact; the direct-exec lifecycle is covered by all three
|
|
92
|
+
# process/socket lanes.
|
|
89
93
|
# cache/revalidate.test now runs on http too: HTTP conditional revalidation is
|
|
90
94
|
# implemented (C++ /init-request validators + vgi-rpc >=0.24.0 surfacing them to
|
|
91
95
|
# the producer's first process()). It needs a community vgi extension carrying
|
|
@@ -96,6 +100,7 @@ if [ "$TRANSPORT" = "http" ]; then
|
|
|
96
100
|
-not -name 'dynamic_filter.test'
|
|
97
101
|
-not -name 'partitioned_sequence.test'
|
|
98
102
|
-not -path './table_in_out/buffer_input/sizes.test'
|
|
103
|
+
-not -path './database_worker/package.test'
|
|
99
104
|
)
|
|
100
105
|
fi
|
|
101
106
|
|
|
@@ -120,6 +125,16 @@ mkdir -p "$STAGE/test/sql/integration"
|
|
|
120
125
|
awk -v http="$AWK_HTTP" -f "$HERE/preprocess-require.awk" "$f" > "$STAGE/test/sql/integration/$f"
|
|
121
126
|
done )
|
|
122
127
|
|
|
128
|
+
# Some integration cases package an executable from the upstream support tree.
|
|
129
|
+
# The staged suite runs with ``$STAGE`` as its working directory, so preserve
|
|
130
|
+
# the relative path used by those SQL fixtures instead of making it depend on
|
|
131
|
+
# the checkout layout outside the stage.
|
|
132
|
+
if [ -f "$VGI_SRC/test/support/database_worker_fixture.sh" ]; then
|
|
133
|
+
mkdir -p "$STAGE/test/support"
|
|
134
|
+
cp "$VGI_SRC/test/support/database_worker_fixture.sh" "$STAGE/test/support/"
|
|
135
|
+
chmod +x "$STAGE/test/support/database_worker_fixture.sh"
|
|
136
|
+
fi
|
|
137
|
+
|
|
123
138
|
# Empty VGI_RPC_SHM_SIZE_BYTES must not reach the C++ client (it would try to
|
|
124
139
|
# attach a zero-size segment); only a real value enables the shm side channel.
|
|
125
140
|
[ -n "${VGI_RPC_SHM_SIZE_BYTES:-}" ] || unset VGI_RPC_SHM_SIZE_BYTES
|
|
@@ -35,6 +35,25 @@ with Client(transport="http", base_url="http://localhost:8080", bearer_token="to
|
|
|
35
35
|
... # same .scalar_function() / .table_function() calls as the subprocess transport
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
The native client advertises a 256 MiB decoded-response limit on every RPC,
|
|
39
|
+
including the short-lived connections used for catalog operations. Override it
|
|
40
|
+
when the application has a smaller memory budget:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
with Client.from_http(
|
|
44
|
+
"http://localhost:8080",
|
|
45
|
+
accepted_max_response_bytes=32 * 1024 * 1024,
|
|
46
|
+
) as client:
|
|
47
|
+
...
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The value must be an integer from 65536 through `2^53 - 1`. Configured clients
|
|
51
|
+
discover server support before the first RPC and fail closed if it is absent.
|
|
52
|
+
Passing `None` omits negotiation for compatibility with a legacy unbounded server;
|
|
53
|
+
doing so is not recommended for externally reachable workers. Capability
|
|
54
|
+
discovery reports the server's own effective response cap and whether it
|
|
55
|
+
honors the client limit.
|
|
56
|
+
|
|
38
57
|
## Add authentication
|
|
39
58
|
|
|
40
59
|
The quickest setup is static bearer tokens via an environment variable — comma-separated
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Serve and call a worker over Iroh
|
|
2
|
+
|
|
3
|
+
VGI supports two authenticated Iroh endpoint forms:
|
|
4
|
+
|
|
5
|
+
- `iroh://<endpoint-id>` keeps stream state on one connection.
|
|
6
|
+
- `httpi://<endpoint-id>[/prefix]` preserves VGI's stateless HTTP exchanges,
|
|
7
|
+
continuation tokens, request/response budgets, authentication headers, and
|
|
8
|
+
externalized batches.
|
|
9
|
+
|
|
10
|
+
Install the optional native client binding:
|
|
11
|
+
|
|
12
|
+
```console
|
|
13
|
+
pip install 'vgi-python[iroh]'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Start a raw upstream
|
|
17
|
+
|
|
18
|
+
The language-neutral `vgi-iroh-bridge` owns the public Iroh endpoint. The
|
|
19
|
+
Python process binds only loopback and requires the bridge's PROXY-v2 identity
|
|
20
|
+
preamble:
|
|
21
|
+
|
|
22
|
+
```console
|
|
23
|
+
vgi-serve my_worker.py \
|
|
24
|
+
--iroh-raw-upstream 127.0.0.1:9400 \
|
|
25
|
+
--iroh-issuer production
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
In another process:
|
|
29
|
+
|
|
30
|
+
```console
|
|
31
|
+
vgi-iroh-bridge \
|
|
32
|
+
--secret-key-file /run/secrets/vgi-iroh-key \
|
|
33
|
+
--raw-upstream tcp://127.0.0.1:9400
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The bridge prints its EndpointId as the first stdout line. Development may use
|
|
37
|
+
`--ephemeral`; a production endpoint must use a persistent secret key.
|
|
38
|
+
The raw Iroh listener also enables standard RPC method description so
|
|
39
|
+
standalone clients in other VGI languages can bootstrap directly.
|
|
40
|
+
|
|
41
|
+
## Start an HTTP upstream
|
|
42
|
+
|
|
43
|
+
HTTP-over-Iroh is the preferred mode for horizontally scaled workers:
|
|
44
|
+
|
|
45
|
+
```console
|
|
46
|
+
vgi-serve my_worker.py \
|
|
47
|
+
--http --host 127.0.0.1 --port 9401 \
|
|
48
|
+
--iroh-issuer production
|
|
49
|
+
|
|
50
|
+
vgi-iroh-bridge \
|
|
51
|
+
--secret-key-file /run/secrets/vgi-iroh-key \
|
|
52
|
+
--http-upstream http://127.0.0.1:9401
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`--iroh-issuer` makes the worker trust exactly `127.0.0.1` by default and
|
|
56
|
+
promotes the bridge-verified EndpointId to `CallContext.auth`. Use repeated
|
|
57
|
+
`--iroh-trusted-proxy <exact-ip>` when the immediate bridge address differs.
|
|
58
|
+
Use `--iroh-observe` to expose evidence without authenticating from it.
|
|
59
|
+
|
|
60
|
+
Never expose the bridge-facing listener as a general public HTTP or TCP
|
|
61
|
+
listener. If another proxy sits between the bridge and worker, isolate that
|
|
62
|
+
route, clear client-supplied `VGI-Forwarded-Iroh-Endpoint` fields on every other
|
|
63
|
+
route, and configure the worker to trust only the intermediary's exact address.
|
|
64
|
+
|
|
65
|
+
## Connect
|
|
66
|
+
|
|
67
|
+
The same high-level client accepts both endpoint schemes:
|
|
68
|
+
|
|
69
|
+
```python test="skip"
|
|
70
|
+
from vgi.client import Client
|
|
71
|
+
|
|
72
|
+
with Client.from_iroh("iroh://<endpoint-id>") as client:
|
|
73
|
+
print(client.catalogs())
|
|
74
|
+
|
|
75
|
+
with Client.from_iroh("httpi://<endpoint-id>/vgi") as client:
|
|
76
|
+
print(client.catalogs())
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For a private relay:
|
|
80
|
+
|
|
81
|
+
```python test="skip"
|
|
82
|
+
client = Client.from_iroh(
|
|
83
|
+
"httpi://<endpoint-id>/vgi",
|
|
84
|
+
relay_urls=["https://relay.example.com"],
|
|
85
|
+
remote_relay_url="https://relay.example.com",
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The local relay set and the remote relay hint are distinct. `no_relay=True`
|
|
90
|
+
disables relays and requires a working direct path; it never falls back to the
|
|
91
|
+
public relay network.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "How to connect the Python VGI client to workers through Tailscale over HTTPS, direct TCP, or userspace SOCKS5h."
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Run VGI through a Tailnet
|
|
6
|
+
|
|
7
|
+
The VGI client uses ordinary HTTPS and TCP endpoints. It does not join a
|
|
8
|
+
Tailnet, manage Tailscale credentials, or introduce a `tailscale://` URL
|
|
9
|
+
scheme. Run Tailscale on the client host or in a sidecar and pass the worker's
|
|
10
|
+
MagicDNS name to the normal client factory.
|
|
11
|
+
|
|
12
|
+
Connectivity and authentication are separate. A client can reach a worker
|
|
13
|
+
through a Tailnet without the worker using Tailnet identity. To authorize from
|
|
14
|
+
the caller's verified Tailscale user, node, tags, or application capabilities,
|
|
15
|
+
configure a peer-identity provider and an authentication policy on the worker.
|
|
16
|
+
|
|
17
|
+
## Authenticate an HTTP worker
|
|
18
|
+
|
|
19
|
+
When Tailscale Serve terminates HTTPS, trust its identity headers only from the
|
|
20
|
+
exact local proxy addresses that can reach the application. `require_peer_identity`
|
|
21
|
+
requires verified evidence but deliberately does not invent a stable principal
|
|
22
|
+
for capability-only or login-scoped Serve requests:
|
|
23
|
+
|
|
24
|
+
```python test="lint"
|
|
25
|
+
from vgi_rpc.http import tailscale_serve_header_provider
|
|
26
|
+
from vgi_rpc.rpc import require_peer_identity
|
|
27
|
+
|
|
28
|
+
from my_worker import MyWorker
|
|
29
|
+
from vgi.serve import create_app
|
|
30
|
+
|
|
31
|
+
tailscale = tailscale_serve_header_provider(
|
|
32
|
+
issuer="tailnet:example",
|
|
33
|
+
trusted_proxy_addresses=("127.0.0.1", "::1"),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
app = create_app(
|
|
37
|
+
MyWorker,
|
|
38
|
+
peer_identity_providers=(tailscale,),
|
|
39
|
+
peer_authentication_policy=require_peer_identity("tailscale"),
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Serve login names have `subject_stability="login"`; tagged clients may be
|
|
44
|
+
capability-only and have no subject at all. Consequently, the built-in
|
|
45
|
+
`peer_identity_primary("tailscale")` policy is intentionally unsuitable for
|
|
46
|
+
Serve. If a deployment elects to authenticate from a Serve login, it must use
|
|
47
|
+
an explicit application policy that accepts that weaker identity and scopes it
|
|
48
|
+
to the configured issuer and trusted proxy boundary.
|
|
49
|
+
|
|
50
|
+
For direct HTTP over a Tailnet, LocalAPI WhoIs can produce a stable user or
|
|
51
|
+
node subject and therefore can be the primary authenticator:
|
|
52
|
+
|
|
53
|
+
```python test="lint"
|
|
54
|
+
from vgi_rpc.http import tailscale_localapi_provider
|
|
55
|
+
from vgi_rpc.rpc import peer_identity_primary
|
|
56
|
+
|
|
57
|
+
from my_worker import MyWorker
|
|
58
|
+
from vgi.serve import create_app
|
|
59
|
+
|
|
60
|
+
tailscale = tailscale_localapi_provider(issuer="tailnet:example")
|
|
61
|
+
app = create_app(
|
|
62
|
+
MyWorker,
|
|
63
|
+
peer_identity_providers=(tailscale,),
|
|
64
|
+
peer_authentication_policy=peer_identity_primary("tailscale"),
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`create_app()` remains a standard WSGI application, so the same configuration
|
|
69
|
+
works behind Waitress, Granian, gunicorn, Envoy, nginx, or a cloud HTTP load
|
|
70
|
+
balancer. The provider determines which proxy evidence is trusted; merely
|
|
71
|
+
placing the worker behind a proxy never makes forwarded identity trustworthy.
|
|
72
|
+
|
|
73
|
+
Trusted-proxy validation must see the physical socket peer. Do not install
|
|
74
|
+
`ProxyFix`, forwarded-address middleware, or an equivalent rewrite ahead of
|
|
75
|
+
VGI's identity middleware: attacker-controlled `Forwarded` or
|
|
76
|
+
`X-Forwarded-For` values must never become `REMOTE_ADDR` before the exact proxy
|
|
77
|
+
allowlist is checked. Keep the backend unreachable except through those
|
|
78
|
+
allowlisted proxies.
|
|
79
|
+
|
|
80
|
+
## HTTPS through Tailscale Serve
|
|
81
|
+
|
|
82
|
+
On a host using Tailscale's normal network interface, connect to a worker
|
|
83
|
+
published with Tailscale Serve exactly like any other HTTPS worker:
|
|
84
|
+
|
|
85
|
+
```python test="lint"
|
|
86
|
+
from vgi.client import Client
|
|
87
|
+
|
|
88
|
+
with Client.from_http("https://worker.example-tailnet.ts.net") as client:
|
|
89
|
+
catalogs = client.catalogs()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Tailscale terminates HTTPS and adds its verified Serve identity and application
|
|
93
|
+
capability headers. The VGI server validates those headers against its trusted
|
|
94
|
+
proxy configuration; the client neither constructs nor trusts identity
|
|
95
|
+
headers. Do not send `Tailscale-*` headers from application code.
|
|
96
|
+
|
|
97
|
+
For userspace networking, inject an HTTP client configured for Tailscale's
|
|
98
|
+
SOCKS5 server. Install SOCKS support with `pip install 'httpx2[socks]'`:
|
|
99
|
+
|
|
100
|
+
```python test="lint"
|
|
101
|
+
import httpx2
|
|
102
|
+
|
|
103
|
+
from vgi.client import Client
|
|
104
|
+
|
|
105
|
+
url = "https://worker.example-tailnet.ts.net"
|
|
106
|
+
with httpx2.Client(
|
|
107
|
+
base_url=url,
|
|
108
|
+
proxy="socks5h://127.0.0.1:1055",
|
|
109
|
+
trust_env=False,
|
|
110
|
+
timeout=httpx2.Timeout(60.0, connect=15.0),
|
|
111
|
+
) as http_client, Client.from_http(url, httpx_client=http_client) as client:
|
|
112
|
+
catalogs = client.catalogs()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Using `socks5h` keeps MagicDNS resolution inside the Tailscale sidecar. A proxy
|
|
116
|
+
failure is an error; this configuration does not fall back to a direct route.
|
|
117
|
+
|
|
118
|
+
## Raw TCP through a Tailnet
|
|
119
|
+
|
|
120
|
+
Start a high-level Worker with LocalAPI identity using `Worker.serve_tcp()`:
|
|
121
|
+
|
|
122
|
+
```python test="lint"
|
|
123
|
+
from vgi_rpc.http import tailscale_localapi_provider
|
|
124
|
+
from vgi_rpc.rpc import peer_identity_primary
|
|
125
|
+
|
|
126
|
+
from my_worker import MyWorker
|
|
127
|
+
|
|
128
|
+
tailscale = tailscale_localapi_provider(issuer="tailnet:example")
|
|
129
|
+
MyWorker().serve_tcp(
|
|
130
|
+
"0.0.0.0",
|
|
131
|
+
9400,
|
|
132
|
+
threaded=True,
|
|
133
|
+
max_connections=64,
|
|
134
|
+
peer_identity_providers=(tailscale,),
|
|
135
|
+
peer_authentication_policy=peer_identity_primary("tailscale"),
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The connection's verified stable Tailnet subject becomes `CallContext.auth` and
|
|
140
|
+
is available to function parameters annotated with `Auth()`. Identity is
|
|
141
|
+
snapshotted once for the stateful TCP connection.
|
|
142
|
+
|
|
143
|
+
When `threaded=True`, all connections share the same Worker instance. Protect
|
|
144
|
+
mutable subclass state with appropriate synchronization and never store
|
|
145
|
+
per-caller authentication in shared instance attributes. Leave the default
|
|
146
|
+
serial mode in place if the implementation is not safe for concurrent calls.
|
|
147
|
+
|
|
148
|
+
Behind a Tailscale Service configured to emit PROXY protocol v2, require the
|
|
149
|
+
preamble and name every immediate proxy address explicitly:
|
|
150
|
+
|
|
151
|
+
```python test="lint"
|
|
152
|
+
MyWorker().serve_tcp(
|
|
153
|
+
"127.0.0.1",
|
|
154
|
+
9400,
|
|
155
|
+
proxy_protocol="required",
|
|
156
|
+
trusted_proxy_addresses=("127.0.0.1", "::1"),
|
|
157
|
+
service_name="svc:vgi-worker",
|
|
158
|
+
peer_identity_providers=(tailscale,),
|
|
159
|
+
peer_authentication_policy=peer_identity_primary("tailscale"),
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Keep that backend unreachable except through the trusted proxy. The asserted
|
|
164
|
+
source is accepted only after immediate-peer validation, and `service_name`
|
|
165
|
+
selects destination-scoped application capabilities in LocalAPI WhoIs.
|
|
166
|
+
|
|
167
|
+
Direct TCP works with a MagicDNS name when the client host has Tailscale's
|
|
168
|
+
normal network interface:
|
|
169
|
+
|
|
170
|
+
```python test="lint"
|
|
171
|
+
from vgi.client import Client
|
|
172
|
+
|
|
173
|
+
with Client.from_tcp("worker.example-tailnet.ts.net", 9400) as client:
|
|
174
|
+
catalogs = client.catalogs()
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
For a userspace Tailscale sidecar, use the explicit SOCKS5h option:
|
|
178
|
+
|
|
179
|
+
```python test="lint"
|
|
180
|
+
with Client.from_tcp(
|
|
181
|
+
"worker.example-tailnet.ts.net",
|
|
182
|
+
9400,
|
|
183
|
+
proxy="socks5h://127.0.0.1:1055",
|
|
184
|
+
) as client:
|
|
185
|
+
catalogs = client.catalogs()
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The proxy applies to ordinary function calls, additional worker connections,
|
|
189
|
+
and catalog discovery. Target hostnames are passed untouched to SOCKS5h and
|
|
190
|
+
are never resolved locally by VGI.
|
|
191
|
+
|
|
192
|
+
Raw VGI TCP has no application-layer encryption. Tailscale encrypts the
|
|
193
|
+
Tailnet path, but the worker port should remain reachable only through the
|
|
194
|
+
Tailnet and should not be exposed directly to an untrusted network. Prefer
|
|
195
|
+
HTTPS through Tailscale Serve at public or mixed-trust boundaries.
|
|
@@ -125,6 +125,8 @@ nav:
|
|
|
125
125
|
- Expose a catalog: how-to/catalogs.md
|
|
126
126
|
- Persist state across workers: how-to/state-storage.md
|
|
127
127
|
- Serve over HTTP with auth: how-to/http-auth.md
|
|
128
|
+
- Connect through a Tailnet: how-to/tailnet.md
|
|
129
|
+
- Serve and connect over Iroh: how-to/iroh.md
|
|
128
130
|
- Integrate with the optimizer: how-to/pushdown-and-statistics.md
|
|
129
131
|
- Function Metadata: metadata.md
|
|
130
132
|
- CLI: cli.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "vgi-python"
|
|
3
|
-
version = "0.31.
|
|
3
|
+
version = "0.31.3"
|
|
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.45.1",
|
|
44
44
|
"httpx2>=2.9.1",
|
|
45
45
|
]
|
|
46
46
|
|
|
@@ -52,6 +52,7 @@ dependencies = [
|
|
|
52
52
|
haybarn = ["haybarn>=1.5.5rc1"]
|
|
53
53
|
duckdb = ["duckdb>=1.5.3"]
|
|
54
54
|
http = ["vgi-rpc[http]"]
|
|
55
|
+
iroh = ["vgi-rpc[http,iroh]"]
|
|
55
56
|
# transport="launch": vgi_rpc.launcher imports filelock unconditionally at
|
|
56
57
|
# module level, which lives behind vgi-rpc's own `cli` extra (alongside
|
|
57
58
|
# typer, already a core dependency here, and httpx2, ditto) rather than in
|
|
@@ -13,6 +13,7 @@ from vgi.catalog import (
|
|
|
13
13
|
AttachOpaqueData,
|
|
14
14
|
FunctionInfo,
|
|
15
15
|
FunctionType,
|
|
16
|
+
IndexInfo,
|
|
16
17
|
MacroInfo,
|
|
17
18
|
MacroType,
|
|
18
19
|
SchemaObjectType,
|
|
@@ -394,6 +395,37 @@ class TestExampleWorkerViews:
|
|
|
394
395
|
assert view.comment is not None
|
|
395
396
|
|
|
396
397
|
|
|
398
|
+
class TestExampleWorkerIndexes:
|
|
399
|
+
"""Test ExampleWorker's index catalog entries."""
|
|
400
|
+
|
|
401
|
+
def test_schema_contents_returns_indexes_in_data(self) -> None:
|
|
402
|
+
"""`data` has 2 real indexes on `numbers`.
|
|
403
|
+
|
|
404
|
+
Regression: `type=SchemaObjectType.INDEX` used to silently fall
|
|
405
|
+
through to the functions RPC (`catalog_schema_contents_functions`)
|
|
406
|
+
instead of the real `catalog_schema_contents_indexes` one.
|
|
407
|
+
"""
|
|
408
|
+
client = Client(EXAMPLE_WORKER)
|
|
409
|
+
attach_result = client.catalog_attach(
|
|
410
|
+
name="example", options={}, data_version_spec=None, implementation_version=None
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
indexes = list(
|
|
414
|
+
client.schema_contents(
|
|
415
|
+
attach_opaque_data=attach_result.attach_opaque_data,
|
|
416
|
+
name="data",
|
|
417
|
+
type=SchemaObjectType.INDEX,
|
|
418
|
+
)
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
index_names = {i.name for i in indexes}
|
|
422
|
+
assert index_names == {"idx_numbers_value", "idx_numbers_value_unique"}
|
|
423
|
+
for index in indexes:
|
|
424
|
+
assert isinstance(index, IndexInfo)
|
|
425
|
+
assert index.table_name == "numbers"
|
|
426
|
+
assert index.expressions == ["value"]
|
|
427
|
+
|
|
428
|
+
|
|
397
429
|
class TestExampleWorkerMacros:
|
|
398
430
|
"""Test ExampleWorker's macro catalog entries."""
|
|
399
431
|
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Copyright 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
# ruff: noqa: S101, D101, D102, D103
|
|
4
|
+
"""`Client.bind()` and `Client.table_function(join_keys=...)`.
|
|
5
|
+
|
|
6
|
+
Both were previously unreachable from `Client`:
|
|
7
|
+
|
|
8
|
+
- Schema discovery for a bare (non-catalog) function name had no
|
|
9
|
+
standalone entry point — only `table_function(bind_result_callback=...)`,
|
|
10
|
+
which only fires as a side effect of a generator's first `next()`, by
|
|
11
|
+
which point `init()`/real execution has already started. `bind()` runs
|
|
12
|
+
only the `bind()` RPC.
|
|
13
|
+
- `join_keys` (semi-join pushdown) has been carried on `InitRequest`/
|
|
14
|
+
`TableFunctionPlanRequest` and threaded through worker-side
|
|
15
|
+
`deserialize_filters(..., join_keys=...)` all along
|
|
16
|
+
(`vgi/table_filter_pushdown.py`), but `Client`'s public methods never
|
|
17
|
+
exposed a way to set it on the request.
|
|
18
|
+
|
|
19
|
+
Exercised against `filter_echo` (`vgi/_test_fixtures/table/filters.py`),
|
|
20
|
+
which auto-applies whatever filters it receives and echoes them back as
|
|
21
|
+
SQL-like text in its `pushed_filters` column — the same fixture the
|
|
22
|
+
`vgi_acero` filter-pushdown spike used, since it proves pushdown is real
|
|
23
|
+
(server-side applied), not just correct final rows.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import json
|
|
29
|
+
from typing import Any, cast
|
|
30
|
+
|
|
31
|
+
import pyarrow as pa
|
|
32
|
+
import pytest
|
|
33
|
+
|
|
34
|
+
from vgi.arguments import Arguments
|
|
35
|
+
|
|
36
|
+
MAIN = "main"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.fixture
|
|
40
|
+
def client(client_transport: Any) -> Any:
|
|
41
|
+
with client_transport() as c:
|
|
42
|
+
yield c
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _join_keys_pushdown_filters_bytes(column_name: str, keys_column: str) -> bytes:
|
|
46
|
+
"""Build `pushdown_filters` IPC bytes containing one `join_keys` filter spec.
|
|
47
|
+
|
|
48
|
+
Unlike `constant`/`in` specs, a `join_keys` spec carries no `value_ref` —
|
|
49
|
+
the actual key values travel separately as `Client.table_function`'s own
|
|
50
|
+
`join_keys=` argument, resolved worker-side by matching `keys_column`
|
|
51
|
+
against each join-key batch's column name (see
|
|
52
|
+
`PushdownFilters.get_join_keys_column`).
|
|
53
|
+
"""
|
|
54
|
+
spec = {"column_name": column_name, "column_index": 0, "type": "join_keys", "keys_column": keys_column}
|
|
55
|
+
spec_field = pa.field("filter_spec", pa.string(), metadata={b"vgi_filter_version": b"1"})
|
|
56
|
+
batch = pa.record_batch({"filter_spec": [json.dumps([spec])]}, schema=pa.schema([spec_field]))
|
|
57
|
+
|
|
58
|
+
sink = pa.BufferOutputStream()
|
|
59
|
+
writer = pa.ipc.new_stream(sink, batch.schema)
|
|
60
|
+
writer.write_batch(batch)
|
|
61
|
+
writer.close()
|
|
62
|
+
return sink.getvalue().to_pybytes()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TestBind:
|
|
66
|
+
def test_bind_returns_the_bound_schema(self, client: Any) -> None:
|
|
67
|
+
resp = client.bind(
|
|
68
|
+
function_name="filter_echo",
|
|
69
|
+
schema_name=MAIN,
|
|
70
|
+
arguments=Arguments(positional=(pa.scalar(10),)),
|
|
71
|
+
)
|
|
72
|
+
assert resp.output_schema.names == ["n", "s", "pushed_filters"]
|
|
73
|
+
|
|
74
|
+
def test_bind_does_not_corrupt_the_connection_for_later_calls(self, client: Any) -> None:
|
|
75
|
+
"""A `bind()` call must leave the connection usable for a real scan afterward.
|
|
76
|
+
|
|
77
|
+
This is the regression the old workaround (peek one batch via
|
|
78
|
+
`bind_result_callback` then `gen.close()`) could not satisfy — closing
|
|
79
|
+
a generator early desyncs the subprocess's lockstep RPC pipe for the
|
|
80
|
+
rest of that `Client`'s life. `bind()` never opens a stream at all.
|
|
81
|
+
"""
|
|
82
|
+
client.bind(
|
|
83
|
+
function_name="filter_echo",
|
|
84
|
+
schema_name=MAIN,
|
|
85
|
+
arguments=Arguments(positional=(pa.scalar(10),)),
|
|
86
|
+
)
|
|
87
|
+
batches = list(
|
|
88
|
+
client.table_function(
|
|
89
|
+
function_name="filter_echo",
|
|
90
|
+
schema_name=MAIN,
|
|
91
|
+
arguments=Arguments(positional=(pa.scalar(5),)),
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
table = pa.Table.from_batches(batches)
|
|
95
|
+
assert table.num_rows == 5
|
|
96
|
+
assert sorted(cast("list[int]", table.column("n").to_pylist())) == [0, 1, 2, 3, 4]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TestJoinKeys:
|
|
100
|
+
def test_join_keys_filters_rows_server_side(self, client: Any) -> None:
|
|
101
|
+
pushdown_filters = _join_keys_pushdown_filters_bytes(column_name="n", keys_column="n")
|
|
102
|
+
join_keys = [pa.record_batch({"n": pa.array([3, 7], type=pa.int64())})]
|
|
103
|
+
|
|
104
|
+
batches = list(
|
|
105
|
+
client.table_function(
|
|
106
|
+
function_name="filter_echo",
|
|
107
|
+
schema_name=MAIN,
|
|
108
|
+
arguments=Arguments(positional=(pa.scalar(10),)),
|
|
109
|
+
pushdown_filters=pushdown_filters,
|
|
110
|
+
join_keys=join_keys,
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
table = pa.Table.from_batches(batches)
|
|
114
|
+
|
|
115
|
+
assert sorted(cast("list[int]", table.column("n").to_pylist())) == [3, 7]
|
|
116
|
+
# auto_apply_filters=True means the worker itself filtered — the
|
|
117
|
+
# echoed pushed_filters text is the proof it's a real server-side
|
|
118
|
+
# InFilter, not us post-filtering client-side.
|
|
119
|
+
for value in cast("list[str]", table.column("pushed_filters").to_pylist()):
|
|
120
|
+
assert "n IN" in value
|
|
121
|
+
assert "3" in value and "7" in value
|
|
122
|
+
|
|
123
|
+
def test_no_join_keys_returns_everything(self, client: Any) -> None:
|
|
124
|
+
"""Sanity check: omitting join_keys behaves exactly like today (no filter)."""
|
|
125
|
+
batches = list(
|
|
126
|
+
client.table_function(
|
|
127
|
+
function_name="filter_echo",
|
|
128
|
+
schema_name=MAIN,
|
|
129
|
+
arguments=Arguments(positional=(pa.scalar(5),)),
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
table = pa.Table.from_batches(batches)
|
|
133
|
+
assert table.num_rows == 5
|
|
134
|
+
assert all(v == "(none)" for v in table.column("pushed_filters").to_pylist())
|