Setup dev environment
Install Rust, uv, and prek hooks, then build the native extension with `maturin develop` to get a local CocoIndex checkout ready for development.
Follow the steps below to get CocoIndex built on the latest codebase locally - if you are making changes to CocoIndex functionality and want to test it out.
-
If you don’t have Rust installed, run
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shAlready have Rust? Make sure it’s up to date
rustup update -
Install uv for Python project management:
# macOS / Linux curl -LsSf https://astral.sh/uv/install.sh | sh # Windows powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" -
Setup your local development environment:
-
Install and enable prek hooks. This ensures all checks run automatically before each commit:
uv run prek install -
(Optionally) Install all optional dependencies:
uv sync --all-extras
-
-
Build the library. Run at the root of cocoindex directory:
uv run maturin developThis step needs to be repeated whenever you make changes to the Rust code.
Running examples
Before running a specific example, set extra environment variables, for exposing extra traces, allowing dev UI, etc.
. ./.env.lib_debug
To run examples during development, you need to use the local editable version of cocoindex.
.env.lib_debug provides a coco-dev-run function to make it more convenient.
# Navigate to an example directory
cd examples/text_embedding
# Run with your local cocoindex changes
coco-dev-run cocoindex update main
The coco-dev-run function runs uv run --with-editable $COCOINDEX_DEV_ROOT, which ensures the example uses your locally built cocoindex package instead of the published version. The COCOINDEX_DEV_ROOT variable is automatically set to the repo root when you source .env.lib_debug.
Debugging the Postgres state-store
When running against the Postgres state-store backend, you can have PG
log every statement server-side by setting COCOINDEX_PG_LOG_STATEMENT.
The value is passed through as the log_statement
GUC via each pooled connection’s startup options:
# Log every statement — most useful when chasing SSI 40001 retries.
export COCOINDEX_PG_LOG_STATEMENT=all
# Or limit to modifying statements (INSERT / UPDATE / DELETE / DDL).
export COCOINDEX_PG_LOG_STATEMENT=mod
Accepted values match PG’s log_statement: none, ddl, mod, all.
Unset or empty is a no-op (PG keeps its server default, normally none).
The setting is applied per connection at startup, so every new pooled
connection picks it up; existing connections are unaffected until they
recycle.
To make the logs themselves useful, pair with these GUCs on the PG side
(postgresql.conf, or ALTER SYSTEM then pg_reload_conf()):
log_lock_waits = on— surface blocking events.log_min_messages = info— keep 40001 detail lines.log_line_prefix = '%m [%p] %a '— timestamp + pid + application name so concurrent transactions can be untangled in the log.
This knob only affects the Postgres backend. The LMDB backend has no server to log against.
log_statement is a SUSET GUC — Postgres requires the connecting role
to be a superuser (or to have explicit SET privilege on it).
Local-dev clusters fit; shared / managed Postgres typically does not. On
a shared cluster, ask the DBA to set it cluster-wide via
ALTER SYSTEM SET log_statement = 'mod' (then SELECT pg_reload_conf()),
or per-role via ALTER ROLE <you> SET log_statement = 'mod', instead of
using the env var.
Troubleshooting
cargo test fails with ModuleNotFoundError: encodings (embedded Python can’t find stdlib)
On some setups (notably when using uv venv with an uv-managed Python), cargo test may crash with:
ModuleNotFoundError: No module named 'encodings'
This can happen when the embedded Python interpreter (used by Rust tests) cannot locate the Python stdlib (you may see sys.prefix=/install in the crash output).
Workaround:
-
Run cargo tests via:
./dev/run_cargo_test.sh -p cocoindex --lib
This wrapper sets PYTHONHOME/PYTHONPATH for that command only, so embedded Python can locate the stdlib and site-packages.
The cargo-test prek hook uses this wrapper.