chtypes — Python SDK
If this row were inserted into this table on this ClickHouse version, what would happen? chtypes answers with ClickHouse's own code: the real C++ type machinery, vendored per release into a native library behind the frozen chs_* C ABI and reached here through stdlib ctypes. Nothing semantic is reimplemented, so "what does ClickHouse do with 256 into a UInt8?" is answered by ClickHouse rather than by a model of it. One peer binding among {go, python, ts, rust} — no language is privileged, and all four give one answer.
Pure Python: zero dependencies, no build step, no compiler.
Install
Two things: this package, and at least one artifact — the per-version native library it dlopens at runtime.
uv add chtypes # or: pip install chtypes
python -m chtypes fetch 25.8
The fetch lands in ~/.cache/chtypes/artifacts/<os>-<arch>/25.8/ — the per-user cache every chtypes binding reads by default — after checking an ed25519 signature over the release and the sha256 of every byte. $CHTYPES_REGISTRY overrides it. The ed25519 verifier is pure stdlib too.
Quickstart
from chtypes import Format, Registry
registry = Registry() # walks the search path
library = registry.for_version("25.8") # a line or an exact patch; never a nearest match
with library.compile_ddl("x UInt8, ts DateTime DEFAULT now()") as schema:
batch = schema.rows(Format.JSON_EACH_ROW, b'{"x":256}\n')
row = batch.rows[0]
print(batch.outcome) # accepted
print(row.value("x").text) # 0 — what would actually be stored
print(row.transformed[0].reason) # overflow_wrap — which is the product
print(row.substituted) # ts: send it explicitly, or preview != stored
The row is accepted and 256 is silently stored as 0. That report — transformed — is the one derived answer in the system and the reason it exists.
ts was substituted rather than stored: send every substituted column as an explicit value in the real INSERT, or the server re-evaluates now() at its own instant and your preview is not what landed. Pin the instant in tests with settings={"chtypes_now_epoch_nanos": "1700000000000000000"}.
Three outcomes, and conflating any two is a bug
A bad row is a verdict, not an exception: outcome becomes Outcome.REJECTED with ClickHouse's own code. Exceptions are for schema-level answers and for the machinery.
SchemaError— the server itself would refuse this, and.codeis a real ClickHouse code.UnsupportedError— this build declines to answer, and a real server might well have accepted. Fall back to the server; never tell a user they are wrong on the strength of a decline.UnsupportedErroris a peer ofSchemaError, not a subclass.except SchemaErrornever catches a decline. Handle the two arms explicitly, or catchChtypesErrorfor both.
Documentation
| Quickstart | the same program in all four languages |
| Python API reference | every symbol, the C entry point under it, what it returns and what it raises |
| Artifacts | getting one, where it lands, verifying and pinning it |
| Batches | always rows, and the two bad-row policies |
| Transformations | the silent-change report, and the DEFAULTs you must echo back |
| Settings · Discovery | the four channels; asking a real server what profile to validate under |
| Filters · Multi-version | boolean expressions over rows; several ClickHouse versions in one process |
| Support matrix · Limitations | what works where; what chtypes declines to answer |
Three things specific to this binding
A settings value must never be a float. encode_settings stringifies an int exactly and raises TypeError on a float: a 19-digit chtypes_now_epoch_nanos does not survive an IEEE double, and as a JSON number the setting would be silently ignored.
substituted is on RowResult, not on BatchResult. Reach it through batch.rows[i].substituted. BatchResult does carry a batch-level transformed, which folds in the storage layer's own verdicts.
ctypes releases the GIL for the whole duration of a foreign call, so the GIL is not the exclusion. The package uses a writer-preferring readers-writer lock per loaded image plus one plain lock per Schema; set_default_settings and close take it exclusively, as the ABI requires. Measured under contention: 27,770 batch reads across 8 threads against 566 concurrent settings swaps, every answer byte-identical to the uncontended one.
Tests
uv run pytest -q. Tests that need an artifact skip loudly by name without a registry on the search path, and a suite that ran nothing fails. The fetch suite runs offline against the miniature releases in tests/fixtures/fetch/ through file:// sources and the test key.
License
Apache 2.0. The artifacts this package loads are Elastic License 2.0 — a separate license, shipped inside each artifact release.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chtypes-0.1.2.tar.gz.
File metadata
- Download URL: chtypes-0.1.2.tar.gz
- Upload date:
- Size: 120.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8baf9a327d9724e39fbdfe6648de4a048178d0d7023f8affec033635b72aba8a
|
|
| MD5 |
7a0be680a1ff6b7f280825eed80bea4a
|
|
| BLAKE2b-256 |
fc291c348fa30bd6f180454dfb4c1db2c16be4e715a1f19269802bca6216c4e0
|
Provenance
The following attestation bundles were made for chtypes-0.1.2.tar.gz:
Publisher:
release-python.yml on Wave-RF/chtypes
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chtypes-0.1.2.tar.gz -
Subject digest:
8baf9a327d9724e39fbdfe6648de4a048178d0d7023f8affec033635b72aba8a - Sigstore transparency entry: 2796704105
- Sigstore integration time:
-
Permalink:
Wave-RF/chtypes@42950ef5f7bdba2a6e1e305d15cd00bcfa103998 -
Branch / Tag:
refs/tags/python/v0.1.2 - Owner: https://github.com/Wave-RF
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@42950ef5f7bdba2a6e1e305d15cd00bcfa103998 -
Trigger Event:
push
-
Statement type:
File details
Details for the file chtypes-0.1.2-py3-none-any.whl.
File metadata
- Download URL: chtypes-0.1.2-py3-none-any.whl
- Upload date:
- Size: 90.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8cf7df6233978ff61b3a29277687ea902b406d98217a1280ae771a47d575e156
|
|
| MD5 |
e00d13928d8e70d4dbda981d0cacf95b
|
|
| BLAKE2b-256 |
894da72b36815ce159d0e7d47d1f4585a63d0ffb5b719fa6714bec7c25d937a7
|
Provenance
The following attestation bundles were made for chtypes-0.1.2-py3-none-any.whl:
Publisher:
release-python.yml on Wave-RF/chtypes
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chtypes-0.1.2-py3-none-any.whl -
Subject digest:
8cf7df6233978ff61b3a29277687ea902b406d98217a1280ae771a47d575e156 - Sigstore transparency entry: 2796704158
- Sigstore integration time:
-
Permalink:
Wave-RF/chtypes@42950ef5f7bdba2a6e1e305d15cd00bcfa103998 -
Branch / Tag:
refs/tags/python/v0.1.2 - Owner: https://github.com/Wave-RF
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@42950ef5f7bdba2a6e1e305d15cd00bcfa103998 -
Trigger Event:
push
-
Statement type: