Skip to content

Commit

Permalink
Add magic trailing comma option
Browse files Browse the repository at this point in the history
  • Loading branch information
legau committed Mar 12, 2022
1 parent 44e3b5d commit 8cde8b2
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 8 deletions.
11 changes: 11 additions & 0 deletions docs/configuration/options.md
Expand Up @@ -889,6 +889,17 @@ Includes a trailing comma on multi line imports that include parentheses.

- --tc
- --trailing-comma
## Split on Trailing Comma

Split imports list followed by a trailing comma into VERTICAL_HANGING_INDENT mode. This follows Black style magic comma.

**Type:** Bool
**Default:** `False`
**Config default:** `false`
**Python & Config File Name:** split_on_trailing_comma
**CLI Flags:**

- --split-on-trailing-comma

## From First

Expand Down
1 change: 1 addition & 0 deletions docs/configuration/profiles.md
Expand Up @@ -16,6 +16,7 @@ To use any of the listed profiles, use `isort --profile PROFILE_NAME` from the c
- **use_parentheses**: `True`
- **ensure_newline_before_comments**: `True`
- **line_length**: `88`
- **split_on_trailing_comma**: `True`

#django

Expand Down
6 changes: 6 additions & 0 deletions isort/main.py
Expand Up @@ -714,6 +714,12 @@ def _build_arg_parser() -> argparse.ArgumentParser:
dest="star_first",
action="store_true",
)
output_group.add_argument(
"--split-on-trailing-comma",
help="Split imports list followed by a trailing comma into VERTICAL_HANGING_INDENT mode",
dest="split_on_trailing_comma",
action="store_true",
)

section_group.add_argument(
"--sd",
Expand Down
14 changes: 12 additions & 2 deletions isort/output.py
Expand Up @@ -505,7 +505,17 @@ def _with_from_imports(
):
do_multiline_reformat = True

if do_multiline_reformat:
if config.split_on_trailing_comma and module in parsed.trailing_commas:
import_statement = wrap.import_statement(
import_start=import_start,
from_imports=from_import_section,
comments=comments,
line_separator=parsed.line_separator,
config=config,
explode=True,
)

elif do_multiline_reformat:
import_statement = wrap.import_statement(
import_start=import_start,
from_imports=from_import_section,
Expand All @@ -530,7 +540,7 @@ def _with_from_imports(
> config.line_length
):
import_statement = other_import_statement
if not do_multiline_reformat and len(import_statement) > config.line_length:
elif len(import_statement) > config.line_length:
import_statement = wrap.line(import_statement, parsed.line_separator, config)

if import_statement:
Expand Down
9 changes: 8 additions & 1 deletion isort/parse.py
Expand Up @@ -2,7 +2,7 @@
from collections import OrderedDict, defaultdict
from functools import partial
from itertools import chain
from typing import TYPE_CHECKING, Any, Dict, List, NamedTuple, Optional, Tuple
from typing import TYPE_CHECKING, Any, Dict, List, NamedTuple, Optional, Set, Tuple
from warnings import warn

from . import place
Expand Down Expand Up @@ -138,6 +138,7 @@ class ParsedContent(NamedTuple):
line_separator: str
sections: Any
verbose_output: List[str]
trailing_commas: Set[str]


def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedContent:
Expand Down Expand Up @@ -176,6 +177,8 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte
"above": {"straight": {}, "from": {}},
}

trailing_commas: Set[str] = set()

index = 0
import_index = -1
in_quote = ""
Expand Down Expand Up @@ -515,6 +518,9 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte

if comments and attach_comments_to is not None:
attach_comments_to.extend(comments)

if "," in import_string.split(just_imports[-1])[-1]:
trailing_commas.add(import_from)
else:
if comments and attach_comments_to is not None:
attach_comments_to.extend(comments)
Expand Down Expand Up @@ -587,4 +593,5 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte
line_separator=line_separator,
sections=config.sections,
verbose_output=verbose_output,
trailing_commas=trailing_commas,
)
1 change: 1 addition & 0 deletions isort/settings.py
Expand Up @@ -243,6 +243,7 @@ class _Config:
format_success: str = "{success}: {message}"
sort_order: str = "natural"
sort_reexports: bool = False
split_on_trailing_comma: bool = False

