A flake8 plugin that integrates with isort to enforce consistent import ordering in Python code
npx @tessl/cli install tessl/pypi-flake8-isort@5.0.0A flake8 plugin that integrates with isort to enforce consistent import ordering in Python code. This plugin checks if imports in Python files are sorted according to isort configuration and reports various import-related style violations including wrong import positions, missing configuration, incorrect blank lines, and missing imports.
pip install flake8-isortThe plugin is automatically discovered by flake8 through entry points - no direct imports needed:
# No direct imports required - plugin auto-discovery via flake8For testing or custom usage, the main class can be imported:
from flake8_isort import Flake8IsortAfter installation, flake8-isort works automatically with flake8:
# Install the plugin
pip install flake8-isort
# Run flake8 - plugin automatically checks import sorting
flake8 myproject/
# Enable specific error codes if using --select
flake8 --select=E,W,I myproject/
# Show detailed traceback when imports are wrong
flake8 --isort-show-traceback myproject/Configure isort behavior through .isort.cfg, setup.cfg, or pyproject.toml:
# .isort.cfg
[settings]
line_length = 88
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
combine_as_imports = trueThe plugin employs a sophisticated multi-version compatibility architecture to support both legacy and modern isort versions:
Flake8Isort automatically selects between Flake8Isort5 (isort >=5) and Flake8Isort4 (isort <5) based on API availabilityFlake8IsortBase provides shared functionality including error message constants and configuration handlingI00 error code prefixThis architecture ensures seamless operation across different isort versions while maintaining consistent error reporting and configuration handling.
The plugin registers with flake8 through entry points and provides error checking for import sorting violations.
# Entry point registration (automatic)
# flake8.extension: I00 = flake8_isort:Flake8IsortThe primary plugin class that flake8 instantiates, which is dynamically assigned to the appropriate implementation.
# Dynamic class alias - automatically selects implementation based on isort version
Flake8Isort = Flake8Isort5 if hasattr(isort, 'api') else Flake8Isort4
# The actual implementation depends on isort version:
# - For isort >=5: Uses Flake8Isort5 with modern isort.api
# - For isort <5: Uses Flake8Isort4 with legacy isort.SortImportsNote: Flake8Isort is not a standalone class but an alias that points to either Flake8Isort5 or Flake8Isort4 based on the installed isort version. This provides backward compatibility while using the most appropriate API for the installed isort version.
Abstract base class containing shared functionality for different isort versions.
from typing import List, Optional, Union, Iterator, Tuple, Type, Any
from ast import AST
from io import StringIO
class Flake8IsortBase:
"""
Abstract base class for isort integration with shared error messages and configuration.
Class Attributes:
name (str): Plugin name 'flake8_isort'
version (str): Plugin version '5.0.3' (Note: hardcoded in class as '5.0.1')
isort_unsorted (str): Error message for I001 - 'isort found an import in the wrong position'
no_config_msg (str): Error message for I002 - 'no configuration found (.isort.cfg or [isort] in configs)'
isort_blank_req (str): Error message for I003 - 'isort expected 1 blank line in imports, found 0'
isort_blank_unexp (str): Error message for I004 - 'isort found an unexpected blank line in imports'
isort_add_unexp (str): Error message for I005 - 'isort found an unexpected missing import'
show_traceback (bool): Whether to show full traceback (defaults to False)
stdin_display_name (Optional[str]): Display name for stdin processing (defaults to None)
search_current (bool): Whether to search current directory (defaults to True)
"""
def __init__(self, tree: Optional[AST], filename: Optional[str], lines: Union[List[str], str]) -> None:
"""
Initialize base plugin functionality.
Args:
tree: AST tree of the Python file (None for string-based processing)
filename: Path to the file being checked (None for stdin)
lines: Lines of the file content as list of strings or single string
"""
@staticmethod
def add_options(option_manager: Any) -> None:
"""
Add plugin-specific command line options to flake8.
Args:
option_manager: flake8's option manager instance
"""
@classmethod
def parse_options(cls, option_manager: Any, options: Any, args: Any) -> None:
"""
Parse and store plugin options.
Args:
option_manager: flake8's option manager instance
options: Parsed command line options
args: Command line arguments
"""Implementation classes for different isort versions with specialized processing logic.
class Flake8Isort4(Flake8IsortBase):
"""
Implementation for isort version <5 compatibility using SortImports API.
"""
def run(self) -> Iterator[Tuple[int, int, str, Type]]:
"""
Run import checking using isort v4 SortImports API.
Yields:
tuple: (line_number, column, message, plugin_class)
"""
def sortimports_linenum_msg(self, sort_result: Any) -> Iterator[Tuple[int, str]]:
"""
Parse isort.SortImports results for line number changes and generate error messages.
Args:
sort_result: isort.SortImports result object
Yields:
tuple: (line_number, error_message)
"""
class Flake8Isort5(Flake8IsortBase):
"""
Implementation for isort version >=5 compatibility using modern isort.api.
"""
def run(self) -> Iterator[Tuple[int, int, str, Type]]:
"""
Run import checking using isort v5 API with stream processing.
Handles isort.exceptions.FileSkipped and isort.exceptions.ISortError gracefully.
Yields:
tuple: (line_number, column, message, plugin_class)
"""
def isort_linenum_msg(self, udiff: str) -> Iterator[Tuple[int, str]]:
"""
Parse unified diff for import changes and generate error messages.
Args:
udiff: Unified diff output from isort comparison
Yields:
tuple: (line_number, error_message)
"""
# Additional utility methods in Flake8Isort4
class Flake8Isort4(Flake8IsortBase):
"""Additional methods for isort v4 compatibility"""
def _format_isort_output(self, isort_buffer: StringIO) -> str:
"""
Format isort output by filtering out diff metadata lines.
Args:
isort_buffer: Buffer containing isort output
Returns:
Formatted output with filtered lines and normalized newlines
"""
@staticmethod
def _fixup_sortimports_eof(sort_imports: Any) -> None:
"""
Ensure single end-of-file newline in isort.SortImports.in_lines.
Fixes EOF blank lines to avoid conflicts with flake8's own EOF checks.
Args:
sort_imports: isort.SortImports result object
"""
@staticmethod
def _fixup_sortimports_wrapped(sort_imports: Any) -> None:
"""
Split wrapped import lines in SortImports.out_lines for diff comparison.
isort combines wrapped lines into single strings, but in_lines keeps them separate.
This method splits them for proper diff comparison.
Args:
sort_imports: isort.SortImports result object
"""Options that can be passed to flake8 when using this plugin.
# Command line options
--isort-show-traceback # Show full traceback with diff from isort (boolean flag)The plugin respects isort configuration from multiple sources:
.isort.cfg: Dedicated isort configuration filesetup.cfg: Under [isort] sectionpyproject.toml: Under [tool.isort] sectiontox.ini: Under [isort] sectionExample configurations:
# .isort.cfg
[settings]
line_length = 88
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
combine_as_imports = true
known_first_party = myproject
known_third_party = requests,click# pyproject.toml
[tool.isort]
line_length = 88
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
combine_as_imports = true
known_first_party = ["myproject"]
known_third_party = ["requests", "click"]The plugin reports five specific error codes for different import issues:
# Error code definitions
I001 # isort found an import in the wrong position
I002 # no configuration found (.isort.cfg or [isort] in configs)
I003 # isort expected 1 blank line in imports, found 0
I004 # isort found an unexpected blank line in imports
I005 # isort found an unexpected missing import# Check specific file
flake8 mymodule.py
# Check entire project with import sorting
flake8 src/ tests/
# Only check import-related issues
flake8 --select=I src/
# Show detailed diff when imports are wrong
flake8 --isort-show-traceback src/# setup.cfg or tox.ini
[flake8]
select = E,W,F,I
max-line-length = 88
isort-show-traceback = true
[isort]
line_length = 88
multi_line_output = 3
include_trailing_comma = true# .github/workflows/lint.yml
- name: Run flake8 with import sorting
run: |
pip install flake8 flake8-isort
flake8 --select=E,W,F,I src/ tests/# For custom tooling or testing
from flake8_isort import Flake8Isort
import ast
# Parse Python code
code = '''
import os
import sys
import requests
'''
tree = ast.parse(code)
lines = code.splitlines(keepends=True)
# Create plugin instance
checker = Flake8Isort(tree, 'example.py', lines)
# Run checks
for line_num, col, message, cls in checker.run():
print(f"{line_num}: {message}")This plugin integrates with flake8 through the entry points system:
flake8.extension with key I00 = flake8_isort:Flake8IsortI (I001-I005)The plugin automatically detects and adapts to different isort versions:
isort.SortImports API with diff processingisort.api.sort_stream with unified diff parsingFlake8Isort5 if hasattr(isort, 'api') else Flake8Isort4Common integration patterns: