5.5 isort: Sorting Import Statements
In the Python ecosystem, the organization of import statements is more than a matter of mere aesthetics; it is a critical component of code readability and maintainability. A haphazard collection of imports can obscure dependencies and make it difficult to understand a module’s structure at a glance. This is where isort comes into play. isort is a Python utility/library that automatically sorts import statements alphabetically and sections them into distinct groups. It enforces a consistent style, resolving disagreements over import order and freeing developers to focus on more substantive problems. Its configuration flexibility allows it to integrate seamlessly with the style dictates of PEP 8, Black, and other tools, making it an indispensable part of the modern Python linting and formatting workflow.
How isort Groups and Orders Imports
By default, isort employs a logical, multi-pass strategy to organize imports. It first separates them into three distinct sections, and then sorts them alphabetically within each group.
- Future Imports: Any imports from
__future__, which must appear at the top of the file before any other code. These are used to enable compatibility features for older Python versions. - Standard Library Imports: Imports from modules that are part of the Python Standard Library (e.g.,
os,sys,json). - Third-Party Imports: Imports from externally installed packages (e.g.,
requests,django,numpy). - Local Application/Project Imports: Imports from other modules within the same project or application.
This grouping provides an immediate, visual hierarchy of a module’s dependencies. The alphabetical sorting within each group ensures a deterministic order, eliminating any stylistic debate.
# Before isort (chaotic)
from my_local_module import ClassB
import os
from django.conf import settings
import sys
from __future__ import annotations
# After isort (ordered and grouped)
from __future__ import annotations
import os
import sys
from django.conf import settings
from my_local_module import ClassB
Configuration and Integration with Black
A common point of friction arises when using isort alongside Black, the uncompromising code formatter. Black has its own opinions on import formatting, particularly regarding wrapping long import lines. By default, isort and Black could fight each other, leading to a format-edit loop. The solution is to configure isort to be compatible with Black’s style.
This is achieved using a pyproject.toml (or .isort.cfg) file with a specific profile. The profile = "black" option tells isort to adjust its line length and wrapping behavior to match Black’s expectations, ensuring perfect harmony between the two tools.
# pyproject.toml
[tool.isort]
profile = "black"
line_length = 88 # Black's default line length
src_paths = ["src", "app"]
With this configuration, both tools can be run in sequence (isort first, then Black) without causing any formatting conflicts.
Using isort as a Pre-commit Hook
To ensure imports are consistently sorted before every commit, integrating isort into a pre-commit hook is a best practice. The pre-commit framework provides a simple way to manage such hooks. You define the tools and commands to run in a .pre-commit-config.yaml file.
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pycqa/isort
rev: 5.13.2 # Use the latest stable release
hooks:
- id: isort
name: isort (python)
args: ["--profile", "black"] # Pass the Black profile as an argument
This setup will automatically run isort on all staged Python files whenever a git commit is performed, preventing unsorted imports from ever entering the repository.
Advanced Configuration: Custom Sections and Known Files
For larger projects, the default grouping might not be sufficient. isort allows for highly granular control over import sections. You can define custom groups and assign specific modules or packages to them using the known_ configuration options. This is especially useful for categorizing first-party internal packages or different types of third-party dependencies (e.g., AWS-related packages vs. database-related packages).
# pyproject.toml (Advanced example)
[tool.isort]
profile = "black"
line_length = 88
known_standard_library = ["os", "sys", "json"]
known_third_party = ["requests", "boto3", "django"]
known_first_party = ["my_app", "common_utils"]
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
default_section = "LOCALFOLDER"
In this configuration, isort will place any import it doesn’t recognize as standard, third-party, or first-party into the LOCALFOLDER group, which is for relative imports from the immediate directory.
Inline Command Usage and CI Integration
While configuration files are ideal for project-wide settings, isort can also be used inline for one-off formatting. The --diff flag is particularly useful in Continuous Integration (CI) pipelines, as it will output what would be changed without actually modifying the files, allowing the CI job to fail if unsorted imports are detected.
# Check if files are sorted without making changes
isort --check --diff .
# Output what would change and return a non-zero exit code if changes are needed
# This command is perfect for a CI/CD pipeline step.
# To actually fix the imports, simply run:
isort .
This fail-fast approach in CI ensures that code merging into the main branch always adheres to the project’s import style standards, maintaining code quality and consistency across the entire codebase.