# How to sort Python imports with Ruff

{{< callout type="warning" >}}
This guide assumes you have uv installed. If not, see [How to install uv](https://pydevtools.com/handbook/how-to/how-to-install-uv.md) first.
{{< /callout >}}

[Ruff](https://pydevtools.com/handbook/reference/ruff.md) sorts imports through its linter, not its formatter. Select the `I` rule and apply fixes to sort the imports in a file or directory:

```console
$ uvx ruff check --select I --fix .
```

To make import sorting a permanent part of a project, add the `I` rule under `[tool.ruff.lint]` in [pyproject.toml](https://pydevtools.com/handbook/reference/pyproject.toml.md):

```toml
[tool.ruff.lint]
extend-select = ["I"]
```

`ruff check --fix .` then sorts imports alongside every other enabled rule. The rest of this guide covers each path: the command, project configuration, a pre-commit hook, and organize-on-save in VS Code.

## Why `ruff format` doesn't sort imports

`ruff format` and `ruff check` do different jobs, and only `ruff check` sorts imports. The formatter rewrites code layout (indentation and line wrapping) to match a consistent style. Import order is a lint rule, `I001`, so it belongs to the linter.

Running the formatter on unsorted imports leaves them unsorted:

```console
$ cat example.py
import pandas
import numpy as np

$ uvx ruff format example.py
1 file left unchanged
$ cat example.py
import pandas
import numpy as np
```

Running the linter with the `I` rule sorts them:

```console
$ uvx ruff check --select I --fix example.py
Found 1 error (1 fixed, 0 remaining).
$ cat example.py
import numpy as np
import pandas
```

To both reorder imports and format the rest of the file, run the linter and the formatter:

```console
$ uvx ruff check --select I --fix .
$ uvx ruff format .
```

## Sort a single file or directory

To sort the imports in one file, pass its name:

```console
$ uvx ruff check --select I --fix example.py
```

To sort every Python file in the current directory and its subfolders, pass `.`:

```console
$ uvx ruff check --select I --fix .
```

`--select I` limits the run to import sorting and leaves every other lint rule off, which makes it a direct replacement for an `isort` invocation. The `I` prefix covers `I001` (unsorted-imports), which reorders and groups imports, and `I002` (missing-required-import), which adds any imports the project requires in every file. Drop `--select I` to run import sorting together with the rest of Ruff's rules.

## Add Ruff to a project for ongoing sorting

For repeated use, add Ruff as a development dependency instead of running it through `uvx` each time:

```console
$ uv add --dev ruff
```

Enable the `I` rule with the `[tool.ruff.lint]` block shown at the top of this guide, then sort imports with:

```console
$ uv run ruff check --fix .
```

Because the rule is enabled in configuration, `--select I` is no longer needed: `ruff check` sorts imports as part of its normal run. To customize grouping, add an `[tool.ruff.lint.isort]` section, for example `known-first-party = ["my_package"]`.

## Sort imports on every commit with pre-commit

To sort imports automatically before each commit, add Ruff to [pre-commit](https://pydevtools.com/handbook/how-to/how-to-set-up-pre-commit-hooks-for-a-python-project.md). Create a `.pre-commit-config.yaml` with the `ruff-check` and `ruff-format` hooks:

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

The `ruff-check` hook sorts imports (`--select I --fix`) and the `ruff-format` hook formats the rest of the file. They run in order, so imports are sorted before formatting. Install the hooks once:

```console
$ uvx pre-commit install
```

From then on, committing a file with unsorted imports rewrites it and stops the commit so the changes can be staged:

```console
$ git commit -m "Add data loader"
ruff check...............................................................Failed
- hook id: ruff-check
- files were modified by this hook

Found 1 error (1 fixed, 0 remaining).

ruff format..............................................................Passed
```

Stage the fixed file and commit again. If the `I` rule is already enabled in `pyproject.toml`, drop `--select, I` and use `args: [--fix]` so the hook applies every configured rule. Run `uvx pre-commit autoupdate` periodically to bump the `rev:` to the latest Ruff release.

## Sort imports on save in VS Code

The [Ruff extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) (`charliermarsh.ruff`) can organize imports every time a Python file is saved. Add this to your `settings.json`:

```json
{
    "[python]": {
        "editor.codeActionsOnSave": {
            "source.organizeImports.ruff": "explicit"
        }
    }
}
```

`source.organizeImports.ruff` runs Ruff's import sorter on save. The `.ruff` suffix scopes the action to this extension, so it doesn't trigger another organize-imports provider you have installed. The extension reads the project's `pyproject.toml`, `ruff.toml`, or `.ruff.toml`, so the grouping matches what `ruff check` does on the command line.

To also format the file and apply other autofixes on save, set Ruff as the default formatter and add `source.fixAll.ruff`. See [How to configure VS Code for a uv project](https://pydevtools.com/handbook/how-to/how-to-configure-vs-code-for-a-uv-project.md) for the full editor setup.

## Learn More

- [Ruff](https://pydevtools.com/handbook/reference/ruff.md) reference
- [How to configure recommended Ruff defaults](https://pydevtools.com/handbook/how-to/how-to-configure-recommended-ruff-defaults.md)
- [How to set up pre-commit hooks for a Python project](https://pydevtools.com/handbook/how-to/how-to-set-up-pre-commit-hooks-for-a-python-project.md)
- [How to enable Ruff security rules](https://pydevtools.com/handbook/how-to/how-to-enable-ruff-security-rules.md)
- [Ruff isort settings](https://docs.astral.sh/ruff/settings/#lintisort)
