diff options
author | Timothy Edmund Crosley <timothy.crosley@gmail.com> | 2020-01-07 13:14:26 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-07 13:14:26 -0800 |
commit | 3523ea274390f27f14a4b6a84e6ec59db26e5a77 (patch) | |
tree | 5aa8683abf76cfc1d18a6cc1112361e6a3a52a3d | |
parent | 3a51988fa75d4e2b71c49764395c5c4564954490 (diff) | |
parent | 9b654e379e4f92ee0bd73c079e2b5c11ed969ed1 (diff) | |
download | isort-3523ea274390f27f14a4b6a84e6ec59db26e5a77.tar.gz |
Merge branch 'develop' into issues/1081
-rw-r--r-- | isort/api.py | 4 | ||||
-rw-r--r-- | isort/main.py | 14 | ||||
-rw-r--r-- | isort/output.py | 18 | ||||
-rw-r--r-- | isort/profiles.py | 7 | ||||
-rw-r--r-- | isort/settings.py | 1 | ||||
-rw-r--r-- | tests/test_isort.py | 58 | ||||
-rw-r--r-- | tests/test_main.py | 21 |
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) |