Skip to content
Query.Farm
Talk with Us

vgi.client

Module overview

VGI client package for communicating with VGI workers.

This package provides:

  • Client: A class for programmatic interaction with VGI workers, including both function invocation and catalog operations
  • ClientError: Exception raised by Client function operations
  • CatalogClientMixin: Mixin class providing catalog operations
  • OutputWriter: Helper for writing output in various formats
  • main: CLI entry point

Usage (API):

from vgi.client import Client, ClientError
from vgi.arguments import Arguments
with Client("./my_worker.py") as client:
for batch in client.table_in_out_function(
function_name="echo",
arguments=Arguments(positional=[], named={}),
input=input_batches,
):
process(batch)

Usage (Catalog API):

from vgi.client import Client
client = Client("./my_worker")
result = client.catalog_attach(
name="my_catalog", options={}, data_version_spec=None, implementation_version=None
)

Usage (CLI):

vgi-client --input data.parquet --function echo
vgi-client --input data.parquet --function sum_all_columns
source

Description

Mixin that adds catalog operations to a VGI Client.

Catalog methods spawn ephemeral connections under the hood — for subprocess transport a pooled subprocess worker; for HTTP transport a short-lived http_connect session reusing the Client’s shared httpx2.Client (bearer token, headers); for TCP transport a short-lived tcp_connect session. Browsing catalogs over HTTP is the canonical non-DuckDB use case this mixin supports.

Other attributes expected from Client: _transport (subprocess, http, or tcp), _base_url (HTTP base URL), _tcp_host / _tcp_port (TCP endpoint), and _get_or_create_httpx_client() (shared HTTP client factory).

Attributes

str | Sequence[str]

Worker command used for subprocess transport — a string to be split, or an argv sequence taken as-is.

Methods

source
catalogs() -> list[CatalogInfo]

Get list of catalog discovery records from the worker.

Returns

List of CatalogInfo records carrying per-catalog name, implementation_version, and data_version_spec.
source
catalog_attach(
*,
name: str,
options: dict[str, Any] | None = None,
data_version_spec: str | None,
implementation_version: str | None,
) -> CatalogAttachResult

Attach to a catalog.

Parameters

name
The catalog name to attach to.
options
Optional dictionary of catalog-specific options.
data_version_spec
Semver constraint for the catalog’s data version (None = unconstrained — worker picks).
implementation_version
Semver constraint for the worker’s implementation version (None = unconstrained).

Returns

CatalogAttachResult with attach_opaque_data, catalog capabilities, and the resolved concrete versions the worker picked.
source
catalog_detach(*, attach_opaque_data: AttachOpaqueData) -> None

Detach from a catalog.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
source
catalog_create(
*,
name: str,
on_conflict: OnConflict = OnConflict.ERROR,
options: dict[str, Any] | None = None,
) -> None

Create a new catalog.

Parameters

name
The name for the new catalog.
on_conflict
Behavior if catalog already exists.
options
Optional dictionary of catalog-specific options.
source
catalog_drop(*, name: str) -> None

Drop a catalog.

Parameters

name
The name of the catalog to drop.
source
catalog_version(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
) -> int

Get the current catalog version.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID for transactional reads.

Returns

The current catalog version number, or 0 if empty.
source
catalog_transaction_begin(
*,
attach_opaque_data: AttachOpaqueData,
) -> TransactionOpaqueData | None

Begin a new transaction.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.

Returns

TransactionOpaqueData for the new transaction, or None if transactions are not supported by this catalog.
source
catalog_transaction_commit(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData,
) -> None

Commit a transaction.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
The transaction ID to commit.
source
catalog_transaction_rollback(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData,
) -> None

Rollback a transaction.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
The transaction ID to rollback.
source
schemas(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
) -> list[SchemaInfo]

List schemas in the catalog.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID for transactional reads.

Returns

List of SchemaInfo for each schema in the catalog.
source
schema_get(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
name: str,
) -> SchemaInfo | None

