vgi-python 0.31.2__tar.gz → 0.31.3__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 (458) hide show
  1. vgi_python-0.31.3/.dockerignore +15 -0
  2. vgi_python-0.31.3/.github/workflows/tailnet-integration.yml +49 -0
  3. {vgi_python-0.31.2 → vgi_python-0.31.3}/PKG-INFO +4 -2
  4. {vgi_python-0.31.2 → vgi_python-0.31.3}/ci/run-integration.sh +15 -0
  5. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/how-to/http-auth.md +19 -0
  6. vgi_python-0.31.3/docs/how-to/iroh.md +91 -0
  7. vgi_python-0.31.3/docs/how-to/tailnet.md +195 -0
  8. {vgi_python-0.31.2 → vgi_python-0.31.3}/mkdocs.yml +2 -0
  9. {vgi_python-0.31.2 → vgi_python-0.31.3}/pyproject.toml +3 -2
  10. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_example_worker_catalog.py +32 -0
  11. vgi_python-0.31.3/tests/client/test_bind_and_join_keys.py +134 -0
  12. vgi_python-0.31.3/tests/client/test_http_response_budget.py +109 -0
  13. vgi_python-0.31.3/tests/client/test_iroh_client.py +66 -0
  14. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_directory_parity.py +9 -0
  15. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/test_wide_columns.py +1 -1
  16. vgi_python-0.31.3/tests/tailnet/Dockerfile +13 -0
  17. vgi_python-0.31.3/tests/tailnet/__init__.py +1 -0
  18. vgi_python-0.31.3/tests/tailnet/compose.yaml +182 -0
  19. vgi_python-0.31.3/tests/tailnet/probe.py +113 -0
  20. vgi_python-0.31.3/tests/tailnet/run.sh +108 -0
  21. vgi_python-0.31.3/tests/tailnet/test_worker.py +64 -0
  22. vgi_python-0.31.3/tests/tailnet/worker.py +198 -0
  23. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_call_state_attach.py +2 -2
  24. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_filter_pushdown.py +69 -1
  25. vgi_python-0.31.3/tests/test_function_shape_mismatch.py +84 -0
  26. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_launch_transport.py +3 -0
  27. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_serve.py +165 -1
  28. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_tcp_transport.py +75 -0
  29. {vgi_python-0.31.2 → vgi_python-0.31.3}/uv.lock +25 -6
  30. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/catalog_mixin.py +64 -9
  31. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/client.py +335 -20
  32. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/serve.py +155 -2
  33. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/table_filter_pushdown.py +20 -2
  34. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/table_in_out_function.py +12 -1
  35. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/worker.py +192 -10
  36. {vgi_python-0.31.2 → vgi_python-0.31.3}/.gitattributes +0 -0
  37. {vgi_python-0.31.2 → vgi_python-0.31.3}/.github/dependabot.yml +0 -0
  38. {vgi_python-0.31.2 → vgi_python-0.31.3}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
  39. {vgi_python-0.31.2 → vgi_python-0.31.3}/.github/workflows/ci.yml +0 -0
  40. {vgi_python-0.31.2 → vgi_python-0.31.3}/.github/workflows/docs.yml +0 -0
  41. {vgi_python-0.31.2 → vgi_python-0.31.3}/.github/workflows/integration.yml +0 -0
  42. {vgi_python-0.31.2 → vgi_python-0.31.3}/.github/workflows/release.yml +0 -0
  43. {vgi_python-0.31.2 → vgi_python-0.31.3}/.gitignore +0 -0
  44. {vgi_python-0.31.2 → vgi_python-0.31.3}/.python-version +0 -0
  45. {vgi_python-0.31.2 → vgi_python-0.31.3}/.vale.ini +0 -0
  46. {vgi_python-0.31.2 → vgi_python-0.31.3}/CLAUDE.md +0 -0
  47. {vgi_python-0.31.2 → vgi_python-0.31.3}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
  48. {vgi_python-0.31.2 → vgi_python-0.31.3}/DOCS_REVIEW_RUBRIC.md +0 -0
  49. {vgi_python-0.31.2 → vgi_python-0.31.3}/DOCS_USABILITY_TEST.md +0 -0
  50. {vgi_python-0.31.2 → vgi_python-0.31.3}/LICENSE +0 -0
  51. {vgi_python-0.31.2 → vgi_python-0.31.3}/README.md +0 -0
  52. {vgi_python-0.31.2 → vgi_python-0.31.3}/RELEASE-RUNBOOK.md +0 -0
  53. {vgi_python-0.31.2 → vgi_python-0.31.3}/SECURITY.md +0 -0
  54. {vgi_python-0.31.2 → vgi_python-0.31.3}/ci/README.md +0 -0
  55. {vgi_python-0.31.2 → vgi_python-0.31.3}/ci/preprocess-require.awk +0 -0
  56. {vgi_python-0.31.2 → vgi_python-0.31.3}/dist-vgi/.gitignore +0 -0
  57. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/aggregate-functions.md +0 -0
  58. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/arguments.md +0 -0
  59. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/auth.md +0 -0
  60. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/catalogs.md +0 -0
  61. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/client.md +0 -0
  62. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/exceptions.md +0 -0
  63. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/filters.md +0 -0
  64. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/functions.md +0 -0
  65. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/http.md +0 -0
  66. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/index.md +0 -0
  67. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/metadata.md +0 -0
  68. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/observability.md +0 -0
  69. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/storage.md +0 -0
  70. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/transactor.md +0 -0
  71. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/api/worker.md +0 -0
  72. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/argument-serialization.md +0 -0
  73. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/apple-touch-icon.png +0 -0
  74. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/favicon-16x16.png +0 -0
  75. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/favicon-32x32.png +0 -0
  76. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/favicon.ico +0 -0
  77. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/kinds/aggregate.svg +0 -0
  78. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/kinds/buffering.svg +0 -0
  79. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/kinds/scalar.svg +0 -0
  80. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/kinds/table-in-out.svg +0 -0
  81. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/kinds/table.svg +0 -0
  82. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/logo.png +0 -0
  83. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/assets/social-card.png +0 -0
  84. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/authentication.md +0 -0
  85. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/catalog-interface.md +0 -0
  86. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/cli.md +0 -0
  87. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/column-statistics.md +0 -0
  88. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/concepts/index.md +0 -0
  89. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/contributing-docs.md +0 -0
  90. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/filter-pushdown.md +0 -0
  91. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/generator-api.md +0 -0
  92. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/global-functions.md +0 -0
  93. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/how-to/catalogs.md +0 -0
  94. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/how-to/function-patterns.md +0 -0
  95. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/how-to/index.md +0 -0
  96. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/how-to/pushdown-and-statistics.md +0 -0
  97. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/how-to/state-storage.md +0 -0
  98. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/index.md +0 -0
  99. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/lifecycle.md +0 -0
  100. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/metadata.md +0 -0
  101. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/overrides/main.html +0 -0
  102. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/robots.txt +0 -0
  103. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/shared-storage.md +0 -0
  104. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/stylesheets/extra.css +0 -0
  105. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/tutorial/index.md +0 -0
  106. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/tutorial/scalar.md +0 -0
  107. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/tutorial/table.md +0 -0
  108. {vgi_python-0.31.2 → vgi_python-0.31.3}/docs/vgi-logo.png +0 -0
  109. {vgi_python-0.31.2 → vgi_python-0.31.3}/examples/batch_index_worker.py +0 -0
  110. {vgi_python-0.31.2 → vgi_python-0.31.3}/examples/cache_worker.py +0 -0
  111. {vgi_python-0.31.2 → vgi_python-0.31.3}/examples/calc_scalar_worker.py +0 -0
  112. {vgi_python-0.31.2 → vgi_python-0.31.3}/examples/calc_worker.py +0 -0
  113. {vgi_python-0.31.2 → vgi_python-0.31.3}/examples/copy_format_worker.py +0 -0
  114. {vgi_python-0.31.2 → vgi_python-0.31.3}/examples/filter_worker.py +0 -0
  115. {vgi_python-0.31.2 → vgi_python-0.31.3}/examples/greeting_scalar_worker.py +0 -0
  116. {vgi_python-0.31.2 → vgi_python-0.31.3}/examples/row_count_worker.py +0 -0
  117. {vgi_python-0.31.2 → vgi_python-0.31.3}/examples/series_streaming_worker.py +0 -0
  118. {vgi_python-0.31.2 → vgi_python-0.31.3}/examples/sum_worker.py +0 -0
  119. {vgi_python-0.31.2 → vgi_python-0.31.3}/packages/vgi-fixtures/LICENSE +0 -0
  120. {vgi_python-0.31.2 → vgi_python-0.31.3}/packages/vgi-fixtures/README.md +0 -0
  121. {vgi_python-0.31.2 → vgi_python-0.31.3}/packages/vgi-fixtures/docker/Dockerfile +0 -0
  122. {vgi_python-0.31.2 → vgi_python-0.31.3}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
  123. {vgi_python-0.31.2 → vgi_python-0.31.3}/packages/vgi-fixtures/pyproject.toml +0 -0
  124. {vgi_python-0.31.2 → vgi_python-0.31.3}/scripts/measure_startup.py +0 -0
  125. {vgi_python-0.31.2 → vgi_python-0.31.3}/scripts/run_all_tests.sh +0 -0
  126. {vgi_python-0.31.2 → vgi_python-0.31.3}/test-data/generate.sh +0 -0
  127. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/__init__.py +0 -0
  128. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/_http_fixtures.py +0 -0
  129. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/__init__.py +0 -0
  130. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_attach_option.py +0 -0
  131. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_catalog_interface.py +0 -0
  132. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_client_catalog.py +0 -0
  133. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_column_statistics.py +0 -0
  134. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_declarative.py +0 -0
  135. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_integration.py +0 -0
  136. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_required_filters.py +0 -0
  137. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_scan_branches.py +0 -0
  138. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_serialization.py +0 -0
  139. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_setting.py +0 -0
  140. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_storage.py +0 -0
  141. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_time_travel.py +0 -0
  142. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/catalog/test_writable_table.py +0 -0
  143. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/__init__.py +0 -0
  144. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_aggregate_client.py +0 -0
  145. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_batch_metadata_callback.py +0 -0
  146. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_broken_pipe.py +0 -0
  147. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_cli.py +0 -0
  148. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_cli_catalog_functions.py +0 -0
  149. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_copy_client.py +0 -0
  150. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_scan_branches_client.py +0 -0
  151. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_split_client.py +0 -0
  152. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_stop_force.py +0 -0
  153. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_table_column_statistics.py +0 -0
  154. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_time_travel_client.py +0 -0
  155. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/client/test_worker_debug.py +0 -0
  156. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/__init__.py +0 -0
  157. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/_stub.py +0 -0
  158. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/conftest.py +0 -0
  159. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_accumulate.py +0 -0
  160. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_aggregate.py +0 -0
  161. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_attach.py +0 -0
  162. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_bearer_auth.py +0 -0
  163. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_const_param_enforcement.py +0 -0
  164. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_container.py +0 -0
  165. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_copy_from.py +0 -0
  166. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_copy_to.py +0 -0
  167. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_function_inventory.py +0 -0
  168. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_github.py +0 -0
  169. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_http_client.py +0 -0
  170. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_http_external_location.py +0 -0
  171. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_http_upload_url.py +0 -0
  172. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_macro.py +0 -0
  173. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_overload.py +0 -0
  174. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_protocol_inventory.py +0 -0
  175. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_protocol_version.py +0 -0
  176. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_resumable_scan.py +0 -0
  177. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_scalar.py +0 -0
  178. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
  179. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_secret.py +0 -0
  180. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_settings.py +0 -0
  181. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_splits.py +0 -0
  182. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_table.py +0 -0
  183. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_table_in_out.py +0 -0
  184. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_view.py +0 -0
  185. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conformance/test_writable.py +0 -0
  186. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/conftest.py +0 -0
  187. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/anchor_len_overrun.bin +0 -0
  188. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/bad_fingerprint.bin +0 -0
  189. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/bad_flags_unsealed_but_key_present.bin +0 -0
  190. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/bad_version.bin +0 -0
  191. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/generate.py +0 -0
  192. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/manifest.json +0 -0
  193. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/reserved_flag_bit.bin +0 -0
  194. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/stale_anchor.bin +0 -0
  195. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/truncated.bin +0 -0
  196. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/valid_sealed.bin +0 -0
  197. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/data/split_tokens/valid_unsealed.bin +0 -0
  198. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/scalar/__init__.py +0 -0
  199. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/scalar/test_bernoulli_function.py +0 -0
  200. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/scalar/test_binary_packet_function.py +0 -0
  201. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/scalar/test_client.py +0 -0
  202. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/scalar/test_conditional_message_function.py +0 -0
  203. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/scalar/test_hash_seed_function.py +0 -0
  204. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/scalar/test_multiply_by_setting_function.py +0 -0
  205. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/scalar/test_multiply_function.py +0 -0
  206. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/scalar/test_random_bytes_function.py +0 -0
  207. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/scalar/test_return_secret_value_function.py +0 -0
  208. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/__init__.py +0 -0
  209. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/__init__.py +0 -0
  210. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_constant_columns_function.py +0 -0
  211. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_double_sequence_function.py +0 -0
  212. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_exception_function.py +0 -0
  213. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_filter_echo_function.py +0 -0
  214. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_logging_function.py +0 -0
  215. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_nested_sequence_function.py +0 -0
  216. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_partitioned_function.py +0 -0
  217. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_projected_data_function.py +0 -0
  218. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_sequence_function.py +0 -0
  219. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_settings_function.py +0 -0
  220. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_struct_settings_function.py +0 -0
  221. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table/generator/test_ten_thousand_function.py +0 -0
  222. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/__init__.py +0 -0
  223. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/generator/__init__.py +0 -0
  224. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
  225. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/generator/test_echo_function.py +0 -0
  226. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/generator/test_exception_functions.py +0 -0
  227. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
  228. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
  229. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
  230. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
  231. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/test_blended_exchange.py +0 -0
  232. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/test_blended_metadata.py +0 -0
  233. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/table_in_out/test_client.py +0 -0
  234. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_access_log_audit.py +0 -0
  235. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_aggregate_function.py +0 -0
  236. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_argument_spec.py +0 -0
  237. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_auth.py +0 -0
  238. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_bind_exceptions.py +0 -0
  239. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_bind_request_at_clause.py +0 -0
  240. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_bound_storage_conformance.py +0 -0
  241. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_cache_control.py +0 -0
  242. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_catalog_auth_binding.py +0 -0
  243. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_client_oauth.py +0 -0
  244. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_copy_from_function.py +0 -0
  245. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_copy_to_function.py +0 -0
  246. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_docstrings.py +0 -0
  247. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_documentation_examples.py +0 -0
  248. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_example_function_arg_types.py +0 -0
  249. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_examples_workers.py +0 -0
  250. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_exception_handling.py +0 -0
  251. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_exceptions.py +0 -0
  252. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_filter_pushdown_extension.py +0 -0
  253. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_fixture_catalog_parity.py +0 -0
  254. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_function_storage.py +0 -0
  255. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_function_storage_azure_sql.py +0 -0
  256. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_function_storage_cf_do.py +0 -0
  257. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_function_storage_cf_do_integration.py +0 -0
  258. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_function_storage_conformance.py +0 -0
  259. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_cpp_constants.py +0 -0
  260. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_cpp_protocol_version.py +0 -0
  261. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_cpp_request_builders.py +0 -0
  262. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_cpp_schemas.py +0 -0
  263. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_cpp_secret.py +0 -0
  264. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_go_schemas.py +0 -0
  265. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_java_schemas.py +0 -0
  266. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_protocol_version.py +0 -0
  267. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_rust_request_builders.py +0 -0
  268. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_rust_schemas.py +0 -0
  269. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_schemas_cross_lang.py +0 -0
  270. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_ts_client.py +0 -0
  271. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_generated_ts_schemas.py +0 -0
  272. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_http_demo_storage.py +0 -0
  273. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_http_s3_offload_input.py +0 -0
  274. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_http_s3_offload_output.py +0 -0
  275. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_metadata.py +0 -0
  276. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_mypy_consolidated.py +0 -0
  277. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_nest_tensor.py +0 -0
  278. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_otel.py +0 -0
  279. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_projection_enforcement.py +0 -0
  280. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_projection_repro.py +0 -0
  281. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_protocol_classes.py +0 -0
  282. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_resolved_secrets.py +0 -0
  283. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_schema_scoped_functions.py +0 -0
  284. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_schema_utils.py +0 -0
  285. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_setting_secret_annotations.py +0 -0
  286. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_signing_key.py +0 -0
  287. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_split_token.py +0 -0
  288. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_table_buffering_function.py +0 -0
  289. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_table_function_dynamic_to_string.py +0 -0
  290. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_type_bounds.py +0 -0
  291. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_union_argument.py +0 -0
  292. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_wire_record_schema_parity.py +0 -0
  293. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_worker.py +0 -0
  294. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_worker_cli.py +0 -0
  295. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/test_worker_page.py +0 -0
  296. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/transactor/__init__.py +0 -0
  297. {vgi_python-0.31.2 → vgi_python-0.31.3}/tests/transactor/test_transactor.py +0 -0
  298. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/__init__.py +0 -0
  299. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_duckdb.py +0 -0
  300. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_storage_profile.py +0 -0
  301. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/__init__.py +0 -0
  302. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
  303. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/accumulate/worker.py +0 -0
  304. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
  305. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/_common.py +0 -0
  306. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/basic.py +0 -0
  307. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
  308. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/generic.py +0 -0
  309. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
  310. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
  311. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/same_name.py +0 -0
  312. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
  313. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
  314. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
  315. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/aggregate/window.py +0 -0
  316. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/attach_options.py +0 -0
  317. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/bad_enum.py +0 -0
  318. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/bad_protocol.py +0 -0
  319. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/cancellable.py +0 -0
  320. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/catalog.py +0 -0
  321. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/companion.py +0 -0
  322. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/copy_from.py +0 -0
  323. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/copy_to.py +0 -0
  324. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/global_functions.py +0 -0
  325. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/http_server.py +0 -0
  326. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
  327. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
  328. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/nest_tensor.py +0 -0
  329. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/orchard_catalog.py +0 -0
  330. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
  331. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
  332. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/__init__.py +0 -0
  333. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/_common.py +0 -0
  334. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
  335. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
  336. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/binary.py +0 -0
  337. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/formatting.py +0 -0
  338. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/geo.py +0 -0
  339. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
  340. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
  341. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/same_name.py +0 -0
  342. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
  343. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/scalar/type_info.py +0 -0
  344. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
  345. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
  346. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/simple_writable.py +0 -0
  347. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/__init__.py +0 -0
  348. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/_common.py +0 -0
  349. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/batch_index.py +0 -0
  350. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
  351. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/cache.py +0 -0
  352. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
  353. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/filters.py +0 -0
  354. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/late_materialization.py +0 -0
  355. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/make_series.py +0 -0
  356. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/misc.py +0 -0
  357. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/order_modes.py +0 -0
  358. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/pairs.py +0 -0
  359. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/partition_columns.py +0 -0
  360. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
  361. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/profiling_example.py +0 -0
  362. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/required_filters.py +0 -0
  363. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/same_name_cached.py +0 -0
  364. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/same_name_schemas.py +0 -0
  365. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/sequence.py +0 -0
  366. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/settings.py +0 -0
  367. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/splits.py +0 -0
  368. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
  369. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
  370. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/typed_probe.py +0 -0
  371. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/versioned.py +0 -0
  372. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table/wide.py +0 -0
  373. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table_in_out.py +0 -0
  374. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/table_in_out_same_name.py +0 -0
  375. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/twin_catalogs.py +0 -0
  376. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/versioned.py +0 -0
  377. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/versioned_tables.py +0 -0
  378. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/worker.py +0 -0
  379. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/writable/__init__.py +0 -0
  380. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/writable/generic.py +0 -0
  381. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/writable/table.py +0 -0
  382. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/_test_fixtures/writable/worker.py +0 -0
  383. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/aggregate_function.py +0 -0
  384. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/argument_spec.py +0 -0
  385. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/arguments.py +0 -0
  386. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/attach_header.py +0 -0
  387. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/auth.py +0 -0
  388. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/cache_control.py +0 -0
  389. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/catalog/__init__.py +0 -0
  390. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/catalog/_descriptor_spec.py +0 -0
  391. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/catalog/attach_option.py +0 -0
  392. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/catalog/catalog_interface.py +0 -0
  393. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/catalog/descriptors.py +0 -0
  394. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/catalog/duckdb_statistics.py +0 -0
  395. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/catalog/secret_type.py +0 -0
  396. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/catalog/setting.py +0 -0
  397. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/catalog/storage.py +0 -0
  398. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/__init__.py +0 -0
  399. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/aggregate.py +0 -0
  400. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/cli.py +0 -0
  401. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/cli_catalog.py +0 -0
  402. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/cli_schema.py +0 -0
  403. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/cli_table.py +0 -0
  404. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/cli_transaction.py +0 -0
  405. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/cli_utils.py +0 -0
  406. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/cli_view.py +0 -0
  407. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/client/errors.py +0 -0
  408. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/__init__.py +0 -0
  409. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/_common.py +0 -0
  410. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/cpp_constants.py +0 -0
  411. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/cpp_protocol_version.py +0 -0
  412. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/cpp_request_builders.py +0 -0
  413. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/cpp_schemas.py +0 -0
  414. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
  415. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/cpp_secret_request_builders.py +0 -0
  416. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/cpp_secret_schemas.py +0 -0
  417. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/go_schemas.py +0 -0
  418. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/java_schemas.py +0 -0
  419. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/protocol_version.py +0 -0
  420. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/rust_request_builders.py +0 -0
  421. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/rust_schemas.py +0 -0
  422. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/ts_client.py +0 -0
  423. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/codegen/ts_schemas.py +0 -0
  424. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/copy_from_function.py +0 -0
  425. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/copy_to_function.py +0 -0
  426. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/exceptions.py +0 -0
  427. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/function.py +0 -0
  428. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/function_storage.py +0 -0
  429. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/function_storage_azure_sql.py +0 -0
  430. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/function_storage_cf_do.py +0 -0
  431. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/http/__init__.py +0 -0
  432. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/http/demo_storage.py +0 -0
  433. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/http/landing.html +0 -0
  434. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/http/landing_page.py +0 -0
  435. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/http/vgi-client.js +0 -0
  436. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/http/worker_page.py +0 -0
  437. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/invocation.py +0 -0
  438. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/logging_config.py +0 -0
  439. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/meta_worker.py +0 -0
  440. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/metadata.py +0 -0
  441. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/otel.py +0 -0
  442. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/profiling.py +0 -0
  443. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/protocol.py +0 -0
  444. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/protocol_version.txt +0 -0
  445. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/py.typed +0 -0
  446. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/scalar_function.py +0 -0
  447. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/schema_utils.py +0 -0
  448. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/secret_protocol.py +0 -0
  449. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/secret_service.py +0 -0
  450. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/split_token.py +0 -0
  451. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/table_buffering_function.py +0 -0
  452. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/table_function.py +0 -0
  453. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/transactor/__init__.py +0 -0
  454. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/transactor/_duckdb_compat.py +0 -0
  455. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/transactor/client.py +0 -0
  456. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/transactor/protocol.py +0 -0
  457. {vgi_python-0.31.2 → vgi_python-0.31.3}/vgi/transactor/server.py +0 -0
  458. {vgi_python-0.31.2 → vgi_python-0.31.3}/wrangler.jsonc +0 -0
