vgi-python 0.8.6__tar.gz → 0.8.7__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 (362) hide show
  1. {vgi_python-0.8.6 → vgi_python-0.8.7}/PKG-INFO +2 -2
  2. {vgi_python-0.8.6 → vgi_python-0.8.7}/pyproject.toml +2 -2
  3. vgi_python-0.8.7/tests/test_tcp_transport.py +122 -0
  4. {vgi_python-0.8.6 → vgi_python-0.8.7}/uv.lock +5 -5
  5. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/client/catalog_mixin.py +23 -7
  6. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/client/client.py +92 -6
  7. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/worker.py +59 -3
  8. {vgi_python-0.8.6 → vgi_python-0.8.7}/.gitattributes +0 -0
  9. {vgi_python-0.8.6 → vgi_python-0.8.7}/.github/dependabot.yml +0 -0
  10. {vgi_python-0.8.6 → vgi_python-0.8.7}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
  11. {vgi_python-0.8.6 → vgi_python-0.8.7}/.github/workflows/ci.yml +0 -0
  12. {vgi_python-0.8.6 → vgi_python-0.8.7}/.github/workflows/docs.yml +0 -0
  13. {vgi_python-0.8.6 → vgi_python-0.8.7}/.github/workflows/integration.yml +0 -0
  14. {vgi_python-0.8.6 → vgi_python-0.8.7}/.github/workflows/release.yml +0 -0
  15. {vgi_python-0.8.6 → vgi_python-0.8.7}/.gitignore +0 -0
  16. {vgi_python-0.8.6 → vgi_python-0.8.7}/.python-version +0 -0
  17. {vgi_python-0.8.6 → vgi_python-0.8.7}/.vale.ini +0 -0
  18. {vgi_python-0.8.6 → vgi_python-0.8.7}/CLAUDE.md +0 -0
  19. {vgi_python-0.8.6 → vgi_python-0.8.7}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
  20. {vgi_python-0.8.6 → vgi_python-0.8.7}/DOCS_REVIEW_RUBRIC.md +0 -0
  21. {vgi_python-0.8.6 → vgi_python-0.8.7}/DOCS_USABILITY_TEST.md +0 -0
  22. {vgi_python-0.8.6 → vgi_python-0.8.7}/LICENSE +0 -0
  23. {vgi_python-0.8.6 → vgi_python-0.8.7}/README.md +0 -0
  24. {vgi_python-0.8.6 → vgi_python-0.8.7}/SECURITY.md +0 -0
  25. {vgi_python-0.8.6 → vgi_python-0.8.7}/ci/README.md +0 -0
  26. {vgi_python-0.8.6 → vgi_python-0.8.7}/ci/preprocess-require.awk +0 -0
  27. {vgi_python-0.8.6 → vgi_python-0.8.7}/ci/run-integration.sh +0 -0
  28. {vgi_python-0.8.6 → vgi_python-0.8.7}/dist-vgi/.gitignore +0 -0
  29. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/aggregate-functions.md +0 -0
  30. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/arguments.md +0 -0
  31. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/auth.md +0 -0
  32. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/catalogs.md +0 -0
  33. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/client.md +0 -0
  34. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/exceptions.md +0 -0
  35. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/filters.md +0 -0
  36. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/functions.md +0 -0
  37. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/http.md +0 -0
  38. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/index.md +0 -0
  39. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/metadata.md +0 -0
  40. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/observability.md +0 -0
  41. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/storage.md +0 -0
  42. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/transactor.md +0 -0
  43. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/api/worker.md +0 -0
  44. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/argument-serialization.md +0 -0
  45. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/apple-touch-icon.png +0 -0
  46. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/favicon-16x16.png +0 -0
  47. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/favicon-32x32.png +0 -0
  48. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/favicon.ico +0 -0
  49. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/kinds/aggregate.svg +0 -0
  50. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/kinds/buffering.svg +0 -0
  51. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/kinds/scalar.svg +0 -0
  52. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/kinds/table-in-out.svg +0 -0
  53. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/kinds/table.svg +0 -0
  54. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/logo.png +0 -0
  55. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/assets/social-card.png +0 -0
  56. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/authentication.md +0 -0
  57. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/catalog-interface.md +0 -0
  58. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/cli.md +0 -0
  59. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/column-statistics.md +0 -0
  60. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/concepts/index.md +0 -0
  61. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/contributing-docs.md +0 -0
  62. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/filter-pushdown.md +0 -0
  63. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/generator-api.md +0 -0
  64. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/how-to/catalogs.md +0 -0
  65. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/how-to/function-patterns.md +0 -0
  66. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/how-to/http-auth.md +0 -0
  67. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/how-to/index.md +0 -0
  68. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/how-to/pushdown-and-statistics.md +0 -0
  69. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/how-to/state-storage.md +0 -0
  70. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/index.md +0 -0
  71. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/lifecycle.md +0 -0
  72. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/metadata.md +0 -0
  73. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/overrides/main.html +0 -0
  74. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/robots.txt +0 -0
  75. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/shared-storage.md +0 -0
  76. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/stylesheets/extra.css +0 -0
  77. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/tutorial/index.md +0 -0
  78. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/tutorial/scalar.md +0 -0
  79. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/tutorial/table.md +0 -0
  80. {vgi_python-0.8.6 → vgi_python-0.8.7}/docs/vgi-logo.png +0 -0
  81. {vgi_python-0.8.6 → vgi_python-0.8.7}/examples/calc_scalar_worker.py +0 -0
  82. {vgi_python-0.8.6 → vgi_python-0.8.7}/examples/calc_worker.py +0 -0
  83. {vgi_python-0.8.6 → vgi_python-0.8.7}/examples/filter_worker.py +0 -0
  84. {vgi_python-0.8.6 → vgi_python-0.8.7}/examples/greeting_scalar_worker.py +0 -0
  85. {vgi_python-0.8.6 → vgi_python-0.8.7}/examples/row_count_worker.py +0 -0
  86. {vgi_python-0.8.6 → vgi_python-0.8.7}/examples/series_streaming_worker.py +0 -0
  87. {vgi_python-0.8.6 → vgi_python-0.8.7}/examples/sum_worker.py +0 -0
  88. {vgi_python-0.8.6 → vgi_python-0.8.7}/mkdocs.yml +0 -0
  89. {vgi_python-0.8.6 → vgi_python-0.8.7}/packages/vgi-fixtures/LICENSE +0 -0
  90. {vgi_python-0.8.6 → vgi_python-0.8.7}/packages/vgi-fixtures/README.md +0 -0
  91. {vgi_python-0.8.6 → vgi_python-0.8.7}/packages/vgi-fixtures/pyproject.toml +0 -0
  92. {vgi_python-0.8.6 → vgi_python-0.8.7}/scripts/measure_startup.py +0 -0
  93. {vgi_python-0.8.6 → vgi_python-0.8.7}/scripts/run_all_tests.sh +0 -0
  94. {vgi_python-0.8.6 → vgi_python-0.8.7}/test-data/generate.sh +0 -0
  95. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/__init__.py +0 -0
  96. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/_http_fixtures.py +0 -0
  97. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/__init__.py +0 -0
  98. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_catalog_interface.py +0 -0
  99. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_client_catalog.py +0 -0
  100. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_column_statistics.py +0 -0
  101. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_declarative.py +0 -0
  102. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_example_worker_catalog.py +0 -0
  103. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_integration.py +0 -0
  104. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_required_field_filter_paths.py +0 -0
  105. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_scan_branches.py +0 -0
  106. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_serialization.py +0 -0
  107. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_setting.py +0 -0
  108. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_storage.py +0 -0
  109. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_time_travel.py +0 -0
  110. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/catalog/test_writable_table.py +0 -0
  111. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/client/__init__.py +0 -0
  112. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/client/test_broken_pipe.py +0 -0
  113. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/client/test_cli.py +0 -0
  114. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/client/test_cli_catalog_functions.py +0 -0
  115. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/client/test_worker_debug.py +0 -0
  116. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/__init__.py +0 -0
  117. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/_stub.py +0 -0
  118. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/conftest.py +0 -0
  119. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_accumulate.py +0 -0
  120. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_aggregate.py +0 -0
  121. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_attach.py +0 -0
  122. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_bearer_auth.py +0 -0
  123. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_directory_parity.py +0 -0
  124. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_function_inventory.py +0 -0
  125. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_http_client.py +0 -0
  126. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_http_external_location.py +0 -0
  127. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_http_upload_url.py +0 -0
  128. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_macro.py +0 -0
  129. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_overload.py +0 -0
  130. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_protocol_inventory.py +0 -0
  131. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_protocol_version.py +0 -0
  132. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_resumable_scan.py +0 -0
  133. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_scalar.py +0 -0
  134. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
  135. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_secret.py +0 -0
  136. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_settings.py +0 -0
  137. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_table.py +0 -0
  138. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_table_in_out.py +0 -0
  139. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_view.py +0 -0
  140. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conformance/test_writable.py +0 -0
  141. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/conftest.py +0 -0
  142. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/scalar/__init__.py +0 -0
  143. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/scalar/test_bernoulli_function.py +0 -0
  144. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/scalar/test_binary_packet_function.py +0 -0
  145. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/scalar/test_client.py +0 -0
  146. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/scalar/test_conditional_message_function.py +0 -0
  147. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/scalar/test_hash_seed_function.py +0 -0
  148. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/scalar/test_multiply_by_setting_function.py +0 -0
  149. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/scalar/test_multiply_function.py +0 -0
  150. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/scalar/test_random_bytes_function.py +0 -0
  151. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/scalar/test_return_secret_value_function.py +0 -0
  152. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/__init__.py +0 -0
  153. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/__init__.py +0 -0
  154. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_constant_columns_function.py +0 -0
  155. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_double_sequence_function.py +0 -0
  156. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_exception_function.py +0 -0
  157. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_filter_echo_function.py +0 -0
  158. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_logging_function.py +0 -0
  159. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_nested_sequence_function.py +0 -0
  160. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_partitioned_function.py +0 -0
  161. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_projected_data_function.py +0 -0
  162. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_sequence_function.py +0 -0
  163. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_settings_function.py +0 -0
  164. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_struct_settings_function.py +0 -0
  165. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table/generator/test_ten_thousand_function.py +0 -0
  166. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table_in_out/__init__.py +0 -0
  167. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table_in_out/generator/__init__.py +0 -0
  168. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
  169. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table_in_out/generator/test_echo_function.py +0 -0
  170. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table_in_out/generator/test_exception_functions.py +0 -0
  171. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
  172. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
  173. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
  174. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/table_in_out/test_client.py +0 -0
  175. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_access_log_audit.py +0 -0
  176. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_aggregate_function.py +0 -0
  177. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_argument_spec.py +0 -0
  178. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_auth.py +0 -0
  179. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_bind_exceptions.py +0 -0
  180. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_bind_request_at_clause.py +0 -0
  181. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_bound_storage_conformance.py +0 -0
  182. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_catalog_auth_binding.py +0 -0
  183. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_docstrings.py +0 -0
  184. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_documentation_examples.py +0 -0
  185. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_example_function_arg_types.py +0 -0
  186. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_examples_workers.py +0 -0
  187. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_exception_handling.py +0 -0
  188. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_exceptions.py +0 -0
  189. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_filter_pushdown.py +0 -0
  190. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_filter_pushdown_extension.py +0 -0
  191. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_function_storage.py +0 -0
  192. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_function_storage_azure_sql.py +0 -0
  193. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_function_storage_cf_do.py +0 -0
  194. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_function_storage_cf_do_integration.py +0 -0
  195. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_function_storage_conformance.py +0 -0
  196. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_generated_cpp_constants.py +0 -0
  197. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_generated_cpp_protocol_version.py +0 -0
  198. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_generated_cpp_request_builders.py +0 -0
  199. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_generated_cpp_schemas.py +0 -0
  200. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_generated_cpp_secret.py +0 -0
  201. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_generated_go_schemas.py +0 -0
  202. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_generated_protocol_version.py +0 -0
  203. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_generated_schemas_cross_lang.py +0 -0
  204. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_generated_ts_client.py +0 -0
  205. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_generated_ts_schemas.py +0 -0
  206. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_http_demo_storage.py +0 -0
  207. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_http_s3_offload_input.py +0 -0
  208. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_http_s3_offload_output.py +0 -0
  209. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_metadata.py +0 -0
  210. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_mypy_consolidated.py +0 -0
  211. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_nest_tensor.py +0 -0
  212. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_otel.py +0 -0
  213. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_projection_enforcement.py +0 -0
  214. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_projection_repro.py +0 -0
  215. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_protocol_classes.py +0 -0
  216. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_schema_utils.py +0 -0
  217. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_serve.py +0 -0
  218. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_setting_secret_annotations.py +0 -0
  219. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_table_buffering_function.py +0 -0
  220. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_table_function_dynamic_to_string.py +0 -0
  221. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_type_bounds.py +0 -0
  222. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_union_argument.py +0 -0
  223. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_worker.py +0 -0
  224. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_worker_cli.py +0 -0
  225. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/test_worker_page.py +0 -0
  226. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/transactor/__init__.py +0 -0
  227. {vgi_python-0.8.6 → vgi_python-0.8.7}/tests/transactor/test_transactor.py +0 -0
  228. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/__init__.py +0 -0
  229. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_duckdb.py +0 -0
  230. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_storage_profile.py +0 -0
  231. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/__init__.py +0 -0
  232. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
  233. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/accumulate/worker.py +0 -0
  234. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
  235. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/aggregate/_common.py +0 -0
  236. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/aggregate/basic.py +0 -0
  237. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
  238. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/aggregate/generic.py +0 -0
  239. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
  240. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
  241. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
  242. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
  243. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/aggregate/window.py +0 -0
  244. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/attach_options.py +0 -0
  245. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/bad_enum.py +0 -0
  246. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/bad_protocol.py +0 -0
  247. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/cancellable.py +0 -0
  248. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/catalog.py +0 -0
  249. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/http_server.py +0 -0
  250. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
  251. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
  252. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/nest_tensor.py +0 -0
  253. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/orchard_catalog.py +0 -0
  254. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
  255. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
  256. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/scalar/__init__.py +0 -0
  257. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/scalar/_common.py +0 -0
  258. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
  259. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/scalar/binary.py +0 -0
  260. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/scalar/formatting.py +0 -0
  261. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/scalar/geo.py +0 -0
  262. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
  263. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
  264. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
  265. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/scalar/type_info.py +0 -0
  266. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
  267. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
  268. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/simple_writable.py +0 -0
  269. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/__init__.py +0 -0
  270. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/_common.py +0 -0
  271. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/batch_index.py +0 -0
  272. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
  273. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
  274. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/filters.py +0 -0
  275. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/late_materialization.py +0 -0
  276. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/make_series.py +0 -0
  277. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/misc.py +0 -0
  278. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/order_modes.py +0 -0
  279. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/pairs.py +0 -0
  280. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/partition_columns.py +0 -0
  281. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
  282. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/profiling_example.py +0 -0
  283. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/required_filters.py +0 -0
  284. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/sequence.py +0 -0
  285. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/settings.py +0 -0
  286. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
  287. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
  288. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/typed_probe.py +0 -0
  289. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table/versioned.py +0 -0
  290. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/table_in_out.py +0 -0
  291. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/versioned.py +0 -0
  292. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/versioned_tables.py +0 -0
  293. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/worker.py +0 -0
  294. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/writable/__init__.py +0 -0
  295. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/writable/generic.py +0 -0
  296. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/writable/table.py +0 -0
  297. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/_test_fixtures/writable/worker.py +0 -0
  298. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/aggregate_function.py +0 -0
  299. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/argument_spec.py +0 -0
  300. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/arguments.py +0 -0
  301. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/auth.py +0 -0
  302. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/catalog/__init__.py +0 -0
  303. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/catalog/_descriptor_spec.py +0 -0
  304. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/catalog/attach_option.py +0 -0
  305. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/catalog/catalog_interface.py +0 -0
  306. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/catalog/descriptors.py +0 -0
  307. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/catalog/duckdb_statistics.py +0 -0
  308. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/catalog/secret_type.py +0 -0
  309. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/catalog/setting.py +0 -0
  310. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/catalog/storage.py +0 -0
  311. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/client/__init__.py +0 -0
  312. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/client/cli.py +0 -0
  313. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/client/cli_catalog.py +0 -0
  314. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/client/cli_schema.py +0 -0
  315. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/client/cli_table.py +0 -0
  316. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/client/cli_transaction.py +0 -0
  317. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/client/cli_utils.py +0 -0
  318. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/client/cli_view.py +0 -0
  319. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/__init__.py +0 -0
  320. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/_common.py +0 -0
  321. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/cpp_constants.py +0 -0
  322. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/cpp_protocol_version.py +0 -0
  323. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/cpp_request_builders.py +0 -0
  324. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/cpp_schemas.py +0 -0
  325. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
  326. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/cpp_secret_request_builders.py +0 -0
  327. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/cpp_secret_schemas.py +0 -0
  328. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/go_schemas.py +0 -0
  329. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/protocol_version.py +0 -0
  330. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/ts_client.py +0 -0
  331. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/codegen/ts_schemas.py +0 -0
  332. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/exceptions.py +0 -0
  333. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/function.py +0 -0
  334. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/function_storage.py +0 -0
  335. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/function_storage_azure_sql.py +0 -0
  336. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/function_storage_cf_do.py +0 -0
  337. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/http/__init__.py +0 -0
  338. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/http/demo_storage.py +0 -0
  339. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/http/worker_page.py +0 -0
  340. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/invocation.py +0 -0
  341. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/logging_config.py +0 -0
  342. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/meta_worker.py +0 -0
  343. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/metadata.py +0 -0
  344. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/otel.py +0 -0
  345. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/protocol.py +0 -0
  346. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/protocol_version.txt +0 -0
  347. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/py.typed +0 -0
  348. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/scalar_function.py +0 -0
  349. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/schema_utils.py +0 -0
  350. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/secret_protocol.py +0 -0
  351. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/secret_service.py +0 -0
  352. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/serve.py +0 -0
  353. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/table_buffering_function.py +0 -0
  354. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/table_filter_pushdown.py +0 -0
  355. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/table_function.py +0 -0
  356. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/table_in_out_function.py +0 -0
  357. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/transactor/__init__.py +0 -0
  358. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/transactor/_duckdb_compat.py +0 -0
  359. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/transactor/client.py +0 -0
  360. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/transactor/protocol.py +0 -0
  361. {vgi_python-0.8.6 → vgi_python-0.8.7}/vgi/transactor/server.py +0 -0
  362. {vgi_python-0.8.6 → vgi_python-0.8.7}/wrangler.jsonc +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vgi-python
