threshio
Know a delimited file before your code reads it.
A CSV, TSV or pipe-delimited export arrives from somewhere you do not control. threshio says how it is written, what its columns hold, and whether its rows fit the schema you expect, value by value, with a stable code for everything that is wrong and the fix when there is one. Then it hands the rows that fit, and the rows that do not, to pyarrow, polars or DuckDB without a copy.
pip install threshio
No dependencies: the package carries libthresh, the C library of thresh, built for Linux (glibc and musl), macOS and Windows on x86-64 and arm64. Python 3.9 and later.
The files below are an invented orchard's harvest, from the repository's
examples/data.
Every block on this page is run by the package's tests, and its output
compared with what is shown.
A first look
import threshio
print(threshio.sniff("data/harvest.csv"))
schema = threshio.infer("data/harvest.csv").schema
print(schema)
report = threshio.validate("data/harvest.csv", schema)
print(report)
data/harvest.csv: delimiter=, quote=" escape=doubled newline=lf header=yes confidence=high
Schema(lug_id int64, picked_on date, orchard_row string, lug_kg decimal, cultivar string, bruised bool, remark string)
pass data/harvest.csv 40 rows 0 rejected
sniff reads the dialect and says how sure it is. infer reads every row
and gives the schema the values fit. validate checks a file against a
schema: yours, or one infer wrote. A schema is JSON (schema.save(path),
threshio.Schema.load(path)), and every call that takes one also takes a
dict, JSON text, a .json path, or columns inline:
"lug_id:int64,lug_kg:decimal(5,2)".
A bad delivery
The same columns, with what goes wrong in a real feed:
report = threshio.validate("data/harvest_dirty.csv", schema)
print(report)
for line in report.rejected_lines:
print(line.row, line.errors)
print(report.column("lug_kg").samples)
fail data/harvest_dirty.csv 30 rows 4 rejected
5 [RowError(column='lug_kg', code='not_decimal')]
9 [RowError(column='picked_on', code='not_in_format')]
13 [RowError(column='bruised', code='not_bool')]
22 [RowError(column=None, code='ragged')]
[Sample(row=5, value='18,5', code='not_decimal')]
A file that fails is a report, not an exception: its result is pass,
fail or error (it could not be read, or its dialect is not settled),
and problems says why. Each column has its nulls, mismatches,
conformance and statistics (report.column("lug_kg").stats: min, max,
distinct values, the most common, their shapes).
Where code should not go on with a file that fails, check raises:
strict = schema.with_column("lug_id", nullable=False)
try:
threshio.check("data/harvest_dirty.csv", strict)
except threshio.FileFailed as e:
print(e)
print(e.report.rejected_sample)
data/harvest_dirty.csv: fail: 5 of 30 rows rejected
[5, 9, 13, 17, 22]
Into pyarrow, polars and DuckDB
read opens the file and is itself an Arrow stream (the PyCapsule
protocol), typed by the schema: decimals as decimal128, dates as date32,
bools as bool. Nothing is copied on the way.
import pyarrow as pa
table = pa.table(threshio.read("data/harvest.csv", schema))
print(table.num_rows, table.schema.field("lug_kg").type)
reader = threshio.read("data/harvest_dirty.csv", schema, rejects=True)
good, bad = pa.table(reader), pa.table(reader.rejects)
print(good.num_rows, bad.num_rows, bad.column("_errors")[0])
print(reader.report())
40 decimal128(18, 1)
26 4 [{'column': 'lug_kg', 'code': 'not_decimal'}]
fail data/harvest_dirty.csv 30 rows 4 rejected
polars.DataFrame(threshio.read(...)) takes it the same way, and in DuckDB
a Reader is a table:
import duckdb
harvest = threshio.read("data/harvest.csv", schema)
print(duckdb.sql("select cultivar, count(*) from harvest group by 1 order by 2 desc, 1 limit 2").fetchall())
[('Brookwood', 10), ('Ember Pip', 9)]
With no schema, read infers one from the file first. A Reader is read
once; rejects=True keeps the rows that did not fit, with _row,
_errors and _extra, for a stream of their own.
When thresh knows the fix
A file thresh cannot read says why, with a code, and the fix when there is one, as options to call again with:
first = threshio.infer("data/windows.csv")
problem = first.files[0].problems[0]
print(problem.code, problem.fix)
again = threshio.infer("data/windows.csv", **problem.fix)
print(again.ok)
column_not_utf8 {'encoding': 'cp1252'}
True
The message names the command line's flags; fix is this package's
options. Every code is listed in
PROBLEMS.md.
For an AI
describe is everything thresh can say of a file in one bounded
document. With shapes_only=True it holds none of the file's values
(digits become 9, letters a), so it can go to an AI, or into an issue:
d = threshio.describe("data/harvest_dirty.csv", shapes_only=True)
print(d.rejected_lines[0].text)
print([(c.name, c.stats.shapes[0].value) for c in d.columns[:3]])
prompt = "Write a loader for this file.\n\n" + d.to_json()
9,a,9-9-9,a9,9.9,a a,a,a 9
[('lug_id', '9'), ('picked_on', '9-9-9'), ('orchard_row', 'a9')]
Options
Every call takes the same keyword options, each the command line's flag of
the same name; a call ignores those it has no use for. Left out (or
None), the sniffer settles the dialect and nothing else changes.
| Option | Value |
|---|---|
delimiter, comment |
one character, as "," or "\t" |
quote |
one character, or False for none |
escape |
"doubled" or "backslash" |
newline |
"lf", "crlf" or "cr" |
header |
True or False |
encoding |
"utf8", "latin1" or "cp1252" (UTF-16 is told by its byte order mark) |
nulls |
a list of strings read as null in every column |
ragged |
"pad" or "truncate" a short or long row, rather than reject it |
threshold |
the fraction of a column's values (and of rows) that must fit |
strict |
extra columns, ragged rows and ambiguous dates fail the file |
day_first |
ambiguous dates read day first |
keep_empty |
an empty line is a one-field record |
sample_bytes |
the sniffer's first window |
sample_rows, jobs |
infer: the first rows only; files read at once |
shapes_only, shape_names |
values (and names) as shapes in what comes back |
mmap |
False reads through a buffer, not a mapping |
cancel |
a threshio.Cancel: .set() from another thread stops the call |
A sniffed dialect pins the next file the same way:
threshio.validate(path, schema, **sniff.dialect.options).
What comes back
| Call | Returns |
|---|---|
sniff(path) |
Sniff: dialect, warnings, problems, ok |
infer(*paths) |
Inference: schema, each file's outcome and notes, problems, ok |
columns(spec) |
Schema from the inline form |
validate(path, schema) |
Report: result, rows, columns, rejected_lines, problems, passed |
check(path, schema) |
the Report, or FileFailed with it |
describe(path, schema=None) |
Description: a Report with every column's statistics, the schema, the encoding |
read(path, schema=None, rejects=False) |
Reader: an Arrow stream of rows, .rejects, .report() |
Every result has to_json(), the document thresh wrote
(REPORT.md),
and raw, the same as a dict.
Exceptions are for the call, not the file: ArgumentError (a call made
wrongly), SchemaError, ReadError (reading failed partway),
Interrupted (a cancel), all threshio.Error; and FileFailed from
check and from read of a file it cannot read.
Calls release the GIL and use a handle of their own, so threads may call
at once; infer reads several files on threads of its own. A Reader is
used from one thread at a time.
More
Whole scripts, each with its output:
examples/python.
Apache-2.0.
Metadata
Release files for threshio 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
Total release size: 3.5 MB
Release files / threshio-0.1.0-py3-none-win_arm64.whl
| Download URL | threshio-0.1.0-py3-none-win_arm64.whl |
|---|---|
| Size | 436.7 kB |
| Tags | Python 3 Windows ARM64 |
|
SHA-256 checksum How to use checksums |
e1ecb37f35df619edf8f9c8bc8f406bc54ccb413c471c5c77b73c1ffa566c878
|
|
BLAKE2b-256 checksum How to use checksums |
fbd55387ff6839388e8fb871c0f26320ad6cb86ec5f3d264d25c2a555f2b2898
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 2, 2026.
Transparency logRelease files / threshio-0.1.0-py3-none-win_amd64.whl
| Download URL | threshio-0.1.0-py3-none-win_amd64.whl |
|---|---|
| Size | 493.2 kB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
acf68e1e9938dd4a7cbe46d413aa1709c4d883240b14df6a1351ea3915639adc
|
|
BLAKE2b-256 checksum How to use checksums |
487885590943a86e79d1b036499767747305d1550d7ad67732b05aa8f65ff634
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 2, 2026.
Transparency logRelease files / threshio-0.1.0-py3-none-musllinux_1_2_x86_64.whl
| Download URL | threshio-0.1.0-py3-none-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 453.4 kB |
| Tags | Linux musl 1.2+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
e67cab6a4acaf74254fa62b50e64875329a7f170057b82b336749dbabd6d0934
|
|
BLAKE2b-256 checksum How to use checksums |
e324f41d9e5c594cd1004660894cac05d35e60084c03b07a11da340c76c3ab66
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 2, 2026.
Transparency logRelease files / threshio-0.1.0-py3-none-musllinux_1_2_aarch64.whl
| Download URL | threshio-0.1.0-py3-none-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 424.9 kB |
| Tags | Linux musl 1.2+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
e865adb5762624f12b28463e1bef51a1f6bd3f39f20a8298623360af139abb57
|
|
BLAKE2b-256 checksum How to use checksums |
1cfedb9a2c6ff30320771ff78ef4c82d067843d76a8a67167002f3696ee0be1c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 2, 2026.
Transparency logRelease files / threshio-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | threshio-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 441.0 kB |
| Tags | Linux glibc 2.17+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
87be9f9e8515454bc8b68914089ed26d0114d007cf55519f5bf0708283c14720
|
|
BLAKE2b-256 checksum How to use checksums |
d8e8e38c1f547589d4a924b08773d12f3989db037dce55a9091af89bdaf71074
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 2, 2026.
Transparency logRelease files / threshio-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | threshio-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 412.7 kB |
| Tags | Linux glibc 2.17+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
ed8a2a2d01d6a5ff53658c66c1a84a6cd980578abd8890c7d745c53859a79441
|
|
BLAKE2b-256 checksum How to use checksums |
7fdac3b8602f0de8f2fd32bfeb071d400e8748a454bf1841286fca1e789cef5b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 2, 2026.
Transparency logRelease files / threshio-0.1.0-py3-none-macosx_13_0_x86_64.whl
| Download URL | threshio-0.1.0-py3-none-macosx_13_0_x86_64.whl |
|---|---|
| Size | 421.9 kB |
| Tags | Python 3 macOS 13.0+ x86-64 |
|
SHA-256 checksum How to use checksums |
3a298f354b5e7ba68a44c8e7b0643fc85dac05cba24cbaf7fe94e80e8aca44fb
|
|
BLAKE2b-256 checksum How to use checksums |
ebc90be0c2f1aa74eb25e4a87e17f85d6f45f4e3670ec6c0c7337d9ac182416e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 2, 2026.
Transparency logRelease files / threshio-0.1.0-py3-none-macosx_13_0_arm64.whl
| Download URL | threshio-0.1.0-py3-none-macosx_13_0_arm64.whl |
|---|---|
| Size | 392.3 kB |
| Tags | Python 3 macOS 13.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
9c3d91d89eea120e49f709fefdf67b0e57f95c0f75a24ec66d297b69066006e2
|
|
BLAKE2b-256 checksum How to use checksums |
9082a22100a05faa820263cd512457d85a808599b62184ce94d42af44714b11f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 2, 2026.
Transparency logSecurity for threshio 0.1.0
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.
Full provenance
All 8 files in this release have been uploaded with provenance metadata.
Attestation for threshio-0.1.0-py3-none-win_arm64.whl
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 Nov 2, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.