@@ -0,0 +1,15 @@
1
+ .git
2
+ .venv
3
+ .mypy_cache
4
+ .pytest_cache
5
+ .ruff_cache
6
+ site
7
+ __pycache__
8
+ *.pyc
9
+ .tailnet/vgi-rpc/.git
10
+ .env
11
+ .env.*
12
+ *.key
13
+ *.pem
14
+ *.p12
15
+ *.pfx
@@ -0,0 +1,49 @@
1
+ name: Real Tailnet authentication
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ tailscale_channel:
7
+ description: Official Tailscale container channel
8
+ required: true
9
+ default: stable
10
+ type: choice
11
+ options: [stable, unstable]
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ concurrency:
17
+ group: vgi-python-real-tailnet
18
+ cancel-in-progress: false
19
+
20
+ jobs:
21
+ tailnet:
22
+ name: high-level worker / ${{ inputs.tailscale_channel }}
23
+ runs-on: ubuntu-24.04
24
+ timeout-minutes: 30
25
+ environment: tailnet-integration
26
+ env:
27
+ TAILSCALE_IMAGE: tailscale/tailscale:${{ inputs.tailscale_channel }}
28
+ TS_OAUTH_CLIENT_ID: ${{ secrets.TS_OAUTH_CLIENT_ID }}
29
+ TS_OAUTH_SECRET: ${{ secrets.TS_OAUTH_SECRET }}
30
+ TAILNET_ISSUER: ${{ vars.TAILNET_ISSUER }}
31
+ TAILNET_EXPECTED_CAPABILITY: ${{ vars.TAILNET_EXPECTED_CAPABILITY }}
32
+ TAILNET_EXPECTED_CLIENT_TAG: ${{ vars.TAILNET_EXPECTED_CLIENT_TAG }}
33
+ steps:
34
+ - uses: actions/checkout@v7
35
+
36
+ - name: Checkout unreleased vgi-rpc identity layer
37
+ uses: actions/checkout@v7
38
+ with:
39
+ repository: Query-farm/vgi-rpc-python
40
+ path: .tailnet/vgi-rpc
41
+ ref: 04f19147625b4f643b38a687151d8233daa8997c
42
+
43
+ - name: Check kernel networking support
44
+ run: |
45
+ test -c /dev/net/tun
46
+ docker compose version
47
+
48
+ - name: Run high-level Tailnet authentication matrix
49
+ run: tests/tailnet/run.sh
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vgi-python
3
- Version: 0.31.2
3
+ Version: 0.31.3
4
4
  Summary: Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow
5
5
  Project-URL: Homepage, https://query.farm
6
6
  Project-URL: Repository, https://github.com/Query-farm/vgi-python
@@ -162,7 +162,7 @@ Requires-Dist: httpx2>=2.9.1
162
162
  Requires-Dist: platformdirs
163
163
  Requires-Dist: pyarrow
164
164
  Requires-Dist: typer>=0.9
165
- Requires-Dist: vgi-rpc>=0.44.1
165
+ Requires-Dist: vgi-rpc>=0.45.1
166
166
  Provides-Extra: azure
167
167
  Requires-Dist: azure-identity>=1.16.0; extra == 'azure'
168
168
  Requires-Dist: pymssql>=2.3.0; extra == 'azure'
@@ -198,6 +198,8 @@ Provides-Extra: haybarn
198
198
  Requires-Dist: haybarn>=1.5.5rc1; extra == 'haybarn'
199
199
  Provides-Extra: http
200
200
  Requires-Dist: vgi-rpc[http]; extra == 'http'
201
+ Provides-Extra: iroh
202
+ Requires-Dist: vgi-rpc[http,iroh]; extra == 'iroh'
201
203
  Provides-Extra: launch
202
204
  Requires-Dist: vgi-rpc[cli]; extra == 'launch'
203
205
  Provides-Extra: oauth