3
- Version: 0.8.6
3
+ Version: 0.8.7
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: httpx>=0.24
162
162
  Requires-Dist: platformdirs
163
163
  Requires-Dist: pyarrow
164
164
  Requires-Dist: typer>=0.9
165
- Requires-Dist: vgi-rpc>=0.20.5
165
+ Requires-Dist: vgi-rpc>=0.21.0
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'
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vgi-python"
3
- version = "0.8.6"
3
+ version = "0.8.7"
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.20.5",
43
+ "vgi-rpc>=0.21.0",
44
44
  "httpx>=0.24",
45
45
  ]
46
46
 
@@ -0,0 +1,122 @@
1
+ # Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+
3
+ """Round-trip tests for the TCP transport.
4
+
5
+ Spawns ``vgi-fixture-worker --tcp 127.0.0.1:0`` (raw Arrow-IPC framing over a
6
+ TCP socket, served by ``vgi_rpc.rpc.run_server``), parses the ``TCP:host:port``
7
+ discovery line it prints on stdout, then drives it through
8
+ ``Client.from_tcp(...)`` — the TCP analog of the HTTP round-trip in
9
+ ``tests/_http_fixtures.py``.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import queue
15
+ import subprocess
16
+ import sys
17
+ import threading
18
+ from collections.abc import Iterator
19
+ from contextlib import contextmanager
20
+
21
+ import pyarrow as pa
22
+ import pytest
23
+
24
+ from vgi.arguments import Arguments
25
+ from vgi.client import Client
26
+
27
+
28
+ @contextmanager
29
+ def run_tcp_worker(*, bind: str = "127.0.0.1:0") -> Iterator[tuple[str, int]]:
30
+ """Run ``vgi-fixture-worker --tcp`` and yield the bound ``(host, port)``.
31
+
32
+ The worker prints ``TCP:<host>:<port>`` once bound and then must not write
33
+ further to stdout (the cross-language launcher discovery contract), so we
34
+ read exactly one line to learn the port. stderr is drained in the
35
+ background to keep the worker from blocking on a full pipe buffer.
36
+ """
37
+ proc = subprocess.Popen(
38
+ [sys.executable, "-m", "vgi._test_fixtures.worker", "--tcp", bind],
39
+ stdout=subprocess.PIPE,
40
+ stderr=subprocess.PIPE,
41
+ text=True,
42
+ bufsize=1,
43
+ )
44
+
45
+ def _drain(pipe: object) -> None:
46
+ for _ in pipe: # type: ignore[attr-defined]
47
+ pass
48
+
49
+ stderr_thread = threading.Thread(target=_drain, args=(proc.stderr,), daemon=True)
50
+ stderr_thread.start()
51
+
52
+ # Read the discovery line off stdout with a timeout so a worker that never
53
+ # binds fails the test instead of hanging it.
54
+ line_q: queue.Queue[str] = queue.Queue(maxsize=1)
55
+
56
+ def _read_line() -> None:
57
+ assert proc.stdout is not None
58
+ for line in proc.stdout:
59
+ if line.startswith("TCP:"):
60
+ line_q.put(line.strip())
61
+ return
62
+
63
+ reader = threading.Thread(target=_read_line, daemon=True)
64
+ reader.start()
65
+ try:
66
+ discovery = line_q.get(timeout=30)
67
+ except queue.Empty:
68
+ proc.terminate()
69
+ raise TimeoutError("worker did not emit a TCP: discovery line within 30s") from None
70
+
71
+ _, host, port_str = discovery.split(":", 2)
72
+ try:
73
+ yield host, int(port_str)
74
+ finally:
75
+ proc.terminate()
76
+ try:
77
+ proc.wait(timeout=10)
78
+ except subprocess.TimeoutExpired:
79
+ proc.kill()
80
+ proc.wait(timeout=5)
81
+ stderr_thread.join(timeout=5)
82
+
83
+
84
+ def test_tcp_round_trip_table_function() -> None:
85
+ """A table function streams rows correctly over the TCP transport."""
86
+ with run_tcp_worker() as (host, port), Client.from_tcp(host, port) as client:
87
+ batches = list(
88
+ client.table_function(
89
+ function_name="sequence",
90
+ arguments=Arguments(positional=(pa.scalar(5),)),
91
+ )
92
+ )
93
+
94
+ table = pa.Table.from_batches(batches)
95
+ assert table.column("n").to_pylist() == [0, 1, 2, 3, 4]
96
+
97
+
98
+ def test_tcp_round_trip_catalog_listing() -> None:
99
+ """Catalog discovery works over the TCP transport (catalog_mixin path)."""
100
+ with run_tcp_worker() as (host, port), Client.from_tcp(host, port) as client:
101
+ catalogs = client.catalogs()
102
+
103
+ assert any(c.name == "example" for c in catalogs)
104
+
105
+
106
+ class TestTcpConstructorValidation:
107
+ """``transport='tcp'`` argument validation."""
108
+
109
+ def test_requires_host_and_port(self) -> None:
110
+ """Tcp transport without host/port is rejected."""
111
+ with pytest.raises(ValueError, match="requires tcp_host and tcp_port"):
112
+ Client(transport="tcp", pool=None)
113
+
114
+ def test_rejects_server_path(self) -> None:
115
+ """server_path is subprocess-only."""
116
+ with pytest.raises(ValueError, match="server_path is only meaningful"):
117
+ Client("some-worker", transport="tcp", tcp_host="127.0.0.1", tcp_port=1, pool=None)
118
+
119
+ def test_rejects_base_url(self) -> None:
120
+ """base_url is http-only."""
121
+ with pytest.raises(ValueError, match="base_url is only meaningful"):
122
+ Client(transport="tcp", tcp_host="127.0.0.1", tcp_port=1, base_url="http://x", pool=None)
@@ -2250,7 +2250,7 @@ requires-dist = [
2250
2250
 
2251
2251
  [[package]]
2252
2252
  name = "vgi-python"
2253
- version = "0.8.5"
2253
+ version = "0.8.7"
2254
2254
  source = { editable = "." }
2255
2255
  dependencies = [
2256
2256
  { name = "click" },
@@ -2360,7 +2360,7 @@ requires-dist = [
2360
2360
  { name = "vgi-python", extras = ["test-fixtures"], marker = "extra == 'test-fixtures-writable'" },
2361
2361
  { name = "vgi-python", extras = ["test-fixtures", "test-fixtures-writable", "http", "oauth", "otel", "sentry", "azure"], marker = "extra == 'dev'" },
2362
2362
  { name = "vgi-python", extras = ["transactor"], marker = "extra == 'test-fixtures-writable'" },
2363
- { name = "vgi-rpc", specifier = ">=0.20.5" },
2363
+ { name = "vgi-rpc", specifier = ">=0.21.0" },
2364
2364
  { name = "vgi-rpc", extras = ["conformance", "external", "http", "oauth", "otel", "sentry"], marker = "extra == 'dev'" },
2365
2365
  { name = "vgi-rpc", extras = ["http"], marker = "extra == 'http'" },
2366
2366
  { name = "vgi-rpc", extras = ["oauth"], marker = "extra == 'oauth'" },
@@ -2389,7 +2389,7 @@ docs = [
2389
2389
 
2390
2390
  [[package]]
2391
2391
  name = "vgi-rpc"
2392
- version = "0.20.6"
2392
+ version = "0.21.0"
2393
2393
  source = { registry = "https://pypi.org/simple" }
2394
2394
  dependencies = [
2395
2395
  { name = "docstring-parser" },
@@ -2397,9 +2397,9 @@ dependencies = [
2397
2397
  { name = "pywin32", marker = "sys_platform == 'win32'" },
2398
2398
  { name = "tzdata", marker = "sys_platform == 'win32'" },
2399
2399
  ]
2400
- sdist = { url = "https://files.pythonhosted.org/packages/40/76/4a2c35af34928c6ab17fcb9b1b50639241bf9d015d45b23ce004d0d70d51/vgi_rpc-0.20.6.tar.gz", hash = "sha256:3172d5041b901b0d1a32c9b45da32363195bbca18adb7e305456f19c241cf8fe", size = 847271, upload-time = "2026-06-22T18:24:18.969Z" }
2400
+ sdist = { url = "https://files.pythonhosted.org/packages/3d/7b/0de17f5f638414188829f8bb4952e9a6116145ad402512ead8d0f035b705/vgi_rpc-0.21.0.tar.gz", hash = "sha256:c82c17149c0977080184d4728bdebe38aebcd1374984f6c81a3ce39be0aca367", size = 857916, upload-time = "2026-06-26T00:05:14.045Z" }
2401
2401
  wheels = [
2402
- { url = "https://files.pythonhosted.org/packages/d3/3c/9ccc888e725ad9d7091e8ec6fd7504eec15dc2ccfabedabd7d072123b0e8/vgi_rpc-0.20.6-py3-none-any.whl", hash = "sha256:74f8b7c0685f1c28a3ff754d76f8c0179c782e33aa5915ad0343bb9955d7509d", size = 371735, upload-time = "2026-06-22T18:24:17.458Z" },
2402
+ { url = "https://files.pythonhosted.org/packages/40/71/1607a7b093e2477d64a0193d21ec9b9fdfe8bc09f0165ce378fc214ef0a9/vgi_rpc-0.21.0-py3-none-any.whl", hash = "sha256:5e5b1ef8585f459eb0761c9232a6e4eeedef69be7b61ead61e0993f4a15b8698", size = 378442, upload-time = "2026-06-26T00:05:11.821Z" },
2403
2403
  ]
2404
2404
 
2405
2405
  [package.optional-dependencies]
@@ -90,12 +90,14 @@ class CatalogClientMixin:
90
90
  Catalog methods spawn ephemeral connections under the hood — for
91
91
  subprocess transport a pooled subprocess worker; for HTTP transport a
92
92
  short-lived ``http_connect`` session reusing the ``Client``'s shared
93
- ``httpx.Client`` (bearer token, headers). Browsing catalogs over HTTP
94
- is the canonical non-DuckDB use case this mixin supports.
93
+ ``httpx.Client`` (bearer token, headers); for TCP transport a short-lived
94
+ ``tcp_connect`` session. Browsing catalogs over HTTP is the canonical
95
+ non-DuckDB use case this mixin supports.
95
96
 
96
- Other attributes expected from ``Client``: ``_transport`` (subprocess vs
97
- http), ``_base_url`` (HTTP base URL), and ``_get_or_create_httpx_client()``
98
- (shared HTTP client factory).
97
+ Other attributes expected from ``Client``: ``_transport`` (subprocess,
98
+ http, or tcp), ``_base_url`` (HTTP base URL), ``_tcp_host`` / ``_tcp_port``
99
+ (TCP endpoint), and ``_get_or_create_httpx_client()`` (shared HTTP client
100
+ factory).
99
101
 
100
102
  Attributes:
101
103
  server_path: Worker shell command used for subprocess transport.
@@ -103,8 +105,10 @@ class CatalogClientMixin:
103
105
 
104
106
  # Type hints for attributes expected from Client
105
107
  server_path: str
106
- _transport: Literal["subprocess", "http"]
108
+ _transport: Literal["subprocess", "http", "tcp"]
107
109
  _base_url: str | None
110
+ _tcp_host: str | None
111
+ _tcp_port: int | None
108
112
  _external_location: Any | None
109
113
 
110
114
  def _get_or_create_httpx_client(self) -> Any: # implemented by Client
@@ -128,7 +132,8 @@ class CatalogClientMixin:
128
132
  A typed `[`VgiProtocol`][]` proxy bound to the active transport.
129
133
  """
