summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ACKNOWLEDGEMENTS.md3
-rw-r--r--isort/isort.py61
-rw-r--r--test_isort.py84
3 files changed, 133 insertions, 15 deletions
diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md
index fede772d..4fcfe64b 100644
--- a/ACKNOWLEDGEMENTS.md
+++ b/ACKNOWLEDGEMENTS.md
@@ -86,6 +86,9 @@ Code Contributors
- Gram (@orsinium)
- Hugo van Kemenade (@hugovk)
- Géry Ogam (@maggyero)
+- Cody Scott (@Siecje)
+- Pedro Algarvio (@s0undt3ch)
+- Chris St. Pierre (@stpierre)
Documenters
===================
diff --git a/isort/isort.py b/isort/isort.py
index 48f65453..1e9f2447 100644
--- a/isort/isort.py
+++ b/isort/isort.py
@@ -66,6 +66,9 @@ if TYPE_CHECKING:
class _SortImports:
+ _import_line_intro_re = re.compile("^(?:from|import) ")
+ _import_line_midline_import_re = re.compile(" import ")
+
def __init__(
self, file_contents: str, config: Dict[str, Any], extension: str = "py"
) -> None:
@@ -273,7 +276,11 @@ class _SortImports:
) and not line_without_comment.strip().startswith(splitter):
line_parts = re.split(exp, line_without_comment)
if comment:
- line_parts[-1] = "{}#{}".format(line_parts[-1], comment)
+ line_parts[-1] = "{}{} #{}".format(
+ line_parts[-1].strip(),
+ "," if self.config["include_trailing_comma"] else "",
+ comment,
+ )
next_line = []
while (len(line) + 2) > (
self.config["wrap_length"] or self.config["line_length"]
@@ -295,7 +302,9 @@ class _SortImports:
splitter,
self.line_separator,
cont_line,
- "," if self.config["include_trailing_comma"] else "",
+ ","
+ if self.config["include_trailing_comma"] and not comment
+ else "",
self.line_separator
if wrap_mode
in {
@@ -426,7 +435,7 @@ class _SortImports:
)
from_imports = None
elif self.config["force_single_line"]:
- import_statements = []
+ import_statement = None
while from_imports:
from_import = from_imports.pop(0)
single_import_line = self._add_comments(
@@ -447,20 +456,19 @@ class _SortImports:
self.config["keep_direct_and_as_imports"]
and self.imports[section]["from"][module][from_import]
):
- import_statements.append(self._wrap(single_import_line))
+ section_output.append(self._wrap(single_import_line))
from_comments = self.comments["straight"].get(
"{}.{}".format(module, from_import)
)
- import_statements.extend(
+ section_output.extend(
self._add_comments(
from_comments, self._wrap(import_start + as_import)
)
for as_import in nsorted(as_imports[from_import])
)
else:
- import_statements.append(self._wrap(single_import_line))
+ section_output.append(self._wrap(single_import_line))
comments = None
- import_statement = self.line_separator.join(import_statements)
else:
while from_imports and from_imports[0] in as_imports:
from_import = from_imports.pop(0)
@@ -699,6 +707,9 @@ class _SortImports:
),
)
+ if self.config["force_sort_within_sections"]:
+ copied_comments = copy.deepcopy(self.comments)
+
section_output = [] # type: List[str]
if self.config["from_first"]:
self._add_from_imports(
@@ -727,19 +738,37 @@ class _SortImports:
def by_module(line: str) -> str:
section = "B"
- if line.startswith("#"):
- return "AA"
- line = re.sub("^from ", "", line)
- line = re.sub("^import ", "", line)
+ line = self._import_line_intro_re.sub(
+ "", self._import_line_midline_import_re.sub(".", line)
+ )
if line.split(" ")[0] in self.config["force_to_top"]:
section = "A"
if not self.config["order_by_type"]:
line = line.lower()
return "{}{}".format(section, line)
+ # Remove comments
+ section_output = [
+ line for line in section_output if not line.startswith("#")
+ ]
+
section_output = nsorted(section_output, key=by_module)
+ # Add comments back
+ all_comments = copied_comments["above"]["from"]
+ all_comments.update(copied_comments["above"]["straight"])
+ comment_indexes = {}
+ for module, comment_list in all_comments.items():
+ for idx, line in enumerate(section_output):
+ if module in line:
+ comment_indexes[idx] = comment_list
+ added = 0
+ for idx, comment_list in comment_indexes.items():
+ for comment in comment_list:
+ section_output.insert(idx + added, comment)
+ added += 1
+
section_name = section
no_lines_before = section_name in self.config["no_lines_before"]
@@ -1285,8 +1314,9 @@ class _SortImports:
placed_module = self.place_module(import_from)
if self.config["verbose"]:
print(
- "from-type place_module for %s returned %s"
- % (import_from, placed_module)
+ "from-type place_module for {} returned {}".format(
+ import_from, placed_module
+ )
)
if placed_module == "":
print(
@@ -1391,8 +1421,9 @@ class _SortImports:
placed_module = self.place_module(module)
if self.config["verbose"]:
print(
- "else-type place_module for %s returned %s"
- % (module, placed_module)
+ "else-type place_module for {} returned {}".format(
+ module, placed_module
+ )
)
if placed_module == "":
print(
diff --git a/test_isort.py b/test_isort.py
index 7ca611f4..c7585889 100644
--- a/test_isort.py
+++ b/test_isort.py
@@ -1066,6 +1066,26 @@ def test_force_single_line_long_imports() -> None:
)
+def test_force_single_line_imports_and_sort_within_sections() -> None:
+ test_input = (
+ "from third_party import lib_a, lib_b, lib_d\n"
+ "from third_party.lib_c import lib1\n"
+ )
+ test_output = SortImports(
+ file_contents=test_input,
+ multi_line_output=WrapModes.GRID,
+ line_length=40,
+ force_single_line=True,
+ force_sort_within_sections=True,
+ ).output
+ assert test_output == (
+ "from third_party import lib_a\n"
+ "from third_party import lib_b\n"
+ "from third_party.lib_c import lib1\n"
+ "from third_party import lib_d\n"
+ )
+
+
def test_titled_imports() -> None:
"""Tests setting custom titled/commented import sections."""
test_input = (
@@ -1523,6 +1543,26 @@ def test_include_trailing_comma() -> None:
"from third_party import (\n lib1,\n)\n"
)
+ trailing_comma_with_comment = "from six.moves.urllib.parse import urlencode # pylint: disable=no-name-in-module,import-error"
+ expected_trailing_comma_with_comment = "from six.moves.urllib.parse import (\n urlencode, # pylint: disable=no-name-in-module,import-error\n)\n"
+ trailing_comma_with_comment = SortImports(
+ file_contents=trailing_comma_with_comment,
+ line_length=80,
+ multi_line_output=WrapModes.VERTICAL_HANGING_INDENT,
+ include_trailing_comma=True,
+ use_parentheses=True,
+ ).output
+ assert trailing_comma_with_comment == expected_trailing_comma_with_comment
+ # The next time around, it should be equal
+ trailing_comma_with_comment = SortImports(
+ file_contents=trailing_comma_with_comment,
+ line_length=80,
+ multi_line_output=WrapModes.VERTICAL_HANGING_INDENT,
+ include_trailing_comma=True,
+ use_parentheses=True,
+ ).output
+ assert trailing_comma_with_comment == expected_trailing_comma_with_comment
+
def test_similar_to_std_library() -> None:
"""Test to ensure modules that are named similarly to a standard library import don't end up clobbered"""
@@ -4244,6 +4284,25 @@ def test_isort_ensures_blank_line_between_import_and_comment() -> None:
assert SortImports(file_contents=test_input, **config).output == expected_output
+def test_moving_comments_issue_726():
+ config = {"force_sort_within_sections": 1} # type: Dict[str, Any]
+ test_input = (
+ "import Blue.models as BlueModels\n"
+ "# comment for PlaidModel\n"
+ "from Plaid.models import PlaidModel\n"
+ )
+ assert SortImports(file_contents=test_input, **config).output == test_input
+
+ test_input = (
+ "# comment for BlueModels\n"
+ "import Blue.models as BlueModels\n"
+ "# comment for PlaidModel\n"
+ "# another comment for PlaidModel\n"
+ "from Plaid.models import PlaidModel\n"
+ )
+ assert SortImports(file_contents=test_input, **config).output == test_input
+
+
def test_pyi_formatting_issue_942(tmpdir) -> None:
test_input = "import os\n\n\ndef my_method():\n"
expected_py_output = test_input.splitlines()
@@ -4270,6 +4329,31 @@ def test_pyi_formatting_issue_942(tmpdir) -> None:
)
+def test_move_class_issue_751() -> None:
+ test_input = (
+ "# -*- coding: utf-8 -*-"
+ "\n"
+ "# Define your item pipelines here"
+ "#"
+ "# Don't forget to add your pipeline to the ITEM_PIPELINES setting"
+ "# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html"
+ "from datetime import datetime"
+ "from .items import WeiboMblogItem"
+ "\n"
+ "class WeiboMblogPipeline(object):"
+ " def process_item(self, item, spider):"
+ " if isinstance(item, WeiboMblogItem):"
+ " item = self._process_item(item, spider)"
+ " return item"
+ "\n"
+ " def _process_item(self, item, spider):"
+ " item['inserted_at'] = datetime.now()"
+ " return item"
+ "\n"
+ )
+ assert SortImports(file_contents=test_input).output == test_input
+
+
def test_python_version() -> None:
from isort.main import parse_args