@@ -86,6 +86,10 @@ if [ "$TRANSPORT" = "http" ]; then
86
86
  # * buffer_input/sizes.test — input buffering semantics differ over HTTP
87
87
  # (same known limitation). (scale.test_slow is a .test_slow file and is
88
88
  # never staged below, which only finds *.test.)
89
+ # * database_worker/package.test — the packaged fixture is deliberately an
90
+ # executable wrapper around VGI_TEST_WORKER. An HTTP URL is not an
91
+ # executable artifact; the direct-exec lifecycle is covered by all three
92
+ # process/socket lanes.
89
93
  # cache/revalidate.test now runs on http too: HTTP conditional revalidation is
90
94
  # implemented (C++ /init-request validators + vgi-rpc >=0.24.0 surfacing them to
91
95
  # the producer's first process()). It needs a community vgi extension carrying
@@ -96,6 +100,7 @@ if [ "$TRANSPORT" = "http" ]; then
96
100
  -not -name 'dynamic_filter.test'
97
101
  -not -name 'partitioned_sequence.test'
98
102
  -not -path './table_in_out/buffer_input/sizes.test'
103
+ -not -path './database_worker/package.test'
99
104
  )
100
105
  fi
101
106
 
@@ -120,6 +125,16 @@ mkdir -p "$STAGE/test/sql/integration"
120
125
  awk -v http="$AWK_HTTP" -f "$HERE/preprocess-require.awk" "$f" > "$STAGE/test/sql/integration/$f"
