vgi-python 0.8.2__tar.gz → 0.8.4__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 (361) hide show
  1. {vgi_python-0.8.2 → vgi_python-0.8.4}/PKG-INFO +1 -1
  2. {vgi_python-0.8.2 → vgi_python-0.8.4}/pyproject.toml +1 -1
  3. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_declarative.py +33 -0
  4. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_example_worker_catalog.py +7 -0
  5. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_metadata.py +115 -0
  6. vgi_python-0.8.4/tests/test_union_argument.py +111 -0
  7. {vgi_python-0.8.2 → vgi_python-0.8.4}/uv.lock +1 -1
  8. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/__init__.py +2 -0
  9. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/_common.py +2 -2
  10. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/worker.py +1 -0
  11. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/arguments.py +55 -2
  12. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/catalog_interface.py +1 -0
  13. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/descriptors.py +5 -0
  14. {vgi_python-0.8.2 → vgi_python-0.8.4}/.gitattributes +0 -0
  15. {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/dependabot.yml +0 -0
  16. {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
  17. {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/workflows/ci.yml +0 -0
  18. {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/workflows/docs.yml +0 -0
  19. {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/workflows/integration.yml +0 -0
  20. {vgi_python-0.8.2 → vgi_python-0.8.4}/.github/workflows/release.yml +0 -0
  21. {vgi_python-0.8.2 → vgi_python-0.8.4}/.gitignore +0 -0
  22. {vgi_python-0.8.2 → vgi_python-0.8.4}/.python-version +0 -0
  23. {vgi_python-0.8.2 → vgi_python-0.8.4}/.vale.ini +0 -0
  24. {vgi_python-0.8.2 → vgi_python-0.8.4}/CLAUDE.md +0 -0
  25. {vgi_python-0.8.2 → vgi_python-0.8.4}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
  26. {vgi_python-0.8.2 → vgi_python-0.8.4}/DOCS_REVIEW_RUBRIC.md +0 -0
  27. {vgi_python-0.8.2 → vgi_python-0.8.4}/DOCS_USABILITY_TEST.md +0 -0
  28. {vgi_python-0.8.2 → vgi_python-0.8.4}/LICENSE +0 -0
  29. {vgi_python-0.8.2 → vgi_python-0.8.4}/README.md +0 -0
  30. {vgi_python-0.8.2 → vgi_python-0.8.4}/SECURITY.md +0 -0
  31. {vgi_python-0.8.2 → vgi_python-0.8.4}/ci/README.md +0 -0
  32. {vgi_python-0.8.2 → vgi_python-0.8.4}/ci/preprocess-require.awk +0 -0
  33. {vgi_python-0.8.2 → vgi_python-0.8.4}/ci/run-integration.sh +0 -0
  34. {vgi_python-0.8.2 → vgi_python-0.8.4}/dist-vgi/.gitignore +0 -0
  35. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/aggregate-functions.md +0 -0
  36. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/arguments.md +0 -0
  37. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/auth.md +0 -0
  38. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/catalogs.md +0 -0
  39. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/client.md +0 -0
  40. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/exceptions.md +0 -0
  41. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/filters.md +0 -0
  42. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/functions.md +0 -0
  43. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/http.md +0 -0
  44. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/index.md +0 -0
  45. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/metadata.md +0 -0
  46. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/observability.md +0 -0
  47. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/storage.md +0 -0
  48. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/transactor.md +0 -0
  49. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/api/worker.md +0 -0
  50. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/argument-serialization.md +0 -0
  51. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/apple-touch-icon.png +0 -0
  52. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/favicon-16x16.png +0 -0
  53. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/favicon-32x32.png +0 -0
  54. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/favicon.ico +0 -0
  55. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/kinds/aggregate.svg +0 -0
  56. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/kinds/buffering.svg +0 -0
  57. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/kinds/scalar.svg +0 -0
  58. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/kinds/table-in-out.svg +0 -0
  59. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/kinds/table.svg +0 -0
  60. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/logo.png +0 -0
  61. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/assets/social-card.png +0 -0
  62. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/authentication.md +0 -0
  63. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/catalog-interface.md +0 -0
  64. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/cli.md +0 -0
  65. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/column-statistics.md +0 -0
  66. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/concepts/index.md +0 -0
  67. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/contributing-docs.md +0 -0
  68. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/filter-pushdown.md +0 -0
  69. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/generator-api.md +0 -0
  70. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/catalogs.md +0 -0
  71. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/function-patterns.md +0 -0
  72. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/http-auth.md +0 -0
  73. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/index.md +0 -0
  74. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/pushdown-and-statistics.md +0 -0
  75. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/how-to/state-storage.md +0 -0
  76. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/index.md +0 -0
  77. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/lifecycle.md +0 -0
  78. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/metadata.md +0 -0
  79. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/overrides/main.html +0 -0
  80. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/robots.txt +0 -0
  81. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/shared-storage.md +0 -0
  82. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/stylesheets/extra.css +0 -0
  83. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/tutorial/index.md +0 -0
  84. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/tutorial/scalar.md +0 -0
  85. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/tutorial/table.md +0 -0
  86. {vgi_python-0.8.2 → vgi_python-0.8.4}/docs/vgi-logo.png +0 -0
  87. {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/calc_scalar_worker.py +0 -0
  88. {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/calc_worker.py +0 -0
  89. {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/filter_worker.py +0 -0
  90. {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/greeting_scalar_worker.py +0 -0
  91. {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/row_count_worker.py +0 -0
  92. {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/series_streaming_worker.py +0 -0
  93. {vgi_python-0.8.2 → vgi_python-0.8.4}/examples/sum_worker.py +0 -0
  94. {vgi_python-0.8.2 → vgi_python-0.8.4}/mkdocs.yml +0 -0
  95. {vgi_python-0.8.2 → vgi_python-0.8.4}/packages/vgi-fixtures/LICENSE +0 -0
  96. {vgi_python-0.8.2 → vgi_python-0.8.4}/packages/vgi-fixtures/README.md +0 -0
  97. {vgi_python-0.8.2 → vgi_python-0.8.4}/packages/vgi-fixtures/pyproject.toml +0 -0
  98. {vgi_python-0.8.2 → vgi_python-0.8.4}/scripts/measure_startup.py +0 -0
  99. {vgi_python-0.8.2 → vgi_python-0.8.4}/scripts/run_all_tests.sh +0 -0
  100. {vgi_python-0.8.2 → vgi_python-0.8.4}/test-data/generate.sh +0 -0
  101. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/__init__.py +0 -0
  102. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/_http_fixtures.py +0 -0
  103. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/__init__.py +0 -0
  104. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_catalog_interface.py +0 -0
  105. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_client_catalog.py +0 -0
  106. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_column_statistics.py +0 -0
  107. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_integration.py +0 -0
  108. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_required_field_filter_paths.py +0 -0
  109. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_scan_branches.py +0 -0
  110. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_serialization.py +0 -0
  111. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_setting.py +0 -0
  112. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_storage.py +0 -0
  113. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_time_travel.py +0 -0
  114. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/catalog/test_writable_table.py +0 -0
  115. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/client/__init__.py +0 -0
  116. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/client/test_broken_pipe.py +0 -0
  117. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/client/test_cli.py +0 -0
  118. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/client/test_cli_catalog_functions.py +0 -0
  119. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/client/test_worker_debug.py +0 -0
  120. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/__init__.py +0 -0
  121. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/_stub.py +0 -0
  122. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/conftest.py +0 -0
  123. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_accumulate.py +0 -0
  124. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_aggregate.py +0 -0
  125. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_attach.py +0 -0
  126. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_bearer_auth.py +0 -0
  127. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_directory_parity.py +0 -0
  128. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_function_inventory.py +0 -0
  129. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_http_client.py +0 -0
  130. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_http_external_location.py +0 -0
  131. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_http_upload_url.py +0 -0
  132. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_macro.py +0 -0
  133. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_overload.py +0 -0
  134. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_protocol_inventory.py +0 -0
  135. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_protocol_version.py +0 -0
  136. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_resumable_scan.py +0 -0
  137. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_scalar.py +0 -0
  138. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
  139. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_secret.py +0 -0
  140. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_settings.py +0 -0
  141. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_table.py +0 -0
  142. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_table_in_out.py +0 -0
  143. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_view.py +0 -0
  144. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conformance/test_writable.py +0 -0
  145. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/conftest.py +0 -0
  146. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/__init__.py +0 -0
  147. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_bernoulli_function.py +0 -0
  148. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_binary_packet_function.py +0 -0
  149. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_client.py +0 -0
  150. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_conditional_message_function.py +0 -0
  151. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_hash_seed_function.py +0 -0
  152. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_multiply_by_setting_function.py +0 -0
  153. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_multiply_function.py +0 -0
  154. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_random_bytes_function.py +0 -0
  155. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/scalar/test_return_secret_value_function.py +0 -0
  156. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/__init__.py +0 -0
  157. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/__init__.py +0 -0
  158. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_constant_columns_function.py +0 -0
  159. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_double_sequence_function.py +0 -0
  160. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_exception_function.py +0 -0
  161. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_filter_echo_function.py +0 -0
  162. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_logging_function.py +0 -0
  163. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_nested_sequence_function.py +0 -0
  164. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_partitioned_function.py +0 -0
  165. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_projected_data_function.py +0 -0
  166. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_sequence_function.py +0 -0
  167. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_settings_function.py +0 -0
  168. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_struct_settings_function.py +0 -0
  169. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table/generator/test_ten_thousand_function.py +0 -0
  170. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/__init__.py +0 -0
  171. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/__init__.py +0 -0
  172. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
  173. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_echo_function.py +0 -0
  174. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_exception_functions.py +0 -0
  175. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
  176. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
  177. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
  178. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/table_in_out/test_client.py +0 -0
  179. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_access_log_audit.py +0 -0
  180. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_aggregate_function.py +0 -0
  181. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_argument_spec.py +0 -0
  182. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_auth.py +0 -0
  183. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_bind_exceptions.py +0 -0
  184. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_bind_request_at_clause.py +0 -0
  185. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_bound_storage_conformance.py +0 -0
  186. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_catalog_auth_binding.py +0 -0
  187. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_docstrings.py +0 -0
  188. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_documentation_examples.py +0 -0
  189. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_example_function_arg_types.py +0 -0
  190. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_examples_workers.py +0 -0
  191. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_exception_handling.py +0 -0
  192. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_exceptions.py +0 -0
  193. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_filter_pushdown.py +0 -0
  194. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_filter_pushdown_extension.py +0 -0
  195. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_function_storage.py +0 -0
  196. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_function_storage_azure_sql.py +0 -0
  197. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_function_storage_cf_do.py +0 -0
  198. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_function_storage_cf_do_integration.py +0 -0
  199. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_function_storage_conformance.py +0 -0
  200. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_cpp_constants.py +0 -0
  201. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_cpp_protocol_version.py +0 -0
  202. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_cpp_request_builders.py +0 -0
  203. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_cpp_schemas.py +0 -0
  204. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_cpp_secret.py +0 -0
  205. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_go_schemas.py +0 -0
  206. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_protocol_version.py +0 -0
  207. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_schemas_cross_lang.py +0 -0
  208. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_ts_client.py +0 -0
  209. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_generated_ts_schemas.py +0 -0
  210. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_http_demo_storage.py +0 -0
  211. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_http_s3_offload_input.py +0 -0
  212. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_http_s3_offload_output.py +0 -0
  213. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_mypy_consolidated.py +0 -0
  214. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_nest_tensor.py +0 -0
  215. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_otel.py +0 -0
  216. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_projection_enforcement.py +0 -0
  217. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_projection_repro.py +0 -0
  218. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_protocol_classes.py +0 -0
  219. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_schema_utils.py +0 -0
  220. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_serve.py +0 -0
  221. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_setting_secret_annotations.py +0 -0
  222. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_table_buffering_function.py +0 -0
  223. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_table_function_dynamic_to_string.py +0 -0
  224. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_type_bounds.py +0 -0
  225. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_worker.py +0 -0
  226. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_worker_cli.py +0 -0
  227. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/test_worker_page.py +0 -0
  228. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/transactor/__init__.py +0 -0
  229. {vgi_python-0.8.2 → vgi_python-0.8.4}/tests/transactor/test_transactor.py +0 -0
  230. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_duckdb.py +0 -0
  231. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_storage_profile.py +0 -0
  232. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/__init__.py +0 -0
  233. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
  234. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/accumulate/worker.py +0 -0
  235. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
  236. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/_common.py +0 -0
  237. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/basic.py +0 -0
  238. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
  239. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/generic.py +0 -0
  240. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
  241. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
  242. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
  243. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
  244. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/aggregate/window.py +0 -0
  245. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/attach_options.py +0 -0
  246. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/bad_enum.py +0 -0
  247. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/bad_protocol.py +0 -0
  248. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/cancellable.py +0 -0
  249. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/catalog.py +0 -0
  250. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/http_server.py +0 -0
  251. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
  252. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
  253. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/nest_tensor.py +0 -0
  254. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/orchard_catalog.py +0 -0
  255. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
  256. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
  257. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/__init__.py +0 -0
  258. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/_common.py +0 -0
  259. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
  260. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/binary.py +0 -0
  261. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/formatting.py +0 -0
  262. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/geo.py +0 -0
  263. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
  264. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
  265. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
  266. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/scalar/type_info.py +0 -0
  267. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
  268. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
  269. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/simple_writable.py +0 -0
  270. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/__init__.py +0 -0
  271. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/batch_index.py +0 -0
  272. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
  273. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
  274. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/filters.py +0 -0
  275. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/late_materialization.py +0 -0
  276. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/make_series.py +0 -0
  277. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/misc.py +0 -0
  278. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/order_modes.py +0 -0
  279. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/pairs.py +0 -0
  280. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/partition_columns.py +0 -0
  281. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
  282. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/profiling_example.py +0 -0
  283. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/required_filters.py +0 -0
  284. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/sequence.py +0 -0
  285. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/settings.py +0 -0
  286. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
  287. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
  288. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/typed_probe.py +0 -0
  289. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table/versioned.py +0 -0
  290. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/table_in_out.py +0 -0
  291. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/versioned.py +0 -0
  292. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/versioned_tables.py +0 -0
  293. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/writable/__init__.py +0 -0
  294. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/writable/generic.py +0 -0
  295. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/writable/table.py +0 -0
  296. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/_test_fixtures/writable/worker.py +0 -0
  297. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/aggregate_function.py +0 -0
  298. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/argument_spec.py +0 -0
  299. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/auth.py +0 -0
  300. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/__init__.py +0 -0
  301. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/_descriptor_spec.py +0 -0
  302. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/attach_option.py +0 -0
  303. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/duckdb_statistics.py +0 -0
  304. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/secret_type.py +0 -0
  305. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/setting.py +0 -0
  306. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/catalog/storage.py +0 -0
  307. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/__init__.py +0 -0
  308. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/catalog_mixin.py +0 -0
  309. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli.py +0 -0
  310. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_catalog.py +0 -0
  311. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_schema.py +0 -0
  312. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_table.py +0 -0
  313. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_transaction.py +0 -0
  314. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_utils.py +0 -0
  315. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/cli_view.py +0 -0
  316. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/client/client.py +0 -0
  317. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/__init__.py +0 -0
  318. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/_common.py +0 -0
  319. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_constants.py +0 -0
  320. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_protocol_version.py +0 -0
  321. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_request_builders.py +0 -0
  322. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_schemas.py +0 -0
  323. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
  324. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_secret_request_builders.py +0 -0
  325. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/cpp_secret_schemas.py +0 -0
  326. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/go_schemas.py +0 -0
  327. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/protocol_version.py +0 -0
  328. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/ts_client.py +0 -0
  329. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/codegen/ts_schemas.py +0 -0
  330. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/exceptions.py +0 -0
  331. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/function.py +0 -0
  332. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/function_storage.py +0 -0
  333. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/function_storage_azure_sql.py +0 -0
  334. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/function_storage_cf_do.py +0 -0
  335. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/http/__init__.py +0 -0
  336. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/http/demo_storage.py +0 -0
  337. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/http/worker_page.py +0 -0
  338. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/invocation.py +0 -0
  339. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/logging_config.py +0 -0
  340. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/meta_worker.py +0 -0
  341. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/metadata.py +0 -0
  342. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/otel.py +0 -0
  343. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/protocol.py +0 -0
  344. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/protocol_version.txt +0 -0
  345. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/py.typed +0 -0
  346. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/scalar_function.py +0 -0
  347. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/schema_utils.py +0 -0
  348. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/secret_protocol.py +0 -0
  349. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/secret_service.py +0 -0
  350. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/serve.py +0 -0
  351. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/table_buffering_function.py +0 -0
  352. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/table_filter_pushdown.py +0 -0
  353. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/table_function.py +0 -0
  354. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/table_in_out_function.py +0 -0
  355. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/transactor/__init__.py +0 -0
  356. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/transactor/_duckdb_compat.py +0 -0
  357. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/transactor/client.py +0 -0
  358. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/transactor/protocol.py +0 -0
  359. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/transactor/server.py +0 -0
  360. {vgi_python-0.8.2 → vgi_python-0.8.4}/vgi/worker.py +0 -0
  361. {vgi_python-0.8.2 → vgi_python-0.8.4}/wrangler.jsonc +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vgi-python
3
- Version: 0.8.2
3
+ Version: 0.8.4
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vgi-python"
3
- version = "0.8.2"
3
+ version = "0.8.4"
4
4
  description = "Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow"
5
5
  readme = "README.md"
6
6
  keywords = [
@@ -1207,6 +1207,20 @@ class TestCatalogDescriptor:
1207
1207
  catalog = Catalog(name="myapp", schemas=[main, analytics])
1208
1208
  assert len(catalog.schemas) == 2
1209
1209
 
1210
+ def test_catalog_source_url_defaults_to_none(self) -> None:
1211
+ """Catalog source_url is None unless set."""
1212
+ catalog = Catalog(name="myapp", schemas=[Schema(name="main")])
1213
+ assert catalog.source_url is None
1214
+
1215
+ def test_catalog_source_url_settable(self) -> None:
1216
+ """Catalog source_url can advertise the repo/docs homepage."""
1217
+ catalog = Catalog(
1218
+ name="myapp",
1219
+ schemas=[Schema(name="main")],
1220
+ source_url="https://github.com/example/myapp",
1221
+ )
1222
+ assert catalog.source_url == "https://github.com/example/myapp"
1223
+
1210
1224
 
1211
1225
  class TestCatalogValidation:
1212
1226
  """Tests for Catalog validation."""
@@ -1311,6 +1325,25 @@ class TestReadOnlyCatalogWithCatalog:
1311
1325
  infos = catalog_interface.catalogs()
1312
1326
  assert [i.name for i in infos] == ["testapp"]
1313
1327
 
1328
+ def test_catalogs_omits_source_url_when_unset(self, catalog_interface: ReadOnlyCatalogInterface) -> None:
1329
+ """catalogs() yields source_url=None when the Catalog doesn't set one."""
1330
+ infos = catalog_interface.catalogs()
1331
+ assert infos[0].source_url is None
1332
+
1333
+ def test_catalogs_serializes_source_url(self, users_table: Table) -> None:
1334
+ """catalogs() surfaces the Catalog.source_url into CatalogInfo."""
1335
+
1336
+ class SourcedCatalog(ReadOnlyCatalogInterface):
1337
+ catalog = Catalog(
1338
+ name="sourced",
1339
+ default_schema="main",
1340
+ schemas=[Schema(name="main", tables=[users_table])],
1341
+ source_url="https://github.com/example/sourced",
1342
+ )
1343
+
1344
+ infos = SourcedCatalog().catalogs()
1345
+ assert infos[0].source_url == "https://github.com/example/sourced"
1346
+
1314
1347
  def test_catalog_attach(self, catalog_interface: ReadOnlyCatalogInterface) -> None:
1315
1348
  """catalog_attach returns result with correct defaults."""
1316
1349
  result = catalog_interface.catalog_attach(
@@ -78,6 +78,13 @@ class TestExampleWorkerCatalog:
78
78
  catalogs = client.catalogs()
79
79
  assert "example" in [c.name for c in catalogs]
80
80
 
81
+ def test_catalogs_advertises_source_url(self) -> None:
82
+ """The declarative catalog's source_url round-trips through discovery."""
83
+ client = Client(EXAMPLE_WORKER)
84
+ catalogs = client.catalogs()
85
+ example = next(c for c in catalogs if c.name == "example")
86
+ assert example.source_url == "https://github.com/query-farm/vgi-python"
87
+
81
88
  def test_catalog_attach_works(self) -> None:
82
89
  """Can attach to the 'example' catalog."""
83
90
  client = Client(EXAMPLE_WORKER)
@@ -613,3 +613,118 @@ class TestFunctionTypeInference:
613
613
 
614
614
  meta = resolve_metadata(TestFunc)
615
615
  assert meta.function_type == CatalogFunctionType.TABLE
616
+
617
+
618
+ class TestFunctionTags:
619
+ """A worker can attach arbitrary free-form tags to a function's metadata.
620
+
621
+ These flow ``Meta.tags`` -> ``ResolvedMetadata.tags`` -> ``FunctionInfo.tags``
622
+ -> DuckDB's per-function ``tags`` map, which is what the ``vgi-lint-check``
623
+ metadata linter reads (e.g. VGI307 requires ``vgi.columns_md`` on a
624
+ dynamic-schema table function).
625
+ """
626
+
627
+ def test_meta_tags_resolved(self) -> None:
628
+ """Arbitrary Meta.tags land on ResolvedMetadata.tags verbatim."""
629
+
630
+ class TaggedFunction(TableInOutFunction): # type: ignore[type-arg]
631
+ class Meta:
632
+ name = "tagged_func"
633
+ tags = {
634
+ "vgi.columns_md": "| col | type |\n| --- | --- |\n| id | BIGINT |",
635
+ "custom.team": "data",
636
+ }
637
+
638
+ data: TableInput = Arg[TableInput](0, doc="Input table") # type: ignore[assignment]
639
+
640
+ meta = resolve_metadata(TaggedFunction)
641
+ assert meta.tags == {
642
+ "vgi.columns_md": "| col | type |\n| --- | --- |\n| id | BIGINT |",
643
+ "custom.team": "data",
644
+ }
645
+
646
+ def test_tags_survive_arrow_roundtrip(self) -> None:
647
+ """Function tags survive the metadata Arrow wire roundtrip (map column)."""
648
+
649
+ class TaggedFunction(TableInOutFunction): # type: ignore[type-arg]
650
+ class Meta:
651
+ name = "tagged_func"
652
+ tags = {"vgi.columns_md": "| col |", "k": "v"}
653
+
654
+ data: TableInput = Arg[TableInput](0, doc="Input table") # type: ignore[assignment]
655
+
656
+ restored = arrow_to_functions(functions_to_arrow([TaggedFunction]))[0]
657
+ assert restored.tags == {"vgi.columns_md": "| col |", "k": "v"}
658
+
659
+ def test_tags_and_examples_are_independent(self) -> None:
660
+ """Worker tags do not clobber the structured examples, and vice versa.
661
+
662
+ ``examples`` is its own structured channel (serialized to the
663
+ ``examples`` struct column / ``FunctionInfo.examples``); it is never
664
+ folded into the ``tags`` map. A worker that *also* sets a
665
+ ``vgi.example_queries`` tag (the tag-encoded examples convention the
666
+ linter understands) keeps both — the tag is preserved untouched and the
667
+ structured examples remain separate.
668
+ """
669
+
670
+ class BothFunction(TableInOutFunction): # type: ignore[type-arg]
671
+ class Meta:
672
+ name = "both_func"
673
+ examples = ["SELECT * FROM both_func((SELECT 1))"]
674
+ tags = {
675
+ "vgi.example_queries": '[{"description":"x","sql":"SELECT tag"}]',
676
+ "vgi.columns_md": "| col |",
677
+ }
678
+
679
+ data: TableInput = Arg[TableInput](0, doc="Input table") # type: ignore[assignment]
680
+
681
+ meta = resolve_metadata(BothFunction)
682
+ # Worker-provided tags untouched (example_queries tag not derived, not dropped).
683
+ assert meta.tags == {
684
+ "vgi.example_queries": '[{"description":"x","sql":"SELECT tag"}]',
685
+ "vgi.columns_md": "| col |",
686
+ }
687
+ # Structured examples live independently and are not merged into tags.
688
+ assert [ex.sql for ex in meta.examples] == ["SELECT * FROM both_func((SELECT 1))"]
689
+
690
+ def test_tags_flow_into_function_info(self) -> None:
691
+ """Tags reach ``FunctionInfo.tags`` (catalog -> DuckDB -> linter path).
692
+
693
+ Also asserts that the structured ``examples`` and the worker-set
694
+ ``vgi.example_queries`` tag coexist on ``FunctionInfo`` without either
695
+ clobbering the other.
696
+ """
697
+ from vgi.catalog.catalog_interface import (
698
+ FunctionInfo,
699
+ ReadOnlyCatalogInterface,
700
+ )
701
+ from vgi.table_function import TableFunctionGenerator
702
+
703
+ class DynamicTable(TableFunctionGenerator): # type: ignore[type-arg]
704
+ class Meta:
705
+ name = "dynamic_table"
706
+ examples = ["SELECT * FROM dynamic_table()"]
707
+ tags = {
708
+ "vgi.columns_md": "| col | type |\n| --- | --- |\n| id | BIGINT |",
709
+ "vgi.example_queries": '[{"description":"x","sql":"SELECT 1"}]',
710
+ "custom.team": "data",
711
+ }
712
+
713
+ FIXED_SCHEMA = pa.schema([pa.field("id", pa.int64())])
714
+
715
+ def process(self, params): # type: ignore[no-untyped-def]
716
+ yield pa.record_batch({"id": [1]})
717
+
718
+ class Cat(ReadOnlyCatalogInterface):
719
+ catalog_name = "functions"
720
+ functions = [DynamicTable]
721
+
722
+ info = Cat()._function_to_info(DynamicTable, "main")
723
+ assert isinstance(info, FunctionInfo)
724
+ assert info.tags == {
725
+ "vgi.columns_md": "| col | type |\n| --- | --- |\n| id | BIGINT |",
726
+ "vgi.example_queries": '[{"description":"x","sql":"SELECT 1"}]',
727
+ "custom.team": "data",
728
+ }
729
+ # Structured examples are carried separately from the tag-encoded ones.
730
+ assert [ex.sql for ex in info.examples] == ["SELECT * FROM dynamic_table()"]
@@ -0,0 +1,111 @@
1
+ # Copyright 2025, 2026 Query Farm LLC - https://query.farm
2
+
3
+ """Union-typed argument decoding.
4
+
5
+ A DuckDB ``UNION`` / Arrow union argument is tagged: the discriminator lives in
6
+ ``UnionScalar.type_code``, which plain ``Scalar.as_py()`` discards. ``Arguments``
7
+ decodes such scalars to ``TaggedUnion`` so the active member name is preserved.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import pyarrow as pa
13
+
14
+ from vgi.arguments import Arguments, TaggedUnion
15
+
16
+ _RF = pa.struct([pa.field("n_estimators", pa.list_(pa.int64())), pa.field("max_depth", pa.list_(pa.int64()))])
17
+ _SVC = pa.struct([pa.field("C", pa.list_(pa.float64())), pa.field("kernel", pa.list_(pa.string()))])
18
+
19
+
20
+ def _union_scalar(code: int, members: dict) -> pa.UnionScalar:
21
+ """Build a one-element sparse-union scalar with named members, active = ``code``."""
22
+ arr = pa.UnionArray.from_sparse(
23
+ pa.array([code], type=pa.int8()),
24
+ [
25
+ pa.array([members.get("random_forest_classifier")], type=_RF),
26
+ pa.array([members.get("svc")], type=_SVC),
27
+ ],
28
+ field_names=["random_forest_classifier", "svc"],
29
+ type_codes=[0, 1],
30
+ )
31
+ return arr[0]
32
+
33
+
34
+ def test_union_arg_preserves_tag() -> None:
35
+ """A union argument decodes to a TaggedUnion carrying the active member name."""
36
+ scalar = _union_scalar(0, {"random_forest_classifier": {"n_estimators": [100, 300], "max_depth": [3, 5]}})
37
+ got = Arguments(named={"config": scalar}).get("config")
38
+ assert isinstance(got, TaggedUnion)
39
+ assert got.tag == "random_forest_classifier"
40
+ assert got.value == {"n_estimators": [100, 300], "max_depth": [3, 5]}
41
+
42
+
43
+ def test_union_arg_other_member() -> None:
44
+ """The tag reflects whichever union member is set."""
45
+ scalar = _union_scalar(1, {"svc": {"C": [1.0, 10.0], "kernel": ["rbf", "linear"]}})
46
+ got = Arguments(named={"config": scalar}).get("config")
47
+ assert got.tag == "svc"
48
+ assert got.value == {"C": [1.0, 10.0], "kernel": ["rbf", "linear"]}
49
+
50
+
51
+ def test_union_arg_null_payload() -> None:
52
+ """A null union slot decodes its value to None (the ``inner is None`` branch).
53
+
54
+ Note: pyarrow reports ``type_code == 0`` for any null union slot regardless of
55
+ the codes buffer, so the active member's tag is not recoverable at the scalar
56
+ level for a null payload — only the value is. (Tag preservation for a
57
+ non-null payload of a non-zero-code member is covered above and through the
58
+ batch-level round-trip in the echo integration suite.)
59
+ """
60
+ scalar = _union_scalar(1, {"svc": None})
61
+ got = Arguments(named={"config": scalar}).get("config")
62
+ assert isinstance(got, TaggedUnion)
63
+ assert got.value is None
64
+
65
+
66
+ def test_union_arg_non_contiguous_type_codes() -> None:
67
+ """Tag resolution maps via type_codes, not member position.
68
+
69
+ Type codes need not be 0,1,2,...; ``_scalar_to_py`` must locate the active
70
+ member by ``type_codes.index(code)`` rather than treating the code as a
71
+ positional index.
72
+ """
73
+ arr = pa.UnionArray.from_sparse(
74
+ pa.array([9], type=pa.int8()),
75
+ [
76
+ pa.array([{"n_estimators": [1], "max_depth": [2]}], type=_RF),
77
+ pa.array([{"C": [1.0], "kernel": ["rbf"]}], type=_SVC),
78
+ ],
79
+ field_names=["random_forest_classifier", "svc"],
80
+ type_codes=[5, 9],
81
+ )
82
+ got = Arguments(named={"config": arr[0]}).get("config")
83
+ assert isinstance(got, TaggedUnion)
84
+ assert got.tag == "svc"
85
+ assert got.value == {"C": [1.0], "kernel": ["rbf"]}
86
+
87
+
88
+ def test_union_arg_dense_union() -> None:
89
+ """Dense unions (what DuckDB emits) decode the same as sparse."""
90
+ arr = pa.UnionArray.from_dense(
91
+ pa.array([1], type=pa.int8()),
92
+ pa.array([0], type=pa.int32()),
93
+ [
94
+ pa.array([], type=_RF),
95
+ pa.array([{"C": [2.0], "kernel": ["linear"]}], type=_SVC),
96
+ ],
97
+ field_names=["random_forest_classifier", "svc"],
98
+ type_codes=[0, 1],
99
+ )
100
+ got = Arguments(named={"config": arr[0]}).get("config")
101
+ assert isinstance(got, TaggedUnion)
102
+ assert got.tag == "svc"
103
+ assert got.value == {"C": [2.0], "kernel": ["linear"]}
104
+
105
+
106
+ def test_non_union_args_unchanged() -> None:
107
+ """Non-union arguments still decode via plain as_py()."""
108
+ args = Arguments(named={"n": pa.scalar(5), "s": pa.scalar("hi")}, positional=(pa.scalar(1.5),))
109
+ assert args.get("n") == 5
110
+ assert args.get("s") == "hi"
111
+ assert args.get(0) == 1.5
@@ -2250,7 +2250,7 @@ requires-dist = [
2250
2250
 
2251
2251
  [[package]]
2252
2252
  name = "vgi-python"
2253
- version = "0.8.2"
2253
+ version = "0.8.4"
2254
2254
  source = { editable = "." }
2255
2255
  dependencies = [
2256
2256
  { name = "click" },
@@ -39,6 +39,7 @@ from vgi.arguments import (
39
39
  Param,
40
40
  Returns,
41
41
  TableInput,
42
+ TaggedUnion,
42
43
  )
43
44
  from vgi.auth import AuthContext, CallContext
44
45
  from vgi.metadata import (
@@ -143,6 +144,7 @@ __all__ = [
143
144
  "TableInOutGenerator",
144
145
  "TableInput",
145
146
  "TableInputValidationError",
147
+ "TaggedUnion",
146
148
  "TypeMismatchError",
147
149
  "Worker",
148
150
  "functions_to_arrow",
@@ -61,7 +61,7 @@ class CountBatchArgs:
61
61
  """
62
62
 
63
63
  count: Annotated[int, Arg(0, doc="Number of rows to generate", ge=0)]
64
- batch_size: Annotated[int, Arg("batch_size", default=1000, doc="Batch size for output", ge=1)]
64
+ batch_size: Annotated[int, Arg("batch_size", default=2048, doc="Batch size for output", ge=1)]
65
65
 
66
66
 
67
67
  @dataclass(slots=True, frozen=True)
@@ -91,7 +91,7 @@ class _BaseSequenceFunction(TableFunctionGenerator[Any, CountdownState]):
91
91
  NUMPY_DTYPE: ClassVar[type[np.generic]] = np.int64
92
92
  STATS_ARROW_TYPE: ClassVar[pa.DataType] = pa.int64()
93
93
  STATS_COLUMN_NAME: ClassVar[str] = "n"
94
- BATCH_SIZE_FALLBACK: ClassVar[int] = 1000
94
+ BATCH_SIZE_FALLBACK: ClassVar[int] = 2048
95
95
 
96
96
  @classmethod
97
97
  def initial_state(cls, params: ProcessParams[Any]) -> CountdownState:
@@ -320,6 +320,7 @@ _EXAMPLE_CATALOG = Catalog(
320
320
  default_schema="main",
321
321
  comment="Example VGI catalog for testing",
322
322
  tags={"source": "vgi-fixture-worker", "version": "1"},
323
+ source_url="https://github.com/query-farm/vgi-python",
323
324
  schemas=[
324
325
  Schema(
325
326
  name="main",
@@ -159,6 +159,7 @@ __all__ = [
159
159
  "PYTHON_TO_ARROW",
160
160
  "Returns",
161
161
  "TableInput",
162
+ "TaggedUnion",
162
163
  "TypeBoundPredicate",
163
164
  "OutputLength",
164
165
  "Setting",
@@ -168,6 +169,58 @@ __all__ = [
168
169
  ]
169
170
 
170
171
 
172
+ @dataclass(frozen=True, slots=True)
173
+ class TaggedUnion:
174
+ """A decoded union-typed argument: which member is set (``tag``) and its ``value``.
175
+
176
+ DuckDB ``UNION`` / Arrow union arguments are *tagged*: the discriminator
177
+ (which member is present) lives in the Arrow ``UnionScalar.type_code``, not
178
+ in the member value. Plain ``Scalar.as_py()`` returns only the member value
179
+ and drops that tag, so union arguments are decoded into this wrapper
180
+ instead — ``tag`` is the active member's field name and ``value`` is its
181
+ Python value.
182
+
183
+ Example::
184
+
185
+ config: Annotated[TaggedUnion, Arg("config", arrow_type=pa.sparse_union([...]))]
186
+ ...
187
+ cfg = params.args.config # TaggedUnion(tag=..., value=...)
188
+ if cfg.tag == "random_forest_classifier":
189
+ grid = cfg.value # the member struct, as a dict
190
+
191
+ """
192
+
193
+ tag: str | None
194
+ value: Any
195
+
196
+
197
+ def _scalar_to_py(scalar: "Scalar[Any]") -> Any:
198
+ """Convert an argument scalar to a Python value, preserving union tags.
199
+
200
+ Identical to ``scalar.as_py()`` for every type except unions: a
201
+ ``UnionScalar`` is decoded to a [`TaggedUnion`][] so the member
202
+ discriminator (which ``as_py()`` discards) is retained.
203
+
204
+ Args:
205
+ scalar: The argument scalar to convert.
206
+
207
+ Returns:
208
+ ``scalar.as_py()`` for non-union scalars; a [`TaggedUnion`][] for unions.
209
+
210
+ """
211
+ if isinstance(scalar, pa.UnionScalar):
212
+ # Map the active ``type_code`` to its member field name via the union
213
+ # type's parallel ``type_codes`` / ``field()``. (``type_code`` is coerced
214
+ # to int — it is an integer at runtime regardless of the stub's typing.)
215
+ union_type = scalar.type
216
+ type_codes = list(union_type.type_codes)
217
+ code = int(scalar.type_code)
218
+ tag = union_type.field(type_codes.index(code)).name if code in type_codes else None
219
+ inner = scalar.value
220
+ return TaggedUnion(tag=tag, value=inner.as_py() if inner is not None else None)
221
+ return scalar.as_py()
222
+
223
+
171
224
  class TableInput:
172
225
  """Sentinel type for table input parameters in table-in-out functions.
173
226
 
@@ -377,7 +430,7 @@ class Arguments:
377
430
  else:
378
431
  raise TypeError(f"Argument '{key}': expected {type}, got {scalar.type}")
379
432
 
380
- return scalar.as_py()
433
+ return _scalar_to_py(scalar)
381
434
 
382
435
  def get_varargs(
383
436
  self,
@@ -410,7 +463,7 @@ class Arguments:
410
463
  if type is not None and scalar.type != type:
411
464
  raise TypeError(f"Argument {i}: expected {type}, got {scalar.type}")
412
465
 
413
- values.append(scalar.as_py())
466
+ values.append(_scalar_to_py(scalar))
414
467
 
415
468
  return tuple(values)
416
469
 
@@ -2319,6 +2319,7 @@ class ReadOnlyCatalogInterface(CatalogInterface):
2319
2319
  implementation_version=None,
2320
2320
  data_version_spec=None,
2321
2321
  attach_option_specs=[spec.serialize() for spec in self.attach_option_specs],
2322
+ source_url=self.catalog.source_url if self.catalog is not None else None,
2322
2323
  )
2323
2324
  ]
2324
2325
 
@@ -873,6 +873,10 @@ class Catalog:
873
873
  schemas: Sequence of Schema objects defining the catalog contents.
874
874
  comment: Optional comment describing the catalog.
875
875
  tags: Optional key-value tags associated with the catalog.
876
+ source_url: Where this worker's code lives — repo, build, or docs
877
+ homepage. ``None`` (the default) when the worker doesn't advertise
878
+ a source location. Surfaced via the ``catalog_catalogs()`` discovery
879
+ record (``CatalogInfo.source_url``).
876
880
 
877
881
  """
878
882
 
@@ -881,6 +885,7 @@ class Catalog:
881
885
  schemas: Sequence[Schema] = ()
882
886
  comment: str | None = None
883
887
  tags: dict[str, str] = field(default_factory=dict)
888
+ source_url: str | None = None
884
889
 
885
890
  def __post_init__(self) -> None:
886
891
  """Validate catalog configuration."""
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
File without changes
File without changes
File without changes