isort & Import Organization
Consistent import order makes diffs readable and prevents circular import surprises. Ruff's isort rules (I) handle this automatically - no separate isort install needed.
Recipe
[tool.ruff.lint.isort]
known-first-party = ["myapp"]
section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]uv run ruff check --fix . # auto-sort importsWhen to reach for this:
- Import blocks are inconsistently ordered across files
- Code review spends time on import nits
- Circular imports need clearer separation of stdlib vs local
Working Example
# Correct order (after ruff fix)
from __future__ import annotations
import os
from pathlib import Path
import httpx
from pydantic import BaseModel
from myapp.config import settings
from myapp.models import User[tool.ruff.lint.isort]
known-first-party = ["myapp", "myapp_utils"]
force-single-line = false
lines-after-imports = 2What this demonstrates:
- Future imports first, then stdlib, third-party, first-party, local
- Blank lines separate groups
known-first-partytells ruff which packages are yours--fixreorders without manual editing
Deep Dive
Import Groups
| Group | Examples |
|---|---|
future | from __future__ import annotations |
| standard-library | os, pathlib, typing |
| third-party | httpx, fastapi, pandas |
| first-party | your installed packages |
| local-folder | relative imports in scripts |
Python Notes
# Prefer absolute imports in libraries
from myapp.services import billing
# Relative imports ok within a package
from .models import UserGotchas
- Missing
known-first-party- your packages sorted as third-party. Fix: list all first-party package names. - isort + ruff both configured - conflicting rules. Fix: use ruff only; remove standalone isort.
- Star imports (
from x import *) - lint flags and obscures origin. Fix: explicit imports except__init__.pyre-exports. - Circular imports from order - reordering doesn't fix design issues. Fix: restructure modules; use lazy imports or
TYPE_CHECKING. - Different order in
__init__.py- re-exports confuse isort. Fix: per-file override or explicit__all__.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Manual ordering | 1-2 file project | Any team project |
| Standalone isort | Not using ruff | Already on ruff |
| No sorting | - | Never for production |
FAQs
Do I need isort if I have ruff?
No. Ruff implements isort rules natively.
How do I force single-line imports?
force-single-line = true in [tool.ruff.lint.isort].
What about import aliases?
import pandas as pd stays in the third-party group. isort preserves aliases.
How do I handle TYPE_CHECKING imports?
Ruff's isort places them correctly. Use if TYPE_CHECKING: blocks as normal.
Can I exclude a file from import sorting?
Per-file ignore: "__init__.py" = ["I001"] if re-exports need manual order.
Why two blank lines after imports?
PEP 8 recommends two blank lines before top-level functions. lines-after-imports = 2 enforces this.
How do monorepos handle first-party?
List all workspace package names in known-first-party.
Does import order affect performance?
No. This is readability and convention only.
How do I sort on save?
VS Code ruff extension with "source.organizeImports.ruff": "explicit" or format on save.
What is I001?
Ruff's import-sorting violation code. Fixed with ruff check --fix.
Related
- Ruff Setup - ruff configuration
- Essential Lint Rules - I rules in context
- Black / Ruff Format - formatting (separate from imports)
- Linting & Formatting Best Practices - conventions
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.