diff options
-rw-r--r-- | isort/api.py | 24 | ||||
-rw-r--r-- | isort/parse.py | 8 | ||||
-rw-r--r-- | isort/settings.py | 5 | ||||
-rw-r--r-- | tests/test_isort.py | 45 |
4 files changed, 64 insertions, 18 deletions
diff --git a/isort/api.py b/isort/api.py index c53fe98a..8d435b21 100644 --- a/isort/api.py +++ b/isort/api.py @@ -15,7 +15,7 @@ from .exceptions import ( ) from .format import format_natural, remove_whitespace, show_unified_diff from .io import File -from .settings import DEFAULT_CONFIG, FILE_SKIP_COMMENT, Config +from .settings import DEFAULT_CONFIG, FILE_SKIP_COMMENTS, Config IMPORT_START_IDENTIFIERS = ("from ", "from.import", "import ", "import*") COMMENT_INDICATORS = ('"""', "'''", "'", '"', "#") @@ -54,12 +54,13 @@ def sorted_imports( config = _config(config=config, **config_kwargs) content_source = str(file_path or "Passed in content") if not disregard_skip: - if FILE_SKIP_COMMENT in file_contents: - raise FileSkipComment(content_source) - - elif file_path and config.is_skipped(file_path): + if file_path and config.is_skipped(file_path): raise FileSkipSetting(content_source) + for file_skip_comment in FILE_SKIP_COMMENTS: + if file_skip_comment in file_contents: + raise FileSkipComment(content_source) + if config.atomic: try: compile(file_contents, content_source, "exec", 0, 1) @@ -156,6 +157,7 @@ def sort_imports( first_import_section: bool = True section_comments = [f"# {heading}" for heading in config.import_headings.values()] indent: str = "" + isort_off: bool = False for index, line in enumerate(chain(input_stream, (None,))): if line is None: @@ -207,10 +209,18 @@ def sort_imports( break char_index += 1 - not_imports = bool(in_quote) or in_top_comment + not_imports = bool(in_quote) or in_top_comment or isort_off if not (in_quote or in_top_comment): stripped_line = line.strip() - if not stripped_line or stripped_line.startswith("#"): + if isort_off: + if stripped_line == "# isort: on": + isort_off = False + elif stripped_line == "# isort: off": + not_imports = True + isort_off = True + elif stripped_line == "# isort: split": + not_imports = True + elif not stripped_line or stripped_line.startswith("#"): import_section += line elif stripped_line.startswith(IMPORT_START_IDENTIFIERS): contains_imports = True diff --git a/isort/parse.py b/isort/parse.py index a87461ff..6089c08b 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -54,7 +54,7 @@ def _normalize_line(raw_line: str) -> Tuple[str, str]: def import_type(line: str) -> Optional[str]: """If the current line is an import line it will return its type (from or straight)""" - if "isort:skip" in line or "NOQA" in line: + if "isort:skip" in line or "isort: skip" in line or "NOQA" in line: return None elif line.startswith("import "): return "straight" @@ -174,6 +174,10 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte section = line.split("isort:imports-")[-1].split()[0].upper() place_imports[section] = [] import_placements[line] = section + elif "isort: imports-" in line and line.startswith("#"): + section = line.split("isort: imports-")[-1].split()[0].upper() + place_imports[section] = [] + import_placements[line] = section if skipping_line: out_lines.append(line) @@ -325,6 +329,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte and not last.endswith('"""') and not last.endswith("'''") and "isort:imports-" not in last + and "isort: imports-" not in last ): categorized_comments["above"]["from"].setdefault(import_from, []).insert( 0, out_lines.pop(-1) @@ -361,6 +366,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte and not last.endswith('"""') and not last.endswith("'''") and "isort:imports-" not in last + and "isort: imports-" not in last ): categorized_comments["above"]["straight"].setdefault(module, []).insert( 0, out_lines.pop(-1) diff --git a/isort/settings.py b/isort/settings.py index 1bdc35cb..ed5e4a51 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -55,7 +55,10 @@ try: except ImportError: appdirs = None -FILE_SKIP_COMMENT: str = ("isort:" + "skip_file") # Concatenated to avoid this file being skipped +FILE_SKIP_COMMENTS: Tuple[str, ...] = ( + "isort:" + "skip_file", + "isort: " + "skip_file", +) # Concatenated to avoid this file being skipped MAX_CONFIG_SEARCH_DEPTH: int = 25 # The number of parent directories to for a config file within STOP_CONFIG_SEARCH_ON_DIRS: Tuple[str, ...] = (".git", ".hg") VALID_PY_TARGETS: Tuple[str, ...] = tuple( diff --git a/tests/test_isort.py b/tests/test_isort.py index 40b22bb2..e5072b0f 100644 --- a/tests/test_isort.py +++ b/tests/test_isort.py @@ -641,7 +641,7 @@ def test_skip() -> None: "import myproject\n" "import django\n" "print('hey')\n" - "import sys # isort:skip this import needs to be placed here\n\n\n\n\n\n\n" + "import sys # isort: skip this import needs to be placed here\n\n\n\n\n\n\n" ) test_output = SortImports(file_contents=test_input, known_third_party=["django"]).output @@ -651,7 +651,7 @@ def test_skip() -> None: "import myproject\n" "\n" "print('hey')\n" - "import sys # isort:skip this import needs to be placed here\n" + "import sys # isort: skip this import needs to be placed here\n" ) @@ -668,7 +668,7 @@ def test_skip_with_file_name() -> None: def test_skip_within_file() -> None: """Ensure skipping a whole file works.""" - test_input = "# isort:skip_file\nimport django\nimport myproject\n" + test_input = "# isort: skip_file\nimport django\nimport myproject\n" sort_imports = SortImports(file_contents=test_input, known_third_party=["django"]) assert sort_imports.skipped assert sort_imports.output == "" @@ -1624,19 +1624,19 @@ def test_place_comments() -> None: "import myproject.test\n" "import django.settings\n" "\n" - "# isort:imports-thirdparty\n" - "# isort:imports-firstparty\n" - "# isort:imports-stdlib\n" + "# isort: imports-thirdparty\n" + "# isort: imports-firstparty\n" + "# isort: imports-stdlib\n" "\n" ) expected_output = ( - "\n# isort:imports-thirdparty\n" + "\n# isort: imports-thirdparty\n" "import django.settings\n" "\n" - "# isort:imports-firstparty\n" + "# isort: imports-firstparty\n" "import myproject.test\n" "\n" - "# isort:imports-stdlib\n" + "# isort: imports-stdlib\n" "import os\n" "import sys\n" ) @@ -4186,3 +4186,30 @@ def test_isort_nested_imports() -> None: return True """ ) + + +def test_isort_off() -> None: + """Test that isort can be turned on and off at will using comments""" + test_input = """import os + +# isort: off +import sys +import os +# isort: on + +from . import local +""" + assert SortImports(file_contents=test_input).output == test_input + + +def test_isort_split() -> None: + """Test the ability to split isort import sections""" + test_input = """import os +import sys + +# isort: split + +import os +import sys +""" + assert SortImports(file_contents=test_input).output == test_input |