Skip to main content
Vane Data / Reference

vane.func

vane.func converts a synchronous Python function or bound method into a scalar Expression UDF. Passing a vane.Expression positionally returns a lazy Expression; Expression keyword arguments are not supported. A call with ordinary Python values executes the original function immediately.

Signature

text
vane.func(
    fn: _PythonFunction | None = None,
    *,
    return_dtype: Any | None = None,
    name: str | None = None,
) -> VaneFunction | Callable[[_PythonFunction], VaneFunction]

Parameters

NameTypeDescriptionDefault
fnSynchronous Python function, bound method, or NoneCallable to wrap. Omitting it returns a decoratorNone
return_dtypeSQL type string, Vane DuckDBPyType, supported pyarrow.DataType, or NoneExpression result type. It is not inferred from the Python return annotation and is required when building an ExpressionNone
nameNon-empty str or NoneUDF name in the plan; defaults to the function __qualname__None

Returns and errors

After decoration, the function can be used like any other Expression in select(). Each input row produces one result of type return_dtype. It can also be called directly with ordinary Python values.

In a query, an input SQL NULL propagates to the result without calling the function. A direct call keeps normal Python semantics and passes None to the function.

Invalid callable definitions or name values are rejected when the wrapper is created. Direct calls retain Python's normal argument validation and error timing. When building an Expression, Expression keyword arguments and a missing or invalid return_dtype raise immediately. The wrapped function is not invoked until query execution, so Python call errors—including an argument-count mismatch—raise when results are fetched, as do a None result for non-NULL input and values that do not match return_dtype. Distributed backends may retry calls, so external effects must be idempotent.

Example

example.py
import vane




@vane.func(return_dtype="VARCHAR")
def normalize(value):
    return value.strip().lower()




source = vane.sql("SELECT '  Vane  ' AS text")
result = source.select(normalize(vane.col("text")).alias("text"))


print(result.fetchall())
vane.close()

Output:

text
[('vane',)]