vgi-python 0.8.2__tar.gz → 0.8.4__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.8.2 → vgi_python-0.8.4}/PKG-INFO +1 -1
- {vgi_python-0.8.2 → vgi_python-0.8.4}/pyproject.toml +1 -1
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_declarative.py +33 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_example_worker_catalog.py +7 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_metadata.py +115 -0
- vgi_python-0.8.4/tests/test_union_argument.py +111 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/uv.lock +1 -1
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/__init__.py +2 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/_common.py +2 -2
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/worker.py +1 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/arguments.py +55 -2
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/catalog_interface.py +1 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/descriptors.py +5 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/.gitattributes +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/dependabot.yml +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/workflows/integration.yml +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/workflows/release.yml +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/.gitignore +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/.python-version +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/.vale.ini +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/CLAUDE.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/LICENSE +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/README.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/SECURITY.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/ci/README.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/ci/run-integration.sh +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/arguments.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/auth.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/catalogs.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/client.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/exceptions.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/filters.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/functions.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/http.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/index.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/metadata.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/observability.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/storage.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/transactor.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/worker.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/argument-serialization.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/logo.png +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/social-card.png +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/authentication.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/catalog-interface.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/cli.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/column-statistics.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/concepts/index.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/contributing-docs.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/generator-api.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/index.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/index.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/lifecycle.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/metadata.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/overrides/main.html +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/robots.txt +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/shared-storage.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/tutorial/index.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/tutorial/table.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/vgi-logo.png +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/calc_worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/filter_worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/row_count_worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/sum_worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/mkdocs.yml +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/packages/vgi-fixtures/pyproject.toml +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/scripts/measure_startup.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/test-data/generate.sh +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_required_field_filter_paths.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_serialization.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/client/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/client/test_cli.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_directory_parity.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_protocol_version.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_secret.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conftest.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_aggregate_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_argument_spec.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_auth.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_docstrings.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_exceptions.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_function_storage.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_otel.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_protocol_classes.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_serve.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_setting_secret_annotations.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_worker_page.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_duckdb.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/http_server.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/formatting.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/filters.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table_in_out.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/aggregate_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/argument_spec.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/auth.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/catalog_mixin.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/client.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/_common.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_request_builders.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/exceptions.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/function_storage.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/http/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/invocation.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/logging_config.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/meta_worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/metadata.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/otel.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/protocol.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/protocol_version.txt +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/py.typed +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/scalar_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/schema_utils.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/secret_service.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/serve.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/table_filter_pushdown.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/table_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/transactor/client.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/transactor/server.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/worker.py +0 -0
- {vgi_python-0.8.2 → vgi_python-0.8.4}/wrangler.jsonc +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
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
|
|
@@ -1207,6 +1207,20 @@ class TestCatalogDescriptor:
|
|
|
1207
1207
|
catalog = Catalog(name="myapp", schemas=[main, analytics])
|
|
1208
1208
|
assert len(catalog.schemas) == 2
|
|
1209
1209
|
|
|
1210
|
+
def test_catalog_source_url_defaults_to_none(self) -> None:
|
|
1211
|
+
"""Catalog source_url is None unless set."""
|
|
1212
|
+
catalog = Catalog(name="myapp", schemas=[Schema(name="main")])
|
|
1213
|
+
assert catalog.source_url is None
|
|
1214
|
+
|
|
1215
|
+
def test_catalog_source_url_settable(self) -> None:
|
|
1216
|
+
"""Catalog source_url can advertise the repo/docs homepage."""
|
|
1217
|
+
catalog = Catalog(
|
|
1218
|
+
name="myapp",
|
|
1219
|
+
schemas=[Schema(name="main")],
|
|
1220
|
+
source_url="https://github.com/example/myapp",
|
|
1221
|
+
)
|
|
1222
|
+
assert catalog.source_url == "https://github.com/example/myapp"
|
|
1223
|
+
|
|
1210
1224
|
|
|
1211
1225
|
class TestCatalogValidation:
|
|
1212
1226
|
"""Tests for Catalog validation."""
|
|
@@ -1311,6 +1325,25 @@ class TestReadOnlyCatalogWithCatalog:
|
|
|
1311
1325
|
infos = catalog_interface.catalogs()
|
|
1312
1326
|
assert [i.name for i in infos] == ["testapp"]
|
|
1313
1327
|
|
|
1328
|
+
def test_catalogs_omits_source_url_when_unset(self, catalog_interface: ReadOnlyCatalogInterface) -> None:
|
|
1329
|
+
"""catalogs() yields source_url=None when the Catalog doesn't set one."""
|
|
1330
|
+
infos = catalog_interface.catalogs()
|
|
1331
|
+
assert infos[0].source_url is None
|
|
1332
|
+
|
|
1333
|
+
def test_catalogs_serializes_source_url(self, users_table: Table) -> None:
|
|
1334
|
+
"""catalogs() surfaces the Catalog.source_url into CatalogInfo."""
|
|
1335
|
+
|
|
1336
|
+
class SourcedCatalog(ReadOnlyCatalogInterface):
|
|
1337
|
+
catalog = Catalog(
|
|
1338
|
+
name="sourced",
|
|
1339
|
+
default_schema="main",
|
|
1340
|
+
schemas=[Schema(name="main", tables=[users_table])],
|
|
1341
|
+
source_url="https://github.com/example/sourced",
|
|
1342
|
+
)
|
|
1343
|
+
|
|
1344
|
+
infos = SourcedCatalog().catalogs()
|
|
1345
|
+
assert infos[0].source_url == "https://github.com/example/sourced"
|
|
1346
|
+
|
|
1314
1347
|
def test_catalog_attach(self, catalog_interface: ReadOnlyCatalogInterface) -> None:
|
|
1315
1348
|
"""catalog_attach returns result with correct defaults."""
|
|
1316
1349
|
result = catalog_interface.catalog_attach(
|
|
@@ -78,6 +78,13 @@ class TestExampleWorkerCatalog:
|
|
|
78
78
|
catalogs = client.catalogs()
|
|
79
79
|
assert "example" in [c.name for c in catalogs]
|
|
80
80
|
|
|
81
|
+
def test_catalogs_advertises_source_url(self) -> None:
|
|
82
|
+
"""The declarative catalog's source_url round-trips through discovery."""
|
|
83
|
+
client = Client(EXAMPLE_WORKER)
|
|
84
|
+
catalogs = client.catalogs()
|
|
85
|
+
example = next(c for c in catalogs if c.name == "example")
|
|
86
|
+
assert example.source_url == "https://github.com/query-farm/vgi-python"
|
|
87
|
+
|
|
81
88
|
def test_catalog_attach_works(self) -> None:
|
|
82
89
|
"""Can attach to the 'example' catalog."""
|
|
83
90
|
client = Client(EXAMPLE_WORKER)
|
|
@@ -613,3 +613,118 @@ class TestFunctionTypeInference:
|
|
|
613
613
|
|
|
614
614
|
meta = resolve_metadata(TestFunc)
|
|
615
615
|
assert meta.function_type == CatalogFunctionType.TABLE
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
class TestFunctionTags:
|
|
619
|
+
"""A worker can attach arbitrary free-form tags to a function's metadata.
|
|
620
|
+
|
|
621
|
+
These flow ``Meta.tags`` -> ``ResolvedMetadata.tags`` -> ``FunctionInfo.tags``
|
|
622
|
+
-> DuckDB's per-function ``tags`` map, which is what the ``vgi-lint-check``
|
|
623
|
+
metadata linter reads (e.g. VGI307 requires ``vgi.columns_md`` on a
|
|
624
|
+
dynamic-schema table function).
|
|
625
|
+
"""
|
|
626
|
+
|
|
627
|
+
def test_meta_tags_resolved(self) -> None:
|
|
628
|
+
"""Arbitrary Meta.tags land on ResolvedMetadata.tags verbatim."""
|
|
629
|
+
|
|
630
|
+
class TaggedFunction(TableInOutFunction): # type: ignore[type-arg]
|
|
631
|
+
class Meta:
|
|
632
|
+
name = "tagged_func"
|
|
633
|
+
tags = {
|
|
634
|
+
"vgi.columns_md": "| col | type |\n| --- | --- |\n| id | BIGINT |",
|
|
635
|
+
"custom.team": "data",
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
data: TableInput = Arg[TableInput](0, doc="Input table") # type: ignore[assignment]
|
|
639
|
+
|
|
640
|
+
meta = resolve_metadata(TaggedFunction)
|
|
641
|
+
assert meta.tags == {
|
|
642
|
+
"vgi.columns_md": "| col | type |\n| --- | --- |\n| id | BIGINT |",
|
|
643
|
+
"custom.team": "data",
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
def test_tags_survive_arrow_roundtrip(self) -> None:
|
|
647
|
+
"""Function tags survive the metadata Arrow wire roundtrip (map column)."""
|
|
648
|
+
|
|
649
|
+
class TaggedFunction(TableInOutFunction): # type: ignore[type-arg]
|
|
650
|
+
class Meta:
|
|
651
|
+
name = "tagged_func"
|
|
652
|
+
tags = {"vgi.columns_md": "| col |", "k": "v"}
|
|
653
|
+
|
|
654
|
+
data: TableInput = Arg[TableInput](0, doc="Input table") # type: ignore[assignment]
|
|
655
|
+
|
|
656
|
+
restored = arrow_to_functions(functions_to_arrow([TaggedFunction]))[0]
|
|
657
|
+
assert restored.tags == {"vgi.columns_md": "| col |", "k": "v"}
|
|
658
|
+
|
|
659
|
+
def test_tags_and_examples_are_independent(self) -> None:
|
|
660
|
+
"""Worker tags do not clobber the structured examples, and vice versa.
|
|
661
|
+
|
|
662
|
+
``examples`` is its own structured channel (serialized to the
|
|
663
|
+
``examples`` struct column / ``FunctionInfo.examples``); it is never
|
|
664
|
+
folded into the ``tags`` map. A worker that *also* sets a
|
|
665
|
+
``vgi.example_queries`` tag (the tag-encoded examples convention the
|
|
666
|
+
linter understands) keeps both — the tag is preserved untouched and the
|
|
667
|
+
structured examples remain separate.
|
|
668
|
+
"""
|
|
669
|
+
|
|
670
|
+
class BothFunction(TableInOutFunction): # type: ignore[type-arg]
|
|
671
|
+
class Meta:
|
|
672
|
+
name = "both_func"
|
|
673
|
+
examples = ["SELECT * FROM both_func((SELECT 1))"]
|
|
674
|
+
tags = {
|
|
675
|
+
"vgi.example_queries": '[{"description":"x","sql":"SELECT tag"}]',
|
|
676
|
+
"vgi.columns_md": "| col |",
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
data: TableInput = Arg[TableInput](0, doc="Input table") # type: ignore[assignment]
|
|
680
|
+
|
|
681
|
+
meta = resolve_metadata(BothFunction)
|
|
682
|
+
# Worker-provided tags untouched (example_queries tag not derived, not dropped).
|
|
683
|
+
assert meta.tags == {
|
|
684
|
+
"vgi.example_queries": '[{"description":"x","sql":"SELECT tag"}]',
|
|
685
|
+
"vgi.columns_md": "| col |",
|
|
686
|
+
}
|
|
687
|
+
# Structured examples live independently and are not merged into tags.
|
|
688
|
+
assert [ex.sql for ex in meta.examples] == ["SELECT * FROM both_func((SELECT 1))"]
|
|
689
|
+
|
|
690
|
+
def test_tags_flow_into_function_info(self) -> None:
|
|
691
|
+
"""Tags reach ``FunctionInfo.tags`` (catalog -> DuckDB -> linter path).
|
|
692
|
+
|
|
693
|
+
Also asserts that the structured ``examples`` and the worker-set
|
|
694
|
+
``vgi.example_queries`` tag coexist on ``FunctionInfo`` without either
|
|
695
|
+
clobbering the other.
|
|
696
|
+
"""
|
|
697
|
+
from vgi.catalog.catalog_interface import (
|
|
698
|
+
FunctionInfo,
|
|
699
|
+
ReadOnlyCatalogInterface,
|
|
700
|
+
)
|
|
701
|
+
from vgi.table_function import TableFunctionGenerator
|
|
702
|
+
|
|
703
|
+
class DynamicTable(TableFunctionGenerator): # type: ignore[type-arg]
|
|
704
|
+
class Meta:
|
|
705
|
+
name = "dynamic_table"
|
|
706
|
+
examples = ["SELECT * FROM dynamic_table()"]
|
|
707
|
+
tags = {
|
|
708
|
+
"vgi.columns_md": "| col | type |\n| --- | --- |\n| id | BIGINT |",
|
|
709
|
+
"vgi.example_queries": '[{"description":"x","sql":"SELECT 1"}]',
|
|
710
|
+
"custom.team": "data",
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
FIXED_SCHEMA = pa.schema([pa.field("id", pa.int64())])
|
|
714
|
+
|
|
715
|
+
def process(self, params): # type: ignore[no-untyped-def]
|
|
716
|
+
yield pa.record_batch({"id": [1]})
|
|
717
|
+
|
|
718
|
+
class Cat(ReadOnlyCatalogInterface):
|
|
719
|
+
catalog_name = "functions"
|
|
720
|
+
functions = [DynamicTable]
|
|
721
|
+
|
|
722
|
+
info = Cat()._function_to_info(DynamicTable, "main")
|
|
723
|
+
assert isinstance(info, FunctionInfo)
|
|
724
|
+
assert info.tags == {
|
|
725
|
+
"vgi.columns_md": "| col | type |\n| --- | --- |\n| id | BIGINT |",
|
|
726
|
+
"vgi.example_queries": '[{"description":"x","sql":"SELECT 1"}]',
|
|
727
|
+
"custom.team": "data",
|
|
728
|
+
}
|
|
729
|
+
# Structured examples are carried separately from the tag-encoded ones.
|
|
730
|
+
assert [ex.sql for ex in info.examples] == ["SELECT * FROM dynamic_table()"]
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Union-typed argument decoding.
|
|
4
|
+
|
|
5
|
+
A DuckDB ``UNION`` / Arrow union argument is tagged: the discriminator lives in
|
|
6
|
+
``UnionScalar.type_code``, which plain ``Scalar.as_py()`` discards. ``Arguments``
|
|
7
|
+
decodes such scalars to ``TaggedUnion`` so the active member name is preserved.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import pyarrow as pa
|
|
13
|
+
|
|
14
|
+
from vgi.arguments import Arguments, TaggedUnion
|
|
15
|
+
|
|
16
|
+
_RF = pa.struct([pa.field("n_estimators", pa.list_(pa.int64())), pa.field("max_depth", pa.list_(pa.int64()))])
|
|
17
|
+
_SVC = pa.struct([pa.field("C", pa.list_(pa.float64())), pa.field("kernel", pa.list_(pa.string()))])
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _union_scalar(code: int, members: dict) -> pa.UnionScalar:
|
|
21
|
+
"""Build a one-element sparse-union scalar with named members, active = ``code``."""
|
|
22
|
+
arr = pa.UnionArray.from_sparse(
|
|
23
|
+
pa.array([code], type=pa.int8()),
|
|
24
|
+
[
|
|
25
|
+
pa.array([members.get("random_forest_classifier")], type=_RF),
|
|
26
|
+
pa.array([members.get("svc")], type=_SVC),
|
|
27
|
+
],
|
|
28
|
+
field_names=["random_forest_classifier", "svc"],
|
|
29
|
+
type_codes=[0, 1],
|
|
30
|
+
)
|
|
31
|
+
return arr[0]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_union_arg_preserves_tag() -> None:
|
|
35
|
+
"""A union argument decodes to a TaggedUnion carrying the active member name."""
|
|
36
|
+
scalar = _union_scalar(0, {"random_forest_classifier": {"n_estimators": [100, 300], "max_depth": [3, 5]}})
|
|
37
|
+
got = Arguments(named={"config": scalar}).get("config")
|
|
38
|
+
assert isinstance(got, TaggedUnion)
|
|
39
|
+
assert got.tag == "random_forest_classifier"
|
|
40
|
+
assert got.value == {"n_estimators": [100, 300], "max_depth": [3, 5]}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_union_arg_other_member() -> None:
|
|
44
|
+
"""The tag reflects whichever union member is set."""
|
|
45
|
+
scalar = _union_scalar(1, {"svc": {"C": [1.0, 10.0], "kernel": ["rbf", "linear"]}})
|
|
46
|
+
got = Arguments(named={"config": scalar}).get("config")
|
|
47
|
+
assert got.tag == "svc"
|
|
48
|
+
assert got.value == {"C": [1.0, 10.0], "kernel": ["rbf", "linear"]}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_union_arg_null_payload() -> None:
|
|
52
|
+
"""A null union slot decodes its value to None (the ``inner is None`` branch).
|
|
53
|
+
|
|
54
|
+
Note: pyarrow reports ``type_code == 0`` for any null union slot regardless of
|
|
55
|
+
the codes buffer, so the active member's tag is not recoverable at the scalar
|
|
56
|
+
level for a null payload — only the value is. (Tag preservation for a
|
|
57
|
+
non-null payload of a non-zero-code member is covered above and through the
|
|
58
|
+
batch-level round-trip in the echo integration suite.)
|
|
59
|
+
"""
|
|
60
|
+
scalar = _union_scalar(1, {"svc": None})
|
|
61
|
+
got = Arguments(named={"config": scalar}).get("config")
|
|
62
|
+
assert isinstance(got, TaggedUnion)
|
|
63
|
+
assert got.value is None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_union_arg_non_contiguous_type_codes() -> None:
|
|
67
|
+
"""Tag resolution maps via type_codes, not member position.
|
|
68
|
+
|
|
69
|
+
Type codes need not be 0,1,2,...; ``_scalar_to_py`` must locate the active
|
|
70
|
+
member by ``type_codes.index(code)`` rather than treating the code as a
|
|
71
|
+
positional index.
|
|
72
|
+
"""
|
|
73
|
+
arr = pa.UnionArray.from_sparse(
|
|
74
|
+
pa.array([9], type=pa.int8()),
|
|
75
|
+
[
|
|
76
|
+
pa.array([{"n_estimators": [1], "max_depth": [2]}], type=_RF),
|
|
77
|
+
pa.array([{"C": [1.0], "kernel": ["rbf"]}], type=_SVC),
|
|
78
|
+
],
|
|
79
|
+
field_names=["random_forest_classifier", "svc"],
|
|
80
|
+
type_codes=[5, 9],
|
|
81
|
+
)
|
|
82
|
+
got = Arguments(named={"config": arr[0]}).get("config")
|
|
83
|
+
assert isinstance(got, TaggedUnion)
|
|
84
|
+
assert got.tag == "svc"
|
|
85
|
+
assert got.value == {"C": [1.0], "kernel": ["rbf"]}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_union_arg_dense_union() -> None:
|
|
89
|
+
"""Dense unions (what DuckDB emits) decode the same as sparse."""
|
|
90
|
+
arr = pa.UnionArray.from_dense(
|
|
91
|
+
pa.array([1], type=pa.int8()),
|
|
92
|
+
pa.array([0], type=pa.int32()),
|
|
93
|
+
[
|
|
94
|
+
pa.array([], type=_RF),
|
|
95
|
+
pa.array([{"C": [2.0], "kernel": ["linear"]}], type=_SVC),
|
|
96
|
+
],
|
|
97
|
+
field_names=["random_forest_classifier", "svc"],
|
|
98
|
+
type_codes=[0, 1],
|
|
99
|
+
)
|
|
100
|
+
got = Arguments(named={"config": arr[0]}).get("config")
|
|
101
|
+
assert isinstance(got, TaggedUnion)
|
|
102
|
+
assert got.tag == "svc"
|
|
103
|
+
assert got.value == {"C": [2.0], "kernel": ["linear"]}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_non_union_args_unchanged() -> None:
|
|
107
|
+
"""Non-union arguments still decode via plain as_py()."""
|
|
108
|
+
args = Arguments(named={"n": pa.scalar(5), "s": pa.scalar("hi")}, positional=(pa.scalar(1.5),))
|
|
109
|
+
assert args.get("n") == 5
|
|
110
|
+
assert args.get("s") == "hi"
|
|
111
|
+
assert args.get(0) == 1.5
|
|
@@ -39,6 +39,7 @@ from vgi.arguments import (
|
|
|
39
39
|
Param,
|
|
40
40
|
Returns,
|
|
41
41
|
TableInput,
|
|
42
|
+
TaggedUnion,
|
|
42
43
|
)
|
|
43
44
|
from vgi.auth import AuthContext, CallContext
|
|
44
45
|
from vgi.metadata import (
|
|
@@ -143,6 +144,7 @@ __all__ = [
|
|
|
143
144
|
"TableInOutGenerator",
|
|
144
145
|
"TableInput",
|
|
145
146
|
"TableInputValidationError",
|
|
147
|
+
"TaggedUnion",
|
|
146
148
|
"TypeMismatchError",
|
|
147
149
|
"Worker",
|
|
148
150
|
"functions_to_arrow",
|
|
@@ -61,7 +61,7 @@ class CountBatchArgs:
|
|
|
61
61
|
"""
|
|
62
62
|
|
|
63
63
|
count: Annotated[int, Arg(0, doc="Number of rows to generate", ge=0)]
|
|
64
|
-
batch_size: Annotated[int, Arg("batch_size", default=
|
|
64
|
+
batch_size: Annotated[int, Arg("batch_size", default=2048, doc="Batch size for output", ge=1)]
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
@dataclass(slots=True, frozen=True)
|
|
@@ -91,7 +91,7 @@ class _BaseSequenceFunction(TableFunctionGenerator[Any, CountdownState]):
|
|
|
91
91
|
NUMPY_DTYPE: ClassVar[type[np.generic]] = np.int64
|
|
92
92
|
STATS_ARROW_TYPE: ClassVar[pa.DataType] = pa.int64()
|
|
93
93
|
STATS_COLUMN_NAME: ClassVar[str] = "n"
|
|
94
|
-
BATCH_SIZE_FALLBACK: ClassVar[int] =
|
|
94
|
+
BATCH_SIZE_FALLBACK: ClassVar[int] = 2048
|
|
95
95
|
|
|
96
96
|
@classmethod
|
|
97
97
|
def initial_state(cls, params: ProcessParams[Any]) -> CountdownState:
|
|
@@ -320,6 +320,7 @@ _EXAMPLE_CATALOG = Catalog(
|
|
|
320
320
|
default_schema="main",
|
|
321
321
|
comment="Example VGI catalog for testing",
|
|
322
322
|
tags={"source": "vgi-fixture-worker", "version": "1"},
|
|
323
|
+
source_url="https://github.com/query-farm/vgi-python",
|
|
323
324
|
schemas=[
|
|
324
325
|
Schema(
|
|
325
326
|
name="main",
|
|
@@ -159,6 +159,7 @@ __all__ = [
|
|
|
159
159
|
"PYTHON_TO_ARROW",
|
|
160
160
|
"Returns",
|
|
161
161
|
"TableInput",
|
|
162
|
+
"TaggedUnion",
|
|
162
163
|
"TypeBoundPredicate",
|
|
163
164
|
"OutputLength",
|
|
164
165
|
"Setting",
|
|
@@ -168,6 +169,58 @@ __all__ = [
|
|
|
168
169
|
]
|
|
169
170
|
|
|
170
171
|
|
|
172
|
+
@dataclass(frozen=True, slots=True)
|
|
173
|
+
class TaggedUnion:
|
|
174
|
+
"""A decoded union-typed argument: which member is set (``tag``) and its ``value``.
|
|
175
|
+
|
|
176
|
+
DuckDB ``UNION`` / Arrow union arguments are *tagged*: the discriminator
|
|
177
|
+
(which member is present) lives in the Arrow ``UnionScalar.type_code``, not
|
|
178
|
+
in the member value. Plain ``Scalar.as_py()`` returns only the member value
|
|
179
|
+
and drops that tag, so union arguments are decoded into this wrapper
|
|
180
|
+
instead — ``tag`` is the active member's field name and ``value`` is its
|
|
181
|
+
Python value.
|
|
182
|
+
|
|
183
|
+
Example::
|
|
184
|
+
|
|
185
|
+
config: Annotated[TaggedUnion, Arg("config", arrow_type=pa.sparse_union([...]))]
|
|
186
|
+
...
|
|
187
|
+
cfg = params.args.config # TaggedUnion(tag=..., value=...)
|
|
188
|
+
if cfg.tag == "random_forest_classifier":
|
|
189
|
+
grid = cfg.value # the member struct, as a dict
|
|
190
|
+
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
tag: str | None
|
|
194
|
+
value: Any
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _scalar_to_py(scalar: "Scalar[Any]") -> Any:
|
|
198
|
+
"""Convert an argument scalar to a Python value, preserving union tags.
|
|
199
|
+
|
|
200
|
+
Identical to ``scalar.as_py()`` for every type except unions: a
|
|
201
|
+
``UnionScalar`` is decoded to a [`TaggedUnion`][] so the member
|
|
202
|
+
discriminator (which ``as_py()`` discards) is retained.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
scalar: The argument scalar to convert.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
``scalar.as_py()`` for non-union scalars; a [`TaggedUnion`][] for unions.
|
|
209
|
+
|
|
210
|
+
"""
|
|
211
|
+
if isinstance(scalar, pa.UnionScalar):
|
|
212
|
+
# Map the active ``type_code`` to its member field name via the union
|
|
213
|
+
# type's parallel ``type_codes`` / ``field()``. (``type_code`` is coerced
|
|
214
|
+
# to int — it is an integer at runtime regardless of the stub's typing.)
|
|
215
|
+
union_type = scalar.type
|
|
216
|
+
type_codes = list(union_type.type_codes)
|
|
217
|
+
code = int(scalar.type_code)
|
|
218
|
+
tag = union_type.field(type_codes.index(code)).name if code in type_codes else None
|
|
219
|
+
inner = scalar.value
|
|
220
|
+
return TaggedUnion(tag=tag, value=inner.as_py() if inner is not None else None)
|
|
221
|
+
return scalar.as_py()
|
|
222
|
+
|
|
223
|
+
|
|
171
224
|
class TableInput:
|
|
172
225
|
"""Sentinel type for table input parameters in table-in-out functions.
|
|
173
226
|
|
|
@@ -377,7 +430,7 @@ class Arguments:
|
|
|
377
430
|
else:
|
|
378
431
|
raise TypeError(f"Argument '{key}': expected {type}, got {scalar.type}")
|
|
379
432
|
|
|
380
|
-
return scalar
|
|
433
|
+
return _scalar_to_py(scalar)
|
|
381
434
|
|
|
382
435
|
def get_varargs(
|
|
383
436
|
self,
|
|
@@ -410,7 +463,7 @@ class Arguments:
|
|
|
410
463
|
if type is not None and scalar.type != type:
|
|
411
464
|
raise TypeError(f"Argument {i}: expected {type}, got {scalar.type}")
|
|
412
465
|
|
|
413
|
-
values.append(scalar
|
|
466
|
+
values.append(_scalar_to_py(scalar))
|
|
414
467
|
|
|
415
468
|
return tuple(values)
|
|
416
469
|
|
|
@@ -2319,6 +2319,7 @@ class ReadOnlyCatalogInterface(CatalogInterface):
|
|
|
2319
2319
|
implementation_version=None,
|
|
2320
2320
|
data_version_spec=None,
|
|
2321
2321
|
attach_option_specs=[spec.serialize() for spec in self.attach_option_specs],
|
|
2322
|
+
source_url=self.catalog.source_url if self.catalog is not None else None,
|
|
2322
2323
|
)
|
|
2323
2324
|
]
|
|
2324
2325
|
|
|
@@ -873,6 +873,10 @@ class Catalog:
|
|
|
873
873
|
schemas: Sequence of Schema objects defining the catalog contents.
|
|
874
874
|
comment: Optional comment describing the catalog.
|
|
875
875
|
tags: Optional key-value tags associated with the catalog.
|
|
876
|
+
source_url: Where this worker's code lives — repo, build, or docs
|
|
877
|
+
homepage. ``None`` (the default) when the worker doesn't advertise
|
|
878
|
+
a source location. Surfaced via the ``catalog_catalogs()`` discovery
|
|
879
|
+
record (``CatalogInfo.source_url``).
|
|
876
880
|
|
|
877
881
|
"""
|
|
878
882
|
|
|
@@ -881,6 +885,7 @@ class Catalog:
|
|
|
881
885
|
schemas: Sequence[Schema] = ()
|
|
882
886
|
comment: str | None = None
|
|
883
887
|
tags: dict[str, str] = field(default_factory=dict)
|
|
888
|
+
source_url: str | None = None
|
|
884
889
|
|
|
885
890
|
def __post_init__(self) -> None:
|
|
886
891
|
"""Validate catalog configuration."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|