def __post_init__(self) -> None:
py_version = self.py_version
Expand Down
17 changes: 12 additions & 5 deletions isort/wrap.py
Expand Up @@ -4,7 +4,7 @@

from .settings import DEFAULT_CONFIG, Config
from .wrap_modes import WrapModes as Modes
from .wrap_modes import formatter_from_string
from .wrap_modes import formatter_from_string, vertical_hanging_indent


def import_statement(
Expand All @@ -14,12 +14,19 @@ def import_statement(
line_separator: str = "\n",
config: Config = DEFAULT_CONFIG,
multi_line_output: Optional[Modes] = None,
explode: bool = False,
) -> str:
"""Returns a multi-line wrapped form of the provided from import statement."""
formatter = formatter_from_string((multi_line_output or config.multi_line_output).name)
if explode:
formatter = vertical_hanging_indent
line_length = 1
include_trailing_comma = True
else:
formatter = formatter_from_string((multi_line_output or config.multi_line_output).name)
line_length = config.wrap_length or config.line_length
include_trailing_comma = config.include_trailing_comma
dynamic_indent = " " * (len(import_start) + 1)
indent = config.indent
line_length = config.wrap_length or config.line_length
statement = formatter(
statement=import_start,
imports=copy.copy(from_imports),
Expand All @@ -29,7 +36,7 @@ def import_statement(
comments=comments,
line_separator=line_separator,
comment_prefix=config.comment_prefix,
include_trailing_comma=config.include_trailing_comma,
include_trailing_comma=include_trailing_comma,
remove_comments=config.ignore_comments,
)
if config.balanced_wrapping:
Expand All @@ -52,7 +59,7 @@ def import_statement(
comments=comments,
line_separator=line_separator,
comment_prefix=config.comment_prefix,
include_trailing_comma=config.include_trailing_comma,
include_trailing_comma=include_trailing_comma,
remove_comments=config.ignore_comments,
)
lines = new_import_statement.split(line_separator)
Expand Down
16 changes: 16 additions & 0 deletions tests/unit/test_isort.py
Expand Up @@ -5562,3 +5562,19 @@ def seekable(self):
test_input = NonSeekableTestStream("import m2\n" "import m1\n" "not_import = 7")
identified_imports = list(map(str, api.find_imports_in_stream(test_input)))
assert identified_imports == [":1 import m2", ":2 import m1"]


def test_split_on_trailing_comma() -> None:
test_input = "from lib import (a, b, c,)"
expected_output = """from lib import (
a,
b,
c,
)
"""

output = isort.code(test_input, split_on_trailing_comma=True)
assert output == expected_output

output = isort.code(expected_output, split_on_trailing_comma=True)
assert output == expected_output
1 change: 1 addition & 0 deletions tests/unit/test_parse.py
Expand Up @@ -38,6 +38,7 @@ def test_file_contents():
_,
_,
_,
_,
) = parse.file_contents(TEST_CONTENTS, config=Config(default_section=""))
assert "\n".join(in_lines) == TEST_CONTENTS
assert "import" not in "\n".join(out_lines)
Expand Down
3 changes: 3 additions & 0 deletions tests/unit/test_wrap.py
Expand Up @@ -13,3 +13,6 @@ def test_import_statement():
== """from long_import (verylong, verylong, verylong, verylong, verylong, verylong,
verylong, verylong, verylong, verylong)"""
)
assert wrap.import_statement("from x import ", ["y", "z"], [], explode=True) == (
"from x import (\n y,\n z,\n)"
)

0 comments on commit 8cde8b2

Please sign in to comment.