v0.28.0 — forced stop, decoded column statistics
Two client additions, and workers are no longer spawned through a shell.
Client.stop(force=True)SIGKILLs a direct subprocess worker before the graceful teardown, so a caller can abandon a worker wedged inside a handler. A graceful stop closes the stream and shuts the RPC connection down first, and both block on exactly the worker you are trying to escape. Pooled workers are returned to their pool rather than owned by the client, soforceis inert there; HTTP and TCP are already prompt.deserialize_column_statistics, the inverseserialize_column_statisticsnever had, plusClient.table_column_statisticsto fetch and decode in one call. Without it, any non-DuckDB client holding the bytes fromcatalog_table_column_statistics_gethad to re-implement the sparse-union layout.
Breaking: no shell, and server_path takes an argv sequence
Direct subprocess workers are spawned without a shell. Under shell=True the tracked process is the shell, so stop(force=True) killed the shell and left the worker holding the pipe — a forced stop still waited the full length of whatever the worker was doing (5.18s against a 5s sleep on Linux), and on Windows it killed cmd.exe and reported exit code 1 while the worker ran on. The pooled and catalog paths had always spawned without a shell; the direct path now matches.
Consequences:
- Shell syntax in
server_pathis no longer interpreted — no pipes, redirection,VAR=valueprefixes, or~expansion. server_pathnow acceptsstr | Sequence[str]. Pass a sequence to give the argv exactly, which is what you want for any argument carrying spaces or quotes ([sys.executable, "-c", script]). Splitting a string is ambiguous on Windows:shlex.splitneedsposix=Falsethere so backslashes in paths survive, which leaves quotes attached to the token.
Also requires vgi-rpc 0.42.1.