vgi-python 0.14.0__tar.gz → 0.16.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 (391) hide show
  1. {vgi_python-0.14.0 → vgi_python-0.16.0}/PKG-INFO +1 -1
  2. {vgi_python-0.14.0 → vgi_python-0.16.0}/ci/run-integration.sh +34 -3
  3. {vgi_python-0.14.0 → vgi_python-0.16.0}/pyproject.toml +1 -1
  4. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_declarative.py +187 -6
  5. vgi_python-0.16.0/tests/catalog/test_required_filters.py +92 -0
  6. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_serialization.py +4 -4
  7. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/landing/describe.expected.json +103 -8
  8. vgi_python-0.16.0/tests/table_in_out/generator/test_substream_partial_sum_function.py +70 -0
  9. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +5 -5
  10. vgi_python-0.16.0/tests/table_in_out/test_blended_metadata.py +150 -0
  11. {vgi_python-0.14.0 → vgi_python-0.16.0}/uv.lock +1 -1
  12. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/required_filters.py +7 -7
  13. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table_in_out.py +747 -61
  14. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/worker.py +68 -32
  15. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/catalog/catalog_interface.py +76 -15
  16. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/catalog/descriptors.py +49 -32
  17. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/metadata.py +69 -1
  18. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/protocol.py +83 -2
  19. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/table_function.py +40 -3
  20. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/table_in_out_function.py +49 -0
  21. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/worker.py +69 -9
  22. vgi_python-0.14.0/tests/catalog/test_required_field_filter_paths.py +0 -98
  23. {vgi_python-0.14.0 → vgi_python-0.16.0}/.gitattributes +0 -0
  24. {vgi_python-0.14.0 → vgi_python-0.16.0}/.github/dependabot.yml +0 -0
  25. {vgi_python-0.14.0 → vgi_python-0.16.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
  26. {vgi_python-0.14.0 → vgi_python-0.16.0}/.github/workflows/ci.yml +0 -0
  27. {vgi_python-0.14.0 → vgi_python-0.16.0}/.github/workflows/docs.yml +0 -0
  28. {vgi_python-0.14.0 → vgi_python-0.16.0}/.github/workflows/integration.yml +0 -0
  29. {vgi_python-0.14.0 → vgi_python-0.16.0}/.github/workflows/release.yml +0 -0
  30. {vgi_python-0.14.0 → vgi_python-0.16.0}/.gitignore +0 -0
  31. {vgi_python-0.14.0 → vgi_python-0.16.0}/.python-version +0 -0
  32. {vgi_python-0.14.0 → vgi_python-0.16.0}/.vale.ini +0 -0
  33. {vgi_python-0.14.0 → vgi_python-0.16.0}/CLAUDE.md +0 -0
  34. {vgi_python-0.14.0 → vgi_python-0.16.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
  35. {vgi_python-0.14.0 → vgi_python-0.16.0}/DOCS_REVIEW_RUBRIC.md +0 -0
  36. {vgi_python-0.14.0 → vgi_python-0.16.0}/DOCS_USABILITY_TEST.md +0 -0
  37. {vgi_python-0.14.0 → vgi_python-0.16.0}/LICENSE +0 -0
  38. {vgi_python-0.14.0 → vgi_python-0.16.0}/README.md +0 -0
  39. {vgi_python-0.14.0 → vgi_python-0.16.0}/SECURITY.md +0 -0
  40. {vgi_python-0.14.0 → vgi_python-0.16.0}/ci/README.md +0 -0
  41. {vgi_python-0.14.0 → vgi_python-0.16.0}/ci/preprocess-require.awk +0 -0
  42. {vgi_python-0.14.0 → vgi_python-0.16.0}/dist-vgi/.gitignore +0 -0
  43. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/aggregate-functions.md +0 -0
  44. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/arguments.md +0 -0
  45. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/auth.md +0 -0
  46. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/catalogs.md +0 -0
  47. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/client.md +0 -0
  48. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/exceptions.md +0 -0
  49. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/filters.md +0 -0
  50. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/functions.md +0 -0
  51. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/http.md +0 -0
  52. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/index.md +0 -0
  53. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/metadata.md +0 -0
  54. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/observability.md +0 -0
  55. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/storage.md +0 -0
  56. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/transactor.md +0 -0
  57. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/api/worker.md +0 -0
  58. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/argument-serialization.md +0 -0
  59. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/apple-touch-icon.png +0 -0
  60. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/favicon-16x16.png +0 -0
  61. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/favicon-32x32.png +0 -0
  62. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/favicon.ico +0 -0
  63. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/kinds/aggregate.svg +0 -0
  64. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/kinds/buffering.svg +0 -0
  65. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/kinds/scalar.svg +0 -0
  66. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/kinds/table-in-out.svg +0 -0
  67. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/kinds/table.svg +0 -0
  68. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/logo.png +0 -0
  69. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/assets/social-card.png +0 -0
  70. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/authentication.md +0 -0
  71. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/catalog-interface.md +0 -0
  72. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/cli.md +0 -0
  73. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/column-statistics.md +0 -0
  74. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/concepts/index.md +0 -0
  75. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/contributing-docs.md +0 -0
  76. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/filter-pushdown.md +0 -0
  77. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/generator-api.md +0 -0
  78. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/how-to/catalogs.md +0 -0
  79. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/how-to/function-patterns.md +0 -0
  80. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/how-to/http-auth.md +0 -0
  81. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/how-to/index.md +0 -0
  82. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/how-to/pushdown-and-statistics.md +0 -0
  83. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/how-to/state-storage.md +0 -0
  84. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/index.md +0 -0
  85. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/lifecycle.md +0 -0
  86. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/metadata.md +0 -0
  87. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/overrides/main.html +0 -0
  88. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/robots.txt +0 -0
  89. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/shared-storage.md +0 -0
  90. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/stylesheets/extra.css +0 -0
  91. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/tutorial/index.md +0 -0
  92. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/tutorial/scalar.md +0 -0
  93. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/tutorial/table.md +0 -0
  94. {vgi_python-0.14.0 → vgi_python-0.16.0}/docs/vgi-logo.png +0 -0
  95. {vgi_python-0.14.0 → vgi_python-0.16.0}/examples/calc_scalar_worker.py +0 -0
  96. {vgi_python-0.14.0 → vgi_python-0.16.0}/examples/calc_worker.py +0 -0
  97. {vgi_python-0.14.0 → vgi_python-0.16.0}/examples/filter_worker.py +0 -0
  98. {vgi_python-0.14.0 → vgi_python-0.16.0}/examples/greeting_scalar_worker.py +0 -0
  99. {vgi_python-0.14.0 → vgi_python-0.16.0}/examples/row_count_worker.py +0 -0
  100. {vgi_python-0.14.0 → vgi_python-0.16.0}/examples/series_streaming_worker.py +0 -0
  101. {vgi_python-0.14.0 → vgi_python-0.16.0}/examples/sum_worker.py +0 -0
  102. {vgi_python-0.14.0 → vgi_python-0.16.0}/mkdocs.yml +0 -0
  103. {vgi_python-0.14.0 → vgi_python-0.16.0}/packages/vgi-fixtures/LICENSE +0 -0
  104. {vgi_python-0.14.0 → vgi_python-0.16.0}/packages/vgi-fixtures/README.md +0 -0
  105. {vgi_python-0.14.0 → vgi_python-0.16.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
  106. {vgi_python-0.14.0 → vgi_python-0.16.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
  107. {vgi_python-0.14.0 → vgi_python-0.16.0}/packages/vgi-fixtures/pyproject.toml +0 -0
  108. {vgi_python-0.14.0 → vgi_python-0.16.0}/scripts/measure_startup.py +0 -0
  109. {vgi_python-0.14.0 → vgi_python-0.16.0}/scripts/run_all_tests.sh +0 -0
  110. {vgi_python-0.14.0 → vgi_python-0.16.0}/test-data/generate.sh +0 -0
  111. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/__init__.py +0 -0
  112. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/_http_fixtures.py +0 -0
  113. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/__init__.py +0 -0
  114. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_catalog_interface.py +0 -0
  115. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_client_catalog.py +0 -0
  116. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_column_statistics.py +0 -0
  117. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_example_worker_catalog.py +0 -0
  118. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_integration.py +0 -0
  119. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_scan_branches.py +0 -0
  120. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_setting.py +0 -0
  121. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_storage.py +0 -0
  122. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_time_travel.py +0 -0
  123. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/catalog/test_writable_table.py +0 -0
  124. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/client/__init__.py +0 -0
  125. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/client/test_broken_pipe.py +0 -0
  126. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/client/test_cli.py +0 -0
  127. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/client/test_cli_catalog_functions.py +0 -0
  128. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/client/test_worker_debug.py +0 -0
  129. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/__init__.py +0 -0
  130. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/_stub.py +0 -0
  131. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/conftest.py +0 -0
  132. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_accumulate.py +0 -0
  133. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_aggregate.py +0 -0
  134. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_attach.py +0 -0
  135. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_bearer_auth.py +0 -0
  136. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_const_param_enforcement.py +0 -0
  137. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_container.py +0 -0
  138. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_copy_from.py +0 -0
  139. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_copy_to.py +0 -0
  140. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_directory_parity.py +0 -0
  141. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_function_inventory.py +0 -0
  142. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_github.py +0 -0
  143. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_http_client.py +0 -0
  144. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_http_external_location.py +0 -0
  145. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_http_upload_url.py +0 -0
  146. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_macro.py +0 -0
  147. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_overload.py +0 -0
  148. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_protocol_inventory.py +0 -0
  149. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_protocol_version.py +0 -0
  150. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_resumable_scan.py +0 -0
  151. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_scalar.py +0 -0
  152. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
  153. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_secret.py +0 -0
  154. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_settings.py +0 -0
  155. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_table.py +0 -0
  156. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_table_in_out.py +0 -0
  157. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_view.py +0 -0
  158. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conformance/test_writable.py +0 -0
  159. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/conftest.py +0 -0
  160. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/landing/_checker.py +0 -0
  161. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/landing/describe.schema.json +0 -0
  162. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/scalar/__init__.py +0 -0
  163. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/scalar/test_bernoulli_function.py +0 -0
  164. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/scalar/test_binary_packet_function.py +0 -0
  165. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/scalar/test_client.py +0 -0
  166. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/scalar/test_conditional_message_function.py +0 -0
  167. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/scalar/test_hash_seed_function.py +0 -0
  168. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
  169. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/scalar/test_multiply_function.py +0 -0
  170. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/scalar/test_random_bytes_function.py +0 -0
  171. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/scalar/test_return_secret_value_function.py +0 -0
  172. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/__init__.py +0 -0
  173. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/__init__.py +0 -0
  174. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_constant_columns_function.py +0 -0
  175. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_double_sequence_function.py +0 -0
  176. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_exception_function.py +0 -0
  177. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_filter_echo_function.py +0 -0
  178. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_logging_function.py +0 -0
  179. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
  180. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_partitioned_function.py +0 -0
  181. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_projected_data_function.py +0 -0
  182. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_sequence_function.py +0 -0
  183. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_settings_function.py +0 -0
  184. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_struct_settings_function.py +0 -0
  185. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
  186. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table_in_out/__init__.py +0 -0
  187. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table_in_out/generator/__init__.py +0 -0
  188. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
  189. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
  190. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
  191. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
  192. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
  193. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/table_in_out/test_client.py +0 -0
  194. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_access_log_audit.py +0 -0
  195. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_aggregate_function.py +0 -0
  196. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_argument_spec.py +0 -0
  197. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_auth.py +0 -0
  198. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_bind_exceptions.py +0 -0
  199. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_bind_request_at_clause.py +0 -0
  200. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_bound_storage_conformance.py +0 -0
  201. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_cache_control.py +0 -0
  202. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_catalog_auth_binding.py +0 -0
  203. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_copy_from_function.py +0 -0
  204. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_copy_to_function.py +0 -0
  205. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_docstrings.py +0 -0
  206. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_documentation_examples.py +0 -0
  207. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_example_function_arg_types.py +0 -0
  208. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_examples_workers.py +0 -0
  209. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_exception_handling.py +0 -0
  210. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_exceptions.py +0 -0
  211. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_filter_pushdown.py +0 -0
  212. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_filter_pushdown_extension.py +0 -0
  213. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_function_storage.py +0 -0
  214. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_function_storage_azure_sql.py +0 -0
  215. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_function_storage_cf_do.py +0 -0
  216. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_function_storage_cf_do_integration.py +0 -0
  217. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_function_storage_conformance.py +0 -0
  218. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_generated_cpp_constants.py +0 -0
  219. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_generated_cpp_protocol_version.py +0 -0
  220. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_generated_cpp_request_builders.py +0 -0
  221. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_generated_cpp_schemas.py +0 -0
  222. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_generated_cpp_secret.py +0 -0
  223. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_generated_go_schemas.py +0 -0
  224. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_generated_protocol_version.py +0 -0
  225. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_generated_schemas_cross_lang.py +0 -0
  226. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_generated_ts_client.py +0 -0
  227. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_generated_ts_schemas.py +0 -0
  228. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_http_demo_storage.py +0 -0
  229. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_http_s3_offload_input.py +0 -0
  230. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_http_s3_offload_output.py +0 -0
  231. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_landing_conformance.py +0 -0
  232. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_metadata.py +0 -0
  233. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_mypy_consolidated.py +0 -0
  234. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_nest_tensor.py +0 -0
  235. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_otel.py +0 -0
  236. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_projection_enforcement.py +0 -0
  237. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_projection_repro.py +0 -0
  238. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_protocol_classes.py +0 -0
  239. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_resolved_secrets.py +0 -0
  240. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_schema_utils.py +0 -0
  241. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_serve.py +0 -0
  242. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_setting_secret_annotations.py +0 -0
  243. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_table_buffering_function.py +0 -0
  244. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_table_function_dynamic_to_string.py +0 -0
  245. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_tcp_transport.py +0 -0
  246. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_type_bounds.py +0 -0
  247. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_union_argument.py +0 -0
  248. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_worker.py +0 -0
  249. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_worker_cli.py +0 -0
  250. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/test_worker_page.py +0 -0
  251. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/transactor/__init__.py +0 -0
  252. {vgi_python-0.14.0 → vgi_python-0.16.0}/tests/transactor/test_transactor.py +0 -0
  253. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/__init__.py +0 -0
  254. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_duckdb.py +0 -0
  255. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_storage_profile.py +0 -0
  256. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/__init__.py +0 -0
  257. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
  258. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
  259. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
  260. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/_common.py +0 -0
  261. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
  262. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
  263. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
  264. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
  265. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
  266. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
  267. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
  268. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
  269. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
  270. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/attach_options.py +0 -0
  271. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/bad_enum.py +0 -0
  272. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
  273. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/cancellable.py +0 -0
  274. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/catalog.py +0 -0
  275. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/companion.py +0 -0
  276. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/copy_from.py +0 -0
  277. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/copy_to.py +0 -0
  278. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/http_server.py +0 -0
  279. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
  280. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
  281. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
  282. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
  283. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
  284. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
  285. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
  286. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
  287. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
  288. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
  289. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/scalar/formatting.py +0 -0
  290. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
  291. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
  292. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
  293. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
  294. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
  295. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
  296. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
  297. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/simple_writable.py +0 -0
  298. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/__init__.py +0 -0
  299. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/_common.py +0 -0
  300. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
  301. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
  302. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/cache.py +0 -0
  303. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
  304. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/filters.py +0 -0
  305. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
  306. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/make_series.py +0 -0
  307. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/misc.py +0 -0
  308. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
  309. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/pairs.py +0 -0
  310. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
  311. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
  312. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
  313. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/sequence.py +0 -0
  314. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/settings.py +0 -0
  315. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
  316. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
  317. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
  318. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/table/versioned.py +0 -0
  319. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/versioned.py +0 -0
  320. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
  321. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
  322. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/writable/generic.py +0 -0
  323. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/writable/table.py +0 -0
  324. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/_test_fixtures/writable/worker.py +0 -0
  325. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/aggregate_function.py +0 -0
  326. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/argument_spec.py +0 -0
  327. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/arguments.py +0 -0
  328. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/auth.py +0 -0
  329. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/cache_control.py +0 -0
  330. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/catalog/__init__.py +0 -0
  331. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/catalog/_descriptor_spec.py +0 -0
  332. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/catalog/attach_option.py +0 -0
  333. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/catalog/duckdb_statistics.py +0 -0
  334. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/catalog/secret_type.py +0 -0
  335. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/catalog/setting.py +0 -0
  336. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/catalog/storage.py +0 -0
  337. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/client/__init__.py +0 -0
  338. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/client/catalog_mixin.py +0 -0
  339. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/client/cli.py +0 -0
  340. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/client/cli_catalog.py +0 -0
  341. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/client/cli_schema.py +0 -0
  342. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/client/cli_table.py +0 -0
  343. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/client/cli_transaction.py +0 -0
  344. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/client/cli_utils.py +0 -0
  345. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/client/cli_view.py +0 -0
  346. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/client/client.py +0 -0
  347. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/__init__.py +0 -0
  348. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/_common.py +0 -0
  349. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/cpp_constants.py +0 -0
  350. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/cpp_protocol_version.py +0 -0
  351. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/cpp_request_builders.py +0 -0
  352. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/cpp_schemas.py +0 -0
  353. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
  354. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/cpp_secret_request_builders.py +0 -0
  355. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/cpp_secret_schemas.py +0 -0
  356. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/go_schemas.py +0 -0
  357. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/protocol_version.py +0 -0
  358. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/ts_client.py +0 -0
  359. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/codegen/ts_schemas.py +0 -0
  360. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/copy_from_function.py +0 -0
  361. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/copy_to_function.py +0 -0
  362. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/exceptions.py +0 -0
  363. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/function.py +0 -0
  364. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/function_storage.py +0 -0
  365. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/function_storage_azure_sql.py +0 -0
  366. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/function_storage_cf_do.py +0 -0
  367. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/http/__init__.py +0 -0
  368. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/http/demo_storage.py +0 -0
  369. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/http/describe_json.py +0 -0
  370. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/http/landing.html +0 -0
  371. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/http/landing_page.py +0 -0
  372. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/http/worker_page.py +0 -0
  373. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/invocation.py +0 -0
  374. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/logging_config.py +0 -0
  375. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/meta_worker.py +0 -0
  376. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/otel.py +0 -0
  377. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/protocol_version.txt +0 -0
  378. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/py.typed +0 -0
  379. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/scalar_function.py +0 -0
  380. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/schema_utils.py +0 -0
  381. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/secret_protocol.py +0 -0
  382. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/secret_service.py +0 -0
  383. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/serve.py +0 -0
  384. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/table_buffering_function.py +0 -0
  385. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/table_filter_pushdown.py +0 -0
  386. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/transactor/__init__.py +0 -0
  387. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/transactor/_duckdb_compat.py +0 -0
  388. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/transactor/client.py +0 -0
  389. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/transactor/protocol.py +0 -0
  390. {vgi_python-0.14.0 → vgi_python-0.16.0}/vgi/transactor/server.py +0 -0
  391. {vgi_python-0.14.0 → vgi_python-0.16.0}/wrangler.jsonc +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vgi-python
3
- Version: 0.14.0
3
+ Version: 0.16.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
@@ -257,12 +257,43 @@ rm -f "$STAGE/test/_warm.test"
257
257
  # simple_writable); the http lane's writes self-skip.
258
258
  echo "Running suite ($SUITE_GLOB, transport=$TRANSPORT) ..."
259
259
  suite_rc=0
260
+
261
+ # run_unittest — invoke haybarn-unittest, streaming its output, and additionally
262
+ # fail on a fatal-signal report that the process's own exit code cannot express.
263
+ #
264
+ # Catch2 arms handlers for SIGTERM/SIGINT/SIGSEGV/... for the duration of a test
265
+ # case. Those handlers are inherited by any process the extension fork()s, and
266
+ # run in the child if a signal lands before it execs. The child then prints a
267
+ # full "FAILED: ... due to a fatal error condition: SIGTERM" block plus a run
268
+ # summary — the *parent's* accumulated counters, since it's an address-space
269
+ # copy — and dies. The parent never sees it, records no failure, and exits 0.
270
+ # The only trace is on stdout, so that is what we scan. Seen on the shm lane in
271
+ # https://github.com/Query-farm/vgi-python/actions/runs/29051359074.
272
+ run_unittest() {
273
+ local log rc=0
274
+ log="$(mktemp)"
275
+ # `set +e` rather than `|| true`: the latter runs before PIPESTATUS is read and
276
+ # overwrites it with true's 0, silently swallowing every real test failure.
277
+ set +e
278
+ "$HAYBARN_UNITTEST" "$@" 2>&1 | tee "$log"
279
+ rc="${PIPESTATUS[0]}"
280
+ set -e
281
+ if grep -q 'due to a fatal error condition' "$log"; then
282
+ echo "::error::a forked child ran the test harness's signal handler (see the" \
283
+ "'fatal error condition' block above). The parent exited $rc and would" \
284
+ "otherwise have passed. This is invisible to the exit code by construction."
285
+ rc=1
286
+ fi
287
+ rm -f "$log"
288
+ return "$rc"
289
+ }
290
+
260
291
  if [ "$TRANSPORT" = "launch" ]; then
261
- "$HAYBARN_UNITTEST" "$SUITE_GLOB" || suite_rc=$?
292
+ run_unittest "$SUITE_GLOB" || suite_rc=$?
262
293
  else
263
- "$HAYBARN_UNITTEST" "$SUITE_GLOB" "~test/sql/integration/simple_writable/*" || suite_rc=$?
294
+ run_unittest "$SUITE_GLOB" "~test/sql/integration/simple_writable/*" || suite_rc=$?
264
295
  echo "Running simple_writable (isolated process) ..."
265
- "$HAYBARN_UNITTEST" "test/sql/integration/simple_writable/*" || suite_rc=$?
296
+ run_unittest "test/sql/integration/simple_writable/*" || suite_rc=$?
266
297
  fi
267
298
 
268
299
  exit "$suite_rc"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vgi-python"
3
- version = "0.14.0"
3
+ version = "0.16.0"
4
4
  description = "Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow"
5
5
  readme = "README.md"
6
6
  keywords = [
@@ -14,13 +14,13 @@ Tests cover:
14
14
  from __future__ import annotations
15
15
 
16
16
  from dataclasses import dataclass
17
- from typing import ClassVar
17
+ from typing import Annotated, ClassVar
18
18
 
19
19
  import pyarrow as pa
20
20
  import pytest
21
21
  from vgi_rpc.rpc import OutputCollector
22
22
 
23
- from vgi import Worker
23
+ from vgi import Arg, Arguments, Worker
24
24
  from vgi.catalog import (
25
25
  AttachOpaqueData,
26
26
  Catalog,
@@ -50,6 +50,7 @@ from vgi.table_function import (
50
50
  bind_fixed_schema,
51
51
  init_single_worker,
52
52
  )
53
+ from vgi.table_in_out_function import TableInOutGenerator
53
54
 
54
55
  # =============================================================================
55
56
  # Test Fixtures: Example Functions for Function-Backed Tables
@@ -61,6 +62,45 @@ class EmptyArgs:
61
62
  """No arguments."""
62
63
 
63
64
 
65
+ @dataclass(frozen=True)
66
+ class CountedRowsArgs:
67
+ """A required positional argument plus an optional named one."""
68
+
69
+ count: Annotated[int, Arg(0, doc="Number of rows to generate", ge=0)]
70
+ label: Annotated[str, Arg("label", default="rows", doc="Label for the rows")]
71
+
72
+
73
+ @init_single_worker
74
+ @bind_fixed_schema
75
+ class CountedRowsFunction(TableFunctionGenerator[CountedRowsArgs]):
76
+ """Table function with a required positional arg, for arg-propagation tests."""
77
+
78
+ class Meta: # noqa: D106
79
+ name = "counted_rows"
80
+ description = "Generate N rows"
81
+
82
+ FIXED_SCHEMA: ClassVar[pa.Schema] = schema({"n": pa.int64()})
83
+
84
+ @classmethod
85
+ def process( # noqa: D102
86
+ cls, params: ProcessParams[CountedRowsArgs], state: None, out: OutputCollector
87
+ ) -> None:
88
+ out.emit(
89
+ pa.RecordBatch.from_pydict(
90
+ {"n": list(range(params.args.count))},
91
+ schema=params.output_schema,
92
+ )
93
+ )
94
+ out.finish()
95
+
96
+
97
+ class CountedRowsInsert(TableInOutGenerator[None, None]):
98
+ """Stub insert function, for the write-path arg-propagation test."""
99
+
100
+ class Meta: # noqa: D106
101
+ name = "counted_rows_insert"
102
+
103
+
64
104
  @init_single_worker
65
105
  @bind_fixed_schema
66
106
  class UsersFunction(TableFunctionGenerator[EmptyArgs]):
@@ -262,6 +302,87 @@ class TestTableWithFunction:
262
302
  assert info.update_function is None
263
303
  assert info.delete_function is None
264
304
 
305
+ def test_inlined_scan_function_carries_declared_positional_arguments(self) -> None:
306
+ """``Table.arguments`` must reach the scan-time bind, not just ``bind()``.
307
+
308
+ Regression: the inlined ``ScanFunctionResult`` used to hardcode empty
309
+ arguments, so a backing function with a required positional ``Arg(0)``
310
+ saw zero positional args at scan and failed with
311
+ ``IndexError: Argument 0: index out of range``.
312
+ """
313
+ from vgi_rpc.utils import deserialize_record_batch
314
+
315
+ table = Table(
316
+ name="counted",
317
+ function=CountedRowsFunction,
318
+ arguments=Arguments(positional=(pa.scalar(7),)),
319
+ )
320
+ info = table.to_table_info("main")
321
+ assert info.scan_function is not None
322
+ batch, _ = deserialize_record_batch(info.scan_function)
323
+ sfr = ScanFunctionResult.deserialize(batch)
324
+ assert sfr.function_name == "counted_rows"
325
+ assert [s.as_py() for s in sfr.positional_arguments] == [7]
326
+ assert sfr.named_arguments == {}
327
+
328
+ def test_inlined_scan_function_carries_declared_named_arguments(self) -> None:
329
+ """Named args declared on the Table also reach the scan-time bind."""
330
+ from vgi_rpc.utils import deserialize_record_batch
331
+
332
+ table = Table(
333
+ name="counted",
334
+ function=CountedRowsFunction,
335
+ arguments=Arguments(
336
+ positional=(pa.scalar(3),),
337
+ named={"label": pa.scalar("widgets")},
338
+ ),
339
+ )
340
+ info = table.to_table_info("main")
341
+ assert info.scan_function is not None
342
+ batch, _ = deserialize_record_batch(info.scan_function)
343
+ sfr = ScanFunctionResult.deserialize(batch)
344
+ assert [s.as_py() for s in sfr.positional_arguments] == [3]
345
+ assert {k: v.as_py() for k, v in sfr.named_arguments.items()} == {"label": "widgets"}
346
+
347
+ def test_inlined_write_functions_ignore_table_arguments(self) -> None:
348
+ """Only the *scan* function is parameterized by ``Table.arguments``.
349
+
350
+ Insert/update/delete functions receive their rows via the streaming table
351
+ input, so their inlined results stay argument-free.
352
+ """
353
+ from vgi_rpc.utils import deserialize_record_batch
354
+
355
+ table = Table(
356
+ name="counted",
357
+ function=CountedRowsFunction,
358
+ arguments=Arguments(positional=(pa.scalar(7),)),
359
+ insert_function=CountedRowsInsert,
360
+ )
361
+ info = table.to_table_info("main")
362
+ assert info.insert_function is not None
363
+ batch, _ = deserialize_record_batch(info.insert_function)
364
+ sfr = ScanFunctionResult.deserialize(batch)
365
+ assert sfr.positional_arguments == []
366
+ assert sfr.named_arguments == {}
367
+
368
+ def test_none_positional_slot_is_rejected(self) -> None:
369
+ """A ``None`` positional slot would shift later args — reject it loudly.
370
+
371
+ Exercised on the ``scan_arguments_from`` helper directly: a Table carrying
372
+ such a slot already fails earlier, in bind()-based schema derivation. This
373
+ guards the serializer against ever silently dropping the slot instead.
374
+ """
375
+ from vgi.catalog.catalog_interface import scan_arguments_from
376
+
377
+ with pytest.raises(ValueError, match="positional slot 0 is None"):
378
+ scan_arguments_from(Arguments(positional=(None, pa.scalar(7))))
379
+
380
+ def test_scan_arguments_from_none_is_empty(self) -> None:
381
+ """A table declaring no arguments serializes empty args (unchanged behavior)."""
382
+ from vgi.catalog.catalog_interface import scan_arguments_from
383
+
384
+ assert scan_arguments_from(None) == ([], {})
385
+
265
386
  def test_table_propagates_cardinality_to_table_info(self) -> None:
266
387
  """``Table.cardinality_estimate`` / ``cardinality_max`` flow into TableInfo.
267
388
 
@@ -1121,8 +1242,6 @@ class TestSchemaDescriptor:
1121
1242
  client hits a cold miss. Reporting an explicit ``0`` for empty
1122
1243
  categories lets the client skip the bulk RPC entirely.
1123
1244
  """
1124
- from typing import Annotated
1125
-
1126
1245
  from vgi.arguments import Param, Returns
1127
1246
  from vgi.scalar_function import ScalarFunction
1128
1247
 
@@ -1174,8 +1293,6 @@ class TestLegacyFunctionsListSchemaInfo:
1174
1293
  """
1175
1294
 
1176
1295
  def _interface(self) -> ReadOnlyCatalogInterface:
1177
- from typing import Annotated
1178
-
1179
1296
  from vgi.arguments import Param, Returns
1180
1297
  from vgi.scalar_function import ScalarFunction
1181
1298
 
@@ -1694,6 +1811,70 @@ class TestTableScanFunctionGet:
1694
1811
  )
1695
1812
  assert isinstance(result, ScanFunctionResult)
1696
1813
  assert result.function_name == "users"
1814
+ # No declared Table.arguments => empty args, unchanged behavior.
1815
+ assert result.positional_arguments == []
1816
+ assert result.named_arguments == {}
1817
+
1818
+ def test_auto_scan_forwards_declared_arguments(self) -> None:
1819
+ """The RPC auto-impl forwards ``Table.arguments`` to the scan bind.
1820
+
1821
+ Companion to the inlined-path regression: workers that don't inline
1822
+ ``TableInfo.scan_function`` reach the scan through this RPC instead.
1823
+ """
1824
+ counted = Table(
1825
+ name="counted",
1826
+ function=CountedRowsFunction,
1827
+ arguments=Arguments(
1828
+ positional=(pa.scalar(7),),
1829
+ named={"label": pa.scalar("widgets")},
1830
+ ),
1831
+ )
1832
+
1833
+ class TestCatalog(ReadOnlyCatalogInterface):
1834
+ catalog = Catalog(
1835
+ name="test",
1836
+ schemas=[Schema(name="main", tables=[counted])],
1837
+ )
1838
+
1839
+ result = TestCatalog().table_scan_function_get(
1840
+ attach_opaque_data=AttachOpaqueData(b"test"),
1841
+ transaction_opaque_data=None,
1842
+ schema_name="main",
1843
+ name="counted",
1844
+ at_unit=None,
1845
+ at_value=None,
1846
+ )
1847
+ assert result.function_name == "counted_rows"
1848
+ assert [s.as_py() for s in result.positional_arguments] == [7]
1849
+ assert {k: v.as_py() for k, v in result.named_arguments.items()} == {"label": "widgets"}
1850
+
1851
+ def test_scan_branches_default_inherits_declared_arguments(self) -> None:
1852
+ """The default ``table_scan_branches_get`` delegates to the legacy method.
1853
+
1854
+ So the one-branch synthesis must carry the declared arguments too.
1855
+ """
1856
+ counted = Table(
1857
+ name="counted",
1858
+ function=CountedRowsFunction,
1859
+ arguments=Arguments(positional=(pa.scalar(7),)),
1860
+ )
1861
+
1862
+ class TestCatalog(ReadOnlyCatalogInterface):
1863
+ catalog = Catalog(
1864
+ name="test",
1865
+ schemas=[Schema(name="main", tables=[counted])],
1866
+ )
1867
+
1868
+ result = TestCatalog().table_scan_branches_get(
1869
+ attach_opaque_data=AttachOpaqueData(b"test"),
1870
+ transaction_opaque_data=None,
1871
+ schema_name="main",
1872
+ name="counted",
1873
+ at_unit=None,
1874
+ at_value=None,
1875
+ )
1876
+ assert len(result.branches) == 1
1877
+ assert [s.as_py() for s in result.branches[0].positional_arguments] == [7]
1697
1878
 
1698
1879
  def test_explicit_columns_table_raises(self) -> None:
1699
1880
  """Tables with explicit columns raise NotImplementedError."""
@@ -0,0 +1,92 @@
1
+ """Unit tests for the ``Table.required_filters`` field.
2
+
3
+ The field is purely declarative on the Python side — enforcement runs in the C++
4
+ optimizer extension. It is conjunctive normal form: an AND (outer tuple) of
5
+ OR-groups (inner tuples) of dotted-path column references. These tests cover:
6
+
7
+ 1. Default-empty behaviour (no field set → ``TableInfo`` ships an empty list).
8
+ 2. Populated round-trip (singleton groups + a multi-path OR-group preserved).
9
+ 3. Validation: each path's leading dotted segment must be a real column.
10
+ 4. Empty-string paths and empty OR-groups are rejected loudly.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import pyarrow as pa
16
+ import pytest
17
+
18
+ from vgi.catalog.descriptors import Table
19
+
20
+
21
+ class TestRequiredFiltersField:
22
+ """Behaviour of the declarative required_filters field."""
23
+
24
+ def _bbox_columns(self) -> pa.Schema:
25
+ """Return a small schema with a ``bbox`` STRUCT plus flat id/ticker."""
26
+ return pa.schema(
27
+ [
28
+ ("id", pa.int64()),
29
+ ("ticker", pa.string()),
30
+ (
31
+ "bbox",
32
+ pa.struct(
33
+ [
34
+ ("xmin", pa.float64()),
35
+ ("ymin", pa.float64()),
36
+ ("xmax", pa.float64()),
37
+ ("ymax", pa.float64()),
38
+ ]
39
+ ),
40
+ ),
41
+ ]
42
+ )
43
+
44
+ def test_default_is_empty_tuple(self) -> None:
45
+ """Default is an empty tuple; TableInfo wire field is an empty list."""
46
+ t = Table(name="t", columns=self._bbox_columns())
47
+ assert t.required_filters == ()
48
+ info = t.to_table_info("main")
49
+ assert info.required_filters == []
50
+
51
+ def test_singleton_groups_round_trip(self) -> None:
52
+ """Singleton groups (the AND-only case) survive as a list of lists."""
53
+ groups = (("bbox.xmin",), ("bbox.xmax",), ("bbox.ymin",), ("bbox.ymax",))
54
+ t = Table(name="place", columns=self._bbox_columns(), required_filters=groups)
55
+ assert t.required_filters == groups
56
+ info = t.to_table_info("main")
57
+ assert info.required_filters == [list(g) for g in groups]
58
+
59
+ def test_or_group_round_trip(self) -> None:
60
+ """A multi-path OR-group survives alongside a singleton mandatory group."""
61
+ groups = (("id",), ("id", "ticker"))
62
+ t = Table(name="t", columns=self._bbox_columns(), required_filters=groups)
63
+ info = t.to_table_info("main")
64
+ assert info.required_filters == [["id"], ["id", "ticker"]]
65
+
66
+ def test_top_level_path(self) -> None:
67
+ """A top-level column name (no dots) is a valid path."""
68
+ t = Table(name="t", columns=self._bbox_columns(), required_filters=(("id",),))
69
+ assert t.to_table_info("main").required_filters == [["id"]]
70
+
71
+ def test_nested_path_is_not_unpacked_for_validation(self) -> None:
72
+ """Only the leading dotted segment is validated; deeper segments pass through."""
73
+ # Subfield validity isn't checked here (descriptor doesn't unpack struct
74
+ # schemas) — only the leading segment matters. Typos like ``bbox.nope``
75
+ # go through and DuckDB catches them at scan time.
76
+ t = Table(name="t", columns=self._bbox_columns(), required_filters=(("bbox.nope",),))
77
+ assert t.required_filters == (("bbox.nope",),)
78
+
79
+ def test_unknown_leading_segment_raises(self) -> None:
80
+ """A path whose leading segment is not a real column is rejected."""
81
+ with pytest.raises(ValueError, match=r"unknown column 'nope'"):
82
+ Table(name="t", columns=self._bbox_columns(), required_filters=(("nope.xmin",),))
83
+
84
+ def test_empty_string_path_raises(self) -> None:
85
+ """An empty string path is rejected loudly rather than silently accepted."""
86
+ with pytest.raises(ValueError, match="must not contain empty strings"):
87
+ Table(name="t", columns=self._bbox_columns(), required_filters=(("",),))
88
+
89
+ def test_empty_group_raises(self) -> None:
90
+ """An empty OR-group is rejected loudly."""
91
+ with pytest.raises(ValueError, match="must not contain empty groups"):
92
+ Table(name="t", columns=self._bbox_columns(), required_filters=((),))
@@ -1149,10 +1149,10 @@ class TestArrowSchemaCorrectness:
1149
1149
  assert schema.field("cardinality_estimate").type == pa.int64()
1150
1150
  assert schema.field("column_statistics").type == pa.binary()
1151
1151
  assert schema.field("bind_result").type == pa.binary()
1152
- assert schema.field("required_field_filter_paths").type == pa.list_(pa.string())
1153
- # required_field_filter_paths is the LAST field (positional schema
1154
- # compatibility: old C++ extensions ignore-by-position trailing fields).
1155
- assert schema.field(len(schema) - 1).name == "required_field_filter_paths"
1152
+ assert schema.field("required_filters").type == pa.list_(pa.list_(pa.string()))
1153
+ # required_filters is the LAST field (positional schema compatibility:
1154
+ # old C++ extensions ignore-by-position trailing fields).
1155
+ assert schema.field(len(schema) - 1).name == "required_filters"
1156
1156
 
1157
1157
  def test_view_info_schema(self) -> None:
1158
1158
  """Verify ViewInfo Arrow schema."""
@@ -3,9 +3,9 @@
3
3
  {
4
4
  "attach_options": [],
5
5
  "counts": {
6
- "functions": 183,
6
+ "functions": 188,
7
7
  "schemas": 2,
8
- "tables": 58,
8
+ "tables": 59,
9
9
  "views": 3
10
10
  },
11
11
  "data_version_spec": null,
@@ -225,9 +225,14 @@
225
225
  },
226
226
  {
227
227
  "cols": 2,
228
- "comment": "rff_none — control table with no required_field_filter_paths (opt-out fast path).",
228
+ "comment": "rff_none — control table with no required_filters (opt-out fast path).",
229
229
  "name": "rff_none"
230
230
  },
231
+ {
232
+ "cols": 2,
233
+ "comment": "rff_or — requires a filter on 'a' OR 'b' (a single OR-group).",
234
+ "name": "rff_or"
235
+ },
231
236
  {
232
237
  "cols": 2,
233
238
  "comment": "rff_parquet — native read_parquet delegation with bbox.* required filters.",
@@ -1242,6 +1247,18 @@
1242
1247
  "returns": "string",
1243
1248
  "type": "scalar"
1244
1249
  },
1250
+ {
1251
+ "args": [
1252
+ {
1253
+ "desc": "Input column (ignored)",
1254
+ "name": "x",
1255
+ "type": "double"
1256
+ }
1257
+ ],
1258
+ "doc": "Blended 1->0 map emitting a single 0-row batch (literal scan-mode)",
1259
+ "name": "blended_drop",
1260
+ "type": "table"
1261
+ },
1245
1262
  {
1246
1263
  "args": [
1247
1264
  {
@@ -1815,6 +1832,59 @@
1815
1832
  "name": "generator_exception",
1816
1833
  "type": "table"
1817
1834
  },
1835
+ {
1836
+ "args": [
1837
+ {
1838
+ "desc": "Latitude input column",
1839
+ "name": "latitude",
1840
+ "type": "double"
1841
+ },
1842
+ {
1843
+ "desc": "Longitude input column",
1844
+ "name": "longitude",
1845
+ "type": "double"
1846
+ },
1847
+ {
1848
+ "default": "4",
1849
+ "desc": "Rounding precision",
1850
+ "name": "precision",
1851
+ "named": true,
1852
+ "type": "int64"
1853
+ }
1854
+ ],
1855
+ "doc": "Blended per-row geo encoder (lat, lon -> geohash)",
1856
+ "name": "geo_encode",
1857
+ "type": "table"
1858
+ },
1859
+ {
1860
+ "args": [
1861
+ {
1862
+ "desc": "Latitude input column",
1863
+ "name": "latitude",
1864
+ "type": "double"
1865
+ },
1866
+ {
1867
+ "desc": "Longitude input column",
1868
+ "name": "longitude",
1869
+ "type": "double"
1870
+ },
1871
+ {
1872
+ "desc": "Altitude input column",
1873
+ "name": "altitude",
1874
+ "type": "double"
1875
+ },
1876
+ {
1877
+ "default": "4",
1878
+ "desc": "Rounding precision",
1879
+ "name": "precision",
1880
+ "named": true,
1881
+ "type": "int64"
1882
+ }
1883
+ ],
1884
+ "doc": "Blended per-row geo encoder (lat, lon, alt -> geohash)",
1885
+ "name": "geo_encode",
1886
+ "type": "table"
1887
+ },
1818
1888
  {
1819
1889
  "args": [
1820
1890
  {
@@ -2316,13 +2386,13 @@
2316
2386
  },
2317
2387
  {
2318
2388
  "args": [],
2319
- "doc": "rff_nested — nested STRUCT(wrapper.mid.leaf) for required_field_filter_paths tests",
2389
+ "doc": "rff_nested — nested STRUCT(wrapper.mid.leaf) for required_filters tests",
2320
2390
  "name": "rff_nested_scan",
2321
2391
  "type": "table"
2322
2392
  },
2323
2393
  {
2324
2394
  "args": [],
2325
- "doc": "rff_none — control table with no required_field_filter_paths",
2395
+ "doc": "rff_none — control table with no required_filters",
2326
2396
  "name": "rff_none_scan",
2327
2397
  "type": "table"
2328
2398
  },
@@ -2334,16 +2404,35 @@
2334
2404
  },
2335
2405
  {
2336
2406
  "args": [],
2337
- "doc": "rff_simple — flat columns (a, b) for required_field_filter_paths tests",
2407
+ "doc": "rff_simple — flat columns (a, b) for required_filters tests",
2338
2408
  "name": "rff_simple_scan",
2339
2409
  "type": "table"
2340
2410
  },
2341
2411
  {
2342
2412
  "args": [],
2343
- "doc": "rff_struct — STRUCT(s.a, s.b) + other for required_field_filter_paths tests",
2413
+ "doc": "rff_struct — STRUCT(s.a, s.b) + other for required_filters tests",
2344
2414
  "name": "rff_struct_scan",
2345
2415
  "type": "table"
2346
2416
  },
2417
+ {
2418
+ "args": [
2419
+ {
2420
+ "desc": "Numeric input columns",
2421
+ "name": "values",
2422
+ "type": "double"
2423
+ },
2424
+ {
2425
+ "default": "false",
2426
+ "desc": "Sum absolute values",
2427
+ "name": "absolute",
2428
+ "named": true,
2429
+ "type": "bool"
2430
+ }
2431
+ ],
2432
+ "doc": "Blended per-row varargs sum",
2433
+ "name": "row_sum",
2434
+ "type": "table"
2435
+ },
2347
2436
  {
2348
2437
  "args": [
2349
2438
  {
@@ -2938,6 +3027,12 @@
2938
3027
  "name": "slow_cancellable_buffering",
2939
3028
  "type": "table_in_out"
2940
3029
  },
3030
+ {
3031
+ "args": [],
3032
+ "doc": "Per-substream partial sum emitted at finalize (parallel streaming finalize)",
3033
+ "name": "substream_partial_sum",
3034
+ "type": "table_in_out"
3035
+ },
2941
3036
  {
2942
3037
  "args": [
2943
3038
  {
@@ -2954,7 +3049,7 @@
2954
3049
  },
2955
3050
  {
2956
3051
  "args": [],
2957
- "doc": "Distributed sum using simple callback API",
3052
+ "doc": "Distributed sum using the buffered (Sink+Combine+Source) model",
2958
3053
  "name": "sum_all_columns_simple_distributed",
2959
3054
  "type": "table_in_out"
2960
3055
  }
@@ -0,0 +1,70 @@
1
+ # Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+
3
+ """Tests for SubstreamPartialSumFunction (parallel streaming finalize, A4).
4
+
5
+ At the single-worker client level there is exactly one substream, so ``finish()``
6
+ sees all input and emits one partial row equal to the whole sum. The per-substream
7
+ fan-out (many partials re-aggregated by an outer ``SELECT sum()``) is exercised by
8
+ the C++ integration test ``table_in_out/parallel_finalize.test`` over both
9
+ transports; here we assert the worker-side transform/finish contract in isolation.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import pyarrow as pa
15
+
16
+ from tests.conftest import filter_non_empty, make_schema
17
+ from vgi.client import Client
18
+
19
+
20
+ class TestSubstreamPartialSumFunction:
21
+ """Worker-side contract for substream_partial_sum."""
22
+
23
+ def test_partial_sum_single_batch(self, fixture_worker: str) -> None:
24
+ """One substream (single client worker) → one partial = the whole sum."""
25
+ schema = make_schema([pa.field("n", pa.int64())])
26
+ batch = pa.RecordBatch.from_pydict({"n": [1, 2, 3, 4, 5]}, schema=schema)
27
+ with Client(fixture_worker) as client:
28
+ output = list(
29
+ client.table_in_out_function(
30
+ function_name="substream_partial_sum",
31
+ input=iter([batch]),
32
+ )
33
+ )
34
+ non_empty = filter_non_empty(output)
35
+ assert len(non_empty) == 1
36
+ assert non_empty[0].to_pydict() == {"n": [15]}
37
+
38
+ def test_partial_sum_many_batches(self, fixture_worker: str) -> None:
39
+ """Accumulates across many input batches, emits one partial at finalize."""
40
+ schema = make_schema([pa.field("n", pa.int64())])
41
+ batches = [
42
+ pa.RecordBatch.from_pydict({"n": list(range(i * 100, (i + 1) * 100))}, schema=schema) for i in range(20)
43
+ ]
44
+ expected = sum(range(0, 2000))
45
+ with Client(fixture_worker) as client:
46
+ output = list(
47
+ client.table_in_out_function(
48
+ function_name="substream_partial_sum",
49
+ input=iter(batches),
50
+ )
51
+ )
52
+ non_empty = filter_non_empty(output)
53
+ assert len(non_empty) == 1
54
+ assert non_empty[0].to_pydict() == {"n": [expected]}
55
+
56
+ def test_partial_sum_empty_input(self, fixture_worker: str) -> None:
57
+ """No input rows → the single substream's partial is 0."""
58
+ schema = make_schema([pa.field("n", pa.int64())])
59
+ empty = pa.RecordBatch.from_pydict({"n": []}, schema=schema)
60
+ with Client(fixture_worker) as client:
61
+ output = list(
62
+ client.table_in_out_function(
63
+ function_name="substream_partial_sum",
64
+ input=iter([empty]),
65
+ )
66
+ )
67
+ non_empty = filter_non_empty(output)
68
+ # A single substream with zero rows still finalizes → one 0 partial.
69
+ assert len(non_empty) == 1
70
+ assert non_empty[0].to_pydict() == {"n": [0]}