130
134
  try:
131
- if getattr(self, "_transport", "subprocess") == "http":
135
+ transport = getattr(self, "_transport", "subprocess")
136
+ if transport == "http":
132
137
  from vgi_rpc.http import http_connect
133
138
 
134
139
  httpx_client = self._get_or_create_httpx_client()
@@ -139,6 +144,17 @@ class CatalogClientMixin:
139
144
  external_location=getattr(self, "_external_location", None),
140
145
  ) as proxy:
141
146
  yield proxy
147
+ elif transport == "tcp":
148
+ from vgi_rpc.rpc import tcp_connect
149
+
150
+ assert self._tcp_host is not None and self._tcp_port is not None
151
+ with tcp_connect(
152
+ VgiProtocol, # type: ignore[type-abstract]
153
+ self._tcp_host,
154
+ self._tcp_port,
155
+ external_location=getattr(self, "_external_location", None),
156
+ ) as proxy:
157
+ yield proxy
142
158
  else:
143
159
  cmd = shlex.split(self.server_path, posix=sys.platform != "win32")
144
160
  with _catalog_pool.connect(VgiProtocol, cmd) as proxy: # type: ignore[type-abstract]
@@ -197,10 +197,10 @@ _HTTP_TRANSPORT_READY = True
197
197
 
198
198
  @dataclass
199
199
  class WorkerConnection:
200
- """Holds state for a single worker connection (subprocess or HTTP).
200
+ """Holds state for a single worker connection (subprocess, HTTP, or TCP).
201
201
 
202
- Exactly one of {proc+connection, _pool_ctx, _http_ctx} is active per
203
- connection — transport-specific teardown inspects these fields.
202
+ Exactly one of {proc+connection, _pool_ctx, _http_ctx, _tcp_ctx} is active
203
+ per connection — transport-specific teardown inspects these fields.
204
204
 
205
205
  Attributes:
206
206
  proxy: The typed `[`VgiProtocol`][]` proxy used to invoke the worker.
@@ -220,6 +220,8 @@ class WorkerConnection:
220
220
  _pool_ctx: AbstractContextManager[Any] | None = field(default=None, repr=False)
221
221
  # HTTP transport: context manager from vgi_rpc.http.http_connect.
222
222
  _http_ctx: AbstractContextManager[Any] | None = field(default=None, repr=False)
223
+ # TCP transport: context manager from vgi_rpc.rpc.tcp_connect.
224
+ _tcp_ctx: AbstractContextManager[Any] | None = field(default=None, repr=False)
223
225
 
224
226
 
225
227
  class Client(CatalogClientMixin):
