Skip to content

Commit

Permalink
feat: add separate_packages option
Browse files Browse the repository at this point in the history
Adds the ability to separate packages within a section with blank lines.
---------

Co-authored-by: Lucas <[email protected]>
  • Loading branch information
alex-liang3 and lucas-bremond authored Dec 12, 2024
1 parent 7de1829 commit ec2b340
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 0 deletions.
10 changes: 10 additions & 0 deletions docs/configuration/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,16 @@ If `True` isort will automatically create section groups by the top-level packag
**Python & Config File Name:** group_by_package
**CLI Flags:** **Not Supported**

## Separate Packages

Separate packages within the listed sections with newlines.

**Type:** List of Strings
**Default:** `frozenset()`
**Config default:** `[]`
**Python & Config File Name:** separate_packages
**CLI Flags:** **Not Supported**

## Ignore Whitespace

Tells isort to ignore whitespace differences when --check-only is being used.
Expand Down
33 changes: 33 additions & 0 deletions isort/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from . import parse, sorting, wrap
from .comments import add_to_line as with_comments
from .identify import STATEMENT_DECLARATIONS
from .place import module_with_reason
from .settings import DEFAULT_CONFIG, Config


Expand Down Expand Up @@ -149,6 +150,38 @@ def sorted_imports(
section_output.append("") # Empty line for black compatibility
section_output.append(section_comment_end)

if section in config.separate_packages:
group_keys: set[str] = set()
comments_above: list[str] = []
processed_section_output: list[str] = []
for section_line in section_output:
if section_line.startswith("#"):
comments_above.append(section_line)
continue

package_name: str = section_line.split(" ")[1]
_, reason = module_with_reason(package_name, config)

if "Matched configured known pattern" in reason:
package_depth = len(reason.split(".")) - 1 # minus 1 for re.compile
key = ".".join(package_name.split(".")[: package_depth + 1])
else:
key = package_name.split(".")[0]

if key not in group_keys:
if group_keys:
processed_section_output.append("")

group_keys.add(key)

if comments_above:
processed_section_output.extend(comments_above)
comments_above = []

processed_section_output.append(section_line)

section_output = processed_section_output

if pending_lines_before or not no_lines_before:
output += [""] * config.lines_between_sections

Expand Down
1 change: 1 addition & 0 deletions isort/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class _Config:
force_sort_within_sections: bool = False
lexicographical: bool = False
group_by_package: bool = False
separate_packages: FrozenSet[str] = frozenset()
ignore_whitespace: bool = False
no_lines_before: FrozenSet[str] = frozenset()
no_inline_sort: bool = False
Expand Down
109 changes: 109 additions & 0 deletions tests/unit/test_ticketed_features.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""A growing set of tests designed to ensure when isort implements a feature described in a ticket
it fully works as defined in the associated ticket.
"""

from functools import partial
from io import StringIO

Expand Down Expand Up @@ -1071,3 +1072,111 @@ def use_libc_math():
""",
show_diff=True,
)


def test_sort_separate_packages_issue_2104():
"""
Test to ensure that packages within a section can be separated by blank lines.
See: https://github.com/PyCQA/isort/issues/2104
"""

# Base case as described in issue
assert (
isort.code(
"""
import os
import sys
from django.db.models.signals import m2m_changed
from django.utils import functional
from django_filters import BooleanFilter
from junitparser import JUnitXml
from junitparser import TestSuite
from loguru import logger
""",
force_single_line=True,
separate_packages=["THIRDPARTY"],
)
== """
import os
import sys
from django.db.models.signals import m2m_changed
from django.utils import functional
from django_filters import BooleanFilter
from junitparser import JUnitXml
from junitparser import TestSuite
from loguru import logger
"""
)

# Check that multiline comments aren't broken up
assert (
isort.code(
"""
from junitparser import TestSuite
# Some multiline
# comment
from loguru import logger
""",
force_single_line=True,
separate_packages=["THIRDPARTY"],
)
== """
from junitparser import TestSuite
# Some multiline
# comment
from loguru import logger
"""
)

# Check it works for custom sections
assert (
isort.code(
"""
import os
from package2 import bar
from package1 import foo
""",
force_single_line=True,
known_MYPACKAGES=["package1", "package2"],
sections=["STDLIB", "MYPACKAGES"],
separate_packages=["MYPACKAGES"],
)
== """
import os
from package1 import foo
from package2 import bar
"""
)

# Check it works for packages with deeper nesting
assert (
isort.code(
"""
import os
from package2 import bar
from package1.a.b import foo
from package1.a.c import baz
""",
force_single_line=True,
known_MYPACKAGES=["package1.a", "package2"],
sections=["STDLIB", "MYPACKAGES"],
separate_packages=["MYPACKAGES"],
)
== """
import os
from package1.a.b import foo
from package1.a.c import baz
from package2 import bar
"""
)

0 comments on commit ec2b340

Please sign in to comment.