Skip to main content

qsv-client

Run the qsv CSV toolkit from Python, without the usual subprocess pitfalls:

  • Timeouts that actually stop the work. On timeout or task cancellation, the run's whole process group gets SIGTERM, then SIGKILL after a grace period. That includes children qsv started itself, which would otherwise keep the output pipe open and hang the caller.
  • Typed errors instead of stderr scraping. A failure raises a QsvError subclass (QsvUsageError, QsvCsvError, QsvIOError, QsvNoMatch, QsvTimeout, ...) that carries the kind, message, exit code and command. Newer qsv builds report these as structured JSON (QSV_ERROR_FORMAT=json). Older ones are classified from their exit code.
  • Capability detection. Qsv().capabilities tells you the binary (qsv, qsvdp, qsvlite, qsvmcp), its version, compiled-in features and installed commands. It reads qsv --capabilities where available and falls back to parsing --version and --list.
  • Secrets stay off the command line. LLM settings for describegpt are passed as environment variables, so an API key never shows up in ps output.
  • Sync and asyncio clients with the same API. No runtime dependencies. Python 3.10+.

qsv itself is not bundled. Install it from the qsv releases or a package manager.

Install

pip install qsv-client

Usage

from qsv_client import Qsv, QsvCsvError, QsvTimeout

qsv = Qsv(timeout=600)  # finds $QSV_BIN, else qsv/qsvmcp/qsvdp/qsvlite on PATH

qsv.count("data.csv")  # -> 1000
qsv.headers("data.csv")  # -> ["id", "name", ...]
stats = qsv.stats("data.csv", "--everything")  # one dict per column
freq = qsv.frequency("data.csv", "--limit", "20")

# anything else: run(command, *args)
res = qsv.run("sqlp", "data.csv", "select count(*) from data")
res.stdout, res.stderr, res.exit_code, res.duration
res.csv_rows()  # or res.json()

# large output: stream stdout to a file instead of holding it in memory
qsv.run("stats", "big.csv", "--everything", stdout_path="big.stats.csv")

try:
    qsv.run("select", "a", "ragged.csv", timeout=30)
except QsvCsvError as e:
    print(e.kind, e.exit_code, e.message)
except QsvTimeout:
    ...

check=False returns the result even when qsv fails. Exit codes 0 and 255 (qsv's "warning", e.g. a broken pipe) count as success. stdin= feeds data on stdin; otherwise stdin is closed.

describegpt without exposing your key

import os

qsv = Qsv(
    llm_api_key=os.environ["OPENROUTER_API_KEY"],  # -> QSV_LLM_APIKEY
    llm_base_url="https://openrouter.ai/api/v1",  # -> QSV_LLM_BASE_URL
    llm_model="google/gemini-2.5-flash-lite",  # -> QSV_LLM_MODEL
)
dictionary = qsv.describegpt("data.csv", "--all")  # adds --format json, returns parsed JSON

asyncio

from qsv_client import AsyncQsv

qsv = AsyncQsv(timeout=600)
n = await qsv.count("data.csv")
caps = await qsv.capabilities()

Cancelling the awaiting task kills the run's process group.

Capabilities and version checks

qsv = Qsv(min_version="24.0.0")  # QsvVersionError on first use if older
caps = qsv.capabilities
caps.binary, caps.version, caps.features, caps.commands
caps.has_feature("polars"), caps.has_command("describegpt"), caps.supports_json_errors

Errors

Exception qsv error kind Typical exit code
QsvUsageError usage 2
QsvCsvError csv 1
QsvIOError io 1
QsvNoMatch no_match (e.g. search found nothing) 1
QsvNetworkError network 3
QsvOutOfMemory out_of_memory 4
QsvEncodingError encoding 5
QsvInferenceError inference (an LLM call failed) 1
QsvWriteError write (e.g. disk full) 1
QsvTimeout timeout (killed by this client) 124
QsvError anything else; base class of all of the above

QsvError.structured is True when the error came from qsv's JSON error line. With qsv versions that predate it, only the exit-code categories (usage, network, out of memory, encoding) can be told apart; other failures are QsvError with kind unknown, and the message is taken from stderr.

QsvNotFound (no binary) and QsvVersionError (binary too old) are raised before anything runs.

Platform notes

Process-group cleanup uses POSIX sessions (Linux, macOS). On Windows, qsv is started in a new process group and killed directly; its own children are not reaped.

Development

uv sync
uv run pytest                        # integration tests need qsv: QSV_BIN=/path/to/qsv
uv run ruff check . && uv run mypy

License

AGPL-3.0-or-later

Metadata

Release files for qsv-client 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qsv-client 0.1.0
File Size Uploaded
qsv_client-0.1.0.tar.gz 74.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qsv-client 0.1.0
File Interpreter ABI Platform
qsv_client-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 101.2 kB

Release files / qsv_client-0.1.0.tar.gz

Download URL qsv_client-0.1.0.tar.gz
Size 74.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ee00c1b23b1c82d2aabe3c8f9ba1efc005af79a16417b051a5fb13c79e73cc92
BLAKE2b-256 checksum
How to use checksums
699f9b7e1dc62f6514e85f9d63ce518210cc31da5ffb499c4c122c599703785f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / qsv_client-0.1.0-py3-none-any.whl

Download URL qsv_client-0.1.0-py3-none-any.whl
Size 27.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5a2a04a93ee9f689a294fd659c4f2a627cc5462d5a618ab34edfe7cdc3802030
BLAKE2b-256 checksum
How to use checksums
67c5eb90cab38ba04bb698dfe1963d6ed1eb4b5278a3888520057aedbe8132da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page