@@ -381,8 +383,10 @@ class Client(CatalogClientMixin):
381
383
  attach_opaque_data: bytes | None = None,
382
384
  pool: WorkerPool | None = _default_pool,
383
385
  *,
384
- transport: Literal["subprocess", "http"] = "subprocess",
386
+ transport: Literal["subprocess", "http", "tcp"] = "subprocess",
385
387
  base_url: str | None = None,
388
+ tcp_host: str | None = None,
389
+ tcp_port: int | None = None,
386
390
  bearer_token: str | None = None,
387
391
  httpx_client: Any | None = None,
388
392
  external_location: Any | None = None,
@@ -413,9 +417,14 @@ class Client(CatalogClientMixin):
413
417
  management.
414
418
  transport: Which transport to use. ``"subprocess"`` (default)
415
419
  spawns a local subprocess per worker; ``"http"`` connects to
416
- a running worker via ``vgi_rpc.http.http_connect``.
420
+ a running worker via ``vgi_rpc.http.http_connect``; ``"tcp"``
421
+ connects to a running worker via ``vgi_rpc.rpc.tcp_connect``
422
+ (raw Arrow-IPC framing, no auth/encryption — loopback /
423
+ trusted networks only; use ``Client.from_tcp(...)``).
417
424
  base_url: HTTP-only. Base URL of the running worker, e.g.