Get information about a schema.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID for transactional reads.
name
The schema name.

Returns

SchemaInfo for the schema, or None if not found.
source
schema_create(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
name: str,
comment: str | None = None,
tags: dict[str, str] | None = None,
) -> None

Create a new schema.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
name
The name for the new schema.
comment
Optional description of the schema.
tags
Optional key-value tags for the schema.
source
schema_drop(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
name: str,
ignore_not_found: bool = False,
cascade: bool = False,
) -> None

Drop a schema.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
name
The name of the schema to drop.
ignore_not_found
If True, don’t error if schema doesn’t exist.
cascade
If True, drop all contained tables and views.
source
schema_contents(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
name: str,
type: SchemaObjectType,
) -> Sequence[TableInfo | ViewInfo | FunctionInfo | MacroInfo]

List contents of a schema (tables, views, functions, macros).

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID for transactional reads.
name
The schema name.
type
The type of objects to return. Must be a SchemaObjectType enum:
SchemaObjectType.TABLE: Return only tables
SchemaObjectType.VIEW: Return only views
SchemaObjectType.SCALAR_FUNCTION: Return only scalar functions
SchemaObjectType.TABLE_FUNCTION: Return only table functions
SchemaObjectType.SCALAR_MACRO: Return only scalar macros
SchemaObjectType.TABLE_MACRO: Return only table macros

Returns

List of TableInfo, ViewInfo, FunctionInfo, or MacroInfo depending on the type.
source
table_get(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
) -> TableInfo | None

Get information about a table.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID for transactional reads.
schema_name
The schema containing the table.
name
The table name.

Returns

TableInfo for the table, or None if not found.
source
table_column_statistics(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
) -> list[ColumnStatistics]

Fetch a table’s column statistics, decoded.

Workers may inline statistics on TableInfo (see TableInfo.column_statistics) or serve them lazily through this per-table call, which is what TableInfo.supports_column_statistics advertises. Prefer the inlined copy when present and fall back to this.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID for transactional reads.
schema_name
The schema containing the table.
name
The table name.

Returns

Per-column statistics, or an empty list when the worker has none for this table.
source
table_create(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
columns: SerializedSchema,
on_conflict: OnConflict = OnConflict.ERROR,
not_null_constraints: list[int] | None = None,
unique_constraints: list[list[int]] | None = None,
check_constraints: list[str] | None = None,
) -> None

Create a new table.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema to create the table in.
name
The name for the new table.
columns
Serialized PyArrow schema for the table columns.
on_conflict
Behavior if table already exists.
not_null_constraints
Column indices that must not be null.
unique_constraints
Lists of column indices for unique constraints.
check_constraints
SQL expressions for check constraints.
source
table_drop(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
ignore_not_found: bool = False,
cascade: bool = False,
) -> None

Drop a table.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The name of the table to drop.
ignore_not_found
If True, don’t error if table doesn’t exist.
cascade
If True, also drop dependent objects.
source
table_scan_function_get(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
at_unit: str | None = None,
at_value: str | None = None,
) -> ScanFunctionResult

Get the scan function for a table.

Returns a ScanFunctionResult that tells the VGI DuckDB extension which DuckDB function to call to obtain the table data.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID for transactional reads.
schema_name
The schema containing the table.
name
The table name.
at_unit
Optional time travel unit (e.g., ‘timestamp’, ‘version’).
at_value
Optional time travel value.

Returns

ScanFunctionResult with function_name, arguments, and extensions.

Raises

CatalogClientError
If table_scan_function_get returned no result.
source
table_comment_set(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
comment: str | None,
ignore_not_found: bool = False,
) -> None

Set or clear the comment on a table.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The table name.
comment
The new comment, or None to clear.
ignore_not_found
If True, don’t error if table doesn’t exist.
source
table_rename(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
new_name: str,
ignore_not_found: bool = False,
) -> None

Rename a table.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The current name of the table.
new_name
The new name for the table.
ignore_not_found
If True, don’t error if table doesn’t exist.
source
table_column_add(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
column_definition: SerializedSchema,
ignore_not_found: bool = False,
if_column_not_exists: bool = False,
) -> None

Add a new column to a table.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The table name.
column_definition
Serialized schema with single field for the new column.
ignore_not_found
If True, don’t error if table doesn’t exist.
if_column_not_exists
If True, don’t error if column already exists.
source
table_column_drop(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
column_name: str,
ignore_not_found: bool = False,
if_column_exists: bool = False,
cascade: bool = False,
) -> None

Drop a column from a table.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The table name.
column_name
The name of the column to drop.
ignore_not_found
If True, don’t error if table doesn’t exist.
if_column_exists
If True, don’t error if column doesn’t exist.
cascade
If True, drop dependent constraints.
source
table_column_rename(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
column_name: str,
new_column_name: str,
ignore_not_found: bool = False,
) -> None

Rename a column.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The table name.
column_name
The current name of the column.
new_column_name
The new name for the column.
ignore_not_found
If True, don’t error if table doesn’t exist.
source
table_column_default_set(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
column_name: str,
expression: SqlExpression,
ignore_not_found: bool = False,
) -> None

Set the default value expression for a column.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The table name.
column_name
The column to set the default for.
expression
The SQL expression for the default value.
ignore_not_found
If True, don’t error if table doesn’t exist.
source
table_column_default_drop(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
column_name: str,
ignore_not_found: bool = False,
) -> None

Remove the default value from a column.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The table name.
column_name
The column to remove the default from.
ignore_not_found
If True, don’t error if table doesn’t exist.
source
table_column_type_change(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
column_definition: SerializedSchema,
expression: SqlExpression | None = None,
ignore_not_found: bool = False,
) -> None

Change the type of a column.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The table name.
column_definition
Serialized schema with single field defining the new type. Column name is taken from the schema field name.
expression
Optional SQL expression to convert existing values.
ignore_not_found
If True, don’t error if table doesn’t exist.
source
table_not_null_drop(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
column_name: str,
ignore_not_found: bool = False,
) -> None

Remove NOT NULL constraint from a column.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The table name.
column_name
The column to remove NOT NULL from.
ignore_not_found
If True, don’t error if table doesn’t exist.
source
table_not_null_set(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
column_name: str,
ignore_not_found: bool = False,
) -> None

Add NOT NULL constraint to a column.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the table.
name
The table name.
column_name
The column to add NOT NULL to.
ignore_not_found
If True, don’t error if table doesn’t exist.
source
view_get(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
) -> ViewInfo | None

Get information about a view.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID for transactional reads.
schema_name
The schema containing the view.
name
The view name.

Returns

ViewInfo for the view, or None if not found.
source
view_create(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
definition: str,
on_conflict: OnConflict = OnConflict.ERROR,
) -> None

Create a new view.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema to create the view in.
name
The name for the new view.
definition
The SQL SELECT statement defining the view.
on_conflict
Behavior if view already exists.
source
view_drop(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
ignore_not_found: bool = False,
cascade: bool = False,
) -> None

Drop a view.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the view.
name
The name of the view to drop.
ignore_not_found
If True, don’t error if view doesn’t exist.
cascade
If True, also drop dependent objects.
source
view_rename(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
new_name: str,
ignore_not_found: bool = False,
) -> None

Rename a view.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the view.
name
The current name of the view.
new_name
The new name for the view.
ignore_not_found
If True, don’t error if view doesn’t exist.
source
view_comment_set(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
comment: str | None,
ignore_not_found: bool = False,
) -> None

Set or clear the comment on a view.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the view.
name
The view name.
comment
The new comment, or None to clear.
ignore_not_found
If True, don’t error if view doesn’t exist.
source
macro_get(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
) -> MacroInfo | None

Get information about a macro.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID for transactional reads.
schema_name
The schema containing the macro.
name
The macro name.

Returns

MacroInfo for the macro, or None if not found.
source
macro_create(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
macro_type: MacroType,
parameters: list[str],
definition: str,
on_conflict: OnConflict = OnConflict.ERROR,
parameter_default_values: pa.RecordBatch | None = None,
arguments_schema: pa.Schema | None = None,
) -> None

Create a new macro.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema to create the macro in.
name
The name for the new macro.
macro_type
Whether this is a scalar or table macro.
parameters
Ordered list of parameter names.
definition
SQL expression (scalar) or query (table).
on_conflict
Behavior if macro already exists.
parameter_default_values
One-row RecordBatch with typed defaults.
arguments_schema
Optional Arrow schema (one nullable field per parameter, in parameters order) carrying per-parameter descriptions via the vgi_doc field metadata key. Build with vgi.argument_spec.macro_arguments_schema.
source
macro_drop(
*,
attach_opaque_data: AttachOpaqueData,
transaction_opaque_data: TransactionOpaqueData | None = None,
schema_name: str,
name: str,
ignore_not_found: bool = False,
) -> None

Drop a macro.

Parameters

attach_opaque_data
The attachment ID from catalog_attach.
transaction_opaque_data
Optional transaction ID.
schema_name
The schema containing the macro.
name
The name of the macro to drop.
ignore_not_found
If True, don’t error if macro doesn’t exist.
source

Bases: CatalogClientMixin

Description

Canonical VGI client — HTTP is the path other-language ports mirror.

Two transports:

  • HTTP (Client.from_http(base_url, bearer_token=...)). The canonical non-DuckDB path. Uses vgi_rpc.http.http_connect under the hood; transparently resolves pointer batches returned by workers that externalize large outputs (demo storage, S3). Transparently externalizes large input batches when the server advertises upload-URL support.
  • Subprocess (Client(server_path)). Python-only convenience for local workers. Uses shell subprocesses + a WorkerPool for reuse. Ports don’t need to mirror this.

Catalog operations (catalogs(), schema_contents(), etc.) are provided by CatalogClientMixin and don’t require start(). They open a short-lived connection per call (HTTP) or borrow a pooled subprocess worker.

Function invocation (scalar_function, table_function, table_in_out_function) requires start() — typically via the context-manager protocol:

with Client.from_http("http://host:port", bearer_token="...") as c:
for batch in c.table_function(function_name="sequence", ...):
...

Attributes

float

Seconds to wait for a worker thread to join during shutdown.

float

Seconds to wait for a worker process to exit during shutdown.

str | Sequence[str]

Subprocess-only. The VGI worker command. A string is split with shlex.split; pass a sequence to give the argv exactly, which is what you want for arguments carrying spaces or quotes ([sys.executable, "-c", script]). No shell is involved either way, so shell syntax — pipes, redirection, VAR=value prefixes, ~ expansion — is not interpreted.

Subprocess-only. If True, worker stderr is passed through to the parent process’s stderr in real-time.

bool

Whether this client’s transport can drive :meth:table_scan_resumable.

True only for HTTP, whose producer streams round-trip state in continuation tokens. The pipe/subprocess transport holds a live stream with no serializable resume point.

Methods

source
from_http(
base_url: str,
*,
bearer_token: str | None = None,
httpx_client: Any | None = None,
external_location: Any | None = None,
worker_limit: int | None = None,
attach_opaque_data: bytes | None = None,
) -> Client

Create a Client bound to a remote HTTP VGI worker.

Canonical entry point for non-DuckDB callers (e.g. a TypeScript port browsing catalog contents). Subprocess-specific kwargs are not accepted; pool/stderr semantics do not apply.

source
from_tcp(
host: str,
port: int,
*,
external_location: Any | None = None,
worker_limit: int | None = None,
attach_opaque_data: bytes | None = None,
) -> Client

Create a Client bound to a running TCP VGI worker.

Connects via vgi_rpc.rpc.tcp_connect (raw Arrow-IPC framing). The framing carries no authentication or encryption — only connect to trusted endpoints on loopback or a trusted network; use Client.from_http(...) for untrusted networks. Spin up a matching worker with vgi-fixture-worker --tcp [HOST:]PORT.

source
get_worker_stderr() -> str

Return all captured stderr from the worker processes.

Returns stderr output from the primary worker and all additional workers spawned for parallel processing. The output is accumulated in a shared buffer throughout the client’s lifetime.

This method is thread-safe and can be called while processing is ongoing, though the buffer may not yet contain all output until the workers have completed.

Note

This method only returns data when passthrough_stderr=False was set in the constructor. When passthrough_stderr=True, stderr goes directly to the parent process’s stderr and this method returns an empty string.

Returns

All captured stderr output as a UTF-8 decoded string. Invalid UTF-8 sequences are replaced with the Unicode replacement character.
source
start() -> None

Start the primary worker subprocess.

Spawns the worker process using the server_path configured in init, sets up RPC transport, and creates a typed VgiProtocol proxy for method calls.

After this method returns, the client is ready to invoke functions via table_in_out_function(), table_function(), or scalar_function(). When using the context manager protocol (with statement), this method is called automatically.

The stderr buffer is cleared when start() is called, so any stderr from previous runs is discarded.

Raises

ClientError
If the client is already started (call stop() first), or if stdout/stderr pipes fail to be created.
source
stop(*, force: bool = False) -> int

Stop all worker subprocesses and clean up resources.

Terminates all workers in the following order:

  1. Stops all additional workers (spawned for parallel processing)
  2. Stops the primary worker
  3. Waits for all stderr drain threads to complete (with timeout)
  4. Resets all internal state

After this method returns, the client can be started again with start(). When using the context manager protocol (with statement), this method is called automatically on exit.

Cancelling an in-flight call

A graceful stop waits for a worker that is blocked inside a handler, so it cannot be used to abandon a scan that has overrun its budget. Pass force=True to SIGKILL direct subprocess workers first, which unblocks any thread waiting on their output immediately. Note this only applies to direct subprocess workers: a pooled worker is returned to its pool rather than owned by this client, so construct the client with pool=None when you need to be able to cancel it. HTTP and TCP workers are already prompt to close.

Parameters

force
Kill direct subprocess workers instead of shutting them down gracefully — see “Cancelling an in-flight call” above.

Returns

The exit code of the primary worker process. Returns 0 for normal termination, non-zero values indicate errors (a forced stop reports the signal exit code, -9 on POSIX). Exit codes from additional workers are logged but not returned.

Raises

ClientError
If the client was not started (call start() first).
source
server_capabilities() -> Any

Return the HTTP server’s advertised capabilities.

Only valid for HTTP-mode clients. The returned HttpServerCapabilities carries max_request_bytes, upload_url_support, and max_upload_bytes — the fields the client consults before deciding to externalize large input batches via upload URLs (see Phase 4 of the whimsical-mccarthy plan).

source
table_in_out_function(
*,
function_name: str,
schema_name: str,
input: Iterator[pa.RecordBatch],
arguments: Arguments | None = None,
bind_result_callback: Callable[[BindResponse], None] | None = None,
projection_ids: list[int] | None = None,
pushdown_filters: bytes | None = None,
settings: dict[str, Any] | None = None,
transaction_opaque_data: bytes | None = None,
) -> Generator[pa.RecordBatch]

Invoke a table-in-out function on the worker and stream results.

For parallel processing (max_workers > 1), input batches are distributed round-robin across workers using dedicated threads. Output order may not match input order in parallel mode. Only the primary worker receives the FINALIZE phase and produces final aggregated output.

Parameters

function_name
Name of the function to invoke. Must exist in the worker’s registry.
schema_name
Name of the catalog schema that declares the function. Required — a worker may register one name in several schemas, so the (schema, name) pair is what identifies the implementation.
input
Iterator yielding input RecordBatches. Must yield at least one batch. The first batch’s schema is used to initialize the IPC stream. Raises ClientError if the iterator is empty.
arguments
Optional Arguments container with positional and named arguments to pass to the function. Defaults to empty Arguments().
bind_result_callback
Optional callback invoked with the BindResponse before processing begins.
projection_ids
Optional list of column indices for column projection.
pushdown_filters
Optional byte string containing filter predicates to push down to the function.
settings
Optional dictionary of settings/pragmas to pass to the function.
transaction_opaque_data
Optional unique identifier for the DuckDB transaction.

Raises

ClientError
If the client is not started, input iterator is empty, input iterator yields non-RecordBatch objects, communication with the worker fails, or the worker returns an unexpected status or exception.
source
table_buffering_function(
*,
function_name: str,
schema_name: str,
input: Iterator[pa.RecordBatch],
arguments: Arguments | None = None,
bind_result_callback: Callable[[BindResponse], None] | None = None,
projection_ids: list[int] | None = None,
pushdown_filters: bytes | None = None,
settings: dict[str, Any] | None = None,
transaction_opaque_data: bytes | None = None,
) -> Generator[pa.RecordBatch]

Invoke a TableBufferingFunction (Sink+Source) and stream results.

This mirrors the C++ PhysicalVgiTableBufferingFunction operator rather than the streaming INPUT/FINALIZE path used by :meth:table_in_out_function. The sequence is:

  1. bindinit(phase=TABLE_BUFFERING) on the primary worker. The sink init persists init metadata to cold storage so any pool worker can serve subsequent process/combine RPCs; its stream carries no data, so it is closed immediately after the header.
  2. table_buffering_process (unary) per input batch — the worker sinks the batch and returns an opaque state_id.
  3. table_buffering_combine (unary) once at end-of-input — the worker hands all state_ids to user combine() and returns opaque finalize_state_ids (the source-side partition keys).
  4. init(phase=TABLE_BUFFERING_FINALIZE, finalize_state_id=...) per finalize key — a producer stream driving user finalize() per tick. Output batches are yielded in finalize-key order.
  5. table_buffering_destructor (unary, best-effort) for cleanup.

Unlike :meth:table_in_out_function this driver runs entirely on the primary worker connection (process/combine are unary RPCs); the worker buffers all input regardless, so the aggregate result is identical to the distributed C++ path.

Parameters

function_name
Name of the TableBufferingFunction to invoke.
schema_name
Name of the catalog schema that declares the function. Required — a worker may register one name in several schemas, so the (schema, name) pair is what identifies the implementation.
input
Iterator yielding input RecordBatches. May be empty — buffering aggregations still produce a result for zero rows.
arguments
Optional Arguments container. Defaults to empty.
bind_result_callback
Optional callback invoked with the BindResponse before processing begins.
projection_ids
Optional column indices for projection.
pushdown_filters
Optional serialized filter predicates.
settings
Optional settings/pragmas to pass to the function.
transaction_opaque_data
Optional DuckDB transaction identifier.

Raises

ClientError
If the client is not started or any RPC fails.
source
table_function(
*,
function_name: str,
schema_name: str,
arguments: Arguments | None = None,
bind_result_callback: Callable[[BindResponse], None] | None = None,
projection_ids: list[int] | None = None,
pushdown_filters: bytes | None = None,
settings: dict[str, Any] | None = None,
transaction_opaque_data: bytes | None = None,
) -> Generator[pa.RecordBatch]

Invoke a table function (source function) and stream output batches.

Table functions generate output batches without receiving input data. They are useful for data sources, generators, or functions that produce results based solely on their arguments.

For parallel processing (max_workers > 1), output is read from all workers concurrently using threads. Output order is non-deterministic.

Parameters

