vgi-python 0.24.0__tar.gz → 0.25.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. {vgi_python-0.24.0 → vgi_python-0.25.0}/.github/workflows/ci.yml +5 -5
  2. {vgi_python-0.24.0 → vgi_python-0.25.0}/.github/workflows/docs.yml +1 -1
  3. {vgi_python-0.24.0 → vgi_python-0.25.0}/.github/workflows/integration.yml +3 -3
  4. {vgi_python-0.24.0 → vgi_python-0.25.0}/CLAUDE.md +95 -0
  5. {vgi_python-0.24.0 → vgi_python-0.25.0}/PKG-INFO +2 -2
  6. {vgi_python-0.24.0 → vgi_python-0.25.0}/pyproject.toml +2 -2
  7. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_serve.py +197 -0
  8. vgi_python-0.25.0/tests/test_worker_cli.py +200 -0
  9. {vgi_python-0.24.0 → vgi_python-0.25.0}/uv.lock +5 -5
  10. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/http_server.py +9 -0
  11. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/auth.py +13 -1
  12. vgi_python-0.25.0/vgi/logging_config.py +319 -0
  13. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/serve.py +197 -1
  14. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/worker.py +75 -0
  15. vgi_python-0.24.0/tests/test_worker_cli.py +0 -112
  16. vgi_python-0.24.0/vgi/logging_config.py +0 -161
  17. {vgi_python-0.24.0 → vgi_python-0.25.0}/.gitattributes +0 -0
  18. {vgi_python-0.24.0 → vgi_python-0.25.0}/.github/dependabot.yml +0 -0
  19. {vgi_python-0.24.0 → vgi_python-0.25.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
  20. {vgi_python-0.24.0 → vgi_python-0.25.0}/.github/workflows/release.yml +0 -0
  21. {vgi_python-0.24.0 → vgi_python-0.25.0}/.gitignore +0 -0
  22. {vgi_python-0.24.0 → vgi_python-0.25.0}/.python-version +0 -0
  23. {vgi_python-0.24.0 → vgi_python-0.25.0}/.vale.ini +0 -0
  24. {vgi_python-0.24.0 → vgi_python-0.25.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
  25. {vgi_python-0.24.0 → vgi_python-0.25.0}/DOCS_REVIEW_RUBRIC.md +0 -0
  26. {vgi_python-0.24.0 → vgi_python-0.25.0}/DOCS_USABILITY_TEST.md +0 -0
  27. {vgi_python-0.24.0 → vgi_python-0.25.0}/LICENSE +0 -0
  28. {vgi_python-0.24.0 → vgi_python-0.25.0}/README.md +0 -0
  29. {vgi_python-0.24.0 → vgi_python-0.25.0}/SECURITY.md +0 -0
  30. {vgi_python-0.24.0 → vgi_python-0.25.0}/ci/README.md +0 -0
  31. {vgi_python-0.24.0 → vgi_python-0.25.0}/ci/preprocess-require.awk +0 -0
  32. {vgi_python-0.24.0 → vgi_python-0.25.0}/ci/run-integration.sh +0 -0
  33. {vgi_python-0.24.0 → vgi_python-0.25.0}/dist-vgi/.gitignore +0 -0
  34. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/aggregate-functions.md +0 -0
  35. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/arguments.md +0 -0
  36. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/auth.md +0 -0
  37. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/catalogs.md +0 -0
  38. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/client.md +0 -0
  39. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/exceptions.md +0 -0
  40. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/filters.md +0 -0
  41. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/functions.md +0 -0
  42. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/http.md +0 -0
  43. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/index.md +0 -0
  44. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/metadata.md +0 -0
  45. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/observability.md +0 -0
  46. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/storage.md +0 -0
  47. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/transactor.md +0 -0
  48. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/api/worker.md +0 -0
  49. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/argument-serialization.md +0 -0
  50. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/apple-touch-icon.png +0 -0
  51. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/favicon-16x16.png +0 -0
  52. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/favicon-32x32.png +0 -0
  53. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/favicon.ico +0 -0
  54. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/kinds/aggregate.svg +0 -0
  55. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/kinds/buffering.svg +0 -0
  56. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/kinds/scalar.svg +0 -0
  57. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/kinds/table-in-out.svg +0 -0
  58. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/kinds/table.svg +0 -0
  59. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/logo.png +0 -0
  60. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/assets/social-card.png +0 -0
  61. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/authentication.md +0 -0
  62. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/catalog-interface.md +0 -0
  63. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/cli.md +0 -0
  64. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/column-statistics.md +0 -0
  65. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/concepts/index.md +0 -0
  66. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/contributing-docs.md +0 -0
  67. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/filter-pushdown.md +0 -0
  68. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/generator-api.md +0 -0
  69. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/global-functions.md +0 -0
  70. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/how-to/catalogs.md +0 -0
  71. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/how-to/function-patterns.md +0 -0
  72. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/how-to/http-auth.md +0 -0
  73. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/how-to/index.md +0 -0
  74. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/how-to/pushdown-and-statistics.md +0 -0
  75. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/how-to/state-storage.md +0 -0
  76. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/index.md +0 -0
  77. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/lifecycle.md +0 -0
  78. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/metadata.md +0 -0
  79. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/overrides/main.html +0 -0
  80. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/robots.txt +0 -0
  81. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/shared-storage.md +0 -0
  82. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/stylesheets/extra.css +0 -0
  83. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/tutorial/index.md +0 -0
  84. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/tutorial/scalar.md +0 -0
  85. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/tutorial/table.md +0 -0
  86. {vgi_python-0.24.0 → vgi_python-0.25.0}/docs/vgi-logo.png +0 -0
  87. {vgi_python-0.24.0 → vgi_python-0.25.0}/examples/calc_scalar_worker.py +0 -0
  88. {vgi_python-0.24.0 → vgi_python-0.25.0}/examples/calc_worker.py +0 -0
  89. {vgi_python-0.24.0 → vgi_python-0.25.0}/examples/filter_worker.py +0 -0
  90. {vgi_python-0.24.0 → vgi_python-0.25.0}/examples/greeting_scalar_worker.py +0 -0
  91. {vgi_python-0.24.0 → vgi_python-0.25.0}/examples/row_count_worker.py +0 -0
  92. {vgi_python-0.24.0 → vgi_python-0.25.0}/examples/series_streaming_worker.py +0 -0
  93. {vgi_python-0.24.0 → vgi_python-0.25.0}/examples/sum_worker.py +0 -0
  94. {vgi_python-0.24.0 → vgi_python-0.25.0}/mkdocs.yml +0 -0
  95. {vgi_python-0.24.0 → vgi_python-0.25.0}/packages/vgi-fixtures/LICENSE +0 -0
  96. {vgi_python-0.24.0 → vgi_python-0.25.0}/packages/vgi-fixtures/README.md +0 -0
  97. {vgi_python-0.24.0 → vgi_python-0.25.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
  98. {vgi_python-0.24.0 → vgi_python-0.25.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
  99. {vgi_python-0.24.0 → vgi_python-0.25.0}/packages/vgi-fixtures/pyproject.toml +0 -0
  100. {vgi_python-0.24.0 → vgi_python-0.25.0}/scripts/measure_startup.py +0 -0
  101. {vgi_python-0.24.0 → vgi_python-0.25.0}/scripts/run_all_tests.sh +0 -0
  102. {vgi_python-0.24.0 → vgi_python-0.25.0}/test-data/generate.sh +0 -0
  103. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/__init__.py +0 -0
  104. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/_http_fixtures.py +0 -0
  105. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/__init__.py +0 -0
  106. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_catalog_interface.py +0 -0
  107. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_client_catalog.py +0 -0
  108. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_column_statistics.py +0 -0
  109. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_declarative.py +0 -0
  110. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_example_worker_catalog.py +0 -0
  111. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_integration.py +0 -0
  112. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_required_filters.py +0 -0
  113. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_scan_branches.py +0 -0
  114. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_serialization.py +0 -0
  115. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_setting.py +0 -0
  116. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_storage.py +0 -0
  117. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_time_travel.py +0 -0
  118. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/catalog/test_writable_table.py +0 -0
  119. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/client/__init__.py +0 -0
  120. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/client/test_broken_pipe.py +0 -0
  121. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/client/test_cli.py +0 -0
  122. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/client/test_cli_catalog_functions.py +0 -0
  123. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/client/test_worker_debug.py +0 -0
  124. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/__init__.py +0 -0
  125. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/_stub.py +0 -0
  126. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/conftest.py +0 -0
  127. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_accumulate.py +0 -0
  128. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_aggregate.py +0 -0
  129. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_attach.py +0 -0
  130. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_bearer_auth.py +0 -0
  131. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_const_param_enforcement.py +0 -0
  132. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_container.py +0 -0
  133. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_copy_from.py +0 -0
  134. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_copy_to.py +0 -0
  135. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_directory_parity.py +0 -0
  136. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_function_inventory.py +0 -0
  137. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_github.py +0 -0
  138. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_http_client.py +0 -0
  139. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_http_external_location.py +0 -0
  140. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_http_upload_url.py +0 -0
  141. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_macro.py +0 -0
  142. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_overload.py +0 -0
  143. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_protocol_inventory.py +0 -0
  144. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_protocol_version.py +0 -0
  145. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_resumable_scan.py +0 -0
  146. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_scalar.py +0 -0
  147. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
  148. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_secret.py +0 -0
  149. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_settings.py +0 -0
  150. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_table.py +0 -0
  151. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_table_in_out.py +0 -0
  152. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_view.py +0 -0
  153. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conformance/test_writable.py +0 -0
  154. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/conftest.py +0 -0
  155. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/landing/_checker.py +0 -0
  156. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/landing/describe.expected.json +0 -0
  157. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/landing/describe.schema.json +0 -0
  158. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/scalar/__init__.py +0 -0
  159. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/scalar/test_bernoulli_function.py +0 -0
  160. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/scalar/test_binary_packet_function.py +0 -0
  161. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/scalar/test_client.py +0 -0
  162. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/scalar/test_conditional_message_function.py +0 -0
  163. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/scalar/test_hash_seed_function.py +0 -0
  164. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
  165. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/scalar/test_multiply_function.py +0 -0
  166. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/scalar/test_random_bytes_function.py +0 -0
  167. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/scalar/test_return_secret_value_function.py +0 -0
  168. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/__init__.py +0 -0
  169. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/__init__.py +0 -0
  170. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_constant_columns_function.py +0 -0
  171. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_double_sequence_function.py +0 -0
  172. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_exception_function.py +0 -0
  173. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_filter_echo_function.py +0 -0
  174. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_logging_function.py +0 -0
  175. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
  176. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_partitioned_function.py +0 -0
  177. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_projected_data_function.py +0 -0
  178. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_sequence_function.py +0 -0
  179. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_settings_function.py +0 -0
  180. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_struct_settings_function.py +0 -0
  181. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
  182. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table/test_wide_columns.py +0 -0
  183. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/__init__.py +0 -0
  184. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/generator/__init__.py +0 -0
  185. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
  186. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
  187. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
  188. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
  189. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
  190. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
  191. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
  192. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/test_blended_metadata.py +0 -0
  193. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/table_in_out/test_client.py +0 -0
  194. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_access_log_audit.py +0 -0
  195. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_aggregate_function.py +0 -0
  196. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_argument_spec.py +0 -0
  197. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_auth.py +0 -0
  198. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_bind_exceptions.py +0 -0
  199. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_bind_request_at_clause.py +0 -0
  200. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_bound_storage_conformance.py +0 -0
  201. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_cache_control.py +0 -0
  202. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_call_state_attach.py +0 -0
  203. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_catalog_auth_binding.py +0 -0
  204. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_copy_from_function.py +0 -0
  205. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_copy_to_function.py +0 -0
  206. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_docstrings.py +0 -0
  207. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_documentation_examples.py +0 -0
  208. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_example_function_arg_types.py +0 -0
  209. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_examples_workers.py +0 -0
  210. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_exception_handling.py +0 -0
  211. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_exceptions.py +0 -0
  212. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_filter_pushdown.py +0 -0
  213. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_filter_pushdown_extension.py +0 -0
  214. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_function_storage.py +0 -0
  215. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_function_storage_azure_sql.py +0 -0
  216. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_function_storage_cf_do.py +0 -0
  217. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_function_storage_cf_do_integration.py +0 -0
  218. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_function_storage_conformance.py +0 -0
  219. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_generated_cpp_constants.py +0 -0
  220. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_generated_cpp_protocol_version.py +0 -0
  221. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_generated_cpp_request_builders.py +0 -0
  222. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_generated_cpp_schemas.py +0 -0
  223. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_generated_cpp_secret.py +0 -0
  224. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_generated_go_schemas.py +0 -0
  225. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_generated_protocol_version.py +0 -0
  226. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_generated_schemas_cross_lang.py +0 -0
  227. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_generated_ts_client.py +0 -0
  228. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_generated_ts_schemas.py +0 -0
  229. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_http_demo_storage.py +0 -0
  230. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_http_s3_offload_input.py +0 -0
  231. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_http_s3_offload_output.py +0 -0
  232. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_landing_conformance.py +0 -0
  233. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_metadata.py +0 -0
  234. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_mypy_consolidated.py +0 -0
  235. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_nest_tensor.py +0 -0
  236. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_otel.py +0 -0
  237. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_projection_enforcement.py +0 -0
  238. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_projection_repro.py +0 -0
  239. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_protocol_classes.py +0 -0
  240. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_resolved_secrets.py +0 -0
  241. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_schema_scoped_functions.py +0 -0
  242. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_schema_utils.py +0 -0
  243. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_setting_secret_annotations.py +0 -0
  244. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_signing_key.py +0 -0
  245. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_table_buffering_function.py +0 -0
  246. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_table_function_dynamic_to_string.py +0 -0
  247. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_tcp_transport.py +0 -0
  248. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_type_bounds.py +0 -0
  249. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_union_argument.py +0 -0
  250. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_worker.py +0 -0
  251. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/test_worker_page.py +0 -0
  252. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/transactor/__init__.py +0 -0
  253. {vgi_python-0.24.0 → vgi_python-0.25.0}/tests/transactor/test_transactor.py +0 -0
  254. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/__init__.py +0 -0
  255. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_duckdb.py +0 -0
  256. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_storage_profile.py +0 -0
  257. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/__init__.py +0 -0
  258. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
  259. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
  260. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
  261. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/_common.py +0 -0
  262. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
  263. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
  264. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
  265. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
  266. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
  267. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/same_name.py +0 -0
  268. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
  269. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
  270. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
  271. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
  272. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/attach_options.py +0 -0
  273. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/bad_enum.py +0 -0
  274. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
  275. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/cancellable.py +0 -0
  276. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/catalog.py +0 -0
  277. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/companion.py +0 -0
  278. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/copy_from.py +0 -0
  279. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/copy_to.py +0 -0
  280. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/global_functions.py +0 -0
  281. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
  282. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
  283. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
  284. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
  285. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
  286. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
  287. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
  288. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
  289. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
  290. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
  291. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
  292. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/formatting.py +0 -0
  293. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
  294. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
  295. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
  296. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/same_name.py +0 -0
  297. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
  298. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
  299. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
  300. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
  301. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/simple_writable.py +0 -0
  302. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/__init__.py +0 -0
  303. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/_common.py +0 -0
  304. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
  305. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
  306. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/cache.py +0 -0
  307. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
  308. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/filters.py +0 -0
  309. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
  310. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/make_series.py +0 -0
  311. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/misc.py +0 -0
  312. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
  313. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/pairs.py +0 -0
  314. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
  315. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
  316. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
  317. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/required_filters.py +0 -0
  318. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/same_name_cached.py +0 -0
  319. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/sequence.py +0 -0
  320. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/settings.py +0 -0
  321. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
  322. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
  323. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
  324. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/versioned.py +0 -0
  325. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table/wide.py +0 -0
  326. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table_in_out.py +0 -0
  327. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/table_in_out_same_name.py +0 -0
  328. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/twin_catalogs.py +0 -0
  329. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/versioned.py +0 -0
  330. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
  331. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/worker.py +0 -0
  332. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
  333. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/writable/generic.py +0 -0
  334. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/writable/table.py +0 -0
  335. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/_test_fixtures/writable/worker.py +0 -0
  336. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/aggregate_function.py +0 -0
  337. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/argument_spec.py +0 -0
  338. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/arguments.py +0 -0
  339. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/attach_header.py +0 -0
  340. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/cache_control.py +0 -0
  341. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/catalog/__init__.py +0 -0
  342. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/catalog/_descriptor_spec.py +0 -0
  343. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/catalog/attach_option.py +0 -0
  344. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/catalog/catalog_interface.py +0 -0
  345. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/catalog/descriptors.py +0 -0
  346. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/catalog/duckdb_statistics.py +0 -0
  347. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/catalog/secret_type.py +0 -0
  348. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/catalog/setting.py +0 -0
  349. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/catalog/storage.py +0 -0
  350. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/client/__init__.py +0 -0
  351. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/client/catalog_mixin.py +0 -0
  352. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/client/cli.py +0 -0
  353. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/client/cli_catalog.py +0 -0
  354. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/client/cli_schema.py +0 -0
  355. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/client/cli_table.py +0 -0
  356. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/client/cli_transaction.py +0 -0
  357. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/client/cli_utils.py +0 -0
  358. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/client/cli_view.py +0 -0
  359. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/client/client.py +0 -0
  360. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/__init__.py +0 -0
  361. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/_common.py +0 -0
  362. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/cpp_constants.py +0 -0
  363. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/cpp_protocol_version.py +0 -0
  364. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/cpp_request_builders.py +0 -0
  365. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/cpp_schemas.py +0 -0
  366. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
  367. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/cpp_secret_request_builders.py +0 -0
  368. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/cpp_secret_schemas.py +0 -0
  369. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/go_schemas.py +0 -0
  370. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/protocol_version.py +0 -0
  371. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/ts_client.py +0 -0
  372. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/codegen/ts_schemas.py +0 -0
  373. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/copy_from_function.py +0 -0
  374. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/copy_to_function.py +0 -0
  375. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/exceptions.py +0 -0
  376. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/function.py +0 -0
  377. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/function_storage.py +0 -0
  378. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/function_storage_azure_sql.py +0 -0
  379. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/function_storage_cf_do.py +0 -0
  380. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/http/__init__.py +0 -0
  381. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/http/demo_storage.py +0 -0
  382. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/http/describe_json.py +0 -0
  383. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/http/landing.html +0 -0
  384. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/http/landing_page.py +0 -0
  385. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/http/worker_page.py +0 -0
  386. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/invocation.py +0 -0
  387. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/meta_worker.py +0 -0
  388. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/metadata.py +0 -0
  389. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/otel.py +0 -0
  390. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/profiling.py +0 -0
  391. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/protocol.py +0 -0
  392. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/protocol_version.txt +0 -0
  393. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/py.typed +0 -0
  394. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/scalar_function.py +0 -0
  395. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/schema_utils.py +0 -0
  396. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/secret_protocol.py +0 -0
  397. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/secret_service.py +0 -0
  398. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/table_buffering_function.py +0 -0
  399. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/table_filter_pushdown.py +0 -0
  400. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/table_function.py +0 -0
  401. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/table_in_out_function.py +0 -0
  402. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/transactor/__init__.py +0 -0
  403. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/transactor/_duckdb_compat.py +0 -0
  404. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/transactor/client.py +0 -0
  405. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/transactor/protocol.py +0 -0
  406. {vgi_python-0.24.0 → vgi_python-0.25.0}/vgi/transactor/server.py +0 -0
  407. {vgi_python-0.24.0 → vgi_python-0.25.0}/wrangler.jsonc +0 -0
@@ -83,8 +83,8 @@ jobs:
83
83
  - name: Install uv
84
84
  uses: astral-sh/setup-uv@v8.2.0
85
85
 
86
- - name: Set up Python 3.13
87
- run: uv python install 3.13
86
+ - name: Set up Python 3.14
87
+ run: uv python install 3.14
88
88
 
89
89
  - name: Install dependencies
90
90
  run: uv sync --all-extras --group docs
@@ -141,8 +141,8 @@ jobs:
141
141
  - name: Install uv
142
142
  uses: astral-sh/setup-uv@v8.2.0
143
143
 
144
- - name: Set up Python 3.13
145
- run: uv python install 3.13
144
+ - name: Set up Python 3.14
145
+ run: uv python install 3.14
146
146
 
147
147
  - name: Install test dependencies
148
148
  run: |
@@ -150,7 +150,7 @@ jobs:
150
150
  # local vgi-rpc checkout with the s3/external extras plus boto3.
151
151
  # Subsequent uv run calls must pass --no-sync or uv re-syncs the
152
152
  # env to the lockfile and drops the overlay.
153
- uv sync --all-extras --python 3.13
153
+ uv sync --all-extras --python 3.14
154
154
  uv pip install './vgi-rpc[http,s3,external]' boto3
155
155
 
156
156
  - name: Create test bucket in LocalStack
@@ -21,7 +21,7 @@ jobs:
21
21
  uses: astral-sh/setup-uv@v8.2.0
22
22
 
23
23
  - name: Set up Python
24
- run: uv python install 3.13
24
+ run: uv python install 3.14
25
25
 
26
26
  - name: Install dependencies
27
27
  run: uv sync --group docs
@@ -78,11 +78,11 @@ jobs:
78
78
  - name: Install uv
79
79
  uses: astral-sh/setup-uv@v8.2.0
80
80
 
81
- - name: Set up Python 3.13
82
- run: uv python install 3.13
81
+ - name: Set up Python 3.14
82
+ run: uv python install 3.14
83
83
 
84
84
  - name: Install dependencies (vgi + fixtures + all extras)
85
- run: uv sync --all-extras --python 3.13
85
+ run: uv sync --all-extras --python 3.14
86
86
 
87
87
  - name: Download haybarn-unittest
88
88
  run: |
@@ -297,6 +297,11 @@ vgi-client --input data.parquet --function sum_all_columns --worker vgi-fixture-
297
297
  | `VGI_OAUTH_DEVICE_CODE_CLIENT_ID` | Client ID for device-code flow (optional, URL-safe chars only) |
298
298
  | `VGI_OAUTH_DEVICE_CODE_CLIENT_SECRET` | Client secret for device-code flow (optional, URL-safe chars only) |
299
299
  | `VGI_OAUTH_USE_ID_TOKEN` | When `1`/`true`/`yes`, clients use OIDC `id_token` as Bearer instead of `access_token` |
300
+ | `VGI_INTROSPECT_PRINCIPALS` | Comma-separated principals permitted to call `__introspect_token__`. Required — no permissive default — whenever the worker implements `resolve_token()` (see below) |
301
+ | `VGI_INTROSPECT_RATE_LIMIT` | Introspection requests allowed per caller per second (default 20) |
302
+ | `VGI_WORKER_ACCESS_LOG_SAMPLE` | Fraction of *successful* calls to keep in the access log, `0.0`–`1.0`. Errors are always kept; the decision is per call, so every record of one stream shares a fate |
303
+ | `VGI_WORKER_ACCESS_LOG_ASYNC` | When `1`/`true`/`yes`, emit access-log records from a listener thread. Bounded queue; full means drop, and a crash loses whatever is queued |
304
+ | `VGI_WORKER_ACCESS_LOG_QUEUE_SIZE` | Bound on the async access-log queue (default 10000) |
300
305
  | `VGI_OTEL_ENABLED` | Enable OpenTelemetry instrumentation (`1`/`true`/`yes`) |
301
306
  | `VGI_OTEL_CUSTOM_ATTRIBUTES` | Comma-separated `key=value` pairs for custom span/metric attributes |
302
307
  | `VGI_OTEL_CLAIM_ATTRIBUTES` | Comma-separated `claim_key=span_attr_name` pairs for claim extraction |
@@ -392,6 +397,96 @@ Set `SENTRY_DSN` (and install `vgi[sentry]`) to forward unhandled exceptions to
392
397
 
393
398
  The same enrichment applies to OTel spans when `VGI_OTEL_ENABLED=1` — both backends read from the same `VgiTracer.set_current_span_attributes()` call sites in `vgi/otel.py`. Either, neither, or both can be active in a process.
394
399
 
400
+ ### Serving Behind a Load Balancer
401
+
402
+ `--max-externalized-response-bytes` caps a single *externalized* response — the
403
+ payload uploaded to blob storage and replaced on the wire by a pointer. Set it
404
+ to whatever the load balancer, API gateway, or object-store policy in front of
405
+ the worker will actually carry.
406
+
407
+ ```bash
408
+ vgi-serve my.worker:MyWorker --http --max-externalized-response-bytes 67108864
409
+ ```
410
+
411
+ Unlike `--max-stream-response-bytes`, which governs the wire and is *soft* for
412
+ producer streams (a continuation token carries the overshoot to the next turn),
413
+ this cap is **hard on every method type with no continuation escape** — bytes
414
+ already uploaded cannot be un-uploaded. Default is no cap. When set, the value
415
+ is advertised as `VGI-Max-Externalized-Response-Bytes` so clients can size their
416
+ own expectations.
417
+
418
+ Also available on `Worker.main --http` and the fixture server. Under
419
+ `--server granian` it travels to worker processes through `export_serve_config`.
420
+
421
+ ### Token Introspection (`resolve_token`)
422
+
423
+ A worker may optionally expose `POST {prefix}/__introspect_token__`, which
424
+ resolves an opaque bearer credential to a principal — for a reverse proxy that
425
+ terminates the only public listener and must know the caller's identity before
426
+ it can authorize anything.
427
+
428
+ ```python
429
+ from vgi.auth import AuthUnavailableError, TokenIdentity
430
+ from vgi.worker import Worker
431
+
432
+ class MyWorker(Worker):
433
+ functions = [...]
434
+
435
+ @classmethod
436
+ def resolve_token(cls, token: str) -> TokenIdentity | None:
437
+ try:
438
+ row = api_keys.lookup(token) # your own store
439
+ except ConnectionError as exc:
440
+ # "I could not find out" — 503 + Retry-After, not 401.
441
+ raise AuthUnavailableError(str(exc)) from exc
442
+ if row is None:
443
+ return None # "the credential is unknown"
444
+ return TokenIdentity(principal=row.principal, token_name=row.label)
445
+ ```
446
+
447
+ **The route does not exist until `resolve_token` is overridden** — absent, not
448
+ routed-and-refusing. That is what keeps a dependency upgrade from growing a
449
+ credential-to-identity oracle on every existing worker.
450
+
451
+ Enabling it also requires an allowlist of principals permitted to ask, via
452
+ `--introspect-principals` or `VGI_INTROSPECT_PRINCIPALS`. There is no permissive
453
+ default and a worker that overrides the hook without one **refuses to start**:
454
+ authenticating and introspecting are different capabilities, and "any
455
+ authenticated caller" lets any user resolve any other user's credential to its
456
+ owner. `--introspect-rate-limit` (default 20/caller/second) bounds, rather than
457
+ closes, the oracle an allowlisted-but-compromised caller still has.
458
+
459
+ Return `None` for "the store answered and this credential is unknown"; raise
460
+ `AuthUnavailableError` for "the answer is not knowable". A caller that
461
+ negative-caches the first must not cache the second. Never return claims — a
462
+ pass-through claims field would let a worker choose its caller's tenant routing
463
+ and policy branch.
464
+
465
+ `AuthUnavailableError` is worth reaching for in any custom `authenticate`
466
+ callback too, not just here: it is deliberately **not** a `ValueError`, because
467
+ `chain_authenticate` advances to the next authenticator on `ValueError` — so a
468
+ sidecar outage raised as one is read as "not my credential, try the next" and
469
+ ends up a 401 from the end of the chain, restarting every session in the fleet
470
+ over a thirty-second blip.
471
+
472
+ ### Access Log Sampling and Async Emission
473
+
474
+ `--access-log-sample 0.05` keeps 5% of *successful* calls. Errors are always
475
+ kept, and the decision is made per call so every record belonging to one stream
476
+ shares a fate. `--access-log-async` moves formatting and writing to a listener
477
+ thread so disk latency stays off the request path; the queue is bounded
478
+ (`--access-log-queue-size`, default 10000), full means drop, and the next record
479
+ through carries `dropped_records` so a gap is never silent. A crash loses
480
+ whatever is still queued — that is the trade.
481
+
482
+ Both apply only to `vgi_rpc.access`, which gets its own handler for the purpose.
483
+ They are deliberately not applied to the diagnostic loggers: dropping half of a
484
+ traceback is not a saving.
485
+
486
+ **Trace correlation needs no configuration.** vgi-rpc reads whatever span is
487
+ current when it emits a record, so `trace_id` / `span_id` appear on every access
488
+ record as soon as `VGI_OTEL_ENABLED=1`.
489
+
395
490
  ### Key Constraints for Scalar Functions:
396
491
  - **1:1 row mapping**: Output must have exactly the same number of rows as input
397
492
  - **Single column output**: Output schema has exactly one column named "result"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vgi-python
3
- Version: 0.24.0
3
+ Version: 0.25.0
4
4
  Summary: Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow
5
5
  Project-URL: Homepage, https://query.farm
6
6
  Project-URL: Repository, https://github.com/Query-farm/vgi-python
@@ -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.33.0
165
+ Requires-Dist: vgi-rpc>=0.39.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'
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vgi-python"
3
- version = "0.24.0"
3
+ version = "0.25.0"
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.33.0",
43
+ "vgi-rpc>=0.39.1",
44
44
  "httpx>=0.24",
45
45
  ]
46
46
 
@@ -270,6 +270,203 @@ class TestCreateApp:
270
270
  assert isinstance(app, falcon.App)
271
271
 
272
272
 
273
+ def _capability_headers(app: object) -> dict[str, str]:
274
+ """Read the capability headers a served app advertises on ``/health``.
275
+
276
+ Args:
277
+ app: The Falcon app under test.
278
+
279
+ Returns:
280
+ Response headers, lowercased.
281
+
282
+ """
283
+ import falcon.testing
284
+
285
+ resp = falcon.testing.TestClient(app).simulate_get("/vgi/health")
286
+ assert resp.status_code == 200
287
+ return {k.lower(): v for k, v in resp.headers.items()}
288
+
289
+
290
+ class TestMaxExternalizedResponseBytes:
291
+ """The externalized-response cap, for workers behind a size-limited proxy."""
292
+
293
+ def test_advertised_when_set(self) -> None:
294
+ """The cap reaches the wire, so a client can size its own expectations."""
295
+ app = create_app(_SingleWorker, prefix="/vgi", describe=False, max_externalized_response_bytes=65536)
296
+ assert _capability_headers(app)["vgi-max-externalized-response-bytes"] == "65536"
297
+
298
+ def test_absent_by_default(self) -> None:
299
+ """No cap unless one is configured.
300
+
301
+ The header's absence is what tells a client there is no ceiling; a
302
+ default value here would be a ceiling nobody chose.
303
+ """
304
+ app = create_app(_SingleWorker, prefix="/vgi", describe=False)
305
+ assert "vgi-max-externalized-response-bytes" not in _capability_headers(app)
306
+
307
+
308
+ class _IntrospectingWorker(Worker):
309
+ """Worker that implements the optional token-introspection hook."""
310
+
311
+ functions = [_DoubleFunc]
312
+
313
+ @classmethod
314
+ def resolve_token(cls, token: str) -> object | None:
315
+ from vgi.auth import TokenIdentity
316
+
317
+ if token == "good-token":
318
+ return TokenIdentity(principal="alice", token_name="deploy-key-1")
319
+ return None
320
+
321
+
322
+ class TestIntrospectResolverDetection:
323
+ """``resolve_token`` presence is detected by override, not by a flag."""
324
+
325
+ def test_base_worker_has_no_resolver(self) -> None:
326
+ """A worker that never wrote the lookup gets no endpoint."""
327
+ assert _SingleWorker._introspect_resolver() is None
328
+
329
+ def test_override_is_detected(self) -> None:
330
+ """Implementing the hook is what enables it."""
331
+ resolver = _IntrospectingWorker._introspect_resolver()
332
+ assert resolver is not None
333
+ assert resolver("good-token") is not None
334
+ assert resolver("nope") is None
335
+
336
+
337
+ class TestTokenIntrospection:
338
+ """``POST /__introspect_token__`` — absent unless a worker implements it."""
339
+
340
+ @staticmethod
341
+ def _client(worker_cls: type[Worker], **env: str) -> object:
342
+ import falcon.testing
343
+
344
+ from vgi.serve import _resolve_authenticate
345
+
346
+ with pytest.MonkeyPatch.context() as mp:
347
+ for key in ("VGI_INTROSPECT_PRINCIPALS", "VGI_INTROSPECT_RATE_LIMIT", "VGI_BEARER_TOKENS"):
348
+ mp.delenv(key, raising=False)
349
+ for key, value in env.items():
350
+ mp.setenv(key, value)
351
+ app = create_app(
352
+ worker_cls,
353
+ prefix="/vgi",
354
+ describe=False,
355
+ authenticate=_resolve_authenticate(),
356
+ )
357
+ return falcon.testing.TestClient(app)
358
+
359
+ def test_route_absent_without_hook(self) -> None:
360
+ """Not "routed and refusing" — absent.
361
+
362
+ This is the property that keeps a dependency upgrade from silently
363
+ growing a credential-to-identity oracle on every existing worker.
364
+ """
365
+ client = self._client(_SingleWorker)
366
+ resp = client.simulate_post("/vgi/__introspect_token__", json={"token": "x"}) # type: ignore[attr-defined]
367
+ assert resp.status_code == 404
368
+
369
+ def test_capability_not_advertised_without_hook(self) -> None:
370
+ """A proxy preflighting at boot learns this worker cannot answer."""
371
+ app = create_app(_SingleWorker, prefix="/vgi", describe=False)
372
+ assert "vgi-token-introspection" not in _capability_headers(app)
373
+
374
+ def test_capability_advertised_with_hook(self) -> None:
375
+ """A proxy can discover support at boot rather than at first login."""
376
+ with pytest.MonkeyPatch.context() as mp:
377
+ mp.setenv("VGI_INTROSPECT_PRINCIPALS", "proxy-a")
378
+ app = create_app(_IntrospectingWorker, prefix="/vgi", describe=False)
379
+ assert _capability_headers(app)["vgi-token-introspection"] == "true"
380
+
381
+ def test_allowlisted_caller_resolves(self) -> None:
382
+ """The endpoint answers with a principal and never with claims."""
383
+ client = self._client(
384
+ _IntrospectingWorker,
385
+ VGI_INTROSPECT_PRINCIPALS="proxy-a",
386
+ VGI_BEARER_TOKENS="ptok=proxy-a",
387
+ )
388
+ resp = client.simulate_post( # type: ignore[attr-defined]
389
+ "/vgi/__introspect_token__",
390
+ json={"token": "good-token"},
391
+ headers={"Authorization": "Bearer ptok"},
392
+ )
393
+ assert resp.status_code == 200
394
+ assert resp.json == {"principal": "alice", "token_name": "deploy-key-1", "ttl_seconds": 300}
395
+ assert "claims" not in resp.json
396
+
397
+ def test_unresolvable_token_is_404(self) -> None:
398
+ """Unknown credential and unknown route answer alike, on purpose."""
399
+ client = self._client(
400
+ _IntrospectingWorker,
401
+ VGI_INTROSPECT_PRINCIPALS="proxy-a",
402
+ VGI_BEARER_TOKENS="ptok=proxy-a",
403
+ )
404
+ resp = client.simulate_post( # type: ignore[attr-defined]
405
+ "/vgi/__introspect_token__",
406
+ json={"token": "nope"},
407
+ headers={"Authorization": "Bearer ptok"},
408
+ )
409
+ assert resp.status_code == 404
410
+
411
+ def test_non_allowlisted_caller_refused(self) -> None:
412
+ """Authenticating is not the same capability as introspecting.
413
+
414
+ Without this, any user holding a valid credential could resolve any
415
+ other user's credential to its owner.
416
+ """
417
+ client = self._client(
418
+ _IntrospectingWorker,
419
+ VGI_INTROSPECT_PRINCIPALS="proxy-a",
420
+ VGI_BEARER_TOKENS="ptok=proxy-a,utok=user-b",
421
+ )
422
+ resp = client.simulate_post( # type: ignore[attr-defined]
423
+ "/vgi/__introspect_token__",
424
+ json={"token": "good-token"},
425
+ headers={"Authorization": "Bearer utok"},
426
+ )
427
+ assert resp.status_code == 403
428
+
429
+ def test_hook_without_allowlist_refuses_to_start(self) -> None:
430
+ """Fail closed and loud, rather than defaulting to an open oracle."""
431
+ with pytest.MonkeyPatch.context() as mp:
432
+ mp.delenv("VGI_INTROSPECT_PRINCIPALS", raising=False)
433
+ with pytest.raises(SystemExit) as exc:
434
+ create_app(_IntrospectingWorker, prefix="/vgi", describe=False)
435
+ assert exc.value.code == 1
436
+
437
+ @pytest.mark.parametrize("bad", ["nonsense", "0", "-5"])
438
+ def test_bad_rate_limit_refuses_to_start(self, bad: str) -> None:
439
+ """A typo must not silently become "refuse everything" or "no bound"."""
440
+ with pytest.MonkeyPatch.context() as mp:
441
+ mp.setenv("VGI_INTROSPECT_PRINCIPALS", "proxy-a")
442
+ mp.setenv("VGI_INTROSPECT_RATE_LIMIT", bad)
443
+ with pytest.raises(SystemExit):
444
+ create_app(_IntrospectingWorker, prefix="/vgi", describe=False)
445
+
446
+
447
+ class TestAuthUnavailableReExport:
448
+ """``AuthUnavailableError`` is reachable without naming a vgi_rpc module."""
449
+
450
+ def test_importable_from_vgi_auth(self) -> None:
451
+ """Worker authors reach it from ``vgi.auth`` like every other auth type."""
452
+ from vgi_rpc.http import AuthUnavailableError as Upstream
453
+
454
+ from vgi.auth import AuthUnavailableError
455
+
456
+ assert AuthUnavailableError is Upstream
457
+
458
+ def test_is_not_a_value_error(self) -> None:
459
+ """The whole point: ``chain_authenticate`` must not swallow it.
460
+
461
+ The chain advances to the next authenticator on ``ValueError``, so an
462
+ outage raised as one is read as "not my credential, try the next" and
463
+ ends up a 401 — restarting every session in the fleet over a blip.
464
+ """
465
+ from vgi.auth import AuthUnavailableError
466
+
467
+ assert not issubclass(AuthUnavailableError, ValueError)
468
+
469
+
273
470
  # ---------------------------------------------------------------------------
274
471
  # Tests: env var helpers
275
472
  # ---------------------------------------------------------------------------
@@ -0,0 +1,200 @@
1
+ # Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+
3
+ """Tests for Worker.main() CLI logging options."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ from typing import TYPE_CHECKING
9
+
10
+ import pytest
11
+
12
+ from vgi.logging_config import (
13
+ _KNOWN_LOGGERS,
14
+ ACCESS_LOGGER,
15
+ LogFormat,
16
+ LogLevel,
17
+ configure_worker_logging,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ from collections.abc import Iterator
22
+
23
+ # All known logger names from the registry
24
+ _ALL_LOGGER_NAMES = [name for name, _, _ in _KNOWN_LOGGERS]
25
+
26
+
27
+ @pytest.fixture(autouse=False)
28
+ def _reset_loggers() -> Iterator[None]:
29
+ """Save and restore logger handlers, levels and propagation after each test.
30
+
31
+ ``propagate`` is restored too because the access-log options turn it off on
32
+ ``vgi_rpc.access``; leaving that set would silently suppress access records
33
+ for every later test in the session.
34
+ """
35
+ saved: dict[str, tuple[int, list[logging.Handler], bool]] = {}
36
+ for name in _ALL_LOGGER_NAMES:
37
+ logger = logging.getLogger(name)
38
+ saved[name] = (logger.level, list(logger.handlers), logger.propagate)
39
+ yield
40
+ for name in _ALL_LOGGER_NAMES:
41
+ logger = logging.getLogger(name)
42
+ level, handlers, propagate = saved[name]
43
+ logger.handlers[:] = handlers
44
+ logger.setLevel(level)
45
+ logger.propagate = propagate
46
+
47
+
48
+ class TestConfigureWorkerLogging:
49
+ """Tests for configure_worker_logging() options."""
50
+
51
+ def test_debug_flag(self, _reset_loggers: None) -> None:
52
+ """``--debug`` sets vgi + vgi_rpc loggers to DEBUG."""
53
+ configure_worker_logging(debug=True)
54
+ for name in ("vgi", "vgi_rpc"):
55
+ logger = logging.getLogger(name)
56
+ assert logger.level == logging.DEBUG
57
+ assert len(logger.handlers) == 1
58
+
59
+ def test_log_level_option(self, _reset_loggers: None) -> None:
60
+ """``--log-level WARNING`` sets correct level."""
61
+ configure_worker_logging(log_level=LogLevel.WARNING)
62
+ for name in ("vgi", "vgi_rpc"):
63
+ logger = logging.getLogger(name)
64
+ assert logger.level == logging.WARNING
65
+
66
+ def test_log_logger_targeting(self, _reset_loggers: None) -> None:
67
+ """``--log-logger vgi.worker`` targets only that logger."""
68
+ configure_worker_logging(log_level=LogLevel.DEBUG, log_loggers=["vgi.worker"])
69
+ target = logging.getLogger("vgi.worker")
70
+ assert target.level == logging.DEBUG
71
+ assert len(target.handlers) == 1
72
+ # Root vgi logger should not have been modified
73
+ root = logging.getLogger("vgi")
74
+ assert root.handlers == [] or root.level != logging.DEBUG
75
+
76
+ def test_log_format_json(self, _reset_loggers: None) -> None:
77
+ """``--log-format json`` uses VgiJsonFormatter."""
78
+ from vgi_rpc.logging_utils import VgiJsonFormatter
79
+
80
+ configure_worker_logging(debug=True, log_format=LogFormat.json)
81
+ logger = logging.getLogger("vgi")
82
+ assert any(isinstance(h.formatter, VgiJsonFormatter) for h in logger.handlers)
83
+
84
+ def test_debug_overrides_log_level(self, _reset_loggers: None) -> None:
85
+ """``--debug --log-level INFO`` resolves to DEBUG."""
86
+ configure_worker_logging(debug=True, log_level=LogLevel.INFO)
87
+ logger = logging.getLogger("vgi")
88
+ assert logger.level == logging.DEBUG
89
+
90
+ def test_unknown_logger_warning(self, _reset_loggers: None, capsys: pytest.CaptureFixture[str]) -> None:
91
+ """Unrecognized logger name warns on stderr."""
92
+ configure_worker_logging(log_level=LogLevel.DEBUG, log_loggers=["not.a.real.logger"])
93
+ captured = capsys.readouterr()
94
+ assert "warning: unknown logger 'not.a.real.logger'" in captured.err
95
+
96
+ def test_configure_idempotent(self, _reset_loggers: None) -> None:
97
+ """Calling configure_worker_logging() twice doesn't duplicate handlers."""
98
+ configure_worker_logging(debug=True)
99
+ configure_worker_logging(debug=True)
100
+ logger = logging.getLogger("vgi")
101
+ assert len(logger.handlers) == 1
102
+
103
+ def test_returns_effective_level(self, _reset_loggers: None) -> None:
104
+ """Return value is the numeric log level."""
105
+ level = configure_worker_logging(log_level=LogLevel.WARNING)
106
+ assert level == logging.WARNING
107
+
108
+ level = configure_worker_logging(debug=True, log_level=LogLevel.WARNING)
109
+ assert level == logging.DEBUG
110
+
111
+ def test_text_format_has_timestamp(self, _reset_loggers: None) -> None:
112
+ """Text format includes asctime in the formatter."""
113
+ configure_worker_logging(log_format=LogFormat.text)
114
+ logger = logging.getLogger("vgi")
115
+ assert len(logger.handlers) == 1
116
+ fmt = logger.handlers[0].formatter
117
+ assert fmt is not None
118
+ assert fmt._fmt is not None
119
+ assert "asctime" in fmt._fmt
120
+
121
+
122
+ class TestAccessLogOptions:
123
+ """Sampling and async emission for ``vgi_rpc.access``."""
124
+
125
+ def test_untouched_by_default(self, _reset_loggers: None) -> None:
126
+ """No options means no dedicated handler — records still propagate."""
127
+ configure_worker_logging(debug=True)
128
+ access = logging.getLogger(ACCESS_LOGGER)
129
+ assert access.handlers == []
130
+ assert access.propagate is True
131
+
132
+ def test_sampling_attaches_filter(self, _reset_loggers: None) -> None:
133
+ """A sample rate below 1.0 installs the sampler on the access logger."""
134
+ from vgi_rpc.logging_utils import AccessLogSampler
135
+
136
+ configure_worker_logging(debug=True, access_log_sample=0.25)
137
+ access = logging.getLogger(ACCESS_LOGGER)
138
+ assert len(access.handlers) == 1
139
+ assert any(isinstance(f, AccessLogSampler) for f in access.handlers[0].filters)
140
+
141
+ def test_sampling_does_not_touch_diagnostic_loggers(self, _reset_loggers: None) -> None:
142
+ """Dropping a fraction of tracebacks is not a saving.
143
+
144
+ The access logger gets its own handler precisely so the sampler cannot
145
+ reach ``vgi`` / ``vgi_rpc``, which share the stderr handler.
146
+ """
147
+ from vgi_rpc.logging_utils import AccessLogSampler
148
+
149
+ configure_worker_logging(debug=True, access_log_sample=0.25)
150
+ for name in ("vgi", "vgi_rpc"):
151
+ for handler in logging.getLogger(name).handlers:
152
+ assert not any(isinstance(f, AccessLogSampler) for f in handler.filters)
153
+
154
+ def test_sampling_stops_propagation(self, _reset_loggers: None) -> None:
155
+ """Otherwise every surviving record is written twice."""
156
+ configure_worker_logging(debug=True, access_log_sample=0.5)
157
+ assert logging.getLogger(ACCESS_LOGGER).propagate is False
158
+
159
+ def test_async_uses_queue_handler(self, _reset_loggers: None) -> None:
160
+ """Async emission puts a bounded queue on the request thread's path."""
161
+ from vgi_rpc.logging_utils import DroppingQueueHandler
162
+
163
+ configure_worker_logging(debug=True, access_log_async=True, access_log_queue_size=64)
164
+ access = logging.getLogger(ACCESS_LOGGER)
165
+ assert len(access.handlers) == 1
166
+ assert isinstance(access.handlers[0], DroppingQueueHandler)
167
+
168
+ def test_json_format_carries_to_access_handler(self, _reset_loggers: None) -> None:
169
+ """The dedicated handler must not silently revert to text output."""
170
+ from vgi_rpc.logging_utils import VgiJsonFormatter
171
+
172
+ configure_worker_logging(debug=True, log_format=LogFormat.json, access_log_sample=0.5)
173
+ access = logging.getLogger(ACCESS_LOGGER)
174
+ assert isinstance(access.handlers[0].formatter, VgiJsonFormatter)
175
+
176
+ @pytest.mark.parametrize("rate", [-0.1, 1.5])
177
+ def test_out_of_range_sample_rate_exits(self, _reset_loggers: None, rate: float) -> None:
178
+ """A typo here silently changes how much of the audit trail survives."""
179
+ with pytest.raises(SystemExit):
180
+ configure_worker_logging(debug=True, access_log_sample=rate)
181
+
182
+ def test_non_positive_queue_size_exits(self, _reset_loggers: None) -> None:
183
+ """A zero-length queue would drop every record."""
184
+ with pytest.raises(SystemExit):
185
+ configure_worker_logging(debug=True, access_log_async=True, access_log_queue_size=0)
186
+
187
+ def test_env_overrides_win(self, _reset_loggers: None, monkeypatch: pytest.MonkeyPatch) -> None:
188
+ """Reachable without owning argv, like the other VGI_WORKER_LOG_* vars."""
189
+ from vgi_rpc.logging_utils import AccessLogSampler
190
+
191
+ monkeypatch.setenv("VGI_WORKER_ACCESS_LOG_SAMPLE", "0.1")
192
+ configure_worker_logging(debug=True)
193
+ access = logging.getLogger(ACCESS_LOGGER)
194
+ assert any(isinstance(f, AccessLogSampler) for f in access.handlers[0].filters)
195
+
196
+ def test_malformed_env_exits(self, _reset_loggers: None, monkeypatch: pytest.MonkeyPatch) -> None:
197
+ """A typo must not leave sampling silently off."""
198
+ monkeypatch.setenv("VGI_WORKER_ACCESS_LOG_SAMPLE", "half")
199
+ with pytest.raises(SystemExit):
200
+ configure_worker_logging(debug=True)
@@ -2352,7 +2352,7 @@ requires-dist = [
2352
2352
 
2353
2353
  [[package]]
2354
2354
  name = "vgi-python"
2355
- version = "0.24.0"
2355
+ version = "0.25.0"
2356
2356
  source = { editable = "." }
2357
2357
  dependencies = [
2358
2358
  { name = "click" },
@@ -2467,7 +2467,7 @@ requires-dist = [
2467
2467
  { name = "vgi-python", extras = ["test-fixtures"], marker = "extra == 'test-fixtures-writable'" },
2468
2468
  { name = "vgi-python", extras = ["test-fixtures", "test-fixtures-writable", "http", "oauth", "otel", "sentry", "azure"], marker = "extra == 'dev'" },
2469
2469
  { name = "vgi-python", extras = ["transactor"], marker = "extra == 'test-fixtures-writable'" },
2470
- { name = "vgi-rpc", specifier = ">=0.33.0" },
2470
+ { name = "vgi-rpc", specifier = ">=0.39.1" },
2471
2471
  { name = "vgi-rpc", extras = ["conformance", "external", "http", "oauth", "otel", "sentry"], marker = "extra == 'dev'" },
2472
2472
  { name = "vgi-rpc", extras = ["http"], marker = "extra == 'granian'" },
2473
2473
  { name = "vgi-rpc", extras = ["http"], marker = "extra == 'http'" },
@@ -2497,7 +2497,7 @@ docs = [
2497
2497
 
2498
2498
  [[package]]
2499
2499
  name = "vgi-rpc"
2500
- version = "0.33.0"
2500
+ version = "0.39.1"
2501
2501
  source = { registry = "https://pypi.org/simple" }
2502
2502
  dependencies = [
2503
2503
  { name = "docstring-parser" },
@@ -2505,9 +2505,9 @@ dependencies = [
2505
2505
  { name = "pywin32", marker = "sys_platform == 'win32'" },
2506
2506
  { name = "tzdata", marker = "sys_platform == 'win32'" },
2507
2507
  ]
2508
- sdist = { url = "https://files.pythonhosted.org/packages/06/5f/8510cc6494dc9183711141b544840f087cfb68ddb3c01d907610101669b3/vgi_rpc-0.33.0.tar.gz", hash = "sha256:90e6221ce481d43fcf45d07d011c3d13cbcb6103a64675dae3fdf0c25e09dd11", size = 986710, upload-time = "2026-08-04T00:20:02.458Z" }
2508
+ sdist = { url = "https://files.pythonhosted.org/packages/72/7d/a4dc94670c7b29eb0d55f3965909d627e4a5e4beadf7787622a049197db7/vgi_rpc-0.39.1.tar.gz", hash = "sha256:3eb71657f34ddc608db817785b596f2653d35341ec57c40481a9fa37ceefe1e7", size = 1060176, upload-time = "2026-08-05T04:22:33.766Z" }
2509
2509
  wheels = [
2510
- { url = "https://files.pythonhosted.org/packages/57/c5/06b7b105abb26667eb47224aa3fb730bd1664686f0794f106b7017f3a8dc/vgi_rpc-0.33.0-py3-none-any.whl", hash = "sha256:1a3ac9e190b4c4625f7d60caf5f85a8a94586ab5786494c9197f080e62c25513", size = 443084, upload-time = "2026-08-04T00:20:00.259Z" },
2510
+ { url = "https://files.pythonhosted.org/packages/60/18/d29ffcb0e3b69ca8d8d7950f99ba5ee6ad9c68e07ce70457bba6567f9859/vgi_rpc-0.39.1-py3-none-any.whl", hash = "sha256:dc2baab02dfc7df37456688e6d4c6cde7c7428bbbb2733e37511b1e950754aca", size = 482199, upload-time = "2026-08-05T04:22:31.935Z" },
2511
2511
  ]
2512
2512
 
2513
2513
  [package.optional-dependencies]
@@ -179,6 +179,14 @@ def main() -> None:
179
179
  "--demo-storage",
180
180
  help="Enable in-process blob storage for externalized payloads (no S3 required)",
181
181
  ),
182
+ max_externalized_response_bytes: int | None = typer.Option( # noqa: B008
183
+ None,
184
+ "--max-externalized-response-bytes",
185
+ help=(
186
+ "Cap on a single externalized response. Hard on every method type, "
187
+ "with no continuation escape. Default: no cap."
188
+ ),
189
+ ),
182
190
  port_file: str | None = typer.Option(
183
191
  None,
184
192
  "--port-file",
@@ -379,6 +387,7 @@ def main() -> None:
379
387
  upload_url_provider=upload_url_provider,
380
388
  max_upload_bytes=max_upload_bytes if upload_url_provider is not None else None,
381
389
  max_request_bytes=max_request_bytes,
390
+ max_externalized_response_bytes=max_externalized_response_bytes,
382
391
  authenticate=authenticate,
383
392
  oauth_resource_metadata=oauth_metadata,
384
393
  enable_landing_page=False,