diff options
author | Timothy Crosley <timothy.crosley@gmail.com> | 2020-10-03 01:24:35 -0700 |
---|---|---|
committer | Timothy Crosley <timothy.crosley@gmail.com> | 2020-10-03 01:24:35 -0700 |
commit | 5fad3537b0e4eed1f21454f33f7387ac79b2caa8 (patch) | |
tree | 8dac26526289cc83a9579623c8795d8545de47e8 | |
parent | 82d76ef81b90ed213f9ce3f46d3c97e1322025be (diff) | |
parent | fc956a7e01ab9f24488a5a18954ea23ed0e649c2 (diff) | |
download | isort-5fad3537b0e4eed1f21454f33f7387ac79b2caa8.tar.gz |
Merge branch 'develop' of https://github.com/timothycrosley/isort into issue/1487/improve-handling-of-encoding-errors
-rw-r--r-- | isort/api.py | 2 | ||||
-rw-r--r-- | isort/core.py | 11 | ||||
-rw-r--r-- | isort/main.py | 8 | ||||
-rw-r--r-- | isort/parse.py | 18 | ||||
-rw-r--r-- | isort/settings.py | 1 | ||||
-rw-r--r-- | tests/unit/test_main.py | 101 | ||||
-rw-r--r-- | tests/unit/test_parse.py | 1 | ||||
-rw-r--r-- | tests/unit/test_regressions.py | 75 |
8 files changed, 213 insertions, 4 deletions
diff --git a/isort/api.py b/isort/api.py index f59bc6d9..6c5876be 100644 --- a/isort/api.py +++ b/isort/api.py @@ -210,7 +210,7 @@ def check_stream( ) printer = create_terminal_printer(color=config.color_output) if not changed: - if config.verbose: + if config.verbose and not config.only_modified: printer.success(f"{file_path or ''} Everything Looks Good!") return True else: diff --git a/isort/core.py b/isort/core.py index 22cba49e..7f4c2c8a 100644 --- a/isort/core.py +++ b/isort/core.py @@ -67,6 +67,7 @@ def process( made_changes: bool = False stripped_line: str = "" end_of_file: bool = False + verbose_output: List[str] = [] if config.float_to_top: new_input = "" @@ -87,6 +88,7 @@ def process( current += line_separator + line_separator.join(add_imports) add_imports = [] parsed = parse.file_contents(current, config=config) + verbose_output += parsed.verbose_output extra_space = "" while current and current[-1] == "\n": extra_space += "\n" @@ -325,8 +327,11 @@ def process( line[len(indent) :] for line in import_section.splitlines(keepends=True) ) + parsed_content = parse.file_contents(import_section, config=config) + verbose_output += parsed_content.verbose_output + sorted_import_section = output.sorted_imports( - parse.file_contents(import_section, config=config), + parsed_content, _indented_config(config, indent), extension, import_type="cimport" if cimports else "import", @@ -384,6 +389,10 @@ def process( output_stream.write(new_line) stripped_line = new_line.strip().split("#")[0] + if made_changes and config.only_modified: + for output_str in verbose_output: + print(output_str) + return made_changes diff --git a/isort/main.py b/isort/main.py index 841caca5..a8e59f0e 100644 --- a/isort/main.py +++ b/isort/main.py @@ -768,6 +768,14 @@ def _build_arg_parser() -> argparse.ArgumentParser: "Imports are unaltered and keep their relative positions within the different sections.", ) + parser.add_argument( + "--only-modified", + "--om", + dest="only_modified", + action="store_true", + help="Suppresses verbose output for non-modified files.", + ) + return parser diff --git a/isort/parse.py b/isort/parse.py index 6f121c6b..9348267c 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -138,6 +138,7 @@ class ParsedContent(NamedTuple): original_line_count: int line_separator: str sections: Any + verbose_output: List[str] def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedContent: @@ -163,6 +164,8 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte "from": defaultdict(list), } imports: OrderedDict[str, Dict[str, Any]] = OrderedDict() + verbose_output: List[str] = [] + for section in chain(config.sections, config.forced_separate): imports[section] = {"straight": OrderedDict(), "from": OrderedDict()} categorized_comments: CommentsDict = { @@ -380,8 +383,13 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte if type_of_import == "from": import_from = just_imports.pop(0) placed_module = finder(import_from) - if config.verbose: + if config.verbose and not config.only_modified: print(f"from-type place_module for {import_from} returned {placed_module}") + + elif config.verbose: + verbose_output.append( + f"from-type place_module for {import_from} returned {placed_module}" + ) if placed_module == "": warn( f"could not place module {import_from} of line {line} --" @@ -469,8 +477,13 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte categorized_comments["above"]["straight"].get(module, []) ) placed_module = finder(module) - if config.verbose: + if config.verbose and not config.only_modified: print(f"else-type place_module for {module} returned {placed_module}") + + elif config.verbose: + verbose_output.append( + f"else-type place_module for {module} returned {placed_module}" + ) if placed_module == "": warn( f"could not place module {module} of line {line} --" @@ -497,4 +510,5 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte original_line_count=original_line_count, line_separator=line_separator, sections=config.sections, + verbose_output=verbose_output, ) diff --git a/isort/settings.py b/isort/settings.py index a4e5905a..937e6e06 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -199,6 +199,7 @@ class _Config: variables: FrozenSet[str] = frozenset() dedup_headings: bool = False only_sections: bool = False + only_modified: bool = False def __post_init__(self): py_version = self.py_version diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 9c2da466..3fb75ed6 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -70,6 +70,8 @@ def test_parse_args(): assert main.parse_args(["--dt"]) == {"order_by_type": False} assert main.parse_args(["--only-sections"]) == {"only_sections": True} assert main.parse_args(["--os"]) == {"only_sections": True} + assert main.parse_args(["--om"]) == {"only_modified": True} + assert main.parse_args(["--only-modified"]) == {"only_modified": True} def test_ascii_art(capsys): @@ -750,4 +752,103 @@ __revision__ = 'יייי' main.main([str(tmp_file), str(normal_file), "--verbose"]) out, error = capsys.readouterr() + # ensures that only-modified flag works with stdin + input_content = TextIOWrapper( + BytesIO( + b""" +import a +import b +""" + ) + ) + + main.main(["-", "--verbose", "--only-modified"], stdin=input_content) + out, error = capsys.readouterr() + + assert "else-type place_module for a returned THIRDPARTY" not in out + assert "else-type place_module for b returned THIRDPARTY" not in out + + +def test_only_modified_flag(tmpdir, capsys): + # ensures there is no verbose output for correct files with only-modified flag + + file1 = tmpdir.join("file1.py") + file1.write( + """ +import a +import b +""" + ) + + file2 = tmpdir.join("file2.py") + file2.write( + """ +import math + +import pandas as pd +""" + ) + + main.main([str(file1), str(file2), "--verbose", "--only-modified"]) + out, error = capsys.readouterr() + + assert ( + out + == f""" + _ _ + (_) ___ ___ _ __| |_ + | |/ _/ / _ \\/ '__ _/ + | |\\__ \\/\\_\\/| | | |_ + |_|\\___/\\___/\\_/ \\_/ + + isort your imports, so you don't have to. + + VERSION {__version__} + +""" + ) + + assert not error + + # ensures that verbose output is only for modified file(s) with only-modified flag + + file3 = tmpdir.join("file3.py") + file3.write( + """ +import sys +import os +""" + ) + + main.main([str(file1), str(file2), str(file3), "--verbose", "--only-modified"]) + out, error = capsys.readouterr() + + assert "else-type place_module for sys returned STDLIB" in out + assert "else-type place_module for os returned STDLIB" in out + assert "else-type place_module for math returned STDLIB" not in out + assert "else-type place_module for pandas returned THIRDPARTY" not in out + + assert not error + + # ensures that the behaviour is consistent for check flag with only-modified flag + + main.main([str(file1), str(file2), "--check-only", "--verbose", "--only-modified"]) + out, error = capsys.readouterr() + + assert ( + out + == f""" + _ _ + (_) ___ ___ _ __| |_ + | |/ _/ / _ \\/ '__ _/ + | |\\__ \\/\\_\\/| | | |_ + |_|\\___/\\___/\\_/ \\_/ + + isort your imports, so you don't have to. + + VERSION {__version__} + +""" + ) + assert not error diff --git a/tests/unit/test_parse.py b/tests/unit/test_parse.py index 98183617..0becac90 100644 --- a/tests/unit/test_parse.py +++ b/tests/unit/test_parse.py @@ -37,6 +37,7 @@ def test_file_contents(): original_line_count, _, _, + _, ) = parse.file_contents(TEST_CONTENTS, config=Config(default_section="")) assert "\n".join(in_lines) == TEST_CONTENTS assert "import" not in "\n".join(out_lines) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 5dc2fc81..75759522 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -716,6 +716,81 @@ import os ) +def test_isort_float_to_top_with_sort_on_off_tests(): + """Characterization test for current behaviour of float-to-top on isort: on/off sections. + - imports in isort:off sections stay where they are + - imports in isort:on sections float up, but to the top of the isort:on section (not the + top of the file)""" + assert ( + isort.code( + """ +def foo(): + pass + +import a + +# isort: off +import stays_in_section + +x = 1 + +import stays_in_place + +# isort: on + +def bar(): + pass + +import floats_to_top_of_section + +def baz(): + pass +""", + float_to_top=True, + ) + == """import a + + +def foo(): + pass + +# isort: off +import stays_in_section + +x = 1 + +import stays_in_place + +# isort: on +import floats_to_top_of_section + + +def bar(): + pass + + +def baz(): + pass +""" + ) + + to_sort = """# isort: off + +def foo(): + pass + +import stays_in_place +import no_float_to_to_top +import no_ordering + +def bar(): + pass +""" + + # No changes if isort is off + assert isort.code(to_sort, float_to_top=True) == to_sort + + def test_isort_doesnt_float_to_top_correctly_when_imports_not_at_top_issue_1382(): """isort should float existing imports to the top, if they are currently below the top. See: https://github.com/PyCQA/isort/issues/1382 |