# How to set up prek hooks for a Python project

{{< callout type="warning" >}}
This guide assumes you have a Python project managed with [uv](https://pydevtools.com/handbook/reference/uv.md). If you haven't created a project yet, see the [project creation tutorial](https://pydevtools.com/handbook/tutorial/create-your-first-python-project.md).
{{< /callout >}}

[prek](https://pydevtools.com/handbook/reference/prek.md) is a fast drop-in replacement for [pre-commit](https://pre-commit.com/) that runs hooks faster, uses less disk space, and ships as a single binary with no Python dependency. It reads the same `.pre-commit-config.yaml` files, so the configuration in this guide also works with pre-commit. See the [pre-commit version of this guide](https://pydevtools.com/handbook/how-to/how-to-set-up-pre-commit-hooks-for-a-python-project.md) for the original tool.

## Installing prek

Install prek as a CLI tool with uv:

```console
$ uv tool install prek
```

Then install the Git hook in your project:

```console
$ prek install
```

This places a hook script in `.git/hooks/pre-commit` that runs automatically on `git commit`.

## Migrating from pre-commit

If the project already uses pre-commit, swap it out without changing any configuration:

```console
$ pre-commit uninstall && prek install
```

The existing `.pre-commit-config.yaml` works as-is.

## Creating the configuration file

prek reads the same `.pre-commit-config.yaml` format as pre-commit. Here is a starting configuration that uses [Ruff](https://pydevtools.com/handbook/reference/ruff.md) for linting and formatting:

```yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.15.14
    hooks:
      - id: ruff-check
        args: [--fix]
      - id: ruff-format
```

The `ruff-check` hook runs the linter with auto-fix enabled. The `ruff-format` hook runs the formatter. They execute in order, so linting fixes are applied before formatting.

> [!TIP]
> Run `prek auto-update` to update all hook versions to their latest releases.

## Adding more hooks

### Trailing whitespace and file endings

The `pre-commit-hooks` repository provides several lightweight checks:

```yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files
```

### Using prek's built-in hooks

prek ships with Rust-native implementations of common hooks that require no external toolchain. Use `repo: builtin` to access them:

```yaml
repos:
  - repo: builtin
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
```

Built-in hooks run faster than their Python equivalents because they skip virtual environment setup entirely.

### Type checking with mypy

To run [mypy](https://pydevtools.com/handbook/reference/mypy.md) as a hook:

```yaml
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v2.1.0
    hooks:
      - id: mypy
        additional_dependencies: []
```

> [!NOTE]
> mypy 2.0 changed several defaults that may surface new errors in code that passed under 1.x. See the [mypy reference page](https://pydevtools.com/handbook/reference/mypy.md) for details. mypy 2.0 also requires Python 3.10 or newer as both a runtime and a target.

Add any type stub packages your project needs to `additional_dependencies`. For example, if your project uses `requests`:

```yaml
        additional_dependencies: [types-requests]
```

## A complete configuration

Combining built-in hooks with Ruff:

```yaml
repos:
  - repo: builtin
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.15.14
    hooks:
      - id: ruff-check
        args: [--fix]
      - id: ruff-format
```

## Running hooks manually

To run all hooks against every file in the repository:

```console
$ prek run --all-files
```

To run a specific hook:

```console
$ prek run ruff-check --all-files
```

To list all configured hooks:

```console
$ prek list
```

## Skipping hooks temporarily

To bypass hooks for a single commit:

```console
$ git commit --no-verify -m "WIP: work in progress"
```

Use this sparingly. AI coding agents reach for `--no-verify` more readily than humans do; see [how to stop AI agents from bypassing pre-commit hooks](https://pydevtools.com/handbook/how-to/how-to-stop-ai-agents-from-bypassing-pre-commit-hooks.md) for the defensive setup.

## Using prek in CI

Run hooks in CI to catch anything contributors skip locally. In a GitHub Actions workflow:

```yaml
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v7
- run: uv tool install prek
- run: prek run --all-files
```

## Troubleshooting

If hooks are not running on commit, verify the hook is installed:

```console
$ prek install
```

To clear cached hook environments (useful after updating Python or hook versions):

```console
$ prek clean
```