418
425
  ``"http://127.0.0.1:8765"``.
426
+ tcp_host: TCP-only. Hostname or IP of the running worker.
427
+ tcp_port: TCP-only. Port of the running worker.
419
428
  bearer_token: HTTP-only. When set, every request carries an
420
429
  ``Authorization: Bearer <token>`` header. Static token
421
430
  support only — no JWT / OAuth flows.
@@ -446,12 +455,21 @@ class Client(CatalogClientMixin):
446
455
  raise ValueError("transport='http' requires base_url")
447
456
  if server_path is not None:
448
457
  raise ValueError("server_path is only meaningful for transport='subprocess'")
458
+ elif transport == "tcp":
459
+ if tcp_host is None or tcp_port is None:
460
+ raise ValueError("transport='tcp' requires tcp_host and tcp_port")
461
+ if server_path is not None:
462
+ raise ValueError("server_path is only meaningful for transport='subprocess'")
463
+ if base_url is not None:
464
+ raise ValueError("base_url is only meaningful for transport='http'")
449
465
  else:
450
466
  raise ValueError(f"unknown transport {transport!r}")
451
467
 
452
468
  self.server_path = server_path or ""
453
469
  self._transport = transport
454
470
  self._base_url = base_url
471
+ self._tcp_host = tcp_host
472
+ self._tcp_port = tcp_port
455
473
  self._bearer_token = bearer_token
456
474
  self._httpx_client = httpx_client
457
475
  # True when ``_get_or_create_httpx_client`` constructed the client and
@@ -460,7 +478,7 @@ class Client(CatalogClientMixin):
460
478
  self._httpx_client_owned = False
461
479
  # Auto-enable pointer-batch resolution for HTTP unless the caller
462
480
  # asked for something different. See ``external_location`` docs above.
463
- if transport == "http" and external_location is None:
481
+ if transport in ("http", "tcp") and external_location is None:
464
482
  from vgi_rpc.external import ExternalLocationConfig
465
483
 
466
484
  external_location = ExternalLocationConfig()
@@ -509,6 +527,34 @@ class Client(CatalogClientMixin):
509
527
  pool=None,
510
528
  )
511
529
 
530
+ @classmethod
531
+ def from_tcp(
532
+ cls,
533
+ host: str,
534
+ port: int,
535
+ *,
536
+ external_location: Any | None = None,
537
+ worker_limit: int | None = None,
538
+ attach_opaque_data: bytes | None = None,
539
+ ) -> Client:
540
+ """Create a `[`Client`][]` bound to a running TCP VGI worker.
541
+
542
+ Connects via ``vgi_rpc.rpc.tcp_connect`` (raw Arrow-IPC framing). The
543
+ framing carries **no authentication or encryption** — only connect to
544
+ trusted endpoints on loopback or a trusted network; use
545
+ ``Client.from_http(...)`` for untrusted networks. Spin up a matching
546
+ worker with ``vgi-fixture-worker --tcp [HOST:]PORT``.
547
+ """
548
+ return cls(
549
+ transport="tcp",
550
+ tcp_host=host,
551
+ tcp_port=port,
552
+ external_location=external_location,
553
+ worker_limit=worker_limit,
554
+ attach_opaque_data=attach_opaque_data,
555
+ pool=None,
556
+ )
557
+
512
558
  def _drain_stderr(self, stderr: IO[bytes]) -> None:
513
559
  """Background thread that continuously reads stderr.
514
560
 
@@ -577,8 +623,40 @@ class Client(CatalogClientMixin):
577
623
  """
578
624
  if self._transport == "http":
579
625
  return self._spawn_http_connection(worker_index)
626
+ if self._transport == "tcp":
627
+ return self._spawn_tcp_connection(worker_index)
580
628
  return self._spawn_subprocess_connection(worker_index)
581
629
 
630
+ def _spawn_tcp_connection(self, worker_index: int) -> WorkerConnection:
631
+ """Connect to a running TCP worker via ``vgi_rpc.rpc.tcp_connect``.
632
+
633
+ Raw Arrow-IPC framing with no auth/encryption — see ``from_tcp``.
634
+ Multiple ``worker_index`` values open independent TCP connections to
635
+ the same ``host:port``.
636
+ """
637
+ from vgi_rpc.rpc import tcp_connect
638
+
639
+ assert self._tcp_host is not None and self._tcp_port is not None # enforced in __init__
640
+ ctx: AbstractContextManager[VgiProtocol] = tcp_connect(
641
+ VgiProtocol, # type: ignore[type-abstract]
642
+ self._tcp_host,
643
+ self._tcp_port,
644
+ on_log=self._on_worker_log,
645
+ external_location=self._external_location,
646
+ )
647
+ proxy = ctx.__enter__()
648
+ _logger.debug(
649
+ "tcp_connection_opened worker_index=%s host=%s port=%s",
650
+ worker_index,
651
+ self._tcp_host,
652
+ self._tcp_port,
653
+ )
654
+ return WorkerConnection(
655
+ proxy=proxy,
656
+ worker_index=worker_index,
657
+ _tcp_ctx=ctx,
658
+ )
659
+
582
660
  def _spawn_http_connection(self, worker_index: int) -> WorkerConnection:
