# Setup dev environment

> **CocoIndex v1.** This page documents CocoIndex **v1** — a ground-up redesign from v0. When writing code, ignore any v0 flow-builder DSL or deprecated decorators.
>
> Source: https://cocoindex.io/docs/contributing/setup_dev_environment/ · Docs index: https://cocoindex.io/docs/llms.txt · Agent skill: https://cocoindex.io/docs/skill.md
>
> v0→v1 quick map — if you reach for these v0 symbols, stop and use the v1 form: `@cocoindex.flow_def`/`FlowBuilder` → `coco.App` + a `@coco.fn` main function; `add_collector()`/`collect()`/`export()` → declare target states (`declare_row`, `declare_file`); `cocoindex.sources/functions/targets.*` → connector APIs (`localfs.walk_dir`, `coco.ops.*`, `postgres.declare_table_target`). Full mapping + API reference: https://cocoindex.io/docs/skill.md.

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.

- 🦀 [Install Rust](https://rust-lang.org/tools/install)

    If you don't have Rust installed, run

    ```sh
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    ```

    Already have Rust? Make sure it's up to date

    ```sh
    rustup update
    ```

- Install [uv](https://docs.astral.sh/uv/) for Python project management:

    ```sh
    # 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:

    ```sh
    uv run prek install
    ```

  - (Optionally) Install all optional dependencies:

    ```sh
    uv sync --all-extras
    ```

- Build the library. Run at the root of cocoindex directory:

    ```sh
    uv run maturin develop
    ```

    This 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.

```sh
. ./.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.

```sh
# 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`](https://www.postgresql.org/docs/current/runtime-config-logging.html#GUC-LOG-STATEMENT)
GUC via each pooled connection's startup options:

```sh
# 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.

**Note**
This knob only affects the Postgres backend. The LMDB backend has no
server to log against.

**Caution**
`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.

**Info**

The cargo-test prek hook uses this wrapper.
