vgi-python 0.18.0__tar.gz → 0.19.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.18.0 → vgi_python-0.19.0}/.github/workflows/integration.yml +5 -2
- {vgi_python-0.18.0 → vgi_python-0.19.0}/PKG-INFO +1 -1
- {vgi_python-0.18.0 → vgi_python-0.19.0}/ci/README.md +47 -7
- {vgi_python-0.18.0 → vgi_python-0.19.0}/ci/run-integration.sh +105 -29
- {vgi_python-0.18.0 → vgi_python-0.19.0}/pyproject.toml +1 -1
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_schema_scoped_functions.py +53 -7
- {vgi_python-0.18.0 → vgi_python-0.19.0}/uv.lock +1 -1
- vgi_python-0.19.0/vgi/attach_header.py +81 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/meta_worker.py +172 -212
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/worker.py +39 -2
- {vgi_python-0.18.0 → vgi_python-0.19.0}/.gitattributes +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/dependabot.yml +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/workflows/release.yml +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/.gitignore +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/.python-version +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/.vale.ini +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/CLAUDE.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/LICENSE +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/README.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/SECURITY.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/arguments.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/auth.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/catalogs.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/client.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/exceptions.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/filters.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/functions.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/http.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/index.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/metadata.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/observability.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/storage.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/transactor.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/worker.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/argument-serialization.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/logo.png +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/social-card.png +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/authentication.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/catalog-interface.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/cli.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/column-statistics.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/concepts/index.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/contributing-docs.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/generator-api.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/index.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/index.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/lifecycle.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/metadata.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/overrides/main.html +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/robots.txt +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/shared-storage.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/tutorial/index.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/tutorial/table.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/vgi-logo.png +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/calc_worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/filter_worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/row_count_worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/sum_worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/mkdocs.yml +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/packages/vgi-fixtures/pyproject.toml +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/scripts/measure_startup.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/test-data/generate.sh +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_declarative.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_example_worker_catalog.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_required_filters.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_serialization.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/client/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/client/test_cli.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_const_param_enforcement.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_container.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_copy_from.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_copy_to.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_directory_parity.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_github.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_protocol_version.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_secret.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conftest.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/landing/_checker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/landing/describe.expected.json +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/landing/describe.schema.json +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/test_blended_metadata.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_aggregate_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_argument_spec.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_auth.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_cache_control.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_copy_to_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_docstrings.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_exceptions.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_function_storage.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_landing_conformance.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_metadata.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_otel.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_protocol_classes.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_serve.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_setting_secret_annotations.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_tcp_transport.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_union_argument.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_worker_page.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_duckdb.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/companion.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/copy_from.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/copy_to.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/http_server.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/formatting.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/same_name.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/_common.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/cache.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/filters.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table_in_out.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/twin_catalogs.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/aggregate_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/argument_spec.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/arguments.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/auth.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/cache_control.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/catalog_interface.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/descriptors.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/catalog_mixin.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/client.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/_common.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_request_builders.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/copy_from_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/copy_to_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/exceptions.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/function_storage.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/describe_json.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/landing.html +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/landing_page.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/invocation.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/logging_config.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/metadata.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/otel.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/protocol.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/protocol_version.txt +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/py.typed +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/scalar_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/schema_utils.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/secret_service.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/serve.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/table_filter_pushdown.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/table_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/transactor/client.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/transactor/server.py +0 -0
- {vgi_python-0.18.0 → vgi_python-0.19.0}/wrangler.jsonc +0 -0
|
@@ -45,8 +45,11 @@ jobs:
|
|
|
45
45
|
matrix:
|
|
46
46
|
include:
|
|
47
47
|
# stdio: the subprocess transport (whole suite — the primary lane).
|
|
48
|
-
#
|
|
49
|
-
# launch
|
|
48
|
+
# launch: the whole suite over the AF_UNIX launcher transport.
|
|
49
|
+
# shm: launch + the POSIX shared-memory side channel. Rides the
|
|
50
|
+
# launcher, not raw subprocess: stdio already covers the
|
|
51
|
+
# fork-per-connection path and spends most of its wall-clock
|
|
52
|
+
# doing it, so re-paying that cost to test shm is waste.
|
|
50
53
|
# http: the whole suite over the stateless HTTP transport.
|
|
51
54
|
- { lane: stdio }
|
|
52
55
|
- { lane: shm, shm_bytes: '67108864' }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.19.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
|
|
@@ -52,21 +52,47 @@ runs them as a matrix):
|
|
|
52
52
|
Also boots the versioned and versioned-tables workers as background HTTP
|
|
53
53
|
servers (`VGI_VERSIONED_HTTP_WORKER` / `VGI_VERSIONED_TABLES_HTTP_WORKER`) so
|
|
54
54
|
the `attach/versioned_tables_*_http` and `versioning_http` tests run.
|
|
55
|
-
- **`shm`** —
|
|
55
|
+
- **`shm`** — the **`launch`** lane plus the POSIX shared-memory side channel
|
|
56
56
|
(`VGI_RPC_SHM_SIZE_BYTES`); the whole suite. vgi-rpc-python attaches the
|
|
57
|
-
segment transparently, so the env var alone flips the
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
segment transparently, so the env var alone flips the side channel on.
|
|
58
|
+
This deliberately differs from the vgi Makefile's `test_shm`, which layers shm
|
|
59
|
+
on raw subprocess: `stdio` already covers the fork-per-connection path and
|
|
60
|
+
spends most of its wall-clock doing it, so paying that cost twice just to
|
|
61
|
+
exercise shm is waste. The side channel is transport-independent — negotiated
|
|
62
|
+
via the `__transport_options__` handshake and carried in POSIX shm rather than
|
|
63
|
+
in the pipe or socket — and engages identically over the launcher (verified:
|
|
64
|
+
the same 18 `[shm]` transfers under `VGI_RPC_SHM_DEBUG=1` on both).
|
|
65
|
+
- **`launch`** — the AF_UNIX launcher transport; the whole suite, with *every*
|
|
66
|
+
`VGI_*_WORKER` fronted by `launch:` so traffic flows through the C++ launcher
|
|
67
|
+
(`ResolveLauncherSocketPath` → AF_UNIX → `UnixSocketWorker`). Mirrors the vgi
|
|
68
|
+
Makefile's `test_launcher`: the point is that the launcher path produces
|
|
69
|
+
identical query results to the subprocess path, which only a full-suite run
|
|
70
|
+
demonstrates. Setting `VGI_REQUIRE_LAUNCHER_TRANSPORT` additionally un-skips
|
|
71
|
+
the launcher-only tests (`launcher/*`), whose options apply solely to the
|
|
72
|
+
`launch:` dispatch path and which the other lanes skip via `require-env`.
|
|
73
|
+
The versioned / versioned-tables *http* worker vars are left unset (as in
|
|
74
|
+
`test_launcher`), so those tests skip here — stdio covers them. One deliberate
|
|
75
|
+
divergence from `test_launcher`: see `filter_echo_partitioned.test` below.
|
|
60
76
|
- **`http`** — the whole suite over the stateless HTTP transport. Staging
|
|
61
77
|
injects `LOAD httpfs` before each worker ATTACH (the prebuilt binary doesn't
|
|
62
78
|
statically link httpfs).
|
|
63
79
|
|
|
64
80
|
The `simple_writable/*.test` write-path tests (INSERT/UPDATE/DELETE/RETURNING)
|
|
65
|
-
run on
|
|
81
|
+
run on **every** lane against `VGI_SIMPLE_WRITABLE_WORKER`
|
|
66
82
|
(`vgi-fixture-simple-writable-worker`), in their own `haybarn-unittest`
|
|
67
83
|
invocation so their warm pooled connections don't perturb the immediately
|
|
68
|
-
following crash-recovery test.
|
|
69
|
-
|
|
84
|
+
following crash-recovery test. That env var is always a spawned binary — a
|
|
85
|
+
`launch:` path on the launch lane, a plain path elsewhere — so these tests run
|
|
86
|
+
over subprocess even on the http lane rather than skipping.
|
|
87
|
+
|
|
88
|
+
The crash / pool-recovery tests (`table_in_out/table_buffering_*`) are gated on
|
|
89
|
+
`VGI_TEST_DEDICATED_WORKER`, which is set on the **`stdio` lane only**. Their
|
|
90
|
+
`crash_on_process` fixture does `os.kill(getpid(), SIGKILL)`; that is a
|
|
91
|
+
recoverable per-DuckDB-process child kill under subprocess transport, but under
|
|
92
|
+
any shared-worker transport (`http://`, `launch:`) it kills the one process
|
|
93
|
+
serving the whole suite and every later `ATTACH` fails. Because `shm` now rides
|
|
94
|
+
the launcher, `stdio` is the only lane that can host them — worth knowing if you
|
|
95
|
+
ever narrow the stdio lane.
|
|
70
96
|
|
|
71
97
|
### Excluded tests
|
|
72
98
|
|
|
@@ -95,8 +121,22 @@ Dropped on the **http** lane only:
|
|
|
95
121
|
`CLAUDE.md`). `buffer_input/scale.test_slow` is a `.test_slow` file, which the
|
|
96
122
|
harness never stages (it only finds `*.test`).
|
|
97
123
|
|
|
124
|
+
Nothing is dropped on the **launch** lane. `test_launcher` upstream excludes two
|
|
125
|
+
files, and neither applies:
|
|
126
|
+
|
|
127
|
+
- `test/sql/vgi_worker_pool.test` lives outside `test/sql/integration`, so the
|
|
128
|
+
harness never stages it.
|
|
129
|
+
- `table/filter_echo_partitioned.test` is excluded upstream for asserting >1
|
|
130
|
+
distinct `worker_pid`, which AF_UNIX socket pooling cannot satisfy. That
|
|
131
|
+
rationale is stale — the test now counts transport-neutral `conn=` ids instead
|
|
132
|
+
— and it passes over `launch:` against the Python fixture worker. Keeping it
|
|
133
|
+
preserves coverage of precisely what the launcher changes: connection
|
|
134
|
+
multiplexing.
|
|
135
|
+
|
|
98
136
|
The HTTP-attach / bearer-auth / dynamic-code / schema-reconcile-only tests skip
|
|
99
137
|
via their `require-env` gates when we don't set the corresponding worker.
|
|
138
|
+
`bad_enum.test` skips on every lane — `VGI_BAD_ENUM_WORKER` is not wired up here
|
|
139
|
+
even though the fixture (`vgi-fixture-bad-enum-worker`) is installed.
|
|
100
140
|
|
|
101
141
|
## Run it locally
|
|
102
142
|
|
|
@@ -16,8 +16,11 @@
|
|
|
16
16
|
# * No worker coverage (unlike vgi-go's GOCOVERDIR) — pass/fail only.
|
|
17
17
|
#
|
|
18
18
|
# This mirrors the env wiring of the vgi repo's Makefile `test_subprocess` /
|
|
19
|
-
# `
|
|
20
|
-
#
|
|
19
|
+
# `test_launcher` / `test_http` targets (the canonical local way to run this
|
|
20
|
+
# suite against the Python worker — see vgi-python/CLAUDE.md). The `shm` lane
|
|
21
|
+
# deliberately differs from Makefile `test_shm`: it layers the shm side channel
|
|
22
|
+
# on the LAUNCHER rather than on raw subprocess, to avoid re-forking a Python
|
|
23
|
+
# worker per connection. See the shm|launch case below.
|
|
21
24
|
#
|
|
22
25
|
# Required environment:
|
|
23
26
|
# VGI_SRC path to a Query-farm/vgi checkout (contains test/sql/integration)
|
|
@@ -59,6 +62,16 @@ done
|
|
|
59
62
|
# ---------------------------------------------------------------------------
|
|
60
63
|
AWK_HTTP=0
|
|
61
64
|
EXTRA_SKIP=()
|
|
65
|
+
# Nothing is dropped on the launch lane. The vgi Makefile's test_launcher
|
|
66
|
+
# excludes two files, and neither applies here:
|
|
67
|
+
# * test/sql/vgi_worker_pool.test — outside test/sql/integration, so it is
|
|
68
|
+
# never staged by the find below.
|
|
69
|
+
# * table/filter_echo_partitioned.test — excluded upstream for asserting >1
|
|
70
|
+
# distinct worker_pid, which AF_UNIX socket pooling can't satisfy. That
|
|
71
|
+
# rationale is stale: the test now counts transport-neutral `conn=` ids
|
|
72
|
+
# instead (see its own comment), and it passes over launch: against the
|
|
73
|
+
# Python fixture worker. Keeping it preserves coverage of exactly what the
|
|
74
|
+
# launcher changes — connection multiplexing.
|
|
62
75
|
if [ "$TRANSPORT" = "http" ]; then
|
|
63
76
|
AWK_HTTP=1
|
|
64
77
|
# Dropped on the http lane only:
|
|
@@ -120,20 +133,38 @@ export VGI_SYNC_INIT_GLOBAL=1
|
|
|
120
133
|
SCHEMA_RECONCILE_DIR="$(mktemp -d)"
|
|
121
134
|
export VGI_SCHEMA_RECONCILE_DB="$SCHEMA_RECONCILE_DIR/vgi_schema_reconcile.sqlite"
|
|
122
135
|
|
|
123
|
-
# Presence gate for the crash / pool-recovery tests
|
|
124
|
-
# (table_in_out/table_buffering_*). The value is not used as a worker spec —
|
|
125
|
-
# the SQL attaches the normal pooled worker; this just un-skips the tests.
|
|
126
|
-
export VGI_TEST_DEDICATED_WORKER=1
|
|
127
|
-
|
|
128
136
|
# Background workers (http servers) are tracked in a file and SIGTERMed on exit.
|
|
137
|
+
# Each line is "<pid>\t<logfile>".
|
|
129
138
|
BG_PIDS_FILE="$(mktemp)"
|
|
130
139
|
# shellcheck disable=SC2329 # invoked indirectly via `trap cleanup EXIT` below
|
|
131
140
|
cleanup() {
|
|
132
141
|
[ -f "$BG_PIDS_FILE" ] || return 0
|
|
133
|
-
while
|
|
142
|
+
while IFS="$(printf '\t')" read -r p _; do
|
|
143
|
+
[ -n "$p" ] && kill "$p" 2>/dev/null || true
|
|
144
|
+
done < "$BG_PIDS_FILE"
|
|
134
145
|
}
|
|
135
146
|
trap cleanup EXIT
|
|
136
147
|
|
|
148
|
+
# assert_bg_workers_alive — fail if any background http worker died during a
|
|
149
|
+
# run, dumping its log. A dead shared worker is otherwise near-invisible: the
|
|
150
|
+
# runner's default error handling turns the resulting cascade of failed ATTACHes
|
|
151
|
+
# into skips, so the lane still prints "All tests passed" having tested nothing
|
|
152
|
+
# past the point of death (the failure mode that hid 136 voided files in run
|
|
153
|
+
# 29979884253). Cheap insurance against a green-but-empty lane.
|
|
154
|
+
assert_bg_workers_alive() {
|
|
155
|
+
local rc=0 p log
|
|
156
|
+
[ -s "$BG_PIDS_FILE" ] || return 0
|
|
157
|
+
while IFS="$(printf '\t')" read -r p log; do
|
|
158
|
+
[ -n "$p" ] || continue
|
|
159
|
+
kill -0 "$p" 2>/dev/null && continue
|
|
160
|
+
echo "::error::background http worker (pid $p) died during the run — everything" \
|
|
161
|
+
"attached to it after that point was not really tested. Its log follows."
|
|
162
|
+
[ -n "$log" ] && [ -f "$log" ] && sed -n '1,200p' "$log"
|
|
163
|
+
rc=1
|
|
164
|
+
done < "$BG_PIDS_FILE"
|
|
165
|
+
return "$rc"
|
|
166
|
+
}
|
|
167
|
+
|
|
137
168
|
# boot_http_worker <binary> — start it as an HTTP server on an ephemeral port and
|
|
138
169
|
# set BOOTED_PORT to the port it reports (PORT:<n>, the worker's readiness
|
|
139
170
|
# contract). Sets a global rather than echoing so the backgrounded worker stays a
|
|
@@ -156,7 +187,7 @@ boot_http_worker() {
|
|
|
156
187
|
log="$(mktemp)"
|
|
157
188
|
( cd "$STAGE" && exec "$exe" --http --port 0 ) >"$log" 2>&1 &
|
|
158
189
|
pid=$!
|
|
159
|
-
|
|
190
|
+
printf '%s\t%s\n' "$pid" "$log" >> "$BG_PIDS_FILE"
|
|
160
191
|
for _ in $(seq 1 60); do
|
|
161
192
|
kill -0 "$pid" 2>/dev/null || { echo "::error::http worker '$exe' exited" >&2; cat "$log" >&2; return 1; }
|
|
162
193
|
port="$(sed -n 's/.*PORT:\([0-9]*\).*/\1/p' "$log" | head -1)"
|
|
@@ -167,20 +198,45 @@ boot_http_worker() {
|
|
|
167
198
|
BOOTED_PORT="$port"
|
|
168
199
|
}
|
|
169
200
|
|
|
201
|
+
# On the launcher-family lanes EVERY worker is fronted by `launch:` so the C++
|
|
202
|
+
# launcher serves it (ResolveLauncherSocketPath -> AF_UNIX -> UnixSocketWorker),
|
|
203
|
+
# as the vgi Makefile's test_launcher does. A worker left unprefixed would
|
|
204
|
+
# silently run over stdio inside a launcher lane. Empty on stdio and http.
|
|
205
|
+
LAUNCH_PREFIX=""
|
|
206
|
+
case "$TRANSPORT" in shm|launch) LAUNCH_PREFIX="launch:" ;; esac
|
|
207
|
+
|
|
170
208
|
# version_mismatch.test attaches a worker that advertises an incompatible
|
|
171
209
|
# protocol_version; set on the subprocess lanes (skips elsewhere via require-env).
|
|
172
|
-
export VGI_BAD_PROTOCOL_WORKER="$BAD_PROTOCOL"
|
|
210
|
+
export VGI_BAD_PROTOCOL_WORKER="${LAUNCH_PREFIX}${BAD_PROTOCOL}"
|
|
173
211
|
|
|
174
212
|
# The simple_writable fixture worker un-skips the cross-language
|
|
175
|
-
# simple_writable/*.test write-path tests. Set on every lane
|
|
176
|
-
# over
|
|
177
|
-
|
|
213
|
+
# simple_writable/*.test write-path tests. Set on every lane — on the http lane
|
|
214
|
+
# it stays a binary path, so those tests run over subprocess there (they do NOT
|
|
215
|
+
# self-skip: the http lane passes all 5 files).
|
|
216
|
+
export VGI_SIMPLE_WRITABLE_WORKER="${LAUNCH_PREFIX}${SIMPLE_WRITABLE}"
|
|
178
217
|
|
|
179
218
|
case "$TRANSPORT" in
|
|
180
|
-
stdio
|
|
181
|
-
# Subprocess transport (the primary lane)
|
|
182
|
-
#
|
|
219
|
+
stdio)
|
|
220
|
+
# Subprocess transport (the primary lane) — the only lane that spawns a
|
|
221
|
+
# fresh worker process per DuckDB connection, and so the only one that can
|
|
222
|
+
# host the crash / pool-recovery tests below.
|
|
183
223
|
export VGI_TEST_WORKER="$WORKER"
|
|
224
|
+
# Presence gate for the crash / pool-recovery tests
|
|
225
|
+
# (table_in_out/table_buffering_{worker_crash,pool_recovery}). The value is
|
|
226
|
+
# not a worker spec — the SQL attaches the normal pooled worker; this just
|
|
227
|
+
# un-skips the tests.
|
|
228
|
+
#
|
|
229
|
+
# DEDICATED (subprocess) LANE ONLY. Those tests call the `crash_on_process`
|
|
230
|
+
# fixture, which does os.kill(getpid(), SIGKILL). Under subprocess transport
|
|
231
|
+
# that kills the per-DuckDB-process worker child and the pool recovers —
|
|
232
|
+
# exactly what the tests assert. Under a SHARED-worker transport (http://,
|
|
233
|
+
# unix://, launch:) it kills the single process serving the whole suite, and
|
|
234
|
+
# every later ATTACH fails. Exporting it unconditionally silently voided 136
|
|
235
|
+
# of 286 files on the http lane while still printing "All tests passed"
|
|
236
|
+
# (run 29979884253) — the crash landed at file 132 and the runner's default
|
|
237
|
+
# error handling swallowed the cascade. Since shm now runs over the launcher
|
|
238
|
+
# (a shared worker), stdio is the ONLY lane that may set this.
|
|
239
|
+
export VGI_TEST_DEDICATED_WORKER=1
|
|
184
240
|
export VGI_VERSIONED_WORKER="$VERSIONED"
|
|
185
241
|
export VGI_VERSIONED_TABLES_WORKER="$VERSIONED_TABLES"
|
|
186
242
|
export VGI_ATTACH_OPTIONS_WORKER="$ATTACH_OPTIONS"
|
|
@@ -192,12 +248,33 @@ case "$TRANSPORT" in
|
|
|
192
248
|
export VGI_VERSIONED_HTTP_WORKER="http://localhost:${vh_port}"
|
|
193
249
|
SUITE_GLOB="test/sql/integration/*"
|
|
194
250
|
;;
|
|
195
|
-
launch)
|
|
196
|
-
# AF_UNIX launcher transport
|
|
197
|
-
#
|
|
251
|
+
shm|launch)
|
|
252
|
+
# AF_UNIX launcher transport — the WHOLE suite with every worker fronted by
|
|
253
|
+
# `launch:`, mirroring the vgi Makefile's test_launcher. Validates that the
|
|
254
|
+
# launcher path produces identical query results to the subprocess path;
|
|
255
|
+
# running only launcher/* here would leave the transport almost untested.
|
|
256
|
+
# VGI_REQUIRE_LAUNCHER_TRANSPORT additionally un-skips launcher/*, whose
|
|
257
|
+
# options only apply to the launch: dispatch path (the other lanes skip them).
|
|
258
|
+
#
|
|
259
|
+
# `shm` is this same lane plus the POSIX shared-memory side channel
|
|
260
|
+
# (VGI_RPC_SHM_SIZE_BYTES, exported by the workflow). It rides the launcher
|
|
261
|
+
# rather than raw subprocess because stdio spends most of its wall-clock
|
|
262
|
+
# fork+exec'ing a fresh Python worker per connection; the launcher keeps one
|
|
263
|
+
# warm worker per argv and reuses it, so the lane exercises the same shm
|
|
264
|
+
# paths far faster. The side channel is transport-independent — it is
|
|
265
|
+
# negotiated via the __transport_options__ handshake and carried in POSIX
|
|
266
|
+
# shm, not in the pipe/socket — and engages identically here (verified: the
|
|
267
|
+
# same 18 [shm] transfers under VGI_RPC_SHM_DEBUG on stdio and on launch).
|
|
268
|
+
#
|
|
269
|
+
# The versioned / versioned_tables *http* worker env vars are deliberately
|
|
270
|
+
# left UNSET (as in test_launcher), so attach/versioned_tables_*_http and
|
|
271
|
+
# versioning_http skip — they're covered over http on the stdio lane.
|
|
198
272
|
export VGI_TEST_WORKER="launch:${WORKER}"
|
|
273
|
+
export VGI_VERSIONED_WORKER="launch:${VERSIONED}"
|
|
274
|
+
export VGI_VERSIONED_TABLES_WORKER="launch:${VERSIONED_TABLES}"
|
|
275
|
+
export VGI_ATTACH_OPTIONS_WORKER="launch:${ATTACH_OPTIONS}"
|
|
199
276
|
export VGI_REQUIRE_LAUNCHER_TRANSPORT=1
|
|
200
|
-
SUITE_GLOB="test/sql/integration
|
|
277
|
+
SUITE_GLOB="test/sql/integration/*"
|
|
201
278
|
;;
|
|
202
279
|
http)
|
|
203
280
|
# Whole-suite-over-HTTP (mirrors make test_http). Every ATTACH goes over
|
|
@@ -253,8 +330,8 @@ rm -f "$STAGE/test/_warm.test"
|
|
|
253
330
|
# pool). Its table-in-out write workers otherwise leave warm pooled connections
|
|
254
331
|
# that perturb the immediately-following crash-recovery test
|
|
255
332
|
# (table_in_out/table_buffering_pool_recovery). A separate process gives the
|
|
256
|
-
# crash test a clean pool.
|
|
257
|
-
#
|
|
333
|
+
# crash test a clean pool. Every lane does the split: simple_writable always
|
|
334
|
+
# attaches a spawned binary (VGI_SIMPLE_WRITABLE_WORKER), even on the http lane.
|
|
258
335
|
echo "Running suite ($SUITE_GLOB, transport=$TRANSPORT) ..."
|
|
259
336
|
suite_rc=0
|
|
260
337
|
|
|
@@ -288,12 +365,11 @@ run_unittest() {
|
|
|
288
365
|
return "$rc"
|
|
289
366
|
}
|
|
290
367
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
fi
|
|
368
|
+
run_unittest "$SUITE_GLOB" "~test/sql/integration/simple_writable/*" || suite_rc=$?
|
|
369
|
+
assert_bg_workers_alive || suite_rc=1
|
|
370
|
+
|
|
371
|
+
echo "Running simple_writable (isolated process) ..."
|
|
372
|
+
run_unittest "test/sql/integration/simple_writable/*" || suite_rc=$?
|
|
373
|
+
assert_bg_workers_alive || suite_rc=1
|
|
298
374
|
|
|
299
375
|
exit "$suite_rc"
|
|
@@ -10,6 +10,7 @@ same_name_schemas.test`` covers the same ground end-to-end through DuckDB.
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import dataclasses
|
|
13
14
|
from typing import Annotated
|
|
14
15
|
|
|
15
16
|
import pyarrow as pa
|
|
@@ -154,7 +155,8 @@ class TestLegacyFunctionsList:
|
|
|
154
155
|
class TestCrossCatalogResolution:
|
|
155
156
|
"""Two catalogs in one worker process, colliding on schema *and* function name.
|
|
156
157
|
|
|
157
|
-
Only
|
|
158
|
+
Only the catalog name sealed into ``attach_opaque_data`` distinguishes them,
|
|
159
|
+
so it is the routing key.
|
|
158
160
|
``vgi/test/sql/integration/scalar/same_name_catalogs.test`` drives the same
|
|
159
161
|
scenario end-to-end through DuckDB.
|
|
160
162
|
"""
|
|
@@ -175,11 +177,29 @@ class TestCrossCatalogResolution:
|
|
|
175
177
|
)
|
|
176
178
|
|
|
177
179
|
def test_attach_opaque_data_selects_the_catalog(self) -> None:
|
|
178
|
-
"""
|
|
180
|
+
"""The catalog name sealed into the attach routes to that catalog's class."""
|
|
179
181
|
meta = self._meta()
|
|
180
|
-
for index, expected in ((0, TwinAFunction), (1, TwinBFunction)):
|
|
181
|
-
|
|
182
|
-
assert meta._resolve_function(self._request(
|
|
182
|
+
for index, catalog, expected in ((0, "twin_a", TwinAFunction), (1, "twin_b", TwinBFunction)):
|
|
183
|
+
attach = meta._workers[index]._seal_attach_with_catalog(b"\x00" * 16, catalog)
|
|
184
|
+
assert meta._resolve_function(self._request(attach)) is expected
|
|
185
|
+
|
|
186
|
+
def test_routing_survives_the_strip_that_http_serialization_performs(self) -> None:
|
|
187
|
+
"""A rehydrated attach still routes — the regression behind the twin bug.
|
|
188
|
+
|
|
189
|
+
HTTP stream state persists the attach the sub-worker was handed, then
|
|
190
|
+
``rehydrate`` re-resolves against the MetaWorker. When routing lived in a
|
|
191
|
+
prefix that ``init`` stripped before the sub-worker saw it, that
|
|
192
|
+
round-trip lost the key and silently resolved to whichever sub-worker
|
|
193
|
+
declared the name first. The name is inside the sealed plaintext now, so
|
|
194
|
+
the value that gets persisted is itself routable.
|
|
195
|
+
"""
|
|
196
|
+
meta = self._meta()
|
|
197
|
+
for index, catalog, expected in ((0, "twin_a", TwinAFunction), (1, "twin_b", TwinBFunction)):
|
|
198
|
+
worker = meta._workers[index]
|
|
199
|
+
attach = worker._seal_attach_with_catalog(b"\x00" * 16, catalog)
|
|
200
|
+
# What init() hands the sub-worker is what ends up serialized.
|
|
201
|
+
assert meta._maybe_worker_for_attach(attach) is worker
|
|
202
|
+
assert meta._resolve_function(self._request(attach)) is expected
|
|
183
203
|
|
|
184
204
|
def test_both_catalogs_declare_the_same_schema_and_name(self) -> None:
|
|
185
205
|
"""The fixture is only meaningful if the collision is total."""
|
|
@@ -187,7 +207,33 @@ class TestCrossCatalogResolution:
|
|
|
187
207
|
assert TwinAWorker._build_schema_registry()[key] == [TwinAFunction]
|
|
188
208
|
assert TwinBWorker._build_schema_registry()[key] == [TwinBFunction]
|
|
189
209
|
|
|
190
|
-
def
|
|
191
|
-
"""No
|
|
210
|
+
def test_without_an_attach_an_ambiguous_name_raises(self) -> None:
|
|
211
|
+
"""No routing key and two declarers — refuse rather than guess.
|
|
212
|
+
|
|
213
|
+
This previously returned ``TwinAFunction``: whichever sub-worker declared
|
|
214
|
+
the name first. That is what made ``b.main.test_same_name_catalog(1)``
|
|
215
|
+
answer ``twin_a:1`` over HTTP instead of failing, so a routing bug read
|
|
216
|
+
as a plausible result. An attach-less call to an ambiguous name means the
|
|
217
|
+
key was lost upstream, which is worth surfacing.
|
|
218
|
+
"""
|
|
192
219
|
meta = self._meta()
|
|
220
|
+
with pytest.raises(ValueError, match="Cannot route"):
|
|
221
|
+
meta._resolve_function(self._request(None))
|
|
222
|
+
|
|
223
|
+
def test_without_an_attach_an_unambiguous_name_still_resolves(self) -> None:
|
|
224
|
+
"""The legitimate attach-less path is untouched.
|
|
225
|
+
|
|
226
|
+
Non-catalog callers (``Worker.functions``) reach ``_resolve_function``
|
|
227
|
+
with no attach at all. With a single declarer there is no ambiguity to
|
|
228
|
+
refuse, so resolution must still succeed — the guard keys off the
|
|
229
|
+
candidate count, not off the attach being absent.
|
|
230
|
+
"""
|
|
231
|
+
meta = MetaWorker([TwinAWorker()])
|
|
193
232
|
assert meta._resolve_function(self._request(None)) is TwinAFunction
|
|
233
|
+
|
|
234
|
+
def test_an_unknown_name_reports_unknown_not_ambiguous(self) -> None:
|
|
235
|
+
"""A name no sub-worker declares is still a plain 'unknown function'."""
|
|
236
|
+
meta = self._meta()
|
|
237
|
+
request = dataclasses.replace(self._request(None), function_name="no_such_function")
|
|
238
|
+
with pytest.raises(ValueError, match="Unknown function"):
|
|
239
|
+
meta._resolve_function(request)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
r"""Routing header that [`MetaWorker`][] encapsulates inside an attach envelope.
|
|
4
|
+
|
|
5
|
+
When several [`Worker`][] instances are composed into one process, a call that
|
|
6
|
+
carries an ``attach_opaque_data`` has to be routed back to the sub-worker whose
|
|
7
|
+
catalog vended it. Nothing in the request identifies the catalog:
|
|
8
|
+
``BindRequest`` carries only ``schema_name``, and the catalog's own opaque bytes
|
|
9
|
+
are implementation-defined — the built-in read-only catalog returns the class
|
|
10
|
+
constant ``b"readonly-catalog-"`` for *every* catalog it serves, so two catalogs
|
|
11
|
+
in one process are byte-identical there.
|
|
12
|
+
|
|
13
|
+
So ``MetaWorker`` records the catalog name itself. At ``catalog_attach`` it opens
|
|
14
|
+
the sub-worker's sealed attach, prepends this header to the plaintext, and
|
|
15
|
+
re-seals:
|
|
16
|
+
|
|
17
|
+
sealed( VGI\0MWC\0 || len(1) || name || uuid(16) || catalog_bytes )
|
|
18
|
+
|
|
19
|
+
Two properties matter:
|
|
20
|
+
|
|
21
|
+
* **Inside the seal.** The signing key is process-wide, so a header riding
|
|
22
|
+
*outside* the AEAD could be edited by a client to route an attach minted for
|
|
23
|
+
one catalog into another — every sub-worker's key would open it. Sealing the
|
|
24
|
+
name makes forging it equivalent to forging the envelope.
|
|
25
|
+
* **Survives serialization.** The header is part of the plaintext, so it rides
|
|
26
|
+
along in whatever the sub-worker persists into an HTTP stream state. A
|
|
27
|
+
rehydrate landing on a different instance re-reads the name and routes
|
|
28
|
+
correctly — no process-local map, and no dependence on a positional index that
|
|
29
|
+
need not agree across deploys.
|
|
30
|
+
|
|
31
|
+
``Worker`` strips the header when opening an envelope, so every existing consumer
|
|
32
|
+
keeps seeing the framework plaintext (``uuid(16) || catalog_bytes``) unchanged.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
# 8 bytes: long enough that a random uuid4 prefix colliding with it is not worth
|
|
38
|
+
# reasoning about (a 6-byte marker would already be ~2^-48 per attach).
|
|
39
|
+
_MAGIC = b"VGI\x00MWC\x00"
|
|
40
|
+
_LEN_WIDTH = 1
|
|
41
|
+
_MAX_NAME_BYTES = (1 << (8 * _LEN_WIDTH)) - 1
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def encode(catalog_name: str, plaintext: bytes) -> bytes:
|
|
45
|
+
"""Prepend the routing header for ``catalog_name`` to an attach plaintext.
|
|
46
|
+
|
|
47
|
+
``plaintext`` is the framework form the sub-worker minted
|
|
48
|
+
(``uuid(16) || catalog_bytes``); the result is meant to be sealed.
|
|
49
|
+
"""
|
|
50
|
+
name = catalog_name.encode("utf-8")
|
|
51
|
+
if len(name) > _MAX_NAME_BYTES:
|
|
52
|
+
msg = f"catalog name too long to encode in an attach header: {len(name)} > {_MAX_NAME_BYTES} bytes"
|
|
53
|
+
raise ValueError(msg)
|
|
54
|
+
return _MAGIC + len(name).to_bytes(_LEN_WIDTH, "big") + name + plaintext
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def split(plaintext: bytes | None) -> tuple[str | None, bytes | None]:
|
|
58
|
+
"""Split an opened attach plaintext into ``(catalog_name, inner_plaintext)``.
|
|
59
|
+
|
|
60
|
+
Returns ``(None, plaintext)`` unchanged when no header is present — the
|
|
61
|
+
single-``Worker`` case, and any attach minted before this header existed.
|
|
62
|
+
Malformed headers are treated the same way rather than raising: the value
|
|
63
|
+
still has to satisfy the AEAD, so a truncated header means a framework bug
|
|
64
|
+
on the mint side, and failing the *call* with a confusing error is worse than
|
|
65
|
+
falling back to the un-routed path (which raises a precise error of its own
|
|
66
|
+
when the function name turns out to be ambiguous).
|
|
67
|
+
"""
|
|
68
|
+
if not plaintext or not plaintext.startswith(_MAGIC):
|
|
69
|
+
return None, plaintext
|
|
70
|
+
at = len(_MAGIC)
|
|
71
|
+
if len(plaintext) < at + _LEN_WIDTH:
|
|
72
|
+
return None, plaintext
|
|
73
|
+
name_len = int.from_bytes(plaintext[at : at + _LEN_WIDTH], "big")
|
|
74
|
+
at += _LEN_WIDTH
|
|
75
|
+
if len(plaintext) < at + name_len:
|
|
76
|
+
return None, plaintext
|
|
77
|
+
try:
|
|
78
|
+
name = plaintext[at : at + name_len].decode("utf-8")
|
|
79
|
+
except UnicodeDecodeError:
|
|
80
|
+
return None, plaintext
|
|
81
|
+
return name, plaintext[at + name_len :]
|