function_name
Name of the function to invoke. Must exist in the worker’s registry and be a table function (not table-in-out).
schema_name
Name of the catalog schema that declares the function. Required — a worker may register one name in several schemas, so the (schema, name) pair is what identifies the implementation.
arguments
Optional Arguments container with positional and named arguments to pass to the function. Defaults to empty Arguments().
bind_result_callback
Optional callback invoked with the BindResponse before processing begins.
projection_ids
Optional list of column indices for column projection.
pushdown_filters
Optional byte string containing filter predicates to push down to the function.
settings
Optional dictionary of settings/pragmas to pass to the function.
transaction_opaque_data
Optional unique identifier for the DuckDB transaction.

Raises

ClientError
If the client is not started, communication with the worker fails, or the worker returns an exception.
source
table_scan_resumable(
*,
function_name: str,
schema_name: str,
arguments: Arguments | None = None,
projection_ids: list[int] | None = None,
pushdown_filters: bytes | None = None,
settings: dict[str, Any] | None = None,
transaction_opaque_data: bytes | None = None,
resume_token: bytes | None = None,
) -> ResumableTableScan

Open (or resume) a resumable table-function scan.

Resumable variant of :meth:table_function: the returned :class:ResumableTableScan yields (batch, token) one batch at a time, surfacing the worker’s continuation token so a stateless caller can persist it and resume on another process/node.

When resume_token is given, the scan continues from that token (the bind/init is still issued — the upstream’s first turn is produced and discarded — so the same function_name/projection/filters must be supplied). When None, a fresh scan starts.

Parameters

function_name
Name of the table function to scan.
schema_name
Name of the catalog schema that declares the function. Required — a worker may register one name in several schemas, so the (schema, name) pair is what identifies the implementation.
arguments
Positional/named arguments for the function’s bind.
projection_ids
Optional column indices to project (projection pushdown). None selects all columns.
pushdown_filters
Optional serialized filter-pushdown payload.
settings
Optional DuckDB settings to apply for the scan.
transaction_opaque_data
Optional catalog transaction handle.
resume_token
Continuation token from a prior batch to resume from; must be paired with the same function_name/projection/ filters. None starts a fresh scan.

Returns

A ResumableTableScan yielding (batch, token) pairs.

Raises

ResumeUnsupported
If the transport is not HTTP.
ClientError
If the client is not started or the worker errors.
source
table_scan_continue(
*,
resume_token: bytes,
output_schema: pa.Schema | None = None,
) -> ResumableTableScan

Resume a producer table scan from a continuation token WITHOUT re-binding.

The cheap counterpart to table_scan_resumable(resume_token=...): a continuation token is a signed, self-describing snapshot of the worker’s producer state, so the server recovers state + schemas + function identity from the token alone. This skips the bind/init round-trip (and the discarded first turn) that table_scan_resumable pays — the right primitive for a stateless relay that holds a per-batch token and resumes on any node every batch.

The client must be started and connected to a worker that honours the token (the token is verified against the caller’s auth identity, and routed by the same init stream method that minted it). HTTP transport only.

Parameters

resume_token
A token previously returned by ResumableTableScan.next().
output_schema
Unused on the producer-continuation path (each response carries its own schema); accepted for symmetry with table_scan_resumable.

Returns

A ResumableTableScan positioned AFTER the token; next() continues the stream, yielding (batch, token) per call.

Raises

ResumeUnsupported
If the transport is not HTTP.
ClientError
If the client is not started.
source
scalar_function(
*,
function_name: str,
schema_name: str,
input: Iterator[pa.RecordBatch],
arguments: Arguments | None = None,
bind_result_callback: Callable[[BindResponse], None] | None = None,
settings: dict[str, Any] | None = None,
secrets: dict[str, Any] | None = None,
transaction_opaque_data: bytes | None = None,
) -> Generator[pa.RecordBatch]

Invoke a scalar function on the worker and stream results.

Scalar functions transform input batches to single-column output with 1:1 row mapping. Processing ends when input is exhausted.

