A Python port of chkit — ClickHouse schema management and migration toolkit, written in strict, imperative Python.
pip install chkit-py
chkit --helpThe package is named chkit-py on PyPI; the import name is chkit.
- Type safety first. Every public surface is annotated. Ships clean under
mypy --strictandpyrightstrict mode. - Pydantic v2 models. Runtime validation, frozen,
extra="forbid". - Imperative core. Pure functions over data; minimal classes outside of Pydantic models and the CLI shell.
- No magic. No dynamic imports, no runtime introspection of user code beyond what Pydantic provides.
src/chkit/
core/ Schema DSL, diff engine, planner, SQL rendering, validation
clickhouse/ ClickHouse client wrapper
cli/ Typer-based CLI (init, generate, migrate, status, check, drift)
In a fresh project:
pip install chkit-py
chkit init # scaffold clickhouse.config.py + example schema
chkit generate --name init # diff schema vs snapshot -> writes migrations/*.sql
chkit migrate --apply # apply pending, journal in ClickHouse _chkit_migrations
chkit status # show applied / pending counts
chkit check --strict # CI gate (pending, drift, checksum)
chkit drift # snapshot vs current schema diffclickhouse.config.py reads its credentials from os.environ.get(...) by
default. Set CLICKHOUSE_URL, CLICKHOUSE_USER, CLICKHOUSE_PASSWORD,
CLICKHOUSE_DB (or override directly in the config).
This port matches the upstream TypeScript chkit on every user-facing
surface: schema DSL, canonicalization + diff + planner pipeline, codec
parser/renderer, validation, all CLI commands, the plugin runtime + its
hooks, and every first-party plugin. The journal lives in the same
ClickHouse _chkit_migrations table as the TS version, so both
implementations can share a database without divergence.
Covered — 1:1 with TS:
chkit.core— model, canonicalization, codec, planner, validation, snapshot, SQL rendering,apply_on_cluster_to_plan. Includes the Dictionary primitive (dictionary()— full lifecycle: DSL, validation, diff/replace planning,--rename-dictionary, pull introspection, codegen), index-only projections, and function expressions inprimaryKey/orderBy.- All CLI commands:
init,generate,migrate,status,check,drift(with live-DB compare),pull,query,plugin. Codegen runs automatically afterchkit generatewhen the plugin is registered (via theon_plan_createdhook). - Flag surface —
--rename-table/--rename-column,--table <selector>on generate/migrate/status/check/drift,--dryrun/--json/--config,--strict,--apply/--execute/--allow-destructive(exit code 3 when blocked). - Plugin runtime + all hooks (
on_config_loaded,on_schema_loaded,on_plan_created,on_before_apply,on_after_apply,on_check,on_check_report,on_before_plugin_command,on_pull_introspect,on_init,on_complete). - First-party plugins:
chkit_plugin_codegen(Pydantic model generator, including dictionary attribute models),chkit_plugin_obsessiondb(auth, service management, remote executor, backfill routing,Shared*-engine rewrites),chkit_plugin_backfill(full chunking + execution engine: smart size-aware chunk planning, async submit/poll execution loop with checkpoint + resume, mv_replay detection, and the managed-jobsubmitpath via ObsessionDB). - Journal —
_chkit_migrationstable (schema +CHKIT_JOURNAL_TABLEoverride + checksum mismatch detection), per-operation async tracking,INSERT race conditionretry, ON CLUSTER +ReplicatedReplacingMergeTreeengine when cluster mode is enabled. ON CLUSTER <name>support — setclickhouse.clusterand every generated DDL statement is stamped as a final plan post-pass.
Not ported by design — Python convention or ecosystem difference:
chkit skillsproxy (nonpxanalogue),create-chkitseparate scaffolder (usechkit init --example <name>instead),deps.tsauto-install (Python convention is explicitpip install),internal-plugins/skill-hintAI-agent detection.
See DRIFT.md for the append-only decision log covering every port choice, known limitation, and won't-port item.
git clone https://github.com/obsessiondb/chkit
cd chkit_python
python -m venv .venv
.venv\Scripts\python.exe -m pip install -e ".[dev]"
.venv\Scripts\python.exe -m pytest
.venv\Scripts\python.exe -m mypy src
.venv\Scripts\python.exe -m ruff check src testsTests under tests/test_*_parity.py and tests/test_sql_validation_e2e.py
are direct ports of the TS suites in
packages/core/src/*.test.ts. The E2E suite requires a reachable ClickHouse
(defaults to http://localhost:8123 with no password — matches a fresh
docker run of clickhouse-server). Override via CLICKHOUSE_URL /
CLICKHOUSE_PASSWORD.