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.
Files changed (395) hide show
  1. {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/workflows/integration.yml +5 -2
  2. {vgi_python-0.18.0 → vgi_python-0.19.0}/PKG-INFO +1 -1
  3. {vgi_python-0.18.0 → vgi_python-0.19.0}/ci/README.md +47 -7
  4. {vgi_python-0.18.0 → vgi_python-0.19.0}/ci/run-integration.sh +105 -29
  5. {vgi_python-0.18.0 → vgi_python-0.19.0}/pyproject.toml +1 -1
  6. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_schema_scoped_functions.py +53 -7
  7. {vgi_python-0.18.0 → vgi_python-0.19.0}/uv.lock +1 -1
  8. vgi_python-0.19.0/vgi/attach_header.py +81 -0
  9. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/meta_worker.py +172 -212
  10. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/worker.py +39 -2
  11. {vgi_python-0.18.0 → vgi_python-0.19.0}/.gitattributes +0 -0
  12. {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/dependabot.yml +0 -0
  13. {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
  14. {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/workflows/ci.yml +0 -0
  15. {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/workflows/docs.yml +0 -0
  16. {vgi_python-0.18.0 → vgi_python-0.19.0}/.github/workflows/release.yml +0 -0
  17. {vgi_python-0.18.0 → vgi_python-0.19.0}/.gitignore +0 -0
  18. {vgi_python-0.18.0 → vgi_python-0.19.0}/.python-version +0 -0
  19. {vgi_python-0.18.0 → vgi_python-0.19.0}/.vale.ini +0 -0
  20. {vgi_python-0.18.0 → vgi_python-0.19.0}/CLAUDE.md +0 -0
  21. {vgi_python-0.18.0 → vgi_python-0.19.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
  22. {vgi_python-0.18.0 → vgi_python-0.19.0}/DOCS_REVIEW_RUBRIC.md +0 -0
  23. {vgi_python-0.18.0 → vgi_python-0.19.0}/DOCS_USABILITY_TEST.md +0 -0
  24. {vgi_python-0.18.0 → vgi_python-0.19.0}/LICENSE +0 -0
  25. {vgi_python-0.18.0 → vgi_python-0.19.0}/README.md +0 -0
  26. {vgi_python-0.18.0 → vgi_python-0.19.0}/SECURITY.md +0 -0
  27. {vgi_python-0.18.0 → vgi_python-0.19.0}/ci/preprocess-require.awk +0 -0
  28. {vgi_python-0.18.0 → vgi_python-0.19.0}/dist-vgi/.gitignore +0 -0
  29. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/aggregate-functions.md +0 -0
  30. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/arguments.md +0 -0
  31. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/auth.md +0 -0
  32. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/catalogs.md +0 -0
  33. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/client.md +0 -0
  34. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/exceptions.md +0 -0
  35. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/filters.md +0 -0
  36. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/functions.md +0 -0
  37. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/http.md +0 -0
  38. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/index.md +0 -0
  39. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/metadata.md +0 -0
  40. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/observability.md +0 -0
  41. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/storage.md +0 -0
  42. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/transactor.md +0 -0
  43. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/api/worker.md +0 -0
  44. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/argument-serialization.md +0 -0
  45. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/apple-touch-icon.png +0 -0
  46. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/favicon-16x16.png +0 -0
  47. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/favicon-32x32.png +0 -0
  48. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/favicon.ico +0 -0
  49. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/kinds/aggregate.svg +0 -0
  50. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/kinds/buffering.svg +0 -0
  51. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/kinds/scalar.svg +0 -0
  52. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/kinds/table-in-out.svg +0 -0
  53. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/kinds/table.svg +0 -0
  54. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/logo.png +0 -0
  55. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/assets/social-card.png +0 -0
  56. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/authentication.md +0 -0
  57. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/catalog-interface.md +0 -0
  58. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/cli.md +0 -0
  59. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/column-statistics.md +0 -0
  60. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/concepts/index.md +0 -0
  61. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/contributing-docs.md +0 -0
  62. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/filter-pushdown.md +0 -0
  63. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/generator-api.md +0 -0
  64. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/catalogs.md +0 -0
  65. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/function-patterns.md +0 -0
  66. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/http-auth.md +0 -0
  67. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/index.md +0 -0
  68. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/pushdown-and-statistics.md +0 -0
  69. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/how-to/state-storage.md +0 -0
  70. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/index.md +0 -0
  71. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/lifecycle.md +0 -0
  72. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/metadata.md +0 -0
  73. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/overrides/main.html +0 -0
  74. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/robots.txt +0 -0
  75. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/shared-storage.md +0 -0
  76. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/stylesheets/extra.css +0 -0
  77. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/tutorial/index.md +0 -0
  78. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/tutorial/scalar.md +0 -0
  79. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/tutorial/table.md +0 -0
  80. {vgi_python-0.18.0 → vgi_python-0.19.0}/docs/vgi-logo.png +0 -0
  81. {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/calc_scalar_worker.py +0 -0
  82. {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/calc_worker.py +0 -0
  83. {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/filter_worker.py +0 -0
  84. {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/greeting_scalar_worker.py +0 -0
  85. {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/row_count_worker.py +0 -0
  86. {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/series_streaming_worker.py +0 -0
  87. {vgi_python-0.18.0 → vgi_python-0.19.0}/examples/sum_worker.py +0 -0
  88. {vgi_python-0.18.0 → vgi_python-0.19.0}/mkdocs.yml +0 -0
  89. {vgi_python-0.18.0 → vgi_python-0.19.0}/packages/vgi-fixtures/LICENSE +0 -0
  90. {vgi_python-0.18.0 → vgi_python-0.19.0}/packages/vgi-fixtures/README.md +0 -0
  91. {vgi_python-0.18.0 → vgi_python-0.19.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
  92. {vgi_python-0.18.0 → vgi_python-0.19.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
  93. {vgi_python-0.18.0 → vgi_python-0.19.0}/packages/vgi-fixtures/pyproject.toml +0 -0
  94. {vgi_python-0.18.0 → vgi_python-0.19.0}/scripts/measure_startup.py +0 -0
  95. {vgi_python-0.18.0 → vgi_python-0.19.0}/scripts/run_all_tests.sh +0 -0
  96. {vgi_python-0.18.0 → vgi_python-0.19.0}/test-data/generate.sh +0 -0
  97. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/__init__.py +0 -0
  98. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/_http_fixtures.py +0 -0
  99. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/__init__.py +0 -0
  100. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_catalog_interface.py +0 -0
  101. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_client_catalog.py +0 -0
  102. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_column_statistics.py +0 -0
  103. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_declarative.py +0 -0
  104. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_example_worker_catalog.py +0 -0
  105. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_integration.py +0 -0
  106. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_required_filters.py +0 -0
  107. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_scan_branches.py +0 -0
  108. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_serialization.py +0 -0
  109. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_setting.py +0 -0
  110. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_storage.py +0 -0
  111. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_time_travel.py +0 -0
  112. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/catalog/test_writable_table.py +0 -0
  113. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/client/__init__.py +0 -0
  114. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/client/test_broken_pipe.py +0 -0
  115. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/client/test_cli.py +0 -0
  116. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/client/test_cli_catalog_functions.py +0 -0
  117. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/client/test_worker_debug.py +0 -0
  118. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/__init__.py +0 -0
  119. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/_stub.py +0 -0
  120. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/conftest.py +0 -0
  121. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_accumulate.py +0 -0
  122. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_aggregate.py +0 -0
  123. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_attach.py +0 -0
  124. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_bearer_auth.py +0 -0
  125. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_const_param_enforcement.py +0 -0
  126. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_container.py +0 -0
  127. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_copy_from.py +0 -0
  128. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_copy_to.py +0 -0
  129. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_directory_parity.py +0 -0
  130. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_function_inventory.py +0 -0
  131. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_github.py +0 -0
  132. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_http_client.py +0 -0
  133. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_http_external_location.py +0 -0
  134. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_http_upload_url.py +0 -0
  135. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_macro.py +0 -0
  136. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_overload.py +0 -0
  137. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_protocol_inventory.py +0 -0
  138. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_protocol_version.py +0 -0
  139. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_resumable_scan.py +0 -0
  140. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_scalar.py +0 -0
  141. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
  142. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_secret.py +0 -0
  143. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_settings.py +0 -0
  144. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_table.py +0 -0
  145. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_table_in_out.py +0 -0
  146. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_view.py +0 -0
  147. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conformance/test_writable.py +0 -0
  148. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/conftest.py +0 -0
  149. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/landing/_checker.py +0 -0
  150. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/landing/describe.expected.json +0 -0
  151. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/landing/describe.schema.json +0 -0
  152. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/__init__.py +0 -0
  153. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_bernoulli_function.py +0 -0
  154. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_binary_packet_function.py +0 -0
  155. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_client.py +0 -0
  156. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_conditional_message_function.py +0 -0
  157. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_hash_seed_function.py +0 -0
  158. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
  159. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_multiply_function.py +0 -0
  160. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_random_bytes_function.py +0 -0
  161. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/scalar/test_return_secret_value_function.py +0 -0
  162. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/__init__.py +0 -0
  163. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/__init__.py +0 -0
  164. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_constant_columns_function.py +0 -0
  165. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_double_sequence_function.py +0 -0
  166. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_exception_function.py +0 -0
  167. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_filter_echo_function.py +0 -0
  168. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_logging_function.py +0 -0
  169. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
  170. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_partitioned_function.py +0 -0
  171. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_projected_data_function.py +0 -0
  172. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_sequence_function.py +0 -0
  173. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_settings_function.py +0 -0
  174. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_struct_settings_function.py +0 -0
  175. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
  176. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/__init__.py +0 -0
  177. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/__init__.py +0 -0
  178. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
  179. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
  180. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
  181. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
  182. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
  183. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
  184. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
  185. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/test_blended_metadata.py +0 -0
  186. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/table_in_out/test_client.py +0 -0
  187. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_access_log_audit.py +0 -0
  188. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_aggregate_function.py +0 -0
  189. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_argument_spec.py +0 -0
  190. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_auth.py +0 -0
  191. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_bind_exceptions.py +0 -0
  192. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_bind_request_at_clause.py +0 -0
  193. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_bound_storage_conformance.py +0 -0
  194. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_cache_control.py +0 -0
  195. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_catalog_auth_binding.py +0 -0
  196. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_copy_from_function.py +0 -0
  197. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_copy_to_function.py +0 -0
  198. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_docstrings.py +0 -0
  199. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_documentation_examples.py +0 -0
  200. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_example_function_arg_types.py +0 -0
  201. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_examples_workers.py +0 -0
  202. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_exception_handling.py +0 -0
  203. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_exceptions.py +0 -0
  204. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_filter_pushdown.py +0 -0
  205. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_filter_pushdown_extension.py +0 -0
  206. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_function_storage.py +0 -0
  207. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_function_storage_azure_sql.py +0 -0
  208. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_function_storage_cf_do.py +0 -0
  209. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_function_storage_cf_do_integration.py +0 -0
  210. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_function_storage_conformance.py +0 -0
  211. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_cpp_constants.py +0 -0
  212. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_cpp_protocol_version.py +0 -0
  213. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_cpp_request_builders.py +0 -0
  214. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_cpp_schemas.py +0 -0
  215. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_cpp_secret.py +0 -0
  216. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_go_schemas.py +0 -0
  217. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_protocol_version.py +0 -0
  218. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_schemas_cross_lang.py +0 -0
  219. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_ts_client.py +0 -0
  220. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_generated_ts_schemas.py +0 -0
  221. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_http_demo_storage.py +0 -0
  222. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_http_s3_offload_input.py +0 -0
  223. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_http_s3_offload_output.py +0 -0
  224. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_landing_conformance.py +0 -0
  225. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_metadata.py +0 -0
  226. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_mypy_consolidated.py +0 -0
  227. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_nest_tensor.py +0 -0
  228. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_otel.py +0 -0
  229. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_projection_enforcement.py +0 -0
  230. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_projection_repro.py +0 -0
  231. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_protocol_classes.py +0 -0
  232. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_resolved_secrets.py +0 -0
  233. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_schema_utils.py +0 -0
  234. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_serve.py +0 -0
  235. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_setting_secret_annotations.py +0 -0
  236. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_table_buffering_function.py +0 -0
  237. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_table_function_dynamic_to_string.py +0 -0
  238. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_tcp_transport.py +0 -0
  239. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_type_bounds.py +0 -0
  240. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_union_argument.py +0 -0
  241. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_worker.py +0 -0
  242. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_worker_cli.py +0 -0
  243. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/test_worker_page.py +0 -0
  244. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/transactor/__init__.py +0 -0
  245. {vgi_python-0.18.0 → vgi_python-0.19.0}/tests/transactor/test_transactor.py +0 -0
  246. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/__init__.py +0 -0
  247. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_duckdb.py +0 -0
  248. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_storage_profile.py +0 -0
  249. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/__init__.py +0 -0
  250. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
  251. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
  252. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
  253. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/_common.py +0 -0
  254. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
  255. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
  256. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
  257. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
  258. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
  259. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
  260. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
  261. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
  262. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
  263. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/attach_options.py +0 -0
  264. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/bad_enum.py +0 -0
  265. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
  266. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/cancellable.py +0 -0
  267. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/catalog.py +0 -0
  268. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/companion.py +0 -0
  269. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/copy_from.py +0 -0
  270. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/copy_to.py +0 -0
  271. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/http_server.py +0 -0
  272. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
  273. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
  274. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
  275. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
  276. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
  277. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
  278. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
  279. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
  280. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
  281. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
  282. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
  283. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/formatting.py +0 -0
  284. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
  285. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
  286. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
  287. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/same_name.py +0 -0
  288. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
  289. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
  290. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
  291. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
  292. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/simple_writable.py +0 -0
  293. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/__init__.py +0 -0
  294. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/_common.py +0 -0
  295. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
  296. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
  297. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/cache.py +0 -0
  298. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
  299. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/filters.py +0 -0
  300. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
  301. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/make_series.py +0 -0
  302. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/misc.py +0 -0
  303. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
  304. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/pairs.py +0 -0
  305. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
  306. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
  307. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
  308. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/required_filters.py +0 -0
  309. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/sequence.py +0 -0
  310. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/settings.py +0 -0
  311. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
  312. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
  313. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
  314. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table/versioned.py +0 -0
  315. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/table_in_out.py +0 -0
  316. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/twin_catalogs.py +0 -0
  317. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/versioned.py +0 -0
  318. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
  319. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/worker.py +0 -0
  320. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
  321. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/writable/generic.py +0 -0
  322. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/writable/table.py +0 -0
  323. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/_test_fixtures/writable/worker.py +0 -0
  324. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/aggregate_function.py +0 -0
  325. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/argument_spec.py +0 -0
  326. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/arguments.py +0 -0
  327. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/auth.py +0 -0
  328. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/cache_control.py +0 -0
  329. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/__init__.py +0 -0
  330. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/_descriptor_spec.py +0 -0
  331. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/attach_option.py +0 -0
  332. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/catalog_interface.py +0 -0
  333. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/descriptors.py +0 -0
  334. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/duckdb_statistics.py +0 -0
  335. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/secret_type.py +0 -0
  336. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/setting.py +0 -0
  337. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/catalog/storage.py +0 -0
  338. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/__init__.py +0 -0
  339. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/catalog_mixin.py +0 -0
  340. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli.py +0 -0
  341. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_catalog.py +0 -0
  342. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_schema.py +0 -0
  343. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_table.py +0 -0
  344. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_transaction.py +0 -0
  345. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_utils.py +0 -0
  346. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/cli_view.py +0 -0
  347. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/client/client.py +0 -0
  348. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/__init__.py +0 -0
  349. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/_common.py +0 -0
  350. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_constants.py +0 -0
  351. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_protocol_version.py +0 -0
  352. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_request_builders.py +0 -0
  353. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_schemas.py +0 -0
  354. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
  355. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_secret_request_builders.py +0 -0
  356. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/cpp_secret_schemas.py +0 -0
  357. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/go_schemas.py +0 -0
  358. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/protocol_version.py +0 -0
  359. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/ts_client.py +0 -0
  360. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/codegen/ts_schemas.py +0 -0
  361. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/copy_from_function.py +0 -0
  362. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/copy_to_function.py +0 -0
  363. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/exceptions.py +0 -0
  364. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/function.py +0 -0
  365. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/function_storage.py +0 -0
  366. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/function_storage_azure_sql.py +0 -0
  367. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/function_storage_cf_do.py +0 -0
  368. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/__init__.py +0 -0
  369. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/demo_storage.py +0 -0
  370. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/describe_json.py +0 -0
  371. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/landing.html +0 -0
  372. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/landing_page.py +0 -0
  373. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/http/worker_page.py +0 -0
  374. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/invocation.py +0 -0
  375. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/logging_config.py +0 -0
  376. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/metadata.py +0 -0
  377. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/otel.py +0 -0
  378. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/protocol.py +0 -0
  379. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/protocol_version.txt +0 -0
  380. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/py.typed +0 -0
  381. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/scalar_function.py +0 -0
  382. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/schema_utils.py +0 -0
  383. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/secret_protocol.py +0 -0
  384. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/secret_service.py +0 -0
  385. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/serve.py +0 -0
  386. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/table_buffering_function.py +0 -0
  387. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/table_filter_pushdown.py +0 -0
  388. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/table_function.py +0 -0
  389. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/table_in_out_function.py +0 -0
  390. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/transactor/__init__.py +0 -0
  391. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/transactor/_duckdb_compat.py +0 -0
  392. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/transactor/client.py +0 -0
  393. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/transactor/protocol.py +0 -0
  394. {vgi_python-0.18.0 → vgi_python-0.19.0}/vgi/transactor/server.py +0 -0
  395. {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
- # shm: stdio + the POSIX shared-memory side channel.
49
- # launch: the AF_UNIX launcher-only tests.
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.18.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`** — `stdio` plus the POSIX shared-memory side channel
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 transport.
58
- - **`launch`** the AF_UNIX launcher transport; the launcher-only tests
59
- (`launcher/*`), which the other lanes skip via `require-env`.
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 the subprocess lanes against `VGI_SIMPLE_WRITABLE_WORKER`
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. They self-skip on the http lane
69
- (skip-on-error `'HTTP'`).
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
- # `test_shm` / `test_launcher` / `test_http` targets (the canonical local way to
20
- # run this suite against the Python worker — see vgi-python/CLAUDE.md).
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 read -r p; do [ -n "$p" ] && kill "$p" 2>/dev/null || true; done < "$BG_PIDS_FILE"
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
- echo "$pid" >> "$BG_PIDS_FILE"
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; they self-skip
176
- # over http (skip-on-error 'HTTP').
177
- export VGI_SIMPLE_WRITABLE_WORKER="$SIMPLE_WRITABLE"
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|shm)
181
- # Subprocess transport (the primary lane). shm is identical plus the POSIX
182
- # shared-memory side channel via VGI_RPC_SHM_SIZE_BYTES.
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. Only the launcher-only tests opt in here
197
- # (the rest of the suite runs on the stdio lane); mirrors make test-launcher.
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/launcher/*"
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. The launcher lane runs only launcher/* (no
257
- # simple_writable); the http lane's writes self-skip.
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
- if [ "$TRANSPORT" = "launch" ]; then
292
- run_unittest "$SUITE_GLOB" || suite_rc=$?
293
- else
294
- run_unittest "$SUITE_GLOB" "~test/sql/integration/simple_writable/*" || suite_rc=$?
295
- echo "Running simple_writable (isolated process) ..."
296
- run_unittest "test/sql/integration/simple_writable/*" || suite_rc=$?
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"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vgi-python"
3
- version = "0.18.0"
3
+ version = "0.19.0"
4
4
  description = "Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow"
5
5
  readme = "README.md"
6
6
  keywords = [
@@ -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 ``attach_opaque_data`` names the catalog, so it is the routing key.
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
- """Each sub-worker's wrapped attach routes to that catalog's class."""
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
- wrapped = meta._wrap_attach_opaque_data(index, b"\x00" * 16)
182
- assert meta._resolve_function(self._request(wrapped)) is expected
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 test_without_an_attach_the_scan_still_finds_a_host(self) -> None:
191
- """No catalog is knowable (HTTP state rehydration) resolve, don't crash."""
210
+ def test_without_an_attach_an_ambiguous_name_raises(self) -> None:
211
+ """No routing key and two declarersrefuse 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)
@@ -2228,7 +2228,7 @@ requires-dist = [
2228
2228
 
2229
2229
  [[package]]
2230
2230
  name = "vgi-python"
2231
- version = "0.18.0"
2231
+ version = "0.19.0"
2232
2232
  source = { editable = "." }
2233
2233
  dependencies = [
2234
2234
  { name = "click" },
@@ -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 :]