For parallel processing (max_workers > 1), input batches are distributed round-robin across workers using dedicated threads. Output order may not match input order in parallel mode.

Parameters

function_name
Name of the function to invoke. Must exist in the worker’s registry.
schema_name
Name of the catalog schema that declares the function. Required — a worker may register one name in several schemas, so the (schema, name) pair is what identifies the implementation.
input
Iterator yielding input RecordBatches. Must yield at least one batch. The first batch’s schema is used to initialize the IPC stream. Raises ClientError if the iterator is empty.
arguments
Optional Arguments container with positional and named arguments to pass to the function. Defaults to empty Arguments().
bind_result_callback
Optional callback invoked with the BindResponse before processing begins.
settings
Optional dictionary of settings/pragmas to pass to the function.
secrets
Optional dictionary of secret name to value pairs. Values can be simple scalars or dicts (for struct-typed secrets).
transaction_opaque_data
Optional unique identifier for the DuckDB transaction.

Raises

ClientError
If the client is not started, input iterator is empty, input iterator yields non-RecordBatch objects, communication with the worker fails, or the worker returns an unexpected status or exception.
Inherited members (37)
source

Bases: Exception

Description

Error raised by Client operations.

The first line of str(ClientError) is the remote exception as the worker raised it ({error_type}: {error_message}), so that whatever a user typed into their raise ValueError(...) shows up at the top of their traceback instead of being buried under VGI framing. Remote traceback and worker-stderr excerpts, when present, follow after an empty line.

Methods

source
from_rpc_error(e: RpcError) -> ClientError

Create a ClientError from an RpcError, including remote traceback.

Lead with the user’s exception (error_type: error_message) so the most actionable line is first. The Remote traceback section trails and is only included when the worker produced one.

source
main() -> None

CLI entry point for vgi-client.

source

Description

Handles writing output batches in various formats.

Supported formats

  • json: JSON Lines format (one JSON object per row)
  • csv: CSV with header
  • parquet: Apache Parquet columnar format
  • arrow-ipc: Apache Arrow IPC streaming format (useful for debugging)

The arrow-ipc format writes batches in the standard Arrow IPC streaming format, which can be read by any Arrow implementation. This is useful for:

- Debugging VGI protocol issues
- Inspecting raw output data with tools like pyarrow or arrow CLI
- Piping data to other Arrow-aware tools

Attributes

Path to output file, “-” for stdout, or None for logging.

Output format (“parquet”, “csv”, “json”, or “arrow-ipc”).

Optional schema for the output data.

Methods

source
write_batch(batch: pa.RecordBatch) -> None

Write a batch to the output destination in the configured format.

source
close() -> None

Close the underlying writer if one exists.

source

Description

A resumable, one-batch-at-a-time handle on an upstream table-function scan.

Unlike :meth:Client.table_function (a live generator that hides the server’s continuation token), each :meth:next returns (batch, token) where token is the worker’s serialized producer state AFTER batch. A stateless client (e.g. a load-balanced proxy) can persist token, drop the connection, and resume on another node via Client.table_scan_resumable(resume_token=token, ...).

Single-worker: reads the primary stream only (parallel max_workers>1 reads are unordered and not resumable from a single token).

Methods

source
next() -> tuple[pa.RecordBatch | None, bytes | None]

Return (batch, resume_token); (None, None) at end-of-stream.

resume_token resumes the scan AFTER batch on any node.

source
close() -> None

Release the underlying stream (no-op over HTTP — stateless).

source

Bases: ClientError

Description

Raised when a resumable scan is requested on a non-resumable transport.

Only the HTTP transport round-trips producer state in continuation tokens, so only HTTP clients can drive :meth:Client.table_scan_resumable. On the pipe/subprocess transport the stream is a live connection with no serializable resume point; the caller must keep the live stream in-process instead.

Inherited members (1)
  • from_rpc_error method · from ClientError — Create a ClientError from an RpcError, including remote traceback.