Skip to content

I output differ with isort by a single blank line #19532

@knguyen1

Description

@knguyen1

Summary

Given this file: https://github.com/metabrainz/picard/blob/master/picard/script/__init__.py
isort is outputting NO blank line between ) and the comment line # Those imports are required to actually parse the code and interpret decorators.

For ruff, see the blank line in this commit: metabrainz/picard@efa2ceb

My LLM did some investigations and gave up and concluded that it's due to "design philosophies". I just wanted to confirm with an actual human.

Given these isort settings:

[tool.isort]
sections = [
    "FUTURE", "STDLIB", "FIRSTPARTY", "THIRDPARTY", "QT", "TEST", "PICARD", "LOCALFOLDER",
    "RESOURCES", "PICARD_UI"
]
default_section = "LOCALFOLDER"
known_first_party = []
known_picard = ["picard"]
known_picard_ui = ["picard.ui"]
known_qt = ["PyQt6"]
known_resources = ["picard.resources"]
known_test = ["test.*"]
known_third_party = ["dateutil", "fasteners", "mutagen", "yaml"]
skip_glob = ["picard/ui/forms/ui_*.py", "picard/resources.py"]
combine_as_imports = true
float_to_top = false
force_grid_wrap = 2
force_sort_within_sections = true
include_trailing_comma = true
indent = "    "
lines_after_imports = 2
multi_line_output = 3
order_by_type = true
use_parentheses = true

Given these tool.ruff.lint.isort settings:


[tool.ruff.lint.isort]
# Custom section order matching your isort sections
section-order = [
    "future",
    "standard-library",
    "first-party",
    "known-first-party",
    "third-party",
    "known-third-party",
    "qt",
    "test",
    "picard",
    "local-folder",
    "resources",
    "picard-ui"
]
default-section = "local-folder"

# Match other isort settings
force-sort-within-sections = true
combine-as-imports = true
lines-after-imports = 2
order-by-type = true
split-on-trailing-comma = true

# Custom section definitions
[tool.ruff.lint.isort.sections]
qt = ["PyQt6"]
test = ["test.*"]
picard = ["picard"]
resources = ["picard.resources"]
picard-ui = ["picard.ui"]

# Keep your existing known mappings for standard sections
known-first-party = []
known-third-party = ["dateutil", "fasteners", "mutagen", "yaml"]

Below is the LLM conclusion of the investigation.

RooCode Output

I've successfully created a comprehensive investigation script and analyzed the root cause of the differences between ruff check --fix and isort import sorting behavior.

Investigation Results

Using the investigate_import_sorting.py script, I've identified the key differences:

Root Cause Analysis

The fundamental issue is that ruff and isort have different philosophies about import organization and formatting, despite similar configurations:

  1. Blank Line Handling:

    • Ruff: Maintains 2 blank lines between import groups, preserving original spacing
    • Isort: Reduces to 1 blank line, compacting the import section
  2. Import Formatting Style:

    • Ruff: Preserves multi-line import formatting with proper indentation (32 lines total)
    • Isort: Compacts imports into fewer lines with different formatting (15 lines total)
  3. Import Order Logic:

    • Ruff: Follows configured section order but maintains relative positioning
    • Isort: Reorders imports more aggressively, moving import picard.script.functions to the top
  4. Multi-line Import Handling:

    • Ruff: Maintains original format with trailing commas and proper indentation
    • Isort: Compacts multi-line imports, changing from vertical to horizontal layout

Configuration Attempts

I tested several configuration modifications in pyproject.toml:

  • lines-between-types = 0 - No effect on blank lines between sections
  • no-lines-before = ["picard", "local-folder"] - No effect
  • no-sections = true - Still maintained different formatting

Conclusion

The differences are inherent to the tools' design philosophies rather than configuration mismatches:

  • Ruff prioritizes preserving existing formatting and being minimally invasive
  • Isort prioritizes aggressive optimization and compacting imports

The blank line issue specifically occurs because ruff maintains spacing between logical import groups (regular imports vs. imports with comments), while isort removes most blank lines as part of its compacting strategy.

Recommendation

For projects requiring consistent import formatting between tools, consider:

  1. Choose one tool as the primary import sorter
  2. Configure pre-commit hooks to use only the chosen tool
  3. Document the decision for team consistency

The current configuration in pyproject.toml is as aligned as possible given the tools' fundamental differences.

Version

ruff 0.12.4

Metadata

Metadata

Assignees

No one assigned

    Labels

    isortRelated to import sorting

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions