summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimothy Edmund Crosley <timothy.crosley@gmail.com>2020-01-07 13:14:26 -0800
committerGitHub <noreply@github.com>2020-01-07 13:14:26 -0800
commit3523ea274390f27f14a4b6a84e6ec59db26e5a77 (patch)
tree5aa8683abf76cfc1d18a6cc1112361e6a3a52a3d
parent3a51988fa75d4e2b71c49764395c5c4564954490 (diff)
parent9b654e379e4f92ee0bd73c079e2b5c11ed969ed1 (diff)
downloadisort-3523ea274390f27f14a4b6a84e6ec59db26e5a77.tar.gz
Merge branch 'develop' into issues/1081
-rw-r--r--isort/api.py4
-rw-r--r--isort/main.py14
-rw-r--r--isort/output.py18
-rw-r--r--isort/profiles.py7
-rw-r--r--isort/settings.py1
-rw-r--r--tests/test_isort.py58
-rw-r--r--tests/test_main.py21
7 files changed, 111 insertions, 12 deletions
diff --git a/isort/api.py b/isort/api.py
index 49c693ae..0ea7187f 100644
--- a/isort/api.py
+++ b/isort/api.py
@@ -321,9 +321,11 @@ def sort_imports(
output_stream.write(line)
indent = ""
- contains_imports = False
if next_import_section:
cimports = not cimports
+ contains_imports = True
+ else:
+ contains_imports = False
import_section = next_import_section
next_import_section = ""
else:
diff --git a/isort/main.py b/isort/main.py
index dc2a4280..0bbf3353 100644
--- a/isort/main.py
+++ b/isort/main.py
@@ -4,6 +4,7 @@ import functools
import glob
import os
import re
+import stat
import sys
from pathlib import Path
from typing import Any, Dict, Iterable, Iterator, List, Optional, Sequence
@@ -45,6 +46,12 @@ def is_python_file(path: str) -> bool:
return False
try:
+ if stat.S_ISFIFO(os.stat(path).st_mode):
+ return False
+ except OSError:
+ pass
+
+ try:
with open(path, "rb") as fp:
line = fp.readline(100)
except OSError:
@@ -416,6 +423,13 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> Dict[str, Any]:
help="Forces all from imports to appear on their own line",
)
parser.add_argument(
+ "--nsl",
+ "--single-line-exclusions",
+ help="One or more modules to exclude from the single line rule.",
+ dest="single_line_exclusions",
+ action="append",
+ )
+ parser.add_argument(
"--sp",
"--settings-path",
dest="settings_path",
diff --git a/isort/output.py b/isort/output.py
index d6a5f562..e9a6f8bf 100644
--- a/isort/output.py
+++ b/isort/output.py
@@ -1,14 +1,15 @@
import copy
import itertools
from functools import partial
-from typing import Iterable, List
-
+from typing import Iterable, List, Tuple
from isort.format import format_simplified
from . import parse, sorting, wrap
from .comments import add_to_line as with_comments
from .settings import DEFAULT_CONFIG, Config
+STATEMENT_DECLERATIONS: Tuple[str, ...] = ("def ", "cdef ", "cpdef ", "class ", "@", "async def")
+
def sorted_imports(
parsed: parse.ParsedContent,
@@ -202,12 +203,7 @@ def sorted_imports(
if config.lines_after_imports != -1:
formatted_output[imports_tail:0] = ["" for line in range(config.lines_after_imports)]
- elif extension != "pyi" and (
- next_construct.startswith("def ")
- or next_construct.startswith("class ")
- or next_construct.startswith("@")
- or next_construct.startswith("async def")
- ):
+ elif extension != "pyi" and next_construct.startswith(STATEMENT_DECLERATIONS):
formatted_output[imports_tail:0] = ["", ""]
else:
formatted_output[imports_tail:0] = [""]
@@ -242,7 +238,9 @@ def _with_from_imports(
import_start = f"from {module} {import_type} "
from_imports = list(parsed.imports[section]["from"][module])
- if not config.no_inline_sort or config.force_single_line:
+ if not config.no_inline_sort or (
+ config.force_single_line and module not in config.single_line_exclusions
+ ):
from_imports = sorting.naturally(
from_imports,
key=lambda key: sorting.module_key(
@@ -291,7 +289,7 @@ def _with_from_imports(
config,
)
from_imports = []
- elif config.force_single_line:
+ elif config.force_single_line and module not in config.single_line_exclusions:
import_statement = ""
while from_imports:
from_import = from_imports.pop(0)
diff --git a/isort/profiles.py b/isort/profiles.py
index 1210c8ef..910da935 100644
--- a/isort/profiles.py
+++ b/isort/profiles.py
@@ -15,7 +15,12 @@ django = {
"line_length": 79,
}
pycharm = {"multi_line_output": 3, "force_grid_wrap": 2}
-google = {"force_single_line": True, "force_sort_within_sections": True, "lexicographical": True}
+google = {
+ "force_single_line": True,
+ "force_sort_within_sections": True,
+ "lexicographical": True,
+ "single_line_exclusions": ("typing",),
+}
open_stack = {
"force_single_line": True,
"force_sort_within_sections": True,
diff --git a/isort/settings.py b/isort/settings.py
index 1885eef3..e05560dd 100644
--- a/isort/settings.py
+++ b/isort/settings.py
@@ -135,6 +135,7 @@ class _Config:
remove_imports: FrozenSet[str] = frozenset()
reverse_relative: bool = False
force_single_line: bool = False
+ single_line_exclusions: Tuple[str, ...] = ()
default_section: str = "FIRSTPARTY"
import_headings: Dict[str, str] = field(default_factory=dict)
balanced_wrapping: bool = False
diff --git a/tests/test_isort.py b/tests/test_isort.py
index 11a7079f..efff42c4 100644
--- a/tests/test_isort.py
+++ b/tests/test_isort.py
@@ -4627,6 +4627,44 @@ IF CEF_VERSION == 3:
SortImports(file_contents=test_input).output == expected_output
+def test_cdef_support():
+ assert (
+ SortImports(
+ file_contents="""
+from cpython.version cimport PY_MAJOR_VERSION
+
+cdef extern from *:
+ ctypedef CefString ConstCefString "const CefString"
+"""
+ ).output
+ == """
+from cpython.version cimport PY_MAJOR_VERSION
+
+
+cdef extern from *:
+ ctypedef CefString ConstCefString "const CefString"
+"""
+ )
+
+ assert (
+ SortImports(
+ file_contents="""
+from cpython.version cimport PY_MAJOR_VERSION
+
+cpdef extern from *:
+ ctypedef CefString ConstCefString "const CefString"
+"""
+ ).output
+ == """
+from cpython.version cimport PY_MAJOR_VERSION
+
+
+cpdef extern from *:
+ ctypedef CefString ConstCefString "const CefString"
+"""
+ )
+
+
def test_top_level_import_order() -> None:
test_input = (
"from rest_framework import throttling, viewsets\n"
@@ -4664,3 +4702,23 @@ from flask_security.signals import user_confirmed # noqa
from flask_security.signals import user_registered # noqa
"""
assert SortImports(file_contents=test_input).output == expected_output
+
+
+def test_single_line_exclusions():
+ test_input = """
+# start comment
+from os import path, system
+from typing import List, TypeVar
+"""
+ expected_output = """
+# start comment
+from os import path
+from os import system
+from typing import List, TypeVar
+"""
+ assert (
+ SortImports(
+ file_contents=test_input, force_single_line=True, single_line_exclusions=("typing",)
+ ).output
+ == expected_output
+ )
diff --git a/tests/test_main.py b/tests/test_main.py
new file mode 100644
index 00000000..300108a0
--- /dev/null
+++ b/tests/test_main.py
@@ -0,0 +1,21 @@
+import os
+import sys
+
+import pytest
+
+from isort import main
+
+
+def test_is_python_file():
+ assert main.is_python_file("file.py")
+ assert main.is_python_file("file.pyi")
+ assert main.is_python_file("file.pyx")
+ assert not main.is_python_file("file.pyc")
+ assert not main.is_python_file("file.txt")
+
+
+@pytest.mark.skipif(sys.platform == "win32", reason="cannot create fifo file on Windows platform")
+def test_is_python_file_fifo(tmpdir):
+ fifo_file = os.path.join(tmpdir, "fifo_file")
+ os.mkfifo(fifo_file)
+ assert not main.is_python_file(fifo_file)