vgi-python 0.28.0__tar.gz → 0.29.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.28.0 → vgi_python-0.29.0}/PKG-INFO +2 -2
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/aggregate-functions.md +115 -2
- vgi_python-0.29.0/examples/cache_worker.py +124 -0
- vgi_python-0.29.0/examples/copy_format_worker.py +178 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/pyproject.toml +2 -2
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_serialization.py +1 -1
- vgi_python-0.29.0/tests/client/test_aggregate_client.py +450 -0
- vgi_python-0.29.0/tests/client/test_copy_client.py +286 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/client/test_stop_force.py +34 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/client/test_worker_debug.py +62 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_aggregate.py +9 -3
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_copy_from.py +6 -1
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_copy_to.py +6 -1
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_protocol_inventory.py +53 -101
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_protocol_version.py +2 -2
- vgi_python-0.29.0/tests/conformance/test_splits.py +31 -0
- vgi_python-0.29.0/tests/data/split_tokens/anchor_len_overrun.bin +0 -0
- vgi_python-0.29.0/tests/data/split_tokens/bad_fingerprint.bin +0 -0
- vgi_python-0.29.0/tests/data/split_tokens/bad_flags_unsealed_but_key_present.bin +0 -0
- vgi_python-0.29.0/tests/data/split_tokens/bad_version.bin +0 -0
- vgi_python-0.29.0/tests/data/split_tokens/generate.py +157 -0
- vgi_python-0.29.0/tests/data/split_tokens/manifest.json +82 -0
- vgi_python-0.29.0/tests/data/split_tokens/reserved_flag_bit.bin +0 -0
- vgi_python-0.29.0/tests/data/split_tokens/stale_anchor.bin +0 -0
- vgi_python-0.29.0/tests/data/split_tokens/truncated.bin +0 -0
- vgi_python-0.29.0/tests/data/split_tokens/valid_sealed.bin +0 -0
- vgi_python-0.29.0/tests/data/split_tokens/valid_unsealed.bin +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_aggregate_function.py +291 -1
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_examples_workers.py +87 -2
- vgi_python-0.29.0/tests/test_fixture_catalog_parity.py +111 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_generated_cpp_schemas.py +21 -2
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_generated_go_schemas.py +21 -3
- vgi_python-0.29.0/tests/test_generated_java_schemas.py +229 -0
- vgi_python-0.29.0/tests/test_generated_rust_request_builders.py +119 -0
- vgi_python-0.29.0/tests/test_generated_rust_schemas.py +310 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_generated_ts_schemas.py +9 -3
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_protocol_classes.py +1 -1
- vgi_python-0.29.0/tests/test_split_token.py +342 -0
- vgi_python-0.29.0/tests/test_wire_record_schema_parity.py +265 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/uv.lock +34 -5
- vgi_python-0.29.0/vgi/_test_fixtures/aggregate/_common.py +65 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/http_server.py +11 -3
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/__init__.py +58 -24
- vgi_python-0.29.0/vgi/_test_fixtures/table/splits.py +1171 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/worker.py +116 -1
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/aggregate_function.py +23 -9
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/catalog/_descriptor_spec.py +23 -13
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/catalog/catalog_interface.py +96 -2
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/catalog/secret_type.py +18 -11
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/client/__init__.py +10 -0
- vgi_python-0.29.0/vgi/client/aggregate.py +939 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/client/catalog_mixin.py +37 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/client/client.py +236 -33
- vgi_python-0.29.0/vgi/client/errors.py +41 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/_common.py +87 -1
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/cpp_constants.py +32 -9
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/cpp_protocol_version.py +34 -9
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/cpp_request_builders.py +30 -8
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/cpp_schemas.py +57 -9
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/cpp_secret_protocol_version.py +20 -2
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/cpp_secret_request_builders.py +20 -3
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/cpp_secret_schemas.py +21 -3
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/go_schemas.py +24 -1
- vgi_python-0.29.0/vgi/codegen/java_schemas.py +325 -0
- vgi_python-0.29.0/vgi/codegen/rust_request_builders.py +296 -0
- vgi_python-0.29.0/vgi/codegen/rust_schemas.py +302 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/ts_schemas.py +30 -1
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/http/landing.html +61 -13
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/http/vgi-client.js +9 -9
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/meta_worker.py +36 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/metadata.py +32 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/protocol.py +296 -2
- vgi_python-0.29.0/vgi/protocol_version.txt +1 -0
- vgi_python-0.29.0/vgi/split_token.py +275 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/table_function.py +185 -2
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/worker.py +182 -5
- vgi_python-0.28.0/vgi/_test_fixtures/aggregate/_common.py +0 -21
- vgi_python-0.28.0/vgi/protocol_version.txt +0 -1
- {vgi_python-0.28.0 → vgi_python-0.29.0}/.gitattributes +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/.github/dependabot.yml +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/.github/workflows/integration.yml +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/.github/workflows/release.yml +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/.gitignore +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/.python-version +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/.vale.ini +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/CLAUDE.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/LICENSE +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/README.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/RELEASE-RUNBOOK.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/SECURITY.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/ci/README.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/ci/run-integration.sh +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/arguments.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/auth.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/catalogs.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/client.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/exceptions.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/filters.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/functions.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/http.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/index.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/metadata.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/observability.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/storage.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/transactor.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/api/worker.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/argument-serialization.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/logo.png +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/assets/social-card.png +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/authentication.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/catalog-interface.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/cli.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/column-statistics.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/concepts/index.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/contributing-docs.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/generator-api.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/global-functions.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/how-to/index.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/index.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/lifecycle.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/metadata.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/overrides/main.html +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/robots.txt +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/shared-storage.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/tutorial/index.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/tutorial/table.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/docs/vgi-logo.png +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/examples/batch_index_worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/examples/calc_worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/examples/filter_worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/examples/row_count_worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/examples/sum_worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/mkdocs.yml +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/packages/vgi-fixtures/pyproject.toml +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/scripts/measure_startup.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/test-data/generate.sh +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_attach_option.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_declarative.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_example_worker_catalog.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_required_filters.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/client/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/client/test_cli.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/client/test_table_column_statistics.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_const_param_enforcement.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_container.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_directory_parity.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_github.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_secret.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/conftest.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table/test_wide_columns.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/test_blended_metadata.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_argument_spec.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_auth.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_cache_control.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_call_state_attach.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_copy_to_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_docstrings.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_exceptions.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_function_storage.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_metadata.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_otel.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_schema_scoped_functions.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_serve.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_setting_secret_annotations.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_signing_key.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_tcp_transport.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_union_argument.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/test_worker_page.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_duckdb.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/same_name.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/companion.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/copy_from.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/copy_to.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/global_functions.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/formatting.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/same_name.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/_common.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/cache.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/filters.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/same_name_cached.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table/wide.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table_in_out.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/table_in_out_same_name.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/twin_catalogs.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/argument_spec.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/arguments.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/attach_header.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/auth.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/cache_control.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/catalog/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/catalog/descriptors.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/client/cli.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/copy_from_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/copy_to_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/exceptions.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/function_storage.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/http/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/http/landing_page.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/invocation.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/logging_config.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/otel.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/profiling.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/py.typed +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/scalar_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/schema_utils.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/secret_service.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/serve.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/table_filter_pushdown.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/transactor/client.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.0}/vgi/transactor/server.py +0 -0
- {vgi_python-0.28.0 → vgi_python-0.29.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.29.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.43.0
|
|
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'
|
|
@@ -88,16 +88,63 @@ class SumFunction(AggregateFunction[SumState]):
|
|
|
88
88
|
|
|
89
89
|
## State Class
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
States are serialized to bytes and stored in `FunctionStorage` between RPC calls. The
|
|
92
|
+
framework requires exactly two things of a state type — `serialize_to_bytes()` and
|
|
93
|
+
`deserialize_from_bytes()`, the [`StreamStateCodec`][vgi.function.StreamStateCodec]
|
|
94
|
+
protocol — and treats the bytes as opaque.
|
|
95
|
+
|
|
96
|
+
The usual answer is a `dataclass` extending `ArrowSerializableDataclass`, which writes
|
|
97
|
+
both methods for you. Each field needs an `ArrowType` annotation for serialization:
|
|
92
98
|
|
|
93
99
|
```python
|
|
100
|
+
from dataclasses import dataclass
|
|
101
|
+
from typing import Annotated
|
|
102
|
+
|
|
103
|
+
import pyarrow as pa
|
|
104
|
+
from vgi_rpc import ArrowSerializableDataclass, ArrowType
|
|
105
|
+
|
|
106
|
+
|
|
94
107
|
@dataclass(kw_only=True)
|
|
95
108
|
class AvgState(ArrowSerializableDataclass):
|
|
96
109
|
total: Annotated[float, ArrowType(pa.float64())] = 0.0
|
|
97
110
|
count: Annotated[int, ArrowType(pa.int64())] = 0
|
|
98
111
|
```
|
|
99
112
|
|
|
100
|
-
|
|
113
|
+
Use simple scalar types (int, float, str, bytes) for efficient serialization.
|
|
114
|
+
|
|
115
|
+
You are not required to go through Arrow. A state that is a couple of integers pays for
|
|
116
|
+
a schema message, a batch message and an end-of-stream marker on every group, every
|
|
117
|
+
batch — packing it yourself is both smaller and faster, and it lets a Python worker match
|
|
118
|
+
the state encoding of a sibling VGI implementation in another language:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
import struct
|
|
122
|
+
from dataclasses import dataclass
|
|
123
|
+
from typing import ClassVar
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclass(kw_only=True)
|
|
127
|
+
class SumState:
|
|
128
|
+
"""Same two counters, packed as little-endian int64s."""
|
|
129
|
+
|
|
130
|
+
total: int = 0
|
|
131
|
+
count: int = 0
|
|
132
|
+
|
|
133
|
+
_STRUCT: ClassVar[struct.Struct] = struct.Struct("<qq")
|
|
134
|
+
|
|
135
|
+
def serialize_to_bytes(self) -> bytes:
|
|
136
|
+
return self._STRUCT.pack(self.total, self.count)
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def deserialize_from_bytes(cls, data: bytes) -> "SumState":
|
|
140
|
+
total, count = cls._STRUCT.unpack(data)
|
|
141
|
+
return cls(total=total, count=count)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The codec must round-trip exactly: `T.deserialize_from_bytes(s.serialize_to_bytes())` has
|
|
145
|
+
to equal `s` for every state the aggregate can produce, including `initial_state()`. The
|
|
146
|
+
framework cannot check that, and a lossy codec surfaces as wrong aggregate results rather
|
|
147
|
+
than as an error. A `TState` that has neither method is rejected at class-definition time.
|
|
101
148
|
|
|
102
149
|
## Method Reference
|
|
103
150
|
|
|
@@ -286,6 +333,50 @@ worker = Worker(
|
|
|
286
333
|
|
|
287
334
|
The framework automatically detects `AggregateFunction` subclasses and registers them with the correct function type in the catalog.
|
|
288
335
|
|
|
336
|
+
## Invoking From Python
|
|
337
|
+
|
|
338
|
+
Real users reach aggregates through DuckDB, which drives the all-unary aggregate RPCs from its hash-aggregate operator. Non-DuckDB callers drive the same protocol through `Client`.
|
|
339
|
+
|
|
340
|
+
`aggregate_function()` is the convenience shape — it keys the groups client-side, pumps every batch through `update`, and finalizes in chunks:
|
|
341
|
+
|
|
342
|
+
```python test="skip"
|
|
343
|
+
import pyarrow as pa
|
|
344
|
+
|
|
345
|
+
from vgi.client import Client
|
|
346
|
+
|
|
347
|
+
batch = pa.RecordBatch.from_pydict({"cat": ["a", "b", "a"], "value": [1, 10, 2]})
|
|
348
|
+
|
|
349
|
+
with Client("vgi-fixture-worker") as client:
|
|
350
|
+
result = client.aggregate_function(
|
|
351
|
+
function_name="vgi_sum",
|
|
352
|
+
schema_name="main",
|
|
353
|
+
input=[batch],
|
|
354
|
+
group_by=["cat"],
|
|
355
|
+
)
|
|
356
|
+
# {'cat': ['a', 'b'], 'result': [3, 10]}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Every input column not named in `group_by` is passed to the aggregate as a value column, in batch order — so order the columns to match the declared `Param`s. Omitting `group_by` gives the global-aggregate shape (`SELECT vgi_sum(x) FROM t`), which returns one row even for empty input.
|
|
360
|
+
|
|
361
|
+
For the raw protocol — caller-allocated group ids, `combine`, and the optional window RPCs — use `aggregate_session()`:
|
|
362
|
+
|
|
363
|
+
```python test="skip"
|
|
364
|
+
with Client("vgi-fixture-worker") as client:
|
|
365
|
+
with client.aggregate_session(
|
|
366
|
+
function_name="vgi_sum",
|
|
367
|
+
schema_name="main",
|
|
368
|
+
input_schema=pa.schema([pa.field("value", pa.int64())]),
|
|
369
|
+
) as session:
|
|
370
|
+
session.update(
|
|
371
|
+
group_ids=[0, 0, 1],
|
|
372
|
+
batch=pa.RecordBatch.from_pydict({"value": [1, 2, 100]}),
|
|
373
|
+
)
|
|
374
|
+
session.combine(source_group_ids=[1], target_group_ids=[0])
|
|
375
|
+
session.finalize([0, 1]) # -> {'result': [103, 100]}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
The session is destroyed on exit, so worker-side group state never outlives the `with` block. Window-capable aggregates add `window_init()` / `window()` / `window_batch()` / `window_destroy()` on the same session; streaming-partitioned aggregates use `client.aggregate_streaming(...)` instead (see below).
|
|
379
|
+
|
|
289
380
|
## Streaming-Partitioned Variant
|
|
290
381
|
|
|
291
382
|
For `OVER (PARTITION BY ... ORDER BY ...)` queries against unbounded inputs (e.g. running aggregates across years of trade history), the standard windowed path materializes each partition in DuckDB memory before the aggregate sees it — fine for bounded data, OOMs at scale.
|
|
@@ -339,6 +430,28 @@ class MyRunningAgg(AggregateFunction[MyState]):
|
|
|
339
430
|
|
|
340
431
|
Queries that don't satisfy all of these fall back to the standard windowed path automatically. The streaming path is opt-in and additive — it does not replace `update`/`combine`/`finalize`, which still service `GROUP BY` queries normally.
|
|
341
432
|
|
|
433
|
+
From Python, `Client.aggregate_streaming()` opens the session and closes it on exit. The chunk schema is positional: partition keys first, then order keys, then value columns.
|
|
434
|
+
|
|
435
|
+
```python test="skip"
|
|
436
|
+
schema = pa.schema(
|
|
437
|
+
[pa.field("k", pa.string()), pa.field("ts", pa.int64()), pa.field("v", pa.int64())]
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
with Client("vgi-fixture-worker") as client:
|
|
441
|
+
with client.aggregate_streaming(
|
|
442
|
+
function_name="vgi_streaming_sum",
|
|
443
|
+
schema_name="main",
|
|
444
|
+
input_schema=schema,
|
|
445
|
+
partition_key_count=1,
|
|
446
|
+
order_key_count=1,
|
|
447
|
+
) as session:
|
|
448
|
+
chunk = pa.RecordBatch.from_pydict(
|
|
449
|
+
{"k": ["a", "b", "a", "b"], "ts": [1, 1, 2, 2], "v": [1, 10, 2, 20]},
|
|
450
|
+
schema=schema,
|
|
451
|
+
)
|
|
452
|
+
session.chunk(chunk) # -> {'result': [1, 10, 3, 30]}
|
|
453
|
+
```
|
|
454
|
+
|
|
342
455
|
**When pre-aggregation is the better answer.** For most analytics shapes — "EOD positions per book per day, carrying forward across days" — pre-aggregating the input is the cleanest pattern in plain SQL:
|
|
343
456
|
|
|
344
457
|
```sql
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# /// script
|
|
2
|
+
# requires-python = ">=3.13"
|
|
3
|
+
# dependencies = ["vgi-python"]
|
|
4
|
+
# ///
|
|
5
|
+
"""A table function that advertises its result as cacheable, and revalidates it.
|
|
6
|
+
|
|
7
|
+
Caching is *advertised*, not requested: the worker attaches ``vgi.cache.*``
|
|
8
|
+
metadata to the **first** data batch it emits, and the client (the DuckDB
|
|
9
|
+
extension) decides what to do with it. Nothing is cached unless you say so.
|
|
10
|
+
|
|
11
|
+
This worker exposes ``rates()``, standing in for a slow upstream — a remote API,
|
|
12
|
+
a rate-limited service — whose answer is worth reusing. It shows the whole
|
|
13
|
+
vocabulary:
|
|
14
|
+
|
|
15
|
+
- a **freshness lifetime** (``ttl``), so repeat queries inside the window never
|
|
16
|
+
reach the worker at all;
|
|
17
|
+
- a **validator** (``etag``) plus ``revalidatable=True``, which is what lets the
|
|
18
|
+
client ask "is this still good?" instead of paying for a recompute;
|
|
19
|
+
- the **304-equivalent** reply — a 0-row batch carrying
|
|
20
|
+
``CacheControl(not_modified=True)`` — which tells the client its stored payload
|
|
21
|
+
is still fresh.
|
|
22
|
+
|
|
23
|
+
``out`` is typed as vgi-rpc's ``OutputCollector``, which knows nothing about
|
|
24
|
+
caching; the object the framework actually passes is a VGI wrapper that accepts
|
|
25
|
+
``cache_control=``. Cast to :class:`vgi.protocol.VgiOutputCollector` to reach it
|
|
26
|
+
with types intact — every cache-aware function does this.
|
|
27
|
+
|
|
28
|
+
ATTACH 'rates' (TYPE vgi, LOCATION 'uv run cache_worker.py');
|
|
29
|
+
SELECT * FROM rates.rates(); -- second call inside the TTL never lands here
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from dataclasses import dataclass
|
|
33
|
+
from typing import cast
|
|
34
|
+
|
|
35
|
+
import pyarrow as pa
|
|
36
|
+
|
|
37
|
+
from vgi import Worker
|
|
38
|
+
from vgi.cache_control import CacheControl
|
|
39
|
+
from vgi.catalog import Catalog, Schema
|
|
40
|
+
from vgi.protocol import VgiOutputCollector
|
|
41
|
+
from vgi.table_function import (
|
|
42
|
+
OutputCollector,
|
|
43
|
+
ProcessParams,
|
|
44
|
+
TableFunctionGenerator,
|
|
45
|
+
bind_fixed_schema,
|
|
46
|
+
init_single_worker,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
_SCHEMA = pa.schema([("currency", pa.string()), ("rate", pa.float64())])
|
|
50
|
+
|
|
51
|
+
# Stands in for whatever makes your upstream's answer change. A real worker
|
|
52
|
+
# would derive this from the source — a last-modified header, a version column,
|
|
53
|
+
# a content hash — and it is the ONLY thing revalidation compares.
|
|
54
|
+
_DATA_VERSION = '"rates-v3"'
|
|
55
|
+
|
|
56
|
+
_ROWS = {"currency": ["EUR", "GBP", "JPY"], "rate": [1.09, 1.27, 0.0067]}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
|
60
|
+
class RatesArgs:
|
|
61
|
+
"""``rates()`` takes no arguments — the whole table is the result."""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@bind_fixed_schema
|
|
65
|
+
@init_single_worker
|
|
66
|
+
class Rates(TableFunctionGenerator[RatesArgs, None]):
|
|
67
|
+
"""Emit the rate table once, advertising it as cacheable for 5 minutes."""
|
|
68
|
+
|
|
69
|
+
FIXED_SCHEMA = _SCHEMA
|
|
70
|
+
|
|
71
|
+
class Meta:
|
|
72
|
+
"""Function metadata."""
|
|
73
|
+
|
|
74
|
+
name = "rates"
|
|
75
|
+
description = "Exchange rates from a slow upstream, cacheable for 5 minutes"
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def process(cls, params: ProcessParams[RatesArgs, None], state: None, out: OutputCollector) -> None:
|
|
79
|
+
"""Answer the scan — or, if the client's copy is still current, say so."""
|
|
80
|
+
vgi_out = cast(VgiOutputCollector, out)
|
|
81
|
+
|
|
82
|
+
# Conditional request: the client holds a stale-but-revalidatable copy
|
|
83
|
+
# and is asking whether it may keep it. Both validators are None on a
|
|
84
|
+
# normal call, so this branch is simply skipped.
|
|
85
|
+
if params.if_none_match == _DATA_VERSION:
|
|
86
|
+
vgi_out.emit(
|
|
87
|
+
pa.RecordBatch.from_pylist([], schema=_SCHEMA), # zero rows: "keep what you have"
|
|
88
|
+
cache_control=CacheControl(
|
|
89
|
+
ttl=300,
|
|
90
|
+
etag=_DATA_VERSION,
|
|
91
|
+
revalidatable=True,
|
|
92
|
+
not_modified=True,
|
|
93
|
+
),
|
|
94
|
+
)
|
|
95
|
+
out.finish()
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
# Normal path: stream the result and advertise how it may be reused.
|
|
99
|
+
# The metadata rides on the FIRST data batch; attaching it later has no
|
|
100
|
+
# effect, because by then the client has decided how to treat the stream.
|
|
101
|
+
vgi_out.emit(
|
|
102
|
+
pa.RecordBatch.from_pydict(_ROWS, schema=_SCHEMA),
|
|
103
|
+
cache_control=CacheControl(
|
|
104
|
+
ttl=300, # reusable for 5 minutes without asking
|
|
105
|
+
etag=_DATA_VERSION, # ...and after that, cheap to revalidate
|
|
106
|
+
revalidatable=True, # gates whether the client ever asks
|
|
107
|
+
stale_while_revalidate=60, # serve stale while refreshing behind it
|
|
108
|
+
stale_if_error=600, # serve stale rather than fail
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
out.finish()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class CacheWorker(Worker):
|
|
115
|
+
"""A worker exposing the ``rates`` catalog."""
|
|
116
|
+
|
|
117
|
+
catalog = Catalog(
|
|
118
|
+
name="rates",
|
|
119
|
+
schemas=[Schema(name="main", functions=[Rates])],
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
CacheWorker().run()
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# /// script
|
|
2
|
+
# requires-python = ">=3.13"
|
|
3
|
+
# dependencies = ["vgi-python"]
|
|
4
|
+
# ///
|
|
5
|
+
"""Custom ``COPY ... FROM`` and ``COPY ... TO`` formats, in one catalog.
|
|
6
|
+
|
|
7
|
+
A VGI catalog can register its own ``COPY`` formats, so users read and write a
|
|
8
|
+
format DuckDB has never heard of. Both directions reuse machinery that already
|
|
9
|
+
exists, which is why they add so few new rules:
|
|
10
|
+
|
|
11
|
+
- a :class:`CopyFromFunction` is an ordinary producer-mode **table function**;
|
|
12
|
+
- a :class:`CopyToFunction` is a **buffered** function with no Source phase —
|
|
13
|
+
``write`` is the sink (per batch, parallel), ``close`` is the combine (once).
|
|
14
|
+
|
|
15
|
+
The format here is tab-separated values, one record per line, no header, no
|
|
16
|
+
quoting. Deliberately trivial — the point is the wiring, not the parser.
|
|
17
|
+
|
|
18
|
+
Note the reader and writer declare **different** format names (``tsvlite`` and
|
|
19
|
+
``tsvlite_out``). A reader and a writer that share one name look fine on the
|
|
20
|
+
Python side — the catalog advertises both, keyed by (direction, format) — but the
|
|
21
|
+
extension registers COPY functions by name alone, so the writer is silently
|
|
22
|
+
dropped and ``COPY ... TO`` fails with "COPY TO is not supported for FORMAT".
|
|
23
|
+
Give each direction its own name.
|
|
24
|
+
|
|
25
|
+
Two rules the framework enforces, and this file demonstrates:
|
|
26
|
+
|
|
27
|
+
1. **The reader must emit ``expected_schema`` exactly.** DuckDB inserts no cast
|
|
28
|
+
between the scan and the INSERT, so the target table's columns are the output
|
|
29
|
+
schema — that is why ``on_bind`` is ``@final`` on a COPY-FROM function.
|
|
30
|
+
2. **The writer must finish inside ``close``.** There is no finalize phase, and
|
|
31
|
+
``write`` and ``close`` may run in different processes, so shards go through
|
|
32
|
+
``params.storage`` scoped by ``execution_id`` — never on ``self``.
|
|
33
|
+
|
|
34
|
+
The SQL ``FORMAT`` name is qualified by the catalog alias you attached as, NOT
|
|
35
|
+
the bare ``COPY_*_FORMAT`` string — the extension namespaces formats per attach,
|
|
36
|
+
so two workers may both call theirs ``tsvlite``:
|
|
37
|
+
|
|
38
|
+
ATTACH 'tsv' (TYPE vgi, LOCATION 'uv run copy_format_worker.py');
|
|
39
|
+
CREATE TABLE people (name VARCHAR, age BIGINT);
|
|
40
|
+
COPY people FROM 'people.tsv' (FORMAT 'tsv.tsvlite');
|
|
41
|
+
COPY (SELECT * FROM people) TO 'out.tsv' (FORMAT 'tsv.tsvlite_out', header true);
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from dataclasses import dataclass
|
|
45
|
+
from typing import Annotated
|
|
46
|
+
|
|
47
|
+
import pyarrow as pa
|
|
48
|
+
|
|
49
|
+
from vgi import Arg, Worker
|
|
50
|
+
from vgi.catalog import Catalog, Schema
|
|
51
|
+
from vgi.copy_from_function import CopyFromFunction
|
|
52
|
+
from vgi.copy_to_function import CopyToFunction
|
|
53
|
+
from vgi.table_buffering_function import TableBufferingParams
|
|
54
|
+
from vgi.table_function import OutputCollector, ProcessParams
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
|
58
|
+
class ReadOptions:
|
|
59
|
+
"""Options accepted by ``COPY ... FROM ... (FORMAT tsvlite, ...)``.
|
|
60
|
+
|
|
61
|
+
These are ordinary ``Arg``-annotated arguments — the source path is NOT one
|
|
62
|
+
of them; it arrives on the bind. Each ``doc`` becomes the option's
|
|
63
|
+
description in ``vgi_copy_formats()``.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
skip_rows: Annotated[int, Arg("skip_rows", doc="Leading lines to discard", default=0)] = 0
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
|
70
|
+
class WriteOptions:
|
|
71
|
+
"""Options accepted by ``COPY ... TO ... (FORMAT 'tsv.tsvlite_out', ...)``."""
|
|
72
|
+
|
|
73
|
+
header: Annotated[bool, Arg("header", doc="Write a header line of column names", default=False)] = False
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ReadTsvLite(CopyFromFunction[ReadOptions]):
|
|
77
|
+
"""Read a tab-separated file into the COPY target table."""
|
|
78
|
+
|
|
79
|
+
COPY_FROM_FORMAT = "tsvlite"
|
|
80
|
+
COPY_FROM_COMMENT = "Tab-separated, one record per line, no quoting"
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def read(
|
|
84
|
+
cls,
|
|
85
|
+
*,
|
|
86
|
+
path: str,
|
|
87
|
+
options: ReadOptions,
|
|
88
|
+
expected_schema: pa.Schema,
|
|
89
|
+
params: ProcessParams[ReadOptions],
|
|
90
|
+
out: OutputCollector,
|
|
91
|
+
) -> None:
|
|
92
|
+
"""Parse ``path`` and emit batches matching ``expected_schema`` exactly."""
|
|
93
|
+
with open(path, encoding="utf-8") as handle:
|
|
94
|
+
lines = [line.rstrip("\n") for line in handle if line.strip()]
|
|
95
|
+
lines = lines[options.skip_rows :]
|
|
96
|
+
|
|
97
|
+
# Split into columns positionally, then let Arrow cast each column to the
|
|
98
|
+
# type the target table declared. Emitting a mismatched type or arity is
|
|
99
|
+
# rejected by the extension at COPY bind, not silently coerced.
|
|
100
|
+
columns: list[list[str | None]] = [[] for _ in expected_schema]
|
|
101
|
+
for line in lines:
|
|
102
|
+
cells = line.split("\t")
|
|
103
|
+
for index in range(len(expected_schema)):
|
|
104
|
+
value = cells[index] if index < len(cells) else ""
|
|
105
|
+
columns[index].append(value if value != "" else None)
|
|
106
|
+
|
|
107
|
+
out.emit(
|
|
108
|
+
pa.RecordBatch.from_arrays(
|
|
109
|
+
[
|
|
110
|
+
pa.array(col, type=pa.string()).cast(field.type)
|
|
111
|
+
for col, field in zip(columns, expected_schema, strict=True)
|
|
112
|
+
],
|
|
113
|
+
schema=expected_schema,
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
out.finish()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class WriteTsvLite(CopyToFunction[WriteOptions]):
|
|
120
|
+
"""Write query results out as a tab-separated file."""
|
|
121
|
+
|
|
122
|
+
COPY_TO_FORMAT = "tsvlite_out"
|
|
123
|
+
COPY_TO_COMMENT = "Tab-separated, one record per line, no quoting"
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def write(
|
|
127
|
+
cls,
|
|
128
|
+
*,
|
|
129
|
+
batch: pa.RecordBatch,
|
|
130
|
+
options: WriteOptions,
|
|
131
|
+
file_path: str,
|
|
132
|
+
params: TableBufferingParams[WriteOptions],
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Sink: stash this batch as a shard. Runs per batch, possibly in parallel."""
|
|
135
|
+
rows = ["\t".join("" if value is None else str(value) for value in row.values()) for row in batch.to_pylist()]
|
|
136
|
+
# execution_id-scoped, because close() may run in a different process.
|
|
137
|
+
params.storage.state_append(b"shards", b"", "\n".join(rows).encode("utf-8"))
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
def close(
|
|
141
|
+
cls,
|
|
142
|
+
*,
|
|
143
|
+
options: WriteOptions,
|
|
144
|
+
file_path: str,
|
|
145
|
+
params: TableBufferingParams[WriteOptions],
|
|
146
|
+
) -> int:
|
|
147
|
+
"""Combine: read every shard back and write the file, once. Returns rows written."""
|
|
148
|
+
shards = params.storage.state_log_scan(b"shards", b"")
|
|
149
|
+
body = [chunk.decode("utf-8") for _key, chunk in shards if chunk]
|
|
150
|
+
|
|
151
|
+
written = 0
|
|
152
|
+
with open(file_path, "w", encoding="utf-8") as handle:
|
|
153
|
+
if options.header:
|
|
154
|
+
schema = params.init_call.bind_call.input_schema
|
|
155
|
+
handle.write("\t".join(schema.names) + "\n")
|
|
156
|
+
for chunk in body:
|
|
157
|
+
handle.write(chunk + "\n")
|
|
158
|
+
written += len(chunk.split("\n"))
|
|
159
|
+
# Called even for an empty COPY — the file is created either way.
|
|
160
|
+
return written
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class TsvWorker(Worker):
|
|
164
|
+
"""A worker whose catalog advertises both COPY directions.
|
|
165
|
+
|
|
166
|
+
The declarative ``Catalog`` introspects its function list for
|
|
167
|
+
``CopyFromFunction`` / ``CopyToFunction`` subclasses and advertises what it
|
|
168
|
+
finds, so registering them here is all the wiring there is.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
catalog = Catalog(
|
|
172
|
+
name="tsv",
|
|
173
|
+
schemas=[Schema(name="main", functions=[ReadTsvLite, WriteTsvLite])],
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == "__main__":
|
|
178
|
+
TsvWorker().run()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "vgi-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.29.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.43.0",
|
|
44
44
|
"httpx2>=2.9.1",
|
|
45
45
|
]
|
|
46
46
|
|
|
@@ -1283,7 +1283,7 @@ class TestScanBranchSerialization:
|
|
|
1283
1283
|
Lenient producers (the Rust/Go/Java SDKs, tolerated by the DuckDB C++
|
|
1284
1284
|
client) omit the IPC stream for an empty nested list, so a strict
|
|
1285
1285
|
consumer must accept a zero-branch result rather than raising — otherwise
|
|
1286
|
-
|
|
1286
|
+
Rowfence (the only strict Python consumer in the path) fails
|
|
1287
1287
|
closed on a legitimate response.
|
|
1288
1288
|
"""
|
|
1289
1289
|
from vgi.catalog import ScanBranchesResult
|