vgi-python 0.33.0__py3-none-any.whl → 0.34.0__py3-none-any.whl
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.
- vgi/_test_fixtures/table_in_out.py +84 -1
- vgi/_test_fixtures/worker.py +4 -0
- vgi/argument_spec.py +42 -8
- {vgi_python-0.33.0.dist-info → vgi_python-0.34.0.dist-info}/METADATA +1 -1
- {vgi_python-0.33.0.dist-info → vgi_python-0.34.0.dist-info}/RECORD +8 -8
- {vgi_python-0.33.0.dist-info → vgi_python-0.34.0.dist-info}/WHEEL +1 -1
- {vgi_python-0.33.0.dist-info → vgi_python-0.34.0.dist-info}/entry_points.txt +0 -0
- {vgi_python-0.33.0.dist-info → vgi_python-0.34.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -51,7 +51,7 @@ from vgi_rpc import ArrowSerializableDataclass, ArrowType
|
|
|
51
51
|
from vgi_rpc.log import Level
|
|
52
52
|
from vgi_rpc.rpc import OutputCollector
|
|
53
53
|
|
|
54
|
-
from vgi.arguments import Arg, Setting, TableInput
|
|
54
|
+
from vgi.arguments import AnyArrow, Arg, Setting, TableInput
|
|
55
55
|
from vgi.cache_control import CacheControl
|
|
56
56
|
from vgi.invocation import BindResponse
|
|
57
57
|
from vgi.metadata import FunctionExample
|
|
@@ -94,6 +94,8 @@ __all__ = [
|
|
|
94
94
|
"GeoEncode3Function",
|
|
95
95
|
"RowSumFunction",
|
|
96
96
|
"BlendedDropFunction",
|
|
97
|
+
"BlendedAnyFunction",
|
|
98
|
+
"BlendedAnyVarargsFunction",
|
|
97
99
|
"SubstreamPartialSumFunction",
|
|
98
100
|
"MultiBatchFinishFunction",
|
|
99
101
|
"SumAllColumnsFunction",
|
|
@@ -1880,6 +1882,87 @@ class BlendedExplodeFunction(RowTransformFunction[_ExplodeArgs]):
|
|
|
1880
1882
|
cast("VgiOutputCollector", out).emit(out_batch, parent_rows=parent_rows)
|
|
1881
1883
|
|
|
1882
1884
|
|
|
1885
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
|
1886
|
+
class _AnyArgs:
|
|
1887
|
+
"""One positional ANY-typed input column."""
|
|
1888
|
+
|
|
1889
|
+
value: Annotated[AnyArrow, Arg(0, doc="Input column of any type (echoed back unchanged)")]
|
|
1890
|
+
|
|
1891
|
+
|
|
1892
|
+
class BlendedAnyFunction(RowTransformFunction[_AnyArgs]):
|
|
1893
|
+
"""Blended 1->1 echo of a single ANY-typed input column.
|
|
1894
|
+
|
|
1895
|
+
Proves a blended positional arg may be declared ANY: the declared type names
|
|
1896
|
+
no concrete Arrow type, so the client must build the worker's input schema
|
|
1897
|
+
from the type DuckDB actually resolved for the call (a STRUCT built per row,
|
|
1898
|
+
a whole-row struct, a LIST, a plain VARCHAR ...) rather than from the
|
|
1899
|
+
declaration. The output column ``value`` is bound to that resolved input
|
|
1900
|
+
type at bind time and the column is passed through untouched, so a test
|
|
1901
|
+
asserts on DuckDB's own ``typeof(value)`` and on the round-tripped values —
|
|
1902
|
+
nothing here depends on how an SDK happens to render a type name.
|
|
1903
|
+
"""
|
|
1904
|
+
|
|
1905
|
+
class Meta:
|
|
1906
|
+
name = "blended_any"
|
|
1907
|
+
description = "Blended 1->1 echo of one ANY-typed input column (output typed from the input)"
|
|
1908
|
+
categories = ["blended", "test"]
|
|
1909
|
+
|
|
1910
|
+
@classmethod
|
|
1911
|
+
def on_bind(cls, params: BindParams[_AnyArgs]) -> BindResponse:
|
|
1912
|
+
input_schema = params.bind_call.input_schema
|
|
1913
|
+
assert input_schema is not None
|
|
1914
|
+
return BindResponse(output_schema=pa.schema([pa.field("value", input_schema.field(0).type)]))
|
|
1915
|
+
|
|
1916
|
+
@classmethod
|
|
1917
|
+
def process(
|
|
1918
|
+
cls,
|
|
1919
|
+
params: ProcessParams[_AnyArgs],
|
|
1920
|
+
state: None,
|
|
1921
|
+
batch: pa.RecordBatch,
|
|
1922
|
+
out: OutputCollector,
|
|
1923
|
+
) -> None:
|
|
1924
|
+
out.emit(pa.record_batch([batch.column(0)], schema=params.output_schema))
|
|
1925
|
+
|
|
1926
|
+
|
|
1927
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
|
1928
|
+
class _AnyVarargsArgs:
|
|
1929
|
+
"""N positional ANY-typed input columns, each free to be a different type."""
|
|
1930
|
+
|
|
1931
|
+
values: Annotated[list[AnyArrow], Arg(0, varargs=True, doc="Input columns of any types (echoed back)")]
|
|
1932
|
+
|
|
1933
|
+
|
|
1934
|
+
class BlendedAnyVarargsFunction(RowTransformFunction[_AnyVarargsArgs]):
|
|
1935
|
+
"""Blended 1->1 echo of N ANY-typed VARARGS input columns.
|
|
1936
|
+
|
|
1937
|
+
The varargs counterpart of ``blended_any``: every runtime column may resolve
|
|
1938
|
+
to a different concrete type, so the client must take each column's type
|
|
1939
|
+
from the call rather than from the (ANY) vararg element type. Output columns
|
|
1940
|
+
are ``col0..colN-1`` — the names the client generates for varargs blended
|
|
1941
|
+
input — each bound to its own resolved input type.
|
|
1942
|
+
"""
|
|
1943
|
+
|
|
1944
|
+
class Meta:
|
|
1945
|
+
name = "blended_any_varargs"
|
|
1946
|
+
description = "Blended 1->1 echo of N ANY-typed varargs input columns (col0..colN-1)"
|
|
1947
|
+
categories = ["blended", "test"]
|
|
1948
|
+
|
|
1949
|
+
@classmethod
|
|
1950
|
+
def on_bind(cls, params: BindParams[_AnyVarargsArgs]) -> BindResponse:
|
|
1951
|
+
input_schema = params.bind_call.input_schema
|
|
1952
|
+
assert input_schema is not None
|
|
1953
|
+
return BindResponse(output_schema=pa.schema([pa.field(f"col{i}", f.type) for i, f in enumerate(input_schema)]))
|
|
1954
|
+
|
|
1955
|
+
@classmethod
|
|
1956
|
+
def process(
|
|
1957
|
+
cls,
|
|
1958
|
+
params: ProcessParams[_AnyVarargsArgs],
|
|
1959
|
+
state: None,
|
|
1960
|
+
batch: pa.RecordBatch,
|
|
1961
|
+
out: OutputCollector,
|
|
1962
|
+
) -> None:
|
|
1963
|
+
out.emit(pa.record_batch(cls.input_columns(batch), schema=params.output_schema))
|
|
1964
|
+
|
|
1965
|
+
|
|
1883
1966
|
class CachedExplodeFunction(RowTransformFunction[_ExplodeArgs]):
|
|
1884
1967
|
"""Cacheable blended 1->N fan-out advertising vgi.cache.per_value.
|
|
1885
1968
|
|
vgi/_test_fixtures/worker.py
CHANGED
|
@@ -267,6 +267,8 @@ from vgi._test_fixtures.table.tt_pushdown import (
|
|
|
267
267
|
)
|
|
268
268
|
from vgi._test_fixtures.table_in_out import (
|
|
269
269
|
BatchIndexBufferInputFunction,
|
|
270
|
+
BlendedAnyFunction,
|
|
271
|
+
BlendedAnyVarargsFunction,
|
|
270
272
|
BlendedDropFunction,
|
|
271
273
|
BlendedExplodeFunction,
|
|
272
274
|
BufferEmitWideFunction,
|
|
@@ -432,6 +434,8 @@ _EXAMPLE_CATALOG = Catalog(
|
|
|
432
434
|
GeoEncode3Function,
|
|
433
435
|
RowSumFunction,
|
|
434
436
|
BlendedDropFunction,
|
|
437
|
+
BlendedAnyFunction,
|
|
438
|
+
BlendedAnyVarargsFunction,
|
|
435
439
|
BlendedExplodeFunction,
|
|
436
440
|
CachedExplodeFunction,
|
|
437
441
|
ProjectableBlendedFunction,
|
vgi/argument_spec.py
CHANGED
|
@@ -19,7 +19,8 @@ import warnings
|
|
|
19
19
|
from collections.abc import Sequence
|
|
20
20
|
from dataclasses import dataclass
|
|
21
21
|
from functools import cache
|
|
22
|
-
from
|
|
22
|
+
from types import UnionType
|
|
23
|
+
from typing import Annotated, Any, Union, get_args, get_origin, get_type_hints
|
|
23
24
|
|
|
24
25
|
import pyarrow as pa
|
|
25
26
|
|
|
@@ -395,14 +396,22 @@ def parameter_default_values_from_specs(
|
|
|
395
396
|
arrays: list[pa.Array[Any]] = []
|
|
396
397
|
names: list[str] = []
|
|
397
398
|
for spec in default_specs:
|
|
398
|
-
if pa.types.is_null(spec.arrow_type):
|
|
399
|
-
raise ValueError(
|
|
400
|
-
f"Parameter {spec.name!r} has a default but no concrete Arrow type; "
|
|
401
|
-
"typed defaults require an explicit parameter type"
|
|
402
|
-
)
|
|
403
399
|
value = spec.default_value
|
|
404
400
|
if isinstance(value, pa.Scalar):
|
|
405
401
|
value = value.as_py()
|
|
402
|
+
# An ANY-typed parameter (AnyArrow) has no concrete Arrow type, so a
|
|
403
|
+
# VALUE default cannot be represented. `None` still can: a null-typed
|
|
404
|
+
# array holds nulls, and it is column PRESENCE, not the value, that
|
|
405
|
+
# marks a parameter as having a default. Allowing it lets a required
|
|
406
|
+
# ANY parameter be declared `default=None` so that omitting it reaches
|
|
407
|
+
# the function's own on_bind() validation — which can name the argument
|
|
408
|
+
# and show its shape — instead of raising a bare KeyError during
|
|
409
|
+
# argument parsing, before any user code runs.
|
|
410
|
+
if pa.types.is_null(spec.arrow_type) and value is not None:
|
|
411
|
+
raise ValueError(
|
|
412
|
+
f"Parameter {spec.name!r} has a non-null default but no concrete Arrow type; "
|
|
413
|
+
"a typed default requires an explicit parameter type (None is allowed)"
|
|
414
|
+
)
|
|
406
415
|
try:
|
|
407
416
|
arrays.append(pa.array([value], type=spec.arrow_type))
|
|
408
417
|
except (pa.ArrowException, TypeError, ValueError) as exc:
|
|
@@ -538,6 +547,30 @@ def _format_range(
|
|
|
538
547
|
return f"{low}, {high}"
|
|
539
548
|
|
|
540
549
|
|
|
550
|
+
def _unwrap_optional(annotation: Any) -> Any:
|
|
551
|
+
"""``T | None`` -> ``T``; anything else unchanged.
|
|
552
|
+
|
|
553
|
+
A special marker type (``AnyArrow``, ``TableInput``) is recognised by
|
|
554
|
+
identity, so an Optional wrapper would hide it — an ``AnyArrow | None``
|
|
555
|
+
parameter would silently become a NULL-typed one, and DuckDB then cannot
|
|
556
|
+
cast anything into it. Unwrapping first lets a marker be declared nullable,
|
|
557
|
+
which is how a REQUIRED ANY argument opts into reaching its function's own
|
|
558
|
+
``on_bind()`` validation (with ``default=None``) instead of being rejected
|
|
559
|
+
during parsing. Mirrors what ``_accepts_none`` already looks for.
|
|
560
|
+
|
|
561
|
+
Args:
|
|
562
|
+
annotation: The declared type of an ``Annotated[T, Arg(...)]`` field.
|
|
563
|
+
|
|
564
|
+
Returns:
|
|
565
|
+
The single non-``None`` member of an optional union, else ``annotation``.
|
|
566
|
+
"""
|
|
567
|
+
origin = get_origin(annotation)
|
|
568
|
+
if origin is not Union and origin is not UnionType:
|
|
569
|
+
return annotation
|
|
570
|
+
members = [a for a in get_args(annotation) if a is not type(None)]
|
|
571
|
+
return members[0] if len(members) == 1 else annotation
|
|
572
|
+
|
|
573
|
+
|
|
541
574
|
def _constraint_kwargs(arg: Arg[Any]) -> dict[str, Any]:
|
|
542
575
|
"""Extract discovery-facing constraint metadata from an [`Arg`][] descriptor.
|
|
543
576
|
|
|
@@ -744,6 +777,7 @@ def _extract_argument_specs(
|
|
|
744
777
|
if type_args:
|
|
745
778
|
infer_type = type_args[0]
|
|
746
779
|
|
|
780
|
+
infer_type = _unwrap_optional(infer_type)
|
|
747
781
|
is_table_input = infer_type is TableInput
|
|
748
782
|
is_any_type = infer_type is AnyArrow or infer_type is AnyArrowValue
|
|
749
783
|
|
|
@@ -790,8 +824,8 @@ def _extract_argument_specs(
|
|
|
790
824
|
# Check for special types (AnyArrow, TableInput)
|
|
791
825
|
# Priority: Arg subscript type (Arg[AnyArrow]) > class type hint
|
|
792
826
|
# Also check _returns_any_arrow_value for Annotated[AnyArrowValue, ...]
|
|
793
|
-
hint = hints.get(attr_name)
|
|
794
|
-
type_param = getattr(arg_legacy, "_type_param", None)
|
|
827
|
+
hint = _unwrap_optional(hints.get(attr_name))
|
|
828
|
+
type_param = _unwrap_optional(getattr(arg_legacy, "_type_param", None))
|
|
795
829
|
is_table_input = type_param is TableInput or hint is TableInput
|
|
796
830
|
is_any_type = (
|
|
797
831
|
type_param is AnyArrow
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.34.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
|
|
@@ -2,7 +2,7 @@ vgi/__init__.py,sha256=8uLMNQcNzWjp8VzYtj2u98_Wp8WpB8c_dKlMv67TH0g,5384
|
|
|
2
2
|
vgi/_duckdb.py,sha256=YB5D7N3Bwg_xP6X8a5QlumtlAovSej1A1Go5XlNGVko,2162
|
|
3
3
|
vgi/_storage_profile.py,sha256=VkTsXojuE0tHEzurmteQSAiL1vI3CZSgYkL6D_h8GvE,5061
|
|
4
4
|
vgi/aggregate_function.py,sha256=v03wBSy2Rrl9F2pMYvu8GnGrTCQrAVm01HXIFNdjnm4,26886
|
|
5
|
-
vgi/argument_spec.py,sha256=
|
|
5
|
+
vgi/argument_spec.py,sha256=SADH97VlFu3asI8CWZ_I7XTuSxwqFciFgdupdAf-tkY,34231
|
|
6
6
|
vgi/arguments.py,sha256=ncnC1CS7KN-2rDjSw9vdf1uHqV5ozebet_eZtcq_ujw,72300
|
|
7
7
|
vgi/attach_header.py,sha256=sxpzCOVa5fl_-kQSCZgeUxS5I9Kuv8VCUjAOF5s1pr0,3782
|
|
8
8
|
vgi/auth.py,sha256=TlD5bhdyXnFuAoCnoHMU9ffjewVEnpwO5auIvp9GsCA,2486
|
|
@@ -52,12 +52,12 @@ vgi/_test_fixtures/http_server.py,sha256=AviomIEClCEuA9WPgQOzrMtQ484N04rQupK8dWO
|
|
|
52
52
|
vgi/_test_fixtures/nest_tensor.py,sha256=UjjW9UwipYG3ehQpZOm4hdzsafMyvVc0ZKJCrqiZTPU,24471
|
|
53
53
|
vgi/_test_fixtures/orchard_catalog.py,sha256=8Uv-mKTIgl-OYU9JLgG23owlHGmgniyF2chPoKXGtuI,1643
|
|
54
54
|
vgi/_test_fixtures/simple_writable.py,sha256=OmyENVnsgJmYH0_5sNWMt_Xgj9JoPaOu7uI5_ROZDKg,32020
|
|
55
|
-
vgi/_test_fixtures/table_in_out.py,sha256=
|
|
55
|
+
vgi/_test_fixtures/table_in_out.py,sha256=sWTSXwH-BUaSNOQsDmDqlFZxZ5GGtD-_xmysD2g7aV8,93148
|
|
56
56
|
vgi/_test_fixtures/table_in_out_same_name.py,sha256=jdsQNdKn64T54W_TcLB-rhydI69Y0Uzr5n6FU7v9rIU,8649
|
|
57
57
|
vgi/_test_fixtures/twin_catalogs.py,sha256=UFsFuZ_nFpe62HatGnOMru36vk35Xt3Ta1Hkr1aWN9w,4450
|
|
58
58
|
vgi/_test_fixtures/versioned.py,sha256=gnxNejc4cNGPKl_DxJk1SU8Ielj-rwmQYGkiShXzxK4,5786
|
|
59
59
|
vgi/_test_fixtures/versioned_tables.py,sha256=_JHPe6LaiO0x2We28Ion8hh6pSATmHZYvAczB4uwBsA,22272
|
|
60
|
-
vgi/_test_fixtures/worker.py,sha256=
|
|
60
|
+
vgi/_test_fixtures/worker.py,sha256=tAdCcL9x3LwgtY0OG56bapmBCwGZJ-M7paQY9hXjgB8,96440
|
|
61
61
|
vgi/_test_fixtures/accumulate/__init__.py,sha256=4hYT8jqRoVHSjV9TB7v0Z1CMJtdLuPaDWSz4J2fvMDs,868
|
|
62
62
|
vgi/_test_fixtures/accumulate/worker.py,sha256=4a2VaCoOIXPEoJpkNume7FoBlQzs9kRl8b-_ZJMHvnQ,30165
|
|
63
63
|
vgi/_test_fixtures/aggregate/__init__.py,sha256=2GCp80FNrsWFOA2SV3lCffzLjoN5AKwrf57t2YRg8O0,2393
|
|
@@ -152,8 +152,8 @@ vgi/transactor/_duckdb_compat.py,sha256=sXVZ9JLKAQyGR1BjWczSwdQEavtr-TcZPoVZZnTr
|
|
|
152
152
|
vgi/transactor/client.py,sha256=7DTeMksogsw6ANjQjGOPpKYrV76rg4_kGjktMJf54jg,4486
|
|
153
153
|
vgi/transactor/protocol.py,sha256=v59IvrKnuvwOXvn_HEcBAbCzDVcx0akgKN0R1mChXg0,5034
|
|
154
154
|
vgi/transactor/server.py,sha256=nzsZQxJZdgappdYX8okrFIrjFtUbdM_Hhskl1fZ2nDY,32553
|
|
155
|
-
vgi_python-0.
|
|
156
|
-
vgi_python-0.
|
|
157
|
-
vgi_python-0.
|
|
158
|
-
vgi_python-0.
|
|
159
|
-
vgi_python-0.
|
|
155
|
+
vgi_python-0.34.0.dist-info/METADATA,sha256=4-WKG1yNlJmW1IRUWKY1kssQ_QLYhOrSlZqQs_7dz28,25617
|
|
156
|
+
vgi_python-0.34.0.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
|
|
157
|
+
vgi_python-0.34.0.dist-info/entry_points.txt,sha256=3Kz1vgodw3pOL_xjtSyDB55-ZRy-U2X-X_Bdr582x0Q,165
|
|
158
|
+
vgi_python-0.34.0.dist-info/licenses/LICENSE,sha256=pbJb4zZasP6n5ifEV81wFu017TarjydaYVmGbHcehtY,6103
|
|
159
|
+
vgi_python-0.34.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|