vgi-python 0.8.2__tar.gz → 0.8.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. {vgi_python-0.8.2 → vgi_python-0.8.3}/PKG-INFO +1 -1
  2. {vgi_python-0.8.2 → vgi_python-0.8.3}/pyproject.toml +1 -1
  3. vgi_python-0.8.3/tests/test_union_argument.py +111 -0
  4. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/__init__.py +2 -0
  5. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/arguments.py +55 -2
  6. {vgi_python-0.8.2 → vgi_python-0.8.3}/.gitattributes +0 -0
  7. {vgi_python-0.8.2 → vgi_python-0.8.3}/.github/dependabot.yml +0 -0
  8. {vgi_python-0.8.2 → vgi_python-0.8.3}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
  9. {vgi_python-0.8.2 → vgi_python-0.8.3}/.github/workflows/ci.yml +0 -0
  10. {vgi_python-0.8.2 → vgi_python-0.8.3}/.github/workflows/docs.yml +0 -0
  11. {vgi_python-0.8.2 → vgi_python-0.8.3}/.github/workflows/integration.yml +0 -0
  12. {vgi_python-0.8.2 → vgi_python-0.8.3}/.github/workflows/release.yml +0 -0
  13. {vgi_python-0.8.2 → vgi_python-0.8.3}/.gitignore +0 -0
  14. {vgi_python-0.8.2 → vgi_python-0.8.3}/.python-version +0 -0
  15. {vgi_python-0.8.2 → vgi_python-0.8.3}/.vale.ini +0 -0
  16. {vgi_python-0.8.2 → vgi_python-0.8.3}/CLAUDE.md +0 -0
  17. {vgi_python-0.8.2 → vgi_python-0.8.3}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
  18. {vgi_python-0.8.2 → vgi_python-0.8.3}/DOCS_REVIEW_RUBRIC.md +0 -0
  19. {vgi_python-0.8.2 → vgi_python-0.8.3}/DOCS_USABILITY_TEST.md +0 -0
  20. {vgi_python-0.8.2 → vgi_python-0.8.3}/LICENSE +0 -0
  21. {vgi_python-0.8.2 → vgi_python-0.8.3}/README.md +0 -0
  22. {vgi_python-0.8.2 → vgi_python-0.8.3}/SECURITY.md +0 -0
  23. {vgi_python-0.8.2 → vgi_python-0.8.3}/ci/README.md +0 -0
  24. {vgi_python-0.8.2 → vgi_python-0.8.3}/ci/preprocess-require.awk +0 -0
  25. {vgi_python-0.8.2 → vgi_python-0.8.3}/ci/run-integration.sh +0 -0
  26. {vgi_python-0.8.2 → vgi_python-0.8.3}/dist-vgi/.gitignore +0 -0
  27. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/aggregate-functions.md +0 -0
  28. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/arguments.md +0 -0
  29. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/auth.md +0 -0
  30. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/catalogs.md +0 -0
  31. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/client.md +0 -0
  32. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/exceptions.md +0 -0
  33. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/filters.md +0 -0
  34. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/functions.md +0 -0
  35. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/http.md +0 -0
  36. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/index.md +0 -0
  37. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/metadata.md +0 -0
  38. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/observability.md +0 -0
  39. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/storage.md +0 -0
  40. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/transactor.md +0 -0
  41. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/api/worker.md +0 -0
  42. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/argument-serialization.md +0 -0
  43. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/apple-touch-icon.png +0 -0
  44. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/favicon-16x16.png +0 -0
  45. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/favicon-32x32.png +0 -0
  46. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/favicon.ico +0 -0
  47. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/kinds/aggregate.svg +0 -0
  48. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/kinds/buffering.svg +0 -0
  49. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/kinds/scalar.svg +0 -0
  50. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/kinds/table-in-out.svg +0 -0
  51. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/kinds/table.svg +0 -0
  52. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/logo.png +0 -0
  53. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/assets/social-card.png +0 -0
  54. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/authentication.md +0 -0
  55. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/catalog-interface.md +0 -0
  56. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/cli.md +0 -0
  57. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/column-statistics.md +0 -0
  58. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/concepts/index.md +0 -0
  59. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/contributing-docs.md +0 -0
  60. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/filter-pushdown.md +0 -0
  61. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/generator-api.md +0 -0
  62. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/how-to/catalogs.md +0 -0
  63. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/how-to/function-patterns.md +0 -0
  64. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/how-to/http-auth.md +0 -0
  65. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/how-to/index.md +0 -0
  66. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/how-to/pushdown-and-statistics.md +0 -0
  67. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/how-to/state-storage.md +0 -0
  68. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/index.md +0 -0
  69. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/lifecycle.md +0 -0
  70. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/metadata.md +0 -0
  71. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/overrides/main.html +0 -0
  72. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/robots.txt +0 -0
  73. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/shared-storage.md +0 -0
  74. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/stylesheets/extra.css +0 -0
  75. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/tutorial/index.md +0 -0
  76. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/tutorial/scalar.md +0 -0
  77. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/tutorial/table.md +0 -0
  78. {vgi_python-0.8.2 → vgi_python-0.8.3}/docs/vgi-logo.png +0 -0
  79. {vgi_python-0.8.2 → vgi_python-0.8.3}/examples/calc_scalar_worker.py +0 -0
  80. {vgi_python-0.8.2 → vgi_python-0.8.3}/examples/calc_worker.py +0 -0
  81. {vgi_python-0.8.2 → vgi_python-0.8.3}/examples/filter_worker.py +0 -0
  82. {vgi_python-0.8.2 → vgi_python-0.8.3}/examples/greeting_scalar_worker.py +0 -0
  83. {vgi_python-0.8.2 → vgi_python-0.8.3}/examples/row_count_worker.py +0 -0
  84. {vgi_python-0.8.2 → vgi_python-0.8.3}/examples/series_streaming_worker.py +0 -0
  85. {vgi_python-0.8.2 → vgi_python-0.8.3}/examples/sum_worker.py +0 -0
  86. {vgi_python-0.8.2 → vgi_python-0.8.3}/mkdocs.yml +0 -0
  87. {vgi_python-0.8.2 → vgi_python-0.8.3}/packages/vgi-fixtures/LICENSE +0 -0
  88. {vgi_python-0.8.2 → vgi_python-0.8.3}/packages/vgi-fixtures/README.md +0 -0
  89. {vgi_python-0.8.2 → vgi_python-0.8.3}/packages/vgi-fixtures/pyproject.toml +0 -0
  90. {vgi_python-0.8.2 → vgi_python-0.8.3}/scripts/measure_startup.py +0 -0
  91. {vgi_python-0.8.2 → vgi_python-0.8.3}/scripts/run_all_tests.sh +0 -0
  92. {vgi_python-0.8.2 → vgi_python-0.8.3}/test-data/generate.sh +0 -0
  93. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/__init__.py +0 -0
  94. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/_http_fixtures.py +0 -0
  95. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/__init__.py +0 -0
  96. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_catalog_interface.py +0 -0
  97. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_client_catalog.py +0 -0
  98. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_column_statistics.py +0 -0
  99. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_declarative.py +0 -0
  100. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_example_worker_catalog.py +0 -0
  101. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_integration.py +0 -0
  102. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_required_field_filter_paths.py +0 -0
  103. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_scan_branches.py +0 -0
  104. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_serialization.py +0 -0
  105. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_setting.py +0 -0
  106. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_storage.py +0 -0
  107. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_time_travel.py +0 -0
  108. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/catalog/test_writable_table.py +0 -0
  109. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/client/__init__.py +0 -0
  110. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/client/test_broken_pipe.py +0 -0
  111. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/client/test_cli.py +0 -0
  112. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/client/test_cli_catalog_functions.py +0 -0
  113. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/client/test_worker_debug.py +0 -0
  114. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/__init__.py +0 -0
  115. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/_stub.py +0 -0
  116. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/conftest.py +0 -0
  117. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_accumulate.py +0 -0
  118. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_aggregate.py +0 -0
  119. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_attach.py +0 -0
  120. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_bearer_auth.py +0 -0
  121. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_directory_parity.py +0 -0
  122. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_function_inventory.py +0 -0
  123. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_http_client.py +0 -0
  124. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_http_external_location.py +0 -0
  125. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_http_upload_url.py +0 -0
  126. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_macro.py +0 -0
  127. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_overload.py +0 -0
  128. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_protocol_inventory.py +0 -0
  129. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_protocol_version.py +0 -0
  130. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_resumable_scan.py +0 -0
  131. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_scalar.py +0 -0
  132. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
  133. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_secret.py +0 -0
  134. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_settings.py +0 -0
  135. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_table.py +0 -0
  136. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_table_in_out.py +0 -0
  137. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_view.py +0 -0
  138. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conformance/test_writable.py +0 -0
  139. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/conftest.py +0 -0
  140. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/scalar/__init__.py +0 -0
  141. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/scalar/test_bernoulli_function.py +0 -0
  142. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/scalar/test_binary_packet_function.py +0 -0
  143. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/scalar/test_client.py +0 -0
  144. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/scalar/test_conditional_message_function.py +0 -0
  145. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/scalar/test_hash_seed_function.py +0 -0
  146. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/scalar/test_multiply_by_setting_function.py +0 -0
  147. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/scalar/test_multiply_function.py +0 -0
  148. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/scalar/test_random_bytes_function.py +0 -0
  149. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/scalar/test_return_secret_value_function.py +0 -0
  150. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/__init__.py +0 -0
  151. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/__init__.py +0 -0
  152. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_constant_columns_function.py +0 -0
  153. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_double_sequence_function.py +0 -0
  154. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_exception_function.py +0 -0
  155. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_filter_echo_function.py +0 -0
  156. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_logging_function.py +0 -0
  157. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_nested_sequence_function.py +0 -0
  158. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_partitioned_function.py +0 -0
  159. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_projected_data_function.py +0 -0
  160. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_sequence_function.py +0 -0
  161. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_settings_function.py +0 -0
  162. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_struct_settings_function.py +0 -0
  163. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table/generator/test_ten_thousand_function.py +0 -0
  164. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table_in_out/__init__.py +0 -0
  165. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table_in_out/generator/__init__.py +0 -0
  166. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
  167. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table_in_out/generator/test_echo_function.py +0 -0
  168. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table_in_out/generator/test_exception_functions.py +0 -0
  169. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
  170. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
  171. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
  172. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/table_in_out/test_client.py +0 -0
  173. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_access_log_audit.py +0 -0
  174. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_aggregate_function.py +0 -0
  175. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_argument_spec.py +0 -0
  176. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_auth.py +0 -0
  177. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_bind_exceptions.py +0 -0
  178. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_bind_request_at_clause.py +0 -0
  179. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_bound_storage_conformance.py +0 -0
  180. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_catalog_auth_binding.py +0 -0
  181. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_docstrings.py +0 -0
  182. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_documentation_examples.py +0 -0
  183. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_example_function_arg_types.py +0 -0
  184. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_examples_workers.py +0 -0
  185. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_exception_handling.py +0 -0
  186. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_exceptions.py +0 -0
  187. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_filter_pushdown.py +0 -0
  188. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_filter_pushdown_extension.py +0 -0
  189. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_function_storage.py +0 -0
  190. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_function_storage_azure_sql.py +0 -0
  191. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_function_storage_cf_do.py +0 -0
  192. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_function_storage_cf_do_integration.py +0 -0
  193. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_function_storage_conformance.py +0 -0
  194. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_generated_cpp_constants.py +0 -0
  195. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_generated_cpp_protocol_version.py +0 -0
  196. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_generated_cpp_request_builders.py +0 -0
  197. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_generated_cpp_schemas.py +0 -0
  198. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_generated_cpp_secret.py +0 -0
  199. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_generated_go_schemas.py +0 -0
  200. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_generated_protocol_version.py +0 -0
  201. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_generated_schemas_cross_lang.py +0 -0
  202. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_generated_ts_client.py +0 -0
  203. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_generated_ts_schemas.py +0 -0
  204. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_http_demo_storage.py +0 -0
  205. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_http_s3_offload_input.py +0 -0
  206. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_http_s3_offload_output.py +0 -0
  207. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_metadata.py +0 -0
  208. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_mypy_consolidated.py +0 -0
  209. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_nest_tensor.py +0 -0
  210. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_otel.py +0 -0
  211. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_projection_enforcement.py +0 -0
  212. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_projection_repro.py +0 -0
  213. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_protocol_classes.py +0 -0
  214. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_schema_utils.py +0 -0
  215. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_serve.py +0 -0
  216. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_setting_secret_annotations.py +0 -0
  217. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_table_buffering_function.py +0 -0
  218. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_table_function_dynamic_to_string.py +0 -0
  219. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_type_bounds.py +0 -0
  220. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_worker.py +0 -0
  221. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_worker_cli.py +0 -0
  222. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/test_worker_page.py +0 -0
  223. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/transactor/__init__.py +0 -0
  224. {vgi_python-0.8.2 → vgi_python-0.8.3}/tests/transactor/test_transactor.py +0 -0
  225. {vgi_python-0.8.2 → vgi_python-0.8.3}/uv.lock +0 -0
  226. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_duckdb.py +0 -0
  227. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_storage_profile.py +0 -0
  228. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/__init__.py +0 -0
  229. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
  230. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/accumulate/worker.py +0 -0
  231. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
  232. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/aggregate/_common.py +0 -0
  233. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/aggregate/basic.py +0 -0
  234. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
  235. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/aggregate/generic.py +0 -0
  236. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
  237. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
  238. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
  239. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
  240. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/aggregate/window.py +0 -0
  241. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/attach_options.py +0 -0
  242. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/bad_enum.py +0 -0
  243. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/bad_protocol.py +0 -0
  244. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/cancellable.py +0 -0
  245. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/catalog.py +0 -0
  246. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/http_server.py +0 -0
  247. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
  248. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
  249. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/nest_tensor.py +0 -0
  250. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/orchard_catalog.py +0 -0
  251. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
  252. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
  253. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/scalar/__init__.py +0 -0
  254. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/scalar/_common.py +0 -0
  255. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
  256. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/scalar/binary.py +0 -0
  257. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/scalar/formatting.py +0 -0
  258. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/scalar/geo.py +0 -0
  259. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
  260. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
  261. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
  262. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/scalar/type_info.py +0 -0
  263. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
  264. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
  265. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/simple_writable.py +0 -0
  266. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/__init__.py +0 -0
  267. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/_common.py +0 -0
  268. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/batch_index.py +0 -0
  269. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
  270. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
  271. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/filters.py +0 -0
  272. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/late_materialization.py +0 -0
  273. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/make_series.py +0 -0
  274. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/misc.py +0 -0
  275. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/order_modes.py +0 -0
  276. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/pairs.py +0 -0
  277. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/partition_columns.py +0 -0
  278. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
  279. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/profiling_example.py +0 -0
  280. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/required_filters.py +0 -0
  281. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/sequence.py +0 -0
  282. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/settings.py +0 -0
  283. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
  284. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
  285. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/typed_probe.py +0 -0
  286. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table/versioned.py +0 -0
  287. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/table_in_out.py +0 -0
  288. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/versioned.py +0 -0
  289. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/versioned_tables.py +0 -0
  290. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/worker.py +0 -0
  291. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/writable/__init__.py +0 -0
  292. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/writable/generic.py +0 -0
  293. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/writable/table.py +0 -0
  294. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/_test_fixtures/writable/worker.py +0 -0
  295. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/aggregate_function.py +0 -0
  296. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/argument_spec.py +0 -0
  297. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/auth.py +0 -0
  298. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/catalog/__init__.py +0 -0
  299. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/catalog/_descriptor_spec.py +0 -0
  300. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/catalog/attach_option.py +0 -0
  301. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/catalog/catalog_interface.py +0 -0
  302. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/catalog/descriptors.py +0 -0
  303. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/catalog/duckdb_statistics.py +0 -0
  304. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/catalog/secret_type.py +0 -0
  305. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/catalog/setting.py +0 -0
  306. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/catalog/storage.py +0 -0
  307. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/client/__init__.py +0 -0
  308. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/client/catalog_mixin.py +0 -0
  309. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/client/cli.py +0 -0
  310. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/client/cli_catalog.py +0 -0
  311. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/client/cli_schema.py +0 -0
  312. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/client/cli_table.py +0 -0
  313. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/client/cli_transaction.py +0 -0
  314. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/client/cli_utils.py +0 -0
  315. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/client/cli_view.py +0 -0
  316. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/client/client.py +0 -0
  317. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/__init__.py +0 -0
  318. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/_common.py +0 -0
  319. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/cpp_constants.py +0 -0
  320. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/cpp_protocol_version.py +0 -0
  321. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/cpp_request_builders.py +0 -0
  322. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/cpp_schemas.py +0 -0
  323. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
  324. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/cpp_secret_request_builders.py +0 -0
  325. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/cpp_secret_schemas.py +0 -0
  326. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/go_schemas.py +0 -0
  327. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/protocol_version.py +0 -0
  328. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/ts_client.py +0 -0
  329. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/codegen/ts_schemas.py +0 -0
  330. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/exceptions.py +0 -0
  331. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/function.py +0 -0
  332. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/function_storage.py +0 -0
  333. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/function_storage_azure_sql.py +0 -0
  334. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/function_storage_cf_do.py +0 -0
  335. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/http/__init__.py +0 -0
  336. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/http/demo_storage.py +0 -0
  337. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/http/worker_page.py +0 -0
  338. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/invocation.py +0 -0
  339. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/logging_config.py +0 -0
  340. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/meta_worker.py +0 -0
  341. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/metadata.py +0 -0
  342. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/otel.py +0 -0
  343. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/protocol.py +0 -0
  344. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/protocol_version.txt +0 -0
  345. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/py.typed +0 -0
  346. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/scalar_function.py +0 -0
  347. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/schema_utils.py +0 -0
  348. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/secret_protocol.py +0 -0
  349. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/secret_service.py +0 -0
  350. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/serve.py +0 -0
  351. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/table_buffering_function.py +0 -0
  352. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/table_filter_pushdown.py +0 -0
  353. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/table_function.py +0 -0
  354. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/table_in_out_function.py +0 -0
  355. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/transactor/__init__.py +0 -0
  356. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/transactor/_duckdb_compat.py +0 -0
  357. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/transactor/client.py +0 -0
  358. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/transactor/protocol.py +0 -0
  359. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/transactor/server.py +0 -0
  360. {vgi_python-0.8.2 → vgi_python-0.8.3}/vgi/worker.py +0 -0
  361. {vgi_python-0.8.2 → vgi_python-0.8.3}/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.3
4
4
  Summary: Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow
5
5
  Project-URL: Homepage, https://query.farm
6
6
  Project-URL: Repository, https://github.com/Query-farm/vgi-python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vgi-python"
3
- version = "0.8.2"
3
+ version = "0.8.3"
4
4
  description = "Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow"
5
5
  readme = "README.md"
6
6
  keywords = [
@@ -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
@@ -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",
@@ -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
 
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
File without changes
File without changes
File without changes
File without changes
File without changes