121
126
  done )
122
127
 
128
+ # Some integration cases package an executable from the upstream support tree.
129
+ # The staged suite runs with ``$STAGE`` as its working directory, so preserve
130
+ # the relative path used by those SQL fixtures instead of making it depend on
131
+ # the checkout layout outside the stage.
132
+ if [ -f "$VGI_SRC/test/support/database_worker_fixture.sh" ]; then
133
+ mkdir -p "$STAGE/test/support"
134
+ cp "$VGI_SRC/test/support/database_worker_fixture.sh" "$STAGE/test/support/"
135
+ chmod +x "$STAGE/test/support/database_worker_fixture.sh"
136
+ fi
137
+
123
138
  # Empty VGI_RPC_SHM_SIZE_BYTES must not reach the C++ client (it would try to
124
139
  # attach a zero-size segment); only a real value enables the shm side channel.
125
140
  [ -n "${VGI_RPC_SHM_SIZE_BYTES:-}" ] || unset VGI_RPC_SHM_SIZE_BYTES
@@ -35,6 +35,25 @@ with Client(transport="http", base_url="http://localhost:8080", bearer_token="to
35
35
  ... # same .scalar_function() / .table_function() calls as the subprocess transport
36
36
  ```
37
37
 
38
+ The native client advertises a 256 MiB decoded-response limit on every RPC,
39
+ including the short-lived connections used for catalog operations. Override it
40
+ when the application has a smaller memory budget:
41
+
42
+ ```python
43
+ with Client.from_http(
44
+ "http://localhost:8080",
45
+ accepted_max_response_bytes=32 * 1024 * 1024,
46
+ ) as client:
47
+ ...
48
+ ```
49
+
50
+ The value must be an integer from 65536 through `2^53 - 1`. Configured clients
51
+ discover server support before the first RPC and fail closed if it is absent.
52
+ Passing `None` omits negotiation for compatibility with a legacy unbounded server;
53
+ doing so is not recommended for externally reachable workers. Capability
54
+ discovery reports the server's own effective response cap and whether it
55
+ honors the client limit.
56
+
38
57
  ## Add authentication
39
58
 
40
59
  The quickest setup is static bearer tokens via an environment variable — comma-separated
@@ -0,0 +1,91 @@
1
+ # Serve and call a worker over Iroh
2
+
3
+ VGI supports two authenticated Iroh endpoint forms:
4
+
5
+ - `iroh://<endpoint-id>` keeps stream state on one connection.
6
+ - `httpi://<endpoint-id>[/prefix]` preserves VGI's stateless HTTP exchanges,
7
+ continuation tokens, request/response budgets, authentication headers, and
8
+ externalized batches.
9
+
10
+ Install the optional native client binding:
11
+
12
+ ```console
13
+ pip install 'vgi-python[iroh]'
14
+ ```
15
+
16
+ ## Start a raw upstream
17
+
18
+ The language-neutral `vgi-iroh-bridge` owns the public Iroh endpoint. The
19
+ Python process binds only loopback and requires the bridge's PROXY-v2 identity
20
+ preamble:
21
+
22
+ ```console
23
+ vgi-serve my_worker.py \
24
+ --iroh-raw-upstream 127.0.0.1:9400 \
25
+ --iroh-issuer production
26
+ ```
27
+
28
+ In another process:
29
+
30
+ ```console
31
+ vgi-iroh-bridge \
32
+ --secret-key-file /run/secrets/vgi-iroh-key \
33
+ --raw-upstream tcp://127.0.0.1:9400
34
+ ```
35
+
36
+ The bridge prints its EndpointId as the first stdout line. Development may use
37
+ `--ephemeral`; a production endpoint must use a persistent secret key.
38
+ The raw Iroh listener also enables standard RPC method description so
39
+ standalone clients in other VGI languages can bootstrap directly.
40
+
41
+ ## Start an HTTP upstream
42
+
43
+ HTTP-over-Iroh is the preferred mode for horizontally scaled workers:
44
+
45
+ ```console
46
+ vgi-serve my_worker.py \
47
+ --http --host 127.0.0.1 --port 9401 \
48
+ --iroh-issuer production
49
+
50
+ vgi-iroh-bridge \
51
+ --secret-key-file /run/secrets/vgi-iroh-key \
52
+ --http-upstream http://127.0.0.1:9401
53
+ ```
54
+
55
+ `--iroh-issuer` makes the worker trust exactly `127.0.0.1` by default and
56
+ promotes the bridge-verified EndpointId to `CallContext.auth`. Use repeated
57
+ `--iroh-trusted-proxy <exact-ip>` when the immediate bridge address differs.
58
+ Use `--iroh-observe` to expose evidence without authenticating from it.
59
+
60
+ Never expose the bridge-facing listener as a general public HTTP or TCP
61
+ listener. If another proxy sits between the bridge and worker, isolate that
62
+ route, clear client-supplied `VGI-Forwarded-Iroh-Endpoint` fields on every other
63
+ route, and configure the worker to trust only the intermediary's exact address.
64
+
65
+ ## Connect
66
+
67
+ The same high-level client accepts both endpoint schemes:
68
+
69
+ ```python test="skip"
70
+ from vgi.client import Client
71
+
72
+ with Client.from_iroh("iroh://<endpoint-id>") as client:
73
+ print(client.catalogs())
74
+
75
+ with Client.from_iroh("httpi://<endpoint-id>/vgi") as client:
76
+ print(client.catalogs())
77
+ ```
78
+
79
+ For a private relay:
80
+
81
+ ```python test="skip"
82
+ client = Client.from_iroh(
83
+ "httpi://<endpoint-id>/vgi",
84
+ relay_urls=["https://relay.example.com"],
85
+ remote_relay_url="https://relay.example.com",
86
+ )
87
+ ```
88
+
89
+ The local relay set and the remote relay hint are distinct. `no_relay=True`
90
+ disables relays and requires a working direct path; it never falls back to the
91
+ public relay network.
@@ -0,0 +1,195 @@
1
+ ---
2
+ description: "How to connect the Python VGI client to workers through Tailscale over HTTPS, direct TCP, or userspace SOCKS5h."
3
+ ---
4
+
5
+ # Run VGI through a Tailnet
6
+
7
+ The VGI client uses ordinary HTTPS and TCP endpoints. It does not join a
8
+ Tailnet, manage Tailscale credentials, or introduce a `tailscale://` URL
9
+ scheme. Run Tailscale on the client host or in a sidecar and pass the worker's
10
+ MagicDNS name to the normal client factory.
11
+
12
+ Connectivity and authentication are separate. A client can reach a worker
13
+ through a Tailnet without the worker using Tailnet identity. To authorize from
14
+ the caller's verified Tailscale user, node, tags, or application capabilities,
15
+ configure a peer-identity provider and an authentication policy on the worker.
16
+
17
+ ## Authenticate an HTTP worker
18
+
19
+ When Tailscale Serve terminates HTTPS, trust its identity headers only from the
20
+ exact local proxy addresses that can reach the application. `require_peer_identity`
21
+ requires verified evidence but deliberately does not invent a stable principal
22
+ for capability-only or login-scoped Serve requests:
23
+
24
+ ```python test="lint"
25
+ from vgi_rpc.http import tailscale_serve_header_provider
26
+ from vgi_rpc.rpc import require_peer_identity
27
+
28
+ from my_worker import MyWorker
29
+ from vgi.serve import create_app
30
+
31
+ tailscale = tailscale_serve_header_provider(
32
+ issuer="tailnet:example",
33
+ trusted_proxy_addresses=("127.0.0.1", "::1"),
34
+ )
35
+
36
+ app = create_app(
37
+ MyWorker,
38
+ peer_identity_providers=(tailscale,),
39
+ peer_authentication_policy=require_peer_identity("tailscale"),
40
+ )
41
+ ```
42
+
43
+ Serve login names have `subject_stability="login"`; tagged clients may be
44
+ capability-only and have no subject at all. Consequently, the built-in
45
+ `peer_identity_primary("tailscale")` policy is intentionally unsuitable for
46
+ Serve. If a deployment elects to authenticate from a Serve login, it must use
47
+ an explicit application policy that accepts that weaker identity and scopes it
48
+ to the configured issuer and trusted proxy boundary.
49
+
50
+ For direct HTTP over a Tailnet, LocalAPI WhoIs can produce a stable user or
51
+ node subject and therefore can be the primary authenticator:
52
+
53
+ ```python test="lint"
54
+ from vgi_rpc.http import tailscale_localapi_provider
55
+ from vgi_rpc.rpc import peer_identity_primary
56
+
57
+ from my_worker import MyWorker
58
+ from vgi.serve import create_app
59
+
60
+ tailscale = tailscale_localapi_provider(issuer="tailnet:example")
61
+ app = create_app(
62
+ MyWorker,
63
+ peer_identity_providers=(tailscale,),
64
+ peer_authentication_policy=peer_identity_primary("tailscale"),
65
+ )
66
+ ```
67
+
68
+ `create_app()` remains a standard WSGI application, so the same configuration
69
+ works behind Waitress, Granian, gunicorn, Envoy, nginx, or a cloud HTTP load
70
+ balancer. The provider determines which proxy evidence is trusted; merely
71
+ placing the worker behind a proxy never makes forwarded identity trustworthy.
72
+
73
+ Trusted-proxy validation must see the physical socket peer. Do not install
74
+ `ProxyFix`, forwarded-address middleware, or an equivalent rewrite ahead of
75
+ VGI's identity middleware: attacker-controlled `Forwarded` or
76
+ `X-Forwarded-For` values must never become `REMOTE_ADDR` before the exact proxy
77
+ allowlist is checked. Keep the backend unreachable except through those
78
+ allowlisted proxies.
79
+
80
+ ## HTTPS through Tailscale Serve
81
+
82
+ On a host using Tailscale's normal network interface, connect to a worker
83
+ published with Tailscale Serve exactly like any other HTTPS worker:
84
+
85
+ ```python test="lint"
86
+ from vgi.client import Client
87
+
88
+ with Client.from_http("https://worker.example-tailnet.ts.net") as client:
89
+ catalogs = client.catalogs()
90
+ ```
91
+
92
+ Tailscale terminates HTTPS and adds its verified Serve identity and application
93
+ capability headers. The VGI server validates those headers against its trusted
94
+ proxy configuration; the client neither constructs nor trusts identity
95
+ headers. Do not send `Tailscale-*` headers from application code.
96
+
97
+ For userspace networking, inject an HTTP client configured for Tailscale's
98
+ SOCKS5 server. Install SOCKS support with `pip install 'httpx2[socks]'`:
99
+
100
+ ```python test="lint"
101
+ import httpx2
102
+
103
+ from vgi.client import Client
104
+
105
+ url = "https://worker.example-tailnet.ts.net"
106
+ with httpx2.Client(
107
+ base_url=url,
108
+ proxy="socks5h://127.0.0.1:1055",
109
+ trust_env=False,
110
+ timeout=httpx2.Timeout(60.0, connect=15.0),
111
+ ) as http_client, Client.from_http(url, httpx_client=http_client) as client:
112
+ catalogs = client.catalogs()
113
+ ```
114
+
115
+ Using `socks5h` keeps MagicDNS resolution inside the Tailscale sidecar. A proxy
116
+ failure is an error; this configuration does not fall back to a direct route.
117
+
118
+ ## Raw TCP through a Tailnet
119
+
120
+ Start a high-level Worker with LocalAPI identity using `Worker.serve_tcp()`:
121
+
122
+ ```python test="lint"
123
+ from vgi_rpc.http import tailscale_localapi_provider
124
+ from vgi_rpc.rpc import peer_identity_primary
125
+
126
+ from my_worker import MyWorker
127
+
128
+ tailscale = tailscale_localapi_provider(issuer="tailnet:example")
129
+ MyWorker().serve_tcp(
130
+ "0.0.0.0",
131
+ 9400,
132
+ threaded=True,
133
+ max_connections=64,
134
+ peer_identity_providers=(tailscale,),
135
+ peer_authentication_policy=peer_identity_primary("tailscale"),
136
+ )
137
+ ```
138
+
139
+ The connection's verified stable Tailnet subject becomes `CallContext.auth` and
140
+ is available to function parameters annotated with `Auth()`. Identity is
141
+ snapshotted once for the stateful TCP connection.
142
+
143
+ When `threaded=True`, all connections share the same Worker instance. Protect
144
+ mutable subclass state with appropriate synchronization and never store
145
+ per-caller authentication in shared instance attributes. Leave the default
146
+ serial mode in place if the implementation is not safe for concurrent calls.
147
+
148
+ Behind a Tailscale Service configured to emit PROXY protocol v2, require the
149
+ preamble and name every immediate proxy address explicitly:
150
+
151
+ ```python test="lint"
152
+ MyWorker().serve_tcp(
153
+ "127.0.0.1",
154
+ 9400,
155
+ proxy_protocol="required",
156
+ trusted_proxy_addresses=("127.0.0.1", "::1"),
157
+ service_name="svc:vgi-worker",
158
+ peer_identity_providers=(tailscale,),
159
+ peer_authentication_policy=peer_identity_primary("tailscale"),
160
+ )
161
+ ```
162
+
163
+ Keep that backend unreachable except through the trusted proxy. The asserted
164
+ source is accepted only after immediate-peer validation, and `service_name`
165
+ selects destination-scoped application capabilities in LocalAPI WhoIs.
166
+
167
+ Direct TCP works with a MagicDNS name when the client host has Tailscale's
168
+ normal network interface:
169
+
170
+ ```python test="lint"
171
+ from vgi.client import Client
172
+
173
+ with Client.from_tcp("worker.example-tailnet.ts.net", 9400) as client:
174
+ catalogs = client.catalogs()
175
+ ```
176
+
177
+ For a userspace Tailscale sidecar, use the explicit SOCKS5h option:
178
+
179
+ ```python test="lint"
180
+ with Client.from_tcp(
181
+ "worker.example-tailnet.ts.net",
182
+ 9400,
183
+ proxy="socks5h://127.0.0.1:1055",
184
+ ) as client:
185
+ catalogs = client.catalogs()
186
+ ```
187
+
188
+ The proxy applies to ordinary function calls, additional worker connections,
189
+ and catalog discovery. Target hostnames are passed untouched to SOCKS5h and
190
+ are never resolved locally by VGI.
191
+
192
+ Raw VGI TCP has no application-layer encryption. Tailscale encrypts the
193
+ Tailnet path, but the worker port should remain reachable only through the
194
+ Tailnet and should not be exposed directly to an untrusted network. Prefer
195
+ HTTPS through Tailscale Serve at public or mixed-trust boundaries.
@@ -125,6 +125,8 @@ nav:
125
125
  - Expose a catalog: how-to/catalogs.md
126
126
  - Persist state across workers: how-to/state-storage.md
127
127
  - Serve over HTTP with auth: how-to/http-auth.md
128
+ - Connect through a Tailnet: how-to/tailnet.md
129
+ - Serve and connect over Iroh: how-to/iroh.md
128
130
  - Integrate with the optimizer: how-to/pushdown-and-statistics.md
129
131
  - Function Metadata: metadata.md
130
132
  - CLI: cli.md
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vgi-python"
3
- version = "0.31.2"
3
+ version = "0.31.3"
4
4
  description = "Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow"
5
5
  readme = "README.md"
6
6
  keywords = [
@@ -40,7 +40,7 @@ dependencies = [
40
40
  "pyarrow",
41
41
  "typer>=0.9",
42
42
  "platformdirs",
43
- "vgi-rpc>=0.44.1",
43
+ "vgi-rpc>=0.45.1",
44
44
  "httpx2>=2.9.1",
45
45
  ]
46
46
 
@@ -52,6 +52,7 @@ dependencies = [
52
52
  haybarn = ["haybarn>=1.5.5rc1"]
53
53
  duckdb = ["duckdb>=1.5.3"]
54
54
  http = ["vgi-rpc[http]"]
55
+ iroh = ["vgi-rpc[http,iroh]"]
55
56
  # transport="launch": vgi_rpc.launcher imports filelock unconditionally at
56
57
  # module level, which lives behind vgi-rpc's own `cli` extra (alongside
57
58
  # typer, already a core dependency here, and httpx2, ditto) rather than in
@@ -13,6 +13,7 @@ from vgi.catalog import (
13
13
  AttachOpaqueData,
14
14
  FunctionInfo,
15
15
  FunctionType,
16
+ IndexInfo,
16
17
  MacroInfo,
17
18
  MacroType,
18
19
  SchemaObjectType,
@@ -394,6 +395,37 @@ class TestExampleWorkerViews:
394
395
  assert view.comment is not None
395
396
 
396
397
 
398
+ class TestExampleWorkerIndexes:
399
+ """Test ExampleWorker's index catalog entries."""
400
+
401
+ def test_schema_contents_returns_indexes_in_data(self) -> None:
402
+ """`data` has 2 real indexes on `numbers`.
403
+
404
+ Regression: `type=SchemaObjectType.INDEX` used to silently fall
405
+ through to the functions RPC (`catalog_schema_contents_functions`)
406
+ instead of the real `catalog_schema_contents_indexes` one.
407
+ """
408
+ client = Client(EXAMPLE_WORKER)
409
+ attach_result = client.catalog_attach(
410
+ name="example", options={}, data_version_spec=None, implementation_version=None
411
+ )
412
+
413
+ indexes = list(
414
+ client.schema_contents(
415
+ attach_opaque_data=attach_result.attach_opaque_data,
416
+ name="data",
417
+ type=SchemaObjectType.INDEX,
418
+ )
419
+ )
420
+
421
+ index_names = {i.name for i in indexes}
422
+ assert index_names == {"idx_numbers_value", "idx_numbers_value_unique"}
423
+ for index in indexes:
424
+ assert isinstance(index, IndexInfo)
425
+ assert index.table_name == "numbers"
426
+ assert index.expressions == ["value"]
427
+
428
+
397
429
  class TestExampleWorkerMacros:
398
430
  """Test ExampleWorker's macro catalog entries."""
399
431
 
@@ -0,0 +1,134 @@
1
+ # Copyright 2026 Query Farm LLC - https://query.farm
2
+
3
+ # ruff: noqa: S101, D101, D102, D103
4
+ """`Client.bind()` and `Client.table_function(join_keys=...)`.
5
+
6
+ Both were previously unreachable from `Client`:
7
+
8
+ - Schema discovery for a bare (non-catalog) function name had no
9
+ standalone entry point — only `table_function(bind_result_callback=...)`,
10
+ which only fires as a side effect of a generator's first `next()`, by
11
+ which point `init()`/real execution has already started. `bind()` runs
12
+ only the `bind()` RPC.
13
+ - `join_keys` (semi-join pushdown) has been carried on `InitRequest`/
14
+ `TableFunctionPlanRequest` and threaded through worker-side
15
+ `deserialize_filters(..., join_keys=...)` all along
16
+ (`vgi/table_filter_pushdown.py`), but `Client`'s public methods never
17
+ exposed a way to set it on the request.
18
+
19
+ Exercised against `filter_echo` (`vgi/_test_fixtures/table/filters.py`),
20
+ which auto-applies whatever filters it receives and echoes them back as
21
+ SQL-like text in its `pushed_filters` column — the same fixture the
22
+ `vgi_acero` filter-pushdown spike used, since it proves pushdown is real
23
+ (server-side applied), not just correct final rows.
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import json
29
+ from typing import Any, cast
30
+
31
+ import pyarrow as pa
32
+ import pytest
33
+
34
+ from vgi.arguments import Arguments
35
+
36
+ MAIN = "main"
37
+
38
+
39
+ @pytest.fixture
40
+ def client(client_transport: Any) -> Any:
41
+ with client_transport() as c:
42
+ yield c
43
+
44
+
45
+ def _join_keys_pushdown_filters_bytes(column_name: str, keys_column: str) -> bytes:
46
+ """Build `pushdown_filters` IPC bytes containing one `join_keys` filter spec.
47
+
48
+ Unlike `constant`/`in` specs, a `join_keys` spec carries no `value_ref` —
49
+ the actual key values travel separately as `Client.table_function`'s own
50
+ `join_keys=` argument, resolved worker-side by matching `keys_column`
51
+ against each join-key batch's column name (see
52
+ `PushdownFilters.get_join_keys_column`).
53
+ """
54
+ spec = {"column_name": column_name, "column_index": 0, "type": "join_keys", "keys_column": keys_column}
55
+ spec_field = pa.field("filter_spec", pa.string(), metadata={b"vgi_filter_version": b"1"})
56
+ batch = pa.record_batch({"filter_spec": [json.dumps([spec])]}, schema=pa.schema([spec_field]))
57
+
58
+ sink = pa.BufferOutputStream()
59
+ writer = pa.ipc.new_stream(sink, batch.schema)
60
+ writer.write_batch(batch)
61
+ writer.close()
62
+ return sink.getvalue().to_pybytes()
63
+
64
+
65
+ class TestBind:
66
+ def test_bind_returns_the_bound_schema(self, client: Any) -> None:
67
+ resp = client.bind(
68
+ function_name="filter_echo",
69
+ schema_name=MAIN,
70
+ arguments=Arguments(positional=(pa.scalar(10),)),
71
+ )
72
+ assert resp.output_schema.names == ["n", "s", "pushed_filters"]
73
+
74
+ def test_bind_does_not_corrupt_the_connection_for_later_calls(self, client: Any) -> None:
75
+ """A `bind()` call must leave the connection usable for a real scan afterward.
76
+
77
+ This is the regression the old workaround (peek one batch via
78
+ `bind_result_callback` then `gen.close()`) could not satisfy — closing
79
+ a generator early desyncs the subprocess's lockstep RPC pipe for the
80
+ rest of that `Client`'s life. `bind()` never opens a stream at all.
81
+ """
82
+ client.bind(
83
+ function_name="filter_echo",
84
+ schema_name=MAIN,
85
+ arguments=Arguments(positional=(pa.scalar(10),)),
86
+ )
87
+ batches = list(
88
+ client.table_function(
89
+ function_name="filter_echo",
90
+ schema_name=MAIN,
91
+ arguments=Arguments(positional=(pa.scalar(5),)),
92
+ )
93
+ )
94
+ table = pa.Table.from_batches(batches)
95
+ assert table.num_rows == 5
96
+ assert sorted(cast("list[int]", table.column("n").to_pylist())) == [0, 1, 2, 3, 4]
97
+
98
+
99
+ class TestJoinKeys:
100
+ def test_join_keys_filters_rows_server_side(self, client: Any) -> None:
101
+ pushdown_filters = _join_keys_pushdown_filters_bytes(column_name="n", keys_column="n")
102
+ join_keys = [pa.record_batch({"n": pa.array([3, 7], type=pa.int64())})]
103
+
104
+ batches = list(
105
+ client.table_function(
106
+ function_name="filter_echo",
107
+ schema_name=MAIN,
108
+ arguments=Arguments(positional=(pa.scalar(10),)),
109
+ pushdown_filters=pushdown_filters,
110
+ join_keys=join_keys,
111
+ )
112
+ )
113
+ table = pa.Table.from_batches(batches)
114
+
115
+ assert sorted(cast("list[int]", table.column("n").to_pylist())) == [3, 7]
116
+ # auto_apply_filters=True means the worker itself filtered — the
117
+ # echoed pushed_filters text is the proof it's a real server-side
118
+ # InFilter, not us post-filtering client-side.
119
+ for value in cast("list[str]", table.column("pushed_filters").to_pylist()):
120
+ assert "n IN" in value
121
+ assert "3" in value and "7" in value
122
+
123
+ def test_no_join_keys_returns_everything(self, client: Any) -> None:
124
+ """Sanity check: omitting join_keys behaves exactly like today (no filter)."""
125
+ batches = list(
126
+ client.table_function(
127
+ function_name="filter_echo",
128
+ schema_name=MAIN,
129
+ arguments=Arguments(positional=(pa.scalar(5),)),
130
+ )
131
+ )
132
+ table = pa.Table.from_batches(batches)
133
+ assert table.num_rows == 5
134
+ assert all(v == "(none)" for v in table.column("pushed_filters").to_pylist())