583
661
  """Connect to a remote HTTP worker via ``vgi_rpc.http.http_connect``.
584
662
 
@@ -724,6 +802,12 @@ class Client(CatalogClientMixin):
724
802
  _logger.debug("http_connection_closed worker_index=%s", worker.worker_index)
725
803
  return 0
726
804
 
805
+ if worker._tcp_ctx is not None:
806
+ # TCP transport — close the RPC proxy (and its socket).
807
+ worker._tcp_ctx.__exit__(None, None, None)
808
+ _logger.debug("tcp_connection_closed worker_index=%s", worker.worker_index)
809
+ return 0
810
+
727
811
  if worker._pool_ctx is not None:
728
812
  # Return to pool — pool handles subprocess lifecycle
729
813
  worker._pool_ctx.__exit__(None, None, None)
@@ -805,6 +889,8 @@ class Client(CatalogClientMixin):
805
889
  id_repr: Any = self._primary.proc.pid
806
890
  elif self._primary._http_ctx is not None:
807
891
  id_repr = f"http({self._base_url})"
892
+ elif self._primary._tcp_ctx is not None:
893
+ id_repr = f"tcp({self._tcp_host}:{self._tcp_port})"
808
894
  else:
809
895
  id_repr = "pooled"
810
896
  _logger.debug("server_started id=%s", id_repr)
@@ -1276,10 +1276,25 @@ class Worker:
1276
1276
  "--unix",
1277
1277
  help="Bind to this AF_UNIX socket path instead of stdin/stdout (mutex with --http).",
1278
1278
  ),
1279
+ # TCP launcher contract — mutually exclusive with --http/--unix.
1280
+ # Accepts ``[HOST:]PORT``; host defaults to loopback (127.0.0.1)
1281
+ # and ``PORT`` may be 0 to auto-select a free port. After binding
1282
+ # the worker prints TCP:<host>:<port> to stdout and self-shuts-down
1283
+ # after --idle-timeout seconds with zero connected clients. The raw
1284
+ # framing carries no auth/encryption — bind loopback only; use
1285
+ # --http for untrusted networks.
1286
+ tcp: str | None = typer.Option(
1287
+ None,
1288
+ "--tcp",
1289
+ help=(
1290
+ "Bind a TCP socket ([HOST:]PORT, host defaults to 127.0.0.1, PORT 0 "
1291
+ "auto-selects) instead of stdin/stdout (mutex with --http/--unix)."
1292
+ ),
1293
+ ),
1279
1294
  idle_timeout: float = typer.Option(
1280
1295
  300.0,
1281
1296
  "--idle-timeout",
1282
- help="Self-shutdown after N seconds idle when serving --unix.",
1297
+ help="Self-shutdown after N seconds idle when serving --unix/--tcp.",
1283
1298
  ),
1284
1299
  http_threads: int | None = typer.Option( # noqa: B008
1285
1300
  None,
@@ -1300,8 +1315,8 @@ class Worker:
1300
1315
  log_format=log_format,
1301
1316
  )
1302
1317
 
1303
- if http and unix is not None:
1304
- raise typer.BadParameter("--http and --unix are mutually exclusive")
1318
+ if sum(x for x in (http, unix is not None, tcp is not None)) > 1:
1319
+ raise typer.BadParameter("--http, --unix, and --tcp are mutually exclusive")
1305
1320
 
1306
1321
  if http:
1307
1322
  from vgi.serve import (
@@ -1358,6 +1373,47 @@ class Worker:
1358
1373
  idle_timeout=effective_idle,
1359
1374
  on_bound=_emit,
1360
1375
  )
1376
+ elif tcp is not None:
1377
+ # TCP launcher path. Bind to [HOST:]PORT, print
1378
+ # TCP:<host>:<port> on stdout (mirrors run_server's
1379
+ # cross-language discovery contract), idle-shutdown after
1380
+ # idle_timeout seconds.
1381
+ from vgi_rpc.rpc import serve_tcp
1382
+
1383
+ from vgi.serve import _maybe_init_sentry, _resolve_otel_config
1384
+
1385
+ if ":" in tcp:
1386
+ host_part, _, port_part = tcp.rpartition(":")
1387
+ tcp_host = host_part or "127.0.0.1"
1388
+ else:
1389
+ tcp_host, port_part = "127.0.0.1", tcp
1390
+ try:
1391
+ tcp_port = int(port_part)
1392
+ except ValueError:
1393
+ raise typer.BadParameter(f"--tcp expects [HOST:]PORT, got {tcp!r}") from None
1394
+
1395
+ _maybe_init_sentry()
1396
+ otel_config = _resolve_otel_config()
1397
+ worker = cls(quiet=quiet, log_level=effective_level)
1398
+ server = RpcServer(cls.protocol_class, worker, server_version=_get_vgi_version())
1399
+ if otel_config is not None:
1400
+ from vgi_rpc.otel import instrument_server
1401
+
1402
+ instrument_server(server, otel_config)
1403
+ worker._vgi_tracer = VgiTracer.create(otel_config)
1404
+ effective_idle = idle_timeout if idle_timeout > 0 else None
1405
+
1406
+ def _emit_tcp(bound_host: str, bound_port: int) -> None:
1407
+ print(f"TCP:{bound_host}:{bound_port}", flush=True)
1408
+
1409
+ serve_tcp(
1410
+ server,
1411
+ tcp_host,
1412
+ tcp_port,
1413
+ threaded=True,
1414
+ idle_timeout=effective_idle,
1415
+ on_bound=_emit_tcp,
1416
+ )
1361
1417
  else:
1362
1418
  from vgi.serve import _maybe_init_sentry, _resolve_otel_config
1363
1419
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes