From 3bad54f55e20ab2d943488507088e7492afd3670 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Thu, 3 Sep 2020 14:27:36 +0530 Subject: [PATCH 001/539] added only_sections in _build_arg_parser --- isort/main.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/isort/main.py b/isort/main.py index f4328a8b6..6df337ba0 100644 --- a/isort/main.py +++ b/isort/main.py @@ -727,6 +727,15 @@ def _build_arg_parser() -> argparse.ArgumentParser: help=argparse.SUPPRESS, ) + parser.add_argument( + "--only-sections", + "--os", + dest="only_sections", + action="store_true", + help="Causes imports to be sorted only based on their sections like STDLIB,THIRDPARTY etc. " + "Imports are unaltered and keep their relative positions within the different sections.", + ) + return parser From 76a9977f071ffd5f5151684902e1e0874b53ac45 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Thu, 3 Sep 2020 14:29:40 +0530 Subject: [PATCH 002/539] added logic to bypass sorting within sections and from_imports when only_sections options is true --- isort/output.py | 81 +++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/isort/output.py b/isort/output.py index 8cf915f95..015252085 100644 --- a/isort/output.py +++ b/isort/output.py @@ -49,16 +49,19 @@ def sorted_imports( pending_lines_before = False for section in sections: straight_modules = parsed.imports[section]["straight"] - straight_modules = sorting.naturally( - straight_modules, - key=lambda key: sorting.module_key( - key, config, section_name=section, straight_import=True - ), - ) + if not config.only_sections: + straight_modules = sorting.naturally( + straight_modules, + key=lambda key: sorting.module_key( + key, config, section_name=section, straight_import=True + ), + ) + from_modules = parsed.imports[section]["from"] - from_modules = sorting.naturally( - from_modules, key=lambda key: sorting.module_key(key, config, section_name=section) - ) + if not config.only_sections: + from_modules = sorting.naturally( + from_modules, key=lambda key: sorting.module_key(key, config, section_name=section) + ) straight_imports = _with_straight_imports( parsed, config, straight_modules, section, remove_imports, import_type @@ -89,7 +92,7 @@ def sorted_imports( comments_above = [] else: new_section_output.append(line) - + # only_sections options is not imposed if force_sort_within_sections is True new_section_output = sorting.naturally( new_section_output, key=partial( @@ -226,16 +229,14 @@ def _with_from_imports( config.force_single_line and module not in config.single_line_exclusions ): ignore_case = config.force_alphabetical_sort_within_sections - from_imports = sorting.naturally( - from_imports, - key=lambda key: sorting.module_key( - key, - config, - True, - ignore_case, - section_name=section, - ), - ) + + if not config.only_sections: + from_imports = sorting.naturally( + from_imports, + key=lambda key: sorting.module_key( + key, config, True, ignore_case, section_name=section, + ), + ) if remove_imports: from_imports = [ line for line in from_imports if f"{module}.{line}" not in remove_imports @@ -252,7 +253,8 @@ def _with_from_imports( if config.combine_as_imports and not ("*" in from_imports and config.combine_star): if not config.no_inline_sort: for as_import in as_imports: - as_imports[as_import] = sorting.naturally(as_imports[as_import]) + if not config.only_sections: + as_imports[as_import] = sorting.naturally(as_imports[as_import]) for from_import in copy.copy(from_imports): if from_import in as_imports: idx = from_imports.index(from_import) @@ -312,22 +314,41 @@ def _with_from_imports( from_comments = parsed.categorized_comments["straight"].get( f"{module}.{from_import}" ) - output.extend( - with_comments( - from_comments, - wrap.line(import_start + as_import, parsed.line_separator, config), - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, + + if not config.only_sections: + output.extend( + with_comments( + from_comments, + wrap.line( + import_start + as_import, parsed.line_separator, config + ), + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + for as_import in sorting.naturally(as_imports[from_import]) + ) + + else: + output.extend( + with_comments( + from_comments, + wrap.line( + import_start + as_import, parsed.line_separator, config + ), + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + for as_import in as_imports[from_import] ) - for as_import in sorting.naturally(as_imports[from_import]) - ) else: output.append(wrap.line(single_import_line, parsed.line_separator, config)) comments = None else: while from_imports and from_imports[0] in as_imports: from_import = from_imports.pop(0) - as_imports[from_import] = sorting.naturally(as_imports[from_import]) + + if not config.only_sections: + as_imports[from_import] = sorting.naturally(as_imports[from_import]) from_comments = ( parsed.categorized_comments["straight"].get(f"{module}.{from_import}") or [] ) From 55499157ee5192339017236358097fe099127993 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Thu, 3 Sep 2020 14:30:41 +0530 Subject: [PATCH 003/539] added only_sections option in data schema _Config --- isort/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/isort/settings.py b/isort/settings.py index 1e10ab60e..a94a17a3d 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -189,6 +189,7 @@ class _Config: classes: FrozenSet[str] = frozenset() variables: FrozenSet[str] = frozenset() dedup_headings: bool = False + only_sections: bool = False def __post_init__(self): py_version = self.py_version From 3b7c3e2ca046b183661a70dd712c2c2f9eaef19e Mon Sep 17 00:00:00 2001 From: anirudnits Date: Thu, 3 Sep 2020 14:31:35 +0530 Subject: [PATCH 004/539] added tests for only_sections option --- tests/unit/test_isort.py | 52 ++++++++++++++++++++++++++++++++-------- tests/unit/test_main.py | 2 ++ 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index dc7f5303e..ffbebf8e8 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -2940,15 +2940,12 @@ def test_not_splitted_sections() -> None: ) # in case when THIRDPARTY section is excluded from sections list, # it's ok to merge STDLIB and FIRSTPARTY - assert ( - isort.code( - code=test_input, - sections=["STDLIB", "FIRSTPARTY", "LOCALFOLDER"], - no_lines_before=["FIRSTPARTY"], - known_first_party=["app"], - ) - == (stdlib_section + firstparty_section + whiteline + local_section + whiteline + statement) - ) + assert isort.code( + code=test_input, + sections=["STDLIB", "FIRSTPARTY", "LOCALFOLDER"], + no_lines_before=["FIRSTPARTY"], + known_first_party=["app"], + ) == (stdlib_section + firstparty_section + whiteline + local_section + whiteline + statement) # it doesn't change output, because stdlib packages don't have any whitelines before them assert ( isort.code(test_input, no_lines_before=["STDLIB"], known_first_party=["app"]) == test_input @@ -3178,11 +3175,12 @@ def test_monkey_patched_urllib() -> None: def test_argument_parsing() -> None: from isort.main import parse_args - args = parse_args(["--dt", "-t", "foo", "--skip=bar", "baz.py"]) + args = parse_args(["--dt", "-t", "foo", "--skip=bar", "baz.py", "--os"]) assert args["order_by_type"] is False assert args["force_to_top"] == ["foo"] assert args["skip"] == ["bar"] assert args["files"] == ["baz.py"] + assert args["only_sections"] is False @pytest.mark.parametrize("multiprocess", (False, True)) @@ -4802,3 +4800,37 @@ def test_deprecated_settings(): """Test to ensure isort warns when deprecated settings are used, but doesn't fail to run""" with pytest.warns(UserWarning): assert isort.code("hi", not_skip=True) + + +def test_only_sections() -> None: + # test to ensure that the within sections relative position of imports are maintained + test_input = ( + "import sys\n" + "\n" + "import numpy as np\n" + "\n" + "import os\n" + "\n" + "import pandas as pd\n" + "\n" + "import math\n" + "import .views\n" + "from collections import defaultdict\n" + ) + + assert isort.code(test_input, only_sections=True) == ( + "import sys\n" + "import os\n" + "import math\n" + "from collections import defaultdict\n" + "\n" + "import numpy as np\n" + "import pandas as pd\n" + "\n" + "import .views\n" + ) + + # test to ensure that from_imports remain intact with only_sections + test_input = "from foo import b, a, c" + + assert isort.code(test_input, only_sections=True) == test_input diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 0d2738710..6514f7b50 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -41,6 +41,8 @@ def test_parse_args(): assert main.parse_args(["--multi-line", "GRID"]) == {"multi_line_output": WrapModes.GRID} assert main.parse_args(["--dont-order-by-type"]) == {"order_by_type": False} 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} def test_ascii_art(capsys): From b231c20e4012158b40c71a62523ffb5e30079f5e Mon Sep 17 00:00:00 2001 From: anirudnits Date: Thu, 3 Sep 2020 14:52:59 +0530 Subject: [PATCH 005/539] added tests for only_sections options --- tests/unit/test_isort.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index ffbebf8e8..d14bdda24 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -3180,7 +3180,7 @@ def test_argument_parsing() -> None: assert args["force_to_top"] == ["foo"] assert args["skip"] == ["bar"] assert args["files"] == ["baz.py"] - assert args["only_sections"] is False + assert args["only_sections"] is True @pytest.mark.parametrize("multiprocess", (False, True)) @@ -4831,6 +4831,6 @@ def test_only_sections() -> None: ) # test to ensure that from_imports remain intact with only_sections - test_input = "from foo import b, a, c" + test_input = "from foo import b, a, c\n" assert isort.code(test_input, only_sections=True) == test_input From 81e18d33a5704c0eb8cae7005bdf0bcb9f06a9bf Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 3 Sep 2020 03:16:39 -0700 Subject: [PATCH 006/539] Stash current progress --- isort/place.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/isort/place.py b/isort/place.py index 34b2eeb86..8e3e880fb 100644 --- a/isort/place.py +++ b/isort/place.py @@ -61,7 +61,39 @@ def _known_pattern(name: str, config: Config) -> Optional[Tuple[str, str]]: def _src_path(name: str, config: Config) -> Optional[Tuple[str, str]]: + search_paths = zip(config.src_paths, config.src_paths) + for index, module_part in enumerate(name.split(".")): + if index == len(module_parts): + for search_path, src_path in search_paths: + module_path = (search_path / module_part).resolve() + if ( + _is_module(module_path) + or _is_package(module_path) + or _src_path_is_module(search_path, module_part) + ): + return (sections.FIRSTPARTY, f"Found in one of the configured src_paths: {src_path}.") + else: + new_search_paths = [] + for search_path, src_path in search_paths: + if + + search_paths = [search_path for search_path in search_paths if + _is_package((src_path / root_module_name).resolve()) or + index == 0 and _src_path_is_module + + for src_path in config.src_paths: + root_module_name = name.split(".")[0] + module_path = + if ( + or + or _src_path_is_module(src_path, root_module_name) + ): + return (sections.FIRSTPARTY, f"Found in one of the configured src_paths: {src_path}.") + + for src_path in config.src_paths: + + for part in root_module_name = name.split(".")[0] module_path = (src_path / root_module_name).resolve() if ( From aaad03bc584074e010939020fa868e789aef70ef Mon Sep 17 00:00:00 2001 From: anirudnits Date: Thu, 3 Sep 2020 21:36:56 +0530 Subject: [PATCH 007/539] added the logic for bypassing sorting of imports within sections when only_sections is True --- isort/output.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/isort/output.py b/isort/output.py index 015252085..d7a14009a 100644 --- a/isort/output.py +++ b/isort/output.py @@ -234,7 +234,11 @@ def _with_from_imports( from_imports = sorting.naturally( from_imports, key=lambda key: sorting.module_key( - key, config, True, ignore_case, section_name=section, + key, + config, + True, + ignore_case, + section_name=section, ), ) if remove_imports: From 8cf525cff8a99d4c2a7ca6a2c8afcb49e8658c58 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Thu, 3 Sep 2020 21:37:31 +0530 Subject: [PATCH 008/539] added tests for only_section --- tests/unit/test_isort.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index d14bdda24..0eb0c322d 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -2940,12 +2940,15 @@ def test_not_splitted_sections() -> None: ) # in case when THIRDPARTY section is excluded from sections list, # it's ok to merge STDLIB and FIRSTPARTY - assert isort.code( - code=test_input, - sections=["STDLIB", "FIRSTPARTY", "LOCALFOLDER"], - no_lines_before=["FIRSTPARTY"], - known_first_party=["app"], - ) == (stdlib_section + firstparty_section + whiteline + local_section + whiteline + statement) + assert ( + isort.code( + code=test_input, + sections=["STDLIB", "FIRSTPARTY", "LOCALFOLDER"], + no_lines_before=["FIRSTPARTY"], + known_first_party=["app"], + ) + == (stdlib_section + firstparty_section + whiteline + local_section + whiteline + statement) + ) # it doesn't change output, because stdlib packages don't have any whitelines before them assert ( isort.code(test_input, no_lines_before=["STDLIB"], known_first_party=["app"]) == test_input From 80cc94e3de50f5dbbf0352890f832aa6dd41fa36 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 5 Sep 2020 04:34:31 -0700 Subject: [PATCH 009/539] Fixed #1463: Better interactive documentation for future option. --- CHANGELOG.md | 3 +++ isort/main.py | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c504a2fb..2bb54d3cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). +### 5.6.0 TBD + - Fixed #1463: Better interactive documentation for future option. + ### 5.5.1 September 4, 2020 - Fixed #1454: Ensure indented import sections with import heading and a preceding comment don't cause import sorting loops. - Fixed #1453: isort error when float to top on almost empty file. diff --git a/isort/main.py b/isort/main.py index e7dc61ad5..3d9283173 100644 --- a/isort/main.py +++ b/isort/main.py @@ -267,7 +267,12 @@ def _build_arg_parser() -> argparse.ArgumentParser: "--future", dest="known_future_library", action="append", - help="Force isort to recognize a module as part of the future compatibility libraries.", + help="Force isort to recognize a module as part of Python's internal future compatibility " + "libraries. WARNING: this overrides the behavior of __future__ handling and therefore" + " can result in code that can't execute. If you're looking to add dependencies such " + "as six a better option is to create a another section below --future using custom " + "sections. See: https://github.com/PyCQA/isort#custom-sections-and-ordering and the " + "discussion here: https://github.com/PyCQA/isort/issues/1463.", ) parser.add_argument( "--fas", From 8971fe4fc2e4344b6658acf81aacad8547be49b6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 5 Sep 2020 06:03:05 -0700 Subject: [PATCH 010/539] Reachieve 100% unit test coverage --- tests/unit/test_isort.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 0eb0c322d..d86156ba8 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -4806,13 +4806,14 @@ def test_deprecated_settings(): def test_only_sections() -> None: - # test to ensure that the within sections relative position of imports are maintained + """Test to ensure that the within sections relative position of imports are maintained""" test_input = ( "import sys\n" "\n" "import numpy as np\n" "\n" "import os\n" + "from os import path as ospath\n" "\n" "import pandas as pd\n" "\n" @@ -4821,16 +4822,21 @@ def test_only_sections() -> None: "from collections import defaultdict\n" ) - assert isort.code(test_input, only_sections=True) == ( - "import sys\n" - "import os\n" - "import math\n" - "from collections import defaultdict\n" - "\n" - "import numpy as np\n" - "import pandas as pd\n" - "\n" - "import .views\n" + assert ( + isort.code(test_input, only_sections=True) + == ( + "import sys\n" + "import os\n" + "import math\n" + "from os import path as ospath\n" + "from collections import defaultdict\n" + "\n" + "import numpy as np\n" + "import pandas as pd\n" + "\n" + "import .views\n" + ) + == isort.code(test_input, only_sections=True, force_single_line=True) ) # test to ensure that from_imports remain intact with only_sections From 5c2362bc4c873aaa6f9e5aa5cd96ee12bc704e2d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 5 Sep 2020 06:05:04 -0700 Subject: [PATCH 011/539] Fixed #1461: Quiet config option not respected by file API in some circumstances. --- CHANGELOG.md | 1 + isort/api.py | 3 +- tests/unit/test_ticketed_features.py | 62 ++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bb54d3cb..53b30a0e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.6.0 TBD - Fixed #1463: Better interactive documentation for future option. + - Fixed #1461: Quiet config option not respected by file API in some circumstances. ### 5.5.1 September 4, 2020 - Fixed #1454: Ensure indented import sections with import heading and a preceding comment don't cause import sorting loops. diff --git a/isort/api.py b/isort/api.py index cbcc3e6ea..f59bc6d91 100644 --- a/isort/api.py +++ b/isort/api.py @@ -299,6 +299,7 @@ def sort_file( """ with io.File.read(filename) as source_file: actual_file_path = file_path or source_file.path + config = _config(path=actual_file_path, config=config, **config_kwargs) changed: bool = False try: if write_to_stdout: @@ -309,7 +310,6 @@ def sort_file( file_path=actual_file_path, disregard_skip=disregard_skip, extension=extension, - **config_kwargs, ) else: tmp_file = source_file.path.with_suffix(source_file.path.suffix + ".isorted") @@ -325,7 +325,6 @@ def sort_file( file_path=actual_file_path, disregard_skip=disregard_skip, extension=extension, - **config_kwargs, ) if changed: if show_diff or ask_to_apply: diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index dc5c77814..2fd9073eb 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -657,3 +657,65 @@ def test_isort_intelligently_places_noqa_comments_issue_1456(): profile="black", show_diff=True, ) + + +def test_isort_respects_quiet_from_sort_file_api_see_1461(capsys, tmpdir): + """Test to ensure isort respects the quiet API parameter when passed in via the API. + See: https://github.com/PyCQA/isort/issues/1461. + """ + settings_file = tmpdir.join(".isort.cfg") + custom_settings_file = tmpdir.join(".custom.isort.cfg") + tmp_file = tmpdir.join("file.py") + tmp_file.write("import b\nimport a\n") + isort.file(tmp_file) + + out, error = capsys.readouterr() + assert not error + assert "Fixing" in out + + # When passed in directly as a setting override + tmp_file.write("import b\nimport a\n") + isort.file(tmp_file, quiet=True) + out, error = capsys.readouterr() + assert not error + assert not out + + # Present in an automatically loaded configuration file + isort.settings._find_config.cache_clear() + settings_file.write( + """ +[isort] +quiet = true +""" + ) + tmp_file.write("import b\nimport a\n") + isort.file(tmp_file) + out, error = capsys.readouterr() + assert not error + assert not out + + # In a custom configuration file + settings_file.write( + """ +[isort] +quiet = false +""" + ) + custom_settings_file.write( + """ +[isort] +quiet = true +""" + ) + tmp_file.write("import b\nimport a\n") + isort.file(tmp_file, settings_file=str(custom_settings_file)) + out, error = capsys.readouterr() + assert not error + assert not out + + # Reused configuration object + custom_config = Config(settings_file=str(custom_settings_file)) + isort.file(tmp_file, config=custom_config) + out, error = capsys.readouterr() + assert not error + assert not out From 28872fafb0a2d47a9ab3b308e0df04786319b314 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 5 Sep 2020 07:05:31 -0700 Subject: [PATCH 012/539] Implemented #1433: Provide helpful feedback in case a custom config file is specified without a configuration. --- CHANGELOG.md | 1 + docs/configuration/config_files.md | 9 +++++++++ isort/settings.py | 8 ++++++++ tests/unit/test_ticketed_features.py | 27 +++++++++++++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53b30a0e3..bcd90378a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). ### 5.6.0 TBD + - Implemented #1433: Provide helpful feedback in case a custom config file is specified without a configuration. - Fixed #1463: Better interactive documentation for future option. - Fixed #1461: Quiet config option not respected by file API in some circumstances. diff --git a/docs/configuration/config_files.md b/docs/configuration/config_files.md index a3d983c2b..8ca265ae3 100644 --- a/docs/configuration/config_files.md +++ b/docs/configuration/config_files.md @@ -75,3 +75,12 @@ indent_size = 4 skip = build,.tox,venv src_paths=isort,test ``` + +## Custom config files + +Optionally, you can also create a config file with a custom name, or directly point isort to a config file that falls lower in the priority order, by using [--settings-file](https://pycqa.github.io/isort/docs/configuration/options/#settings-path). +This can be useful, for instance, if you want to have one configuration for `.py` files and another for `.pyx` - while keeping the config files at the root of your repository. + +!!! tip + Custom config files should place their configuration options inside an `[isort]` section and never a generic `[settings]` section. This is because isort can't know for sure + how other tools are utilizing the config file. diff --git a/isort/settings.py b/isort/settings.py index c9fd69173..1a0a1fc14 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -269,6 +269,14 @@ def __init__( CONFIG_SECTIONS.get(os.path.basename(settings_file), FALLBACK_CONFIG_SECTIONS), ) project_root = os.path.dirname(settings_file) + if not config_settings: + warn( + f"A custom settings file was specified: {settings_file} but no configuration " + "was found inside. This can happen when [settings] is used as the config " + "header instead of [isort]. " + "See: https://pycqa.github.io/isort/docs/configuration/config_files" + "/#custom_config_files for more information." + ) elif settings_path: if not os.path.exists(settings_path): raise InvalidSettingsPath(settings_path) diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 2fd9073eb..75b43da89 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -719,3 +719,30 @@ def test_isort_respects_quiet_from_sort_file_api_see_1461(capsys, tmpdir): out, error = capsys.readouterr() assert not error assert not out + + +def test_isort_should_warn_on_empty_custom_config_issue_1433(tmpdir): + """Feedback should be provided when a user provides a custom settings file that has no + discoverable configuration. + See: https://github.com/PyCQA/isort/issues/1433 + """ + settings_file = tmpdir.join(".custom.cfg") + settings_file.write( + """ +[settings] +quiet = true +""" + ) + with pytest.warns(UserWarning): + assert not Config(settings_file=str(settings_file)).quiet + + isort.settings._get_config_data.cache_clear() + settings_file.write( + """ +[isort] +quiet = true +""" + ) + with pytest.warns(None) as warning: + assert Config(settings_file=str(settings_file)).quiet + assert not warning From a8d23a3c91e679499e556c939491e2b200cd268b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 5 Sep 2020 07:17:48 -0700 Subject: [PATCH 013/539] Resolve #1384: add documentation for how relative config file search is done --- docs/configuration/config_files.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/configuration/config_files.md b/docs/configuration/config_files.md index 8ca265ae3..7cda9cd35 100644 --- a/docs/configuration/config_files.md +++ b/docs/configuration/config_files.md @@ -1,11 +1,13 @@ Supported Config Files ======== -isort supports a variety of standard config formats, to allow customizations to easily be integrated into any project. +isort supports various standard config formats to allow customizations to be integrated into any project quickly. When applying configurations, isort looks for the closest supported config file, in the order files are listed below. -You can manually specify the settings file or path by setting `--settings-path` from the commandline, otherwise isort will +You can manually specify the settings file or path by setting `--settings-path` from the command-line. Otherwise, isort will traverse up to 25 parent directories until it finds a suitable config file. -As soon as it finds a file, it stops looking. isort **never** merges config files together due to the confusion it can cause. +As soon as it finds a file, it stops looking. The config file search is done relative to the current directory if `isort .` +or a file stream is passed in, or relative to the first path passed in if multiple paths are passed in. +isort **never** merges config files together due to the confusion it can cause. !!! tip You can always introspect the configuration settings isort determined, and find out which config file it picked up, by running `isort . --show-config` From d842244c5365403882af64fc6f20b7d76b16cde8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 7 Sep 2020 20:40:12 -0700 Subject: [PATCH 014/539] Fix deep source link #1449 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6bd080430..0e1f3ce93 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ [![Downloads](https://pepy.tech/badge/isort)](https://pepy.tech/project/isort) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) -[![DeepSource](https://static.deepsource.io/deepsource-badge-light-mini.svg)](https://deepsource.io/gh/timothycrosley/isort/?ref=repository-badge) +[![DeepSource](https://static.deepsource.io/deepsource-badge-light-mini.svg)](https://deepsource.io/gh/pycqa/isort/?ref=repository-badge) _________________ [Read Latest Documentation](https://pycqa.github.io/isort/) - [Browse GitHub Code Repository](https://github.com/pycqa/isort/) From 14a7e372f555da37973fb2e4578f6a7861297313 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 8 Sep 2020 23:26:09 -0700 Subject: [PATCH 015/539] Attempt to fix #1466: Include given example config, ensure deadline setting is turned off --- .isort.cfg | 2 +- tests/integration/test_setting_combinations.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.isort.cfg b/.isort.cfg index 567d1abd6..60ece5b8a 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,4 +1,4 @@ [settings] profile=hug -src_paths=isort,test +src_paths= skip=tests/unit/example_crlf_file.py diff --git a/tests/integration/test_setting_combinations.py b/tests/integration/test_setting_combinations.py index 7e236f9f9..ed693cd19 100644 --- a/tests/integration/test_setting_combinations.py +++ b/tests/integration/test_setting_combinations.py @@ -153,10 +153,15 @@ def _raise(*a): ] +@hypothesis.example( + config=isort.Config(py_version='all', force_to_top=frozenset(), skip=frozenset({'.svn', '.venv', 'build', 'dist', '.bzr', '.tox', '.hg', '.mypy_cache', '.nox', '_build', 'buck-out', 'node_modules', '.git', '.eggs', '.pants.d', 'venv', '.direnv'}), skip_glob=frozenset(), skip_gitignore=True, line_length=79, wrap_length=0, line_ending='', sections=('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), no_sections=False, known_future_library=frozenset({'__future__'}), known_third_party=frozenset(), known_first_party=frozenset(), known_local_folder=frozenset(), known_standard_library=frozenset({'pwd', 'types', 'nntplib', 'jpeg', 'pyclbr', 'encodings', 'ctypes', 'macerrors', 'filecmp', 'dbm', 'mimetypes', 'statvfs', 'msvcrt', 'spwd', 'codecs', 'SimpleHTTPServer', 'compiler', 'pickletools', 'tkinter', 'pickle', 'fm', 'bsddb', 'contextvars', 'dummy_thread', 'pipes', 'heapq', 'dircache', 'commands', 'unicodedata', 'ntpath', 'marshal', 'fpformat', 'linecache', 'calendar', 'pty', 'MimeWriter', 'inspect', 'mmap', 'ic', 'tty', 'nis', 'new', 'wave', 'HTMLParser', 'anydbm', 'tracemalloc', 'pdb', 'sunau', 'GL', 'parser', 'winsound', 'dbhash', 'zlib', 'MacOS', 'pprint', 'crypt', 'aetools', 'DEVICE', 'fl', 'gettext', 'asyncore', 'copyreg', 'queue', 'resource', 'turtledemo', 'fnmatch', 'hotshot', 'trace', 'string', 'plistlib', 'gzip', 'functools', 'aepack', 'hashlib', 'imp', 'MiniAEFrame', 'getpass', 'shutil', 'ttk', 'multifile', 'operator', 'reprlib', 'subprocess', 'cgi', 'select', 'SimpleXMLRPCServer', 'audioop', 'macresource', 'stringprep', 'wsgiref', 'SUNAUDIODEV', 'atexit', 'lzma', 'asyncio', 'datetime', 'binhex', 'autoGIL', 'doctest', 'thread', 'enum', 'tempfile', 'posixfile', 'mhlib', 'html', 'itertools', 'exceptions', 'sgmllib', 'array', 'test', 'imputil', 'shlex', 'flp', 'uu', 'gdbm', 'urlparse', 'msilib', 'termios', 'modulefinder', 'ossaudiodev', 'timeit', 'binascii', 'popen2', 'ConfigParser', 'poplib', 'zipfile', 'cfmfile', 'pstats', 'AL', 'contextlib', 'code', 'zipimport', 'base64', 'platform', 'ast', 'fileinput', 'locale', 'buildtools', 'stat', 'quopri', 'readline', 'collections', 'aetypes', 'concurrent', 'runpy', 'copy_reg', 'rexec', 'cmath', 'optparse', 'dummy_threading', 'ColorPicker', 'sched', 'netrc', 'sunaudiodev', 'socketserver', 'logging', 'PixMapWrapper', 'sysconfig', 'Nav', 'copy', 'cmd', 'csv', 'chunk', 'multiprocessing', 'warnings', 'weakref', 'py_compile', 'sre', 'sre_parse', 'curses', 'threading', 're', 'FrameWork', '_thread', 'imgfile', 'cd', 'sre_constants', 'xdrlib', 'dataclasses', 'urllib2', 'StringIO', 'configparser', 'importlib', 'UserList', 'posixpath', 'mailbox', 'rfc822', 'grp', 'pydoc', 'sets', 'textwrap', 'numbers', 'W', 'gl', 'htmllib', 'macostools', 'tarfile', 'ipaddress', 'xmlrpc', 'icopen', 'traceback', '_winreg', 'random', 'CGIHTTPServer', 'dis', 'sha', 'selectors', 'statistics', 'DocXMLRPCServer', 'imghdr', 'venv', 'keyword', 'xmlrpclib', 'ftplib', 'getopt', 'posix', 'smtpd', 'profile', 'sndhdr', 'signal', 'EasyDialogs', 'dumbdbm', 'fcntl', 'SocketServer', 'distutils', 'symbol', 'pathlib', 'cStringIO', 'imaplib', 'unittest', 'al', 'cProfile', 'robotparser', 'BaseHTTPServer', 'os', 'pkgutil', 'socket', 'fractions', 'shelve', 'aifc', 'cgitb', 'xml', 'decimal', 'sre_compile', 'ssl', 'user', 'Bastion', 'formatter', 'time', 'abc', 'winreg', 'difflib', 'FL', 'bz2', 'asynchat', 'gc', 'gensuitemodule', 'symtable', 'secrets', 'Carbon', 'mailcap', 'sys', 'bdb', 'fpectl', 'httplib', 'webbrowser', 'smtplib', 'Cookie', 'whichdb', 'turtle', 'tokenize', 'UserString', 'tabnanny', 'site', 'struct', 'codeop', 'email', 'typing', 'cookielib', 'Queue', 'rlcompleter', 'errno', 'macpath', 'videoreader', 'md5', 'cPickle', 'Tix', 'io', 'faulthandler', 'Tkinter', 'glob', 'syslog', 'telnetlib', '_dummy_thread', 'hmac', 'uuid', 'imageop', 'future_builtins', 'json', 'htmlentitydefs', 'lib2to3', 'UserDict', 'mutex', 'sqlite3', 'findertools', 'bisect', 'builtins', 'urllib', 'http', 'compileall', 'argparse', 'ScrolledText', 'token', 'dl', 'applesingle', 'math', 'ensurepip', 'mimify', 'mimetools', 'colorsys', 'zipapp', '__builtin__'}), extra_standard_library=frozenset(), known_other={'other': frozenset({'', '\x10\x1bm'})}, multi_line_output=0, forced_separate=(), indent=' ', comment_prefix=' #', length_sort=True, length_sort_straight=False, length_sort_sections=frozenset(), add_imports=frozenset(), remove_imports=frozenset({'', '\U00076fe7þs\x0c\U000c8b75v\U00106541', '𥒒>\U0001960euj𒎕\x9e', '\x15\x9b', '\x02l', '\U000b71ef.\x1c', '\x7f?\U000ec91c', '\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«', 'Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø', ';À¨|\x1b 𑐒🍸V'}), append_only=False, reverse_relative=True, force_single_line=False, single_line_exclusions=('Y\U000347d9g\x957K', '', 'Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.', '·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ', '', ':sI¶', ''), default_section='THIRDPARTY', import_headings={}, balanced_wrapping=False, use_parentheses=True, order_by_type=True, atomic=False, lines_after_imports=-1, lines_between_sections=1, lines_between_types=0, combine_as_imports=True, combine_star=False, include_trailing_comma=False, from_first=False, verbose=False, quiet=False, force_adds=False, force_alphabetical_sort_within_sections=False, force_alphabetical_sort=False, force_grid_wrap=0, force_sort_within_sections=False, lexicographical=False, ignore_whitespace=False, no_lines_before=frozenset({'uøø', '¢', '&\x8c5Ï\U000e5f01Ø', '\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ', '\U000e374c8', 'w'}), no_inline_sort=False, ignore_comments=False, case_sensitive=False, sources=({'py_version': 'py3', 'force_to_top': frozenset(), 'skip': frozenset({'.svn', '.venv', 'build', 'dist', '.bzr', '.tox', '.hg', '.mypy_cache', '.nox', '_build', 'buck-out', 'node_modules', '.git', '.eggs', '.pants.d', 'venv', '.direnv'}), 'skip_glob': frozenset(), 'skip_gitignore': False, 'line_length': 79, 'wrap_length': 0, 'line_ending': '', 'sections': ('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), 'no_sections': False, 'known_future_library': frozenset({'__future__'}), 'known_third_party': frozenset(), 'known_first_party': frozenset(), 'known_local_folder': frozenset(), 'known_standard_library': frozenset({'pwd', 'copy', 'cmd', 'csv', 'chunk', 'multiprocessing', 'warnings', 'types', 'weakref', 'nntplib', 'pyclbr', 'encodings', 'py_compile', 'sre', 'ctypes', 'sre_parse', 'filecmp', 'curses', 'threading', 'dbm', 're', '_thread', 'sre_constants', 'xdrlib', 'dataclasses', 'mimetypes', 'configparser', 'importlib', 'msvcrt', 'spwd', 'posixpath', 'mailbox', 'codecs', 'grp', 'pickletools', 'tkinter', 'pickle', 'contextvars', 'pydoc', 'textwrap', 'numbers', 'pipes', 'heapq', 'tarfile', 'unicodedata', 'ntpath', 'ipaddress', 'marshal', 'xmlrpc', 'traceback', 'linecache', 'calendar', 'pty', 'random', 'dis', 'selectors', 'statistics', 'imghdr', 'venv', 'inspect', 'mmap', 'keyword', 'ftplib', 'tty', 'nis', 'getopt', 'posix', 'smtpd', 'wave', 'profile', 'sndhdr', 'signal', 'tracemalloc', 'pdb', 'sunau', 'winsound', 'parser', 'zlib', 'fcntl', 'pprint', 'distutils', 'crypt', 'symbol', 'gettext', 'pathlib', 'asyncore', 'copyreg', 'imaplib', 'unittest', 'queue', 'resource', 'turtledemo', 'fnmatch', 'cProfile', 'os', 'pkgutil', 'socket', 'trace', 'fractions', 'string', 'shelve', 'plistlib', 'aifc', 'gzip', 'functools', 'cgitb', 'xml', 'hashlib', 'decimal', 'imp', 'sre_compile', 'ssl', 'formatter', 'winreg', 'time', 'getpass', 'shutil', 'abc', 'difflib', 'bz2', 'operator', 'reprlib', 'subprocess', 'cgi', 'select', 'asynchat', 'audioop', 'gc', 'secrets', 'symtable', 'mailcap', 'sys', 'bdb', 'fpectl', 'stringprep', 'webbrowser', 'smtplib', 'wsgiref', 'atexit', 'lzma', 'asyncio', 'datetime', 'binhex', 'doctest', 'turtle', 'enum', 'tempfile', 'tokenize', 'tabnanny', 'site', 'html', 'struct', 'itertools', 'codeop', 'email', 'array', 'test', 'typing', 'shlex', 'uu', 'msilib', 'termios', 'rlcompleter', 'modulefinder', 'ossaudiodev', 'timeit', 'binascii', 'poplib', 'errno', 'macpath', 'zipfile', 'io', 'faulthandler', 'pstats', 'contextlib', 'code', 'glob', 'zipimport', 'base64', 'syslog', 'platform', 'ast', 'fileinput', 'telnetlib', 'locale', '_dummy_thread', 'hmac', 'stat', 'uuid', 'quopri', 'readline', 'collections', 'json', 'concurrent', 'lib2to3', 'sqlite3', 'runpy', 'cmath', 'optparse', 'bisect', 'builtins', 'urllib', 'dummy_threading', 'http', 'compileall', 'argparse', 'token', 'sched', 'netrc', 'math', 'ensurepip', 'socketserver', 'colorsys', 'zipapp', 'logging', 'sysconfig'}), 'extra_standard_library': frozenset(), 'known_other': {}, 'multi_line_output': 0, 'forced_separate': (), 'indent': ' ', 'comment_prefix': ' #', 'length_sort': False, 'length_sort_straight': False, 'length_sort_sections': frozenset(), 'add_imports': frozenset(), 'remove_imports': frozenset(), 'append_only': False, 'reverse_relative': False, 'force_single_line': False, 'single_line_exclusions': (), 'default_section': 'THIRDPARTY', 'import_headings': {}, 'balanced_wrapping': False, 'use_parentheses': False, 'order_by_type': True, 'atomic': False, 'lines_after_imports': -1, 'lines_between_sections': 1, 'lines_between_types': 0, 'combine_as_imports': False, 'combine_star': False, 'include_trailing_comma': False, 'from_first': False, 'verbose': False, 'quiet': False, 'force_adds': False, 'force_alphabetical_sort_within_sections': False, 'force_alphabetical_sort': False, 'force_grid_wrap': 0, 'force_sort_within_sections': False, 'lexicographical': False, 'ignore_whitespace': False, 'no_lines_before': frozenset(), 'no_inline_sort': False, 'ignore_comments': False, 'case_sensitive': False, 'sources': (), 'virtual_env': '', 'conda_env': '', 'ensure_newline_before_comments': False, 'directory': '', 'profile': '', 'honor_noqa': False, 'src_paths': frozenset(), 'old_finders': False, 'remove_redundant_aliases': False, 'float_to_top': False, 'filter_files': False, 'formatter': '', 'formatting_function': None, 'color_output': False, 'treat_comments_as_code': frozenset(), 'treat_all_comments_as_code': False, 'supported_extensions': frozenset({'py', 'pyx', 'pyi'}), 'blocked_extensions': frozenset({'pex'}), 'constants': frozenset(), 'classes': frozenset(), 'variables': frozenset(), 'dedup_headings': False, 'source': 'defaults'}, {'classes': frozenset({'\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ', 'C', '\x8e\U000422ac±\U000b5a1f\U000c4166', 'ùÚ'}), 'single_line_exclusions': ('Y\U000347d9g\x957K', '', 'Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.', '·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ', '', ':sI¶', ''), 'indent': ' ', 'no_lines_before': frozenset({'uøø', '¢', '&\x8c5Ï\U000e5f01Ø', '\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ', '\U000e374c8', 'w'}), 'quiet': False, 'honor_noqa': False, 'dedup_headings': True, 'known_other': {'\x10\x1bm': frozenset({'\U000682a49\U000e1a63²KǶ4', '', '\x1a', '©'}), '': frozenset({'íå\x94Ì', '\U000cf258'})}, 'treat_comments_as_code': frozenset({''}), 'length_sort': True, 'reverse_relative': True, 'combine_as_imports': True, 'py_version': 'all', 'use_parentheses': True, 'skip_gitignore': True, 'remove_imports': frozenset({'', '\U00076fe7þs\x0c\U000c8b75v\U00106541', '𥒒>\U0001960euj𒎕\x9e', '\x15\x9b', '\x02l', '\U000b71ef.\x1c', '\x7f?\U000ec91c', '\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«', 'Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø', ';À¨|\x1b 𑐒🍸V'}), 'atomic': False, 'source': 'runtime'}), virtual_env='', conda_env='', ensure_newline_before_comments=False, directory='/home/abuild/rpmbuild/BUILD/isort-5.5.1', profile='', honor_noqa=False, old_finders=False, remove_redundant_aliases=False, float_to_top=False, filter_files=False, formatting_function=None, color_output=False, treat_comments_as_code=frozenset({''}), treat_all_comments_as_code=False, supported_extensions=frozenset({'py', 'pyx', 'pyi'}), blocked_extensions=frozenset({'pex'}), constants=frozenset(), classes=frozenset({'\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ', 'C', '\x8e\U000422ac±\U000b5a1f\U000c4166', 'ùÚ'}), variables=frozenset(), dedup_headings=True), + disregard_skip=True +) @hypothesis.given( config=st.from_type(isort.Config), disregard_skip=st.booleans(), ) +@hypothesis.settings(deadline=None) def test_isort_is_idempotent(config: isort.Config, disregard_skip: bool) -> None: try: result = isort.code(CODE_SNIPPET, config=config, disregard_skip=disregard_skip) @@ -166,10 +171,15 @@ def test_isort_is_idempotent(config: isort.Config, disregard_skip: bool) -> None pass +@hypothesis.example( + config=isort.Config(py_version='all', force_to_top=frozenset(), skip=frozenset({'.svn', '.venv', 'build', 'dist', '.bzr', '.tox', '.hg', '.mypy_cache', '.nox', '_build', 'buck-out', 'node_modules', '.git', '.eggs', '.pants.d', 'venv', '.direnv'}), skip_glob=frozenset(), skip_gitignore=True, line_length=79, wrap_length=0, line_ending='', sections=('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), no_sections=False, known_future_library=frozenset({'__future__'}), known_third_party=frozenset(), known_first_party=frozenset(), known_local_folder=frozenset(), known_standard_library=frozenset({'pwd', 'types', 'nntplib', 'jpeg', 'pyclbr', 'encodings', 'ctypes', 'macerrors', 'filecmp', 'dbm', 'mimetypes', 'statvfs', 'msvcrt', 'spwd', 'codecs', 'SimpleHTTPServer', 'compiler', 'pickletools', 'tkinter', 'pickle', 'fm', 'bsddb', 'contextvars', 'dummy_thread', 'pipes', 'heapq', 'dircache', 'commands', 'unicodedata', 'ntpath', 'marshal', 'fpformat', 'linecache', 'calendar', 'pty', 'MimeWriter', 'inspect', 'mmap', 'ic', 'tty', 'nis', 'new', 'wave', 'HTMLParser', 'anydbm', 'tracemalloc', 'pdb', 'sunau', 'GL', 'parser', 'winsound', 'dbhash', 'zlib', 'MacOS', 'pprint', 'crypt', 'aetools', 'DEVICE', 'fl', 'gettext', 'asyncore', 'copyreg', 'queue', 'resource', 'turtledemo', 'fnmatch', 'hotshot', 'trace', 'string', 'plistlib', 'gzip', 'functools', 'aepack', 'hashlib', 'imp', 'MiniAEFrame', 'getpass', 'shutil', 'ttk', 'multifile', 'operator', 'reprlib', 'subprocess', 'cgi', 'select', 'SimpleXMLRPCServer', 'audioop', 'macresource', 'stringprep', 'wsgiref', 'SUNAUDIODEV', 'atexit', 'lzma', 'asyncio', 'datetime', 'binhex', 'autoGIL', 'doctest', 'thread', 'enum', 'tempfile', 'posixfile', 'mhlib', 'html', 'itertools', 'exceptions', 'sgmllib', 'array', 'test', 'imputil', 'shlex', 'flp', 'uu', 'gdbm', 'urlparse', 'msilib', 'termios', 'modulefinder', 'ossaudiodev', 'timeit', 'binascii', 'popen2', 'ConfigParser', 'poplib', 'zipfile', 'cfmfile', 'pstats', 'AL', 'contextlib', 'code', 'zipimport', 'base64', 'platform', 'ast', 'fileinput', 'locale', 'buildtools', 'stat', 'quopri', 'readline', 'collections', 'aetypes', 'concurrent', 'runpy', 'copy_reg', 'rexec', 'cmath', 'optparse', 'dummy_threading', 'ColorPicker', 'sched', 'netrc', 'sunaudiodev', 'socketserver', 'logging', 'PixMapWrapper', 'sysconfig', 'Nav', 'copy', 'cmd', 'csv', 'chunk', 'multiprocessing', 'warnings', 'weakref', 'py_compile', 'sre', 'sre_parse', 'curses', 'threading', 're', 'FrameWork', '_thread', 'imgfile', 'cd', 'sre_constants', 'xdrlib', 'dataclasses', 'urllib2', 'StringIO', 'configparser', 'importlib', 'UserList', 'posixpath', 'mailbox', 'rfc822', 'grp', 'pydoc', 'sets', 'textwrap', 'numbers', 'W', 'gl', 'htmllib', 'macostools', 'tarfile', 'ipaddress', 'xmlrpc', 'icopen', 'traceback', '_winreg', 'random', 'CGIHTTPServer', 'dis', 'sha', 'selectors', 'statistics', 'DocXMLRPCServer', 'imghdr', 'venv', 'keyword', 'xmlrpclib', 'ftplib', 'getopt', 'posix', 'smtpd', 'profile', 'sndhdr', 'signal', 'EasyDialogs', 'dumbdbm', 'fcntl', 'SocketServer', 'distutils', 'symbol', 'pathlib', 'cStringIO', 'imaplib', 'unittest', 'al', 'cProfile', 'robotparser', 'BaseHTTPServer', 'os', 'pkgutil', 'socket', 'fractions', 'shelve', 'aifc', 'cgitb', 'xml', 'decimal', 'sre_compile', 'ssl', 'user', 'Bastion', 'formatter', 'time', 'abc', 'winreg', 'difflib', 'FL', 'bz2', 'asynchat', 'gc', 'gensuitemodule', 'symtable', 'secrets', 'Carbon', 'mailcap', 'sys', 'bdb', 'fpectl', 'httplib', 'webbrowser', 'smtplib', 'Cookie', 'whichdb', 'turtle', 'tokenize', 'UserString', 'tabnanny', 'site', 'struct', 'codeop', 'email', 'typing', 'cookielib', 'Queue', 'rlcompleter', 'errno', 'macpath', 'videoreader', 'md5', 'cPickle', 'Tix', 'io', 'faulthandler', 'Tkinter', 'glob', 'syslog', 'telnetlib', '_dummy_thread', 'hmac', 'uuid', 'imageop', 'future_builtins', 'json', 'htmlentitydefs', 'lib2to3', 'UserDict', 'mutex', 'sqlite3', 'findertools', 'bisect', 'builtins', 'urllib', 'http', 'compileall', 'argparse', 'ScrolledText', 'token', 'dl', 'applesingle', 'math', 'ensurepip', 'mimify', 'mimetools', 'colorsys', 'zipapp', '__builtin__'}), extra_standard_library=frozenset(), known_other={'other': frozenset({'', '\x10\x1bm'})}, multi_line_output=0, forced_separate=(), indent=' ', comment_prefix=' #', length_sort=True, length_sort_straight=False, length_sort_sections=frozenset(), add_imports=frozenset(), remove_imports=frozenset({'', '\U00076fe7þs\x0c\U000c8b75v\U00106541', '𥒒>\U0001960euj𒎕\x9e', '\x15\x9b', '\x02l', '\U000b71ef.\x1c', '\x7f?\U000ec91c', '\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«', 'Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø', ';À¨|\x1b 𑐒🍸V'}), append_only=False, reverse_relative=True, force_single_line=False, single_line_exclusions=('Y\U000347d9g\x957K', '', 'Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.', '·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ', '', ':sI¶', ''), default_section='THIRDPARTY', import_headings={}, balanced_wrapping=False, use_parentheses=True, order_by_type=True, atomic=False, lines_after_imports=-1, lines_between_sections=1, lines_between_types=0, combine_as_imports=True, combine_star=False, include_trailing_comma=False, from_first=False, verbose=False, quiet=False, force_adds=False, force_alphabetical_sort_within_sections=False, force_alphabetical_sort=False, force_grid_wrap=0, force_sort_within_sections=False, lexicographical=False, ignore_whitespace=False, no_lines_before=frozenset({'uøø', '¢', '&\x8c5Ï\U000e5f01Ø', '\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ', '\U000e374c8', 'w'}), no_inline_sort=False, ignore_comments=False, case_sensitive=False, sources=({'py_version': 'py3', 'force_to_top': frozenset(), 'skip': frozenset({'.svn', '.venv', 'build', 'dist', '.bzr', '.tox', '.hg', '.mypy_cache', '.nox', '_build', 'buck-out', 'node_modules', '.git', '.eggs', '.pants.d', 'venv', '.direnv'}), 'skip_glob': frozenset(), 'skip_gitignore': False, 'line_length': 79, 'wrap_length': 0, 'line_ending': '', 'sections': ('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), 'no_sections': False, 'known_future_library': frozenset({'__future__'}), 'known_third_party': frozenset(), 'known_first_party': frozenset(), 'known_local_folder': frozenset(), 'known_standard_library': frozenset({'pwd', 'copy', 'cmd', 'csv', 'chunk', 'multiprocessing', 'warnings', 'types', 'weakref', 'nntplib', 'pyclbr', 'encodings', 'py_compile', 'sre', 'ctypes', 'sre_parse', 'filecmp', 'curses', 'threading', 'dbm', 're', '_thread', 'sre_constants', 'xdrlib', 'dataclasses', 'mimetypes', 'configparser', 'importlib', 'msvcrt', 'spwd', 'posixpath', 'mailbox', 'codecs', 'grp', 'pickletools', 'tkinter', 'pickle', 'contextvars', 'pydoc', 'textwrap', 'numbers', 'pipes', 'heapq', 'tarfile', 'unicodedata', 'ntpath', 'ipaddress', 'marshal', 'xmlrpc', 'traceback', 'linecache', 'calendar', 'pty', 'random', 'dis', 'selectors', 'statistics', 'imghdr', 'venv', 'inspect', 'mmap', 'keyword', 'ftplib', 'tty', 'nis', 'getopt', 'posix', 'smtpd', 'wave', 'profile', 'sndhdr', 'signal', 'tracemalloc', 'pdb', 'sunau', 'winsound', 'parser', 'zlib', 'fcntl', 'pprint', 'distutils', 'crypt', 'symbol', 'gettext', 'pathlib', 'asyncore', 'copyreg', 'imaplib', 'unittest', 'queue', 'resource', 'turtledemo', 'fnmatch', 'cProfile', 'os', 'pkgutil', 'socket', 'trace', 'fractions', 'string', 'shelve', 'plistlib', 'aifc', 'gzip', 'functools', 'cgitb', 'xml', 'hashlib', 'decimal', 'imp', 'sre_compile', 'ssl', 'formatter', 'winreg', 'time', 'getpass', 'shutil', 'abc', 'difflib', 'bz2', 'operator', 'reprlib', 'subprocess', 'cgi', 'select', 'asynchat', 'audioop', 'gc', 'secrets', 'symtable', 'mailcap', 'sys', 'bdb', 'fpectl', 'stringprep', 'webbrowser', 'smtplib', 'wsgiref', 'atexit', 'lzma', 'asyncio', 'datetime', 'binhex', 'doctest', 'turtle', 'enum', 'tempfile', 'tokenize', 'tabnanny', 'site', 'html', 'struct', 'itertools', 'codeop', 'email', 'array', 'test', 'typing', 'shlex', 'uu', 'msilib', 'termios', 'rlcompleter', 'modulefinder', 'ossaudiodev', 'timeit', 'binascii', 'poplib', 'errno', 'macpath', 'zipfile', 'io', 'faulthandler', 'pstats', 'contextlib', 'code', 'glob', 'zipimport', 'base64', 'syslog', 'platform', 'ast', 'fileinput', 'telnetlib', 'locale', '_dummy_thread', 'hmac', 'stat', 'uuid', 'quopri', 'readline', 'collections', 'json', 'concurrent', 'lib2to3', 'sqlite3', 'runpy', 'cmath', 'optparse', 'bisect', 'builtins', 'urllib', 'dummy_threading', 'http', 'compileall', 'argparse', 'token', 'sched', 'netrc', 'math', 'ensurepip', 'socketserver', 'colorsys', 'zipapp', 'logging', 'sysconfig'}), 'extra_standard_library': frozenset(), 'known_other': {}, 'multi_line_output': 0, 'forced_separate': (), 'indent': ' ', 'comment_prefix': ' #', 'length_sort': False, 'length_sort_straight': False, 'length_sort_sections': frozenset(), 'add_imports': frozenset(), 'remove_imports': frozenset(), 'append_only': False, 'reverse_relative': False, 'force_single_line': False, 'single_line_exclusions': (), 'default_section': 'THIRDPARTY', 'import_headings': {}, 'balanced_wrapping': False, 'use_parentheses': False, 'order_by_type': True, 'atomic': False, 'lines_after_imports': -1, 'lines_between_sections': 1, 'lines_between_types': 0, 'combine_as_imports': False, 'combine_star': False, 'include_trailing_comma': False, 'from_first': False, 'verbose': False, 'quiet': False, 'force_adds': False, 'force_alphabetical_sort_within_sections': False, 'force_alphabetical_sort': False, 'force_grid_wrap': 0, 'force_sort_within_sections': False, 'lexicographical': False, 'ignore_whitespace': False, 'no_lines_before': frozenset(), 'no_inline_sort': False, 'ignore_comments': False, 'case_sensitive': False, 'sources': (), 'virtual_env': '', 'conda_env': '', 'ensure_newline_before_comments': False, 'directory': '', 'profile': '', 'honor_noqa': False, 'src_paths': frozenset(), 'old_finders': False, 'remove_redundant_aliases': False, 'float_to_top': False, 'filter_files': False, 'formatter': '', 'formatting_function': None, 'color_output': False, 'treat_comments_as_code': frozenset(), 'treat_all_comments_as_code': False, 'supported_extensions': frozenset({'py', 'pyx', 'pyi'}), 'blocked_extensions': frozenset({'pex'}), 'constants': frozenset(), 'classes': frozenset(), 'variables': frozenset(), 'dedup_headings': False, 'source': 'defaults'}, {'classes': frozenset({'\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ', 'C', '\x8e\U000422ac±\U000b5a1f\U000c4166', 'ùÚ'}), 'single_line_exclusions': ('Y\U000347d9g\x957K', '', 'Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.', '·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ', '', ':sI¶', ''), 'indent': ' ', 'no_lines_before': frozenset({'uøø', '¢', '&\x8c5Ï\U000e5f01Ø', '\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ', '\U000e374c8', 'w'}), 'quiet': False, 'honor_noqa': False, 'dedup_headings': True, 'known_other': {'\x10\x1bm': frozenset({'\U000682a49\U000e1a63²KǶ4', '', '\x1a', '©'}), '': frozenset({'íå\x94Ì', '\U000cf258'})}, 'treat_comments_as_code': frozenset({''}), 'length_sort': True, 'reverse_relative': True, 'combine_as_imports': True, 'py_version': 'all', 'use_parentheses': True, 'skip_gitignore': True, 'remove_imports': frozenset({'', '\U00076fe7þs\x0c\U000c8b75v\U00106541', '𥒒>\U0001960euj𒎕\x9e', '\x15\x9b', '\x02l', '\U000b71ef.\x1c', '\x7f?\U000ec91c', '\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«', 'Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø', ';À¨|\x1b 𑐒🍸V'}), 'atomic': False, 'source': 'runtime'}), virtual_env='', conda_env='', ensure_newline_before_comments=False, directory='/home/abuild/rpmbuild/BUILD/isort-5.5.1', profile='', honor_noqa=False, old_finders=False, remove_redundant_aliases=False, float_to_top=False, filter_files=False, formatting_function=None, color_output=False, treat_comments_as_code=frozenset({''}), treat_all_comments_as_code=False, supported_extensions=frozenset({'py', 'pyx', 'pyi'}), blocked_extensions=frozenset({'pex'}), constants=frozenset(), classes=frozenset({'\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ', 'C', '\x8e\U000422ac±\U000b5a1f\U000c4166', 'ùÚ'}), variables=frozenset(), dedup_headings=True), + disregard_skip=True +) @hypothesis.given( config=st.from_type(isort.Config), disregard_skip=st.booleans(), ) +@hypothesis.settings(deadline=None) def test_isort_doesnt_lose_imports_or_comments(config: isort.Config, disregard_skip: bool) -> None: result = isort.code(CODE_SNIPPET, config=config, disregard_skip=disregard_skip) for should_be_retained in SHOULD_RETAIN: From 8405eb743467a084fbb0a340770583311437d2b4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 8 Sep 2020 23:27:00 -0700 Subject: [PATCH 016/539] Restore src_paths --- .isort.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.isort.cfg b/.isort.cfg index 60ece5b8a..567d1abd6 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -1,4 +1,4 @@ [settings] profile=hug -src_paths= +src_paths=isort,test skip=tests/unit/example_crlf_file.py From 7a550f7bd8a2ab00a2b5db893709b84eea23cdc0 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 8 Sep 2020 23:32:54 -0700 Subject: [PATCH 017/539] Black formatting --- .../integration/test_setting_combinations.py | 1680 ++++++++++++++++- 1 file changed, 1676 insertions(+), 4 deletions(-) diff --git a/tests/integration/test_setting_combinations.py b/tests/integration/test_setting_combinations.py index ed693cd19..929b877a9 100644 --- a/tests/integration/test_setting_combinations.py +++ b/tests/integration/test_setting_combinations.py @@ -154,8 +154,844 @@ def _raise(*a): @hypothesis.example( - config=isort.Config(py_version='all', force_to_top=frozenset(), skip=frozenset({'.svn', '.venv', 'build', 'dist', '.bzr', '.tox', '.hg', '.mypy_cache', '.nox', '_build', 'buck-out', 'node_modules', '.git', '.eggs', '.pants.d', 'venv', '.direnv'}), skip_glob=frozenset(), skip_gitignore=True, line_length=79, wrap_length=0, line_ending='', sections=('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), no_sections=False, known_future_library=frozenset({'__future__'}), known_third_party=frozenset(), known_first_party=frozenset(), known_local_folder=frozenset(), known_standard_library=frozenset({'pwd', 'types', 'nntplib', 'jpeg', 'pyclbr', 'encodings', 'ctypes', 'macerrors', 'filecmp', 'dbm', 'mimetypes', 'statvfs', 'msvcrt', 'spwd', 'codecs', 'SimpleHTTPServer', 'compiler', 'pickletools', 'tkinter', 'pickle', 'fm', 'bsddb', 'contextvars', 'dummy_thread', 'pipes', 'heapq', 'dircache', 'commands', 'unicodedata', 'ntpath', 'marshal', 'fpformat', 'linecache', 'calendar', 'pty', 'MimeWriter', 'inspect', 'mmap', 'ic', 'tty', 'nis', 'new', 'wave', 'HTMLParser', 'anydbm', 'tracemalloc', 'pdb', 'sunau', 'GL', 'parser', 'winsound', 'dbhash', 'zlib', 'MacOS', 'pprint', 'crypt', 'aetools', 'DEVICE', 'fl', 'gettext', 'asyncore', 'copyreg', 'queue', 'resource', 'turtledemo', 'fnmatch', 'hotshot', 'trace', 'string', 'plistlib', 'gzip', 'functools', 'aepack', 'hashlib', 'imp', 'MiniAEFrame', 'getpass', 'shutil', 'ttk', 'multifile', 'operator', 'reprlib', 'subprocess', 'cgi', 'select', 'SimpleXMLRPCServer', 'audioop', 'macresource', 'stringprep', 'wsgiref', 'SUNAUDIODEV', 'atexit', 'lzma', 'asyncio', 'datetime', 'binhex', 'autoGIL', 'doctest', 'thread', 'enum', 'tempfile', 'posixfile', 'mhlib', 'html', 'itertools', 'exceptions', 'sgmllib', 'array', 'test', 'imputil', 'shlex', 'flp', 'uu', 'gdbm', 'urlparse', 'msilib', 'termios', 'modulefinder', 'ossaudiodev', 'timeit', 'binascii', 'popen2', 'ConfigParser', 'poplib', 'zipfile', 'cfmfile', 'pstats', 'AL', 'contextlib', 'code', 'zipimport', 'base64', 'platform', 'ast', 'fileinput', 'locale', 'buildtools', 'stat', 'quopri', 'readline', 'collections', 'aetypes', 'concurrent', 'runpy', 'copy_reg', 'rexec', 'cmath', 'optparse', 'dummy_threading', 'ColorPicker', 'sched', 'netrc', 'sunaudiodev', 'socketserver', 'logging', 'PixMapWrapper', 'sysconfig', 'Nav', 'copy', 'cmd', 'csv', 'chunk', 'multiprocessing', 'warnings', 'weakref', 'py_compile', 'sre', 'sre_parse', 'curses', 'threading', 're', 'FrameWork', '_thread', 'imgfile', 'cd', 'sre_constants', 'xdrlib', 'dataclasses', 'urllib2', 'StringIO', 'configparser', 'importlib', 'UserList', 'posixpath', 'mailbox', 'rfc822', 'grp', 'pydoc', 'sets', 'textwrap', 'numbers', 'W', 'gl', 'htmllib', 'macostools', 'tarfile', 'ipaddress', 'xmlrpc', 'icopen', 'traceback', '_winreg', 'random', 'CGIHTTPServer', 'dis', 'sha', 'selectors', 'statistics', 'DocXMLRPCServer', 'imghdr', 'venv', 'keyword', 'xmlrpclib', 'ftplib', 'getopt', 'posix', 'smtpd', 'profile', 'sndhdr', 'signal', 'EasyDialogs', 'dumbdbm', 'fcntl', 'SocketServer', 'distutils', 'symbol', 'pathlib', 'cStringIO', 'imaplib', 'unittest', 'al', 'cProfile', 'robotparser', 'BaseHTTPServer', 'os', 'pkgutil', 'socket', 'fractions', 'shelve', 'aifc', 'cgitb', 'xml', 'decimal', 'sre_compile', 'ssl', 'user', 'Bastion', 'formatter', 'time', 'abc', 'winreg', 'difflib', 'FL', 'bz2', 'asynchat', 'gc', 'gensuitemodule', 'symtable', 'secrets', 'Carbon', 'mailcap', 'sys', 'bdb', 'fpectl', 'httplib', 'webbrowser', 'smtplib', 'Cookie', 'whichdb', 'turtle', 'tokenize', 'UserString', 'tabnanny', 'site', 'struct', 'codeop', 'email', 'typing', 'cookielib', 'Queue', 'rlcompleter', 'errno', 'macpath', 'videoreader', 'md5', 'cPickle', 'Tix', 'io', 'faulthandler', 'Tkinter', 'glob', 'syslog', 'telnetlib', '_dummy_thread', 'hmac', 'uuid', 'imageop', 'future_builtins', 'json', 'htmlentitydefs', 'lib2to3', 'UserDict', 'mutex', 'sqlite3', 'findertools', 'bisect', 'builtins', 'urllib', 'http', 'compileall', 'argparse', 'ScrolledText', 'token', 'dl', 'applesingle', 'math', 'ensurepip', 'mimify', 'mimetools', 'colorsys', 'zipapp', '__builtin__'}), extra_standard_library=frozenset(), known_other={'other': frozenset({'', '\x10\x1bm'})}, multi_line_output=0, forced_separate=(), indent=' ', comment_prefix=' #', length_sort=True, length_sort_straight=False, length_sort_sections=frozenset(), add_imports=frozenset(), remove_imports=frozenset({'', '\U00076fe7þs\x0c\U000c8b75v\U00106541', '𥒒>\U0001960euj𒎕\x9e', '\x15\x9b', '\x02l', '\U000b71ef.\x1c', '\x7f?\U000ec91c', '\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«', 'Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø', ';À¨|\x1b 𑐒🍸V'}), append_only=False, reverse_relative=True, force_single_line=False, single_line_exclusions=('Y\U000347d9g\x957K', '', 'Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.', '·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ', '', ':sI¶', ''), default_section='THIRDPARTY', import_headings={}, balanced_wrapping=False, use_parentheses=True, order_by_type=True, atomic=False, lines_after_imports=-1, lines_between_sections=1, lines_between_types=0, combine_as_imports=True, combine_star=False, include_trailing_comma=False, from_first=False, verbose=False, quiet=False, force_adds=False, force_alphabetical_sort_within_sections=False, force_alphabetical_sort=False, force_grid_wrap=0, force_sort_within_sections=False, lexicographical=False, ignore_whitespace=False, no_lines_before=frozenset({'uøø', '¢', '&\x8c5Ï\U000e5f01Ø', '\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ', '\U000e374c8', 'w'}), no_inline_sort=False, ignore_comments=False, case_sensitive=False, sources=({'py_version': 'py3', 'force_to_top': frozenset(), 'skip': frozenset({'.svn', '.venv', 'build', 'dist', '.bzr', '.tox', '.hg', '.mypy_cache', '.nox', '_build', 'buck-out', 'node_modules', '.git', '.eggs', '.pants.d', 'venv', '.direnv'}), 'skip_glob': frozenset(), 'skip_gitignore': False, 'line_length': 79, 'wrap_length': 0, 'line_ending': '', 'sections': ('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), 'no_sections': False, 'known_future_library': frozenset({'__future__'}), 'known_third_party': frozenset(), 'known_first_party': frozenset(), 'known_local_folder': frozenset(), 'known_standard_library': frozenset({'pwd', 'copy', 'cmd', 'csv', 'chunk', 'multiprocessing', 'warnings', 'types', 'weakref', 'nntplib', 'pyclbr', 'encodings', 'py_compile', 'sre', 'ctypes', 'sre_parse', 'filecmp', 'curses', 'threading', 'dbm', 're', '_thread', 'sre_constants', 'xdrlib', 'dataclasses', 'mimetypes', 'configparser', 'importlib', 'msvcrt', 'spwd', 'posixpath', 'mailbox', 'codecs', 'grp', 'pickletools', 'tkinter', 'pickle', 'contextvars', 'pydoc', 'textwrap', 'numbers', 'pipes', 'heapq', 'tarfile', 'unicodedata', 'ntpath', 'ipaddress', 'marshal', 'xmlrpc', 'traceback', 'linecache', 'calendar', 'pty', 'random', 'dis', 'selectors', 'statistics', 'imghdr', 'venv', 'inspect', 'mmap', 'keyword', 'ftplib', 'tty', 'nis', 'getopt', 'posix', 'smtpd', 'wave', 'profile', 'sndhdr', 'signal', 'tracemalloc', 'pdb', 'sunau', 'winsound', 'parser', 'zlib', 'fcntl', 'pprint', 'distutils', 'crypt', 'symbol', 'gettext', 'pathlib', 'asyncore', 'copyreg', 'imaplib', 'unittest', 'queue', 'resource', 'turtledemo', 'fnmatch', 'cProfile', 'os', 'pkgutil', 'socket', 'trace', 'fractions', 'string', 'shelve', 'plistlib', 'aifc', 'gzip', 'functools', 'cgitb', 'xml', 'hashlib', 'decimal', 'imp', 'sre_compile', 'ssl', 'formatter', 'winreg', 'time', 'getpass', 'shutil', 'abc', 'difflib', 'bz2', 'operator', 'reprlib', 'subprocess', 'cgi', 'select', 'asynchat', 'audioop', 'gc', 'secrets', 'symtable', 'mailcap', 'sys', 'bdb', 'fpectl', 'stringprep', 'webbrowser', 'smtplib', 'wsgiref', 'atexit', 'lzma', 'asyncio', 'datetime', 'binhex', 'doctest', 'turtle', 'enum', 'tempfile', 'tokenize', 'tabnanny', 'site', 'html', 'struct', 'itertools', 'codeop', 'email', 'array', 'test', 'typing', 'shlex', 'uu', 'msilib', 'termios', 'rlcompleter', 'modulefinder', 'ossaudiodev', 'timeit', 'binascii', 'poplib', 'errno', 'macpath', 'zipfile', 'io', 'faulthandler', 'pstats', 'contextlib', 'code', 'glob', 'zipimport', 'base64', 'syslog', 'platform', 'ast', 'fileinput', 'telnetlib', 'locale', '_dummy_thread', 'hmac', 'stat', 'uuid', 'quopri', 'readline', 'collections', 'json', 'concurrent', 'lib2to3', 'sqlite3', 'runpy', 'cmath', 'optparse', 'bisect', 'builtins', 'urllib', 'dummy_threading', 'http', 'compileall', 'argparse', 'token', 'sched', 'netrc', 'math', 'ensurepip', 'socketserver', 'colorsys', 'zipapp', 'logging', 'sysconfig'}), 'extra_standard_library': frozenset(), 'known_other': {}, 'multi_line_output': 0, 'forced_separate': (), 'indent': ' ', 'comment_prefix': ' #', 'length_sort': False, 'length_sort_straight': False, 'length_sort_sections': frozenset(), 'add_imports': frozenset(), 'remove_imports': frozenset(), 'append_only': False, 'reverse_relative': False, 'force_single_line': False, 'single_line_exclusions': (), 'default_section': 'THIRDPARTY', 'import_headings': {}, 'balanced_wrapping': False, 'use_parentheses': False, 'order_by_type': True, 'atomic': False, 'lines_after_imports': -1, 'lines_between_sections': 1, 'lines_between_types': 0, 'combine_as_imports': False, 'combine_star': False, 'include_trailing_comma': False, 'from_first': False, 'verbose': False, 'quiet': False, 'force_adds': False, 'force_alphabetical_sort_within_sections': False, 'force_alphabetical_sort': False, 'force_grid_wrap': 0, 'force_sort_within_sections': False, 'lexicographical': False, 'ignore_whitespace': False, 'no_lines_before': frozenset(), 'no_inline_sort': False, 'ignore_comments': False, 'case_sensitive': False, 'sources': (), 'virtual_env': '', 'conda_env': '', 'ensure_newline_before_comments': False, 'directory': '', 'profile': '', 'honor_noqa': False, 'src_paths': frozenset(), 'old_finders': False, 'remove_redundant_aliases': False, 'float_to_top': False, 'filter_files': False, 'formatter': '', 'formatting_function': None, 'color_output': False, 'treat_comments_as_code': frozenset(), 'treat_all_comments_as_code': False, 'supported_extensions': frozenset({'py', 'pyx', 'pyi'}), 'blocked_extensions': frozenset({'pex'}), 'constants': frozenset(), 'classes': frozenset(), 'variables': frozenset(), 'dedup_headings': False, 'source': 'defaults'}, {'classes': frozenset({'\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ', 'C', '\x8e\U000422ac±\U000b5a1f\U000c4166', 'ùÚ'}), 'single_line_exclusions': ('Y\U000347d9g\x957K', '', 'Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.', '·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ', '', ':sI¶', ''), 'indent': ' ', 'no_lines_before': frozenset({'uøø', '¢', '&\x8c5Ï\U000e5f01Ø', '\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ', '\U000e374c8', 'w'}), 'quiet': False, 'honor_noqa': False, 'dedup_headings': True, 'known_other': {'\x10\x1bm': frozenset({'\U000682a49\U000e1a63²KǶ4', '', '\x1a', '©'}), '': frozenset({'íå\x94Ì', '\U000cf258'})}, 'treat_comments_as_code': frozenset({''}), 'length_sort': True, 'reverse_relative': True, 'combine_as_imports': True, 'py_version': 'all', 'use_parentheses': True, 'skip_gitignore': True, 'remove_imports': frozenset({'', '\U00076fe7þs\x0c\U000c8b75v\U00106541', '𥒒>\U0001960euj𒎕\x9e', '\x15\x9b', '\x02l', '\U000b71ef.\x1c', '\x7f?\U000ec91c', '\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«', 'Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø', ';À¨|\x1b 𑐒🍸V'}), 'atomic': False, 'source': 'runtime'}), virtual_env='', conda_env='', ensure_newline_before_comments=False, directory='/home/abuild/rpmbuild/BUILD/isort-5.5.1', profile='', honor_noqa=False, old_finders=False, remove_redundant_aliases=False, float_to_top=False, filter_files=False, formatting_function=None, color_output=False, treat_comments_as_code=frozenset({''}), treat_all_comments_as_code=False, supported_extensions=frozenset({'py', 'pyx', 'pyi'}), blocked_extensions=frozenset({'pex'}), constants=frozenset(), classes=frozenset({'\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ', 'C', '\x8e\U000422ac±\U000b5a1f\U000c4166', 'ùÚ'}), variables=frozenset(), dedup_headings=True), - disregard_skip=True + config=isort.Config( + py_version="all", + force_to_top=frozenset(), + skip=frozenset( + { + ".svn", + ".venv", + "build", + "dist", + ".bzr", + ".tox", + ".hg", + ".mypy_cache", + ".nox", + "_build", + "buck-out", + "node_modules", + ".git", + ".eggs", + ".pants.d", + "venv", + ".direnv", + } + ), + skip_glob=frozenset(), + skip_gitignore=True, + line_length=79, + wrap_length=0, + line_ending="", + sections=("FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"), + no_sections=False, + known_future_library=frozenset({"__future__"}), + known_third_party=frozenset(), + known_first_party=frozenset(), + known_local_folder=frozenset(), + known_standard_library=frozenset( + { + "pwd", + "types", + "nntplib", + "jpeg", + "pyclbr", + "encodings", + "ctypes", + "macerrors", + "filecmp", + "dbm", + "mimetypes", + "statvfs", + "msvcrt", + "spwd", + "codecs", + "SimpleHTTPServer", + "compiler", + "pickletools", + "tkinter", + "pickle", + "fm", + "bsddb", + "contextvars", + "dummy_thread", + "pipes", + "heapq", + "dircache", + "commands", + "unicodedata", + "ntpath", + "marshal", + "fpformat", + "linecache", + "calendar", + "pty", + "MimeWriter", + "inspect", + "mmap", + "ic", + "tty", + "nis", + "new", + "wave", + "HTMLParser", + "anydbm", + "tracemalloc", + "pdb", + "sunau", + "GL", + "parser", + "winsound", + "dbhash", + "zlib", + "MacOS", + "pprint", + "crypt", + "aetools", + "DEVICE", + "fl", + "gettext", + "asyncore", + "copyreg", + "queue", + "resource", + "turtledemo", + "fnmatch", + "hotshot", + "trace", + "string", + "plistlib", + "gzip", + "functools", + "aepack", + "hashlib", + "imp", + "MiniAEFrame", + "getpass", + "shutil", + "ttk", + "multifile", + "operator", + "reprlib", + "subprocess", + "cgi", + "select", + "SimpleXMLRPCServer", + "audioop", + "macresource", + "stringprep", + "wsgiref", + "SUNAUDIODEV", + "atexit", + "lzma", + "asyncio", + "datetime", + "binhex", + "autoGIL", + "doctest", + "thread", + "enum", + "tempfile", + "posixfile", + "mhlib", + "html", + "itertools", + "exceptions", + "sgmllib", + "array", + "test", + "imputil", + "shlex", + "flp", + "uu", + "gdbm", + "urlparse", + "msilib", + "termios", + "modulefinder", + "ossaudiodev", + "timeit", + "binascii", + "popen2", + "ConfigParser", + "poplib", + "zipfile", + "cfmfile", + "pstats", + "AL", + "contextlib", + "code", + "zipimport", + "base64", + "platform", + "ast", + "fileinput", + "locale", + "buildtools", + "stat", + "quopri", + "readline", + "collections", + "aetypes", + "concurrent", + "runpy", + "copy_reg", + "rexec", + "cmath", + "optparse", + "dummy_threading", + "ColorPicker", + "sched", + "netrc", + "sunaudiodev", + "socketserver", + "logging", + "PixMapWrapper", + "sysconfig", + "Nav", + "copy", + "cmd", + "csv", + "chunk", + "multiprocessing", + "warnings", + "weakref", + "py_compile", + "sre", + "sre_parse", + "curses", + "threading", + "re", + "FrameWork", + "_thread", + "imgfile", + "cd", + "sre_constants", + "xdrlib", + "dataclasses", + "urllib2", + "StringIO", + "configparser", + "importlib", + "UserList", + "posixpath", + "mailbox", + "rfc822", + "grp", + "pydoc", + "sets", + "textwrap", + "numbers", + "W", + "gl", + "htmllib", + "macostools", + "tarfile", + "ipaddress", + "xmlrpc", + "icopen", + "traceback", + "_winreg", + "random", + "CGIHTTPServer", + "dis", + "sha", + "selectors", + "statistics", + "DocXMLRPCServer", + "imghdr", + "venv", + "keyword", + "xmlrpclib", + "ftplib", + "getopt", + "posix", + "smtpd", + "profile", + "sndhdr", + "signal", + "EasyDialogs", + "dumbdbm", + "fcntl", + "SocketServer", + "distutils", + "symbol", + "pathlib", + "cStringIO", + "imaplib", + "unittest", + "al", + "cProfile", + "robotparser", + "BaseHTTPServer", + "os", + "pkgutil", + "socket", + "fractions", + "shelve", + "aifc", + "cgitb", + "xml", + "decimal", + "sre_compile", + "ssl", + "user", + "Bastion", + "formatter", + "time", + "abc", + "winreg", + "difflib", + "FL", + "bz2", + "asynchat", + "gc", + "gensuitemodule", + "symtable", + "secrets", + "Carbon", + "mailcap", + "sys", + "bdb", + "fpectl", + "httplib", + "webbrowser", + "smtplib", + "Cookie", + "whichdb", + "turtle", + "tokenize", + "UserString", + "tabnanny", + "site", + "struct", + "codeop", + "email", + "typing", + "cookielib", + "Queue", + "rlcompleter", + "errno", + "macpath", + "videoreader", + "md5", + "cPickle", + "Tix", + "io", + "faulthandler", + "Tkinter", + "glob", + "syslog", + "telnetlib", + "_dummy_thread", + "hmac", + "uuid", + "imageop", + "future_builtins", + "json", + "htmlentitydefs", + "lib2to3", + "UserDict", + "mutex", + "sqlite3", + "findertools", + "bisect", + "builtins", + "urllib", + "http", + "compileall", + "argparse", + "ScrolledText", + "token", + "dl", + "applesingle", + "math", + "ensurepip", + "mimify", + "mimetools", + "colorsys", + "zipapp", + "__builtin__", + } + ), + extra_standard_library=frozenset(), + known_other={"other": frozenset({"", "\x10\x1bm"})}, + multi_line_output=0, + forced_separate=(), + indent=" ", + comment_prefix=" #", + length_sort=True, + length_sort_straight=False, + length_sort_sections=frozenset(), + add_imports=frozenset(), + remove_imports=frozenset( + { + "", + "\U00076fe7þs\x0c\U000c8b75v\U00106541", + "𥒒>\U0001960euj𒎕\x9e", + "\x15\x9b", + "\x02l", + "\U000b71ef.\x1c", + "\x7f?\U000ec91c", + "\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«", + "Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø", + ";À¨|\x1b 𑐒🍸V", + } + ), + append_only=False, + reverse_relative=True, + force_single_line=False, + single_line_exclusions=( + "Y\U000347d9g\x957K", + "", + "Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.", + "·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ", + "", + ":sI¶", + "", + ), + default_section="THIRDPARTY", + import_headings={}, + balanced_wrapping=False, + use_parentheses=True, + order_by_type=True, + atomic=False, + lines_after_imports=-1, + lines_between_sections=1, + lines_between_types=0, + combine_as_imports=True, + combine_star=False, + include_trailing_comma=False, + from_first=False, + verbose=False, + quiet=False, + force_adds=False, + force_alphabetical_sort_within_sections=False, + force_alphabetical_sort=False, + force_grid_wrap=0, + force_sort_within_sections=False, + lexicographical=False, + ignore_whitespace=False, + no_lines_before=frozenset( + { + "uøø", + "¢", + "&\x8c5Ï\U000e5f01Ø", + "\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ", + "\U000e374c8", + "w", + } + ), + no_inline_sort=False, + ignore_comments=False, + case_sensitive=False, + sources=( + { + "py_version": "py3", + "force_to_top": frozenset(), + "skip": frozenset( + { + ".svn", + ".venv", + "build", + "dist", + ".bzr", + ".tox", + ".hg", + ".mypy_cache", + ".nox", + "_build", + "buck-out", + "node_modules", + ".git", + ".eggs", + ".pants.d", + "venv", + ".direnv", + } + ), + "skip_glob": frozenset(), + "skip_gitignore": False, + "line_length": 79, + "wrap_length": 0, + "line_ending": "", + "sections": ("FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"), + "no_sections": False, + "known_future_library": frozenset({"__future__"}), + "known_third_party": frozenset(), + "known_first_party": frozenset(), + "known_local_folder": frozenset(), + "known_standard_library": frozenset( + { + "pwd", + "copy", + "cmd", + "csv", + "chunk", + "multiprocessing", + "warnings", + "types", + "weakref", + "nntplib", + "pyclbr", + "encodings", + "py_compile", + "sre", + "ctypes", + "sre_parse", + "filecmp", + "curses", + "threading", + "dbm", + "re", + "_thread", + "sre_constants", + "xdrlib", + "dataclasses", + "mimetypes", + "configparser", + "importlib", + "msvcrt", + "spwd", + "posixpath", + "mailbox", + "codecs", + "grp", + "pickletools", + "tkinter", + "pickle", + "contextvars", + "pydoc", + "textwrap", + "numbers", + "pipes", + "heapq", + "tarfile", + "unicodedata", + "ntpath", + "ipaddress", + "marshal", + "xmlrpc", + "traceback", + "linecache", + "calendar", + "pty", + "random", + "dis", + "selectors", + "statistics", + "imghdr", + "venv", + "inspect", + "mmap", + "keyword", + "ftplib", + "tty", + "nis", + "getopt", + "posix", + "smtpd", + "wave", + "profile", + "sndhdr", + "signal", + "tracemalloc", + "pdb", + "sunau", + "winsound", + "parser", + "zlib", + "fcntl", + "pprint", + "distutils", + "crypt", + "symbol", + "gettext", + "pathlib", + "asyncore", + "copyreg", + "imaplib", + "unittest", + "queue", + "resource", + "turtledemo", + "fnmatch", + "cProfile", + "os", + "pkgutil", + "socket", + "trace", + "fractions", + "string", + "shelve", + "plistlib", + "aifc", + "gzip", + "functools", + "cgitb", + "xml", + "hashlib", + "decimal", + "imp", + "sre_compile", + "ssl", + "formatter", + "winreg", + "time", + "getpass", + "shutil", + "abc", + "difflib", + "bz2", + "operator", + "reprlib", + "subprocess", + "cgi", + "select", + "asynchat", + "audioop", + "gc", + "secrets", + "symtable", + "mailcap", + "sys", + "bdb", + "fpectl", + "stringprep", + "webbrowser", + "smtplib", + "wsgiref", + "atexit", + "lzma", + "asyncio", + "datetime", + "binhex", + "doctest", + "turtle", + "enum", + "tempfile", + "tokenize", + "tabnanny", + "site", + "html", + "struct", + "itertools", + "codeop", + "email", + "array", + "test", + "typing", + "shlex", + "uu", + "msilib", + "termios", + "rlcompleter", + "modulefinder", + "ossaudiodev", + "timeit", + "binascii", + "poplib", + "errno", + "macpath", + "zipfile", + "io", + "faulthandler", + "pstats", + "contextlib", + "code", + "glob", + "zipimport", + "base64", + "syslog", + "platform", + "ast", + "fileinput", + "telnetlib", + "locale", + "_dummy_thread", + "hmac", + "stat", + "uuid", + "quopri", + "readline", + "collections", + "json", + "concurrent", + "lib2to3", + "sqlite3", + "runpy", + "cmath", + "optparse", + "bisect", + "builtins", + "urllib", + "dummy_threading", + "http", + "compileall", + "argparse", + "token", + "sched", + "netrc", + "math", + "ensurepip", + "socketserver", + "colorsys", + "zipapp", + "logging", + "sysconfig", + } + ), + "extra_standard_library": frozenset(), + "known_other": {}, + "multi_line_output": 0, + "forced_separate": (), + "indent": " ", + "comment_prefix": " #", + "length_sort": False, + "length_sort_straight": False, + "length_sort_sections": frozenset(), + "add_imports": frozenset(), + "remove_imports": frozenset(), + "append_only": False, + "reverse_relative": False, + "force_single_line": False, + "single_line_exclusions": (), + "default_section": "THIRDPARTY", + "import_headings": {}, + "balanced_wrapping": False, + "use_parentheses": False, + "order_by_type": True, + "atomic": False, + "lines_after_imports": -1, + "lines_between_sections": 1, + "lines_between_types": 0, + "combine_as_imports": False, + "combine_star": False, + "include_trailing_comma": False, + "from_first": False, + "verbose": False, + "quiet": False, + "force_adds": False, + "force_alphabetical_sort_within_sections": False, + "force_alphabetical_sort": False, + "force_grid_wrap": 0, + "force_sort_within_sections": False, + "lexicographical": False, + "ignore_whitespace": False, + "no_lines_before": frozenset(), + "no_inline_sort": False, + "ignore_comments": False, + "case_sensitive": False, + "sources": (), + "virtual_env": "", + "conda_env": "", + "ensure_newline_before_comments": False, + "directory": "", + "profile": "", + "honor_noqa": False, + "src_paths": frozenset(), + "old_finders": False, + "remove_redundant_aliases": False, + "float_to_top": False, + "filter_files": False, + "formatter": "", + "formatting_function": None, + "color_output": False, + "treat_comments_as_code": frozenset(), + "treat_all_comments_as_code": False, + "supported_extensions": frozenset({"py", "pyx", "pyi"}), + "blocked_extensions": frozenset({"pex"}), + "constants": frozenset(), + "classes": frozenset(), + "variables": frozenset(), + "dedup_headings": False, + "source": "defaults", + }, + { + "classes": frozenset( + { + "\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ", + "C", + "\x8e\U000422ac±\U000b5a1f\U000c4166", + "ùÚ", + } + ), + "single_line_exclusions": ( + "Y\U000347d9g\x957K", + "", + "Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.", + "·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ", + "", + ":sI¶", + "", + ), + "indent": " ", + "no_lines_before": frozenset( + { + "uøø", + "¢", + "&\x8c5Ï\U000e5f01Ø", + "\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ", + "\U000e374c8", + "w", + } + ), + "quiet": False, + "honor_noqa": False, + "dedup_headings": True, + "known_other": { + "\x10\x1bm": frozenset({"\U000682a49\U000e1a63²KǶ4", "", "\x1a", "©"}), + "": frozenset({"íå\x94Ì", "\U000cf258"}), + }, + "treat_comments_as_code": frozenset({""}), + "length_sort": True, + "reverse_relative": True, + "combine_as_imports": True, + "py_version": "all", + "use_parentheses": True, + "skip_gitignore": True, + "remove_imports": frozenset( + { + "", + "\U00076fe7þs\x0c\U000c8b75v\U00106541", + "𥒒>\U0001960euj𒎕\x9e", + "\x15\x9b", + "\x02l", + "\U000b71ef.\x1c", + "\x7f?\U000ec91c", + "\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«", + "Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø", + ";À¨|\x1b 𑐒🍸V", + } + ), + "atomic": False, + "source": "runtime", + }, + ), + virtual_env="", + conda_env="", + ensure_newline_before_comments=False, + directory="/home/abuild/rpmbuild/BUILD/isort-5.5.1", + profile="", + honor_noqa=False, + old_finders=False, + remove_redundant_aliases=False, + float_to_top=False, + filter_files=False, + formatting_function=None, + color_output=False, + treat_comments_as_code=frozenset({""}), + treat_all_comments_as_code=False, + supported_extensions=frozenset({"py", "pyx", "pyi"}), + blocked_extensions=frozenset({"pex"}), + constants=frozenset(), + classes=frozenset( + {"\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ", "C", "\x8e\U000422ac±\U000b5a1f\U000c4166", "ùÚ"} + ), + variables=frozenset(), + dedup_headings=True, + ), + disregard_skip=True, ) @hypothesis.given( config=st.from_type(isort.Config), @@ -172,8 +1008,844 @@ def test_isort_is_idempotent(config: isort.Config, disregard_skip: bool) -> None @hypothesis.example( - config=isort.Config(py_version='all', force_to_top=frozenset(), skip=frozenset({'.svn', '.venv', 'build', 'dist', '.bzr', '.tox', '.hg', '.mypy_cache', '.nox', '_build', 'buck-out', 'node_modules', '.git', '.eggs', '.pants.d', 'venv', '.direnv'}), skip_glob=frozenset(), skip_gitignore=True, line_length=79, wrap_length=0, line_ending='', sections=('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), no_sections=False, known_future_library=frozenset({'__future__'}), known_third_party=frozenset(), known_first_party=frozenset(), known_local_folder=frozenset(), known_standard_library=frozenset({'pwd', 'types', 'nntplib', 'jpeg', 'pyclbr', 'encodings', 'ctypes', 'macerrors', 'filecmp', 'dbm', 'mimetypes', 'statvfs', 'msvcrt', 'spwd', 'codecs', 'SimpleHTTPServer', 'compiler', 'pickletools', 'tkinter', 'pickle', 'fm', 'bsddb', 'contextvars', 'dummy_thread', 'pipes', 'heapq', 'dircache', 'commands', 'unicodedata', 'ntpath', 'marshal', 'fpformat', 'linecache', 'calendar', 'pty', 'MimeWriter', 'inspect', 'mmap', 'ic', 'tty', 'nis', 'new', 'wave', 'HTMLParser', 'anydbm', 'tracemalloc', 'pdb', 'sunau', 'GL', 'parser', 'winsound', 'dbhash', 'zlib', 'MacOS', 'pprint', 'crypt', 'aetools', 'DEVICE', 'fl', 'gettext', 'asyncore', 'copyreg', 'queue', 'resource', 'turtledemo', 'fnmatch', 'hotshot', 'trace', 'string', 'plistlib', 'gzip', 'functools', 'aepack', 'hashlib', 'imp', 'MiniAEFrame', 'getpass', 'shutil', 'ttk', 'multifile', 'operator', 'reprlib', 'subprocess', 'cgi', 'select', 'SimpleXMLRPCServer', 'audioop', 'macresource', 'stringprep', 'wsgiref', 'SUNAUDIODEV', 'atexit', 'lzma', 'asyncio', 'datetime', 'binhex', 'autoGIL', 'doctest', 'thread', 'enum', 'tempfile', 'posixfile', 'mhlib', 'html', 'itertools', 'exceptions', 'sgmllib', 'array', 'test', 'imputil', 'shlex', 'flp', 'uu', 'gdbm', 'urlparse', 'msilib', 'termios', 'modulefinder', 'ossaudiodev', 'timeit', 'binascii', 'popen2', 'ConfigParser', 'poplib', 'zipfile', 'cfmfile', 'pstats', 'AL', 'contextlib', 'code', 'zipimport', 'base64', 'platform', 'ast', 'fileinput', 'locale', 'buildtools', 'stat', 'quopri', 'readline', 'collections', 'aetypes', 'concurrent', 'runpy', 'copy_reg', 'rexec', 'cmath', 'optparse', 'dummy_threading', 'ColorPicker', 'sched', 'netrc', 'sunaudiodev', 'socketserver', 'logging', 'PixMapWrapper', 'sysconfig', 'Nav', 'copy', 'cmd', 'csv', 'chunk', 'multiprocessing', 'warnings', 'weakref', 'py_compile', 'sre', 'sre_parse', 'curses', 'threading', 're', 'FrameWork', '_thread', 'imgfile', 'cd', 'sre_constants', 'xdrlib', 'dataclasses', 'urllib2', 'StringIO', 'configparser', 'importlib', 'UserList', 'posixpath', 'mailbox', 'rfc822', 'grp', 'pydoc', 'sets', 'textwrap', 'numbers', 'W', 'gl', 'htmllib', 'macostools', 'tarfile', 'ipaddress', 'xmlrpc', 'icopen', 'traceback', '_winreg', 'random', 'CGIHTTPServer', 'dis', 'sha', 'selectors', 'statistics', 'DocXMLRPCServer', 'imghdr', 'venv', 'keyword', 'xmlrpclib', 'ftplib', 'getopt', 'posix', 'smtpd', 'profile', 'sndhdr', 'signal', 'EasyDialogs', 'dumbdbm', 'fcntl', 'SocketServer', 'distutils', 'symbol', 'pathlib', 'cStringIO', 'imaplib', 'unittest', 'al', 'cProfile', 'robotparser', 'BaseHTTPServer', 'os', 'pkgutil', 'socket', 'fractions', 'shelve', 'aifc', 'cgitb', 'xml', 'decimal', 'sre_compile', 'ssl', 'user', 'Bastion', 'formatter', 'time', 'abc', 'winreg', 'difflib', 'FL', 'bz2', 'asynchat', 'gc', 'gensuitemodule', 'symtable', 'secrets', 'Carbon', 'mailcap', 'sys', 'bdb', 'fpectl', 'httplib', 'webbrowser', 'smtplib', 'Cookie', 'whichdb', 'turtle', 'tokenize', 'UserString', 'tabnanny', 'site', 'struct', 'codeop', 'email', 'typing', 'cookielib', 'Queue', 'rlcompleter', 'errno', 'macpath', 'videoreader', 'md5', 'cPickle', 'Tix', 'io', 'faulthandler', 'Tkinter', 'glob', 'syslog', 'telnetlib', '_dummy_thread', 'hmac', 'uuid', 'imageop', 'future_builtins', 'json', 'htmlentitydefs', 'lib2to3', 'UserDict', 'mutex', 'sqlite3', 'findertools', 'bisect', 'builtins', 'urllib', 'http', 'compileall', 'argparse', 'ScrolledText', 'token', 'dl', 'applesingle', 'math', 'ensurepip', 'mimify', 'mimetools', 'colorsys', 'zipapp', '__builtin__'}), extra_standard_library=frozenset(), known_other={'other': frozenset({'', '\x10\x1bm'})}, multi_line_output=0, forced_separate=(), indent=' ', comment_prefix=' #', length_sort=True, length_sort_straight=False, length_sort_sections=frozenset(), add_imports=frozenset(), remove_imports=frozenset({'', '\U00076fe7þs\x0c\U000c8b75v\U00106541', '𥒒>\U0001960euj𒎕\x9e', '\x15\x9b', '\x02l', '\U000b71ef.\x1c', '\x7f?\U000ec91c', '\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«', 'Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø', ';À¨|\x1b 𑐒🍸V'}), append_only=False, reverse_relative=True, force_single_line=False, single_line_exclusions=('Y\U000347d9g\x957K', '', 'Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.', '·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ', '', ':sI¶', ''), default_section='THIRDPARTY', import_headings={}, balanced_wrapping=False, use_parentheses=True, order_by_type=True, atomic=False, lines_after_imports=-1, lines_between_sections=1, lines_between_types=0, combine_as_imports=True, combine_star=False, include_trailing_comma=False, from_first=False, verbose=False, quiet=False, force_adds=False, force_alphabetical_sort_within_sections=False, force_alphabetical_sort=False, force_grid_wrap=0, force_sort_within_sections=False, lexicographical=False, ignore_whitespace=False, no_lines_before=frozenset({'uøø', '¢', '&\x8c5Ï\U000e5f01Ø', '\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ', '\U000e374c8', 'w'}), no_inline_sort=False, ignore_comments=False, case_sensitive=False, sources=({'py_version': 'py3', 'force_to_top': frozenset(), 'skip': frozenset({'.svn', '.venv', 'build', 'dist', '.bzr', '.tox', '.hg', '.mypy_cache', '.nox', '_build', 'buck-out', 'node_modules', '.git', '.eggs', '.pants.d', 'venv', '.direnv'}), 'skip_glob': frozenset(), 'skip_gitignore': False, 'line_length': 79, 'wrap_length': 0, 'line_ending': '', 'sections': ('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), 'no_sections': False, 'known_future_library': frozenset({'__future__'}), 'known_third_party': frozenset(), 'known_first_party': frozenset(), 'known_local_folder': frozenset(), 'known_standard_library': frozenset({'pwd', 'copy', 'cmd', 'csv', 'chunk', 'multiprocessing', 'warnings', 'types', 'weakref', 'nntplib', 'pyclbr', 'encodings', 'py_compile', 'sre', 'ctypes', 'sre_parse', 'filecmp', 'curses', 'threading', 'dbm', 're', '_thread', 'sre_constants', 'xdrlib', 'dataclasses', 'mimetypes', 'configparser', 'importlib', 'msvcrt', 'spwd', 'posixpath', 'mailbox', 'codecs', 'grp', 'pickletools', 'tkinter', 'pickle', 'contextvars', 'pydoc', 'textwrap', 'numbers', 'pipes', 'heapq', 'tarfile', 'unicodedata', 'ntpath', 'ipaddress', 'marshal', 'xmlrpc', 'traceback', 'linecache', 'calendar', 'pty', 'random', 'dis', 'selectors', 'statistics', 'imghdr', 'venv', 'inspect', 'mmap', 'keyword', 'ftplib', 'tty', 'nis', 'getopt', 'posix', 'smtpd', 'wave', 'profile', 'sndhdr', 'signal', 'tracemalloc', 'pdb', 'sunau', 'winsound', 'parser', 'zlib', 'fcntl', 'pprint', 'distutils', 'crypt', 'symbol', 'gettext', 'pathlib', 'asyncore', 'copyreg', 'imaplib', 'unittest', 'queue', 'resource', 'turtledemo', 'fnmatch', 'cProfile', 'os', 'pkgutil', 'socket', 'trace', 'fractions', 'string', 'shelve', 'plistlib', 'aifc', 'gzip', 'functools', 'cgitb', 'xml', 'hashlib', 'decimal', 'imp', 'sre_compile', 'ssl', 'formatter', 'winreg', 'time', 'getpass', 'shutil', 'abc', 'difflib', 'bz2', 'operator', 'reprlib', 'subprocess', 'cgi', 'select', 'asynchat', 'audioop', 'gc', 'secrets', 'symtable', 'mailcap', 'sys', 'bdb', 'fpectl', 'stringprep', 'webbrowser', 'smtplib', 'wsgiref', 'atexit', 'lzma', 'asyncio', 'datetime', 'binhex', 'doctest', 'turtle', 'enum', 'tempfile', 'tokenize', 'tabnanny', 'site', 'html', 'struct', 'itertools', 'codeop', 'email', 'array', 'test', 'typing', 'shlex', 'uu', 'msilib', 'termios', 'rlcompleter', 'modulefinder', 'ossaudiodev', 'timeit', 'binascii', 'poplib', 'errno', 'macpath', 'zipfile', 'io', 'faulthandler', 'pstats', 'contextlib', 'code', 'glob', 'zipimport', 'base64', 'syslog', 'platform', 'ast', 'fileinput', 'telnetlib', 'locale', '_dummy_thread', 'hmac', 'stat', 'uuid', 'quopri', 'readline', 'collections', 'json', 'concurrent', 'lib2to3', 'sqlite3', 'runpy', 'cmath', 'optparse', 'bisect', 'builtins', 'urllib', 'dummy_threading', 'http', 'compileall', 'argparse', 'token', 'sched', 'netrc', 'math', 'ensurepip', 'socketserver', 'colorsys', 'zipapp', 'logging', 'sysconfig'}), 'extra_standard_library': frozenset(), 'known_other': {}, 'multi_line_output': 0, 'forced_separate': (), 'indent': ' ', 'comment_prefix': ' #', 'length_sort': False, 'length_sort_straight': False, 'length_sort_sections': frozenset(), 'add_imports': frozenset(), 'remove_imports': frozenset(), 'append_only': False, 'reverse_relative': False, 'force_single_line': False, 'single_line_exclusions': (), 'default_section': 'THIRDPARTY', 'import_headings': {}, 'balanced_wrapping': False, 'use_parentheses': False, 'order_by_type': True, 'atomic': False, 'lines_after_imports': -1, 'lines_between_sections': 1, 'lines_between_types': 0, 'combine_as_imports': False, 'combine_star': False, 'include_trailing_comma': False, 'from_first': False, 'verbose': False, 'quiet': False, 'force_adds': False, 'force_alphabetical_sort_within_sections': False, 'force_alphabetical_sort': False, 'force_grid_wrap': 0, 'force_sort_within_sections': False, 'lexicographical': False, 'ignore_whitespace': False, 'no_lines_before': frozenset(), 'no_inline_sort': False, 'ignore_comments': False, 'case_sensitive': False, 'sources': (), 'virtual_env': '', 'conda_env': '', 'ensure_newline_before_comments': False, 'directory': '', 'profile': '', 'honor_noqa': False, 'src_paths': frozenset(), 'old_finders': False, 'remove_redundant_aliases': False, 'float_to_top': False, 'filter_files': False, 'formatter': '', 'formatting_function': None, 'color_output': False, 'treat_comments_as_code': frozenset(), 'treat_all_comments_as_code': False, 'supported_extensions': frozenset({'py', 'pyx', 'pyi'}), 'blocked_extensions': frozenset({'pex'}), 'constants': frozenset(), 'classes': frozenset(), 'variables': frozenset(), 'dedup_headings': False, 'source': 'defaults'}, {'classes': frozenset({'\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ', 'C', '\x8e\U000422ac±\U000b5a1f\U000c4166', 'ùÚ'}), 'single_line_exclusions': ('Y\U000347d9g\x957K', '', 'Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.', '·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ', '', ':sI¶', ''), 'indent': ' ', 'no_lines_before': frozenset({'uøø', '¢', '&\x8c5Ï\U000e5f01Ø', '\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ', '\U000e374c8', 'w'}), 'quiet': False, 'honor_noqa': False, 'dedup_headings': True, 'known_other': {'\x10\x1bm': frozenset({'\U000682a49\U000e1a63²KǶ4', '', '\x1a', '©'}), '': frozenset({'íå\x94Ì', '\U000cf258'})}, 'treat_comments_as_code': frozenset({''}), 'length_sort': True, 'reverse_relative': True, 'combine_as_imports': True, 'py_version': 'all', 'use_parentheses': True, 'skip_gitignore': True, 'remove_imports': frozenset({'', '\U00076fe7þs\x0c\U000c8b75v\U00106541', '𥒒>\U0001960euj𒎕\x9e', '\x15\x9b', '\x02l', '\U000b71ef.\x1c', '\x7f?\U000ec91c', '\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«', 'Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø', ';À¨|\x1b 𑐒🍸V'}), 'atomic': False, 'source': 'runtime'}), virtual_env='', conda_env='', ensure_newline_before_comments=False, directory='/home/abuild/rpmbuild/BUILD/isort-5.5.1', profile='', honor_noqa=False, old_finders=False, remove_redundant_aliases=False, float_to_top=False, filter_files=False, formatting_function=None, color_output=False, treat_comments_as_code=frozenset({''}), treat_all_comments_as_code=False, supported_extensions=frozenset({'py', 'pyx', 'pyi'}), blocked_extensions=frozenset({'pex'}), constants=frozenset(), classes=frozenset({'\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ', 'C', '\x8e\U000422ac±\U000b5a1f\U000c4166', 'ùÚ'}), variables=frozenset(), dedup_headings=True), - disregard_skip=True + config=isort.Config( + py_version="all", + force_to_top=frozenset(), + skip=frozenset( + { + ".svn", + ".venv", + "build", + "dist", + ".bzr", + ".tox", + ".hg", + ".mypy_cache", + ".nox", + "_build", + "buck-out", + "node_modules", + ".git", + ".eggs", + ".pants.d", + "venv", + ".direnv", + } + ), + skip_glob=frozenset(), + skip_gitignore=True, + line_length=79, + wrap_length=0, + line_ending="", + sections=("FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"), + no_sections=False, + known_future_library=frozenset({"__future__"}), + known_third_party=frozenset(), + known_first_party=frozenset(), + known_local_folder=frozenset(), + known_standard_library=frozenset( + { + "pwd", + "types", + "nntplib", + "jpeg", + "pyclbr", + "encodings", + "ctypes", + "macerrors", + "filecmp", + "dbm", + "mimetypes", + "statvfs", + "msvcrt", + "spwd", + "codecs", + "SimpleHTTPServer", + "compiler", + "pickletools", + "tkinter", + "pickle", + "fm", + "bsddb", + "contextvars", + "dummy_thread", + "pipes", + "heapq", + "dircache", + "commands", + "unicodedata", + "ntpath", + "marshal", + "fpformat", + "linecache", + "calendar", + "pty", + "MimeWriter", + "inspect", + "mmap", + "ic", + "tty", + "nis", + "new", + "wave", + "HTMLParser", + "anydbm", + "tracemalloc", + "pdb", + "sunau", + "GL", + "parser", + "winsound", + "dbhash", + "zlib", + "MacOS", + "pprint", + "crypt", + "aetools", + "DEVICE", + "fl", + "gettext", + "asyncore", + "copyreg", + "queue", + "resource", + "turtledemo", + "fnmatch", + "hotshot", + "trace", + "string", + "plistlib", + "gzip", + "functools", + "aepack", + "hashlib", + "imp", + "MiniAEFrame", + "getpass", + "shutil", + "ttk", + "multifile", + "operator", + "reprlib", + "subprocess", + "cgi", + "select", + "SimpleXMLRPCServer", + "audioop", + "macresource", + "stringprep", + "wsgiref", + "SUNAUDIODEV", + "atexit", + "lzma", + "asyncio", + "datetime", + "binhex", + "autoGIL", + "doctest", + "thread", + "enum", + "tempfile", + "posixfile", + "mhlib", + "html", + "itertools", + "exceptions", + "sgmllib", + "array", + "test", + "imputil", + "shlex", + "flp", + "uu", + "gdbm", + "urlparse", + "msilib", + "termios", + "modulefinder", + "ossaudiodev", + "timeit", + "binascii", + "popen2", + "ConfigParser", + "poplib", + "zipfile", + "cfmfile", + "pstats", + "AL", + "contextlib", + "code", + "zipimport", + "base64", + "platform", + "ast", + "fileinput", + "locale", + "buildtools", + "stat", + "quopri", + "readline", + "collections", + "aetypes", + "concurrent", + "runpy", + "copy_reg", + "rexec", + "cmath", + "optparse", + "dummy_threading", + "ColorPicker", + "sched", + "netrc", + "sunaudiodev", + "socketserver", + "logging", + "PixMapWrapper", + "sysconfig", + "Nav", + "copy", + "cmd", + "csv", + "chunk", + "multiprocessing", + "warnings", + "weakref", + "py_compile", + "sre", + "sre_parse", + "curses", + "threading", + "re", + "FrameWork", + "_thread", + "imgfile", + "cd", + "sre_constants", + "xdrlib", + "dataclasses", + "urllib2", + "StringIO", + "configparser", + "importlib", + "UserList", + "posixpath", + "mailbox", + "rfc822", + "grp", + "pydoc", + "sets", + "textwrap", + "numbers", + "W", + "gl", + "htmllib", + "macostools", + "tarfile", + "ipaddress", + "xmlrpc", + "icopen", + "traceback", + "_winreg", + "random", + "CGIHTTPServer", + "dis", + "sha", + "selectors", + "statistics", + "DocXMLRPCServer", + "imghdr", + "venv", + "keyword", + "xmlrpclib", + "ftplib", + "getopt", + "posix", + "smtpd", + "profile", + "sndhdr", + "signal", + "EasyDialogs", + "dumbdbm", + "fcntl", + "SocketServer", + "distutils", + "symbol", + "pathlib", + "cStringIO", + "imaplib", + "unittest", + "al", + "cProfile", + "robotparser", + "BaseHTTPServer", + "os", + "pkgutil", + "socket", + "fractions", + "shelve", + "aifc", + "cgitb", + "xml", + "decimal", + "sre_compile", + "ssl", + "user", + "Bastion", + "formatter", + "time", + "abc", + "winreg", + "difflib", + "FL", + "bz2", + "asynchat", + "gc", + "gensuitemodule", + "symtable", + "secrets", + "Carbon", + "mailcap", + "sys", + "bdb", + "fpectl", + "httplib", + "webbrowser", + "smtplib", + "Cookie", + "whichdb", + "turtle", + "tokenize", + "UserString", + "tabnanny", + "site", + "struct", + "codeop", + "email", + "typing", + "cookielib", + "Queue", + "rlcompleter", + "errno", + "macpath", + "videoreader", + "md5", + "cPickle", + "Tix", + "io", + "faulthandler", + "Tkinter", + "glob", + "syslog", + "telnetlib", + "_dummy_thread", + "hmac", + "uuid", + "imageop", + "future_builtins", + "json", + "htmlentitydefs", + "lib2to3", + "UserDict", + "mutex", + "sqlite3", + "findertools", + "bisect", + "builtins", + "urllib", + "http", + "compileall", + "argparse", + "ScrolledText", + "token", + "dl", + "applesingle", + "math", + "ensurepip", + "mimify", + "mimetools", + "colorsys", + "zipapp", + "__builtin__", + } + ), + extra_standard_library=frozenset(), + known_other={"other": frozenset({"", "\x10\x1bm"})}, + multi_line_output=0, + forced_separate=(), + indent=" ", + comment_prefix=" #", + length_sort=True, + length_sort_straight=False, + length_sort_sections=frozenset(), + add_imports=frozenset(), + remove_imports=frozenset( + { + "", + "\U00076fe7þs\x0c\U000c8b75v\U00106541", + "𥒒>\U0001960euj𒎕\x9e", + "\x15\x9b", + "\x02l", + "\U000b71ef.\x1c", + "\x7f?\U000ec91c", + "\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«", + "Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø", + ";À¨|\x1b 𑐒🍸V", + } + ), + append_only=False, + reverse_relative=True, + force_single_line=False, + single_line_exclusions=( + "Y\U000347d9g\x957K", + "", + "Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.", + "·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ", + "", + ":sI¶", + "", + ), + default_section="THIRDPARTY", + import_headings={}, + balanced_wrapping=False, + use_parentheses=True, + order_by_type=True, + atomic=False, + lines_after_imports=-1, + lines_between_sections=1, + lines_between_types=0, + combine_as_imports=True, + combine_star=False, + include_trailing_comma=False, + from_first=False, + verbose=False, + quiet=False, + force_adds=False, + force_alphabetical_sort_within_sections=False, + force_alphabetical_sort=False, + force_grid_wrap=0, + force_sort_within_sections=False, + lexicographical=False, + ignore_whitespace=False, + no_lines_before=frozenset( + { + "uøø", + "¢", + "&\x8c5Ï\U000e5f01Ø", + "\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ", + "\U000e374c8", + "w", + } + ), + no_inline_sort=False, + ignore_comments=False, + case_sensitive=False, + sources=( + { + "py_version": "py3", + "force_to_top": frozenset(), + "skip": frozenset( + { + ".svn", + ".venv", + "build", + "dist", + ".bzr", + ".tox", + ".hg", + ".mypy_cache", + ".nox", + "_build", + "buck-out", + "node_modules", + ".git", + ".eggs", + ".pants.d", + "venv", + ".direnv", + } + ), + "skip_glob": frozenset(), + "skip_gitignore": False, + "line_length": 79, + "wrap_length": 0, + "line_ending": "", + "sections": ("FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"), + "no_sections": False, + "known_future_library": frozenset({"__future__"}), + "known_third_party": frozenset(), + "known_first_party": frozenset(), + "known_local_folder": frozenset(), + "known_standard_library": frozenset( + { + "pwd", + "copy", + "cmd", + "csv", + "chunk", + "multiprocessing", + "warnings", + "types", + "weakref", + "nntplib", + "pyclbr", + "encodings", + "py_compile", + "sre", + "ctypes", + "sre_parse", + "filecmp", + "curses", + "threading", + "dbm", + "re", + "_thread", + "sre_constants", + "xdrlib", + "dataclasses", + "mimetypes", + "configparser", + "importlib", + "msvcrt", + "spwd", + "posixpath", + "mailbox", + "codecs", + "grp", + "pickletools", + "tkinter", + "pickle", + "contextvars", + "pydoc", + "textwrap", + "numbers", + "pipes", + "heapq", + "tarfile", + "unicodedata", + "ntpath", + "ipaddress", + "marshal", + "xmlrpc", + "traceback", + "linecache", + "calendar", + "pty", + "random", + "dis", + "selectors", + "statistics", + "imghdr", + "venv", + "inspect", + "mmap", + "keyword", + "ftplib", + "tty", + "nis", + "getopt", + "posix", + "smtpd", + "wave", + "profile", + "sndhdr", + "signal", + "tracemalloc", + "pdb", + "sunau", + "winsound", + "parser", + "zlib", + "fcntl", + "pprint", + "distutils", + "crypt", + "symbol", + "gettext", + "pathlib", + "asyncore", + "copyreg", + "imaplib", + "unittest", + "queue", + "resource", + "turtledemo", + "fnmatch", + "cProfile", + "os", + "pkgutil", + "socket", + "trace", + "fractions", + "string", + "shelve", + "plistlib", + "aifc", + "gzip", + "functools", + "cgitb", + "xml", + "hashlib", + "decimal", + "imp", + "sre_compile", + "ssl", + "formatter", + "winreg", + "time", + "getpass", + "shutil", + "abc", + "difflib", + "bz2", + "operator", + "reprlib", + "subprocess", + "cgi", + "select", + "asynchat", + "audioop", + "gc", + "secrets", + "symtable", + "mailcap", + "sys", + "bdb", + "fpectl", + "stringprep", + "webbrowser", + "smtplib", + "wsgiref", + "atexit", + "lzma", + "asyncio", + "datetime", + "binhex", + "doctest", + "turtle", + "enum", + "tempfile", + "tokenize", + "tabnanny", + "site", + "html", + "struct", + "itertools", + "codeop", + "email", + "array", + "test", + "typing", + "shlex", + "uu", + "msilib", + "termios", + "rlcompleter", + "modulefinder", + "ossaudiodev", + "timeit", + "binascii", + "poplib", + "errno", + "macpath", + "zipfile", + "io", + "faulthandler", + "pstats", + "contextlib", + "code", + "glob", + "zipimport", + "base64", + "syslog", + "platform", + "ast", + "fileinput", + "telnetlib", + "locale", + "_dummy_thread", + "hmac", + "stat", + "uuid", + "quopri", + "readline", + "collections", + "json", + "concurrent", + "lib2to3", + "sqlite3", + "runpy", + "cmath", + "optparse", + "bisect", + "builtins", + "urllib", + "dummy_threading", + "http", + "compileall", + "argparse", + "token", + "sched", + "netrc", + "math", + "ensurepip", + "socketserver", + "colorsys", + "zipapp", + "logging", + "sysconfig", + } + ), + "extra_standard_library": frozenset(), + "known_other": {}, + "multi_line_output": 0, + "forced_separate": (), + "indent": " ", + "comment_prefix": " #", + "length_sort": False, + "length_sort_straight": False, + "length_sort_sections": frozenset(), + "add_imports": frozenset(), + "remove_imports": frozenset(), + "append_only": False, + "reverse_relative": False, + "force_single_line": False, + "single_line_exclusions": (), + "default_section": "THIRDPARTY", + "import_headings": {}, + "balanced_wrapping": False, + "use_parentheses": False, + "order_by_type": True, + "atomic": False, + "lines_after_imports": -1, + "lines_between_sections": 1, + "lines_between_types": 0, + "combine_as_imports": False, + "combine_star": False, + "include_trailing_comma": False, + "from_first": False, + "verbose": False, + "quiet": False, + "force_adds": False, + "force_alphabetical_sort_within_sections": False, + "force_alphabetical_sort": False, + "force_grid_wrap": 0, + "force_sort_within_sections": False, + "lexicographical": False, + "ignore_whitespace": False, + "no_lines_before": frozenset(), + "no_inline_sort": False, + "ignore_comments": False, + "case_sensitive": False, + "sources": (), + "virtual_env": "", + "conda_env": "", + "ensure_newline_before_comments": False, + "directory": "", + "profile": "", + "honor_noqa": False, + "src_paths": frozenset(), + "old_finders": False, + "remove_redundant_aliases": False, + "float_to_top": False, + "filter_files": False, + "formatter": "", + "formatting_function": None, + "color_output": False, + "treat_comments_as_code": frozenset(), + "treat_all_comments_as_code": False, + "supported_extensions": frozenset({"py", "pyx", "pyi"}), + "blocked_extensions": frozenset({"pex"}), + "constants": frozenset(), + "classes": frozenset(), + "variables": frozenset(), + "dedup_headings": False, + "source": "defaults", + }, + { + "classes": frozenset( + { + "\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ", + "C", + "\x8e\U000422ac±\U000b5a1f\U000c4166", + "ùÚ", + } + ), + "single_line_exclusions": ( + "Y\U000347d9g\x957K", + "", + "Ê\U000e8ad2\U0008fa72ùÏ\x19ç\U000eaecc𤎪.", + "·o\U000d00e5\U000b36de+\x8f\U000b5953´\x08oÜ", + "", + ":sI¶", + "", + ), + "indent": " ", + "no_lines_before": frozenset( + { + "uøø", + "¢", + "&\x8c5Ï\U000e5f01Ø", + "\U0005d415\U000a3df2h\U000f24e5\U00104d7b34¹ÒÀ", + "\U000e374c8", + "w", + } + ), + "quiet": False, + "honor_noqa": False, + "dedup_headings": True, + "known_other": { + "\x10\x1bm": frozenset({"\U000682a49\U000e1a63²KǶ4", "", "\x1a", "©"}), + "": frozenset({"íå\x94Ì", "\U000cf258"}), + }, + "treat_comments_as_code": frozenset({""}), + "length_sort": True, + "reverse_relative": True, + "combine_as_imports": True, + "py_version": "all", + "use_parentheses": True, + "skip_gitignore": True, + "remove_imports": frozenset( + { + "", + "\U00076fe7þs\x0c\U000c8b75v\U00106541", + "𥒒>\U0001960euj𒎕\x9e", + "\x15\x9b", + "\x02l", + "\U000b71ef.\x1c", + "\x7f?\U000ec91c", + "\x7f,ÞoÀP8\x1b\x1e»3\x86\x94¤ÁÓ~\U00066b1a,O\U0010ab28\x90«", + "Y\x06ºZ\x04Ýì\U00078ce1.\U0010c1f9[EK\x83EÖø", + ";À¨|\x1b 𑐒🍸V", + } + ), + "atomic": False, + "source": "runtime", + }, + ), + virtual_env="", + conda_env="", + ensure_newline_before_comments=False, + directory="/home/abuild/rpmbuild/BUILD/isort-5.5.1", + profile="", + honor_noqa=False, + old_finders=False, + remove_redundant_aliases=False, + float_to_top=False, + filter_files=False, + formatting_function=None, + color_output=False, + treat_comments_as_code=frozenset({""}), + treat_all_comments_as_code=False, + supported_extensions=frozenset({"py", "pyx", "pyi"}), + blocked_extensions=frozenset({"pex"}), + constants=frozenset(), + classes=frozenset( + {"\U000eb6c6\x9eÑ\U0008297dâhï\x8eÆ", "C", "\x8e\U000422ac±\U000b5a1f\U000c4166", "ùÚ"} + ), + variables=frozenset(), + dedup_headings=True, + ), + disregard_skip=True, ) @hypothesis.given( config=st.from_type(isort.Config), From 77c6a5e34ed8f785ed264b4a16872ba5505f4509 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 8 Sep 2020 23:35:44 -0700 Subject: [PATCH 018/539] Remove code climate link; Resolve #1449 --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 0e1f3ce93..153772603 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ [![Test Status](https://github.com/pycqa/isort/workflows/Test/badge.svg?branch=develop)](https://github.com/pycqa/isort/actions?query=workflow%3ATest) [![Lint Status](https://github.com/pycqa/isort/workflows/Lint/badge.svg?branch=develop)](https://github.com/pycqa/isort/actions?query=workflow%3ALint) [![Code coverage Status](https://codecov.io/gh/pycqa/isort/branch/develop/graph/badge.svg)](https://codecov.io/gh/pycqa/isort) -[![Maintainability](https://api.codeclimate.com/v1/badges/060372d3e77573072609/maintainability)](https://codeclimate.com/github/timothycrosley/isort/maintainability) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://pypi.org/project/isort/) [![Join the chat at https://gitter.im/timothycrosley/isort](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/timothycrosley/isort?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Downloads](https://pepy.tech/badge/isort)](https://pepy.tech/project/isort) From 24efea1e6cb5247f696e072c8ff40c350b425ce0 Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 10 Sep 2020 16:30:56 +0200 Subject: [PATCH 019/539] Test Datadog integrations-core --- tests/integration/test_projects_using_isort.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index bb7d61de5..417a6c72e 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -170,3 +170,18 @@ def test_attrs(tmpdir): "_compat.py", ] ) + + +def test_datadog_integrations_core(tmpdir): + check_call( + [ + "git", + "clone", + "--depth", + "1", + "https://github.com/DataDog/integrations-core.git", + str(tmpdir), + ] + ) + + main(["--check-only", "--diff", str(tmpdir), "--skip", "tests"]) From 8feacc297cc295edfb828a3b95898cce5abe4c66 Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 10 Sep 2020 16:50:50 +0200 Subject: [PATCH 020/539] remove skip tests --- tests/integration/test_projects_using_isort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 417a6c72e..1b877fdad 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -184,4 +184,4 @@ def test_datadog_integrations_core(tmpdir): ] ) - main(["--check-only", "--diff", str(tmpdir), "--skip", "tests"]) + main(["--check-only", "--diff", str(tmpdir)]) From 95367522ca8dd7e2a1cda56ec7e86972bf024536 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 10 Sep 2020 20:19:38 -0700 Subject: [PATCH 021/539] Update precommit version --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad78369f1..586a5edda 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: - repo: https://github.com/pycqa/isort - rev: 5.3.0 + rev: 5.5.2 hooks: - id: isort From 5f81e6216c6fa5d14ee86f4e80616943620c1453 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 10 Sep 2020 22:42:21 -0700 Subject: [PATCH 022/539] Resolve #1457: Improve handling of unsupported configuration option errors --- CHANGELOG.md | 1 + isort/exceptions.py | 24 ++++++++++++++++++++++++ isort/settings.py | 20 +++++++++++++++++++- tests/unit/test_exceptions.py | 8 ++++++++ tests/unit/test_settings.py | 8 ++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 682c6a347..96503547c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.6.0 TBD - Implemented #1433: Provide helpful feedback in case a custom config file is specified without a configuration. + - Improved handling of unsupported configuration option errors (see #1475). - Fixed #1463: Better interactive documentation for future option. - Fixed #1461: Quiet config option not respected by file API in some circumstances. diff --git a/isort/exceptions.py b/isort/exceptions.py index 9f45744c7..3d152587b 100644 --- a/isort/exceptions.py +++ b/isort/exceptions.py @@ -1,4 +1,6 @@ """All isort specific exception classes should be defined here""" +from typing import Any, Dict + from .profiles import profiles @@ -132,3 +134,25 @@ def __init__(self, code: str): "...\n\n" ) self.code = code + + +class UnsupportedSettings(ISortError): + """Raised when settings are passed into isort (either from config, CLI, or runtime) + that it doesn't support. + """ + + def _format_option(self, name: str, value: Any, source: str) -> str: + return f"\t- {name} = {value} (source: '{source}')" + + def __init__(self, unsupported_settings: Dict[str, Dict[str, str]]): + errors = "\n".join( + self._format_option(name, **option) for name, option in unsupported_settings.items() + ) + + super().__init__( + "isort was provided settings that it doesn't support:\n\n" + f"{errors}\n\n" + "For a complete and up-to-date listing of supported settings see: " + "https://pycqa.github.io/isort/docs/configuration/options/.\n" + ) + self.unsupported_settings = unsupported_settings diff --git a/isort/settings.py b/isort/settings.py index 1a0a1fc14..126934c13 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -18,7 +18,12 @@ from . import stdlibs from ._future import dataclass, field from ._vendored import toml -from .exceptions import FormattingPluginDoesNotExist, InvalidSettingsPath, ProfileDoesNotExist +from .exceptions import ( + FormattingPluginDoesNotExist, + InvalidSettingsPath, + ProfileDoesNotExist, + UnsupportedSettings, +) from .profiles import profiles from .sections import DEFAULT as SECTION_DEFAULTS from .sections import FIRSTPARTY, FUTURE, LOCALFOLDER, STDLIB, THIRDPARTY @@ -432,6 +437,19 @@ def __init__( combined_config.pop(f"{IMPORT_HEADING_PREFIX}{import_heading_key}") combined_config["import_headings"] = import_headings + unsupported_config_errors = {} + for option in set(combined_config.keys()).difference( + getattr(_Config, "__dataclass_fields__", {}).keys() + ): + for source in reversed(sources): + if option in source: + unsupported_config_errors[option] = { + "value": source[option], + "source": source["source"], + } + if unsupported_config_errors: + raise UnsupportedSettings(unsupported_config_errors) + super().__init__(sources=tuple(sources), **combined_config) # type: ignore def is_supported_filetype(self, file_name: str): diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py index 8a6f60fcc..bb6ee2a31 100644 --- a/tests/unit/test_exceptions.py +++ b/tests/unit/test_exceptions.py @@ -82,3 +82,11 @@ def setup_class(self): def test_variables(self): assert self.instance.code == "print x" + + +class TestUnsupportedSettings(TestISortError): + def setup_class(self): + self.instance = exceptions.UnsupportedSettings({"apply": {"value": "true", "source": "/"}}) + + def test_variables(self): + assert self.instance.unsupported_settings == {"apply": {"value": "true", "source": "/"}} diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index fb2316181..1f6a52080 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -14,6 +14,14 @@ class TestConfig: def test_init(self): assert Config() + def test_init_unsupported_settings_fails_gracefully(self): + with pytest.raises(exceptions.UnsupportedSettings): + Config(apply=True) + try: + Config(apply=True) + except exceptions.UnsupportedSettings as error: + assert error.unsupported_settings == {"apply": {"value": True, "source": "runtime"}} + def test_known_settings(self): assert Config(known_third_party=["one"]).known_third_party == frozenset({"one"}) assert Config(known_thirdparty=["two"]).known_third_party == frozenset({"two"}) From 1e9bea3c84d24867b60751f7fb4e001285e35151 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 10 Sep 2020 22:52:13 -0700 Subject: [PATCH 023/539] Fix errors found by deepsource --- .deepsource.toml | 1 + isort/exceptions.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.deepsource.toml b/.deepsource.toml index 81eded698..cfbbec30a 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -14,3 +14,4 @@ enabled = true [analyzers.meta] runtime_version = "3.x.x" + max_line_length = 100 diff --git a/isort/exceptions.py b/isort/exceptions.py index 3d152587b..265928e98 100644 --- a/isort/exceptions.py +++ b/isort/exceptions.py @@ -141,7 +141,8 @@ class UnsupportedSettings(ISortError): that it doesn't support. """ - def _format_option(self, name: str, value: Any, source: str) -> str: + @staticmethod + def _format_option(name: str, value: Any, source: str) -> str: return f"\t- {name} = {value} (source: '{source}')" def __init__(self, unsupported_settings: Dict[str, Dict[str, str]]): From 1f64554e7e94a94734ce832ebd06b06226a4d0be Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 10 Sep 2020 23:23:00 -0700 Subject: [PATCH 024/539] Resolve #1471: Confusing documentation for force-grid-wrap --- docs/configuration/options.md | 76 +++++++++++++++++++++++++++++------ isort/main.py | 7 ++-- 2 files changed, 67 insertions(+), 16 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index 10c1cfb8a..bf2205243 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -4,7 +4,8 @@ As a code formatter isort has opinions. However, it also allows you to have your isort will disagree but commit to your way of formatting. To enable this, isort exposes a plethora of options to specify how you want your imports sorted, organized, and formatted. -Too busy to build your perfect isort configuration? For curated common configurations, see isort's [built-in profiles](https://pycqa.github.io/isort/docs/configuration/profiles/). +Too busy to build your perfect isort configuration? For curated common configurations, see isort's [built-in +profiles](https://pycqa.github.io/isort/docs/configuration/profiles/). ## Python Version @@ -35,7 +36,7 @@ Force specific imports to the top of their appropriate section. Files that sort imports should skip over. If you want to skip multiple files you should specify twice: --skip file1 --skip file2. **Type:** Frozenset -**Default:** `('.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.tox', '.venv', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv')` +**Default:** `('.bzr', '.direnv', '.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.svn', '.tox', '.venv', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv')` **Python & Config File Name:** skip **CLI Flags:** @@ -128,7 +129,7 @@ Put all imports into the same section bucket ## Known Future Library -Force isort to recognize a module as part of the future compatibility libraries. +Force isort to recognize a module as part of Python's internal future compatibility libraries. WARNING: this overrides the behavior of __future__ handling and therefore can result in code that can't execute. If you're looking to add dependencies such as six a better option is to create a another section below --future using custom sections. See: https://github.com/PyCQA/isort#custom-sections-and-ordering and the discussion here: https://github.com/PyCQA/isort/issues/1463. **Type:** Frozenset **Default:** `('__future__',)` @@ -293,14 +294,13 @@ Sort imports by their string length. - --ls - --length-sort -## Length Sort Straight Imports +## Length Sort Straight -Sort straight imports by their string length. Similar to `length_sort` but applies only to -straight imports and doesn't affect from imports. +Sort straight imports by their string length. -**Type:** Bool -**Default:** `False` -**Python & Config File Name:** length_sort_straight +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** length_sort_straight **CLI Flags:** - --lss @@ -602,7 +602,7 @@ Force all imports to be sorted as a single section ## Force Grid Wrap -Force number of from imports (defaults to 2) to be grid wrapped regardless of line length +Force number of from imports (defaults to 2 when passed as CLI flag without value) to be grid wrapped regardless of line length. If 0 is passed in (the global default) only line length is considered. **Type:** Int **Default:** `0` @@ -880,7 +880,7 @@ Tells isort to treat all single line comments as if they are code. Specifies what extensions isort can be ran against. **Type:** Frozenset -**Default:** `('.py', '.pyi', '.pyx')` +**Default:** `('py', 'pyi', 'pyx')` **Python & Config File Name:** supported_extensions **CLI Flags:** @@ -893,12 +893,62 @@ Specifies what extensions isort can be ran against. Specifies what extensions isort can never be ran against. **Type:** Frozenset -**Default:** `('.pex',)` +**Default:** `('pex',)` **Python & Config File Name:** blocked_extensions **CLI Flags:** - --blocked-extension +## Constants + +**No Description** + +**Type:** Frozenset +**Default:** `frozenset()` +**Python & Config File Name:** constants +**CLI Flags:** **Not Supported** + +## Classes + +**No Description** + +**Type:** Frozenset +**Default:** `frozenset()` +**Python & Config File Name:** classes +**CLI Flags:** **Not Supported** + +## Variables + +**No Description** + +**Type:** Frozenset +**Default:** `frozenset()` +**Python & Config File Name:** variables +**CLI Flags:** **Not Supported** + +## Dedup Headings + +Tells isort to only show an identical custom import heading comment once, even if there are multiple sections with the comment set. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** dedup_headings +**CLI Flags:** + +- --dedup-headings + +## Only Sections + +Causes imports to be sorted only based on their sections like STDLIB,THIRDPARTY etc. Imports are unaltered and keep their relative positions within the different sections. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** only_sections +**CLI Flags:** + +- --only-sections +- --os + ## Check Checks the file for unsorted / unformatted imports and prints them to the command line without modifying the file. @@ -948,7 +998,7 @@ Number of files to process in parallel. - -j - --jobs -## Don't Order By Type +## Dont Order By Type Don't order imports by type, which is determined by case, in addition to alphabetically. diff --git a/isort/main.py b/isort/main.py index c1bf35ebb..56f015837 100644 --- a/isort/main.py +++ b/isort/main.py @@ -302,8 +302,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: const=2, type=int, dest="force_grid_wrap", - help="Force number of from imports (defaults to 2) to be grid wrapped regardless of line " - "length", + help="Force number of from imports (defaults to 2 when passed as CLI flag without value) to be grid wrapped regardless of line " + "length. If 0 is passed in (the global default) only line length is considered.", ) parser.add_argument( "--fss", @@ -343,7 +343,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: parser.add_argument( "--lss", "--length-sort-straight", - help="Sort straight imports by their string length.", + help="Sort straight imports by their string length. Similar to `length_sort` " + "but applies only to straight imports and doesn't affect from imports.", dest="length_sort_straight", action="store_true", ) From b5a9b61029ad09f7a8d3c1ed02ca8c6c63fdab5b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 10 Sep 2020 23:29:39 -0700 Subject: [PATCH 025/539] Add missing contributors (Aniruddha Bhattacharjee (@anirudnits), Alexandre Yang (@AlexandreYang), and Andrew Howe (@howeaj) --- docs/contributing/4.-acknowledgements.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index db3fde562..ef8aecb56 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -194,6 +194,9 @@ Code Contributors - Guillaume Lostis (@glostis) - Krzysztof Jagiełło (@kjagiello) - Nicholas Devenish (@ndevenish) +- Aniruddha Bhattacharjee (@anirudnits) +- Alexandre Yang (@AlexandreYang) +- Andrew Howe (@howeaj) Documenters =================== From 23f5fd69ef8e9ea7149309a034feb83ecd778704 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 11 Sep 2020 00:45:38 -0700 Subject: [PATCH 026/539] Fix formatting --- isort/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index 56f015837..cd71a40a1 100644 --- a/isort/main.py +++ b/isort/main.py @@ -302,7 +302,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: const=2, type=int, dest="force_grid_wrap", - help="Force number of from imports (defaults to 2 when passed as CLI flag without value) to be grid wrapped regardless of line " + help="Force number of from imports (defaults to 2 when passed as CLI flag without value)" + "to be grid wrapped regardless of line " "length. If 0 is passed in (the global default) only line length is considered.", ) parser.add_argument( @@ -344,7 +345,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: "--lss", "--length-sort-straight", help="Sort straight imports by their string length. Similar to `length_sort` " - "but applies only to straight imports and doesn't affect from imports.", + "but applies only to straight imports and doesn't affect from imports.", dest="length_sort_straight", action="store_true", ) From b0e555fa3001c777990114b9f79015994d47157f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 12 Sep 2020 23:55:53 -0700 Subject: [PATCH 027/539] Update cruft template --- .cruft.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cruft.json b/.cruft.json index f70213bc6..dcbd6c8eb 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/timothycrosley/cookiecutter-python/", - "commit": "4fe165a760a98a06d3fbef89aae3149767e489f3", + "commit": "ff6836bfaa247c65ff50b39c520ed12d91bf5a20", "context": { "cookiecutter": { "full_name": "Timothy Crosley", @@ -13,4 +13,4 @@ } }, "directory": "" -} +} \ No newline at end of file From 7071d038feb43f3a2db19d735d7a9910ef0808fc Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sun, 13 Sep 2020 15:47:59 +0530 Subject: [PATCH 028/539] omitted the coverage for deprecated directory --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 56861b744..b9a746519 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,6 +4,7 @@ omit = isort/_vendored/* except ImportError: tests/* + isort/deprecated/* [report] exclude_lines = From 31c2d3d1b85714b115bc2a32279fc7a605cd0faf Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sun, 13 Sep 2020 15:48:33 +0530 Subject: [PATCH 029/539] removed deprecated_finders.py from test suite --- setup.cfg | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index a75e3dffd..8a59aaffd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,10 +14,7 @@ strict_optional = False [tool:pytest] testpaths = tests -filterwarnings = - ignore::DeprecationWarning:distlib - ignore::DeprecationWarning:requirementslib - ignore::hypothesis.errors.NonInteractiveExampleWarning:hypothesis + [flake8] max-line-length = 100 From 3d1ee23caaff702bf09e1615cb67be8e4a2906fd Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sun, 13 Sep 2020 15:49:45 +0530 Subject: [PATCH 030/539] moved property testing from hypothesis-auto to hypothesis using ghostwriter --- tests/unit/test_comments.py | 36 ++- tests/unit/test_format.py | 28 +- tests/unit/test_main.py | 23 +- tests/unit/test_output.py | 23 +- tests/unit/test_parse.py | 41 ++- tests/unit/test_wrap_modes.py | 520 ++++++++++++++++++++++++++++++++-- 6 files changed, 630 insertions(+), 41 deletions(-) diff --git a/tests/unit/test_comments.py b/tests/unit/test_comments.py index 4098f8ec6..31ae8a6c4 100644 --- a/tests/unit/test_comments.py +++ b/tests/unit/test_comments.py @@ -1,10 +1,34 @@ -from hypothesis_auto import auto_pytest_magic +from hypothesis import given +from hypothesis import strategies as st -from isort import comments - -auto_pytest_magic(comments.parse) -auto_pytest_magic(comments.add_to_line) +import isort.comments def test_add_to_line(): - assert comments.add_to_line([], "import os # comment", removed=True).strip() == "import os" + assert ( + isort.comments.add_to_line([], "import os # comment", removed=True).strip() == "import os" + ) + + +# These tests were written by the `hypothesis.extra.ghostwriter` module +# and is provided under the Creative Commons Zero public domain dedication. + + +@given( + comments=st.one_of(st.none(), st.lists(st.text())), + original_string=st.text(), + removed=st.booleans(), + comment_prefix=st.text(), +) +def test_fuzz_add_to_line(comments, original_string, removed, comment_prefix): + isort.comments.add_to_line( + comments=comments, + original_string=original_string, + removed=removed, + comment_prefix=comment_prefix, + ) + + +@given(line=st.text()) +def test_fuzz_parse(line): + isort.comments.parse(line=line) diff --git a/tests/unit/test_format.py b/tests/unit/test_format.py index fee61c67d..f331578a8 100644 --- a/tests/unit/test_format.py +++ b/tests/unit/test_format.py @@ -1,14 +1,14 @@ from io import StringIO +from pathlib import Path from unittest.mock import MagicMock, patch import colorama import pytest -from hypothesis_auto import auto_pytest_magic +from hypothesis import given, reject +from hypothesis import strategies as st import isort.format -auto_pytest_magic(isort.format.show_unified_diff, auto_allow_exceptions_=(UnicodeEncodeError,)) - def test_ask_whether_to_apply_changes_to_file(): with patch("isort.format.input", MagicMock(return_value="y")): @@ -97,3 +97,25 @@ def test_colorama_not_available_handled_gracefully(capsys): _, err = capsys.readouterr() assert "colorama" in err assert "colors extra" in err + + +# This test code was written by the `hypothesis.extra.ghostwriter` module +# and is provided under the Creative Commons Zero public domain dedication. + + +@given( + file_input=st.text(), + file_output=st.text(), + file_path=st.one_of(st.none(), st.builds(Path)), + output=st.one_of(st.none(), st.builds(StringIO, st.text())), +) +def test_fuzz_show_unified_diff(file_input, file_output, file_path, output): + try: + isort.format.show_unified_diff( + file_input=file_input, + file_output=file_output, + file_path=file_path, + output=output, + ) + except UnicodeEncodeError: + reject() diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index e4193e516..92faafd4b 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -4,7 +4,8 @@ from io import BytesIO, TextIOWrapper import pytest -from hypothesis_auto import auto_pytest_magic +from hypothesis import given +from hypothesis import strategies as st from isort import main from isort._version import __version__ @@ -12,7 +13,25 @@ from isort.settings import DEFAULT_CONFIG, Config from isort.wrap_modes import WrapModes -auto_pytest_magic(main.sort_imports) +# This test code was written by the `hypothesis.extra.ghostwriter` module +# and is provided under the Creative Commons Zero public domain dedication. + + +@given( + file_name=st.text(), + config=st.builds(Config), + check=st.booleans(), + ask_to_apply=st.booleans(), + write_to_stdout=st.booleans(), +) +def test_fuzz_sort_imports(file_name, config, check, ask_to_apply, write_to_stdout): + main.sort_imports( + file_name=file_name, + config=config, + check=check, + ask_to_apply=ask_to_apply, + write_to_stdout=write_to_stdout, + ) def test_iter_source_code(tmpdir): diff --git a/tests/unit/test_output.py b/tests/unit/test_output.py index 2457f70d7..b4d8ed1a4 100644 --- a/tests/unit/test_output.py +++ b/tests/unit/test_output.py @@ -1,5 +1,22 @@ -from hypothesis_auto import auto_pytest_magic +from hypothesis import given, reject +from hypothesis import strategies as st -from isort import output +import isort.comments -auto_pytest_magic(output.with_comments, auto_allow_exceptions_=(ValueError,)) + +@given( + comments=st.one_of(st.none(), st.lists(st.text())), + original_string=st.text(), + removed=st.booleans(), + comment_prefix=st.text(), +) +def test_fuzz_add_to_line(comments, original_string, removed, comment_prefix): + try: + isort.comments.add_to_line( + comments=comments, + original_string=original_string, + removed=removed, + comment_prefix=comment_prefix, + ) + except ValueError: + reject() diff --git a/tests/unit/test_parse.py b/tests/unit/test_parse.py index 685ebcf73..98183617d 100644 --- a/tests/unit/test_parse.py +++ b/tests/unit/test_parse.py @@ -1,4 +1,5 @@ -from hypothesis_auto import auto_pytest_magic +from hypothesis import given +from hypothesis import strategies as st from isort import parse from isort.settings import Config @@ -44,7 +45,37 @@ def test_file_contents(): assert original_line_count == len(in_lines) -auto_pytest_magic(parse.import_type) -auto_pytest_magic(parse.skip_line) -auto_pytest_magic(parse._strip_syntax) -auto_pytest_magic(parse._infer_line_separator) +# These tests were written by the `hypothesis.extra.ghostwriter` module +# and is provided under the Creative Commons Zero public domain dedication. + + +@given(contents=st.text()) +def test_fuzz__infer_line_separator(contents): + parse._infer_line_separator(contents=contents) + + +@given(import_string=st.text()) +def test_fuzz__strip_syntax(import_string): + parse._strip_syntax(import_string=import_string) + + +@given(line=st.text(), config=st.builds(Config)) +def test_fuzz_import_type(line, config): + parse.import_type(line=line, config=config) + + +@given( + line=st.text(), + in_quote=st.text(), + index=st.integers(), + section_comments=st.lists(st.text()).map(tuple), + needs_import=st.booleans(), +) +def test_fuzz_skip_line(line, in_quote, index, section_comments, needs_import): + parse.skip_line( + line=line, + in_quote=in_quote, + index=index, + section_comments=section_comments, + needs_import=needs_import, + ) diff --git a/tests/unit/test_wrap_modes.py b/tests/unit/test_wrap_modes.py index c95a75136..3ef01ae0c 100644 --- a/tests/unit/test_wrap_modes.py +++ b/tests/unit/test_wrap_modes.py @@ -1,29 +1,9 @@ -from hypothesis_auto import auto_pytest_magic +from hypothesis import given, reject +from hypothesis import strategies as st import isort from isort import wrap_modes -auto_pytest_magic(wrap_modes.grid, auto_allow_exceptions_=(ValueError,)) -auto_pytest_magic(wrap_modes.vertical, auto_allow_exceptions_=(ValueError,)) -auto_pytest_magic(wrap_modes.hanging_indent, auto_allow_exceptions_=(ValueError,)) -auto_pytest_magic(wrap_modes.vertical_hanging_indent, auto_allow_exceptions_=(ValueError,)) -auto_pytest_magic(wrap_modes.vertical_grid, auto_allow_exceptions_=(ValueError,)) -auto_pytest_magic(wrap_modes.vertical_grid_grouped, auto_allow_exceptions_=(ValueError,)) -auto_pytest_magic(wrap_modes.vertical_grid_grouped_no_comma, auto_allow_exceptions_=(ValueError,)) -auto_pytest_magic(wrap_modes.noqa, auto_allow_exceptions_=(ValueError,)) -auto_pytest_magic(wrap_modes.noqa, auto_allow_exceptions_=(ValueError,), comments=["NOQA"]) -auto_pytest_magic( - wrap_modes.vertical_prefix_from_module_import, auto_allow_exceptions_=(ValueError,) -) -auto_pytest_magic(wrap_modes.vertical_hanging_indent_bracket, auto_allow_exceptions_=(ValueError,)) -auto_pytest_magic( - wrap_modes.vertical_hanging_indent_bracket, - auto_allow_exceptions_=(ValueError,), - imports=["one", "two"], -) -auto_pytest_magic(wrap_modes.hanging_indent_with_parentheses, auto_allow_exceptions_=(ValueError,)) -auto_pytest_magic(wrap_modes.backslash_grid, auto_allow_exceptions_=(ValueError,)) - def test_wrap_mode_interface(): assert ( @@ -94,3 +74,499 @@ def test_backslash_grid(): handlers as handlers_, patches, resources """ ) + + +# This test code was written by the `hypothesis.extra.ghostwriter` module +# and is provided under the Creative Commons Zero public domain dedication. + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_backslash_grid( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.backslash_grid( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_grid( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.grid( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_hanging_indent( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.hanging_indent( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_hanging_indent_with_parentheses( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.hanging_indent_with_parentheses( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_noqa( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.noqa( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_vertical( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.vertical( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_vertical_grid( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.vertical_grid( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_vertical_grid_grouped( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.vertical_grid_grouped( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_vertical_grid_grouped_no_comma( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.vertical_grid_grouped_no_comma( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_vertical_hanging_indent( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.vertical_hanging_indent( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_vertical_hanging_indent_bracket( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.vertical_hanging_indent_bracket( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() + + +@given( + statement=st.text(), + imports=st.lists(st.text()), + white_space=st.text(), + indent=st.text(), + line_length=st.integers(), + comments=st.lists(st.text()), + line_separator=st.text(), + comment_prefix=st.text(), + include_trailing_comma=st.booleans(), + remove_comments=st.booleans(), +) +def test_fuzz_vertical_prefix_from_module_import( + statement, + imports, + white_space, + indent, + line_length, + comments, + line_separator, + comment_prefix, + include_trailing_comma, + remove_comments, +): + try: + isort.wrap_modes.vertical_prefix_from_module_import( + statement=statement, + imports=imports, + white_space=white_space, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=remove_comments, + ) + except ValueError: + reject() From dd8d0df389c5c211da34a333d2cdb6447a79a7c3 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sun, 13 Sep 2020 15:53:54 +0530 Subject: [PATCH 031/539] moved testing of deprecated_finders outside of test suite --- scripts/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test.sh b/scripts/test.sh index 5aa1c7648..d76d8cce4 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -2,5 +2,5 @@ set -euxo pipefail ./scripts/lint.sh -poetry run pytest tests/unit/ -s --cov=isort/ --cov-report=term-missing ${@-} +poetry run pytest tests/unit/ -s --cov=isort/ --cov-report=term-missing ${@-} --ignore=tests/unit/test_deprecated_finders.py poetry run coverage html From 59c4ca0469598e8891b495519b07cb27e726a4c2 Mon Sep 17 00:00:00 2001 From: swedge Date: Sun, 13 Sep 2020 17:14:29 -0400 Subject: [PATCH 032/539] Fixes #1479 --- tests/unit/test_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 92faafd4b..fc5938289 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -292,5 +292,5 @@ def test_main(capsys, tmpdir): def test_isort_command(): - """Ensure ISortCommand got registered, otherwise setuptools error must have occured""" + """Ensure ISortCommand got registered, otherwise setuptools error must have occurred""" assert main.ISortCommand From fc8f7841203abaf8d557ceecadc19c175138d31f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 14 Sep 2020 23:17:35 -0700 Subject: [PATCH 033/539] Fix plugin definition to match pylama expectation --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 31e126565..4b3bdff4d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,7 +86,7 @@ isort = "isort.main:main" isort = "isort.main:ISortCommand" [tool.poetry.plugins."pylama.linter"] -isort = "isort = isort.pylama_isort:Linter" +isort = "isort.pylama_isort:Linter" [tool.portray.mkdocs] edit_uri = "https://github.com/pycqa/isort/edit/develop/" From 7d6580f2eb0e142a7ff7c77e6fc1d75f2a3d71b3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 14 Sep 2020 23:19:05 -0700 Subject: [PATCH 034/539] Fix pylama integration to work with file skip comments --- isort/pylama_isort.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/isort/pylama_isort.py b/isort/pylama_isort.py index 0e14d5696..4d4ffb922 100644 --- a/isort/pylama_isort.py +++ b/isort/pylama_isort.py @@ -5,6 +5,8 @@ from pylama.lint import Linter as BaseLinter +from isort.exceptions import FileSkipped + from . import api @@ -25,9 +27,17 @@ def allow(self, path: str) -> bool: def run(self, path: str, **meta: Any) -> List[Dict[str, Any]]: """Lint the file. Return an array of error dicts if appropriate.""" with supress_stdout(): - if not api.check_file(path): - return [ - {"lnum": 0, "col": 0, "text": "Incorrectly sorted imports.", "type": "ISORT"} - ] - else: - return [] + try: + if not api.check_file(path, disregard_skip=False): + return [ + { + "lnum": 0, + "col": 0, + "text": "Incorrectly sorted imports.", + "type": "ISORT", + } + ] + except FileSkipped: + pass + + return [] From d34620af3434aeb42aa98cf17af5d9dcec18b89d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 14 Sep 2020 23:19:39 -0700 Subject: [PATCH 035/539] Update to latest cruft --- .cruft.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cruft.json b/.cruft.json index f70213bc6..dcbd6c8eb 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/timothycrosley/cookiecutter-python/", - "commit": "4fe165a760a98a06d3fbef89aae3149767e489f3", + "commit": "ff6836bfaa247c65ff50b39c520ed12d91bf5a20", "context": { "cookiecutter": { "full_name": "Timothy Crosley", @@ -13,4 +13,4 @@ } }, "directory": "" -} +} \ No newline at end of file From 48081a925d5b69e18a1f04c74cbe98b590e77c5b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 14 Sep 2020 23:26:48 -0700 Subject: [PATCH 036/539] Add a test for skip functionality --- tests/unit/test_pylama_isort.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/unit/test_pylama_isort.py b/tests/unit/test_pylama_isort.py index b7b78c138..1fe19bfa1 100644 --- a/tests/unit/test_pylama_isort.py +++ b/tests/unit/test_pylama_isort.py @@ -17,3 +17,8 @@ def test_run(self, src_dir, tmpdir): incorrect = tmpdir.join("incorrect.py") incorrect.write("import b\nimport a\n") assert self.instance.run(str(incorrect)) + + def test_skip(self, src_dir, tmpdir): + incorrect = tmpdir.join("incorrect.py") + incorrect.write("# isort: skip_file\nimport b\nimport a\n") + assert not self.instance.run(str(incorrect)) From 226f18ab0a2cfde43748aeb577e77a4bc02ba598 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 14 Sep 2020 23:27:55 -0700 Subject: [PATCH 037/539] Mark Fix #1482: pylama integration - in changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96503547c..ac76e56ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Improved handling of unsupported configuration option errors (see #1475). - Fixed #1463: Better interactive documentation for future option. - Fixed #1461: Quiet config option not respected by file API in some circumstances. + - Fixed #1482: pylama integration is not working correctly out-of-the-box. ### 5.5.2 [Hotfix] September 9, 2020 - Fixed #1469: --diff option is ignored when input is from stdin. From 0349b3685061e22835bb93335d778d2d13831b76 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Thu, 17 Sep 2020 00:25:22 +0900 Subject: [PATCH 038/539] Exit non zero if all of passed path is broken - Check that path is broken at `iter_source_code` - Only exit all passed path is broken --- isort/main.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index cd71a40a1..95e910d5d 100644 --- a/isort/main.py +++ b/isort/main.py @@ -114,7 +114,9 @@ def sort_imports( raise -def iter_source_code(paths: Iterable[str], config: Config, skipped: List[str]) -> Iterator[str]: +def iter_source_code( + paths: Iterable[str], config: Config, skipped: List[str], broken: List[str] +) -> Iterator[str]: """Iterate over all Python source files defined in paths.""" visited_dirs: Set[Path] = set() @@ -142,6 +144,8 @@ def iter_source_code(paths: Iterable[str], config: Config, skipped: List[str]) - skipped.append(filename) else: yield filepath + elif not os.path.exists(path): + broken.append(path) else: yield path @@ -837,6 +841,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = deprecated_flags = config_dict.pop("deprecated_flags", False) remapped_deprecated_args = config_dict.pop("remapped_deprecated_args", False) wrong_sorted_files = False + all_attempt_broken = False if "src_paths" in config_dict: config_dict["src_paths"] = { @@ -856,6 +861,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = ) else: skipped: List[str] = [] + broken: List[str] = [] if config.filter_files: filtered_files = [] @@ -866,8 +872,9 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = filtered_files.append(file_name) file_names = filtered_files - file_names = iter_source_code(file_names, config, skipped) + file_names = iter_source_code(file_names, config, skipped, broken) num_skipped = 0 + num_broken = 0 if config.verbose: print(ASCII_ART) @@ -899,6 +906,8 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = for file_name in file_names ) + # If any files passed in are missing considered as error, should be removed + is_no_attempt = True for sort_attempt in attempt_iterator: if not sort_attempt: continue # pragma: no cover - shouldn't happen, satisfies type constraint @@ -909,6 +918,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = num_skipped += ( 1 # pragma: no cover - shouldn't happen, due to skip in iter_source_code ) + is_no_attempt = False num_skipped += len(skipped) if num_skipped and not arguments.get("quiet", False): @@ -920,6 +930,16 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = ) print(f"Skipped {num_skipped} files") + num_broken += len(broken) + if num_broken and not arguments.get("quite", False): + if config.verbose: + for was_broken in broken: + warn(f"{was_broken} was broken path, make sure it exists correctly") + print(f"Broken {num_broken} paths") + + if num_broken > 0 and is_no_attempt: + all_attempt_broken = True + if not config.quiet and (remapped_deprecated_args or deprecated_flags): if remapped_deprecated_args: warn( @@ -939,6 +959,9 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = if wrong_sorted_files: sys.exit(1) + if all_attempt_broken: + sys.exit(1) + if __name__ == "__main__": main() From eaad31ae0b3625959aafeee7fb269b8fa659d3bd Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Thu, 17 Sep 2020 00:26:00 +0900 Subject: [PATCH 039/539] Add testcase that process broken path --- tests/unit/test_isort.py | 26 ++++++++++++++++++++++---- tests/unit/test_main.py | 11 ++++++++++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index d86156ba8..91eb1d610 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -3243,13 +3243,14 @@ def test_safety_skips(tmpdir, enabled: bool) -> None: else: config = Config(skip=[], directory=str(tmpdir)) skipped = [] # type: List[str] + broken = [] # type: List[str] codes = [str(tmpdir)] - main.iter_source_code(codes, config, skipped) + main.iter_source_code(codes, config, skipped, broken) # if enabled files within nested unsafe directories should be skipped file_names = { os.path.relpath(f, str(tmpdir)) - for f in main.iter_source_code([str(tmpdir)], config, skipped) + for f in main.iter_source_code([str(tmpdir)], config, skipped, broken) } if enabled: assert file_names == {"victim.py"} @@ -3266,7 +3267,9 @@ def test_safety_skips(tmpdir, enabled: bool) -> None: # directly pointing to files within unsafe directories shouldn't skip them either way file_names = { os.path.relpath(f, str(toxdir)) - for f in main.iter_source_code([str(toxdir)], Config(directory=str(toxdir)), skipped) + for f in main.iter_source_code( + [str(toxdir)], Config(directory=str(toxdir)), skipped, broken + ) } assert file_names == {"verysafe.py"} @@ -3287,14 +3290,29 @@ def test_skip_glob(tmpdir, skip_glob_assert: Tuple[List[str], int, Set[str]]) -> config = Config(skip_glob=skip_glob, directory=str(base_dir)) skipped = [] # type: List[str] + broken = [] # type: List[str] file_names = { os.path.relpath(f, str(base_dir)) - for f in main.iter_source_code([str(base_dir)], config, skipped) + for f in main.iter_source_code([str(base_dir)], config, skipped, broken) } assert len(skipped) == skipped_count assert file_names == file_names_expected +def test_broken(tmpdir) -> None: + base_dir = tmpdir.mkdir("broken") + + config = Config(directory=str(base_dir)) + skipped = [] # type: List[str] + broken = [] # type: List[str] + file_names = { + os.path.relpath(f, str(base_dir)) + for f in main.iter_source_code(["not-exist"], config, skipped, broken) + } + assert len(broken) == 1 + assert file_names == set() + + def test_comments_not_removed_issue_576() -> None: test_input = ( "import distutils\n" diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index fc5938289..82561b38c 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -37,7 +37,7 @@ def test_fuzz_sort_imports(file_name, config, check, ask_to_apply, write_to_stdo def test_iter_source_code(tmpdir): tmp_file = tmpdir.join("file.py") tmp_file.write("import os, sys\n") - assert tuple(main.iter_source_code((tmp_file,), DEFAULT_CONFIG, [])) == (tmp_file,) + assert tuple(main.iter_source_code((tmp_file,), DEFAULT_CONFIG, [], [])) == (tmp_file,) def test_sort_imports(tmpdir): @@ -268,6 +268,15 @@ def test_main(capsys, tmpdir): # without filter options passed in should successfully sort files main.main([str(python_file), str(should_skip), "--verbose", "--atomic"]) + # Should raise a system exit if all passed path is broken + with pytest.raises(SystemExit): + main.main(["not-exist", "--check-only"]) + + # Should not raise system exit if any of passed path is not broken + main.main([str(python_file), "not-exist", "--verbose", "--check-only"]) + out, error = capsys.readouterr() + assert "Broken" in out + # should respect gitignore if requested. out, error = capsys.readouterr() # clear sysoutput before tests subprocess.run(["git", "init", str(tmpdir)]) From 01ea371c37e649289c98ec801c61695fed955505 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 18 Sep 2020 00:03:11 +0900 Subject: [PATCH 040/539] Cleanup old style typing comments --- tests/unit/test_isort.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 91eb1d610..b2fedb8cb 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -3242,8 +3242,8 @@ def test_safety_skips(tmpdir, enabled: bool) -> None: config = Config(directory=str(tmpdir)) else: config = Config(skip=[], directory=str(tmpdir)) - skipped = [] # type: List[str] - broken = [] # type: List[str] + skipped: List[str] = [] + broken: List[str] = [] codes = [str(tmpdir)] main.iter_source_code(codes, config, skipped, broken) @@ -3289,8 +3289,8 @@ def test_skip_glob(tmpdir, skip_glob_assert: Tuple[List[str], int, Set[str]]) -> code_dir.join("file.py").write("import os") config = Config(skip_glob=skip_glob, directory=str(base_dir)) - skipped = [] # type: List[str] - broken = [] # type: List[str] + skipped: List[str] = [] + broken: List[str] = [] file_names = { os.path.relpath(f, str(base_dir)) for f in main.iter_source_code([str(base_dir)], config, skipped, broken) @@ -3303,8 +3303,8 @@ def test_broken(tmpdir) -> None: base_dir = tmpdir.mkdir("broken") config = Config(directory=str(base_dir)) - skipped = [] # type: List[str] - broken = [] # type: List[str] + skipped: List[str] = [] + broken: List[str] = [] file_names = { os.path.relpath(f, str(base_dir)) for f in main.iter_source_code(["not-exist"], config, skipped, broken) From 37d6d52b343229179f6f6d96d2247dead180d7bd Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Sep 2020 21:35:29 -0700 Subject: [PATCH 041/539] Add Sang-Heon Jeon (@lntuition) and @scottwedge to acknowledgements --- docs/contributing/4.-acknowledgements.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index ef8aecb56..f4588d763 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -197,6 +197,7 @@ Code Contributors - Aniruddha Bhattacharjee (@anirudnits) - Alexandre Yang (@AlexandreYang) - Andrew Howe (@howeaj) +- Sang-Heon Jeon (@lntuition) Documenters =================== @@ -218,6 +219,7 @@ Documenters - Kosei Kitahara (@Surgo) - Marat Sharafutdinov (@decaz) - Abtin (@abtinmo) +- @scottwedge -------------------------------------------- From 15f7723e180e790b2391d19e1c5bf51f967bd6d6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 19 Sep 2020 02:22:08 -0700 Subject: [PATCH 042/539] Update precommit --- .pre-commit-config.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 586a5edda..6ff5151ab 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,3 +3,6 @@ repos: rev: 5.5.2 hooks: - id: isort + files: 'isort/.*' + - id: isort + files: 'tests/.*' From cc34b2c0874094cc419c07d06a2a9cc4bae54255 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Tue, 22 Sep 2020 00:15:49 +0530 Subject: [PATCH 043/539] extended check-only option to work with stdin --- isort/main.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/isort/main.py b/isort/main.py index 95e910d5d..064be1e23 100644 --- a/isort/main.py +++ b/isort/main.py @@ -853,6 +853,15 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = print(json.dumps(config.__dict__, indent=4, separators=(",", ": "), default=_preconvert)) return elif file_names == ["-"]: + if check: + incorrectly_sorted = not api.check_stream( + input_stream=sys.stdin if stdin is None else stdin, + config=config, + show_diff=show_diff, + ) + + wrong_sorted_files = incorrectly_sorted + api.sort_stream( input_stream=sys.stdin if stdin is None else stdin, output_stream=sys.stdout, From bec1b6c1b2d23eda302a2bdca40bc65b2564653a Mon Sep 17 00:00:00 2001 From: anirudnits Date: Tue, 22 Sep 2020 00:16:26 +0530 Subject: [PATCH 044/539] added test for check-only option with stdin --- tests/unit/test_main.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 82561b38c..44e1f7efc 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -203,6 +203,22 @@ def test_main(capsys, tmpdir): assert "import a" in out assert "import b" in out + # check should work with stdin + + input_content_check = TextIOWrapper( + BytesIO( + b""" +import b +import a +""" + ) + ) + + with pytest.raises(SystemExit): + main.main(config_args + ["-", "--check-only"], stdin=input_content_check) + out, error = capsys.readouterr() + assert error == "ERROR: Imports are incorrectly sorted and/or formatted.\n" + # Should be able to run with just a file python_file = tmpdir.join("has_imports.py") python_file.write( From ce397f6880eca421e9a73ed87ff0d0b730053634 Mon Sep 17 00:00:00 2001 From: Timothy Edmund Crosley Date: Mon, 21 Sep 2020 20:21:22 -0700 Subject: [PATCH 045/539] Update main.py Either check or sort, not both --- isort/main.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/isort/main.py b/isort/main.py index 064be1e23..2db643fbc 100644 --- a/isort/main.py +++ b/isort/main.py @@ -861,13 +861,13 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = ) wrong_sorted_files = incorrectly_sorted - - api.sort_stream( - input_stream=sys.stdin if stdin is None else stdin, - output_stream=sys.stdout, - config=config, - show_diff=show_diff, - ) + else: + api.sort_stream( + input_stream=sys.stdin if stdin is None else stdin, + output_stream=sys.stdout, + config=config, + show_diff=show_diff, + ) else: skipped: List[str] = [] broken: List[str] = [] From 958fed9264ec3d3c7a80419dfd27517ecbeafc4c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Sep 2020 20:37:34 -0700 Subject: [PATCH 046/539] Mark Fixed #1492: --check does not work with stdin source. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b22f81d83..3d6809282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1463: Better interactive documentation for future option. - Fixed #1461: Quiet config option not respected by file API in some circumstances. - Fixed #1482: pylama integration is not working correctly out-of-the-box. + - Fixed #1492: --check does not work with stdin source. ### 5.5.3 [Hotfix] September 20, 2020 - Fixed #1488: in rare cases isort can mangle `yield from` or `raise from` statements. From 2c51557632643ca1f02bc6ac06d43f1ef87a143b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 23 Sep 2020 21:24:04 -0700 Subject: [PATCH 047/539] Fix issue #1494: pxd files should be sorted by default --- CHANGELOG.md | 1 + isort/settings.py | 2 +- tests/unit/test_settings.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d6809282..ebfb1f38e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.6.0 TBD - Implemented #1433: Provide helpful feedback in case a custom config file is specified without a configuration. + - Implemented #1494: Default to sorting imports within `.pxd` files. - Improved handling of unsupported configuration option errors (see #1475). - Fixed #1463: Better interactive documentation for future option. - Fixed #1461: Quiet config option not respected by file API in some circumstances. diff --git a/isort/settings.py b/isort/settings.py index 126934c13..a173366a9 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -31,7 +31,7 @@ from .wrap_modes import from_string as wrap_mode_from_string _SHEBANG_RE = re.compile(br"^#!.*\bpython[23w]?\b") -SUPPORTED_EXTENSIONS = frozenset({"py", "pyi", "pyx"}) +SUPPORTED_EXTENSIONS = frozenset({"py", "pyi", "pyx", "pxd"}) BLOCKED_EXTENSIONS = frozenset({"pex"}) FILE_SKIP_COMMENTS: Tuple[str, ...] = ( "isort:" + "skip_file", diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index 1f6a52080..462b667d0 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -49,6 +49,7 @@ def test_is_supported_filetype(self): assert self.instance.is_supported_filetype("file.py") assert self.instance.is_supported_filetype("file.pyi") assert self.instance.is_supported_filetype("file.pyx") + assert self.instance.is_supported_filetype("file.pxd") assert not self.instance.is_supported_filetype("file.pyc") assert not self.instance.is_supported_filetype("file.txt") assert not self.instance.is_supported_filetype("file.pex") From f398389b672b397b84826c7f3947fe280c94e476 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 23 Sep 2020 22:01:22 -0700 Subject: [PATCH 048/539] Update integration test to skip pxd files on pandas project until they are sorted within the project --- tests/integration/test_projects_using_isort.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 1b877fdad..756ea3030 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -36,10 +36,14 @@ def test_plone(tmpdir): def test_pandas(tmpdir): + # Need to limit extensions as isort has just made sorting pxd the default, and pandas + # will have not picked it up yet + # TODO: Remove below line as soon as these files are sorted on the mainline pandas project + limit_extensions = ["--ext", "py", "--ext", "pyi", "--ext", "pyx"] check_call( ["git", "clone", "--depth", "1", "https://github.com/pandas-dev/pandas.git", str(tmpdir)] ) - main(["--check-only", "--diff", str(tmpdir / "pandas"), "--skip", "__init__.py"]) + main(["--check-only", "--diff", str(tmpdir / "pandas"), "--skip", "__init__.py"] + limit_extensions) def test_fastapi(tmpdir): From abcaff93f8215addabdd12afdb594702ec816ea4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 23 Sep 2020 22:14:47 -0700 Subject: [PATCH 049/539] Formatting fix --- tests/integration/test_projects_using_isort.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 756ea3030..77537adaf 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -43,7 +43,10 @@ def test_pandas(tmpdir): check_call( ["git", "clone", "--depth", "1", "https://github.com/pandas-dev/pandas.git", str(tmpdir)] ) - main(["--check-only", "--diff", str(tmpdir / "pandas"), "--skip", "__init__.py"] + limit_extensions) + main( + ["--check-only", "--diff", str(tmpdir / "pandas"), "--skip", "__init__.py"] + + limit_extensions + ) def test_fastapi(tmpdir): From e50bd1e774dd15161cac7c102a4a51007af9577e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 24 Sep 2020 23:02:08 -0700 Subject: [PATCH 050/539] Resolve #1495 --- isort/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index 2db643fbc..fb5f3200e 100644 --- a/isort/main.py +++ b/isort/main.py @@ -665,7 +665,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="float_to_top", action="store_true", help="Causes all non-indented imports to float to the top of the file having its imports " - "sorted. It can be an excellent shortcut for collecting imports every once in a while " + "sorted (immediately below the top of file comment).\n" + "This can be an excellent shortcut for collecting imports every once in a while " "when you place them in the middle of a file to avoid context switching.\n\n" "*NOTE*: It currently doesn't work with cimports and introduces some extra over-head " "and a performance penalty.", From 0ea8a95eaad4698e78ab8fc0a16196778dfe9922 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 24 Sep 2020 23:19:40 -0700 Subject: [PATCH 051/539] Move chdir util to deprecated finders, since it is only used there and otherwise counts against coverage --- isort/deprecated/finders.py | 14 +++++++++++++- isort/utils.py | 13 ------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/isort/deprecated/finders.py b/isort/deprecated/finders.py index 77eb23fa4..dbb6fec02 100644 --- a/isort/deprecated/finders.py +++ b/isort/deprecated/finders.py @@ -7,6 +7,7 @@ import sys import sysconfig from abc import ABCMeta, abstractmethod +from contextlib import contextmanager from fnmatch import fnmatch from functools import lru_cache from glob import glob @@ -15,7 +16,7 @@ from isort import sections from isort.settings import KNOWN_SECTION_MAPPING, Config -from isort.utils import chdir, exists_case_sensitive +from isort.utils import exists_case_sensitive try: from pipreqs import pipreqs @@ -36,6 +37,17 @@ Pipfile = None +@contextmanager +def chdir(path: str) -> Iterator[None]: + """Context manager for changing dir and restoring previous workdir after exit.""" + curdir = os.getcwd() + os.chdir(path) + try: + yield + finally: + os.chdir(curdir) + + class BaseFinder(metaclass=ABCMeta): def __init__(self, config: Config) -> None: self.config = config diff --git a/isort/utils.py b/isort/utils.py index 27f17b4a5..63b519902 100644 --- a/isort/utils.py +++ b/isort/utils.py @@ -1,7 +1,5 @@ import os import sys -from contextlib import contextmanager -from typing import Iterator def exists_case_sensitive(path: str) -> bool: @@ -16,14 +14,3 @@ def exists_case_sensitive(path: str) -> bool: directory, basename = os.path.split(path) result = basename in os.listdir(directory) return result - - -@contextmanager -def chdir(path: str) -> Iterator[None]: - """Context manager for changing dir and restoring previous workdir after exit.""" - curdir = os.getcwd() - os.chdir(path) - try: - yield - finally: - os.chdir(curdir) From 856b53043c6b841a448090f8b8147a421250083c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 24 Sep 2020 23:41:34 -0700 Subject: [PATCH 052/539] Add additional test for noqa wap mode to ensure 100% test coverage --- tests/unit/test_wrap_modes.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unit/test_wrap_modes.py b/tests/unit/test_wrap_modes.py index 3ef01ae0c..29ecfd0df 100644 --- a/tests/unit/test_wrap_modes.py +++ b/tests/unit/test_wrap_modes.py @@ -47,6 +47,23 @@ def test_auto_saved(): ) == '*\x12\x07\U0009e994🁣"\U000ae787\x0e \x00\U0001ae99\U0005c3e7\U0004d08e \x1e ' ) + assert ( + wrap_modes.noqa( + **{ + "comment_prefix": " #", + "comments": ["NOQA", "THERE"], + "imports": [], + "include_trailing_comma": False, + "indent": "0\x19", + "line_length": -19659, + "line_separator": "\n", + "remove_comments": False, + "statement": "hi", + "white_space": " ", + } + ) + == "hi # NOQA THERE" + ) def test_backslash_grid(): From e2a95eb4afd7cfe6c549aeb82551b65addb4f68d Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Fri, 25 Sep 2020 16:39:50 +0200 Subject: [PATCH 053/539] Fix typo in README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 153772603..38f735faf 100644 --- a/README.md +++ b/README.md @@ -533,9 +533,9 @@ isort --rm "os.system" *.py The `--check-only` option ------------------------- -isort can also be used to used to verify that code is correctly -formatted by running it with `-c`. Any files that contain incorrectly -sorted and/or formatted imports will be outputted to `stderr`. +isort can also be used to verify that code is correctly formatted +by running it with `-c`. Any files that contain incorrectly sorted +and/or formatted imports will be outputted to `stderr`. ```bash isort **/*.py -c -v From 0f8eff147a1e6d6d1fa081e4738a97dd3c02e13d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 25 Sep 2020 20:42:56 -0700 Subject: [PATCH 054/539] Add test case for #1499: single-line multi-line string comment confuses float-to-top --- tests/unit/test_regressions.py | 77 ++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 4869d444f..5c609d200 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1081,3 +1081,80 @@ def generator_function(): from \\ """ assert isort.check_code(raise_from_at_file_end_ignored, show_diff=True) + + +def test_isort_float_to_top_correctly_identifies_single_line_comments_1499(): + """Test to ensure isort correctly handles the case where float to top is used + to push imports to the top and the top comment is a multiline type but only + one line. + See: https://github.com/PyCQA/isort/issues/1499 + """ + assert ( + isort.code( + '''#!/bin/bash +"""My comment""" +def foo(): + pass + +import a + +def bar(): + pass +''', + float_to_top=True, + ) + == ( + '''#!/bin/bash +"""My comment""" +import a + + +def foo(): + pass + + +def bar(): + pass +''' + ) + ) + assert ( + isort.code( + """#!/bin/bash +'''My comment''' +def foo(): + pass + +import a + +def bar(): + pass +""", + float_to_top=True, + ) + == ( + """#!/bin/bash +'''My comment''' +import a + + +def foo(): + pass + + +def bar(): + pass +""" + ) + ) + + assert isort.check_code( + """#!/bin/bash +'''My comment''' +import a + +x = 1 +""", + float_to_top=True, + show_diff=True, + ) From d9379fbb4841c57b9611954228275d4c7c82264a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 25 Sep 2020 20:43:33 -0700 Subject: [PATCH 055/539] Fix #1499: detect start of tripple quote comments --- isort/parse.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/isort/parse.py b/isort/parse.py index 613f7fa76..d9ab60e2c 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -200,12 +200,16 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte if skipping_line: out_lines.append(line) continue - elif ( + + lstripped_line = line.lstrip() + if ( config.float_to_top and import_index == -1 and line and not in_quote - and not line.strip().startswith("#") + and not lstripped_line.startswith("#") + and not lstripped_line.startswith("'''") + and not lstripped_line.startswith('"""') ): import_index = index - 1 while import_index and not in_lines[import_index - 1]: From ac5f198275cb98a7d49081f7399f1382beb44ee1 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 25 Sep 2020 20:44:17 -0700 Subject: [PATCH 056/539] Mark #1499: as fixed in changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebfb1f38e..b820a0325 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1461: Quiet config option not respected by file API in some circumstances. - Fixed #1482: pylama integration is not working correctly out-of-the-box. - Fixed #1492: --check does not work with stdin source. + - Fixed #1499: isort gets confused by single line, multi-line style comments when using float-to-top. ### 5.5.3 [Hotfix] September 20, 2020 - Fixed #1488: in rare cases isort can mangle `yield from` or `raise from` statements. From 11ca48b4accb2da22302bb3a948f1f7f704da613 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 26 Sep 2020 22:00:16 -0700 Subject: [PATCH 057/539] Set google profile not to order by type (#1486) --- isort/profiles.py | 1 + tests/unit/profiles/test_google.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/isort/profiles.py b/isort/profiles.py index cd976cd29..77afef242 100644 --- a/isort/profiles.py +++ b/isort/profiles.py @@ -21,6 +21,7 @@ "force_sort_within_sections": True, "lexicographical": True, "single_line_exclusions": ("typing",), + "order_by_type": False, } open_stack = { "force_single_line": True, diff --git a/tests/unit/profiles/test_google.py b/tests/unit/profiles/test_google.py index c558664dd..3a40bbc30 100644 --- a/tests/unit/profiles/test_google.py +++ b/tests/unit/profiles/test_google.py @@ -118,8 +118,8 @@ def test_google_code_snippet_one(): # flake8: noqa: F401 import collections -from contextlib import ExitStack from contextlib import contextmanager +from contextlib import ExitStack import functools import inspect import itertools as it @@ -136,8 +136,8 @@ def test_google_code_snippet_one(): from . import dtypes from . import linear_util as lu from .abstract_arrays import ConcreteArray -from .abstract_arrays import ShapedArray from .abstract_arrays import raise_to_shaped +from .abstract_arrays import ShapedArray from .api_util import apply_flat_fun from .api_util import argnums_partial from .api_util import donation_vector From 52ea01b4d967bbd9d06fe6d7220c0ccd1e67cd4e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 27 Sep 2020 15:06:18 -0700 Subject: [PATCH 058/539] Resolved #1502: Improved float-to-top behavior when there is an existing import section present at top-of-file. --- CHANGELOG.md | 1 + isort/core.py | 3 + isort/parse.py | 2 + tests/unit/test_ticketed_features.py | 122 ++++++++++++++++++++++++--- 4 files changed, 118 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b820a0325..68d114f3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.6.0 TBD - Implemented #1433: Provide helpful feedback in case a custom config file is specified without a configuration. - Implemented #1494: Default to sorting imports within `.pxd` files. + - Implemented #1502: Improved float-to-top behavior when there is an existing import section present at top-of-file. - Improved handling of unsupported configuration option errors (see #1475). - Fixed #1463: Better interactive documentation for future option. - Fixed #1461: Quiet config option not respected by file API in some circumstances. diff --git a/isort/core.py b/isort/core.py index d1e945d48..11ae35f95 100644 --- a/isort/core.py +++ b/isort/core.py @@ -83,6 +83,9 @@ def process( if line == "# isort: off\n": isort_off = True if current: + if add_imports: + current += line_separator + line_separator.join(add_imports) + add_imports = [] parsed = parse.file_contents(current, config=config) extra_space = "" while current and current[-1] == "\n": diff --git a/isort/parse.py b/isort/parse.py index d9ab60e2c..6f121c6bd 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -210,6 +210,8 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte and not lstripped_line.startswith("#") and not lstripped_line.startswith("'''") and not lstripped_line.startswith('"""') + and not lstripped_line.startswith("import") + and not lstripped_line.startswith("from") ): import_index = index - 1 while import_index and not in_lines[import_index - 1]: diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 75b43da89..f952bdc51 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -60,7 +60,8 @@ def my_function_2(): """, float_to_top=True, ) - == """import os + == """ +import os import sys @@ -90,7 +91,8 @@ def my_function_2(): """, float_to_top=True, ) - == """import os + == """ +import os def my_function_1(): @@ -105,10 +107,9 @@ def my_function_2(): """ ) - -assert ( - isort.code( - """ + assert ( + isort.code( + """ import os @@ -129,9 +130,10 @@ def my_function_2(): import a """, - float_to_top=True, - ) - == """import os + float_to_top=True, + ) + == """ +import os def my_function_1(): @@ -151,7 +153,7 @@ def y(): def my_function_2(): pass """ -) + ) def test_isort_provides_official_api_for_diff_output_issue_1335(): @@ -746,3 +748,103 @@ def test_isort_should_warn_on_empty_custom_config_issue_1433(tmpdir): with pytest.warns(None) as warning: assert Config(settings_file=str(settings_file)).quiet assert not warning + + +def test_float_to_top_should_respect_existing_newlines_between_imports_issue_1502(): + """When a file has an existing top of file import block before code but after comments + isort's float to top feature should respect the existing spacing between the top file comment + and the import statements. + See: https://github.com/PyCQA/isort/issues/1502 + """ + assert isort.check_code( + """#!/bin/bash +'''My comment''' + +import a + +x = 1 +""", + float_to_top=True, + show_diff=True, + ) + assert isort.check_code( + """#!/bin/bash +'''My comment''' + + +import a + +x = 1 +""", + float_to_top=True, + show_diff=True, + ) + assert ( + isort.code( + """#!/bin/bash +'''My comment''' + + +import a + +x = 1 +""", + float_to_top=True, + add_imports=["import b"], + ) + == """#!/bin/bash +'''My comment''' + + +import a +import b + +x = 1 +""" + ) + + assert ( + isort.code( + """#!/bin/bash +'''My comment''' + + +def my_function(): + pass + + +import a +""", + float_to_top=True, + ) + == """#!/bin/bash +'''My comment''' +import a + + +def my_function(): + pass +""" + ) + + assert ( + isort.code( + """#!/bin/bash +'''My comment''' + + +def my_function(): + pass +""", + add_imports=["import os"], + float_to_top=True, + ) + == """#!/bin/bash +'''My comment''' +import os + + +def my_function(): + pass +""" + ) From 85c2537452f457b9298e54f5cb3e933cf8058694 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 28 Sep 2020 22:54:12 -0700 Subject: [PATCH 059/539] Refactor integration tests to make it easier to add additional projects --- .../integration/test_projects_using_isort.py | 165 ++++++------------ 1 file changed, 50 insertions(+), 115 deletions(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 77537adaf..325498860 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -6,116 +6,78 @@ It is important to isort that as few regressions as possible are experienced by our users. Having your project tested here is the most sure way to keep those regressions form ever happening. """ +from pathlib import Path from subprocess import check_call +from typing import Sequence from isort.main import main +def git_clone(repository_url: str, directory: Path): + """Clones the given repository into the given directory path""" + check_call(["git", "clone", "--depth", "1", repository_url, str(directory)]) + + +def run_isort(arguments: Sequence[str]): + """Runs isort in diff and check mode with the given arguments""" + main(["--check-only", "--diff", *arguments]) + + def test_django(tmpdir): - check_call( - ["git", "clone", "--depth", "1", "https://github.com/django/django.git", str(tmpdir)] - ) - isort_target_dirs = [ + git_clone("https://github.com/django/django.git", tmpdir) + run_isort( str(target_dir) for target_dir in (tmpdir / "django", tmpdir / "tests", tmpdir / "scripts") - ] - main(["--check-only", "--diff", *isort_target_dirs]) + ) def test_plone(tmpdir): - check_call( - [ - "git", - "clone", - "--depth", - "1", - "https://github.com/plone/plone.app.multilingualindexes.git", - str(tmpdir), - ] - ) - main(["--check-only", "--diff", str(tmpdir / "src")]) + git_clone("https://github.com/plone/plone.app.multilingualindexes.git", tmpdir) + run_isort([str(tmpdir / "src")]) def test_pandas(tmpdir): # Need to limit extensions as isort has just made sorting pxd the default, and pandas # will have not picked it up yet # TODO: Remove below line as soon as these files are sorted on the mainline pandas project - limit_extensions = ["--ext", "py", "--ext", "pyi", "--ext", "pyx"] - check_call( - ["git", "clone", "--depth", "1", "https://github.com/pandas-dev/pandas.git", str(tmpdir)] - ) - main( - ["--check-only", "--diff", str(tmpdir / "pandas"), "--skip", "__init__.py"] - + limit_extensions - ) + git_clone("https://github.com/pandas-dev/pandas.git", tmpdir) + limit_extensions = ("--ext", "py", "--ext", "pyi", "--ext", "pyx") + run_isort((str(tmpdir / "pandas"), "--skip", "__init__.py", *limit_extensions)) def test_fastapi(tmpdir): - check_call( - ["git", "clone", "--depth", "1", "https://github.com/tiangolo/fastapi.git", str(tmpdir)] - ) - main(["--check-only", "--diff", str(tmpdir / "fastapi")]) + git_clone("https://github.com/tiangolo/fastapi.git", tmpdir) + run_isort([str(tmpdir / "fastapi")]) def test_zulip(tmpdir): - check_call(["git", "clone", "--depth", "1", "https://github.com/zulip/zulip.git", str(tmpdir)]) - main(["--check-only", "--diff", str(tmpdir), "--skip", "__init__.pyi"]) + git_clone("https://github.com/zulip/zulip.git", tmpdir) + run_isort((str(tmpdir), "--skip", "__init__.pyi")) def test_habitat_lab(tmpdir): - check_call( - [ - "git", - "clone", - "--depth", - "1", - "https://github.com/facebookresearch/habitat-lab.git", - str(tmpdir), - ] - ) - main(["--check-only", "--diff", str(tmpdir)]) + git_clone("https://github.com/facebookresearch/habitat-lab.git", tmpdir) + run_isort([str(tmpdir)]) def test_tmuxp(tmpdir): - check_call( - ["git", "clone", "--depth", "1", "https://github.com/tmux-python/tmuxp.git", str(tmpdir)] - ) - main(["--check-only", "--diff", str(tmpdir)]) + git_clone("https://github.com/tmux-python/tmuxp.git", tmpdir) + run_isort([str(tmpdir)]) def test_websockets(tmpdir): - check_call( - ["git", "clone", "--depth", "1", "https://github.com/aaugustin/websockets.git", str(tmpdir)] - ) - main( - [ - "--check-only", - "--diff", - str(tmpdir), - "--skip", - "example", - "--skip", - "docs", - "--skip", - "compliance", - ] - ) + git_clone("https://github.com/aaugustin/websockets.git", tmpdir) + run_isort((str(tmpdir), "--skip", "example", "--skip", "docs", "--skip", "compliance")) def test_airflow(tmpdir): - check_call( - ["git", "clone", "--depth", "1", "https://github.com/apache/airflow.git", str(tmpdir)] - ) - main(["--check-only", "--diff", str(tmpdir)]) + git_clone("https://github.com/apache/airflow.git", tmpdir) + run_isort([str(tmpdir)]) def test_typeshed(tmpdir): - check_call( - ["git", "clone", "--depth", "1", "https://github.com/python/typeshed.git", str(tmpdir)] - ) - main( - [ - "--check-only", - "--diff", + git_clone("https://github.com/python/typeshed.git", tmpdir) + run_isort( + ( str(tmpdir), "--skip", "tests", @@ -123,51 +85,34 @@ def test_typeshed(tmpdir): "scripts", "--skip", f"{tmpdir}/third_party/2and3/yaml/__init__.pyi", - ] + ) ) def test_pylint(tmpdir): - check_call(["git", "clone", "--depth", "1", "https://github.com/PyCQA/pylint.git", str(tmpdir)]) - main(["--check-only", "--diff", str(tmpdir)]) + git_clone("https://github.com/PyCQA/pylint.git", tmpdir) + run_isort([str(tmpdir)]) def test_poetry(tmpdir): - check_call( - ["git", "clone", "--depth", "1", "https://github.com/python-poetry/poetry.git", str(tmpdir)] - ) - main(["--check-only", "--diff", str(tmpdir), "--skip", "tests"]) + git_clone("https://github.com/python-poetry/poetry.git", tmpdir) + run_isort((str(tmpdir), "--skip", "tests")) def test_hypothesis(tmpdir): - check_call( - [ - "git", - "clone", - "--depth", - "1", - "https://github.com/HypothesisWorks/hypothesis.git", - str(tmpdir), - ] - ) - main(["--check-only", "--diff", str(tmpdir), "--skip", "tests"]) + git_clone("https://github.com/HypothesisWorks/hypothesis.git", tmpdir) + run_isort((str(tmpdir), "--skip", "tests")) def test_pillow(tmpdir): - check_call( - ["git", "clone", "--depth", "1", "https://github.com/python-pillow/Pillow.git", str(tmpdir)] - ) - main(["--check-only", "--diff", str(tmpdir), "--skip", "tests"]) + git_clone("https://github.com/python-pillow/Pillow.git", tmpdir) + run_isort((str(tmpdir), "--skip", "tests")) def test_attrs(tmpdir): - check_call( - ["git", "clone", "--depth", "1", "https://github.com/python-attrs/attrs.git", str(tmpdir)] - ) - main( - [ - "--check-only", - "--diff", + git_clone("https://github.com/python-attrs/attrs.git", tmpdir) + run_isort( + ( str(tmpdir), "--skip", "tests", @@ -175,20 +120,10 @@ def test_attrs(tmpdir): "py", "--skip", "_compat.py", - ] + ) ) def test_datadog_integrations_core(tmpdir): - check_call( - [ - "git", - "clone", - "--depth", - "1", - "https://github.com/DataDog/integrations-core.git", - str(tmpdir), - ] - ) - - main(["--check-only", "--diff", str(tmpdir)]) + git_clone("https://github.com/DataDog/integrations-core.git", tmpdir) + run_isort([str(tmpdir)]) From 9a4da5fe559db5701e6364c1e8911c8fde75695e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 28 Sep 2020 22:55:17 -0700 Subject: [PATCH 060/539] Add Pyramid project to isort integration tests --- tests/integration/test_projects_using_isort.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 325498860..2dd5ee62c 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -127,3 +127,11 @@ def test_attrs(tmpdir): def test_datadog_integrations_core(tmpdir): git_clone("https://github.com/DataDog/integrations-core.git", tmpdir) run_isort([str(tmpdir)]) + + +def test_pyramid(tmpdir): + git_clone("https://github.com/Pylons/pyramid.git", tmpdir) + run_isort( + str(target_dir) + for target_dir in (tmpdir / "src" / "pyramid", tmpdir / "tests", tmpdir / "setup.py") + ) From a8da08a6cf3bff12ce1668c5f4f99710317e42f0 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 28 Sep 2020 23:03:19 -0700 Subject: [PATCH 061/539] Add unit test case for desired placement behavior --- tests/unit/test_place.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/unit/test_place.py b/tests/unit/test_place.py index cc231d5f5..b1159471f 100644 --- a/tests/unit/test_place.py +++ b/tests/unit/test_place.py @@ -20,3 +20,10 @@ def test_extra_standard_library(src_path): ) assert place_tester("os") == sections.STDLIB assert place_tester("hug") == sections.STDLIB + + +def test_no_standard_library_placement(): + assert place.module_with_reason( + "pathlib", config=Config(sections=["THIRDPARTY"], default_section="THIRDPARTY") + ) == ("THIRDPARTY", "Default option in Config or universal default.") + assert place.module("pathlib") == "STDLIB" From 012b080df9fce9e3d645680b94b8c18c092b1211 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 28 Sep 2020 23:04:04 -0700 Subject: [PATCH 062/539] Improve placement logic to ensure it takes into account the case where known leads to non existant section --- isort/place.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/place.py b/isort/place.py index 34b2eeb86..117428aeb 100644 --- a/isort/place.py +++ b/isort/place.py @@ -54,7 +54,7 @@ def _known_pattern(name: str, config: Config) -> Optional[Tuple[str, str]]: module_names_to_check = (".".join(parts[:first_k]) for first_k in range(len(parts), 0, -1)) for module_name_to_check in module_names_to_check: for pattern, placement in config.known_patterns: - if pattern.match(module_name_to_check): + if placement in config.sections and pattern.match(module_name_to_check): return (placement, f"Matched configured known pattern {pattern}") return None From 1466a4bd03cce569da0eccdae43c9d2f10562203 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 28 Sep 2020 23:05:14 -0700 Subject: [PATCH 063/539] Mark Fixed #1505: Support case where known_SECTION points to a section not listed in sections. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d114f3a..6f40c3fde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1482: pylama integration is not working correctly out-of-the-box. - Fixed #1492: --check does not work with stdin source. - Fixed #1499: isort gets confused by single line, multi-line style comments when using float-to-top. + - Fixed #1505: Support case where known_SECTION points to a section not listed in sections. ### 5.5.3 [Hotfix] September 20, 2020 - Fixed #1488: in rare cases isort can mangle `yield from` or `raise from` statements. From b8db9898857fe9464495642f9a8a4fb981765e06 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 30 Sep 2020 12:43:17 +0300 Subject: [PATCH 064/539] Fixes syntax highlight in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 38f735faf..c2c6a8244 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ run against code written using a different version of Python) **From within Python**: -```bash +```python import isort isort.file("pythonfile.py") @@ -144,7 +144,7 @@ isort.file("pythonfile.py") or: -```bash +```python import isort sorted_code = isort.code("import b\nimport a\n") From 607438dc4ec945dac14a3f246310e6e77effce92 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Wed, 30 Sep 2020 23:06:20 +0530 Subject: [PATCH 065/539] added cli flag tests with stdin --- tests/unit/test_main.py | 384 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 44e1f7efc..ddca98dd4 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -319,3 +319,387 @@ def test_main(capsys, tmpdir): def test_isort_command(): """Ensure ISortCommand got registered, otherwise setuptools error must have occurred""" assert main.ISortCommand + + +def test_isort_with_stdin(capsys): + # ensures that isort sorts stdin without any flags + + input_content = TextIOWrapper( + BytesIO( + b""" +import b +import a +""" + ) + ) + + main.main(["-"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +import a +import b +""" + ) + + input_content_from = TextIOWrapper( + BytesIO( + b""" +import c +import b +from a import z, y, x +""" + ) + ) + + main.main(["-"], stdin=input_content_from) + out, error = capsys.readouterr() + + assert out == ( + """ +import b +import c +from a import x, y, z +""" + ) + + # ensures that isort correctly sorts stdin with --fas flag + + input_content = TextIOWrapper( + BytesIO( + b""" +import sys +import pandas +from z import abc +from a import xyz +""" + ) + ) + + main.main(["-", "--fas"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +from a import xyz +from z import abc + +import pandas +import sys +""" + ) + + # ensures that isort correctly sorts stdin with --fass flag + + input_content = TextIOWrapper( + BytesIO( + b""" +from a import Path, abc +""" + ) + ) + + main.main(["-", "--fass"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +from a import abc, Path +""" + ) + + # ensures that isort correctly sorts stdin with --ff flag + + input_content = TextIOWrapper( + BytesIO( + b""" +import b +from c import x +from a import y +""" + ) + ) + + main.main(["-", "--ff", "FROM_FIRST"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +from a import y +from c import x +import b +""" + ) + + # ensures that isort correctly sorts stdin with -fss flag + + input_content = TextIOWrapper( + BytesIO( + b""" +import b +from a import a +""" + ) + ) + + main.main(["-", "--fss"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +from a import a +import b +""" + ) + + input_content = TextIOWrapper( + BytesIO( + b""" +import a +from b import c +""" + ) + ) + + main.main(["-", "--fss"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +import a +from b import c +""" + ) + + # ensures that isort correctly sorts stdin with --ds flag + + input_content = TextIOWrapper( + BytesIO( + b""" +import sys +import pandas +import a +""" + ) + ) + + main.main(["-", "--ds"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +import a +import pandas +import sys +""" + ) + + # ensures that isort correctly sorts stdin with --cs flag + + input_content = TextIOWrapper( + BytesIO( + b""" +from a import b +from a import * +""" + ) + ) + + main.main(["-", "--cs"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +from a import * +""" + ) + + # ensures that isort correctly sorts stdin with --ca flag + + input_content = TextIOWrapper( + BytesIO( + b""" +from a import x as X +from a import y as Y +""" + ) + ) + + main.main(["-", "--ca"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +from a import x as X, y as Y +""" + ) + + # ensures that isort works consistently with check and ws flags + + input_content = TextIOWrapper( + BytesIO( + b""" +import os +import a +import b +""" + ) + ) + + main.main(["-", "--check-only", "--ws"], stdin=input_content) + out, error = capsys.readouterr() + + assert not error + + # ensures that isort correctly sorts stdin with --ls flag + + input_content = TextIOWrapper( + BytesIO( + b""" +import abcdef +import x +""" + ) + ) + + main.main(["-", "--ls"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +import x +import abcdef +""" + ) + + # ensures that isort correctly sorts stdin with --nis flag + + input_content = TextIOWrapper( + BytesIO( + b""" +from z import b, c, a +""" + ) + ) + + main.main(["-", "--nis"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +from z import b, c, a +""" + ) + + # ensures that isort correctly sorts stdin with --sl flag + + input_content = TextIOWrapper( + BytesIO( + b""" +from z import b, c, a +""" + ) + ) + + main.main(["-", "--sl"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +from z import a +from z import b +from z import c +""" + ) + + # ensures that isort correctly sorts stdin with --top flag + + input_content = TextIOWrapper( + BytesIO( + b""" +import os +import sys +""" + ) + ) + + main.main(["-", "--top", "sys"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +import sys +import os +""" + ) + + # ensure that isort correctly sorts stdin with --os flag + + input_content = TextIOWrapper( + BytesIO( + b""" +import sys +import os +import z +from a import b, e, c +""" + ) + ) + + main.main(["-", "--os"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +import sys +import os + +import z +from a import b, e, c +""" + ) + + # ensures that isort warns with deprecated flags with stdin + + input_content = TextIOWrapper( + BytesIO( + b""" +import sys +import os +""" + ) + ) + + with pytest.warns(UserWarning): + main.main(["-", "-ns"], stdin=input_content) + + out, error = capsys.readouterr() + + assert out == ( + """ +import os +import sys +""" + ) + + input_content = TextIOWrapper( + BytesIO( + b""" +import sys +import os +""" + ) + ) + + with pytest.warns(UserWarning): + main.main(["-", "-k"], stdin=input_content) + + out, error = capsys.readouterr() + + assert out == ( + """ +import os +import sys +""" + ) From c2473cf30fa37d72bb8918bb365b0fae281a451f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Sep 2020 21:31:06 -0700 Subject: [PATCH 066/539] Add note about fix for #1472 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60c8dac39..87baa08a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1492: --check does not work with stdin source. - Fixed #1499: isort gets confused by single line, multi-line style comments when using float-to-top. + Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): + - Implemented #1472: Full testing of stdin CLI Options + ### 5.5.4 [Hotfix] September 29, 2020 - Fixed #1507: in rare cases isort changes the content of multiline strings after a yield statement. - Fixed #1505: Support case where known_SECTION points to a section not listed in sections. From a5df07f146bab07da2df7d581b44d0942add362c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Sep 2020 21:40:28 -0700 Subject: [PATCH 067/539] Implemented #1511: Support for easily seeing all files isort will be ran against --- CHANGELOG.md | 1 + isort/main.py | 16 ++++++++++++++++ tests/unit/test_main.py | 24 ++++++++++++++++++++---- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87baa08a3..712e554d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1433: Provide helpful feedback in case a custom config file is specified without a configuration. - Implemented #1494: Default to sorting imports within `.pxd` files. - Implemented #1502: Improved float-to-top behavior when there is an existing import section present at top-of-file. + - Implemented #1511: Support for easily seeing all files isort will be ran against using `isort . --show-files`. - Improved handling of unsupported configuration option errors (see #1475). - Fixed #1463: Better interactive documentation for future option. - Fixed #1461: Quiet config option not respected by file API in some circumstances. diff --git a/isort/main.py b/isort/main.py index fb5f3200e..c518a92dc 100644 --- a/isort/main.py +++ b/isort/main.py @@ -638,6 +638,12 @@ def _build_arg_parser() -> argparse.ArgumentParser: action="store_true", help="See isort's determined config, as well as sources of config options.", ) + parser.add_argument( + "--show-files", + dest="show_files", + action="store_true", + help="See the files isort will be ran against with the current config options.", + ) parser.add_argument( "--honor-noqa", dest="honor_noqa", @@ -805,6 +811,9 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = return show_config: bool = arguments.pop("show_config", False) + show_files: bool = arguments.pop("show_files", False) + if show_config and show_files: + sys.exit("Error: either specify show-config or show-files not both.") if "settings_path" in arguments: if os.path.isfile(arguments["settings_path"]): @@ -854,6 +863,9 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = print(json.dumps(config.__dict__, indent=4, separators=(",", ": "), default=_preconvert)) return elif file_names == ["-"]: + if show_files: + sys.exit("Error: can't show files for streaming input.") + if check: incorrectly_sorted = not api.check_stream( input_stream=sys.stdin if stdin is None else stdin, @@ -883,6 +895,10 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = file_names = filtered_files file_names = iter_source_code(file_names, config, skipped, broken) + if show_files: + for file_name in file_names: + print(file_name) + return num_skipped = 0 num_broken = 0 if config.verbose: diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index ddca98dd4..ef837b614 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -13,9 +13,6 @@ from isort.settings import DEFAULT_CONFIG, Config from isort.wrap_modes import WrapModes -# This test code was written by the `hypothesis.extra.ghostwriter` module -# and is provided under the Creative Commons Zero public domain dedication. - @given( file_name=st.text(), @@ -104,6 +101,26 @@ def test_preconvert(): main._preconvert(datetime.now()) +def test_show_files(capsys, tmpdir): + tmpdir.join("a.py").write("import a") + tmpdir.join("b.py").write("import b") + + # show files should list the files isort would sort + main.main([str(tmpdir), "--show-files"]) + out, error = capsys.readouterr() + assert "a.py" in out + assert "b.py" in out + assert not error + + # can not be used for stream + with pytest.raises(SystemExit): + main.main(["-", "--show-files"]) + + # can not be used with show-config + with pytest.raises(SystemExit): + main.main([str(tmpdir), "--show-files", "--show-config"]) + + def test_main(capsys, tmpdir): base_args = [ "-sp", @@ -661,7 +678,6 @@ def test_isort_with_stdin(capsys): ) # ensures that isort warns with deprecated flags with stdin - input_content = TextIOWrapper( BytesIO( b""" From 28ccbe23c5629a14c43bfb62e5f6c7ce78dfbd0e Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Thu, 1 Oct 2020 10:56:41 +0300 Subject: [PATCH 068/539] Fixes bug in isort-assignments sorting. Fixes #1515. Bonus: increases branch coverage of literals.py to 100% --- isort/literal.py | 21 +++++++++++---------- tests/unit/test_literal.py | 8 ++++++-- tests/unit/test_ticketed_features.py | 8 ++++---- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/isort/literal.py b/isort/literal.py index 28e0855c3..01bd05e79 100644 --- a/isort/literal.py +++ b/isort/literal.py @@ -21,17 +21,18 @@ def __init__(self, config: Config): def assignments(code: str) -> str: - sort_assignments = {} + values = {} for line in code.splitlines(keepends=True): - if line: - if " = " not in line: - raise AssignmentsFormatMismatch(code) - else: - variable_name, value = line.split(" = ", 1) - sort_assignments[variable_name] = value - - sorted_assignments = dict(sorted(sort_assignments.items(), key=lambda item: item[1])) - return "".join(f"{key} = {value}" for key, value in sorted_assignments.items()) + if not line.strip(): + continue + if " = " not in line: + raise AssignmentsFormatMismatch(code) + variable_name, value = line.split(" = ", 1) + values[variable_name] = value + + return "".join( + f"{variable_name} = {values[variable_name]}" for variable_name in sorted(values.keys()) + ) def assignment(code: str, sort_type: str, extension: str, config: Config = DEFAULT_CONFIG) -> str: diff --git a/tests/unit/test_literal.py b/tests/unit/test_literal.py index 0dd7458c7..ee623927a 100644 --- a/tests/unit/test_literal.py +++ b/tests/unit/test_literal.py @@ -20,7 +20,7 @@ def test_invalid_sort_type(): isort.literal.assignment("x = [1, 2, 3", "tuple-list-not-exist", "py") -def test_value_assignment(): +def test_value_assignment_list(): assert isort.literal.assignment("x = ['b', 'a']", "list", "py") == "x = ['a', 'b']" assert ( isort.literal.assignment("x = ['b', 'a']", "list", "py", Config(formatter="example")) @@ -28,6 +28,10 @@ def test_value_assignment(): ) +def test_value_assignment_assignments(): + assert isort.literal.assignment("b = 1\na = 2\n", "assignments", "py") == "a = 2\nb = 1\n" + + def test_assignments_invalid_section(): with pytest.raises(exceptions.AssignmentsFormatMismatch): - isort.literal.assignment("x++", "assignments", "py") + isort.literal.assignment("\n\nx = 1\nx++", "assignments", "py") diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index f952bdc51..51b130095 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -442,9 +442,9 @@ def method(): # isort: assignments -d = x +d = 1 b = 2 -a = 1 +a = 3 # isort: dict y = {"z": "z", "b": "b", "b": "c"}""", @@ -476,9 +476,9 @@ def method(): # isort: assignments -a = 1 +a = 3 b = 2 -d = x +d = 1 # isort: dict y = {"b": "c", "z": "z"}""" From 59730a8246f8a4ebd9a2e5180ce43342ed913b15 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Fri, 2 Oct 2020 00:38:15 +0530 Subject: [PATCH 069/539] Added the only-modified option in the config data schema --- isort/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/isort/settings.py b/isort/settings.py index a173366a9..a356a7dee 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -198,6 +198,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 From 1e634eabf28eb8acf9658acf82ad215f9c1cb7f3 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Fri, 2 Oct 2020 00:38:54 +0530 Subject: [PATCH 070/539] Added only-modified flag in parse.args --- isort/main.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/isort/main.py b/isort/main.py index c518a92dc..1cfdb56f7 100644 --- a/isort/main.py +++ b/isort/main.py @@ -763,6 +763,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 From b6c3c5997c0c1079a18ef2f26376875efa279e69 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Fri, 2 Oct 2020 00:40:03 +0530 Subject: [PATCH 071/539] Added attribute verbose_output in ParsedContent class --- isort/parse.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/isort/parse.py b/isort/parse.py index 6f121c6bd..9348267c3 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, ) From 6d32601a998cc55a2b72c90f2b91102202ce864c Mon Sep 17 00:00:00 2001 From: anirudnits Date: Fri, 2 Oct 2020 00:41:10 +0530 Subject: [PATCH 072/539] Made sure that verbose output is only shown for modified files --- isort/api.py | 2 +- isort/core.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/isort/api.py b/isort/api.py index f59bc6d91..6c5876be4 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 22cba49e0..7f4c2c8ad 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 From 9f09ca6255a23d5a8c3f58aceb5a2dfc116affba Mon Sep 17 00:00:00 2001 From: anirudnits Date: Fri, 2 Oct 2020 00:41:56 +0530 Subject: [PATCH 073/539] added tests for only-modified flag --- tests/unit/test_main.py | 103 +++++++++++++++++++++++++++++++++++++++ tests/unit/test_parse.py | 1 + 2 files changed, 104 insertions(+) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index ef837b614..2490cd0ed 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): @@ -719,3 +721,104 @@ def test_isort_with_stdin(capsys): import sys """ ) + + # 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 98183617d..0becac900 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) From b583c12d8218bf499b5063b2a3fe657a6b2b2a3b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 1 Oct 2020 23:04:25 -0700 Subject: [PATCH 074/539] Fix #1486: Google profile not quite correct. --- CHANGELOG.md | 2 ++ isort/output.py | 1 + isort/profiles.py | 1 + isort/settings.py | 1 + isort/sorting.py | 3 +++ tests/unit/profiles/test_google.py | 20 ++++++++++++++++++-- 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 712e554d0..150256ea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1482: pylama integration is not working correctly out-of-the-box. - Fixed #1492: --check does not work with stdin source. - Fixed #1499: isort gets confused by single line, multi-line style comments when using float-to-top. +Potentially breaking changes: + - Fixed #1486: "Google" profile is not quite Google style. Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1472: Full testing of stdin CLI Options diff --git a/isort/output.py b/isort/output.py index d7a14009a..d2633ffdd 100644 --- a/isort/output.py +++ b/isort/output.py @@ -102,6 +102,7 @@ def sorted_imports( lexicographical=config.lexicographical, length_sort=config.length_sort, reverse_relative=config.reverse_relative, + group_by_package=config.group_by_package, ), ) diff --git a/isort/profiles.py b/isort/profiles.py index 77afef242..bfc9b4f7b 100644 --- a/isort/profiles.py +++ b/isort/profiles.py @@ -22,6 +22,7 @@ "lexicographical": True, "single_line_exclusions": ("typing",), "order_by_type": False, + "group_by_package": True, } open_stack = { "force_single_line": True, diff --git a/isort/settings.py b/isort/settings.py index a173366a9..a4e5905ad 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -169,6 +169,7 @@ class _Config: force_grid_wrap: int = 0 force_sort_within_sections: bool = False lexicographical: bool = False + group_by_package: bool = False ignore_whitespace: bool = False no_lines_before: FrozenSet[str] = frozenset() no_inline_sort: bool = False diff --git a/isort/sorting.py b/isort/sorting.py index 780747a3d..3d3961367 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -58,6 +58,7 @@ def section_key( lexicographical: bool = False, length_sort: bool = False, reverse_relative: bool = False, + group_by_package: bool = False, ) -> str: section = "B" @@ -65,6 +66,8 @@ def section_key( match = re.match(r"^from (\.+)\s*(.*)", line) if match: line = f"from {' '.join(match.groups())}" + if group_by_package and line.strip().startswith("from"): + line = line.split(" import", 1)[0] if lexicographical: line = _import_line_intro_re.sub("", _import_line_midline_import_re.sub(".", line)) diff --git a/tests/unit/profiles/test_google.py b/tests/unit/profiles/test_google.py index 3a40bbc30..4a3b41313 100644 --- a/tests/unit/profiles/test_google.py +++ b/tests/unit/profiles/test_google.py @@ -5,6 +5,22 @@ google_isort_test = partial(isort_test, profile="google") +def test_google_code_snippet_shared_example(): + """Tests snippet examples directly shared with the isort project. + See: https://github.com/PyCQA/isort/issues/1486. + """ + google_isort_test( + """import collections +import cProfile +""" + ) + google_isort_test( + """from a import z +from a.b import c +""" + ) + + def test_google_code_snippet_one(): google_isort_test( '''# coding=utf-8 @@ -156,12 +172,13 @@ def test_google_code_snippet_one(): from .interpreters import ad from .interpreters import batching from .interpreters import invertible_ad as iad -from .interpreters.invertible_ad import custom_ivjp from .interpreters import masking from .interpreters import partial_eval as pe from .interpreters import pxla from .interpreters import xla +from .interpreters.invertible_ad import custom_ivjp from .lib import xla_bridge as xb +from .lib import xla_client as xc # Unused imports to be exported from .lib.xla_bridge import device_count from .lib.xla_bridge import devices @@ -170,7 +187,6 @@ def test_google_code_snippet_one(): from .lib.xla_bridge import host_ids from .lib.xla_bridge import local_device_count from .lib.xla_bridge import local_devices -from .lib import xla_client as xc from .traceback_util import api_boundary from .tree_util import Partial from .tree_util import tree_flatten From a00ac1c8825c901b4692ea03f4786ac4f20a0947 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 1 Oct 2020 23:25:51 -0700 Subject: [PATCH 075/539] Fix #1481: more prominent callout for how config files are loaded --- docs/upgrade_guides/5.0.0.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/upgrade_guides/5.0.0.md b/docs/upgrade_guides/5.0.0.md index d31fb9dc3..fb767c2c4 100644 --- a/docs/upgrade_guides/5.0.0.md +++ b/docs/upgrade_guides/5.0.0.md @@ -48,6 +48,10 @@ The first thing to keep in mind is how isort loads config options has changed in If you have multiple configs, they will need to be merged into 1 single one. You can see the priority order of configuration files and the manner in which they are loaded on the [config files documentation page](https://pycqa.github.io/isort/docs/configuration/config_files/). +!!! tip - "Config options are loaded relative to the file, not the isort instance." + isort looks for a config file based on the path of the file you request to sort. If you have your config placed outside of the project, you can use `--settings-path` to manually specify the config location instead. Full information about how config files are loaded is in the linked config files documentation page. + + ### `not_skip` This is the same as the `--dont-skip` CLI option above. In an earlier version isort had a default skip of `__init__.py`. To get around that many projects wanted a way to not skip `__init__.py` or any other files that were automatically skipped in the future by isort. isort no longer has any default skips, so if the value here is `__init__.py` you can simply remove the setting. If it is something else, just make sure you aren't specifying to skip that file somewhere else in your config. From 6d556bd206b1165b2b6d674c7e4fe0ad77946551 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 1 Oct 2020 23:48:38 -0700 Subject: [PATCH 076/539] Skip tests that are failing on github for windows only --- tests/unit/test_isort.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index b2fedb8cb..19e81b09c 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -3732,6 +3732,7 @@ def test_standard_library_deprecates_user_issue_778() -> None: assert isort.code(test_input) == test_input +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_settings_path_skip_issue_909(tmpdir) -> None: base_dir = tmpdir.mkdir("project") config_dir = base_dir.mkdir("conf") @@ -3762,6 +3763,7 @@ def test_settings_path_skip_issue_909(tmpdir) -> None: assert b"skipped 2" in result.stdout.lower() +@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") def test_skip_paths_issue_938(tmpdir) -> None: base_dir = tmpdir.mkdir("project") config_dir = base_dir.mkdir("conf") From a397eb698f245dd6b46c47e426a4f80927867446 Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Sat, 3 Oct 2020 10:27:30 +0300 Subject: [PATCH 077/539] Adds characterization tests for float_to_top and on/off. --- tests/unit/test_regressions.py | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 5dc2fc81b..757595224 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -716,6 +716,81 @@ def test_isort_doesnt_mangle_code_when_adding_imports_issue_1444(): ) +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 From dbeaaa63611c19b942261ec0115c57290c8f8719 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 3 Oct 2020 01:01:49 -0700 Subject: [PATCH 078/539] Implemented #1487: Improved handling of encoding errors. --- CHANGELOG.md | 3 ++- isort/exceptions.py | 14 +++++++++++++- isort/io.py | 15 ++++++++++++--- isort/main.py | 29 +++++++++++++++++++++++++---- tests/unit/test_exceptions.py | 8 ++++++++ tests/unit/test_main.py | 31 +++++++++++++++++++++++++++++++ 6 files changed, 91 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 150256ea0..e7fb1aab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1494: Default to sorting imports within `.pxd` files. - Implemented #1502: Improved float-to-top behavior when there is an existing import section present at top-of-file. - Implemented #1511: Support for easily seeing all files isort will be ran against using `isort . --show-files`. + - Implemented #1487: Improved handling of encoding errors. - Improved handling of unsupported configuration option errors (see #1475). - Fixed #1463: Better interactive documentation for future option. - Fixed #1461: Quiet config option not respected by file API in some circumstances. @@ -16,7 +17,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1492: --check does not work with stdin source. - Fixed #1499: isort gets confused by single line, multi-line style comments when using float-to-top. Potentially breaking changes: - - Fixed #1486: "Google" profile is not quite Google style. + - Fixed #1486: "Google" profile is not quite Google style. Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1472: Full testing of stdin CLI Options diff --git a/isort/exceptions.py b/isort/exceptions.py index 265928e98..b98454a2c 100644 --- a/isort/exceptions.py +++ b/isort/exceptions.py @@ -1,5 +1,6 @@ """All isort specific exception classes should be defined here""" -from typing import Any, Dict +from pathlib import Path +from typing import Any, Dict, Union from .profiles import profiles @@ -157,3 +158,14 @@ def __init__(self, unsupported_settings: Dict[str, Dict[str, str]]): "https://pycqa.github.io/isort/docs/configuration/options/.\n" ) self.unsupported_settings = unsupported_settings + + +class UnsupportedEncoding(ISortError): + """Raised when isort encounters an encoding error while trying to read a file""" + + def __init__( + self, + filename: Union[str, Path], + ): + super().__init__(f"Unknown or unsupported encoding in {filename}") + self.filename = filename diff --git a/isort/io.py b/isort/io.py index a0357347b..7ff2807d2 100644 --- a/isort/io.py +++ b/isort/io.py @@ -4,7 +4,9 @@ from contextlib import contextmanager from io import BytesIO, StringIO, TextIOWrapper from pathlib import Path -from typing import Iterator, NamedTuple, TextIO, Union +from typing import Callable, Iterator, NamedTuple, TextIO, Union + +from isort.exceptions import UnsupportedEncoding _ENCODING_PATTERN = re.compile(br"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)") @@ -14,9 +16,16 @@ class File(NamedTuple): path: Path encoding: str + @staticmethod + def detect_encoding(filename: str, readline: Callable[[], bytes]): + try: + return tokenize.detect_encoding(readline)[0] + except Exception: + raise UnsupportedEncoding(filename) + @staticmethod def from_contents(contents: str, filename: str) -> "File": - encoding, _ = tokenize.detect_encoding(BytesIO(contents.encode("utf-8")).readline) + encoding = File.detect_encoding(filename, BytesIO(contents.encode("utf-8")).readline) return File(StringIO(contents), path=Path(filename).resolve(), encoding=encoding) @property @@ -30,7 +39,7 @@ def _open(filename): """ buffer = open(filename, "rb") try: - encoding, _ = tokenize.detect_encoding(buffer.readline) + encoding = File.detect_encoding(filename, buffer.readline) buffer.seek(0) text = TextIOWrapper(buffer, encoding, line_buffering=True, newline="") text.mode = "r" # type: ignore diff --git a/isort/main.py b/isort/main.py index c518a92dc..841caca52 100644 --- a/isort/main.py +++ b/isort/main.py @@ -10,7 +10,7 @@ from warnings import warn from . import __version__, api, sections -from .exceptions import FileSkipped +from .exceptions import FileSkipped, UnsupportedEncoding from .format import create_terminal_printer from .logo import ASCII_ART from .profiles import profiles @@ -67,9 +67,10 @@ class SortAttempt: - def __init__(self, incorrectly_sorted: bool, skipped: bool) -> None: + def __init__(self, incorrectly_sorted: bool, skipped: bool, supported_encoding: bool) -> None: self.incorrectly_sorted = incorrectly_sorted self.skipped = skipped + self.supported_encoding = supported_encoding def sort_imports( @@ -88,7 +89,7 @@ def sort_imports( incorrectly_sorted = not api.check_file(file_name, config=config, **kwargs) except FileSkipped: skipped = True - return SortAttempt(incorrectly_sorted, skipped) + return SortAttempt(incorrectly_sorted, skipped, True) else: try: incorrectly_sorted = not api.sort_file( @@ -100,10 +101,14 @@ def sort_imports( ) except FileSkipped: skipped = True - return SortAttempt(incorrectly_sorted, skipped) + return SortAttempt(incorrectly_sorted, skipped, True) except (OSError, ValueError) as error: warn(f"Unable to parse file {file_name} due to {error}") return None + except UnsupportedEncoding: + if config.verbose: + warn(f"Encoding not supported for {file_name}") + return SortAttempt(incorrectly_sorted, skipped, False) except Exception: printer = create_terminal_printer(color=config.color_output) printer.error( @@ -852,6 +857,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = remapped_deprecated_args = config_dict.pop("remapped_deprecated_args", False) wrong_sorted_files = False all_attempt_broken = False + no_valid_encodings = False if "src_paths" in config_dict: config_dict["src_paths"] = { @@ -901,6 +907,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = return num_skipped = 0 num_broken = 0 + num_invalid_encoding = 0 if config.verbose: print(ASCII_ART) @@ -934,6 +941,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = # If any files passed in are missing considered as error, should be removed is_no_attempt = True + any_encoding_valid = False for sort_attempt in attempt_iterator: if not sort_attempt: continue # pragma: no cover - shouldn't happen, satisfies type constraint @@ -944,6 +952,12 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = num_skipped += ( 1 # pragma: no cover - shouldn't happen, due to skip in iter_source_code ) + + if not sort_attempt.supported_encoding: + num_invalid_encoding += 1 + else: + any_encoding_valid = True + is_no_attempt = False num_skipped += len(skipped) @@ -965,6 +979,8 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = if num_broken > 0 and is_no_attempt: all_attempt_broken = True + if num_invalid_encoding > 0 and not any_encoding_valid: + no_valid_encodings = True if not config.quiet and (remapped_deprecated_args or deprecated_flags): if remapped_deprecated_args: @@ -988,6 +1004,11 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = if all_attempt_broken: sys.exit(1) + if no_valid_encodings: + printer = create_terminal_printer(color=config.color_output) + printer.error("No valid encodings.") + sys.exit(1) + if __name__ == "__main__": main() diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py index bb6ee2a31..d9eae8bf8 100644 --- a/tests/unit/test_exceptions.py +++ b/tests/unit/test_exceptions.py @@ -90,3 +90,11 @@ def setup_class(self): def test_variables(self): assert self.instance.unsupported_settings == {"apply": {"value": "true", "source": "/"}} + + +class TestUnsupportedEncoding(TestISortError): + def setup_class(self): + self.instance = exceptions.UnsupportedEncoding("file.py") + + def test_variables(self): + assert self.instance.filename == "file.py" diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index ef837b614..28da37eb8 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -719,3 +719,34 @@ def test_isort_with_stdin(capsys): import sys """ ) + + +def test_unsupported_encodings(tmpdir, capsys): + tmp_file = tmpdir.join("file.py") + # fmt: off + tmp_file.write( + u''' +# [syntax-error]\ +# -*- coding: IBO-8859-1 -*- +""" check correct unknown encoding declaration +""" +__revision__ = 'יייי' +''' + ) + # fmt: on + + # should throw an error if only unsupported encoding provided + with pytest.raises(SystemExit): + main.main([str(tmp_file)]) + out, error = capsys.readouterr() + + assert "No valid encodings." in error + + # should not throw an error if at least one valid encoding found + normal_file = tmpdir.join("file1.py") + normal_file.write("import os\nimport sys") + + main.main([str(tmp_file), str(normal_file), "--verbose"]) + out, error = capsys.readouterr() + + assert not error From 82d76ef81b90ed213f9ce3f46d3c97e1322025be Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 3 Oct 2020 01:11:58 -0700 Subject: [PATCH 079/539] Fix how unsupported encoding file is created --- tests/unit/test_main.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 28da37eb8..9c2da466c 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -724,14 +724,15 @@ def test_isort_with_stdin(capsys): def test_unsupported_encodings(tmpdir, capsys): tmp_file = tmpdir.join("file.py") # fmt: off - tmp_file.write( - u''' + tmp_file.write_text( + ''' # [syntax-error]\ # -*- coding: IBO-8859-1 -*- """ check correct unknown encoding declaration """ __revision__ = 'יייי' -''' +''', + encoding="utf8" ) # fmt: on From 36e592975d175cb03b92058cf5921513796e1ceb Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 3 Oct 2020 01:24:50 -0700 Subject: [PATCH 080/539] Remove uneccesary spacing --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7fb1aab1..1a7e9f966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1492: --check does not work with stdin source. - Fixed #1499: isort gets confused by single line, multi-line style comments when using float-to-top. Potentially breaking changes: - - Fixed #1486: "Google" profile is not quite Google style. + - Fixed #1486: "Google" profile is not quite Google style. Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1472: Full testing of stdin CLI Options From 4045450a106405e97fc71e0ea4b0852ddeb299bb Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sun, 4 Oct 2020 17:41:17 +0530 Subject: [PATCH 081/539] added another test for full coverage of only-modified flag with check and also corrected a misplaced test --- tests/unit/test_main.py | 49 +++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 3fb75ed64..caf20e7ed 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -722,6 +722,22 @@ def test_isort_with_stdin(capsys): """ ) + # 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_unsupported_encodings(tmpdir, capsys): tmp_file = tmpdir.join("file.py") @@ -752,22 +768,6 @@ def test_unsupported_encodings(tmpdir, capsys): 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 @@ -852,3 +852,20 @@ def test_only_modified_flag(tmpdir, capsys): ) assert not error + + file4 = tmpdir.join("file4.py") + file4.write( + """ +import sys +import os +""" + ) + + with pytest.raises(SystemExit): + main.main([str(file2), str(file4), "--check-only", "--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 From 2c796710b3f44f0858291a4754bb07ea3f99be02 Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Sun, 4 Oct 2020 20:58:29 +0300 Subject: [PATCH 082/539] Improve test coverage api.py. --- isort/api.py | 2 +- tests/unit/test_api.py | 51 ++++++++++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/isort/api.py b/isort/api.py index 6c5876be4..a8cecb623 100644 --- a/isort/api.py +++ b/isort/api.py @@ -354,7 +354,7 @@ def sort_file( try: # Python 3.8+: use `missing_ok=True` instead of try except. tmp_file.unlink() except FileNotFoundError: - pass + pass # pragma: no cover except ExistingSyntaxErrors: warn(f"{actual_file_path} unable to sort due to existing syntax errors") except IntroducedSyntaxErrors: # pragma: no cover diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 1b3ed3701..baec283a1 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -7,36 +7,63 @@ from isort import api from isort.settings import Config +imperfect_content = "import b\nimport a\n" +fixed_content = "import a\nimport b\n" +fixed_diff = "\n+import a\n import b\n-import a\n" -def test_sort_file(tmpdir) -> None: + +@pytest.fixture +def imperfect(tmpdir) -> None: + imperfect_file = tmpdir.join("test_needs_changes.py") + imperfect_file.write_text(imperfect_content, "utf8") + return imperfect_file + + +def test_sort_file_with_bad_syntax(tmpdir) -> None: tmp_file = tmpdir.join("test_bad_syntax.py") - tmp_file.write_text("""print('mismathing quotes")""", "utf8") + tmp_file.write_text("""print('mismatching quotes")""", "utf8") with pytest.warns(UserWarning): api.sort_file(tmp_file, atomic=True) with pytest.warns(UserWarning): api.sort_file(tmp_file, atomic=True, write_to_stdout=True) - imperfect = tmpdir.join("test_needs_changes.py") - imperfect.write_text("import b\nimport a\n", "utf8") - api.sort_file(imperfect, write_to_stdout=True, show_diff=True) +def test_sort_file(imperfect) -> None: + assert api.sort_file(imperfect) + assert imperfect.read() == fixed_content + + +def test_sort_file_to_stdout(capsys, imperfect) -> None: + assert api.sort_file(imperfect, write_to_stdout=True) + out, _ = capsys.readouterr() + assert out == fixed_content + + +def test_other_ask_to_apply(imperfect) -> None: # First show diff, but ensure change wont get written by asking to apply # and ensuring answer is no. with patch("isort.format.input", MagicMock(return_value="n")): - api.sort_file(imperfect, show_diff=True, ask_to_apply=True) + assert not api.sort_file(imperfect, ask_to_apply=True) + assert imperfect.read() == imperfect_content - # Then run again, but apply the change without asking - api.sort_file(imperfect, show_diff=True) + # Then run again, but apply the change (answer is yes) + with patch("isort.format.input", MagicMock(return_value="y")): + assert api.sort_file(imperfect, ask_to_apply=True) + assert imperfect.read() == fixed_content -def test_check_file(tmpdir) -> None: +def test_check_file_no_changes(capsys, tmpdir) -> None: perfect = tmpdir.join("test_no_changes.py") perfect.write_text("import a\nimport b\n", "utf8") assert api.check_file(perfect, show_diff=True) + out, _ = capsys.readouterr() + assert not out + - imperfect = tmpdir.join("test_needs_changes.py") - imperfect.write_text("import b\nimport a\n", "utf8") +def test_check_file_with_changes(capsys, imperfect) -> None: assert not api.check_file(imperfect, show_diff=True) + out, _ = capsys.readouterr() + assert fixed_diff in out def test_sorted_imports_multiple_configs() -> None: @@ -48,7 +75,7 @@ def test_diff_stream() -> None: output = StringIO() assert api.sort_stream(StringIO("import b\nimport a\n"), output, show_diff=True) output.seek(0) - assert "import a\n import b\n" in output.read() + assert fixed_diff in output.read() def test_sort_code_string_mixed_newlines(): From 533c26f0d45324bb5e4c1a157f46ddb2e313f37e Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Sun, 4 Oct 2020 22:10:17 +0300 Subject: [PATCH 083/539] Fix warnings can't surpressed with quiet. Fixes #1525 --- isort/settings.py | 16 +++++++++------- tests/unit/test_isort.py | 6 ++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index 937e6e067..a89f6d69f 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -266,6 +266,11 @@ def __init__( super().__init__(**config_vars) # type: ignore return + # We can't use self.quiet to conditionally show warnings before super.__init__() is called + # at the end of this method. _Config is also frozen so setting self.quiet isn't possible. + # Therefore we extract quiet early here in a variable and use that in warning conditions. + quiet = config_overrides.get("quiet", False) + sources: List[Dict[str, Any]] = [_DEFAULT_SETTINGS] config_settings: Dict[str, Any] @@ -276,7 +281,7 @@ def __init__( CONFIG_SECTIONS.get(os.path.basename(settings_file), FALLBACK_CONFIG_SECTIONS), ) project_root = os.path.dirname(settings_file) - if not config_settings: + if not config_settings and not quiet: warn( f"A custom settings file was specified: {settings_file} but no configuration " "was found inside. This can happen when [settings] is used as the config " @@ -343,7 +348,7 @@ def __init__( combined_config.pop(key) if maps_to_section in KNOWN_SECTION_MAPPING: section_name = f"known_{KNOWN_SECTION_MAPPING[maps_to_section].lower()}" - if section_name in combined_config and not self.quiet: + if section_name in combined_config and not quiet: warn( f"Can't set both {key} and {section_name} in the same config file.\n" f"Default to {section_name} if unsure." @@ -355,10 +360,7 @@ def __init__( combined_config[section_name] = frozenset(value) else: known_other[import_heading] = frozenset(value) - if ( - maps_to_section not in combined_config.get("sections", ()) - and not self.quiet - ): + if maps_to_section not in combined_config.get("sections", ()) and not quiet: warn( f"`{key}` setting is defined, but {maps_to_section} is not" " included in `sections` config option:" @@ -425,7 +427,7 @@ def __init__( if deprecated_options_used: for deprecated_option in deprecated_options_used: combined_config.pop(deprecated_option) - if not self.quiet: + if not quiet: warn( "W0503: Deprecated config options were used: " f"{', '.join(deprecated_options_used)}." diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 19e81b09c..31550f953 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -4825,6 +4825,12 @@ def test_deprecated_settings(): assert isort.code("hi", not_skip=True) +def test_deprecated_settings_no_warn_in_quiet_mode(recwarn): + """Test to ensure isort does NOT warn in quiet mode even though settings are deprecated""" + assert isort.code("hi", not_skip=True, quiet=True) + assert not recwarn + + def test_only_sections() -> None: """Test to ensure that the within sections relative position of imports are maintained""" test_input = ( From 97686bc4d9034695407682362af8511823574a0e Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Sun, 4 Oct 2020 20:59:44 +0300 Subject: [PATCH 084/539] Improve test coverage hooks.py --- tests/unit/test_hooks.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/tests/unit/test_hooks.py b/tests/unit/test_hooks.py index 083fbfd48..ddaff0e25 100644 --- a/tests/unit/test_hooks.py +++ b/tests/unit/test_hooks.py @@ -31,17 +31,34 @@ def test_git_hook(src_dir): "HEAD", ] - # Test with incorrectly sorted file returned from git + # Test that non python files aren't processed with patch( - "isort.hooks.get_lines", MagicMock(return_value=[os.path.join(src_dir, "main.py")]) - ) as run_mock: + "isort.hooks.get_lines", + MagicMock(return_value=["README.md", "setup.cfg", "LICDENSE", "mkdocs.yml", "test"]), + ): + with patch("subprocess.run", MagicMock()) as run_mock: + hooks.git_hook(modify=True) + run_mock.assert_not_called() - class FakeProcessResponse(object): - stdout = b"import b\nimport a" + mock_main_py = MagicMock(return_value=[os.path.join(src_dir, "main.py")]) - with patch("subprocess.run", MagicMock(return_value=FakeProcessResponse())) as run_mock: - with patch("isort.api", MagicMock(return_value=False)): + mock_imperfect = MagicMock() + mock_imperfect.return_value.stdout = b"import b\nimport a" + + # Test with incorrectly sorted file returned from git + with patch("isort.hooks.get_lines", mock_main_py): + with patch("subprocess.run", mock_imperfect): + with patch("isort.api.sort_file", MagicMock(return_value=False)) as api_mock: hooks.git_hook(modify=True) + api_mock.assert_called_once() + assert api_mock.call_args.args[0] == mock_main_py.return_value[0] + + # Test with sorted file returned from git and modify=False + with patch("isort.hooks.get_lines", mock_main_py): + with patch("subprocess.run", mock_imperfect): + with patch("isort.api.sort_file", MagicMock(return_value=False)) as api_mock: + hooks.git_hook(modify=False) + api_mock.assert_not_called() # Test with skipped file returned from git with patch( From d8f1f1dfa8b910ec8195d1ebdf18407af1de3060 Mon Sep 17 00:00:00 2001 From: Denis Veselov Date: Sun, 4 Oct 2020 22:41:41 +0300 Subject: [PATCH 085/539] Improve PyCharm Profile --- docs/configuration/profiles.md | 1 + isort/profiles.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/configuration/profiles.md b/docs/configuration/profiles.md index 3b0e4cc47..1b6d6f75e 100644 --- a/docs/configuration/profiles.md +++ b/docs/configuration/profiles.md @@ -30,6 +30,7 @@ To use any of the listed profiles, use `isort --profile PROFILE_NAME` from the c - **multi_line_output**: `3` - **force_grid_wrap**: `2` + - **lines_after_imports**: `2` #google diff --git a/isort/profiles.py b/isort/profiles.py index bfc9b4f7b..cb8cb5688 100644 --- a/isort/profiles.py +++ b/isort/profiles.py @@ -15,7 +15,11 @@ "multi_line_output": 5, "line_length": 79, } -pycharm = {"multi_line_output": 3, "force_grid_wrap": 2} +pycharm = { + "multi_line_output": 3, + "force_grid_wrap": 2, + "lines_after_imports": 2, +} google = { "force_single_line": True, "force_sort_within_sections": True, From a9853b95e09c98babed94c95d122ac751de60fbe Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Sun, 4 Oct 2020 22:59:49 +0300 Subject: [PATCH 086/539] Improve test coverage of place.py. More characterisation around forced_separate: - what happens when we use * to match the end of packages - changing order of forced_separate sections (ie. making django.utils come before django.contrib) --- tests/unit/test_isort.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 19e81b09c..cb911b5ab 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -980,6 +980,7 @@ def test_forced_separate() -> None: "from django.db import models\n" "from django.db.models.fields import FieldDoesNotExist\n" "from django.utils import six\n" + "\n" "from django.utils.deprecation import RenameMethodsBase\n" "from django.utils.encoding import force_str, force_text\n" "from django.utils.http import urlencode\n" @@ -993,7 +994,7 @@ def test_forced_separate() -> None: assert ( isort.code( code=test_input, - forced_separate=["django.contrib"], + forced_separate=["django.utils.*", "django.contrib"], known_third_party=["django"], line_length=120, order_by_type=False, @@ -1003,7 +1004,7 @@ def test_forced_separate() -> None: assert ( isort.code( code=test_input, - forced_separate=["django.contrib"], + forced_separate=["django.utils.*", "django.contrib"], known_third_party=["django"], line_length=120, order_by_type=False, From e94d2e3192ce6f2e92309a0706227932e5ef018c Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Mon, 5 Oct 2020 00:27:06 +0300 Subject: [PATCH 087/539] Improve test coverage of sorting.py and setuptools_commands.py pragma: no cover for these. Setuptools commands would be hard to test and sorting.py has an if that should never be reached. --- isort/setuptools_commands.py | 4 ++-- isort/sorting.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/isort/setuptools_commands.py b/isort/setuptools_commands.py index f67008877..96e41dd0b 100644 --- a/isort/setuptools_commands.py +++ b/isort/setuptools_commands.py @@ -31,13 +31,13 @@ def finalize_options(self) -> None: def distribution_files(self) -> Iterator[str]: """Find distribution packages.""" # This is verbatim from flake8 - if self.distribution.packages: + if self.distribution.packages: # pragma: no cover package_dirs = self.distribution.package_dir or {} for package in self.distribution.packages: pkg_dir = package if package in package_dirs: pkg_dir = package_dirs[package] - elif "" in package_dirs: + elif "" in package_dirs: # pragma: no cover pkg_dir = package_dirs[""] + os.path.sep + pkg_dir yield pkg_dir.replace(".", os.path.sep) diff --git a/isort/sorting.py b/isort/sorting.py index 3d3961367..cab77011b 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -64,7 +64,7 @@ def section_key( if reverse_relative and line.startswith("from ."): match = re.match(r"^from (\.+)\s*(.*)", line) - if match: + if match: # pragma: no cover - regex always matches if line starts with "from ." line = f"from {' '.join(match.groups())}" if group_by_package and line.strip().startswith("from"): line = line.split(" import", 1)[0] From c92b7608c3f7b40e991a70388e128a20eed42eb8 Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Mon, 5 Oct 2020 01:19:55 +0300 Subject: [PATCH 088/539] Fix for test_hooks.py Mock.call_args.args can be used only on Python 3.8, fails on 3.6 --- tests/unit/test_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_hooks.py b/tests/unit/test_hooks.py index ddaff0e25..2757f414f 100644 --- a/tests/unit/test_hooks.py +++ b/tests/unit/test_hooks.py @@ -51,7 +51,7 @@ def test_git_hook(src_dir): with patch("isort.api.sort_file", MagicMock(return_value=False)) as api_mock: hooks.git_hook(modify=True) api_mock.assert_called_once() - assert api_mock.call_args.args[0] == mock_main_py.return_value[0] + assert api_mock.call_args[0][0] == mock_main_py.return_value[0] # Test with sorted file returned from git and modify=False with patch("isort.hooks.get_lines", mock_main_py): From bdd1ebf0b50d8b0e8fc43a624db73966319c8ef6 Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Mon, 5 Oct 2020 01:37:40 +0300 Subject: [PATCH 089/539] Fix test failures on Windows. --- tests/unit/test_api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index baec283a1..e888b24e7 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -1,5 +1,6 @@ """Tests the isort API module""" from io import StringIO +import os from unittest.mock import MagicMock, patch import pytest @@ -8,8 +9,8 @@ from isort.settings import Config imperfect_content = "import b\nimport a\n" -fixed_content = "import a\nimport b\n" -fixed_diff = "\n+import a\n import b\n-import a\n" +fixed_content = "import a\nimport b\n".replace('\n', os.linesep) +fixed_diff = "\n+import a\n import b\n-import a\n".replace ('\n', os.linesep) @pytest.fixture From 7bd1511a7daf5dadc1234faef4cc88c8f7ec61cd Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Mon, 5 Oct 2020 01:42:52 +0300 Subject: [PATCH 090/539] Linting. --- tests/unit/test_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index e888b24e7..920890568 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -1,6 +1,6 @@ """Tests the isort API module""" -from io import StringIO import os +from io import StringIO from unittest.mock import MagicMock, patch import pytest @@ -9,8 +9,8 @@ from isort.settings import Config imperfect_content = "import b\nimport a\n" -fixed_content = "import a\nimport b\n".replace('\n', os.linesep) -fixed_diff = "\n+import a\n import b\n-import a\n".replace ('\n', os.linesep) +fixed_content = "import a\nimport b\n".replace("\n", os.linesep) +fixed_diff = "\n+import a\n import b\n-import a\n".replace("\n", os.linesep) @pytest.fixture From fcd020695c95647d77e57d508fb905d6d0cab13b Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Mon, 5 Oct 2020 02:10:19 +0300 Subject: [PATCH 091/539] Another go at fixing the WIndows tests. --- tests/unit/test_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 920890568..c60b19f8d 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -9,8 +9,8 @@ from isort.settings import Config imperfect_content = "import b\nimport a\n" -fixed_content = "import a\nimport b\n".replace("\n", os.linesep) -fixed_diff = "\n+import a\n import b\n-import a\n".replace("\n", os.linesep) +fixed_content = "import a\nimport b\n" +fixed_diff = "\n+import a\n import b\n-import a\n" @pytest.fixture @@ -37,7 +37,7 @@ def test_sort_file(imperfect) -> None: def test_sort_file_to_stdout(capsys, imperfect) -> None: assert api.sort_file(imperfect, write_to_stdout=True) out, _ = capsys.readouterr() - assert out == fixed_content + assert out == fixed_content.replace("\n", os.linesep) def test_other_ask_to_apply(imperfect) -> None: From fa65b2d2500692d7d3ff97a9805d0ee56b917a50 Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Mon, 5 Oct 2020 02:20:28 +0300 Subject: [PATCH 092/539] Another go at fixing the Windows tests. --- tests/unit/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index c60b19f8d..1235626ed 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -64,7 +64,7 @@ def test_check_file_no_changes(capsys, tmpdir) -> None: def test_check_file_with_changes(capsys, imperfect) -> None: assert not api.check_file(imperfect, show_diff=True) out, _ = capsys.readouterr() - assert fixed_diff in out + assert fixed_diff.replace("\n", os.linesep) in out def test_sorted_imports_multiple_configs() -> None: From 4085dd5f1bc79dce8c2c3116ca1e6f36c15a43e3 Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Mon, 5 Oct 2020 02:31:14 +0300 Subject: [PATCH 093/539] And another go to fix the Windows tests. ... it is getting late :-) --- tests/unit/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 1235626ed..3d257e705 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -10,7 +10,7 @@ imperfect_content = "import b\nimport a\n" fixed_content = "import a\nimport b\n" -fixed_diff = "\n+import a\n import b\n-import a\n" +fixed_diff = "+import a\n import b\n-import a\n" @pytest.fixture From 6c2cfdbc06164d60473b543eefbded99fdb4dcc3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 4 Oct 2020 16:43:10 -0700 Subject: [PATCH 094/539] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a7e9f966..a33d78fc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,10 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1482: pylama integration is not working correctly out-of-the-box. - Fixed #1492: --check does not work with stdin source. - Fixed #1499: isort gets confused by single line, multi-line style comments when using float-to-top. + - Fixed #1525: Some warnings can't be disabled with --quiet. Potentially breaking changes: - Fixed #1486: "Google" profile is not quite Google style. + - Fixed "PyCharm" profile to always add 2 lines to be consistent with what PyCharm "Optimize Imports" does. Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1472: Full testing of stdin CLI Options From 080a9038ee4ccefdf6b70800c10e40836e4015cc Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 4 Oct 2020 17:08:46 -0700 Subject: [PATCH 095/539] Add failing test for #1523 --- tests/unit/test_regressions.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 757595224..3a1d94e06 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1273,3 +1273,14 @@ def d(): ''', show_diff=True, ) + + +def test_isort_should_keep_all_as_and_non_as_imports_issue_1523(): + """isort should keep as and non-as imports of the same path that happen to exist within the + same statement. + See: https://github.com/PyCQA/isort/issues/1523. + """ + assert isort.check_code( + """ +from selenium.webdriver import Remote, Remote as Driver +""", show_diff=True, combine_as_imports=True) From 0971e635720ea3af4ecd2c18b8359dd30e2b8218 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 4 Oct 2020 21:08:15 -0700 Subject: [PATCH 096/539] Initial approach for fixing handling of as mixed with non as imports of same path --- isort/parse.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/isort/parse.py b/isort/parse.py index 9348267c3..d7e22a2d8 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -336,10 +336,10 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte item.replace("{|", "{ ").replace("|}", " }") for item in _strip_syntax(import_string).split() ] - straight_import = True + attach_comments_to: Optional[List[Any]] = None if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): - straight_import = False + straight_imports = set() while "as" in just_imports: nested_module = None as_index = just_imports.index("as") @@ -379,6 +379,8 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte module, [] ) del just_imports[as_index : as_index + 2] + else: + straight_imports = set(just_imports[1:]) if type_of_import == "from": import_from = just_imports.pop(0) From bacf2fd56c807e8a62305c2d15fe11e402e840f0 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 4 Oct 2020 23:20:11 -0700 Subject: [PATCH 097/539] Fix issue #1523: isort should keep all as and non as imports --- isort/parse.py | 13 ++++++++----- tests/unit/test_isort.py | 23 +++++++++++++++++++++++ tests/unit/test_regressions.py | 5 ++++- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/isort/parse.py b/isort/parse.py index d7e22a2d8..9a80e97b9 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -338,8 +338,10 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte ] attach_comments_to: Optional[List[Any]] = None + direct_imports = just_imports[1:] + straight_import = True if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): - straight_imports = set() + straight_import = False while "as" in just_imports: nested_module = None as_index = just_imports.index("as") @@ -348,6 +350,9 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte top_level_module = just_imports[0] module = top_level_module + "." + nested_module as_name = just_imports[as_index + 1] + direct_imports.remove(nested_module) + direct_imports.remove(as_name) + direct_imports.remove("as") if nested_module == as_name and config.remove_redundant_aliases: pass elif as_name not in as_map["from"][module]: @@ -379,8 +384,6 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte module, [] ) del just_imports[as_index : as_index + 2] - else: - straight_imports = set(just_imports[1:]) if type_of_import == "from": import_from = just_imports.pop(0) @@ -435,11 +438,11 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte if import_from not in root: root[import_from] = OrderedDict( - (module, straight_import) for module in just_imports + (module, module in direct_imports) for module in just_imports ) else: root[import_from].update( - (module, straight_import | root[import_from].get(module, False)) + (module, root[import_from].get(module, False) or module in direct_imports) for module in just_imports ) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 4598de8cd..c07d655d7 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -966,6 +966,29 @@ def test_check_newline_in_imports(capsys) -> None: out, _ = capsys.readouterr() assert "SUCCESS" in out + # if the verbose is only on modified outputs no output will be given + assert api.check_code_string( + code=test_input, + multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, + line_length=20, + verbose=True, + only_modified=True, + ) + out, _ = capsys.readouterr() + assert not out + + # we can make the input invalid to again see output + test_input = "from lib1 import (\n sub2,\n sub1,\n sub3\n)\n" + assert not api.check_code_string( + code=test_input, + multi_line_output=WrapModes.VERTICAL_HANGING_INDENT, + line_length=20, + verbose=True, + only_modified=True, + ) + out, _ = capsys.readouterr() + assert out + def test_forced_separate() -> None: """Ensure that forcing certain sub modules to show separately works as expected.""" diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 3a1d94e06..f334e4827 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1283,4 +1283,7 @@ def test_isort_should_keep_all_as_and_non_as_imports_issue_1523(): assert isort.check_code( """ from selenium.webdriver import Remote, Remote as Driver -""", show_diff=True, combine_as_imports=True) +""", + show_diff=True, + combine_as_imports=True, + ) From 8bad5b06e5bb1b5eabe8858ddc8622e48ed4db5d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 4 Oct 2020 23:29:49 -0700 Subject: [PATCH 098/539] Add #1523 to changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a33d78fc6..9734f518b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,9 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1492: --check does not work with stdin source. - Fixed #1499: isort gets confused by single line, multi-line style comments when using float-to-top. - Fixed #1525: Some warnings can't be disabled with --quiet. -Potentially breaking changes: + - Fixed #1523: in rare cases isort can ignore direct from import if as import is also on same line. + + Potentially breaking changes: - Fixed #1486: "Google" profile is not quite Google style. - Fixed "PyCharm" profile to always add 2 lines to be consistent with what PyCharm "Optimize Imports" does. From 82ff239e462338b89e1baa96d78f0f5efddab856 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 5 Oct 2020 01:27:43 -0700 Subject: [PATCH 099/539] Add @jugmac00's Products.ZopeTree to integration test suite --- tests/integration/test_projects_using_isort.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 2dd5ee62c..95f256b29 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -135,3 +135,8 @@ def test_pyramid(tmpdir): str(target_dir) for target_dir in (tmpdir / "src" / "pyramid", tmpdir / "tests", tmpdir / "setup.py") ) + + +def test_products_zopetree(tmpdir): + git_clone("https://github.com/jugmac00/Products.ZopeTree.git", tmpdir) + run_isort([str(tmpdir)]) From 49040889a94308c201e6c5a2c42d176cec8205c6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 5 Oct 2020 23:41:11 -0700 Subject: [PATCH 100/539] Add Hasan Ramezani (@hramezani) and Denis Veselov (@saippuakauppias) to acknowledgements. --- docs/contributing/4.-acknowledgements.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index f4588d763..0d8ef7b85 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -198,6 +198,7 @@ Code Contributors - Alexandre Yang (@AlexandreYang) - Andrew Howe (@howeaj) - Sang-Heon Jeon (@lntuition) +- Denis Veselov (@saippuakauppias) Documenters =================== @@ -220,6 +221,7 @@ Documenters - Marat Sharafutdinov (@decaz) - Abtin (@abtinmo) - @scottwedge +- Hasan Ramezani (@hramezani) -------------------------------------------- From d2954516ef4de49fd3ded305151a5bfb84827b1d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 5 Oct 2020 23:43:11 -0700 Subject: [PATCH 101/539] Add Aniruddha Bhattacharjee (@anirudnits) to core developer list --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 0d8ef7b85..9f0ccae89 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -1,5 +1,6 @@ Core Developers =================== +- Aniruddha Bhattacharjee (@anirudnits) - Jon Dufresne (@jdufresne) - Tamas Szabo (@sztamas) - Timothy Edmund Crosley (@timothycrosley) From e3dc4bdcd8d33bc2f2e1c390cd0c34a5071434b3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 6 Oct 2020 21:20:42 -0700 Subject: [PATCH 102/539] Add example namespace packages for testing --- tests/unit/example_projects/namespaces/implicit/.isort.cfg | 0 .../example_projects/namespaces/implicit/root/nested/__init__.py | 0 tests/unit/example_projects/namespaces/implicit/root/nested/x.py | 0 tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg | 0 .../example_projects/namespaces/pkg_resource/root/__init__.py | 1 + .../namespaces/pkg_resource/root/nested/__init__.py | 0 .../example_projects/namespaces/pkg_resource/root/nested/x.py | 0 tests/unit/example_projects/namespaces/pkgutil/.isort.cfg | 0 tests/unit/example_projects/namespaces/pkgutil/root/__init__.py | 1 + .../example_projects/namespaces/pkgutil/root/nested/__init__.py | 0 tests/unit/example_projects/namespaces/pkgutil/root/nested/x.py | 0 11 files changed, 2 insertions(+) create mode 100644 tests/unit/example_projects/namespaces/implicit/.isort.cfg create mode 100644 tests/unit/example_projects/namespaces/implicit/root/nested/__init__.py create mode 100644 tests/unit/example_projects/namespaces/implicit/root/nested/x.py create mode 100644 tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg create mode 100644 tests/unit/example_projects/namespaces/pkg_resource/root/__init__.py create mode 100644 tests/unit/example_projects/namespaces/pkg_resource/root/nested/__init__.py create mode 100644 tests/unit/example_projects/namespaces/pkg_resource/root/nested/x.py create mode 100644 tests/unit/example_projects/namespaces/pkgutil/.isort.cfg create mode 100644 tests/unit/example_projects/namespaces/pkgutil/root/__init__.py create mode 100644 tests/unit/example_projects/namespaces/pkgutil/root/nested/__init__.py create mode 100644 tests/unit/example_projects/namespaces/pkgutil/root/nested/x.py diff --git a/tests/unit/example_projects/namespaces/implicit/.isort.cfg b/tests/unit/example_projects/namespaces/implicit/.isort.cfg new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/implicit/root/nested/__init__.py b/tests/unit/example_projects/namespaces/implicit/root/nested/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/implicit/root/nested/x.py b/tests/unit/example_projects/namespaces/implicit/root/nested/x.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg b/tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/pkg_resource/root/__init__.py b/tests/unit/example_projects/namespaces/pkg_resource/root/__init__.py new file mode 100644 index 000000000..de40ea7ca --- /dev/null +++ b/tests/unit/example_projects/namespaces/pkg_resource/root/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/tests/unit/example_projects/namespaces/pkg_resource/root/nested/__init__.py b/tests/unit/example_projects/namespaces/pkg_resource/root/nested/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/pkg_resource/root/nested/x.py b/tests/unit/example_projects/namespaces/pkg_resource/root/nested/x.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/pkgutil/.isort.cfg b/tests/unit/example_projects/namespaces/pkgutil/.isort.cfg new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/pkgutil/root/__init__.py b/tests/unit/example_projects/namespaces/pkgutil/root/__init__.py new file mode 100644 index 000000000..69e3be50d --- /dev/null +++ b/tests/unit/example_projects/namespaces/pkgutil/root/__init__.py @@ -0,0 +1 @@ +__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/tests/unit/example_projects/namespaces/pkgutil/root/nested/__init__.py b/tests/unit/example_projects/namespaces/pkgutil/root/nested/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/pkgutil/root/nested/x.py b/tests/unit/example_projects/namespaces/pkgutil/root/nested/x.py new file mode 100644 index 000000000..e69de29bb From 4a7a1ef80f85b1abe155b5d4995a54ab171a9be7 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 02:43:48 -0700 Subject: [PATCH 103/539] Add a lack of namespace package example --- tests/unit/example_projects/namespaces/none/.isort.cfg | 0 tests/unit/example_projects/namespaces/none/root/__init__.py | 0 .../unit/example_projects/namespaces/none/root/nested/__init__.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/unit/example_projects/namespaces/none/.isort.cfg create mode 100644 tests/unit/example_projects/namespaces/none/root/__init__.py create mode 100644 tests/unit/example_projects/namespaces/none/root/nested/__init__.py diff --git a/tests/unit/example_projects/namespaces/none/.isort.cfg b/tests/unit/example_projects/namespaces/none/.isort.cfg new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/none/root/__init__.py b/tests/unit/example_projects/namespaces/none/root/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/none/root/nested/__init__.py b/tests/unit/example_projects/namespaces/none/root/nested/__init__.py new file mode 100644 index 000000000..e69de29bb From 786d9209ab1fb3ade9ab24303f0038e5760fb260 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 02:44:34 -0700 Subject: [PATCH 104/539] Add isortcfgs for namespace examples --- tests/unit/example_projects/namespaces/implicit/.isort.cfg | 2 ++ tests/unit/example_projects/namespaces/none/.isort.cfg | 2 ++ tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg | 2 ++ .../example_projects/namespaces/pkg_resource/root/__init__.py | 2 +- tests/unit/example_projects/namespaces/pkgutil/.isort.cfg | 2 ++ tests/unit/example_projects/namespaces/pkgutil/root/__init__.py | 2 +- 6 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/unit/example_projects/namespaces/implicit/.isort.cfg b/tests/unit/example_projects/namespaces/implicit/.isort.cfg index e69de29bb..d3ae4c35a 100644 --- a/tests/unit/example_projects/namespaces/implicit/.isort.cfg +++ b/tests/unit/example_projects/namespaces/implicit/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +src_paths=root diff --git a/tests/unit/example_projects/namespaces/none/.isort.cfg b/tests/unit/example_projects/namespaces/none/.isort.cfg index e69de29bb..d3ae4c35a 100644 --- a/tests/unit/example_projects/namespaces/none/.isort.cfg +++ b/tests/unit/example_projects/namespaces/none/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +src_paths=root diff --git a/tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg b/tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg index e69de29bb..d3ae4c35a 100644 --- a/tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg +++ b/tests/unit/example_projects/namespaces/pkg_resource/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +src_paths=root diff --git a/tests/unit/example_projects/namespaces/pkg_resource/root/__init__.py b/tests/unit/example_projects/namespaces/pkg_resource/root/__init__.py index de40ea7ca..5284146eb 100644 --- a/tests/unit/example_projects/namespaces/pkg_resource/root/__init__.py +++ b/tests/unit/example_projects/namespaces/pkg_resource/root/__init__.py @@ -1 +1 @@ -__import__('pkg_resources').declare_namespace(__name__) +__import__("pkg_resources").declare_namespace(__name__) diff --git a/tests/unit/example_projects/namespaces/pkgutil/.isort.cfg b/tests/unit/example_projects/namespaces/pkgutil/.isort.cfg index e69de29bb..d3ae4c35a 100644 --- a/tests/unit/example_projects/namespaces/pkgutil/.isort.cfg +++ b/tests/unit/example_projects/namespaces/pkgutil/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +src_paths=root diff --git a/tests/unit/example_projects/namespaces/pkgutil/root/__init__.py b/tests/unit/example_projects/namespaces/pkgutil/root/__init__.py index 69e3be50d..8db66d3d0 100644 --- a/tests/unit/example_projects/namespaces/pkgutil/root/__init__.py +++ b/tests/unit/example_projects/namespaces/pkgutil/root/__init__.py @@ -1 +1 @@ -__path__ = __import__('pkgutil').extend_path(__path__, __name__) +__path__ = __import__("pkgutil").extend_path(__path__, __name__) From 81dbbba88c39c365dedcea9e1f06ca9349c0c679 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 03:12:16 -0700 Subject: [PATCH 105/539] Add test cases for intended placement support of namespace packages --- tests/unit/conftest.py | 5 +++++ tests/unit/test_place.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 09e4acbdb..1e0bd9dfa 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -26,3 +26,8 @@ def test_path(): @pytest.fixture def src_path(): return Path(SRC_DIR).resolve() + + +@pytest.fixture +def examples_path(): + return Path(TEST_DIR).resolve() / "example_projects" diff --git a/tests/unit/test_place.py b/tests/unit/test_place.py index b1159471f..b2cc28d42 100644 --- a/tests/unit/test_place.py +++ b/tests/unit/test_place.py @@ -27,3 +27,32 @@ def test_no_standard_library_placement(): "pathlib", config=Config(sections=["THIRDPARTY"], default_section="THIRDPARTY") ) == ("THIRDPARTY", "Default option in Config or universal default.") assert place.module("pathlib") == "STDLIB" + + +def test_namespace_package_placement(examples_path): + namespace_examples = examples_path / "namespaces" + + implicit = namespace_examples / "implicit" + pkg_resource = namespace_examples / "pkg_resource" + pkgutil = namespace_examples / "pkgutil" + for namespace_test in (implicit, pkg_resource, pkgutil): + print(namespace_test) + config = Config(settings_path=namespace_test) + no_namespaces = Config(settings_path=namespace_test, auto_identify_namespace_packages=False) + namespace_override = Config(settings_path=namespace_test, known_firstparty=["root.name"]) + assert place.module("root.name", config=config) == "THIRDPARTY" + assert place.module("root.nested", config=config) == "FIRSTPARTY" + assert place.module("root.name", config=no_namespaces) == "FIRSTPARTY" + assert place.module("root.name", config=namespace_override) == "FIRSTPARTY" + + no_namespace = namespace_examples / "none" + config = Config(settings_path=no_namespace) + manual_namespace = Config(settings_path=no_namespace, namespace_packages=["root"]) + assert place.module("root.name", config=config) == "FIRSTPARTY" + assert place.module("root.nested", config=config) == "FIRSTPARTY" + assert place.module("root.name", config=manual_namespace) == "THIRDPARTY" + assert place.module("root.nested", config=config) == "FIRSTPARTY" + + + + From f02dbf046917bafe1774bb9c15f9553be20f2e34 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 03:12:44 -0700 Subject: [PATCH 106/539] Add initial namespace support --- isort/place.py | 86 +++++++++++++++++++++++++++-------------------- isort/settings.py | 2 ++ 2 files changed, 51 insertions(+), 37 deletions(-) diff --git a/isort/place.py b/isort/place.py index fcb3dcbdd..8b80cd22e 100644 --- a/isort/place.py +++ b/isort/place.py @@ -3,7 +3,7 @@ from fnmatch import fnmatch from functools import lru_cache from pathlib import Path -from typing import Optional, Tuple +from typing import FrozenSet, Iterable, Optional, Tuple from isort import sections from isort.settings import DEFAULT_CONFIG, Config @@ -60,43 +60,32 @@ def _known_pattern(name: str, config: Config) -> Optional[Tuple[str, str]]: return None -def _src_path(name: str, config: Config) -> Optional[Tuple[str, str]]: - search_paths = zip(config.src_paths, config.src_paths) - for index, module_part in enumerate(name.split(".")): - if index == len(module_parts): - for search_path, src_path in search_paths: - module_path = (search_path / module_part).resolve() - if ( - _is_module(module_path) - or _is_package(module_path) - or _src_path_is_module(search_path, module_part) - ): - return (sections.FIRSTPARTY, f"Found in one of the configured src_paths: {src_path}.") - else: - new_search_paths = [] - for search_path, src_path in search_paths: - if - - search_paths = [search_path for search_path in search_paths if - _is_package((src_path / root_module_name).resolve()) or - index == 0 and _src_path_is_module - - for src_path in config.src_paths: - root_module_name = name.split(".")[0] - module_path = - if ( - or - or _src_path_is_module(src_path, root_module_name) - ): - return (sections.FIRSTPARTY, f"Found in one of the configured src_paths: {src_path}.") - - - for src_path in config.src_paths: - - for part in - root_module_name = name.split(".")[0] +def _src_path( + name: str, + config: Config, + src_paths: Optional[Iterable[Path]] = None, + prefix: Tuple[str, ...] = (), +) -> Optional[Tuple[str, str]]: + if src_paths is None: + src_paths = config.src_paths + + root_module_name, *nested_module = name.split(".", 1) + new_prefix = prefix + (root_module_name,) + namespace = ".".join(new_prefix) + + for src_path in src_paths: module_path = (src_path / root_module_name).resolve() - if ( + if not prefix and not module_path.is_dir() and src_path.name == root_module_name: + module_path = src_path.resolve() + if nested_module and ( + namespace in config.namespace_packages + or ( + config.auto_identify_namespace_packages + and _is_namespace_package(module_path, config.supported_extensions) + ) + ): + return _src_path(nested_module[0], config, (module_path,), new_prefix) + elif ( _is_module(module_path) or _is_package(module_path) or _src_path_is_module(src_path, root_module_name) @@ -121,6 +110,29 @@ def _is_package(path: Path) -> bool: return exists_case_sensitive(str(path)) and path.is_dir() +def _is_namespace_package(path: Path, src_extensions: FrozenSet[str]) -> bool: + if not _is_package(path): + return False + + init_file = path / "__init__.py" + if not init_file.exists(): + if [filename for filename in path.iterdir() if filename.suffix in src_extensions]: + return False + else: + with init_file.open("rb") as open_init_file: + file_start = open_init_file.read(4096) + if ( + b"__import__('pkg_resources').declare_namespace(__name__)\n" not in file_start + and b'__import__("pkg_resources").declare_namespace(__name__)\n' not in file_start + and b"__path__ = __import__('pkgutil').extend_path(__path__, __name__)" + not in file_start + and b'__path__ = __import__("pkgutil").extend_path(__path__, __name__)' + not in file_start + ): + return False + return True + + def _src_path_is_module(src_path: Path, module_name: str) -> bool: return ( module_name == src_path.name and src_path.is_dir() and exists_case_sensitive(str(src_path)) diff --git a/isort/settings.py b/isort/settings.py index a89f6d69f..e80a989dc 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -200,6 +200,8 @@ class _Config: dedup_headings: bool = False only_sections: bool = False only_modified: bool = False + auto_identify_namespace_packages: bool = True + namespace_packages: FrozenSet[str] = frozenset() def __post_init__(self): py_version = self.py_version From dd22f8ccf22c0059e9b88e2bb2cf2c313828d95b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 03:14:24 -0700 Subject: [PATCH 107/539] Mark Fixed #1443 in changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9734f518b..4bb62f3c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1523: in rare cases isort can ignore direct from import if as import is also on same line. Potentially breaking changes: + - Fixed #1443: Incorrect third vs first party categorization - namespace packages. - Fixed #1486: "Google" profile is not quite Google style. - Fixed "PyCharm" profile to always add 2 lines to be consistent with what PyCharm "Optimize Imports" does. From a9fc7b2caca15c1b5f9a9cf036aae356f19c616a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 03:17:26 -0700 Subject: [PATCH 108/539] Add an almost implicit example of namespaces --- .../unit/example_projects/namespaces/almost-implicit/.isort.cfg | 2 ++ .../namespaces/almost-implicit/root/nested/__init__.py | 0 .../namespaces/almost-implicit/root/nested/x.py | 0 tests/unit/example_projects/namespaces/almost-implicit/y.py | 0 4 files changed, 2 insertions(+) create mode 100644 tests/unit/example_projects/namespaces/almost-implicit/.isort.cfg create mode 100644 tests/unit/example_projects/namespaces/almost-implicit/root/nested/__init__.py create mode 100644 tests/unit/example_projects/namespaces/almost-implicit/root/nested/x.py create mode 100644 tests/unit/example_projects/namespaces/almost-implicit/y.py diff --git a/tests/unit/example_projects/namespaces/almost-implicit/.isort.cfg b/tests/unit/example_projects/namespaces/almost-implicit/.isort.cfg new file mode 100644 index 000000000..d3ae4c35a --- /dev/null +++ b/tests/unit/example_projects/namespaces/almost-implicit/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +src_paths=root diff --git a/tests/unit/example_projects/namespaces/almost-implicit/root/nested/__init__.py b/tests/unit/example_projects/namespaces/almost-implicit/root/nested/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/almost-implicit/root/nested/x.py b/tests/unit/example_projects/namespaces/almost-implicit/root/nested/x.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/example_projects/namespaces/almost-implicit/y.py b/tests/unit/example_projects/namespaces/almost-implicit/y.py new file mode 100644 index 000000000..e69de29bb From 23933cbdb75339c3f6460cd535c1e4ec7e1a2d7e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 03:28:26 -0700 Subject: [PATCH 109/539] Finalize built in namespace support tests --- isort/place.py | 5 ++++- .../namespaces/almost-implicit/y.py | 0 tests/unit/test_place.py | 20 +++++++++---------- 3 files changed, 13 insertions(+), 12 deletions(-) delete mode 100644 tests/unit/example_projects/namespaces/almost-implicit/y.py diff --git a/isort/place.py b/isort/place.py index 8b80cd22e..e9909b599 100644 --- a/isort/place.py +++ b/isort/place.py @@ -116,7 +116,10 @@ def _is_namespace_package(path: Path, src_extensions: FrozenSet[str]) -> bool: init_file = path / "__init__.py" if not init_file.exists(): - if [filename for filename in path.iterdir() if filename.suffix in src_extensions]: + filenames = [ + filename for filename in path.iterdir() if filename.suffix.lstrip(".") in src_extensions + ] + if filenames: return False else: with init_file.open("rb") as open_init_file: diff --git a/tests/unit/example_projects/namespaces/almost-implicit/y.py b/tests/unit/example_projects/namespaces/almost-implicit/y.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/unit/test_place.py b/tests/unit/test_place.py index b2cc28d42..18ac7fccd 100644 --- a/tests/unit/test_place.py +++ b/tests/unit/test_place.py @@ -31,7 +31,7 @@ def test_no_standard_library_placement(): def test_namespace_package_placement(examples_path): namespace_examples = examples_path / "namespaces" - + implicit = namespace_examples / "implicit" pkg_resource = namespace_examples / "pkg_resource" pkgutil = namespace_examples / "pkgutil" @@ -46,13 +46,11 @@ def test_namespace_package_placement(examples_path): assert place.module("root.name", config=namespace_override) == "FIRSTPARTY" no_namespace = namespace_examples / "none" - config = Config(settings_path=no_namespace) - manual_namespace = Config(settings_path=no_namespace, namespace_packages=["root"]) - assert place.module("root.name", config=config) == "FIRSTPARTY" - assert place.module("root.nested", config=config) == "FIRSTPARTY" - assert place.module("root.name", config=manual_namespace) == "THIRDPARTY" - assert place.module("root.nested", config=config) == "FIRSTPARTY" - - - - + almost_implicit = namespace_examples / "almost-implicit" + for lacks_namespace in (no_namespace, almost_implicit): + config = Config(settings_path=lacks_namespace) + manual_namespace = Config(settings_path=lacks_namespace, namespace_packages=["root"]) + assert place.module("root.name", config=config) == "FIRSTPARTY" + assert place.module("root.nested", config=config) == "FIRSTPARTY" + assert place.module("root.name", config=manual_namespace) == "THIRDPARTY" + assert place.module("root.nested", config=config) == "FIRSTPARTY" From 8933c8a5c1855c7ba3745404fe60feb478902a3f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 03:33:20 -0700 Subject: [PATCH 110/539] Add missing file --- tests/unit/example_projects/namespaces/almost-implicit/root/y.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/unit/example_projects/namespaces/almost-implicit/root/y.py diff --git a/tests/unit/example_projects/namespaces/almost-implicit/root/y.py b/tests/unit/example_projects/namespaces/almost-implicit/root/y.py new file mode 100644 index 000000000..e69de29bb From 2aab8d1b9bc59afc1227291e9ec8ea6a562593fa Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 03:37:12 -0700 Subject: [PATCH 111/539] Address deepsource issue --- isort/place.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/place.py b/isort/place.py index e9909b599..f83252612 100644 --- a/isort/place.py +++ b/isort/place.py @@ -85,7 +85,7 @@ def _src_path( ) ): return _src_path(nested_module[0], config, (module_path,), new_prefix) - elif ( + if ( _is_module(module_path) or _is_package(module_path) or _src_path_is_module(src_path, root_module_name) From 466872adffb8cf9df4140f71c87bf9cc37b03d34 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 03:46:36 -0700 Subject: [PATCH 112/539] Try different way to introspect __init__ file --- isort/place.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/isort/place.py b/isort/place.py index f83252612..c85cf83b6 100644 --- a/isort/place.py +++ b/isort/place.py @@ -122,14 +122,14 @@ def _is_namespace_package(path: Path, src_extensions: FrozenSet[str]) -> bool: if filenames: return False else: - with init_file.open("rb") as open_init_file: + with init_file.open() as open_init_file: file_start = open_init_file.read(4096) if ( - b"__import__('pkg_resources').declare_namespace(__name__)\n" not in file_start - and b'__import__("pkg_resources").declare_namespace(__name__)\n' not in file_start - and b"__path__ = __import__('pkgutil').extend_path(__path__, __name__)" + "__import__('pkg_resources').declare_namespace(__name__)" not in file_start + and '__import__("pkg_resources").declare_namespace(__name__)' not in file_start + and "__path__ = __import__('pkgutil').extend_path(__path__, __name__)" not in file_start - and b'__path__ = __import__("pkgutil").extend_path(__path__, __name__)' + and '__path__ = __import__("pkgutil").extend_path(__path__, __name__)' not in file_start ): return False From d6b69e9be396f1f28d468fa0c7e60f10fb094fbb Mon Sep 17 00:00:00 2001 From: James Curtin Date: Wed, 7 Oct 2020 09:06:53 -0400 Subject: [PATCH 113/539] Add documentation for Github Actions --- docs/configuration/github_action.md | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 docs/configuration/github_action.md diff --git a/docs/configuration/github_action.md b/docs/configuration/github_action.md new file mode 100644 index 000000000..c7a8dc971 --- /dev/null +++ b/docs/configuration/github_action.md @@ -0,0 +1,63 @@ +# Github Action + +isort provides an official [Github Action][github-action-docs] that can be used as part of a CI/CD workflow to ensure a project's imports are properly sorted. +The action can be found on the [Github Actions Marketplace][python-isort]. + +## Usage + +The `python-isort` plugin is designed to be run in combination with the [`checkout`][checkout-action] and [`setup-python`][setup-python] actions. +By default, it will run recursively from the root of the repository being linted and will exit with an error if the code is not properly sorted. + +### Inputs + +#### `isortVersion` + +Optional. Version of `isort` to use. Defaults to latest version of `isort`. + +#### `sortPaths` + +Optional. List of paths to sort, relative to your project root. Defaults to `.` + +#### `configuration` + +Optional. `isort` configuration options to pass to the `isort` CLI. Defaults to `--check-only --diff`. + +#### `requirementsFiles` + +Optional. Paths to python requirements files to install before running isort. +If multiple requirements files are provided, they should be separated by a space. +If custom package installation is required, dependencies should be installed in a separate step before using this action. + +!!! tip + It is important that the project's dependencies be installed before running isort so that third-party libraries are properly sorted. + +### Outputs + +#### `isort-result` + +Output of the `isort` CLI. + +### Example usage + +```yaml +name: Run isort +on: + - push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: 3.8 + - uses: jamescurtin/isort-action@master + with: + requirementsFiles: "requirements.txt requirements-test.txt" +``` + +[github-action-docs]: https://docs.github.com/en/free-pro-team@latest/actions +[python-isort]: https://github.com/marketplace/actions/python-isort +[checkout-action]: https://github.com/actions/checkout +[setup-python]: https://github.com/actions/setup-python From c8d37de3d11b03652e4f096d75e6b65dfb45b663 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 21:35:01 -0700 Subject: [PATCH 114/539] Fixed #1539: In extremely rare cases isort 5.5.4 introduces syntax error by removing closing paren. --- CHANGELOG.md | 5 +++- isort/_version.py | 2 +- isort/core.py | 5 ++-- pyproject.toml | 2 +- tests/unit/test_regressions.py | 49 ++++++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c5ee4d73..a46e8e92a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,12 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). +### 5.5.5 [Hotfix] October 7, 2020 + - Fixed #1539: isort 5.5.4 introduces syntax error by removing closing paren. + ### 5.5.4 [Hotfix] September 29, 2020 - Fixed #1507: in rare cases isort changes the content of multiline strings after a yield statement. - - Fixed #1505: Support case where known_SECTION points to a section not listed in sections. + - Fixed #1505: Support case where known_SECTION points to a section not listed in sections. ### 5.5.3 [Hotfix] September 20, 2020 - Fixed #1488: in rare cases isort can mangle `yield from` or `raise from` statements. diff --git a/isort/_version.py b/isort/_version.py index e82c92eb4..3a831202b 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.5.4" +__version__ = "5.5.5" diff --git a/isort/core.py b/isort/core.py index 696e5d751..558fc2adc 100644 --- a/isort/core.py +++ b/isort/core.py @@ -153,6 +153,7 @@ def process( in_top_comment = False first_comment_index_end = index - 1 + was_in_quote = bool(in_quote) if (not stripped_line.startswith("#") or in_quote) and '"' in line or "'" in line: char_index = 0 if first_comment_index_start == -1 and ( @@ -178,8 +179,8 @@ def process( break char_index += 1 - not_imports = bool(in_quote) or in_top_comment or isort_off - if not (in_quote or in_top_comment): + not_imports = bool(in_quote) or was_in_quote or in_top_comment or isort_off + if not (in_quote or was_in_quote or in_top_comment): if isort_off: if stripped_line == "# isort: on": isort_off = False diff --git a/pyproject.toml b/pyproject.toml index 75fb08633..e9b1fdc49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.5.4" +version = "5.5.5" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 78888c409..09d4302d1 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1121,3 +1121,52 @@ def d(): ''', show_diff=True, ) + + +def test_isort_shouldnt_introduce_syntax_error_issue_1539(): + """isort should NEVER introduce syntax errors. + In 5.5.4 some strings that contained a line starting with from could lead to no empty paren. + See: https://github.com/PyCQA/isort/issues/1539. + """ + assert isort.check_code( + '''"""Foobar + from {}""".format( + "bar", +) +''', + show_diff=True, + ) + assert isort.check_code( + '''"""Foobar + import {}""".format( + "bar", +) +''', + show_diff=True, + ) + assert ( + isort.code( + '''"""Foobar + from {}""" + from a import b, a +''', + ) + == '''"""Foobar + from {}""" + from a import a, b +''' + ) + assert ( + isort.code( + '''"""Foobar + from {}""" + import b + import a +''', + ) + == '''"""Foobar + from {}""" + import a + import b +''' + ) From 4691317a2b6b03e521004d8b824a7f22129dc09f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 21:36:37 -0700 Subject: [PATCH 115/539] Emphasis rarity of issue --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a46e8e92a..a512acbb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). ### 5.5.5 [Hotfix] October 7, 2020 - - Fixed #1539: isort 5.5.4 introduces syntax error by removing closing paren. + - Fixed #1539: in extremely rare cases isort 5.5.4 introduces syntax error by removing closing paren. ### 5.5.4 [Hotfix] September 29, 2020 - Fixed #1507: in rare cases isort changes the content of multiline strings after a yield statement. From 092bdb15126710d285f160d84f1c883b3aee9b51 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 23:35:49 -0700 Subject: [PATCH 116/539] Implemented #1540: Officially support Python 3.9 stdlib imports by default. --- CHANGELOG.md | 1 + isort/stdlibs/py3.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2869cbab..dcafa5347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1523: in rare cases isort can ignore direct from import if as import is also on same line. Potentially breaking changes: + - Implemented #1540: Officially support Python 3.9 stdlib imports by default. - Fixed #1443: Incorrect third vs first party categorization - namespace packages. - Fixed #1486: "Google" profile is not quite Google style. - Fixed "PyCharm" profile to always add 2 lines to be consistent with what PyCharm "Optimize Imports" does. diff --git a/isort/stdlibs/py3.py b/isort/stdlibs/py3.py index 78e0984d5..b7dbcc2cb 100644 --- a/isort/stdlibs/py3.py +++ b/isort/stdlibs/py3.py @@ -1,3 +1,3 @@ -from . import py35, py36, py37, py38 +from . import py35, py36, py37, py38, py39 -stdlib = py35.stdlib | py36.stdlib | py37.stdlib | py38.stdlib +stdlib = py35.stdlib | py36.stdlib | py37.stdlib | py38.stdlib | py39.stdlib From 6930fb277ed135f3c2a0fbd928c4a8d6bf803447 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 23:37:38 -0700 Subject: [PATCH 117/539] Finalize 5.6.0 changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcafa5347..44e5fd899 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). -### 5.6.0 TBD +### 5.6.0 October 7, 2020 - Implemented #1433: Provide helpful feedback in case a custom config file is specified without a configuration. - Implemented #1494: Default to sorting imports within `.pxd` files. - Implemented #1502: Improved float-to-top behavior when there is an existing import section present at top-of-file. @@ -27,6 +27,8 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1472: Full testing of stdin CLI Options + - Added additional branch coverage. + - More projects added to integration test suite. ### 5.5.5 [Hotfix] October 7, 2020 - Fixed #1539: in extremely rare cases isort 5.5.4 introduces syntax error by removing closing paren. From 9b260bb079dca3d3d2f42935037f0c5092fb429d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 23:38:58 -0700 Subject: [PATCH 118/539] Bump version to 5.6.0 --- isort/_version.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/_version.py b/isort/_version.py index 3a831202b..a1e2271ea 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.5.5" +__version__ = "5.6.0" diff --git a/pyproject.toml b/pyproject.toml index 45fd6fc0f..8831e38c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.5.5" +version = "5.6.0" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" From f0b7ec9d694821b0a92e7769b1341e7a6ca2c47d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 23:49:24 -0700 Subject: [PATCH 119/539] Test against 3.9 --- .github/workflows/integration.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 283103118..beefbf5a9 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.8] + python-version: [3.9] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef93a1eea..bd46ab7b8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.6, 3.7, 3.8] + python-version: [3.6, 3.7, 3.8, 3.9] os: [ubuntu-latest, ubuntu-18.04, macos-latest, windows-latest] steps: From 2837927fb3f31ef11394617b4475fdb0799a5f73 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 23:52:07 -0700 Subject: [PATCH 120/539] Revert "Test against 3.9" This reverts commit f0b7ec9d694821b0a92e7769b1341e7a6ca2c47d. --- .github/workflows/integration.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index beefbf5a9..283103118 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.9] + python-version: [3.8] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bd46ab7b8..ef93a1eea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: [3.6, 3.7, 3.8] os: [ubuntu-latest, ubuntu-18.04, macos-latest, windows-latest] steps: From 9ed3c03a81414a2ced94991c4c66f18d2ee422db Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Oct 2020 23:58:24 -0700 Subject: [PATCH 121/539] Improve changelog formatting --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e5fd899..595033f32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,13 +19,13 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1525: Some warnings can't be disabled with --quiet. - Fixed #1523: in rare cases isort can ignore direct from import if as import is also on same line. - Potentially breaking changes: +#### Potentially breaking changes: - Implemented #1540: Officially support Python 3.9 stdlib imports by default. - Fixed #1443: Incorrect third vs first party categorization - namespace packages. - Fixed #1486: "Google" profile is not quite Google style. - Fixed "PyCharm" profile to always add 2 lines to be consistent with what PyCharm "Optimize Imports" does. - Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): +#### Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1472: Full testing of stdin CLI Options - Added additional branch coverage. - More projects added to integration test suite. @@ -66,12 +66,12 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1405: Added support for coloring diff output. - Implemented #1434: New multi-line grid mode without parentheses. -Goal Zero (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): +#### Goal Zero (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1392: Extensive profile testing. - Implemented #1393: Proprety based testing applied to code snippets. - Implemented #1391: Create automated integration test that includes full code base of largest OpenSource isort users. -Potentially breaking changes: +#### Potentially breaking changes: - Fixed #1429: --check doesn't print to stderr as the documentation says. This means if you were looking for `ERROR:` messages for files that contain incorrect imports within stdout you will now need to look in stderr. ### 5.4.2 Aug 14, 2020 @@ -106,7 +106,7 @@ Potentially breaking changes: - Fixed #1366: spurious errors when combining skip with --gitignore. - Fixed #1359: --skip-gitignore does not honor ignored symlink -Internal Development: +#### Internal Development: - Initial hypothesmith powered test to help catch unexpected syntax parsing and output errors (thanks @Zac-HD!) ### 5.2.2 July 30, 2020 @@ -158,7 +158,7 @@ Internal Development: - Fixed #1315: Extra newline before comment with -n + --fss. - Fixed #1192: `-k` or `--keep-direct-and-as-imports` option has been deprecated as it is now always on. -**Formatting changes implied:** +#### Formatting changes implied: - Fixed #1280: rewrite of as imports changes the behavior of the imports. ### 5.0.9 July 11, 2020 From 9c0ff4952bc3a2e8dc605e88815c6e5662b81f8d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 8 Oct 2020 05:11:22 -0700 Subject: [PATCH 122/539] Hotfix: Fixed #1546: Unstable (non-idempotent) behavior with certain src trees. --- CHANGELOG.md | 3 +++ isort/_version.py | 2 +- isort/place.py | 5 ++++- isort/settings.py | 14 +++++++++----- pyproject.toml | 2 +- tests/integration/test_projects_using_isort.py | 5 +++++ 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 595033f32..02bb467f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). +### 5.6.1 [Hotfix] October 8, 2020 + - Fixed #1546: Unstable (non-idempotent) behavior with certain src trees. + ### 5.6.0 October 7, 2020 - Implemented #1433: Provide helpful feedback in case a custom config file is specified without a configuration. - Implemented #1494: Default to sorting imports within `.pxd` files. diff --git a/isort/_version.py b/isort/_version.py index a1e2271ea..a23647501 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.6.0" +__version__ = "5.6.1" diff --git a/isort/place.py b/isort/place.py index c85cf83b6..aaf954aa8 100644 --- a/isort/place.py +++ b/isort/place.py @@ -117,7 +117,10 @@ def _is_namespace_package(path: Path, src_extensions: FrozenSet[str]) -> bool: init_file = path / "__init__.py" if not init_file.exists(): filenames = [ - filename for filename in path.iterdir() if filename.suffix.lstrip(".") in src_extensions + filepath + for filepath in path.iterdir() + if filepath.suffix.lstrip(".") in src_extensions + or filepath.name.lower() in ("setup.cfg", "pyproject.toml") ] if filenames: return False diff --git a/isort/settings.py b/isort/settings.py index e80a989dc..158aa36ca 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -182,7 +182,7 @@ class _Config: directory: str = "" profile: str = "" honor_noqa: bool = False - src_paths: FrozenSet[Path] = frozenset() + src_paths: Tuple[Path, ...] = tuple() old_finders: bool = False remove_redundant_aliases: bool = False float_to_top: bool = False @@ -401,11 +401,15 @@ def __init__( path_root = Path(combined_config.get("directory", project_root)).resolve() path_root = path_root if path_root.is_dir() else path_root.parent if "src_paths" not in combined_config: - combined_config["src_paths"] = frozenset((path_root, path_root / "src")) + combined_config["src_paths"] = (path_root / "src", path_root) else: - combined_config["src_paths"] = frozenset( - path_root / path for path in combined_config.get("src_paths", ()) - ) + src_paths: List[Path] = [] + for src_path in combined_config.get("src_paths", ()): + full_path = path_root / src_path + if full_path not in src_paths: + src_paths.append(full_path) + + combined_config["src_paths"] = tuple(src_paths) if "formatter" in combined_config: import pkg_resources diff --git a/pyproject.toml b/pyproject.toml index 8831e38c2..81d45cc27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.6.0" +version = "5.6.1" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 95f256b29..d39b942b3 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -140,3 +140,8 @@ def test_pyramid(tmpdir): def test_products_zopetree(tmpdir): git_clone("https://github.com/jugmac00/Products.ZopeTree.git", tmpdir) run_isort([str(tmpdir)]) + + +def test_dobby(tmpdir): + git_clone("https://github.com/rocketDuck/dobby.git", tmpdir) + run_isort([str(tmpdir / "tests"), str(tmpdir / "src")]) From ba43b3cf2c182b3be20964d30b85ef6e7c82ee9f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 8 Oct 2020 05:14:49 -0700 Subject: [PATCH 123/539] Fix issue identified by deepsource: unnecessary call to tuple --- isort/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/settings.py b/isort/settings.py index 158aa36ca..96fc9410e 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -182,7 +182,7 @@ class _Config: directory: str = "" profile: str = "" honor_noqa: bool = False - src_paths: Tuple[Path, ...] = tuple() + src_paths: Tuple[Path, ...] = () old_finders: bool = False remove_redundant_aliases: bool = False float_to_top: bool = False From 49878c505ccfd6dceb73c12ead3459c50115a8b7 Mon Sep 17 00:00:00 2001 From: Marco Gorelli Date: Thu, 8 Oct 2020 13:42:38 +0100 Subject: [PATCH 124/539] Let pre-commit sort pxd files too In #1494, it looks like support for `.pxd` files was added. However, these are currently ignored when running isort in pre-commit because there is `types: [python]` --- .pre-commit-hooks.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 0027e49df..25b8325ab 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -3,5 +3,6 @@ entry: isort require_serial: true language: python - types: [python] + files: '.pxd$|.py$' + types: [file] args: ['--filter-files'] From 0179b07d680a624922bf75ee02c03551aab16f05 Mon Sep 17 00:00:00 2001 From: Marco Gorelli Date: Thu, 8 Oct 2020 13:44:43 +0100 Subject: [PATCH 125/539] remove unnecessary types file --- .pre-commit-hooks.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 25b8325ab..ad64de981 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -4,5 +4,4 @@ require_serial: true language: python files: '.pxd$|.py$' - types: [file] args: ['--filter-files'] From 245471d3dec8453d27a3c3c4895526a9cd49c647 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 8 Oct 2020 23:58:41 -0700 Subject: [PATCH 126/539] Fixed #1548: On rare occasions an unecessary empty line can be added when an import is marked as skipped. --- CHANGELOG.md | 3 ++ isort/core.py | 3 +- isort/parse.py | 20 ++++++-- tests/unit/test_regressions.py | 86 ++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02bb467f9..f861830c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). +### 5.6.2 TBD + - Fixed #1548: On rare occasions an unecessary empty line can be added when an import is marked as skipped. + ### 5.6.1 [Hotfix] October 8, 2020 - Fixed #1546: Unstable (non-idempotent) behavior with certain src trees. diff --git a/isort/core.py b/isort/core.py index 3131c2011..292bdc1c2 100644 --- a/isort/core.py +++ b/isort/core.py @@ -85,7 +85,8 @@ def process( isort_off = True if current: if add_imports: - current += line_separator + line_separator.join(add_imports) + add_line_separator = line_separator or "\n" + current += add_line_separator + add_line_separator.join(add_imports) add_imports = [] parsed = parse.file_contents(current, config=config) verbose_output += parsed.verbose_output diff --git a/isort/parse.py b/isort/parse.py index 9a80e97b9..96468f095 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -213,12 +213,22 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte and not lstripped_line.startswith("#") and not lstripped_line.startswith("'''") and not lstripped_line.startswith('"""') - and not lstripped_line.startswith("import") - and not lstripped_line.startswith("from") ): - import_index = index - 1 - while import_index and not in_lines[import_index - 1]: - import_index -= 1 + if not lstripped_line.startswith("import") and not lstripped_line.startswith("from"): + import_index = index - 1 + while import_index and not in_lines[import_index - 1]: + import_index -= 1 + elif "isort:skip" in line or "isort: skip" in line: + commentless = line.split("#", 1)[0] + if ( + "(" in commentless + and not commentless.rstrip().endswith(")") + and import_index < line_count + ): + import_index = index + while import_index < line_count and not commentless.rstrip().endswith(")"): + commentless = in_lines[import_index].split("#", 1)[0] + import_index += 1 line, *end_of_line_comment = line.split("#", 1) if ";" in line: diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 9f6f92de3..87b533d63 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1336,3 +1336,89 @@ def test_isort_shouldnt_introduce_syntax_error_issue_1539(): import b ''' ) + + +def test_isort_shouldnt_split_skip_issue_1548(): + """Ensure isort doesn't add a spurious new line if isort: skip is combined with float to top. + See: https://github.com/PyCQA/isort/issues/1548. + """ + assert isort.check_code( + """from tools.dependency_pruning.prune_dependencies import ( # isort:skip + prune_dependencies, +) +""", + show_diff=True, + profile="black", + float_to_top=True, + ) + assert isort.check_code( + """from tools.dependency_pruning.prune_dependencies import ( # isort:skip + prune_dependencies, +) +import a +import b +""", + show_diff=True, + profile="black", + float_to_top=True, + ) + assert isort.check_code( + """from tools.dependency_pruning.prune_dependencies import # isort:skip +import a +import b +""", + show_diff=True, + float_to_top=True, + ) + assert isort.check_code( + """from tools.dependency_pruning.prune_dependencies import ( # isort:skip + a +) +import b +""", + show_diff=True, + profile="black", + float_to_top=True, + ) + assert isort.check_code( + """from tools.dependency_pruning.prune_dependencies import ( # isort:skip + ) +""", + show_diff=True, + profile="black", + float_to_top=True, + ) + assert isort.check_code( + """from tools.dependency_pruning.prune_dependencies import ( # isort:skip +)""", + show_diff=True, + profile="black", + float_to_top=True, + ) + assert ( + isort.code( + """from tools.dependency_pruning.prune_dependencies import ( # isort:skip +) +""", + profile="black", + float_to_top=True, + add_imports=["import os"], + ) + == """from tools.dependency_pruning.prune_dependencies import ( # isort:skip +) +import os +""" + ) + assert ( + isort.code( + """from tools.dependency_pruning.prune_dependencies import ( # isort:skip +)""", + profile="black", + float_to_top=True, + add_imports=["import os"], + ) + == """from tools.dependency_pruning.prune_dependencies import ( # isort:skip +) +import os +""" + ) From b66853ff81b02bada800f9222905e45fa4871b28 Mon Sep 17 00:00:00 2001 From: Timothy Edmund Crosley Date: Fri, 9 Oct 2020 00:12:48 -0700 Subject: [PATCH 127/539] Update .pre-commit-hooks.yaml Use types, not extensions --- .pre-commit-hooks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index ad64de981..6bfb2bba9 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -3,5 +3,5 @@ entry: isort require_serial: true language: python - files: '.pxd$|.py$' + types: [python, cython, pyi] args: ['--filter-files'] From d5afc93770437eb0048718bfe676da9bfb187dc6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 9 Oct 2020 03:59:48 -0700 Subject: [PATCH 128/539] Add zope to integration test suite --- tests/integration/test_projects_using_isort.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index d39b942b3..34540cbba 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -145,3 +145,8 @@ def test_products_zopetree(tmpdir): def test_dobby(tmpdir): git_clone("https://github.com/rocketDuck/dobby.git", tmpdir) run_isort([str(tmpdir / "tests"), str(tmpdir / "src")]) + + +def test_zope(tmpdir): + git_clone("https://github.com/zopefoundation/Zope.git", tmpdir) + run_isort([str(tmpdir)]) From 8b9e45e18320d395152d3ea49b4d4071ed5312a3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 10 Oct 2020 02:52:33 -0700 Subject: [PATCH 129/539] Fixed #1542: Bug in VERTICAL_PREFIX_FROM_MODULE_IMPORT wrap mode. --- CHANGELOG.md | 1 + isort/wrap_modes.py | 34 +++++++++++++++++++++------------- tests/unit/test_regressions.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f861830c3..e774eaad6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.6.2 TBD - Fixed #1548: On rare occasions an unecessary empty line can be added when an import is marked as skipped. + - Fixed #1542: Bug in VERTICAL_PREFIX_FROM_MODULE_IMPORT wrap mode. ### 5.6.1 [Hotfix] October 8, 2020 - Fixed #1546: Unstable (non-idempotent) behavior with certain src trees. diff --git a/isort/wrap_modes.py b/isort/wrap_modes.py index ccfc1cd5a..db89d0ecc 100644 --- a/isort/wrap_modes.py +++ b/isort/wrap_modes.py @@ -273,33 +273,41 @@ def vertical_hanging_indent_bracket(**interface): def vertical_prefix_from_module_import(**interface): if not interface["imports"]: return "" + prefix_statement = interface["statement"] - interface["statement"] += interface["imports"].pop(0) - while interface["imports"]: - next_import = interface["imports"].pop(0) - next_statement = isort.comments.add_to_line( - interface["comments"], - interface["statement"] + ", " + next_import, + output_statement = prefix_statement + interface["imports"].pop(0) + comments = interface["comments"] + + statement = output_statement + statement_with_comments = "" + for next_import in interface["imports"]: + statement = statement + ", " + next_import + statement_with_comments = isort.comments.add_to_line( + comments, + statement, removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) if ( - len(next_statement.split(interface["line_separator"])[-1]) + 1 + len(statement_with_comments.split(interface["line_separator"])[-1]) + 1 > interface["line_length"] ): - next_statement = ( + statement = ( isort.comments.add_to_line( interface["comments"], - f"{interface['statement']}", + output_statement, removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) + f"{interface['line_separator']}{prefix_statement}{next_import}" ) - interface["comments"] = [] - interface["statement"] = next_statement - return interface["statement"] - + comments = [] + output_statement = statement + + if comments and statement_with_comments: + output_statement = statement_with_comments + return output_statement + @_wrap_mode def hanging_indent_with_parentheses(**interface): diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 87b533d63..248285429 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1422,3 +1422,36 @@ def test_isort_shouldnt_split_skip_issue_1548(): import os """ ) + + +def test_isort_losing_imports_vertical_prefix_from_module_import_wrap_mode_issue_1542(): + """Ensure isort doesnt lose imports when a comment is combined with an import and + wrap mode VERTICAL_PREFIX_FROM_MODULE_IMPORT is used. + See: https://github.com/PyCQA/isort/issues/1542. + """ + assert isort.code( + """ +from xxxxxxxxxxxxxxxx import AAAAAAAAAA, BBBBBBBBBB +from xxxxxxxxxxxxxxxx import CCCCCCCCC, DDDDDDDDD # xxxxxxxxxxxxxxxxxx + +print(CCCCCCCCC) +""", + multi_line_output=9, + ) == """ +from xxxxxxxxxxxxxxxx import AAAAAAAAAA, BBBBBBBBBB # xxxxxxxxxxxxxxxxxx +from xxxxxxxxxxxxxxxx import CCCCCCCCC, DDDDDDDDD + +print(CCCCCCCCC) +""" + + assert isort.check_code( + """ +from xxxxxxxxxxxxxxxx import AAAAAAAAAA, BBBBBBBBBB + +from xxxxxxxxxxxxxxxx import CCCCCCCCC, DDDDDDDDD # xxxxxxxxxxxxxxxxxx isort: skip + +print(CCCCCCCCC) +""", + show_diff = True, + multi_line_output=9, + ) From e478e7c8f8a5e56dcda6f5b87f350445efdaaea0 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 10 Oct 2020 03:12:58 -0700 Subject: [PATCH 130/539] Fix formatting --- isort/wrap_modes.py | 10 +++++----- tests/unit/test_regressions.py | 13 ++++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/isort/wrap_modes.py b/isort/wrap_modes.py index db89d0ecc..02b793067 100644 --- a/isort/wrap_modes.py +++ b/isort/wrap_modes.py @@ -273,11 +273,11 @@ def vertical_hanging_indent_bracket(**interface): def vertical_prefix_from_module_import(**interface): if not interface["imports"]: return "" - + prefix_statement = interface["statement"] output_statement = prefix_statement + interface["imports"].pop(0) comments = interface["comments"] - + statement = output_statement statement_with_comments = "" for next_import in interface["imports"]: @@ -294,7 +294,7 @@ def vertical_prefix_from_module_import(**interface): ): statement = ( isort.comments.add_to_line( - interface["comments"], + comments, output_statement, removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], @@ -303,11 +303,11 @@ def vertical_prefix_from_module_import(**interface): ) comments = [] output_statement = statement - + if comments and statement_with_comments: output_statement = statement_with_comments return output_statement - + @_wrap_mode def hanging_indent_with_parentheses(**interface): diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 248285429..873cc2f72 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1429,20 +1429,23 @@ def test_isort_losing_imports_vertical_prefix_from_module_import_wrap_mode_issue wrap mode VERTICAL_PREFIX_FROM_MODULE_IMPORT is used. See: https://github.com/PyCQA/isort/issues/1542. """ - assert isort.code( - """ + assert ( + isort.code( + """ from xxxxxxxxxxxxxxxx import AAAAAAAAAA, BBBBBBBBBB from xxxxxxxxxxxxxxxx import CCCCCCCCC, DDDDDDDDD # xxxxxxxxxxxxxxxxxx print(CCCCCCCCC) """, - multi_line_output=9, - ) == """ + multi_line_output=9, + ) + == """ from xxxxxxxxxxxxxxxx import AAAAAAAAAA, BBBBBBBBBB # xxxxxxxxxxxxxxxxxx from xxxxxxxxxxxxxxxx import CCCCCCCCC, DDDDDDDDD print(CCCCCCCCC) """ + ) assert isort.check_code( """ @@ -1452,6 +1455,6 @@ def test_isort_losing_imports_vertical_prefix_from_module_import_wrap_mode_issue print(CCCCCCCCC) """, - show_diff = True, + show_diff=True, multi_line_output=9, ) From 9a79067846cff03aca6b7460056aaacffec6a36c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 10 Oct 2020 03:41:32 -0700 Subject: [PATCH 131/539] Add test for unseekable pipe errors --- isort/api.py | 3 ++ tests/unit/test_main.py | 62 ++++++++++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/isort/api.py b/isort/api.py index a8cecb623..5a2df6af3 100644 --- a/isort/api.py +++ b/isort/api.py @@ -200,6 +200,9 @@ def check_stream( """ config = _config(path=file_path, config=config, **config_kwargs) + if show_diff: + input_stream = StringIO(input_stream.read()) + changed: bool = sort_stream( input_stream=input_stream, output_stream=Empty, diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index caf20e7ed..b7bc7a00e 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -14,6 +14,11 @@ from isort.wrap_modes import WrapModes +class UnseekableTextIOWrapper(TextIOWrapper): + def seek(self, *args, **kwargs): + raise ValueError("underlying stream is not seekable") + + @given( file_name=st.text(), config=st.builds(Config), @@ -343,7 +348,7 @@ def test_isort_command(): def test_isort_with_stdin(capsys): # ensures that isort sorts stdin without any flags - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import b @@ -362,7 +367,7 @@ def test_isort_with_stdin(capsys): """ ) - input_content_from = TextIOWrapper( + input_content_from = UnseekableTextIOWrapper( BytesIO( b""" import c @@ -385,7 +390,7 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --fas flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import sys @@ -411,7 +416,7 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --fass flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" from a import Path, abc @@ -430,7 +435,7 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --ff flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import b @@ -453,7 +458,7 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with -fss flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import b @@ -472,7 +477,7 @@ def test_isort_with_stdin(capsys): """ ) - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import a @@ -493,7 +498,7 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --ds flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import sys @@ -516,7 +521,7 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --cs flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" from a import b @@ -536,7 +541,7 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --ca flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" from a import x as X @@ -556,7 +561,7 @@ def test_isort_with_stdin(capsys): # ensures that isort works consistently with check and ws flags - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import os @@ -570,10 +575,29 @@ def test_isort_with_stdin(capsys): out, error = capsys.readouterr() assert not error + + # ensures that isort works consistently with check and diff flags + + input_content = UnseekableTextIOWrapper( + BytesIO( + b""" +import b +import a +""" + ) + ) + + with pytest.raises(SystemExit): + main.main(["-", "--check", "--diff"], stdin=input_content) + out, error = capsys.readouterr() + + assert error + assert not "underlying stream is not seekable" in error + assert not "underlying stream is not seekable" in error # ensures that isort correctly sorts stdin with --ls flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import abcdef @@ -594,7 +618,7 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --nis flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" from z import b, c, a @@ -613,7 +637,7 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --sl flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" from z import b, c, a @@ -634,7 +658,7 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --top flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import os @@ -655,7 +679,7 @@ def test_isort_with_stdin(capsys): # ensure that isort correctly sorts stdin with --os flag - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import sys @@ -680,7 +704,7 @@ def test_isort_with_stdin(capsys): ) # ensures that isort warns with deprecated flags with stdin - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import sys @@ -701,7 +725,7 @@ def test_isort_with_stdin(capsys): """ ) - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import sys @@ -723,7 +747,7 @@ def test_isort_with_stdin(capsys): ) # ensures that only-modified flag works with stdin - input_content = TextIOWrapper( + input_content = UnseekableTextIOWrapper( BytesIO( b""" import a From 320124e8a5637fdcd9850eb2d2aa6d5b7e72be33 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 10 Oct 2020 03:51:18 -0700 Subject: [PATCH 132/539] Fixed #1552: Pylama test dependent on source layout. --- CHANGELOG.md | 5 +++++ tests/unit/test_pylama_isort.py | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e774eaad6..661ef3a3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.6.2 TBD - Fixed #1548: On rare occasions an unecessary empty line can be added when an import is marked as skipped. - Fixed #1542: Bug in VERTICAL_PREFIX_FROM_MODULE_IMPORT wrap mode. + - Fixed #1552: Pylama test dependent on source layout. + +#### Goal Zero: (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): + - Zope added to integration test suite + - Additional testing of CLI (simulate unseekable streams) ### 5.6.1 [Hotfix] October 8, 2020 - Fixed #1546: Unstable (non-idempotent) behavior with certain src trees. diff --git a/tests/unit/test_pylama_isort.py b/tests/unit/test_pylama_isort.py index 1fe19bfa1..035b5e31d 100644 --- a/tests/unit/test_pylama_isort.py +++ b/tests/unit/test_pylama_isort.py @@ -11,14 +11,16 @@ def test_allow(self): assert not self.instance.allow("test_case.c") assert self.instance.allow("test_case.py") - def test_run(self, src_dir, tmpdir): - assert not self.instance.run(os.path.join(src_dir, "api.py")) + def test_run(self, tmpdir): + correct = tmpdir.join("incorrect.py") + correct.write("import a\nimport b\n") + assert not self.instance.run(str(correct)) incorrect = tmpdir.join("incorrect.py") incorrect.write("import b\nimport a\n") assert self.instance.run(str(incorrect)) - def test_skip(self, src_dir, tmpdir): + def test_skip(self, tmpdir): incorrect = tmpdir.join("incorrect.py") incorrect.write("# isort: skip_file\nimport b\nimport a\n") assert not self.instance.run(str(incorrect)) From 618300622a897ee61a614586a58a2095df953d71 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 10 Oct 2020 03:52:05 -0700 Subject: [PATCH 133/539] Bump version to 5.6.2 --- isort/_version.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/_version.py b/isort/_version.py index a23647501..8d9ad75cd 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.6.1" +__version__ = "5.6.2" diff --git a/pyproject.toml b/pyproject.toml index 81d45cc27..a5ffa0381 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.6.1" +version = "5.6.2" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" From 6633e69b0966fc779fd54c24f7333fb8c0166f80 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 10 Oct 2020 03:53:23 -0700 Subject: [PATCH 134/539] Add release date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 661ef3a3d..2b090d416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). -### 5.6.2 TBD +### 5.6.2 October 10, 2020 - Fixed #1548: On rare occasions an unecessary empty line can be added when an import is marked as skipped. - Fixed #1542: Bug in VERTICAL_PREFIX_FROM_MODULE_IMPORT wrap mode. - Fixed #1552: Pylama test dependent on source layout. From 59dda8d503be642e6a7d38ab666bac4a0d317463 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 10 Oct 2020 03:57:11 -0700 Subject: [PATCH 135/539] Formatting --- tests/unit/test_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index b7bc7a00e..6c5bef76c 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -575,7 +575,7 @@ def test_isort_with_stdin(capsys): out, error = capsys.readouterr() assert not error - + # ensures that isort works consistently with check and diff flags input_content = UnseekableTextIOWrapper( @@ -593,7 +593,7 @@ def test_isort_with_stdin(capsys): assert error assert not "underlying stream is not seekable" in error - assert not "underlying stream is not seekable" in error + assert not "underlying stream is not seekable" in error # ensures that isort correctly sorts stdin with --ls flag From c2412345aa9b75ec732aca57ba60ef4fe4b6c0aa Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 10 Oct 2020 04:06:17 -0700 Subject: [PATCH 136/539] Resolve linting errors --- tests/unit/test_main.py | 4 ++-- tests/unit/test_pylama_isort.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 6c5bef76c..59e224aed 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -592,8 +592,8 @@ def test_isort_with_stdin(capsys): out, error = capsys.readouterr() assert error - assert not "underlying stream is not seekable" in error - assert not "underlying stream is not seekable" in error + assert "underlying stream is not seekable" not in error + assert "underlying stream is not seekable" not in error # ensures that isort correctly sorts stdin with --ls flag diff --git a/tests/unit/test_pylama_isort.py b/tests/unit/test_pylama_isort.py index 035b5e31d..d3900c3f0 100644 --- a/tests/unit/test_pylama_isort.py +++ b/tests/unit/test_pylama_isort.py @@ -1,5 +1,3 @@ -import os - from isort.pylama_isort import Linter From 93db086913bcfcff4d9de06c3842ef4425bbde8a Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sat, 10 Oct 2020 20:27:02 +0530 Subject: [PATCH 137/539] Added combine_straight_import flag in configurations schema --- isort/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/isort/settings.py b/isort/settings.py index 96fc9410e..553475e93 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -200,6 +200,7 @@ class _Config: dedup_headings: bool = False only_sections: bool = False only_modified: bool = False + combine_straight_imports: bool = False auto_identify_namespace_packages: bool = True namespace_packages: FrozenSet[str] = frozenset() From 0caca49f304271ad37f7f99dd70af9fe81da58d6 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sat, 10 Oct 2020 20:28:54 +0530 Subject: [PATCH 138/539] Added combine_straight_imports flag in arg_parser --- isort/main.py | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/isort/main.py b/isort/main.py index a8e59f0ef..d2cd76b00 100644 --- a/isort/main.py +++ b/isort/main.py @@ -722,6 +722,32 @@ def _build_arg_parser() -> argparse.ArgumentParser: " there are multiple sections with the comment set.", ) + parser.add_argument( + "--only-sections", + "--os", + dest="only_sections", + action="store_true", + help="Causes imports to be sorted only based on their sections like STDLIB,THIRDPARTY etc. " + "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.", + ) + + parser.add_argument( + "--combine-straight-imports", + "--csi", + dest="combine_straight_imports", + action="store_true", + help="Combines all the bare straight imports of the same section in a single line. " + "Won't work with sections which have 'as' imports", + ) + # deprecated options parser.add_argument( "--recursive", @@ -759,23 +785,6 @@ def _build_arg_parser() -> argparse.ArgumentParser: help=argparse.SUPPRESS, ) - parser.add_argument( - "--only-sections", - "--os", - dest="only_sections", - action="store_true", - help="Causes imports to be sorted only based on their sections like STDLIB,THIRDPARTY etc. " - "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 From 23897bf091e444c8556fcdec6f77db93653c2b3a Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sat, 10 Oct 2020 20:29:52 +0530 Subject: [PATCH 139/539] Added code to combine all bare straight imports in a single line when flag is set --- isort/output.py | 84 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/isort/output.py b/isort/output.py index d2633ffdd..c6c92d962 100644 --- a/isort/output.py +++ b/isort/output.py @@ -513,35 +513,71 @@ def _with_straight_imports( import_type: str, ) -> List[str]: output: List[str] = [] - for module in straight_modules: - if module in remove_imports: - continue - import_definition = [] - if module in parsed.as_map["straight"]: - if parsed.imports[section]["straight"][module]: - import_definition.append(f"{import_type} {module}") - import_definition.extend( - f"{import_type} {module} as {as_import}" - for as_import in parsed.as_map["straight"][module] + as_imports = any([module in parsed.as_map["straight"] for module in straight_modules]) + + # combine_straight_imports only works for bare imports, 'as' imports not included + if config.combine_straight_imports and not as_imports: + if not straight_modules: + return [] + + above_comments: List[str] = [] + inline_comments: List[str] = [] + + for module in straight_modules: + if module in parsed.categorized_comments["above"]["straight"]: + above_comments.extend(parsed.categorized_comments["above"]["straight"].pop(module)) + + for module in parsed.categorized_comments["straight"]: + inline_comments.extend(parsed.categorized_comments["straight"][module]) + + combined_straight_imports = " ".join(straight_modules) + if inline_comments: + combined_inline_comments = " ".join(inline_comments) + else: + combined_inline_comments = "" + + output.extend(above_comments) + + if combined_inline_comments: + output.append( + f"{import_type} {combined_straight_imports} # {combined_inline_comments}" ) else: - import_definition.append(f"{import_type} {module}") - - comments_above = parsed.categorized_comments["above"]["straight"].pop(module, None) - if comments_above: - output.extend(comments_above) - output.extend( - with_comments( - parsed.categorized_comments["straight"].get(module), - idef, - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, + output.append(f"{import_type} {combined_straight_imports}") + + return output + + else: + for module in straight_modules: + if module in remove_imports: + continue + + import_definition = [] + if module in parsed.as_map["straight"]: + if parsed.imports[section]["straight"][module]: + import_definition.append(f"{import_type} {module}") + import_definition.extend( + f"{import_type} {module} as {as_import}" + for as_import in parsed.as_map["straight"][module] + ) + else: + import_definition.append(f"{import_type} {module}") + + comments_above = parsed.categorized_comments["above"]["straight"].pop(module, None) + if comments_above: + output.extend(comments_above) + output.extend( + with_comments( + parsed.categorized_comments["straight"].get(module), + idef, + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + for idef in import_definition ) - for idef in import_definition - ) - return output + return output def _output_as_string(lines: List[str], line_separator: str) -> str: From e6ec382eed4432ef2ab43bd5964c83dc45ac615d Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sat, 10 Oct 2020 20:30:40 +0530 Subject: [PATCH 140/539] Added tests for combine_straight_imports option --- tests/unit/test_isort.py | 20 ++++++++++++++++++++ tests/unit/test_main.py | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index c07d655d7..703f8dab4 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -4893,3 +4893,23 @@ def test_only_sections() -> None: test_input = "from foo import b, a, c\n" assert isort.code(test_input, only_sections=True) == test_input + + +def test_combine_straight_imports() -> None: + """ Tests to ensure that combine_straight_imports works correctly """ + + test_input = ( + "import os\n" "import sys\n" "# this is a comment\n" "import math # inline comment\n" + ) + + assert isort.code(test_input, combine_straight_imports=True) == ( + "# this is a comment\n" "import math os sys # inline comment\n" + ) + + # test to ensure that combine_straight_import works with only_sections + + test_input = "import sys\n" "import a\n" "import math\n" "import os\n" "import b\n" + + assert isort.code(test_input, combine_straight_imports=True, only_sections=True) == ( + "import sys math os\n" "\n" "import a b\n" + ) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 59e224aed..0fa4cd765 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -77,6 +77,8 @@ def test_parse_args(): 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} + assert main.parse_args(["--csi"]) == {"combine_straight_imports": True} + assert main.parse_args(["--combine-straight-imports"]) == {"combine_straight_imports": True} def test_ascii_art(capsys): @@ -762,6 +764,25 @@ def test_isort_with_stdin(capsys): assert "else-type place_module for a returned THIRDPARTY" not in out assert "else-type place_module for b returned THIRDPARTY" not in out + # ensures that combine-straight-imports flag works with stdin + input_content = UnseekableTextIOWrapper( + BytesIO( + b""" +import a +import b +""" + ) + ) + + main.main(["-", "--combine-straight-imports"], stdin=input_content) + out, error = capsys.readouterr() + + assert out == ( + """ +import a b +""" + ) + def test_unsupported_encodings(tmpdir, capsys): tmp_file = tmpdir.join("file.py") From 4c54a63929579da70d2a2713319aa673571ecd7a Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sat, 10 Oct 2020 20:48:33 +0530 Subject: [PATCH 141/539] corrected an error with joining imports in one line --- isort/output.py | 2 +- tests/unit/test_isort.py | 6 +++--- tests/unit/test_main.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/isort/output.py b/isort/output.py index c6c92d962..f7ff50ce1 100644 --- a/isort/output.py +++ b/isort/output.py @@ -531,7 +531,7 @@ def _with_straight_imports( for module in parsed.categorized_comments["straight"]: inline_comments.extend(parsed.categorized_comments["straight"][module]) - combined_straight_imports = " ".join(straight_modules) + combined_straight_imports = ", ".join(straight_modules) if inline_comments: combined_inline_comments = " ".join(inline_comments) else: diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 703f8dab4..01fba9a77 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -4903,13 +4903,13 @@ def test_combine_straight_imports() -> None: ) assert isort.code(test_input, combine_straight_imports=True) == ( - "# this is a comment\n" "import math os sys # inline comment\n" + "# this is a comment\n" "import math, os, sys # inline comment\n" ) # test to ensure that combine_straight_import works with only_sections - test_input = "import sys\n" "import a\n" "import math\n" "import os\n" "import b\n" + test_input = "import sys, os\n" "import a\n" "import math\n" "import b\n" assert isort.code(test_input, combine_straight_imports=True, only_sections=True) == ( - "import sys math os\n" "\n" "import a b\n" + "import sys, os, math\n" "\n" "import a, b\n" ) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 0fa4cd765..b2035f2cc 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -779,7 +779,7 @@ def test_isort_with_stdin(capsys): assert out == ( """ -import a b +import a, b """ ) From b66ed3862e1b882da1c18247356cfa832cfefbe9 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sat, 10 Oct 2020 21:16:05 +0530 Subject: [PATCH 142/539] made some changes to pass deepsource check --- isort/output.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/isort/output.py b/isort/output.py index f7ff50ce1..5a41896d2 100644 --- a/isort/output.py +++ b/isort/output.py @@ -512,15 +512,15 @@ def _with_straight_imports( remove_imports: List[str], import_type: str, ) -> List[str]: + if not straight_modules: + return [] + output: List[str] = [] - as_imports = any([module in parsed.as_map["straight"] for module in straight_modules]) + as_imports = any((module in parsed.as_map["straight"] for module in straight_modules)) # combine_straight_imports only works for bare imports, 'as' imports not included if config.combine_straight_imports and not as_imports: - if not straight_modules: - return [] - above_comments: List[str] = [] inline_comments: List[str] = [] From f77f059e2cf7a0d5fb8876ead8e392d448543dc0 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Sat, 10 Oct 2020 21:23:21 +0530 Subject: [PATCH 143/539] made changes to pass deepsource check --- isort/output.py | 57 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/isort/output.py b/isort/output.py index 5a41896d2..f4b30304b 100644 --- a/isort/output.py +++ b/isort/output.py @@ -512,15 +512,15 @@ def _with_straight_imports( remove_imports: List[str], import_type: str, ) -> List[str]: - if not straight_modules: - return [] - output: List[str] = [] as_imports = any((module in parsed.as_map["straight"] for module in straight_modules)) # combine_straight_imports only works for bare imports, 'as' imports not included if config.combine_straight_imports and not as_imports: + if not straight_modules: + return [] + above_comments: List[str] = [] inline_comments: List[str] = [] @@ -548,36 +548,35 @@ def _with_straight_imports( return output - else: - for module in straight_modules: - if module in remove_imports: - continue + for module in straight_modules: + if module in remove_imports: + continue - import_definition = [] - if module in parsed.as_map["straight"]: - if parsed.imports[section]["straight"][module]: - import_definition.append(f"{import_type} {module}") - import_definition.extend( - f"{import_type} {module} as {as_import}" - for as_import in parsed.as_map["straight"][module] - ) - else: + import_definition = [] + if module in parsed.as_map["straight"]: + if parsed.imports[section]["straight"][module]: import_definition.append(f"{import_type} {module}") - - comments_above = parsed.categorized_comments["above"]["straight"].pop(module, None) - if comments_above: - output.extend(comments_above) - output.extend( - with_comments( - parsed.categorized_comments["straight"].get(module), - idef, - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ) - for idef in import_definition + import_definition.extend( + f"{import_type} {module} as {as_import}" + for as_import in parsed.as_map["straight"][module] ) + else: + import_definition.append(f"{import_type} {module}") + + comments_above = parsed.categorized_comments["above"]["straight"].pop(module, None) + if comments_above: + output.extend(comments_above) + output.extend( + with_comments( + parsed.categorized_comments["straight"].get(module), + idef, + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + for idef in import_definition + ) - return output + return output def _output_as_string(lines: List[str], line_separator: str) -> str: From d929358766df52590df03b35fe515e6a369c1591 Mon Sep 17 00:00:00 2001 From: Louis Sautier Date: Sat, 10 Oct 2020 19:11:20 +0200 Subject: [PATCH 144/539] Prevent installation of the "tests" package Tests should not be listed under packages, otherwise setup.py attempts to install a "tests" package, causing potential clashes with other packages. --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 81d45cc27..5f8fdb0e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,9 @@ classifiers = [ urls = { Changelog = "https://github.com/pycqa/isort/blob/master/CHANGELOG.md" } packages = [ { include = "isort" }, - { include = "tests", format = "sdist" }, +] +include = [ + { path = "tests", format = "sdist" }, ] [tool.poetry.dependencies] From 66d6345171b398c9e025904789d121e78dcad79a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 10 Oct 2020 23:53:16 -0700 Subject: [PATCH 145/539] Specify minimum build system --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5f8fdb0e7..158c92fe6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,5 +101,5 @@ logo = "art/logo.png" palette = {scheme = "isort"} [build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" From b662f440ac859fb6b5fcfacea6cb94052a873388 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 11 Oct 2020 00:07:04 -0700 Subject: [PATCH 146/539] Add James Curtin (@jamescurtin) to acknowledgements --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 9f0ccae89..73f2b3f76 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -200,6 +200,7 @@ Code Contributors - Andrew Howe (@howeaj) - Sang-Heon Jeon (@lntuition) - Denis Veselov (@saippuakauppias) +- James Curtin (@jamescurtin) Documenters =================== From 383f323e44da4065a069c4817817e56bcfa7d546 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 11 Oct 2020 00:09:06 -0700 Subject: [PATCH 147/539] Update changelog to include source distribution improvement --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b090d416..c8231bd02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). +### 5.6.3 October 11, 2020 + - Improved packaging of test files alongside source distribution (see: https://github.com/PyCQA/isort/pull/1555). + ### 5.6.2 October 10, 2020 - Fixed #1548: On rare occasions an unecessary empty line can be added when an import is marked as skipped. - Fixed #1542: Bug in VERTICAL_PREFIX_FROM_MODULE_IMPORT wrap mode. From 491b3461d572249de71a7b2572af281322d4f40b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 11 Oct 2020 00:09:39 -0700 Subject: [PATCH 148/539] Bump version to 5.6.3 --- isort/_version.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/_version.py b/isort/_version.py index 8d9ad75cd..06497b9f5 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.6.2" +__version__ = "5.6.3" diff --git a/pyproject.toml b/pyproject.toml index b83a81082..a91cea2ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.6.2" +version = "5.6.3" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" From 586cfe1951cd7be8976a8beda0803910ac2cdd3e Mon Sep 17 00:00:00 2001 From: Marco Gorelli Date: Sun, 11 Oct 2020 15:07:23 +0100 Subject: [PATCH 149/539] update pre-commit example in documentation --- .pre-commit-hooks.yaml | 2 +- docs/upgrade_guides/5.0.0.md | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 6bfb2bba9..0027e49df 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -3,5 +3,5 @@ entry: isort require_serial: true language: python - types: [python, cython, pyi] + types: [python] args: ['--filter-files'] diff --git a/docs/upgrade_guides/5.0.0.md b/docs/upgrade_guides/5.0.0.md index fb767c2c4..6aeb0f65a 100644 --- a/docs/upgrade_guides/5.0.0.md +++ b/docs/upgrade_guides/5.0.0.md @@ -82,9 +82,17 @@ isort now includes an optimized precommit configuration in the repo itself. To u ``` - repo: https://github.com/pycqa/isort - rev: 5.3.2 + rev: 5.6.3 hooks: - id: isort + name: isort (python) + types: [python] + - id: isort + name: isort (cython) + types: [cython] + - id: isort + name: isort (pyi) + types: [pyi] ``` under the `repos` section of your projects `.pre-commit-config.yaml` config. From 1622543078eae774118fa163efb7bab26d3963b8 Mon Sep 17 00:00:00 2001 From: Marco Gorelli Date: Sun, 11 Oct 2020 15:30:26 +0100 Subject: [PATCH 150/539] remove unnecessary types=[python] --- docs/upgrade_guides/5.0.0.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/upgrade_guides/5.0.0.md b/docs/upgrade_guides/5.0.0.md index 6aeb0f65a..c5e806069 100644 --- a/docs/upgrade_guides/5.0.0.md +++ b/docs/upgrade_guides/5.0.0.md @@ -86,7 +86,6 @@ isort now includes an optimized precommit configuration in the repo itself. To u hooks: - id: isort name: isort (python) - types: [python] - id: isort name: isort (cython) types: [cython] From 49bb9babe3d2fb268b95d4170d5fc1dc267c213d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 12 Oct 2020 21:35:06 -0700 Subject: [PATCH 151/539] Fixed #1556: Empty line added between imports that should be skipped. --- CHANGELOG.md | 3 +++ isort/parse.py | 34 ++++++++++++++++++++++++++-------- tests/unit/test_regressions.py | 27 +++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b090d416..37d0ebc38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). +### 5.6.3 October TBD, 2020 + - Fixed #1556: Empty line added between imports that should be skipped. + ### 5.6.2 October 10, 2020 - Fixed #1548: On rare occasions an unecessary empty line can be added when an import is marked as skipped. - Fixed #1542: Bug in VERTICAL_PREFIX_FROM_MODULE_IMPORT wrap mode. diff --git a/isort/parse.py b/isort/parse.py index 96468f095..2c152651a 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -218,17 +218,35 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte import_index = index - 1 while import_index and not in_lines[import_index - 1]: import_index -= 1 - elif "isort:skip" in line or "isort: skip" in line: - commentless = line.split("#", 1)[0] + else: + commentless = line.split("#", 1)[0].strip() if ( - "(" in commentless - and not commentless.rstrip().endswith(")") - and import_index < line_count + ("isort:skip" in line + or "isort: skip" in line) + and "(" in commentless + and ")" not in commentless ): import_index = index - while import_index < line_count and not commentless.rstrip().endswith(")"): - commentless = in_lines[import_index].split("#", 1)[0] - import_index += 1 + + starting_line = line + while "isort:skip" in starting_line or "isort: skip" in starting_line: + commentless = starting_line.split("#", 1)[0] + if ( + "(" in commentless + and not commentless.rstrip().endswith(")") + and import_index < line_count + ): + + while import_index < line_count and not commentless.rstrip().endswith(")"): + commentless = in_lines[import_index].split("#", 1)[0] + import_index += 1 + else: + import_index += 1 + + if import_index >= line_count: + break + else: + starting_line = in_lines[import_index] line, *end_of_line_comment = line.split("#", 1) if ";" in line: diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 873cc2f72..c25ed19c7 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1424,6 +1424,33 @@ def test_isort_shouldnt_split_skip_issue_1548(): ) +def test_isort_shouldnt_split_skip_issue_1556(): + assert isort.check_code( + """ +from tools.dependency_pruning.prune_dependencies import ( # isort:skip + prune_dependencies, +) +from tools.developer_pruning.prune_developers import ( # isort:skip + prune_developers, +) +""", + show_diff=True, + profile="black", + float_to_top=True, + ) + assert isort.check_code( + """ +from tools.dependency_pruning.prune_dependencies import ( # isort:skip + prune_dependencies, +) +from tools.developer_pruning.prune_developers import x # isort:skip +""", + show_diff=True, + profile="black", + float_to_top=True, + ) + + def test_isort_losing_imports_vertical_prefix_from_module_import_wrap_mode_issue_1542(): """Ensure isort doesnt lose imports when a comment is combined with an import and wrap mode VERTICAL_PREFIX_FROM_MODULE_IMPORT is used. From b4878742ff3f6c9b3e46c329ddbdbdeda8553d9e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 12 Oct 2020 23:09:17 -0700 Subject: [PATCH 152/539] formatting fix --- isort/parse.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/isort/parse.py b/isort/parse.py index 2c152651a..fe3dd157a 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -221,8 +221,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte else: commentless = line.split("#", 1)[0].strip() if ( - ("isort:skip" in line - or "isort: skip" in line) + ("isort:skip" in line or "isort: skip" in line) and "(" in commentless and ")" not in commentless ): @@ -237,7 +236,9 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte and import_index < line_count ): - while import_index < line_count and not commentless.rstrip().endswith(")"): + while import_index < line_count and not commentless.rstrip().endswith( + ")" + ): commentless = in_lines[import_index].split("#", 1)[0] import_index += 1 else: From 6bb47b7acc1554ecb59d2855e9110c447162f674 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 12 Oct 2020 23:21:39 -0700 Subject: [PATCH 153/539] Bump version to 5.6.4 --- CHANGELOG.md | 2 +- isort/_version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e57cc61d8..a94901a0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). -### 5.6.4 October TBD, 2020 +### 5.6.4 October 12, 2020 - Fixed #1556: Empty line added between imports that should be skipped. ### 5.6.3 October 11, 2020 diff --git a/isort/_version.py b/isort/_version.py index 06497b9f5..f0b716b28 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.6.3" +__version__ = "5.6.4" diff --git a/pyproject.toml b/pyproject.toml index a91cea2ab..7047b0add 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.6.3" +version = "5.6.4" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" From 1682a3385ec91563bc7e0b70ca32b80ff23b0832 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 12 Oct 2020 23:39:19 -0700 Subject: [PATCH 154/539] Add Marco Gorelli (@MarcoGorelli) and Louis Sautier (@sbraz) to contributors list --- docs/contributing/4.-acknowledgements.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 73f2b3f76..9c04e3f8c 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -201,6 +201,8 @@ Code Contributors - Sang-Heon Jeon (@lntuition) - Denis Veselov (@saippuakauppias) - James Curtin (@jamescurtin) +- Marco Gorelli (@MarcoGorelli) +- Louis Sautier (@sbraz) Documenters =================== From d2c1b34b8910b78ccece23f86bf9900b4f38fc02 Mon Sep 17 00:00:00 2001 From: Bhupesh Varshney Date: Tue, 13 Oct 2020 13:50:53 +0000 Subject: [PATCH 155/539] add compatibility docs with black --- docs/configuration/compatibility_black.md | 57 +++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/configuration/compatibility_black.md diff --git a/docs/configuration/compatibility_black.md b/docs/configuration/compatibility_black.md new file mode 100644 index 000000000..d731bf23b --- /dev/null +++ b/docs/configuration/compatibility_black.md @@ -0,0 +1,57 @@ +Compatibility with black +======== + +black and isort sometimes don't agree on some rules. Although you can configure isort to behave nicely with black. + + +#Basic compatibility + +Use the profile option while using isort, `isort --profile black`. + +A demo of how this would look like in your _.travis.yml_ + +```yaml +language: python +python: + - "3.6" + - "3.7" + - "3.8" + +install: + - pip install -r requirements-dev.txt + - pip install isort black + - pip install coveralls +script: + - pytest my-package + - isort --profile black my-package + - black --check --diff my-package +after_success: + - coveralls + +``` + +See [built-in profiles](https://pycqa.github.io/isort/docs/configuration/profiles/) for more profiles. + +#Integration with pre-commit + +isort can be easily used with your pre-commit hooks. + +```yaml +- repo: https://github.com/pycqa/isort + rev: 5.6.4 + hooks: + - id: isort + args: ["--profile", "black"] +``` + +#Using a config file (.isort.cfg) + +The easiest way to configure black with isort is to use a config file. + +```ini +[tool.isort] +profile = "black" +multi_line_output = 3 +``` + +Read More about supported [config files](https://pycqa.github.io/isort/docs/configuration/config_files/). \ No newline at end of file From fdfd55ad9027954ecb640a41b5f2fa6490c10237 Mon Sep 17 00:00:00 2001 From: Bhupesh Varshney Date: Tue, 13 Oct 2020 13:56:20 +0000 Subject: [PATCH 156/539] fix headings --- docs/configuration/compatibility_black.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/configuration/compatibility_black.md b/docs/configuration/compatibility_black.md index d731bf23b..4afa2459e 100644 --- a/docs/configuration/compatibility_black.md +++ b/docs/configuration/compatibility_black.md @@ -4,7 +4,7 @@ Compatibility with black black and isort sometimes don't agree on some rules. Although you can configure isort to behave nicely with black. -#Basic compatibility +## Basic compatibility Use the profile option while using isort, `isort --profile black`. @@ -32,7 +32,7 @@ after_success: See [built-in profiles](https://pycqa.github.io/isort/docs/configuration/profiles/) for more profiles. -#Integration with pre-commit +## Integration with pre-commit isort can be easily used with your pre-commit hooks. @@ -44,7 +44,7 @@ isort can be easily used with your pre-commit hooks. args: ["--profile", "black"] ``` -#Using a config file (.isort.cfg) +## Using a config file (.isort.cfg) The easiest way to configure black with isort is to use a config file. From 78771148995f3a1d3a12f0177d5ab153dd6de3f0 Mon Sep 17 00:00:00 2001 From: Timur Kushukov Date: Thu, 8 Oct 2020 00:04:50 +0500 Subject: [PATCH 157/539] get imports command --- isort/__init__.py | 9 ++++++- isort/api.py | 56 ++++++++++++++++++++++++++++++++++++++++ isort/core.py | 12 +++++++++ tests/unit/test_isort.py | 41 +++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) diff --git a/isort/__init__.py b/isort/__init__.py index 236255dd8..6f2c2c833 100644 --- a/isort/__init__.py +++ b/isort/__init__.py @@ -2,7 +2,14 @@ from . import settings from ._version import __version__ from .api import check_code_string as check_code -from .api import check_file, check_stream, place_module, place_module_with_reason +from .api import ( + check_file, + check_stream, + get_imports_stream, + get_imports_string, + place_module, + place_module_with_reason, +) from .api import sort_code_string as code from .api import sort_file as file from .api import sort_stream as stream diff --git a/isort/api.py b/isort/api.py index 5a2df6af3..aed041ce4 100644 --- a/isort/api.py +++ b/isort/api.py @@ -366,6 +366,62 @@ def sort_file( return changed +def get_imports_string( + code: str, + extension: Optional[str] = None, + config: Config = DEFAULT_CONFIG, + file_path: Optional[Path] = None, + **config_kwargs, +) -> str: + """Finds all imports within the provided code string, returning a new string with them. + + - **code**: The string of code with imports that need to be sorted. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - ****config_kwargs**: Any config modifications. + """ + input_stream = StringIO(code) + output_stream = StringIO() + config = _config(path=file_path, config=config, **config_kwargs) + get_imports_stream( + input_stream, + output_stream, + extension=extension, + config=config, + file_path=file_path, + ) + output_stream.seek(0) + return output_stream.read() + + +def get_imports_stream( + input_stream: TextIO, + output_stream: TextIO, + extension: Optional[str] = None, + config: Config = DEFAULT_CONFIG, + file_path: Optional[Path] = None, + **config_kwargs, +) -> None: + """Finds all imports within the provided code stream, outputs to the provided output stream. + + - **input_stream**: The stream of code with imports that need to be sorted. + - **output_stream**: The stream where sorted imports should be written to. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - ****config_kwargs**: Any config modifications. + """ + config = _config(path=file_path, config=config, **config_kwargs) + core.process( + input_stream, + output_stream, + extension=extension or (file_path and file_path.suffix.lstrip(".")) or "py", + config=config, + imports_only=True, + ) + + def _config( path: Optional[Path] = None, config: Config = DEFAULT_CONFIG, **config_kwargs ) -> Config: diff --git a/isort/core.py b/isort/core.py index 292bdc1c2..3668ae9e8 100644 --- a/isort/core.py +++ b/isort/core.py @@ -30,6 +30,7 @@ def process( output_stream: TextIO, extension: str = "py", config: Config = DEFAULT_CONFIG, + imports_only: bool = False, ) -> bool: """Parses stream identifying sections of contiguous imports and sorting them @@ -68,6 +69,7 @@ def process( stripped_line: str = "" end_of_file: bool = False verbose_output: List[str] = [] + all_imports: List[str] = [] if config.float_to_top: new_input = "" @@ -331,6 +333,11 @@ def process( parsed_content = parse.file_contents(import_section, config=config) verbose_output += parsed_content.verbose_output + all_imports.extend( + li + for li in parsed_content.in_lines + if li and li not in set(parsed_content.lines_without_imports) + ) sorted_import_section = output.sorted_imports( parsed_content, @@ -395,6 +402,11 @@ def process( for output_str in verbose_output: print(output_str) + if imports_only: + output_stream.seek(0) + output_stream.truncate(0) + output_stream.write(line_separator.join(all_imports) + line_separator) + return made_changes diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 01fba9a77..f28c5de63 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -4913,3 +4913,44 @@ def test_combine_straight_imports() -> None: assert isort.code(test_input, combine_straight_imports=True, only_sections=True) == ( "import sys, os, math\n" "\n" "import a, b\n" ) + + +def test_get_imports_string() -> None: + test_input = ( + "import first_straight\n" + "\n" + "import second_straight\n" + "from first_from import first_from_function_1, first_from_function_2\n" + "import bad_name as good_name\n" + "from parent.some_bad_defs import bad_name_1 as ok_name_1, bad_name_2 as ok_name_2\n" + "\n" + "# isort: list\n" + "__all__ = ['b', 'c', 'a']\n" + "\n" + "def bla():\n" + " import needed_in_bla_2\n" + "\n" + "\n" + " import needed_in_bla\n" + " pass" + "\n" + "def bla_bla():\n" + " import needed_in_bla_bla\n" + "\n" + " #import not_really_an_import\n" + " pass" + "\n" + "import needed_in_end\n" + ) + result = api.get_imports_string(test_input) + assert result == ( + "import first_straight\n" + "import second_straight\n" + "from first_from import first_from_function_1, first_from_function_2\n" + "import bad_name as good_name\n" + "from parent.some_bad_defs import bad_name_1 as ok_name_1, bad_name_2 as ok_name_2\n" + "import needed_in_bla_2\n" + "import needed_in_bla\n" + "import needed_in_bla_bla\n" + "import needed_in_end\n" + ) From f9f5cc9314cc1be1e2f9c2b0270580c1351d8fe1 Mon Sep 17 00:00:00 2001 From: Tamas Szabo Date: Wed, 14 Oct 2020 10:15:34 +0300 Subject: [PATCH 158/539] Improves test coverage of settings.py. Branch coverage -> 100%. --- tests/unit/test_settings.py | 55 +++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index 462b667d0..be8b5020b 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -86,12 +86,27 @@ def test_is_supported_filetype_fifo(self, tmpdir): os.mkfifo(fifo_file) assert not self.instance.is_supported_filetype(fifo_file) + def test_src_paths_are_combined_and_deduplicated(self): + src_paths = ["src", "tests"] + src_full_paths = (Path(os.getcwd()) / f for f in src_paths) + assert Config(src_paths=src_paths * 2).src_paths == tuple(src_full_paths) + def test_as_list(): assert settings._as_list([" one "]) == ["one"] assert settings._as_list("one,two") == ["one", "two"] +def _write_simple_settings(tmp_file): + tmp_file.write_text( + """ +[isort] +force_grid_wrap=true +""", + "utf8", + ) + + def test_find_config(tmpdir): tmp_config = tmpdir.join(".isort.cfg") @@ -116,31 +131,46 @@ def test_find_config(tmpdir): # can when it has either a file format, or generic relevant section settings._find_config.cache_clear() settings._get_config_data.cache_clear() - tmp_config.write_text( - """ -[isort] -force_grid_wrap=true -""", - "utf8", - ) + _write_simple_settings(tmp_config) assert settings._find_config(str(tmpdir))[1] +def test_find_config_deep(tmpdir): + # can't find config if it is further up than MAX_CONFIG_SEARCH_DEPTH + dirs = [f"dir{i}" for i in range(settings.MAX_CONFIG_SEARCH_DEPTH + 1)] + tmp_dirs = tmpdir.ensure(*dirs, dirs=True) + tmp_config = tmpdir.join("dir0", ".isort.cfg") + settings._find_config.cache_clear() + settings._get_config_data.cache_clear() + _write_simple_settings(tmp_config) + assert not settings._find_config(str(tmp_dirs))[1] + # but can find config if it is MAX_CONFIG_SEARCH_DEPTH up + one_parent_up = os.path.split(str(tmp_dirs))[0] + assert settings._find_config(one_parent_up)[1] + + def test_get_config_data(tmpdir): test_config = tmpdir.join("test_config.editorconfig") test_config.write_text( """ root = true -[*.py] +[*.{js,py}] indent_style=tab indent_size=tab + +[*.py] force_grid_wrap=false comment_prefix="text" + +[*.{java}] +indent_style = space """, "utf8", ) - loaded_settings = settings._get_config_data(str(test_config), sections=("*.py",)) + loaded_settings = settings._get_config_data( + str(test_config), sections=settings.CONFIG_SECTIONS[".editorconfig"] + ) assert loaded_settings assert loaded_settings["comment_prefix"] == "text" assert loaded_settings["force_grid_wrap"] == 0 @@ -148,6 +178,13 @@ def test_get_config_data(tmpdir): assert str(tmpdir) in loaded_settings["source"] +def test_editorconfig_without_sections(tmpdir): + test_config = tmpdir.join("test_config.editorconfig") + test_config.write_text("\nroot = true\n", "utf8") + loaded_settings = settings._get_config_data(str(test_config), sections=("*.py",)) + assert not loaded_settings + + def test_as_bool(): assert settings._as_bool("TrUe") is True assert settings._as_bool("true") is True From 68bf16a0de7391ff5036ff1fac530f4ea359e489 Mon Sep 17 00:00:00 2001 From: Timur Kushukov Date: Wed, 14 Oct 2020 11:45:33 +0500 Subject: [PATCH 159/539] get imports stdout fix --- isort/core.py | 9 ++++++--- tests/unit/test_isort.py | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/isort/core.py b/isort/core.py index 3668ae9e8..c62687e5a 100644 --- a/isort/core.py +++ b/isort/core.py @@ -1,3 +1,4 @@ +import os import textwrap from io import StringIO from itertools import chain @@ -70,6 +71,9 @@ def process( end_of_file: bool = False verbose_output: List[str] = [] all_imports: List[str] = [] + if imports_only: + _output_stream = output_stream + output_stream = open(os.devnull, "wt") if config.float_to_top: new_input = "" @@ -403,9 +407,8 @@ def process( print(output_str) if imports_only: - output_stream.seek(0) - output_stream.truncate(0) - output_stream.write(line_separator.join(all_imports) + line_separator) + result = line_separator.join(all_imports) + line_separator + _output_stream.write(result) return made_changes diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index f28c5de63..cdec1c4e2 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -7,6 +7,7 @@ from pathlib import Path import subprocess import sys +from io import StringIO from tempfile import NamedTemporaryFile from typing import Any, Dict, Iterator, List, Set, Tuple @@ -4954,3 +4955,24 @@ def test_get_imports_string() -> None: "import needed_in_bla_bla\n" "import needed_in_end\n" ) + + +def test_get_imports_stdout() -> None: + """Ensure that get_imports_stream can work with nonseekable streams like STDOUT""" + + global_output = [] + + class NonSeekableTestStream(StringIO): + def seek(self, position): + raise OSError("Stream is not seekable") + + def seekable(self): + return False + + def write(self, s): + global_output.append(s) + + test_input = StringIO("import m2\n" "import m1\n" "not_import = 7") + test_output = NonSeekableTestStream() + api.get_imports_stream(test_input, test_output) + assert "".join(global_output) == "import m2\nimport m1\n" From e24c3b0b144f73319f12193155c66ef24a071d8d Mon Sep 17 00:00:00 2001 From: Timur Kushukov Date: Thu, 15 Oct 2020 12:35:06 +0500 Subject: [PATCH 160/539] fix devnull linter warning --- isort/core.py | 8 ++++++-- tests/unit/test_isort.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/isort/core.py b/isort/core.py index c62687e5a..badf2a04c 100644 --- a/isort/core.py +++ b/isort/core.py @@ -1,4 +1,3 @@ -import os import textwrap from io import StringIO from itertools import chain @@ -73,7 +72,12 @@ def process( all_imports: List[str] = [] if imports_only: _output_stream = output_stream - output_stream = open(os.devnull, "wt") + + class DevNull(StringIO): + def write(self, *a, **kw): + pass + + output_stream = DevNull() if config.float_to_top: new_input = "" diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index cdec1c4e2..aea2c74b6 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -4969,7 +4969,7 @@ def seek(self, position): def seekable(self): return False - def write(self, s): + def write(self, s, *a, **kw): global_output.append(s) test_input = StringIO("import m2\n" "import m1\n" "not_import = 7") From 9bdc453b13d2ae7f4d763ed15955c8471c633857 Mon Sep 17 00:00:00 2001 From: Rohan Khanna Date: Thu, 15 Oct 2020 21:37:05 +0200 Subject: [PATCH 161/539] Unnecessary else / elif used after break (deepsource.io PYL-R1723) --- isort/output.py | 2 +- isort/parse.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/isort/output.py b/isort/output.py index f4b30304b..cda5c24d8 100644 --- a/isort/output.py +++ b/isort/output.py @@ -184,7 +184,7 @@ def sorted_imports( continue next_construct = line break - elif in_quote: + if in_quote: next_construct = line break diff --git a/isort/parse.py b/isort/parse.py index fe3dd157a..6a999391a 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -246,8 +246,8 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte if import_index >= line_count: break - else: - starting_line = in_lines[import_index] + + starting_line = in_lines[import_index] line, *end_of_line_comment = line.split("#", 1) if ";" in line: From 579d93c40c736d87f5219c42222df8f02abf3abf Mon Sep 17 00:00:00 2001 From: Timur Kushukov Date: Fri, 16 Oct 2020 21:44:27 +0500 Subject: [PATCH 162/539] CLI to get imports --- isort/__init__.py | 1 + isort/api.py | 28 ++++++++++++++++++++++++++++ isort/core.py | 12 +++++++----- isort/main.py | 17 +++++++++++++++++ pyproject.toml | 1 + tests/unit/test_api.py | 7 +++++++ tests/unit/test_main.py | 12 ++++++++++++ 7 files changed, 73 insertions(+), 5 deletions(-) diff --git a/isort/__init__.py b/isort/__init__.py index 6f2c2c833..b800a03de 100644 --- a/isort/__init__.py +++ b/isort/__init__.py @@ -5,6 +5,7 @@ from .api import ( check_file, check_stream, + get_imports_file, get_imports_stream, get_imports_string, place_module, diff --git a/isort/api.py b/isort/api.py index aed041ce4..c6a824124 100644 --- a/isort/api.py +++ b/isort/api.py @@ -422,6 +422,34 @@ def get_imports_stream( ) +def get_imports_file( + filename: Union[str, Path], + output_stream: TextIO, + extension: Optional[str] = None, + config: Config = DEFAULT_CONFIG, + file_path: Optional[Path] = None, + **config_kwargs, +) -> None: + """Finds all imports within the provided file, outputs to the provided output stream. + + - **filename**: The name or Path of the file to check. + - **output_stream**: The stream where sorted imports should be written to. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - ****config_kwargs**: Any config modifications. + """ + with io.File.read(filename) as source_file: + get_imports_stream( + source_file.stream, + output_stream, + extension, + config, + file_path, + **config_kwargs, + ) + + def _config( path: Optional[Path] = None, config: Config = DEFAULT_CONFIG, **config_kwargs ) -> Config: diff --git a/isort/core.py b/isort/core.py index badf2a04c..72b10c7c6 100644 --- a/isort/core.py +++ b/isort/core.py @@ -341,11 +341,13 @@ def write(self, *a, **kw): parsed_content = parse.file_contents(import_section, config=config) verbose_output += parsed_content.verbose_output - all_imports.extend( - li - for li in parsed_content.in_lines - if li and li not in set(parsed_content.lines_without_imports) - ) + if imports_only: + lines_without_imports_set = set(parsed_content.lines_without_imports) + all_imports.extend( + li + for li in parsed_content.in_lines + if li and li not in lines_without_imports_set + ) sorted_import_section = output.sorted_imports( parsed_content, diff --git a/isort/main.py b/isort/main.py index d2cd76b00..5cf96ba73 100644 --- a/isort/main.py +++ b/isort/main.py @@ -826,6 +826,23 @@ def _preconvert(item): raise TypeError("Unserializable object {} of type {}".format(item, type(item))) +def identify_imports_main(argv: Optional[Sequence[str]] = None) -> None: + parser = argparse.ArgumentParser( + description="Get all import definitions from a given file." + "Use `-` as the first argument to represent stdin." + ) + parser.add_argument("file", help="Python source file to get imports from.") + arguments = parser.parse_args(argv) + + file_name = arguments.file + if file_name == "-": + api.get_imports_stream(sys.stdin, sys.stdout) + else: + if os.path.isdir(file_name): + sys.exit("Path must be a file, not a directory") + api.get_imports_file(file_name, sys.stdout) + + def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = None) -> None: arguments = parse_args(argv) if arguments.get("show_version"): diff --git a/pyproject.toml b/pyproject.toml index 7047b0add..840c64916 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,6 +83,7 @@ example_isort_formatting_plugin = "^0.0.2" [tool.poetry.scripts] isort = "isort.main:main" +isort-identify-imports = "isort.main:identify_imports_main" [tool.poetry.plugins."distutils.commands"] isort = "isort.main:ISortCommand" diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 3d257e705..fc29c33f5 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -1,5 +1,6 @@ """Tests the isort API module""" import os +import sys from io import StringIO from unittest.mock import MagicMock, patch @@ -81,3 +82,9 @@ def test_diff_stream() -> None: def test_sort_code_string_mixed_newlines(): assert api.sort_code_string("import A\n\r\nimportA\n\n") == "import A\r\n\r\nimportA\r\n\n" + + +def test_get_import_file(imperfect, capsys): + api.get_imports_file(imperfect, sys.stdout) + out, _ = capsys.readouterr() + assert out == imperfect_content diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index b2035f2cc..93ed94e86 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -914,3 +914,15 @@ def test_only_modified_flag(tmpdir, capsys): 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 + + +def test_identify_imports_main(tmpdir, capsys): + file_content = "import mod2\n" "a = 1\n" "import mod1\n" + file_imports = "import mod2\n" "import mod1\n" + some_file = tmpdir.join("some_file.py") + some_file.write(file_content) + + main.identify_imports_main([str(some_file)]) + + out, error = capsys.readouterr() + assert out == file_imports From e737cb5b4fb385a49c562ff646197cce2e0452b7 Mon Sep 17 00:00:00 2001 From: Timur Kushukov Date: Mon, 19 Oct 2020 19:06:01 +0500 Subject: [PATCH 163/539] fix Windows tests --- tests/unit/test_api.py | 2 +- tests/unit/test_main.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index fc29c33f5..4ee19bc43 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -87,4 +87,4 @@ def test_sort_code_string_mixed_newlines(): def test_get_import_file(imperfect, capsys): api.get_imports_file(imperfect, sys.stdout) out, _ = capsys.readouterr() - assert out == imperfect_content + assert out == imperfect_content.replace("\n", os.linesep) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 93ed94e86..70eafb3ad 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -1,4 +1,5 @@ import json +import os import subprocess from datetime import datetime from io import BytesIO, TextIOWrapper @@ -925,4 +926,4 @@ def test_identify_imports_main(tmpdir, capsys): main.identify_imports_main([str(some_file)]) out, error = capsys.readouterr() - assert out == file_imports + assert out == file_imports.replace("\n", os.linesep) From 04e56598de7a38c85982548a25ea6097c9e6403e Mon Sep 17 00:00:00 2001 From: Vasilis Gerakaris Date: Wed, 21 Oct 2020 23:10:38 +0300 Subject: [PATCH 164/539] fix deepsource.io warnings "Safe" warnings, regarding style, and ternary operators were fixed. added some variable initialisations to suppress false-positive "possibly unbound" warnings "Unused arguments" warnings were left untouched, as they might be used as kwargs and renaming/removing them might break stuff. --- isort/api.py | 48 ++++++++++++++++++------------------ isort/comments.py | 12 ++++----- isort/core.py | 6 ++--- isort/deprecated/finders.py | 4 +-- isort/literal.py | 2 +- isort/main.py | 40 ++++++++++++++---------------- isort/output.py | 3 +-- isort/parse.py | 16 ++++++------ isort/settings.py | 2 +- isort/setuptools_commands.py | 2 +- isort/sorting.py | 2 +- isort/stdlibs/__init__.py | 3 ++- isort/wrap_modes.py | 14 +++++------ 13 files changed, 75 insertions(+), 79 deletions(-) diff --git a/isort/api.py b/isort/api.py index aed041ce4..c93851306 100644 --- a/isort/api.py +++ b/isort/api.py @@ -216,30 +216,30 @@ def check_stream( if config.verbose and not config.only_modified: printer.success(f"{file_path or ''} Everything Looks Good!") return True - else: - printer.error(f"{file_path or ''} Imports are incorrectly sorted and/or formatted.") - if show_diff: - output_stream = StringIO() - input_stream.seek(0) - file_contents = input_stream.read() - sort_stream( - input_stream=StringIO(file_contents), - output_stream=output_stream, - extension=extension, - config=config, - file_path=file_path, - disregard_skip=disregard_skip, - ) - output_stream.seek(0) - - show_unified_diff( - file_input=file_contents, - file_output=output_stream.read(), - file_path=file_path, - output=None if show_diff is True else cast(TextIO, show_diff), - color_output=config.color_output, - ) - return False + + printer.error(f"{file_path or ''} Imports are incorrectly sorted and/or formatted.") + if show_diff: + output_stream = StringIO() + input_stream.seek(0) + file_contents = input_stream.read() + sort_stream( + input_stream=StringIO(file_contents), + output_stream=output_stream, + extension=extension, + config=config, + file_path=file_path, + disregard_skip=disregard_skip, + ) + output_stream.seek(0) + + show_unified_diff( + file_input=file_contents, + file_output=output_stream.read(), + file_path=file_path, + output=None if show_diff is True else cast(TextIO, show_diff), + color_output=config.color_output, + ) + return False def check_file( diff --git a/isort/comments.py b/isort/comments.py index b865b3281..55c3da674 100644 --- a/isort/comments.py +++ b/isort/comments.py @@ -24,9 +24,9 @@ def add_to_line( if not comments: return original_string - else: - unique_comments: List[str] = [] - for comment in comments: - if comment not in unique_comments: - unique_comments.append(comment) - return f"{parse(original_string)[0]}{comment_prefix} {'; '.join(unique_comments)}" + + unique_comments: List[str] = [] + for comment in comments: + if comment not in unique_comments: + unique_comments.append(comment) + return f"{parse(original_string)[0]}{comment_prefix} {'; '.join(unique_comments)}" diff --git a/isort/core.py b/isort/core.py index badf2a04c..c4ba054af 100644 --- a/isort/core.py +++ b/isort/core.py @@ -70,8 +70,9 @@ def process( end_of_file: bool = False verbose_output: List[str] = [] all_imports: List[str] = [] + + _output_stream = output_stream # Used if imports_only == True if imports_only: - _output_stream = output_stream class DevNull(StringIO): def write(self, *a, **kw): @@ -435,5 +436,4 @@ def _has_changed(before: str, after: str, line_separator: str, ignore_whitespace remove_whitespace(before, line_separator=line_separator).strip() != remove_whitespace(after, line_separator=line_separator).strip() ) - else: - return before.strip() != after.strip() + return before.strip() != after.strip() diff --git a/isort/deprecated/finders.py b/isort/deprecated/finders.py index dbb6fec02..5be8a419f 100644 --- a/isort/deprecated/finders.py +++ b/isort/deprecated/finders.py @@ -188,9 +188,9 @@ def find(self, module_name: str) -> Optional[str]: or (self.virtual_env and self.virtual_env_src in prefix) ): return sections.THIRDPARTY - elif os.path.normcase(prefix) == self.stdlib_lib_prefix: + if os.path.normcase(prefix) == self.stdlib_lib_prefix: return sections.STDLIB - elif self.conda_env and self.conda_env in prefix: + if self.conda_env and self.conda_env in prefix: return sections.THIRDPARTY for src_path in self.config.src_paths: if src_path in path_obj.parents and not self.config.is_skipped(path_obj): diff --git a/isort/literal.py b/isort/literal.py index 01bd05e79..0b1838fe3 100644 --- a/isort/literal.py +++ b/isort/literal.py @@ -41,7 +41,7 @@ def assignment(code: str, sort_type: str, extension: str, config: Config = DEFAU """ if sort_type == "assignments": return assignments(code) - elif sort_type not in type_mapping: + if sort_type not in type_mapping: raise ValueError( "Trying to sort using an undefined sort_type. " f"Defined sort types are {', '.join(type_mapping.keys())}." diff --git a/isort/main.py b/isort/main.py index d2cd76b00..182dd3f61 100644 --- a/isort/main.py +++ b/isort/main.py @@ -81,27 +81,27 @@ def sort_imports( write_to_stdout: bool = False, **kwargs: Any, ) -> Optional[SortAttempt]: + incorrectly_sorted: bool = False + skipped: bool = False try: - incorrectly_sorted: bool = False - skipped: bool = False if check: try: incorrectly_sorted = not api.check_file(file_name, config=config, **kwargs) except FileSkipped: skipped = True return SortAttempt(incorrectly_sorted, skipped, True) - else: - try: - incorrectly_sorted = not api.sort_file( - file_name, - config=config, - ask_to_apply=ask_to_apply, - write_to_stdout=write_to_stdout, - **kwargs, - ) - except FileSkipped: - skipped = True - return SortAttempt(incorrectly_sorted, skipped, True) + + try: + incorrectly_sorted = not api.sort_file( + file_name, + config=config, + ask_to_apply=ask_to_apply, + write_to_stdout=write_to_stdout, + **kwargs, + ) + except FileSkipped: + skipped = True + return SortAttempt(incorrectly_sorted, skipped, True) except (OSError, ValueError) as error: warn(f"Unable to parse file {file_name} due to {error}") return None @@ -816,14 +816,13 @@ def _preconvert(item): """Preconverts objects from native types into JSONifyiable types""" if isinstance(item, (set, frozenset)): return list(item) - elif isinstance(item, WrapModes): + if isinstance(item, WrapModes): return item.name - elif isinstance(item, Path): + if isinstance(item, Path): return str(item) - elif callable(item) and hasattr(item, "__name__"): + if callable(item) and hasattr(item, "__name__"): return item.__name__ - else: - raise TypeError("Unserializable object {} of type {}".format(item, type(item))) + raise TypeError("Unserializable object {} of type {}".format(item, type(item))) def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = None) -> None: @@ -855,8 +854,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = print(QUICK_GUIDE) if arguments: sys.exit("Error: arguments passed in without any paths or content.") - else: - return + return if "settings_path" not in arguments: arguments["settings_path"] = ( os.path.abspath(file_names[0] if file_names else ".") or os.getcwd() diff --git a/isort/output.py b/isort/output.py index cda5c24d8..66fc44397 100644 --- a/isort/output.py +++ b/isort/output.py @@ -615,5 +615,4 @@ def _with_star_comments(parsed: parse.ParsedContent, module: str, comments: List star_comment = parsed.categorized_comments["nested"].get(module, {}).pop("*", None) if star_comment: return comments + [star_comment] - else: - return comments + return comments diff --git a/isort/parse.py b/isort/parse.py index 6a999391a..819c5cd85 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -31,10 +31,9 @@ def _infer_line_separator(contents: str) -> str: if "\r\n" in contents: return "\r\n" - elif "\r" in contents: + if "\r" in contents: return "\r" - else: - return "\n" + return "\n" def _normalize_line(raw_line: str) -> Tuple[str, str]: @@ -55,11 +54,11 @@ def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: """If the current line is an import line it will return its type (from or straight)""" if config.honor_noqa and line.lower().rstrip().endswith("noqa"): return None - elif "isort:skip" in line or "isort: skip" in line or "isort: split" in line: + if "isort:skip" in line or "isort: skip" in line or "isort: split" in line: return None - elif line.startswith(("import ", "cimport ")): + if line.startswith(("import ", "cimport ")): return "straight" - elif line.startswith("from "): + if line.startswith("from "): return "from" return None @@ -369,6 +368,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte attach_comments_to: Optional[List[Any]] = None direct_imports = just_imports[1:] straight_import = True + top_level_module = "" if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): straight_import = False while "as" in just_imports: @@ -443,7 +443,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte attach_comments_to = categorized_comments["from"].setdefault(import_from, []) if len(out_lines) > max(import_index, 1) - 1: - last = out_lines and out_lines[-1].rstrip() or "" + last = out_lines[-1].rstrip() if out_lines else "" while ( last.startswith("#") and not last.endswith('"""') @@ -489,7 +489,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte if len(out_lines) > max(import_index, +1, 1) - 1: - last = out_lines and out_lines[-1].rstrip() or "" + last = out_lines[-1].rstrip() if out_lines else "" while ( last.startswith("#") and not last.endswith('"""') diff --git a/isort/settings.py b/isort/settings.py index 553475e93..8b93e4236 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -468,7 +468,7 @@ def is_supported_filetype(self, file_name: str): ext = ext.lstrip(".") if ext in self.supported_extensions: return True - elif ext in self.blocked_extensions: + if ext in self.blocked_extensions: return False # Skip editor backup files. diff --git a/isort/setuptools_commands.py b/isort/setuptools_commands.py index 96e41dd0b..6906604ed 100644 --- a/isort/setuptools_commands.py +++ b/isort/setuptools_commands.py @@ -24,7 +24,7 @@ def initialize_options(self) -> None: setattr(self, key, value) def finalize_options(self) -> None: - "Get options from config files." + """Get options from config files.""" self.arguments: Dict[str, Any] = {} # skipcq: PYL-W0201 self.arguments["settings_path"] = os.getcwd() diff --git a/isort/sorting.py b/isort/sorting.py index cab77011b..b614abe79 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -47,7 +47,7 @@ def module_key( or (config.length_sort_straight and straight_import) or str(section_name).lower() in config.length_sort_sections ) - _length_sort_maybe = length_sort and (str(len(module_name)) + ":" + module_name) or module_name + _length_sort_maybe = (str(len(module_name)) + ":" + module_name) if length_sort else module_name return f"{module_name in config.force_to_top and 'A' or 'B'}{prefix}{_length_sort_maybe}" diff --git a/isort/stdlibs/__init__.py b/isort/stdlibs/__init__.py index 9021bc455..ed5aa89dc 100644 --- a/isort/stdlibs/__init__.py +++ b/isort/stdlibs/__init__.py @@ -1 +1,2 @@ -from . import all, py2, py3, py27, py35, py36, py37, py38, py39 +from . import all as _all +from . import py2, py3, py27, py35, py36, py37, py38, py39 diff --git a/isort/wrap_modes.py b/isort/wrap_modes.py index 02b793067..5c2695263 100644 --- a/isort/wrap_modes.py +++ b/isort/wrap_modes.py @@ -250,15 +250,13 @@ def noqa(**interface): <= interface["line_length"] ): return f"{retval}{interface['comment_prefix']} {comment_str}" - elif "NOQA" in interface["comments"]: + if "NOQA" in interface["comments"]: return f"{retval}{interface['comment_prefix']} {comment_str}" - else: - return f"{retval}{interface['comment_prefix']} NOQA {comment_str}" - else: - if len(retval) <= interface["line_length"]: - return retval - else: - return f"{retval}{interface['comment_prefix']} NOQA" + return f"{retval}{interface['comment_prefix']} NOQA {comment_str}" + + if len(retval) <= interface["line_length"]: + return retval + return f"{retval}{interface['comment_prefix']} NOQA" @_wrap_mode From bacd6c7b9f390dbe02e65bdc5a2c1670197f05cf Mon Sep 17 00:00:00 2001 From: jaydesl Date: Sun, 25 Oct 2020 11:42:39 +0000 Subject: [PATCH 165/539] Refactor error printer --- isort/main.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/isort/main.py b/isort/main.py index fa93256c3..84569441f 100644 --- a/isort/main.py +++ b/isort/main.py @@ -110,15 +110,23 @@ def sort_imports( warn(f"Encoding not supported for {file_name}") return SortAttempt(incorrectly_sorted, skipped, False) except Exception: - printer = create_terminal_printer(color=config.color_output) - printer.error( - f"Unrecoverable exception thrown when parsing {file_name}! " - "This should NEVER happen.\n" - "If encountered, please open an issue: https://github.com/PyCQA/isort/issues/new" - ) + _print_hard_fail(config, offending_file=file_name) raise +def _print_hard_fail( + config: Config, offending_file: Optional[str] = None, message: Optional[str] = None +) -> None: + """Fail on unrecoverable exception with custom message.""" + message = message or ( + f"Unrecoverable exception thrown when parsing {offending_file or ''}!" + "This should NEVER happen.\n" + "If encountered, please open an issue: https://github.com/PyCQA/isort/issues/new" + ) + printer = create_terminal_printer(color=config.color_output) + printer.error(message) + + def iter_source_code( paths: Iterable[str], config: Config, skipped: List[str], broken: List[str] ) -> Iterator[str]: From 435d2cd3470c24057ea0519140a000a09be4a526 Mon Sep 17 00:00:00 2001 From: jaydesl Date: Sun, 25 Oct 2020 11:43:16 +0000 Subject: [PATCH 166/539] Gracefully fail on missing default sections --- isort/main.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index 84569441f..8ac59a709 100644 --- a/isort/main.py +++ b/isort/main.py @@ -14,7 +14,7 @@ from .format import create_terminal_printer from .logo import ASCII_ART from .profiles import profiles -from .settings import VALID_PY_TARGETS, Config, WrapModes +from .settings import DEFAULT_CONFIG, VALID_PY_TARGETS, Config, WrapModes try: from .setuptools_commands import ISortCommand # noqa: F401 @@ -109,6 +109,18 @@ def sort_imports( if config.verbose: warn(f"Encoding not supported for {file_name}") return SortAttempt(incorrectly_sorted, skipped, False) + except KeyError as error: + if error.args[0] not in DEFAULT_CONFIG.sections: + _print_hard_fail(config, offending_file=file_name) + raise + msg = ( + f"Found {error} imports while parsing, but {error} was not included " + "in the `sections` setting of your config. Please add it before continuing\n" + "See https://pycqa.github.io/isort/#custom-sections-and-ordering " + "for more info." + ) + _print_hard_fail(config, message=msg) + sys.exit(os.EX_CONFIG) except Exception: _print_hard_fail(config, offending_file=file_name) raise From 1bb257663caefc347b48ab3c2b2bccb2e0c42e50 Mon Sep 17 00:00:00 2001 From: jaydesl Date: Sun, 25 Oct 2020 11:43:30 +0000 Subject: [PATCH 167/539] Improve docs around custom section ordering --- README.md | 2 +- docs/configuration/options.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c2c6a8244..f55c15883 100644 --- a/README.md +++ b/README.md @@ -348,7 +348,7 @@ of: FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER ``` -to your preference: +to your preference (if defined, omitting a default section may cause errors): ```ini sections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER diff --git a/docs/configuration/options.md b/docs/configuration/options.md index bf2205243..de70eda48 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -108,7 +108,9 @@ Forces line endings to the specified value. If not set, values will be guessed p ## Sections -**No Description** +Specifies a custom ordering for sections. Any custom defined sections should also be +included in this ordering. Omitting any of the default sections from this tuple may +result in unexpected sorting or an exception being raised. **Type:** Tuple **Default:** `('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER')` From 94ae8995739a9c8e678527d615d9d9759e0781e9 Mon Sep 17 00:00:00 2001 From: Tonci Kokan Date: Sun, 25 Oct 2020 12:33:29 +0000 Subject: [PATCH 168/539] Fix a typo --- isort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index fa93256c3..ceaf3c8a8 100644 --- a/isort/main.py +++ b/isort/main.py @@ -1003,7 +1003,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = print(f"Skipped {num_skipped} files") num_broken += len(broken) - if num_broken and not arguments.get("quite", False): + if num_broken and not arguments.get("quiet", False): if config.verbose: for was_broken in broken: warn(f"{was_broken} was broken path, make sure it exists correctly") From c6499339e8010f42b32f9c31d40dd8bf6fe15319 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 25 Oct 2020 21:08:49 -0700 Subject: [PATCH 169/539] Add additional contributors: Timur Kushukov (@timqsh), Bhupesh Varshney (@Bhupesh-V), Rohan Khanna (@rohankhanna) , and Vasilis Gerakaris (@vgerak) --- docs/contributing/4.-acknowledgements.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 9c04e3f8c..fcae04e81 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -203,6 +203,10 @@ Code Contributors - James Curtin (@jamescurtin) - Marco Gorelli (@MarcoGorelli) - Louis Sautier (@sbraz) +- Timur Kushukov (@timqsh) +- Bhupesh Varshney (@Bhupesh-V) +- Rohan Khanna (@rohankhanna) +- Vasilis Gerakaris (@vgerak) Documenters =================== From 0233e7873d80a6c3fc59ffbeee99536e7a51e5c4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 25 Oct 2020 21:10:11 -0700 Subject: [PATCH 170/539] Update automatically produced docs (profiles & config options --- docs/configuration/options.md | 77 ++++++++++++++++++++++++++++++---- docs/configuration/profiles.md | 2 + 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index bf2205243..12753c31b 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -179,7 +179,7 @@ Force isort to recognize a module as being a local folder. Generally, this is re Force isort to recognize a module as part of Python's standard library. **Type:** Frozenset -**Default:** `('_dummy_thread', '_thread', 'abc', 'aifc', 'argparse', 'array', 'ast', 'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'base64', 'bdb', 'binascii', 'binhex', 'bisect', 'builtins', 'bz2', 'cProfile', 'calendar', 'cgi', 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections', 'colorsys', 'compileall', 'concurrent', 'configparser', 'contextlib', 'contextvars', 'copy', 'copyreg', 'crypt', 'csv', 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbm', 'decimal', 'difflib', 'dis', 'distutils', 'doctest', 'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno', 'faulthandler', 'fcntl', 'filecmp', 'fileinput', 'fnmatch', 'formatter', 'fpectl', 'fractions', 'ftplib', 'functools', 'gc', 'getopt', 'getpass', 'gettext', 'glob', 'grp', 'gzip', 'hashlib', 'heapq', 'hmac', 'html', 'http', 'imaplib', 'imghdr', 'imp', 'importlib', 'inspect', 'io', 'ipaddress', 'itertools', 'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma', 'macpath', 'mailbox', 'mailcap', 'marshal', 'math', 'mimetypes', 'mmap', 'modulefinder', 'msilib', 'msvcrt', 'multiprocessing', 'netrc', 'nis', 'nntplib', 'ntpath', 'numbers', 'operator', 'optparse', 'os', 'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform', 'plistlib', 'poplib', 'posix', 'posixpath', 'pprint', 'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc', 'queue', 'quopri', 'random', 're', 'readline', 'reprlib', 'resource', 'rlcompleter', 'runpy', 'sched', 'secrets', 'select', 'selectors', 'shelve', 'shlex', 'shutil', 'signal', 'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver', 'spwd', 'sqlite3', 'sre', 'sre_compile', 'sre_constants', 'sre_parse', 'ssl', 'stat', 'statistics', 'string', 'stringprep', 'struct', 'subprocess', 'sunau', 'symbol', 'symtable', 'sys', 'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios', 'test', 'textwrap', 'threading', 'time', 'timeit', 'tkinter', 'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'tty', 'turtle', 'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'uu', 'uuid', 'venv', 'warnings', 'wave', 'weakref', 'webbrowser', 'winreg', 'winsound', 'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'zipapp', 'zipfile', 'zipimport', 'zlib')` +**Default:** `('_dummy_thread', '_thread', 'abc', 'aifc', 'argparse', 'array', 'ast', 'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'base64', 'bdb', 'binascii', 'binhex', 'bisect', 'builtins', 'bz2', 'cProfile', 'calendar', 'cgi', 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections', 'colorsys', 'compileall', 'concurrent', 'configparser', 'contextlib', 'contextvars', 'copy', 'copyreg', 'crypt', 'csv', 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbm', 'decimal', 'difflib', 'dis', 'distutils', 'doctest', 'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno', 'faulthandler', 'fcntl', 'filecmp', 'fileinput', 'fnmatch', 'formatter', 'fpectl', 'fractions', 'ftplib', 'functools', 'gc', 'getopt', 'getpass', 'gettext', 'glob', 'graphlib', 'grp', 'gzip', 'hashlib', 'heapq', 'hmac', 'html', 'http', 'imaplib', 'imghdr', 'imp', 'importlib', 'inspect', 'io', 'ipaddress', 'itertools', 'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma', 'macpath', 'mailbox', 'mailcap', 'marshal', 'math', 'mimetypes', 'mmap', 'modulefinder', 'msilib', 'msvcrt', 'multiprocessing', 'netrc', 'nis', 'nntplib', 'ntpath', 'numbers', 'operator', 'optparse', 'os', 'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform', 'plistlib', 'poplib', 'posix', 'posixpath', 'pprint', 'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc', 'queue', 'quopri', 'random', 're', 'readline', 'reprlib', 'resource', 'rlcompleter', 'runpy', 'sched', 'secrets', 'select', 'selectors', 'shelve', 'shlex', 'shutil', 'signal', 'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver', 'spwd', 'sqlite3', 'sre', 'sre_compile', 'sre_constants', 'sre_parse', 'ssl', 'stat', 'statistics', 'string', 'stringprep', 'struct', 'subprocess', 'sunau', 'symbol', 'symtable', 'sys', 'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios', 'test', 'textwrap', 'threading', 'time', 'timeit', 'tkinter', 'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'tty', 'turtle', 'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'uu', 'uuid', 'venv', 'warnings', 'wave', 'weakref', 'webbrowser', 'winreg', 'winsound', 'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'zipapp', 'zipfile', 'zipimport', 'zlib', 'zoneinfo')` **Python & Config File Name:** known_standard_library **CLI Flags:** @@ -296,7 +296,7 @@ Sort imports by their string length. ## Length Sort Straight -Sort straight imports by their string length. +Sort straight imports by their string length. Similar to `length_sort` but applies only to straight imports and doesn't affect from imports. **Type:** Bool **Default:** `False` @@ -602,7 +602,7 @@ Force all imports to be sorted as a single section ## Force Grid Wrap -Force number of from imports (defaults to 2 when passed as CLI flag without value) to be grid wrapped regardless of line length. If 0 is passed in (the global default) only line length is considered. +Force number of from imports (defaults to 2 when passed as CLI flag without value)to be grid wrapped regardless of line length. If 0 is passed in (the global default) only line length is considered. **Type:** Int **Default:** `0` @@ -633,6 +633,15 @@ Don't sort straight-style imports (like import sys) before from-style imports (l **Python & Config File Name:** lexicographical **CLI Flags:** **Not Supported** +## Group By Package + +**No Description** + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** group_by_package +**CLI Flags:** **Not Supported** + ## Ignore Whitespace Tells isort to ignore whitespace differences when --check-only is being used. @@ -767,8 +776,8 @@ Tells isort to honor noqa comments to enforce skipping those comments. Add an explicitly defined source path (modules within src paths have their imports automatically categorized as first_party). -**Type:** Frozenset -**Default:** `frozenset()` +**Type:** Tuple +**Default:** `()` **Python & Config File Name:** src_paths **CLI Flags:** @@ -800,7 +809,8 @@ Tells isort to remove redundant aliases from imports, such as `import os as os`. ## Float To Top -Causes all non-indented imports to float to the top of the file having its imports sorted. It can be an excellent shortcut for collecting imports every once in a while when you place them in the middle of a file to avoid context switching. +Causes all non-indented imports to float to the top of the file having its imports sorted (immediately below the top of file comment). +This can be an excellent shortcut for collecting imports every once in a while when you place them in the middle of a file to avoid context switching. *NOTE*: It currently doesn't work with cimports and introduces some extra over-head and a performance penalty. @@ -880,7 +890,7 @@ Tells isort to treat all single line comments as if they are code. Specifies what extensions isort can be ran against. **Type:** Frozenset -**Default:** `('py', 'pyi', 'pyx')` +**Default:** `('pxd', 'py', 'pyi', 'pyx')` **Python & Config File Name:** supported_extensions **CLI Flags:** @@ -949,6 +959,48 @@ Causes imports to be sorted only based on their sections like STDLIB,THIRDPARTY - --only-sections - --os +## Only Modified + +Suppresses verbose output for non-modified files. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** only_modified +**CLI Flags:** + +- --only-modified +- --om + +## Combine Straight Imports + +Combines all the bare straight imports of the same section in a single line. Won't work with sections which have 'as' imports + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** combine_straight_imports +**CLI Flags:** + +- --combine-straight-imports +- --csi + +## Auto Identify Namespace Packages + +**No Description** + +**Type:** Bool +**Default:** `True` +**Python & Config File Name:** auto_identify_namespace_packages +**CLI Flags:** **Not Supported** + +## Namespace Packages + +**No Description** + +**Type:** Frozenset +**Default:** `frozenset()` +**Python & Config File Name:** namespace_packages +**CLI Flags:** **Not Supported** + ## Check Checks the file for unsorted / unformatted imports and prints them to the command line without modifying the file. @@ -1090,6 +1142,17 @@ See isort's determined config, as well as sources of config options. - --show-config +## Show Files + +See the files isort will be ran against with the current config options. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** **Not Supported** +**CLI Flags:** + +- --show-files + ## Deprecated Flags ==SUPPRESS== diff --git a/docs/configuration/profiles.md b/docs/configuration/profiles.md index 1b6d6f75e..9de48e0f7 100644 --- a/docs/configuration/profiles.md +++ b/docs/configuration/profiles.md @@ -39,6 +39,8 @@ To use any of the listed profiles, use `isort --profile PROFILE_NAME` from the c - **force_sort_within_sections**: `True` - **lexicographical**: `True` - **single_line_exclusions**: `('typing',)` + - **order_by_type**: `False` + - **group_by_package**: `True` #open_stack From 42ef40994af61e7da4280492afa5843f5bcd9bbe Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 25 Oct 2020 21:11:54 -0700 Subject: [PATCH 171/539] Add @tonci-bw to contributors list --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index fcae04e81..19d06a9f2 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -207,6 +207,7 @@ Code Contributors - Bhupesh Varshney (@Bhupesh-V) - Rohan Khanna (@rohankhanna) - Vasilis Gerakaris (@vgerak) +- @tonci-bw Documenters =================== From 13a96af4678bef5d359b90af76d5c2bc1d0c35a8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 26 Oct 2020 21:01:49 -0700 Subject: [PATCH 172/539] Add @jaydesl to contributors list --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 19d06a9f2..c6b065fde 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -208,6 +208,7 @@ Code Contributors - Rohan Khanna (@rohankhanna) - Vasilis Gerakaris (@vgerak) - @tonci-bw +- @jaydesl Documenters =================== From 35a22053c42797f82c293a59502af815230d2c69 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 27 Oct 2020 23:43:49 -0700 Subject: [PATCH 173/539] Fix issue #1582: Provide a flag for not following links --- isort/main.py | 12 ++++++++---- isort/settings.py | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/isort/main.py b/isort/main.py index c8715e10d..7f776e0cd 100644 --- a/isort/main.py +++ b/isort/main.py @@ -147,7 +147,7 @@ def iter_source_code( for path in paths: if os.path.isdir(path): - for dirpath, dirnames, filenames in os.walk(path, topdown=True, followlinks=True): + for dirpath, dirnames, filenames in os.walk(path, topdown=True, followlinks=config.follow_links): base_path = Path(dirpath) for dirname in list(dirnames): full_path = base_path / dirname @@ -741,7 +741,6 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Tells isort to only show an identical custom import heading comment once, even if" " there are multiple sections with the comment set.", ) - parser.add_argument( "--only-sections", "--os", @@ -750,7 +749,6 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Causes imports to be sorted only based on their sections like STDLIB,THIRDPARTY etc. " "Imports are unaltered and keep their relative positions within the different sections.", ) - parser.add_argument( "--only-modified", "--om", @@ -758,7 +756,6 @@ def _build_arg_parser() -> argparse.ArgumentParser: action="store_true", help="Suppresses verbose output for non-modified files.", ) - parser.add_argument( "--combine-straight-imports", "--csi", @@ -767,6 +764,11 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Combines all the bare straight imports of the same section in a single line. " "Won't work with sections which have 'as' imports", ) + parser.add_argument( + "--dont-follow-links", + dest="dont_follow_links", + action="store_true" + ) # deprecated options parser.add_argument( @@ -823,6 +825,8 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> Dict[str, Any]: if "dont_order_by_type" in arguments: arguments["order_by_type"] = False del arguments["dont_order_by_type"] + if "dont_follow_links" in arguments: + arguments["follow_links"] = False multi_line_output = arguments.get("multi_line_output", None) if multi_line_output: if multi_line_output.isdigit(): diff --git a/isort/settings.py b/isort/settings.py index 8b93e4236..f0bd14b2d 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -203,6 +203,7 @@ class _Config: combine_straight_imports: bool = False auto_identify_namespace_packages: bool = True namespace_packages: FrozenSet[str] = frozenset() + follow_links: bool = True def __post_init__(self): py_version = self.py_version From 669a6c36309ce89581074ab1e3aaa5bf7785aba7 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 27 Oct 2020 23:44:51 -0700 Subject: [PATCH 174/539] Add help --- isort/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index 7f776e0cd..b1274ef33 100644 --- a/isort/main.py +++ b/isort/main.py @@ -767,7 +767,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: parser.add_argument( "--dont-follow-links", dest="dont_follow_links", - action="store_true" + action="store_true", + help="Tells isort not to follow symlinks that are encountered when running recursively.", ) # deprecated options From 21b509797ba72631a506d333e86d8effe4584791 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 27 Oct 2020 23:49:30 -0700 Subject: [PATCH 175/539] black --- isort/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index b1274ef33..0aeed6994 100644 --- a/isort/main.py +++ b/isort/main.py @@ -147,7 +147,9 @@ def iter_source_code( for path in paths: if os.path.isdir(path): - for dirpath, dirnames, filenames in os.walk(path, topdown=True, followlinks=config.follow_links): + for dirpath, dirnames, filenames in os.walk( + path, topdown=True, followlinks=config.follow_links + ): base_path = Path(dirpath) for dirname in list(dirnames): full_path = base_path / dirname From a7bbdc4f564e5267627157044491907038667e4f Mon Sep 17 00:00:00 2001 From: Tamara Khalbashkeeva Date: Thu, 29 Oct 2020 02:02:20 +0200 Subject: [PATCH 176/539] Group options in help: general, target, general output, section output, deprecated --- isort/main.py | 739 ++++++++++++++++++++++++++------------------------ 1 file changed, 381 insertions(+), 358 deletions(-) diff --git a/isort/main.py b/isort/main.py index 0aeed6994..58dfecf77 100644 --- a/isort/main.py +++ b/isort/main.py @@ -4,6 +4,7 @@ import json import os import sys +from gettext import gettext as _ from io import TextIOWrapper from pathlib import Path from typing import Any, Dict, Iterable, Iterator, List, Optional, Sequence, Set @@ -186,18 +187,203 @@ def _build_arg_parser() -> argparse.ArgumentParser: "interactive behavior." " " "If you've used isort 4 but are new to isort 5, see the upgrading guide:" - "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0/." + "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0/.", + add_help=False, # prevent help option from appearing in "optional arguments" group ) - inline_args_group = parser.add_mutually_exclusive_group() - parser.add_argument( - "--src", - "--src-path", - dest="src_paths", + + general_group = parser.add_argument_group("general options") + target_group = parser.add_argument_group("target options") + output_group = parser.add_argument_group("general output options") + inline_args_group = output_group.add_mutually_exclusive_group() + section_group = parser.add_argument_group("section output options") + deprecated_group = parser.add_argument_group("deprecated options") + + general_group.add_argument( + "-h", + "--help", + action="help", + default=argparse.SUPPRESS, + help=_('show this help message and exit'), + ) + general_group.add_argument( + "-V", + "--version", + action="store_true", + dest="show_version", + help="Displays the currently installed version of isort.", + ) + general_group.add_argument( + "--vn", + "--version-number", + action="version", + version=__version__, + help="Returns just the current version number without the logo", + ) + general_group.add_argument( + "-v", + "--verbose", + action="store_true", + dest="verbose", + help="Shows verbose output, such as when files are skipped or when a check is successful.", + ) + general_group.add_argument( + "--only-modified", + "--om", + dest="only_modified", + action="store_true", + help="Suppresses verbose output for non-modified files.", + ) + general_group.add_argument( + "--dedup-headings", + dest="dedup_headings", + action="store_true", + help="Tells isort to only show an identical custom import heading comment once, even if" + " there are multiple sections with the comment set.", + ) + general_group.add_argument( + "-q", + "--quiet", + action="store_true", + dest="quiet", + help="Shows extra quiet output, only errors are outputted.", + ) + general_group.add_argument( + "-d", + "--stdout", + help="Force resulting output to stdout, instead of in-place.", + dest="write_to_stdout", + action="store_true", + ) + general_group.add_argument( + "--show-config", + dest="show_config", + action="store_true", + help="See isort's determined config, as well as sources of config options.", + ) + general_group.add_argument( + "--show-files", + dest="show_files", + action="store_true", + help="See the files isort will be ran against with the current config options.", + ) + general_group.add_argument( + "--df", + "--diff", + dest="show_diff", + action="store_true", + help="Prints a diff of all the changes isort would make to a file, instead of " + "changing it in place", + ) + general_group.add_argument( + "-c", + "--check-only", + "--check", + action="store_true", + dest="check", + help="Checks the file for unsorted / unformatted imports and prints them to the " + "command line without modifying the file.", + ) + general_group.add_argument( + "--ws", + "--ignore-whitespace", + action="store_true", + dest="ignore_whitespace", + help="Tells isort to ignore whitespace differences when --check-only is being used.", + ) + general_group.add_argument( + "--sp", + "--settings-path", + "--settings-file", + "--settings", + dest="settings_path", + help="Explicitly set the settings path or file instead of auto determining " + "based on file location.", + ) + general_group.add_argument( + "--profile", + dest="profile", + type=str, + help="Base profile type to use for configuration. " + f"Profiles include: {', '.join(profiles.keys())}. As well as any shared profiles.", + ) + general_group.add_argument( + "--old-finders", + "--magic-placement", + dest="old_finders", + action="store_true", + help="Use the old deprecated finder logic that relies on environment introspection magic.", + ) + general_group.add_argument( + "-j", "--jobs", help="Number of files to process in parallel.", dest="jobs", type=int + ) + general_group.add_argument( + "--ac", + "--atomic", + dest="atomic", + action="store_true", + help="Ensures the output doesn't save if the resulting file contains syntax errors.", + ) + general_group.add_argument( + "--interactive", + dest="ask_to_apply", + action="store_true", + help="Tells isort to apply changes interactively.", + ) + + target_group.add_argument( + "files", nargs="*", help="One or more Python source files that need their imports sorted." + ) + target_group.add_argument( + "--filter-files", + dest="filter_files", + action="store_true", + help="Tells isort to filter files even when they are explicitly passed in as " + "part of the CLI command.", + ) + target_group.add_argument( + "-s", + "--skip", + help="Files that sort imports should skip over. If you want to skip multiple " + "files you should specify twice: --skip file1 --skip file2.", + dest="skip", action="append", - help="Add an explicitly defined source path " - "(modules within src paths have their imports automatically categorized as first_party).", ) - parser.add_argument( + target_group.add_argument( + "--sg", + "--skip-glob", + help="Files that sort imports should skip over.", + dest="skip_glob", + action="append", + ) + target_group.add_argument( + "--gitignore", + "--skip-gitignore", + action="store_true", + dest="skip_gitignore", + help="Treat project as a git repository and ignore files listed in .gitignore", + ) + target_group.add_argument( + "--ext", + "--extension", + "--supported-extension", + dest="supported_extensions", + action="append", + help="Specifies what extensions isort can be ran against.", + ) + target_group.add_argument( + "--blocked-extension", + dest="blocked_extensions", + action="append", + help="Specifies what extensions isort can never be ran against.", + ) + target_group.add_argument( + "--dont-follow-links", + dest="dont_follow_links", + action="store_true", + help="Tells isort not to follow symlinks that are encountered when running recursively.", + ) + + output_group.add_argument( "-a", "--add-import", dest="add_imports", @@ -205,58 +391,47 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Adds the specified import line to all files, " "automatically determining correct placement.", ) - parser.add_argument( + output_group.add_argument( "--append", "--append-only", dest="append_only", action="store_true", - help="Only adds the imports specified in --add-imports if the file" + help="Only adds the imports specified in --add-import if the file" " contains existing imports.", ) - parser.add_argument( - "--ac", - "--atomic", - dest="atomic", - action="store_true", - help="Ensures the output doesn't save if the resulting file contains syntax errors.", - ) - parser.add_argument( + output_group.add_argument( "--af", "--force-adds", dest="force_adds", action="store_true", help="Forces import adds even if the original file is empty.", ) - parser.add_argument( - "-b", - "--builtin", - dest="known_standard_library", - action="append", - help="Force isort to recognize a module as part of Python's standard library.", - ) - parser.add_argument( - "--extra-builtin", - dest="extra_standard_library", + output_group.add_argument( + "--rm", + "--remove-import", + dest="remove_imports", action="append", - help="Extra modules to be included in the list of ones in Python's standard library.", + help="Removes the specified import from all files.", ) - parser.add_argument( - "-c", - "--check-only", - "--check", + output_group.add_argument( + "--float-to-top", + dest="float_to_top", action="store_true", - dest="check", - help="Checks the file for unsorted / unformatted imports and prints them to the " - "command line without modifying the file.", + help="Causes all non-indented imports to float to the top of the file having its imports " + "sorted (immediately below the top of file comment).\n" + "This can be an excellent shortcut for collecting imports every once in a while " + "when you place them in the middle of a file to avoid context switching.\n\n" + "*NOTE*: It currently doesn't work with cimports and introduces some extra over-head " + "and a performance penalty.", ) - parser.add_argument( + output_group.add_argument( "--ca", "--combine-as", dest="combine_as_imports", action="store_true", help="Combines as imports on the same line.", ) - parser.add_argument( + output_group.add_argument( "--cs", "--combine-star", dest="combine_star", @@ -264,69 +439,21 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Ensures that if a star import is present, " "nothing else is imported from that namespace.", ) - parser.add_argument( - "-d", - "--stdout", - help="Force resulting output to stdout, instead of in-place.", - dest="write_to_stdout", - action="store_true", - ) - parser.add_argument( - "--df", - "--diff", - dest="show_diff", - action="store_true", - help="Prints a diff of all the changes isort would make to a file, instead of " - "changing it in place", - ) - parser.add_argument( - "--ds", - "--no-sections", - help="Put all imports into the same section bucket", - dest="no_sections", - action="store_true", - ) - parser.add_argument( + output_group.add_argument( "-e", "--balanced", dest="balanced_wrapping", action="store_true", help="Balances wrapping to produce the most consistent line length possible", ) - parser.add_argument( - "-f", - "--future", - dest="known_future_library", - action="append", - help="Force isort to recognize a module as part of Python's internal future compatibility " - "libraries. WARNING: this overrides the behavior of __future__ handling and therefore" - " can result in code that can't execute. If you're looking to add dependencies such " - "as six a better option is to create a another section below --future using custom " - "sections. See: https://github.com/PyCQA/isort#custom-sections-and-ordering and the " - "discussion here: https://github.com/PyCQA/isort/issues/1463.", - ) - parser.add_argument( - "--fas", - "--force-alphabetical-sort", - action="store_true", - dest="force_alphabetical_sort", - help="Force all imports to be sorted as a single section", - ) - parser.add_argument( - "--fass", - "--force-alphabetical-sort-within-sections", - action="store_true", - dest="force_alphabetical_sort_within_sections", - help="Force all imports to be sorted alphabetically within a section", - ) - parser.add_argument( + output_group.add_argument( "--ff", "--from-first", dest="from_first", help="Switches the typical ordering preference, " "showing from imports first then straight ones.", ) - parser.add_argument( + output_group.add_argument( "--fgw", "--force-grid-wrap", nargs="?", @@ -337,42 +464,34 @@ def _build_arg_parser() -> argparse.ArgumentParser: "to be grid wrapped regardless of line " "length. If 0 is passed in (the global default) only line length is considered.", ) - parser.add_argument( - "--fss", - "--force-sort-within-sections", - action="store_true", - dest="force_sort_within_sections", - help="Don't sort straight-style imports (like import sys) before from-style imports " - "(like from itertools import groupby). Instead, sort the imports by module, " - "independent of import style.", - ) - parser.add_argument( + output_group.add_argument( "-i", "--indent", help='String to place for indents defaults to " " (4 spaces).', dest="indent", type=str, ) - parser.add_argument( - "-j", "--jobs", help="Number of files to process in parallel.", dest="jobs", type=int + output_group.add_argument( + "--lai", "--lines-after-imports", dest="lines_after_imports", type=int + ) + output_group.add_argument( + "--lbt", "--lines-between-types", dest="lines_between_types", type=int ) - parser.add_argument("--lai", "--lines-after-imports", dest="lines_after_imports", type=int) - parser.add_argument("--lbt", "--lines-between-types", dest="lines_between_types", type=int) - parser.add_argument( + output_group.add_argument( "--le", "--line-ending", dest="line_ending", help="Forces line endings to the specified value. " "If not set, values will be guessed per-file.", ) - parser.add_argument( + output_group.add_argument( "--ls", "--length-sort", help="Sort imports by their string length.", dest="length_sort", action="store_true", ) - parser.add_argument( + output_group.add_argument( "--lss", "--length-sort-straight", help="Sort straight imports by their string length. Similar to `length_sort` " @@ -380,7 +499,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="length_sort_straight", action="store_true", ) - parser.add_argument( + output_group.add_argument( "-m", "--multi-line", dest="multi_line_output", @@ -392,7 +511,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: "8-vertical-hanging-indent-bracket, 9-vertical-prefix-from-module-import, " "10-hanging-indent-with-parentheses).", ) - parser.add_argument( + output_group.add_argument( "-n", "--ensure-newline-before-comments", dest="ensure_newline_before_comments", @@ -407,21 +526,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Leaves `from` imports with multiple imports 'as-is' " "(e.g. `from foo import a, c ,b`).", ) - parser.add_argument( - "--nlb", - "--no-lines-before", - help="Sections which should not be split with previous by empty lines", - dest="no_lines_before", - action="append", - ) - parser.add_argument( - "-o", - "--thirdparty", - dest="known_third_party", - action="append", - help="Force isort to recognize a module as being part of a third party library.", - ) - parser.add_argument( + output_group.add_argument( "--ot", "--order-by-type", dest="order_by_type", @@ -434,7 +539,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: "likely will want to turn it off. From the CLI the `--dont-order-by-type` option will turn " "this off.", ) - parser.add_argument( + output_group.add_argument( "--dt", "--dont-order-by-type", dest="dont_order_by_type", @@ -447,69 +552,13 @@ def _build_arg_parser() -> argparse.ArgumentParser: " or a related coding standard and has many imports this is a good default. You can turn " "this on from the CLI using `--order-by-type`.", ) - parser.add_argument( - "-p", - "--project", - dest="known_first_party", - action="append", - help="Force isort to recognize a module as being part of the current python project.", - ) - parser.add_argument( - "--known-local-folder", - dest="known_local_folder", - action="append", - help="Force isort to recognize a module as being a local folder. " - "Generally, this is reserved for relative imports (from . import module).", - ) - parser.add_argument( - "-q", - "--quiet", - action="store_true", - dest="quiet", - help="Shows extra quiet output, only errors are outputted.", - ) - parser.add_argument( - "--rm", - "--remove-import", - dest="remove_imports", - action="append", - help="Removes the specified import from all files.", - ) - parser.add_argument( + output_group.add_argument( "--rr", "--reverse-relative", dest="reverse_relative", action="store_true", help="Reverse order of relative imports.", ) - parser.add_argument( - "-s", - "--skip", - help="Files that sort imports should skip over. If you want to skip multiple " - "files you should specify twice: --skip file1 --skip file2.", - dest="skip", - action="append", - ) - parser.add_argument( - "--sd", - "--section-default", - dest="default_section", - help="Sets the default section for import options: " + str(sections.DEFAULT), - ) - parser.add_argument( - "--sg", - "--skip-glob", - help="Files that sort imports should skip over.", - dest="skip_glob", - action="append", - ) - parser.add_argument( - "--gitignore", - "--skip-gitignore", - action="store_true", - dest="skip_gitignore", - help="Treat project as a git repository and ignore files listed in .gitignore", - ) inline_args_group.add_argument( "--sl", "--force-single-line-imports", @@ -517,37 +566,21 @@ def _build_arg_parser() -> argparse.ArgumentParser: action="store_true", help="Forces all from imports to appear on their own line", ) - parser.add_argument( + output_group.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", - "--settings-file", - "--settings", - dest="settings_path", - help="Explicitly set the settings path or file instead of auto determining " - "based on file location.", - ) - parser.add_argument( - "-t", - "--top", - help="Force specific imports to the top of their appropriate section.", - dest="force_to_top", - action="append", - ) - parser.add_argument( + output_group.add_argument( "--tc", "--trailing-comma", dest="include_trailing_comma", action="store_true", help="Includes a trailing comma on multi line imports that include parentheses.", ) - parser.add_argument( + output_group.add_argument( "--up", "--use-parentheses", dest="use_parentheses", @@ -556,38 +589,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: " **NOTE**: This is separate from wrap modes, and only affects how individual lines that " " are too long get continued, not sections of multiple imports.", ) - parser.add_argument( - "-V", - "--version", - action="store_true", - dest="show_version", - help="Displays the currently installed version of isort.", - ) - parser.add_argument( - "-v", - "--verbose", - action="store_true", - dest="verbose", - help="Shows verbose output, such as when files are skipped or when a check is successful.", - ) - parser.add_argument( - "--virtual-env", - dest="virtual_env", - help="Virtual environment to use for determining whether a package is third-party", - ) - parser.add_argument( - "--conda-env", - dest="conda_env", - help="Conda environment to use for determining whether a package is third-party", - ) - parser.add_argument( - "--vn", - "--version-number", - action="version", - version=__version__, - help="Returns just the current version number without the logo", - ) - parser.add_argument( + output_group.add_argument( "-l", "-w", "--line-length", @@ -596,7 +598,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="line_length", type=int, ) - parser.add_argument( + output_group.add_argument( "--wl", "--wrap-length", dest="wrap_length", @@ -604,80 +606,13 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Specifies how long lines that are wrapped should be, if not set line_length is used." "\nNOTE: wrap_length must be LOWER than or equal to line_length.", ) - parser.add_argument( - "--ws", - "--ignore-whitespace", - action="store_true", - dest="ignore_whitespace", - help="Tells isort to ignore whitespace differences when --check-only is being used.", - ) - parser.add_argument( + output_group.add_argument( "--case-sensitive", dest="case_sensitive", action="store_true", help="Tells isort to include casing when sorting module names", ) - parser.add_argument( - "--filter-files", - dest="filter_files", - action="store_true", - help="Tells isort to filter files even when they are explicitly passed in as " - "part of the CLI command.", - ) - parser.add_argument( - "files", nargs="*", help="One or more Python source files that need their imports sorted." - ) - parser.add_argument( - "--py", - "--python-version", - action="store", - dest="py_version", - choices=tuple(VALID_PY_TARGETS) + ("auto",), - help="Tells isort to set the known standard library based on the specified Python " - "version. Default is to assume any Python 3 version could be the target, and use a union " - "of all stdlib modules across versions. If auto is specified, the version of the " - "interpreter used to run isort " - f"(currently: {sys.version_info.major}{sys.version_info.minor}) will be used.", - ) - parser.add_argument( - "--profile", - dest="profile", - type=str, - help="Base profile type to use for configuration. " - f"Profiles include: {', '.join(profiles.keys())}. As well as any shared profiles.", - ) - parser.add_argument( - "--interactive", - dest="ask_to_apply", - action="store_true", - help="Tells isort to apply changes interactively.", - ) - parser.add_argument( - "--old-finders", - "--magic-placement", - dest="old_finders", - action="store_true", - help="Use the old deprecated finder logic that relies on environment introspection magic.", - ) - parser.add_argument( - "--show-config", - dest="show_config", - action="store_true", - help="See isort's determined config, as well as sources of config options.", - ) - parser.add_argument( - "--show-files", - dest="show_files", - action="store_true", - help="See the files isort will be ran against with the current config options.", - ) - parser.add_argument( - "--honor-noqa", - dest="honor_noqa", - action="store_true", - help="Tells isort to honor noqa comments to enforce skipping those comments.", - ) - parser.add_argument( + output_group.add_argument( "--remove-redundant-aliases", dest="remove_redundant_aliases", action="store_true", @@ -687,63 +622,44 @@ def _build_arg_parser() -> argparse.ArgumentParser: " aliases to signify intent and change behaviour." ), ) - parser.add_argument( - "--color", - dest="color_output", - action="store_true", - help="Tells isort to use color in terminal output.", - ) - parser.add_argument( - "--float-to-top", - dest="float_to_top", + output_group.add_argument( + "--honor-noqa", + dest="honor_noqa", action="store_true", - help="Causes all non-indented imports to float to the top of the file having its imports " - "sorted (immediately below the top of file comment).\n" - "This can be an excellent shortcut for collecting imports every once in a while " - "when you place them in the middle of a file to avoid context switching.\n\n" - "*NOTE*: It currently doesn't work with cimports and introduces some extra over-head " - "and a performance penalty.", + help="Tells isort to honor noqa comments to enforce skipping those comments.", ) - parser.add_argument( + output_group.add_argument( "--treat-comment-as-code", dest="treat_comments_as_code", action="append", help="Tells isort to treat the specified single line comment(s) as if they are code.", ) - parser.add_argument( + output_group.add_argument( "--treat-all-comment-as-code", dest="treat_all_comments_as_code", action="store_true", help="Tells isort to treat all single line comments as if they are code.", ) - parser.add_argument( + output_group.add_argument( "--formatter", dest="formatter", type=str, help="Specifies the name of a formatting plugin to use when producing output.", ) - parser.add_argument( - "--ext", - "--extension", - "--supported-extension", - dest="supported_extensions", - action="append", - help="Specifies what extensions isort can be ran against.", - ) - parser.add_argument( - "--blocked-extension", - dest="blocked_extensions", - action="append", - help="Specifies what extensions isort can never be ran against.", - ) - parser.add_argument( - "--dedup-headings", - dest="dedup_headings", + output_group.add_argument( + "--color", + dest="color_output", action="store_true", - help="Tells isort to only show an identical custom import heading comment once, even if" - " there are multiple sections with the comment set.", + help="Tells isort to use color in terminal output.", + ) + + section_group.add_argument( + "--sd", + "--section-default", + dest="default_section", + help="Sets the default section for import options: " + str(sections.DEFAULT), ) - parser.add_argument( + section_group.add_argument( "--only-sections", "--os", dest="only_sections", @@ -751,14 +667,44 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Causes imports to be sorted only based on their sections like STDLIB,THIRDPARTY etc. " "Imports are unaltered and keep their relative positions within the different sections.", ) - parser.add_argument( - "--only-modified", - "--om", - dest="only_modified", + section_group.add_argument( + "--ds", + "--no-sections", + help="Put all imports into the same section bucket", + dest="no_sections", action="store_true", - help="Suppresses verbose output for non-modified files.", ) - parser.add_argument( + section_group.add_argument( + "--fas", + "--force-alphabetical-sort", + action="store_true", + dest="force_alphabetical_sort", + help="Force all imports to be sorted as a single section", + ) + section_group.add_argument( + "--fss", + "--force-sort-within-sections", + action="store_true", + dest="force_sort_within_sections", + help="Don't sort straight-style imports (like import sys) before from-style imports " + "(like from itertools import groupby). Instead, sort the imports by module, " + "independent of import style.", + ) + section_group.add_argument( + "--fass", + "--force-alphabetical-sort-within-sections", + action="store_true", + dest="force_alphabetical_sort_within_sections", + help="Force all imports to be sorted alphabetically within a section", + ) + section_group.add_argument( + "-t", + "--top", + help="Force specific imports to the top of their appropriate section.", + dest="force_to_top", + action="append", + ) + section_group.add_argument( "--combine-straight-imports", "--csi", dest="combine_straight_imports", @@ -766,42 +712,119 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Combines all the bare straight imports of the same section in a single line. " "Won't work with sections which have 'as' imports", ) - parser.add_argument( - "--dont-follow-links", - dest="dont_follow_links", - action="store_true", - help="Tells isort not to follow symlinks that are encountered when running recursively.", + section_group.add_argument( + "--nlb", + "--no-lines-before", + help="Sections which should not be split with previous by empty lines", + dest="no_lines_before", + action="append", + ) + section_group.add_argument( + "--src", + "--src-path", + dest="src_paths", + action="append", + help="Add an explicitly defined source path " + "(modules within src paths have their imports automatically categorized as first_party).", + ) + section_group.add_argument( + "-b", + "--builtin", + dest="known_standard_library", + action="append", + help="Force isort to recognize a module as part of Python's standard library.", + ) + section_group.add_argument( + "--extra-builtin", + dest="extra_standard_library", + action="append", + help="Extra modules to be included in the list of ones in Python's standard library.", + ) + section_group.add_argument( + "-f", + "--future", + dest="known_future_library", + action="append", + help="Force isort to recognize a module as part of Python's internal future compatibility " + "libraries. WARNING: this overrides the behavior of __future__ handling and therefore" + " can result in code that can't execute. If you're looking to add dependencies such " + "as six a better option is to create a another section below --future using custom " + "sections. See: https://github.com/PyCQA/isort#custom-sections-and-ordering and the " + "discussion here: https://github.com/PyCQA/isort/issues/1463.", + ) + section_group.add_argument( + "-o", + "--thirdparty", + dest="known_third_party", + action="append", + help="Force isort to recognize a module as being part of a third party library.", + ) + section_group.add_argument( + "-p", + "--project", + dest="known_first_party", + action="append", + help="Force isort to recognize a module as being part of the current python project.", + ) + section_group.add_argument( + "--known-local-folder", + dest="known_local_folder", + action="append", + help="Force isort to recognize a module as being a local folder. " + "Generally, this is reserved for relative imports (from . import module).", + ) + section_group.add_argument( + "--virtual-env", + dest="virtual_env", + help="Virtual environment to use for determining whether a package is third-party", + ) + section_group.add_argument( + "--conda-env", + dest="conda_env", + help="Conda environment to use for determining whether a package is third-party", + ) + section_group.add_argument( + "--py", + "--python-version", + action="store", + dest="py_version", + choices=tuple(VALID_PY_TARGETS) + ("auto",), + help="Tells isort to set the known standard library based on the specified Python " + "version. Default is to assume any Python 3 version could be the target, and use a union " + "of all stdlib modules across versions. If auto is specified, the version of the " + "interpreter used to run isort " + f"(currently: {sys.version_info.major}{sys.version_info.minor}) will be used.", ) # deprecated options - parser.add_argument( + deprecated_group.add_argument( "--recursive", dest="deprecated_flags", action="append_const", const="--recursive", help=argparse.SUPPRESS, ) - parser.add_argument( + deprecated_group.add_argument( "-rc", dest="deprecated_flags", action="append_const", const="-rc", help=argparse.SUPPRESS ) - parser.add_argument( + deprecated_group.add_argument( "--dont-skip", dest="deprecated_flags", action="append_const", const="--dont-skip", help=argparse.SUPPRESS, ) - parser.add_argument( + deprecated_group.add_argument( "-ns", dest="deprecated_flags", action="append_const", const="-ns", help=argparse.SUPPRESS ) - parser.add_argument( + deprecated_group.add_argument( "--apply", dest="deprecated_flags", action="append_const", const="--apply", help=argparse.SUPPRESS, ) - parser.add_argument( + deprecated_group.add_argument( "-k", "--keep-direct-and-as", dest="deprecated_flags", From 04766b35855355d4e02fbb0039efced816bada8f Mon Sep 17 00:00:00 2001 From: Tamara Khalbashkeeva Date: Thu, 29 Oct 2020 02:30:09 +0200 Subject: [PATCH 177/539] black --- isort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index 58dfecf77..a795ffac1 100644 --- a/isort/main.py +++ b/isort/main.py @@ -203,7 +203,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: "--help", action="help", default=argparse.SUPPRESS, - help=_('show this help message and exit'), + help=_("show this help message and exit"), ) general_group.add_argument( "-V", From 22f4161c7f2beab321302e07656bdae883674ba3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 28 Oct 2020 23:55:04 -0700 Subject: [PATCH 178/539] Add Tamara (@infinityxxx) to acknowledgements --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index c6b065fde..a3ec7471f 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -209,6 +209,7 @@ Code Contributors - Vasilis Gerakaris (@vgerak) - @tonci-bw - @jaydesl +- Tamara (@infinityxxx) Documenters =================== From 3c7344f4d6991a93039f74732f526f2ec621af73 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Fri, 30 Oct 2020 14:03:10 +0100 Subject: [PATCH 179/539] Profile: follow black behaviour with regard to gitignore Fixes #1585 --- isort/profiles.py | 1 + 1 file changed, 1 insertion(+) diff --git a/isort/profiles.py b/isort/profiles.py index cb8cb5688..523b1ec66 100644 --- a/isort/profiles.py +++ b/isort/profiles.py @@ -8,6 +8,7 @@ "use_parentheses": True, "ensure_newline_before_comments": True, "line_length": 88, + "skip_gitignore": True, } django = { "combine_as_imports": True, From 0ce5bfc0594cb9eb741d7d89484042aa7a9638df Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Fri, 30 Oct 2020 15:10:36 +0100 Subject: [PATCH 180/539] Implement custom output handling in sort_file This will allow the following to be used: isort_diff = StringIO() isort_output = StringIO() isort.file("x.py", show_diff=isort_diff, output=isort_output) Fixes #1583 --- isort/api.py | 110 +++++++++++++++++---------- tests/unit/test_ticketed_features.py | 25 ++++++ 2 files changed, 94 insertions(+), 41 deletions(-) diff --git a/isort/api.py b/isort/api.py index 611187437..502994b66 100644 --- a/isort/api.py +++ b/isort/api.py @@ -284,6 +284,7 @@ def sort_file( ask_to_apply: bool = False, show_diff: Union[bool, TextIO] = False, write_to_stdout: bool = False, + output: Optional[TextIO] = None, **config_kwargs, ) -> bool: """Sorts and formats any groups of imports imports within the provided file or Path. @@ -298,6 +299,8 @@ def sort_file( - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a TextIO stream is provided results will be written to it, otherwise no diff will be computed. - **write_to_stdout**: If `True`, write to stdout instead of the input file. + - **output**: If a TextIO is provided, results will be written there rather than replacing + the original file content. - ****config_kwargs**: Any config modifications. """ with io.File.read(filename) as source_file: @@ -315,49 +318,74 @@ def sort_file( extension=extension, ) else: - tmp_file = source_file.path.with_suffix(source_file.path.suffix + ".isorted") - try: - with tmp_file.open( - "w", encoding=source_file.encoding, newline="" - ) as output_stream: - shutil.copymode(filename, tmp_file) - changed = sort_stream( - input_stream=source_file.stream, - output_stream=output_stream, - config=config, - file_path=actual_file_path, - disregard_skip=disregard_skip, - extension=extension, - ) + if output is None: + tmp_file = source_file.path.with_suffix(source_file.path.suffix + ".isorted") + try: + with tmp_file.open( + "w", encoding=source_file.encoding, newline="" + ) as output_stream: + shutil.copymode(filename, tmp_file) + changed = sort_stream( + input_stream=source_file.stream, + output_stream=output_stream, + config=config, + file_path=actual_file_path, + disregard_skip=disregard_skip, + extension=extension, + ) + if changed: + if show_diff or ask_to_apply: + source_file.stream.seek(0) + with tmp_file.open( + encoding=source_file.encoding, newline="" + ) as tmp_out: + show_unified_diff( + file_input=source_file.stream.read(), + file_output=tmp_out.read(), + file_path=actual_file_path, + output=None + if show_diff is True + else cast(TextIO, show_diff), + color_output=config.color_output, + ) + if show_diff or ( + ask_to_apply + and not ask_whether_to_apply_changes_to_file( + str(source_file.path) + ) + ): + return False + source_file.stream.close() + tmp_file.replace(source_file.path) + if not config.quiet: + print(f"Fixing {source_file.path}") + finally: + try: # Python 3.8+: use `missing_ok=True` instead of try except. + tmp_file.unlink() + except FileNotFoundError: + pass # pragma: no cover + else: + changed = sort_stream( + input_stream=source_file.stream, + output_stream=output, + config=config, + file_path=actual_file_path, + disregard_skip=disregard_skip, + extension=extension, + ) if changed: - if show_diff or ask_to_apply: + if show_diff: source_file.stream.seek(0) - with tmp_file.open( - encoding=source_file.encoding, newline="" - ) as tmp_out: - show_unified_diff( - file_input=source_file.stream.read(), - file_output=tmp_out.read(), - file_path=actual_file_path, - output=None if show_diff is True else cast(TextIO, show_diff), - color_output=config.color_output, - ) - if show_diff or ( - ask_to_apply - and not ask_whether_to_apply_changes_to_file( - str(source_file.path) - ) - ): - return False - source_file.stream.close() - tmp_file.replace(source_file.path) - if not config.quiet: - print(f"Fixing {source_file.path}") - finally: - try: # Python 3.8+: use `missing_ok=True` instead of try except. - tmp_file.unlink() - except FileNotFoundError: - pass # pragma: no cover + output.seek(0) + show_unified_diff( + file_input=source_file.stream.read(), + file_output=output.read(), + file_path=actual_file_path, + output=None if show_diff is True else cast(TextIO, show_diff), + color_output=config.color_output, + ) + source_file.stream.close() + except ExistingSyntaxErrors: warn(f"{actual_file_path} unable to sort due to existing syntax errors") except IntroducedSyntaxErrors: # pragma: no cover diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 51b130095..050d1c4c7 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -848,3 +848,28 @@ def my_function(): pass """ ) + + +def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): + """isort should provide a way from the Python API to process an existing + file and output to a stream the new version of that file, as well as a diff + to a different stream. + See: https://github.com/PyCQA/isort/issues/1583 + """ + + tmp_file = tmpdir.join("file.py") + tmp_file.write("import b\nimport a\n") + + isort_diff = StringIO() + isort_output = StringIO() + + isort.file(tmp_file, show_diff=isort_diff, output=isort_output) + + _, error = capsys.readouterr() + assert not error + + isort_diff.seek(0) + assert "+import a\n import b\n-import a\n" in isort_diff.read() + + isort_output.seek(0) + assert isort_output.read() == "import a\nimport b\n" From a663af8c1f40eb524d8a3cb296ac021822f6cf4d Mon Sep 17 00:00:00 2001 From: Akihiro Nitta Date: Sun, 1 Nov 2020 03:41:43 +0900 Subject: [PATCH 181/539] Create branch issue/1579 From 6282d29b4ab041db4a9e824e738dfc96a9ec56d7 Mon Sep 17 00:00:00 2001 From: Akihiro Nitta Date: Sun, 1 Nov 2020 04:00:00 +0900 Subject: [PATCH 182/539] Update description of --check --- docs/configuration/options.md | 2 +- isort/main.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index 289926106..d32e3f2a6 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -1005,7 +1005,7 @@ Combines all the bare straight imports of the same section in a single line. Won ## Check -Checks the file for unsorted / unformatted imports and prints them to the command line without modifying the file. +Checks the file for unsorted / unformatted imports and prints them to the command line without modifying the file. Returns 0 when nothing would change and returns 1 when the file would be reformatted. **Type:** Bool **Default:** `False` diff --git a/isort/main.py b/isort/main.py index a795ffac1..5dd661f41 100644 --- a/isort/main.py +++ b/isort/main.py @@ -281,7 +281,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: action="store_true", dest="check", help="Checks the file for unsorted / unformatted imports and prints them to the " - "command line without modifying the file.", + "command line without modifying the file. Returns 0 when nothing would change and " + "returns 1 when the file would be reformatted.", ) general_group.add_argument( "--ws", From 5472c810943d7d23c3ce016ceb4d9c24084d8e8c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 31 Oct 2020 23:47:56 -0700 Subject: [PATCH 183/539] Fix non-idempotent behavior for combine-straight --- isort/output.py | 5 ++--- tests/integration/test_setting_combinations.py | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/isort/output.py b/isort/output.py index 66fc44397..45faa6468 100644 --- a/isort/output.py +++ b/isort/output.py @@ -527,9 +527,8 @@ def _with_straight_imports( for module in straight_modules: if module in parsed.categorized_comments["above"]["straight"]: above_comments.extend(parsed.categorized_comments["above"]["straight"].pop(module)) - - for module in parsed.categorized_comments["straight"]: - inline_comments.extend(parsed.categorized_comments["straight"][module]) + if module in parsed.categorized_comments["straight"]: + inline_comments.extend(parsed.categorized_comments["straight"][module]) combined_straight_imports = ", ".join(straight_modules) if inline_comments: diff --git a/tests/integration/test_setting_combinations.py b/tests/integration/test_setting_combinations.py index 929b877a9..7b57199f0 100644 --- a/tests/integration/test_setting_combinations.py +++ b/tests/integration/test_setting_combinations.py @@ -993,6 +993,13 @@ def _raise(*a): ), disregard_skip=True, ) +@hypothesis.example( + config=isort.Config( + py_version="2", + combine_straight_imports=True, + ), + disregard_skip=True, +) @hypothesis.given( config=st.from_type(isort.Config), disregard_skip=st.booleans(), From 4cc84eec18cbc31d82741a5f8bc7d57f59ee8653 Mon Sep 17 00:00:00 2001 From: Timothy Edmund Crosley Date: Sun, 1 Nov 2020 00:03:34 -0700 Subject: [PATCH 184/539] Update test_ticketed_features.py Update test to be OS newline agnostic for diff --- tests/unit/test_ticketed_features.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 050d1c4c7..58a3efcae 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -869,7 +869,10 @@ def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): assert not error isort_diff.seek(0) - assert "+import a\n import b\n-import a\n" in isort_diff.read() - + isort_diff_content = isort_diff.read() + assert "+import a" in isort_diff_content + assert " import b" in isort_diff_content + assert "-import a" in isort_diff_content + isort_output.seek(0) assert isort_output.read() == "import a\nimport b\n" From d40ca26754c9eed472131a07b96f5d3ba73dd5bb Mon Sep 17 00:00:00 2001 From: Timothy Edmund Crosley Date: Sun, 1 Nov 2020 00:11:37 -0700 Subject: [PATCH 185/539] OS agnostic test changes --- tests/unit/test_ticketed_features.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 58a3efcae..76f08894c 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -873,6 +873,6 @@ def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): assert "+import a" in isort_diff_content assert " import b" in isort_diff_content assert "-import a" in isort_diff_content - + isort_output.seek(0) - assert isort_output.read() == "import a\nimport b\n" + assert isort_output.read().splitlines() == ["import a", "import b"] From 3424d8310bb3756834d4251597b02f32d9ccd2c8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 1 Nov 2020 00:30:58 -0700 Subject: [PATCH 186/539] Add - Akihiro Nitta (@akihironitta) - Samuel Gaist (@sgaist) to acknowledgements --- docs/contributing/4.-acknowledgements.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index a3ec7471f..48f7d8cf0 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -210,6 +210,8 @@ Code Contributors - @tonci-bw - @jaydesl - Tamara (@infinityxxx) +- Akihiro Nitta (@akihironitta) +- Samuel Gaist (@sgaist) Documenters =================== From 1dc62fff4718d5463b6633591998c45c5943ba2d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 2 Nov 2020 23:46:57 -0800 Subject: [PATCH 187/539] Fix #1575: Leading space is removed from wrong line --- isort/core.py | 10 +++++++--- tests/unit/test_ticketed_features.py | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/isort/core.py b/isort/core.py index 42fbec80b..7408c1f1c 100644 --- a/isort/core.py +++ b/isort/core.py @@ -275,7 +275,7 @@ def write(self, *a, **kw): ): cimport_statement = True - if cimport_statement != cimports or (new_indent != indent and import_section): + if cimport_statement != cimports and import_section: if import_section: next_cimports = cimport_statement next_import_section = import_statement @@ -284,8 +284,12 @@ def write(self, *a, **kw): line = "" else: cimports = cimport_statement - - indent = new_indent + else: + if new_indent != indent: + if import_section: + import_statement = import_statement.lstrip() + else: + indent = new_indent import_section += import_statement else: not_imports = True diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 76f08894c..bf760f8d9 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -876,3 +876,18 @@ def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): isort_output.seek(0) assert isort_output.read().splitlines() == ["import a", "import b"] + + +def test_autofix_mixed_indent_imports_1575(): + """isort should automatically fix import statements that are sent in + with incorrect mixed indentation. + See: https://github.com/PyCQA/isort/issues/1575 + """ + assert isort.code(""" +import os + import os + """) == """ +import os +""" + + From 69f2a353dd54670f2348374e446358e85f3db545 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 3 Nov 2020 23:57:14 -0800 Subject: [PATCH 188/539] Fix new_indent behavior --- isort/core.py | 3 ++- tests/unit/test_ticketed_features.py | 13 ++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/isort/core.py b/isort/core.py index 7408c1f1c..28f1010ef 100644 --- a/isort/core.py +++ b/isort/core.py @@ -246,6 +246,7 @@ def write(self, *a, **kw): ): import_section += line elif stripped_line.startswith(IMPORT_START_IDENTIFIERS): + did_contain_imports = contains_imports contains_imports = True new_indent = line[: -len(line.lstrip())] @@ -286,7 +287,7 @@ def write(self, *a, **kw): cimports = cimport_statement else: if new_indent != indent: - if import_section: + if import_section and did_contain_imports: import_statement = import_statement.lstrip() else: indent = new_indent diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index bf760f8d9..06b59f5ff 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -876,18 +876,21 @@ def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): isort_output.seek(0) assert isort_output.read().splitlines() == ["import a", "import b"] - + def test_autofix_mixed_indent_imports_1575(): """isort should automatically fix import statements that are sent in with incorrect mixed indentation. See: https://github.com/PyCQA/isort/issues/1575 """ - assert isort.code(""" + assert ( + isort.code( + """ import os import os - """) == """ + """ + ) + == """ import os """ - - + ) From cd387678824161bcc50d1437d091737a3a80d02a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 4 Nov 2020 00:57:17 -0800 Subject: [PATCH 189/539] Fix handling of mix indented imports --- isort/core.py | 9 +++++++-- tests/unit/test_ticketed_features.py | 29 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/isort/core.py b/isort/core.py index 28f1010ef..27e615414 100644 --- a/isort/core.py +++ b/isort/core.py @@ -276,7 +276,12 @@ def write(self, *a, **kw): ): cimport_statement = True - if cimport_statement != cimports and import_section: + if cimport_statement != cimports or ( + new_indent != indent + and import_section + and (not did_contain_imports or len(new_indent) < len(indent)) + ): + indent = new_indent if import_section: next_cimports = cimport_statement next_import_section = import_statement @@ -288,7 +293,7 @@ def write(self, *a, **kw): else: if new_indent != indent: if import_section and did_contain_imports: - import_statement = import_statement.lstrip() + import_statement = indent + import_statement.lstrip() else: indent = new_indent import_section += import_statement diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 06b59f5ff..7871e38c5 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -892,5 +892,34 @@ def test_autofix_mixed_indent_imports_1575(): ) == """ import os +""" + ) + assert ( + isort.code( + """ +def one(): + import os +import os + """ + ) + == """ +def one(): + import os + +import os +""" + ) + assert ( + isort.code( + """ +import os + import os + import os + import os +import os +""" + ) + == """ +import os """ ) From 8c1158b3f472efe47879e72f96e690ea73b1d065 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 10 Nov 2020 23:49:04 -0800 Subject: [PATCH 190/539] Fix issue #1593 & Fix issue #1592: isort doesn't work on Python 3.6.0 --- isort/io.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/isort/io.py b/isort/io.py index 7ff2807d2..a002bc89b 100644 --- a/isort/io.py +++ b/isort/io.py @@ -4,14 +4,16 @@ from contextlib import contextmanager from io import BytesIO, StringIO, TextIOWrapper from pathlib import Path -from typing import Callable, Iterator, NamedTuple, TextIO, Union +from typing import Callable, Iterator, TextIO, Union +from isort._future import dataclass from isort.exceptions import UnsupportedEncoding _ENCODING_PATTERN = re.compile(br"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)") -class File(NamedTuple): +@dataclass(frozen=True) +class File: stream: TextIO path: Path encoding: str @@ -26,7 +28,11 @@ def detect_encoding(filename: str, readline: Callable[[], bytes]): @staticmethod def from_contents(contents: str, filename: str) -> "File": encoding = File.detect_encoding(filename, BytesIO(contents.encode("utf-8")).readline) - return File(StringIO(contents), path=Path(filename).resolve(), encoding=encoding) + return File( # type: ignore + stream=StringIO(contents), + path=Path(filename).resolve(), + encoding=encoding + ) @property def extension(self): @@ -55,7 +61,7 @@ def read(filename: Union[str, Path]) -> Iterator["File"]: stream = None try: stream = File._open(file_path) - yield File(stream=stream, path=file_path, encoding=stream.encoding) + yield File(stream=stream, path=file_path, encoding=stream.encoding) # type: ignore finally: if stream is not None: stream.close() From 66ba8c6e43b723e6748e30149cfb640c0e42bd6f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 11 Nov 2020 00:11:00 -0800 Subject: [PATCH 191/539] black formatting --- isort/io.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/isort/io.py b/isort/io.py index a002bc89b..2f30be0c7 100644 --- a/isort/io.py +++ b/isort/io.py @@ -29,9 +29,7 @@ def detect_encoding(filename: str, readline: Callable[[], bytes]): def from_contents(contents: str, filename: str) -> "File": encoding = File.detect_encoding(filename, BytesIO(contents.encode("utf-8")).readline) return File( # type: ignore - stream=StringIO(contents), - path=Path(filename).resolve(), - encoding=encoding + stream=StringIO(contents), path=Path(filename).resolve(), encoding=encoding ) @property From b04ff9fb8f4098bc4498d60a05e19c711a0524d9 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 12 Nov 2020 23:52:28 -0800 Subject: [PATCH 192/539] Implemented #1596: Provide ways for extension formatting and file paths to be specified when using streaming input from CLI. --- CHANGELOG.md | 3 +++ isort/main.py | 23 ++++++++++++++++ tests/unit/test_main.py | 60 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a94901a0c..524f4ea56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). +### 5.7.0 TBD + - Implemented #1596: Provide ways for extension formatting and file paths to be specified when using streaming input from CLI. + ### 5.6.4 October 12, 2020 - Fixed #1556: Empty line added between imports that should be skipped. diff --git a/isort/main.py b/isort/main.py index 5dd661f41..8e7d0e017 100644 --- a/isort/main.py +++ b/isort/main.py @@ -383,6 +383,11 @@ def _build_arg_parser() -> argparse.ArgumentParser: action="store_true", help="Tells isort not to follow symlinks that are encountered when running recursively.", ) + target_group.add_argument( + "--filename", + dest="filename", + help="Provide the filename associated with a stream.", + ) output_group.add_argument( "-a", @@ -653,6 +658,11 @@ def _build_arg_parser() -> argparse.ArgumentParser: action="store_true", help="Tells isort to use color in terminal output.", ) + output_group.add_argument( + "--ext-format", + dest="ext_format", + help="Tells isort to format the given files according to an extensions formatting rules.", + ) section_group.add_argument( "--sd", @@ -938,6 +948,8 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = write_to_stdout = config_dict.pop("write_to_stdout", False) deprecated_flags = config_dict.pop("deprecated_flags", False) remapped_deprecated_args = config_dict.pop("remapped_deprecated_args", False) + stream_filename = config_dict.pop("filename", None) + ext_format = config_dict.pop("ext_format", None) wrong_sorted_files = False all_attempt_broken = False no_valid_encodings = False @@ -952,6 +964,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = print(json.dumps(config.__dict__, indent=4, separators=(",", ": "), default=_preconvert)) return elif file_names == ["-"]: + file_path = Path(stream_filename) if stream_filename else None if show_files: sys.exit("Error: can't show files for streaming input.") @@ -960,6 +973,8 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = input_stream=sys.stdin if stdin is None else stdin, config=config, show_diff=show_diff, + file_path=file_path, + extension=ext_format, ) wrong_sorted_files = incorrectly_sorted @@ -969,8 +984,14 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = output_stream=sys.stdout, config=config, show_diff=show_diff, + file_path=file_path, + extension=ext_format, ) else: + if stream_filename: + printer = create_terminal_printer(color=config.color_output) + printer.error("Filename override is intended only for stream (-) sorting.") + sys.exit(1) skipped: List[str] = [] broken: List[str] = [] @@ -1005,6 +1026,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = check=check, ask_to_apply=ask_to_apply, write_to_stdout=write_to_stdout, + extension=ext_format, ), file_names, ) @@ -1018,6 +1040,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = ask_to_apply=ask_to_apply, show_diff=show_diff, write_to_stdout=write_to_stdout, + extension=ext_format, ) for file_name in file_names ) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 70eafb3ad..08f274630 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -348,6 +348,66 @@ def test_isort_command(): assert main.ISortCommand +def test_isort_filename_overrides(tmpdir, capsys): + """Tests isorts available approaches for overriding filename and extension based behavior""" + input_text = """ +import b +import a + +def function(): + pass +""" + + def build_input_content(): + return UnseekableTextIOWrapper(BytesIO(input_text.encode("utf8"))) + + main.main(["-"], stdin=build_input_content()) + out, error = capsys.readouterr() + assert not error + assert out == ( + """ +import a +import b + + +def function(): + pass +""" + ) + + main.main(["-", "--ext-format", "pyi"], stdin=build_input_content()) + out, error = capsys.readouterr() + assert not error + assert out == ( + """ +import a +import b + +def function(): + pass +""" + ) + + tmp_file = tmpdir.join("tmp.pyi") + tmp_file.write_text(input_text, encoding="utf8") + main.main(["-", "--filename", str(tmp_file)], stdin=build_input_content()) + out, error = capsys.readouterr() + assert not error + assert out == ( + """ +import a +import b + +def function(): + pass +""" + ) + + # setting a filename override when file is passed in as non-stream is not supported. + with pytest.raises(SystemExit): + main.main([str(tmp_file), "--filename", str(tmp_file)], stdin=build_input_content()) + + def test_isort_with_stdin(capsys): # ensures that isort sorts stdin without any flags From 7c2cb61e7ce2200a99f6852532e1f40f502d4e2c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 13 Nov 2020 00:02:04 -0800 Subject: [PATCH 193/539] Fix integration test to skip unsorted files --- tests/integration/test_projects_using_isort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 34540cbba..2a2abe398 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -61,7 +61,7 @@ def test_habitat_lab(tmpdir): def test_tmuxp(tmpdir): git_clone("https://github.com/tmux-python/tmuxp.git", tmpdir) - run_isort([str(tmpdir)]) + run_isort([str(tmpdir), "--skip", "cli.py", "--skip", "test_workspacebuilder.py"]) def test_websockets(tmpdir): From 41fa2902f62eb8f30561065a8105da4bf4a5372d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 13 Nov 2020 23:15:41 -0800 Subject: [PATCH 194/539] Update configuration docs --- docs/configuration/options.md | 65 +++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index bf2205243..4a58573d3 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -179,7 +179,7 @@ Force isort to recognize a module as being a local folder. Generally, this is re Force isort to recognize a module as part of Python's standard library. **Type:** Frozenset -**Default:** `('_dummy_thread', '_thread', 'abc', 'aifc', 'argparse', 'array', 'ast', 'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'base64', 'bdb', 'binascii', 'binhex', 'bisect', 'builtins', 'bz2', 'cProfile', 'calendar', 'cgi', 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections', 'colorsys', 'compileall', 'concurrent', 'configparser', 'contextlib', 'contextvars', 'copy', 'copyreg', 'crypt', 'csv', 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbm', 'decimal', 'difflib', 'dis', 'distutils', 'doctest', 'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno', 'faulthandler', 'fcntl', 'filecmp', 'fileinput', 'fnmatch', 'formatter', 'fpectl', 'fractions', 'ftplib', 'functools', 'gc', 'getopt', 'getpass', 'gettext', 'glob', 'grp', 'gzip', 'hashlib', 'heapq', 'hmac', 'html', 'http', 'imaplib', 'imghdr', 'imp', 'importlib', 'inspect', 'io', 'ipaddress', 'itertools', 'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma', 'macpath', 'mailbox', 'mailcap', 'marshal', 'math', 'mimetypes', 'mmap', 'modulefinder', 'msilib', 'msvcrt', 'multiprocessing', 'netrc', 'nis', 'nntplib', 'ntpath', 'numbers', 'operator', 'optparse', 'os', 'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform', 'plistlib', 'poplib', 'posix', 'posixpath', 'pprint', 'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc', 'queue', 'quopri', 'random', 're', 'readline', 'reprlib', 'resource', 'rlcompleter', 'runpy', 'sched', 'secrets', 'select', 'selectors', 'shelve', 'shlex', 'shutil', 'signal', 'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver', 'spwd', 'sqlite3', 'sre', 'sre_compile', 'sre_constants', 'sre_parse', 'ssl', 'stat', 'statistics', 'string', 'stringprep', 'struct', 'subprocess', 'sunau', 'symbol', 'symtable', 'sys', 'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios', 'test', 'textwrap', 'threading', 'time', 'timeit', 'tkinter', 'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'tty', 'turtle', 'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'uu', 'uuid', 'venv', 'warnings', 'wave', 'weakref', 'webbrowser', 'winreg', 'winsound', 'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'zipapp', 'zipfile', 'zipimport', 'zlib')` +**Default:** `('_dummy_thread', '_thread', 'abc', 'aifc', 'argparse', 'array', 'ast', 'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'base64', 'bdb', 'binascii', 'binhex', 'bisect', 'builtins', 'bz2', 'cProfile', 'calendar', 'cgi', 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections', 'colorsys', 'compileall', 'concurrent', 'configparser', 'contextlib', 'contextvars', 'copy', 'copyreg', 'crypt', 'csv', 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbm', 'decimal', 'difflib', 'dis', 'distutils', 'doctest', 'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno', 'faulthandler', 'fcntl', 'filecmp', 'fileinput', 'fnmatch', 'formatter', 'fpectl', 'fractions', 'ftplib', 'functools', 'gc', 'getopt', 'getpass', 'gettext', 'glob', 'graphlib', 'grp', 'gzip', 'hashlib', 'heapq', 'hmac', 'html', 'http', 'imaplib', 'imghdr', 'imp', 'importlib', 'inspect', 'io', 'ipaddress', 'itertools', 'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma', 'macpath', 'mailbox', 'mailcap', 'marshal', 'math', 'mimetypes', 'mmap', 'modulefinder', 'msilib', 'msvcrt', 'multiprocessing', 'netrc', 'nis', 'nntplib', 'ntpath', 'numbers', 'operator', 'optparse', 'os', 'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform', 'plistlib', 'poplib', 'posix', 'posixpath', 'pprint', 'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc', 'queue', 'quopri', 'random', 're', 'readline', 'reprlib', 'resource', 'rlcompleter', 'runpy', 'sched', 'secrets', 'select', 'selectors', 'shelve', 'shlex', 'shutil', 'signal', 'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver', 'spwd', 'sqlite3', 'sre', 'sre_compile', 'sre_constants', 'sre_parse', 'ssl', 'stat', 'statistics', 'string', 'stringprep', 'struct', 'subprocess', 'sunau', 'symbol', 'symtable', 'sys', 'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios', 'test', 'textwrap', 'threading', 'time', 'timeit', 'tkinter', 'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'tty', 'turtle', 'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'uu', 'uuid', 'venv', 'warnings', 'wave', 'weakref', 'webbrowser', 'winreg', 'winsound', 'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'zipapp', 'zipfile', 'zipimport', 'zlib', 'zoneinfo')` **Python & Config File Name:** known_standard_library **CLI Flags:** @@ -296,7 +296,7 @@ Sort imports by their string length. ## Length Sort Straight -Sort straight imports by their string length. +Sort straight imports by their string length. Similar to `length_sort` but applies only to straight imports and doesn't affect from imports. **Type:** Bool **Default:** `False` @@ -602,7 +602,7 @@ Force all imports to be sorted as a single section ## Force Grid Wrap -Force number of from imports (defaults to 2 when passed as CLI flag without value) to be grid wrapped regardless of line length. If 0 is passed in (the global default) only line length is considered. +Force number of from imports (defaults to 2 when passed as CLI flag without value)to be grid wrapped regardless of line length. If 0 is passed in (the global default) only line length is considered. **Type:** Int **Default:** `0` @@ -633,6 +633,15 @@ Don't sort straight-style imports (like import sys) before from-style imports (l **Python & Config File Name:** lexicographical **CLI Flags:** **Not Supported** +## Group By Package + +**No Description** + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** group_by_package +**CLI Flags:** **Not Supported** + ## Ignore Whitespace Tells isort to ignore whitespace differences when --check-only is being used. @@ -767,8 +776,8 @@ Tells isort to honor noqa comments to enforce skipping those comments. Add an explicitly defined source path (modules within src paths have their imports automatically categorized as first_party). -**Type:** Frozenset -**Default:** `frozenset()` +**Type:** Tuple +**Default:** `()` **Python & Config File Name:** src_paths **CLI Flags:** @@ -800,7 +809,8 @@ Tells isort to remove redundant aliases from imports, such as `import os as os`. ## Float To Top -Causes all non-indented imports to float to the top of the file having its imports sorted. It can be an excellent shortcut for collecting imports every once in a while when you place them in the middle of a file to avoid context switching. +Causes all non-indented imports to float to the top of the file having its imports sorted (immediately below the top of file comment). +This can be an excellent shortcut for collecting imports every once in a while when you place them in the middle of a file to avoid context switching. *NOTE*: It currently doesn't work with cimports and introduces some extra over-head and a performance penalty. @@ -880,7 +890,7 @@ Tells isort to treat all single line comments as if they are code. Specifies what extensions isort can be ran against. **Type:** Frozenset -**Default:** `('py', 'pyi', 'pyx')` +**Default:** `('pxd', 'py', 'pyi', 'pyx')` **Python & Config File Name:** supported_extensions **CLI Flags:** @@ -949,6 +959,36 @@ Causes imports to be sorted only based on their sections like STDLIB,THIRDPARTY - --only-sections - --os +## Only Modified + +Suppresses verbose output for non-modified files. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** only_modified +**CLI Flags:** + +- --only-modified +- --om + +## Auto Identify Namespace Packages + +**No Description** + +**Type:** Bool +**Default:** `True` +**Python & Config File Name:** auto_identify_namespace_packages +**CLI Flags:** **Not Supported** + +## Namespace Packages + +**No Description** + +**Type:** Frozenset +**Default:** `frozenset()` +**Python & Config File Name:** namespace_packages +**CLI Flags:** **Not Supported** + ## Check Checks the file for unsorted / unformatted imports and prints them to the command line without modifying the file. @@ -1090,6 +1130,17 @@ See isort's determined config, as well as sources of config options. - --show-config +## Show Files + +See the files isort will be ran against with the current config options. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** **Not Supported** +**CLI Flags:** + +- --show-files + ## Deprecated Flags ==SUPPRESS== From 35f2f8f50e3e0f992bb44198a2902ce8533ad9a1 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 14 Nov 2020 00:49:19 -0800 Subject: [PATCH 195/539] Update config option docs --- docs/configuration/options.md | 148 +++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 54 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index d32e3f2a6..e6d0e8058 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -108,9 +108,7 @@ Forces line endings to the specified value. If not set, values will be guessed p ## Sections -Specifies a custom ordering for sections. Any custom defined sections should also be -included in this ordering. Omitting any of the default sections from this tuple may -result in unexpected sorting or an exception being raised. +**No Description** **Type:** Tuple **Default:** `('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER')` @@ -343,7 +341,7 @@ Removes the specified import from all files. ## Append Only -Only adds the imports specified in --add-imports if the file contains existing imports. +Only adds the imports specified in --add-import if the file contains existing imports. **Type:** Bool **Default:** `False` @@ -1003,18 +1001,44 @@ Combines all the bare straight imports of the same section in a single line. Won **Python & Config File Name:** namespace_packages **CLI Flags:** **Not Supported** -## Check +## Follow Links -Checks the file for unsorted / unformatted imports and prints them to the command line without modifying the file. Returns 0 when nothing would change and returns 1 when the file would be reformatted. +**No Description** + +**Type:** Bool +**Default:** `True` +**Python & Config File Name:** follow_links +**CLI Flags:** **Not Supported** + +## Show Version + +Displays the currently installed version of isort. **Type:** Bool **Default:** `False` **Python & Config File Name:** **Not Supported** **CLI Flags:** -- -c -- --check-only -- --check +- -V +- --version + +**Examples:** + +### Example cli usage + +`isort --version` + +## Version Number + +Returns just the current version number without the logo + +**Type:** String +**Default:** `==SUPPRESS==` +**Python & Config File Name:** **Not Supported** +**CLI Flags:** + +- --vn +- --version-number ## Write To Stdout @@ -1028,44 +1052,52 @@ Force resulting output to stdout, instead of in-place. - -d - --stdout -## Show Diff +## Show Config -Prints a diff of all the changes isort would make to a file, instead of changing it in place +See isort's determined config, as well as sources of config options. **Type:** Bool **Default:** `False` **Python & Config File Name:** **Not Supported** **CLI Flags:** -- --df -- --diff +- --show-config -## Jobs +## Show Files -Number of files to process in parallel. +See the files isort will be ran against with the current config options. -**Type:** Int -**Default:** `None` +**Type:** Bool +**Default:** `False` **Python & Config File Name:** **Not Supported** **CLI Flags:** -- -j -- --jobs +- --show-files -## Dont Order By Type +## Show Diff -Don't order imports by type, which is determined by case, in addition to alphabetically. +Prints a diff of all the changes isort would make to a file, instead of changing it in place -**NOTE**: type here refers to the implied type from the import name capitalization. - isort does not do type introspection for the imports. These "types" are simply: CONSTANT_VARIABLE, CamelCaseClass, variable_or_function. If your project follows PEP8 or a related coding standard and has many imports this is a good default. You can turn this on from the CLI using `--order-by-type`. +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** **Not Supported** +**CLI Flags:** + +- --df +- --diff + +## Check + +Checks the file for unsorted / unformatted imports and prints them to the command line without modifying the file. Returns 0 when nothing would change and returns 1 when the file would be reformatted. **Type:** Bool **Default:** `False` **Python & Config File Name:** **Not Supported** **CLI Flags:** -- --dt -- --dont-order-by-type +- -c +- --check-only +- --check ## Settings Path @@ -1081,35 +1113,28 @@ Explicitly set the settings path or file instead of auto determining based on fi - --settings-file - --settings -## Show Version +## Jobs -Displays the currently installed version of isort. +Number of files to process in parallel. -**Type:** Bool -**Default:** `False` +**Type:** Int +**Default:** `None` **Python & Config File Name:** **Not Supported** **CLI Flags:** -- -V -- --version - -**Examples:** - -### Example cli usage - -`isort --version` +- -j +- --jobs -## Version Number +## Ask To Apply -Returns just the current version number without the logo +Tells isort to apply changes interactively. -**Type:** String -**Default:** `==SUPPRESS==` +**Type:** Bool +**Default:** `False` **Python & Config File Name:** **Not Supported** **CLI Flags:** -- --vn -- --version-number +- --interactive ## Files @@ -1122,38 +1147,53 @@ One or more Python source files that need their imports sorted. - -## Ask To Apply +## Dont Follow Links -Tells isort to apply changes interactively. +Tells isort not to follow symlinks that are encountered when running recursively. **Type:** Bool **Default:** `False` **Python & Config File Name:** **Not Supported** **CLI Flags:** -- --interactive +- --dont-follow-links -## Show Config +## Filename -See isort's determined config, as well as sources of config options. +Provide the filename associated with a stream. -**Type:** Bool -**Default:** `False` +**Type:** String +**Default:** `None` **Python & Config File Name:** **Not Supported** **CLI Flags:** -- --show-config +- --filename -## Show Files +## Dont Order By Type -See the files isort will be ran against with the current config options. +Don't order imports by type, which is determined by case, in addition to alphabetically. + +**NOTE**: type here refers to the implied type from the import name capitalization. + isort does not do type introspection for the imports. These "types" are simply: CONSTANT_VARIABLE, CamelCaseClass, variable_or_function. If your project follows PEP8 or a related coding standard and has many imports this is a good default. You can turn this on from the CLI using `--order-by-type`. **Type:** Bool **Default:** `False` **Python & Config File Name:** **Not Supported** **CLI Flags:** -- --show-files +- --dt +- --dont-order-by-type + +## Ext Format + +Tells isort to format the given files according to an extensions formatting rules. + +**Type:** String +**Default:** `None` +**Python & Config File Name:** **Not Supported** +**CLI Flags:** + +- --ext-format ## Deprecated Flags From 6644bd6434f4d2c17260ac5cb2205614f15843c1 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 15 Nov 2020 23:59:02 -0800 Subject: [PATCH 196/539] Add Implemented #1583 to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 524f4ea56..99a37c4d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.7.0 TBD - Implemented #1596: Provide ways for extension formatting and file paths to be specified when using streaming input from CLI. + - Implemented #1583: Ability to output and diff within a single API call to isort.file. ### 5.6.4 October 12, 2020 - Fixed #1556: Empty line added between imports that should be skipped. From 6ed25c68c738405229faf8664c54cd7f020154e9 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 24 Nov 2020 23:51:14 -0800 Subject: [PATCH 197/539] Update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99a37c4d9..e2b4f92a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,10 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.7.0 TBD - Implemented #1596: Provide ways for extension formatting and file paths to be specified when using streaming input from CLI. - - Implemented #1583: Ability to output and diff within a single API call to isort.file. + - Implemented #1583: Ability to output and diff within a single API call to `isort.file`. + - Implemented #1562, #1592 & #1593: Better more useful fatal error messages. + - Implemented #1575: Support for automatically fixing mixed indentation of import sections. + - Implemented #1582: Added a CLI option for skipping symlinks. ### 5.6.4 October 12, 2020 - Fixed #1556: Empty line added between imports that should be skipped. From e9b1bd4e126327bc415b26d1308da5a2cdcdf51e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 28 Nov 2020 23:39:11 -0800 Subject: [PATCH 198/539] Implemented #1603: Support for disabling float_to_top from the command line. --- .isort.cfg | 1 + CHANGELOG.md | 1 + isort/main.py | 12 +++++++++++ tests/unit/test_main.py | 47 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+) diff --git a/.isort.cfg b/.isort.cfg index 567d1abd6..9ab265a44 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -2,3 +2,4 @@ profile=hug src_paths=isort,test skip=tests/unit/example_crlf_file.py +float_to_top=true diff --git a/CHANGELOG.md b/CHANGELOG.md index e2b4f92a0..6e81a6877 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1562, #1592 & #1593: Better more useful fatal error messages. - Implemented #1575: Support for automatically fixing mixed indentation of import sections. - Implemented #1582: Added a CLI option for skipping symlinks. + - Implemented #1603: Support for disabling float_to_top from the command line. ### 5.6.4 October 12, 2020 - Fixed #1556: Empty line added between imports that should be skipped. diff --git a/isort/main.py b/isort/main.py index 8e7d0e017..cdf9ba00d 100644 --- a/isort/main.py +++ b/isort/main.py @@ -430,6 +430,12 @@ def _build_arg_parser() -> argparse.ArgumentParser: "*NOTE*: It currently doesn't work with cimports and introduces some extra over-head " "and a performance penalty.", ) + output_group.add_argument( + "--dont-float-to-top", + dest="dont_float_to_top", + action="store_true", + help="Forces --float-to-top setting off. See --float-to-top for more information.", + ) output_group.add_argument( "--ca", "--combine-as", @@ -864,6 +870,12 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> Dict[str, Any]: del arguments["dont_order_by_type"] if "dont_follow_links" in arguments: arguments["follow_links"] = False + if "dont_float_to_top" in arguments: + del arguments["dont_float_to_top"] + if arguments.get("float_to_top", False): + sys.exit("Can't set both --float-to-top and --dont-float-to-top.") + else: + arguments["float_to_top"] = False multi_line_output = arguments.get("multi_line_output", None) if multi_line_output: if multi_line_output.isdigit(): diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 08f274630..4af4d58c2 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -408,6 +408,53 @@ def function(): main.main([str(tmp_file), "--filename", str(tmp_file)], stdin=build_input_content()) +def test_isort_float_to_top_overrides(tmpdir, capsys): + """Tests isorts supports overriding float to top from CLI""" + test_input = """ +import b + + +def function(): + pass + + +import a +""" + config_file = tmpdir.join(".isort.cfg") + config_file.write( + """ +[settings] +float_to_top=True +""" + ) + python_file = tmpdir.join("file.py") + python_file.write(test_input) + + main.main([str(python_file)]) + out, error = capsys.readouterr() + assert not error + assert "Fixing" in out + assert python_file.read_text(encoding="utf8") == ( + """ +import a +import b + + +def function(): + pass +""" + ) + + python_file.write(test_input) + main.main([str(python_file), "--dont-float-to-top"]) + _, error = capsys.readouterr() + assert not error + assert python_file.read_text(encoding="utf8") == test_input + + with pytest.raises(SystemExit): + main.main([str(python_file), "--float-to-top", "--dont-float-to-top"]) + + def test_isort_with_stdin(capsys): # ensures that isort sorts stdin without any flags From 0b42e54ff33b2f99d49e1fdef7b1aa281ed0dd29 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 29 Nov 2020 23:39:45 -0800 Subject: [PATCH 199/539] Formatting fixes --- .isort.cfg | 2 +- tests/unit/test_main.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.isort.cfg b/.isort.cfg index 9ab265a44..545be9799 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -2,4 +2,4 @@ profile=hug src_paths=isort,test skip=tests/unit/example_crlf_file.py -float_to_top=true + diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 4af4d58c2..ac4a8e4e8 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -444,13 +444,13 @@ def function(): pass """ ) - + python_file.write(test_input) main.main([str(python_file), "--dont-float-to-top"]) _, error = capsys.readouterr() assert not error assert python_file.read_text(encoding="utf8") == test_input - + with pytest.raises(SystemExit): main.main([str(python_file), "--float-to-top", "--dont-float-to-top"]) From 940d74abbb08a1a8d3e86d94d38d655201b590ba Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 30 Nov 2020 21:29:15 -0800 Subject: [PATCH 200/539] Remove spaces in blank line --- tests/unit/test_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index ac4a8e4e8..84ee76531 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -417,7 +417,7 @@ def test_isort_float_to_top_overrides(tmpdir, capsys): def function(): pass - + import a """ config_file = tmpdir.join(".isort.cfg") From dc6020b2ffb6a1cccca8a16108ca7984a4aa7841 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 1 Dec 2020 21:30:27 -0800 Subject: [PATCH 201/539] Improve test coverage, add test for dont follow links --- isort/main.py | 1 + tests/unit/test_main.py | 1 + 2 files changed, 2 insertions(+) diff --git a/isort/main.py b/isort/main.py index cdf9ba00d..c16fdbd60 100644 --- a/isort/main.py +++ b/isort/main.py @@ -870,6 +870,7 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> Dict[str, Any]: del arguments["dont_order_by_type"] if "dont_follow_links" in arguments: arguments["follow_links"] = False + del arguments["dont_follow_links"] if "dont_float_to_top" in arguments: del arguments["dont_float_to_top"] if arguments.get("float_to_top", False): diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 84ee76531..65f0dae42 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -80,6 +80,7 @@ def test_parse_args(): assert main.parse_args(["--only-modified"]) == {"only_modified": True} assert main.parse_args(["--csi"]) == {"combine_straight_imports": True} assert main.parse_args(["--combine-straight-imports"]) == {"combine_straight_imports": True} + assert main.parse_args(["--dont-follow-links"]) == {"follow_links": False} def test_ascii_art(capsys): From 3d2264b9ca1761b715cb985a4d940201bd257ca3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 1 Dec 2020 21:47:54 -0800 Subject: [PATCH 202/539] Improve code coverage: identify imports stream --- isort/main.py | 6 ++++-- tests/unit/test_main.py | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index c16fdbd60..5352f64f8 100644 --- a/isort/main.py +++ b/isort/main.py @@ -899,7 +899,9 @@ def _preconvert(item): raise TypeError("Unserializable object {} of type {}".format(item, type(item))) -def identify_imports_main(argv: Optional[Sequence[str]] = None) -> None: +def identify_imports_main( + argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = None +) -> None: parser = argparse.ArgumentParser( description="Get all import definitions from a given file." "Use `-` as the first argument to represent stdin." @@ -909,7 +911,7 @@ def identify_imports_main(argv: Optional[Sequence[str]] = None) -> None: file_name = arguments.file if file_name == "-": - api.get_imports_stream(sys.stdin, sys.stdout) + api.get_imports_stream(sys.stdin if stdin is None else stdin, sys.stdout) else: if os.path.isdir(file_name): sys.exit("Path must be a file, not a directory") diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 65f0dae42..dd2afddaa 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -20,6 +20,10 @@ def seek(self, *args, **kwargs): raise ValueError("underlying stream is not seekable") +def as_stream(text: str) -> UnseekableTextIOWrapper: + return UnseekableTextIOWrapper(BytesIO(text.encode("utf8"))) + + @given( file_name=st.text(), config=st.builds(Config), @@ -1035,3 +1039,8 @@ def test_identify_imports_main(tmpdir, capsys): out, error = capsys.readouterr() assert out == file_imports.replace("\n", os.linesep) + assert not error + + main.identify_imports_main(["-"], stdin=as_stream(file_content)) + out, error = capsys.readouterr() + assert out == file_imports.replace("\n", os.linesep) From 402df716d4673bac2fdd09e332958df5d4e9c80b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 2 Dec 2020 22:49:53 -0800 Subject: [PATCH 203/539] Updates to black_compatability page --- ...bility_black.md => black_compatibility.md} | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) rename docs/configuration/{compatibility_black.md => black_compatibility.md} (50%) diff --git a/docs/configuration/compatibility_black.md b/docs/configuration/black_compatibility.md similarity index 50% rename from docs/configuration/compatibility_black.md rename to docs/configuration/black_compatibility.md index 4afa2459e..ff88ff2dc 100644 --- a/docs/configuration/compatibility_black.md +++ b/docs/configuration/black_compatibility.md @@ -1,12 +1,25 @@ Compatibility with black ======== -black and isort sometimes don't agree on some rules. Although you can configure isort to behave nicely with black. +Compatibility with black is very important to the isort project and comes baked in starting with version 5. +All that's required to use isort alongside black is to set the isort profile to "black". +## Using a config file (such as .isort.cfg) -## Basic compatibility +For projects that officially use both isort and black, we recommend setting the black profile in a config file at the root of your project's repository. +This way independent to how users call isort (pre-commit, CLI, or editor integration) the black profile will automatically be applied. -Use the profile option while using isort, `isort --profile black`. +```ini +[tool.isort] +profile = "black" +multi_line_output = 3 +``` + +Read More about supported [config files](https://pycqa.github.io/isort/docs/configuration/config_files/). + +## CLI + +To use the profile option when calling isort directly from the commandline simply add the --profile black argument: `isort --profile black`. A demo of how this would look like in your _.travis.yml_ @@ -34,7 +47,7 @@ See [built-in profiles](https://pycqa.github.io/isort/docs/configuration/profile ## Integration with pre-commit -isort can be easily used with your pre-commit hooks. +You can also set the profile directly when integrating isort within pre-commit. ```yaml - repo: https://github.com/pycqa/isort @@ -44,14 +57,3 @@ isort can be easily used with your pre-commit hooks. args: ["--profile", "black"] ``` -## Using a config file (.isort.cfg) - -The easiest way to configure black with isort is to use a config file. - -```ini -[tool.isort] -profile = "black" -multi_line_output = 3 -``` - -Read More about supported [config files](https://pycqa.github.io/isort/docs/configuration/config_files/). \ No newline at end of file From 1c45f49fefe7e2d4474cb804690c9f3d477f8234 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 2 Dec 2020 22:50:59 -0800 Subject: [PATCH 204/539] Ensure filter-files is in pre-commit example --- docs/configuration/black_compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/black_compatibility.md b/docs/configuration/black_compatibility.md index ff88ff2dc..35f6812bf 100644 --- a/docs/configuration/black_compatibility.md +++ b/docs/configuration/black_compatibility.md @@ -54,6 +54,6 @@ You can also set the profile directly when integrating isort within pre-commit. rev: 5.6.4 hooks: - id: isort - args: ["--profile", "black"] + args: ["--profile", "black", "--filter-files"] ``` From 5dc0b4e032f77252ea48f4a0a6728db96a9831b4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 2 Dec 2020 22:54:24 -0800 Subject: [PATCH 205/539] Fix windows test --- tests/unit/test_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index dd2afddaa..e53a626dc 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -1038,9 +1038,9 @@ def test_identify_imports_main(tmpdir, capsys): main.identify_imports_main([str(some_file)]) out, error = capsys.readouterr() - assert out == file_imports.replace("\n", os.linesep) + assert out == file_imports assert not error main.identify_imports_main(["-"], stdin=as_stream(file_content)) out, error = capsys.readouterr() - assert out == file_imports.replace("\n", os.linesep) + assert out == file_imports From f496eea5b763d6985fc2e3450e67fa179f97b6d4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 2 Dec 2020 23:05:40 -0800 Subject: [PATCH 206/539] Add isot black logo --- art/isort_loves_black.png | Bin 0 -> 61394 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 art/isort_loves_black.png diff --git a/art/isort_loves_black.png b/art/isort_loves_black.png new file mode 100644 index 0000000000000000000000000000000000000000..baeb7baa73cc9705b72cff9d48357b67aade6065 GIT binary patch literal 61394 zcmeFYRaBMX8Z~MX(%m2+B3;rYCDJI}Al=<14bmW8(%s$7LP9zhy`;Mv&dc8WKR4(4 z-2FHh>S8R`8&AwRpZSHz%Zj7CA$;@f*)tT0FQVU`J$qI5?Adc(1UT@@8RDN0;6MJR zA|mnoi*%J(eKmT$#ziLb$pp`2l6doW)|Qa^(-A?# zxW+No`|YK4bvxNNf4gKOgxM}|EpXqZ#6_PyKK=RKnjZ^Z zLA3dzX8-INBjwZgbC+B}C-5S?gM^G2{6FNE@GmI|g#r%2O9T#LstzL7mX?NA4$nmF z4D}og4c}p3VO6~F9Suzb=Ko%g&dFJk{69a4iSecTf8QYaf8X$bPwD^f(D*lo9t^(D^!q+3 zU`@`5SX!RRdGdec5N1ZmL>HxCF!mxnawDgD{Y`KP9mn*cldo27)@;wNvmkU%#B6NY-s2P55EaN*QPBvj|Az8S=$BU)he4K5 z-9cQ{X54ssAIDwY`O_sI|V+?em>0;^a*Fa%^}nG~#li}uQ}BQ7Y5AcYL4WJ9cu z*S3x|TbrB*8R`6D3OXJmApu;!brYl}>y{`B~JDe!DH1aJB(M;QE~#^5zhC)2<*4x#1sX;zt@n`Hgj& zC~S;qFQLvja4=t3D=lX$UkcozYrDRFxb)7oz!xNiL!CV4`L$!F z+JwF2@-p3>QC%+%d3=0jEp)YNPiDgrt5!o}(2zZ9i&j+hZp zg+~>oTlczy{oB@Ze7E4Ba5hr#Rafl>IW)NcYyN+iZvqZA-Q|VXNi3Fe1|%#5PBr6} zI46gg>~?Yz+p_MVn@m`&H^iR4{Z^~t!402PG{X>lbkYAlT0%zV=wy)D-so{(Z%QLU z3(k3&q!mlgyL6rfE}khUqC@>V?PSEwfuqpD+kruK&Krv$T;Q-J7)SoWaCo)TPF; zkKia&r{fpRA01CaWREipk4@eRljsO74O7!GUOlgEsg+A}K2gzs!C!5tz*&zcI2}eP ztMOkX_N~c#of0)%sDF`>`MYm!;ZL3654E1D$m127 zYMr$H7>Qdh4*n+0LPn9wI8DtZ(4pVkw-gbDEBy8_l4Ir4jU5+kiyB+gQD`ZR$s8Rl z+P`8`CBY26i4xy``2qK!RYiXc&+B^7q;_J=>SA#Lx!SmUD2WHob9Kpm6}lYw>61P= z1x0Hfk2Yv|(di~;z8R02nMyjz+*lwivUwM)GM{_hCQ&xlpX6l9`iSJ@EgqwhX%{k@_8XkL_D8(?p{HsX_2087tFm<2W4RiO{D><) zA1l}8-0p5bTW)5aJ?M2J5q=tdkxkZ_9r;<17u+!a@BajLPO-KdqE)jD%%+C#0{?CW zesp%$dCPuPybx5ha&5WhjYc9aFwLzF?yHhhR`J?@ z1d)z6kXnK}zefh?<`DbeC3VlWkytfJz-&tHP7;p#DO*#ljG$wN1)Ou0_7gH=BQc<4 zaenwu-{k(4m$&6mJ5|h%3mNym$7{LzKBDv3g*0Z^Us>(u=#ZRu@bLo$#p3@K&eqrQ zc!zek&O{h$U;_(3>V#kPUW6wr_|B+ycRg}+HZe954}Lk`e}DP*P?e$0YLQfj2RcI>=~U% zuRah7s3QM~g#Jzrt1xB%FDD>wHbxHbj;~L%6~^DWF51hTRlld8utWH7^O*;ixkX-3 zG_Lxt8EPH0*bMddygb;_k69=x%F1AePOF05<^Q+9)YKXv@*Vx?%M>gq_yJ;>B6_-9 zjAE=@NzugP#t)t}*lPnUfG2ZBQWn80p%=Y9r3xF3I^PYS)FyVb*2dgKR5Yj5Mm1B( z`xLs|#d}!uaGU0wr4#!A^MTLG;0O1Qs@jL2t;bc}j!-+}?B5if4bh#jX7xjE3-C_G`D6 znOfwhl<&u&*4;Ixgzy*`rjTs%C|LBM;c=q^-0|?KI13{aQu&~y%fjJ#OkDb(qb%}N z&fGv}!NQfcMx_#~8hI}&3OnDQqAQW!lU!%wI5%uLe{@r?A ztIRSoG9NiPCq_p-_W@12WO;Fikv#1fa9W}3H)dtH4>y7OCz72oI z7zg`l*x4@2v{q*?&Nd1==-XZRg%i07QJLHGzEg;wF1$|(?nms!Ph=B^y|tqsPI`&; zUQ}*N)a~AqU!4HwRzXKcPD_hFJRIfY$B*c50@iR8pT!S$b#;NWy41~QC++L;9`o$y zK}U+1w(dcJlZJj`e1UIfzFWb})rY>Q2=6!bEpxqV^E^5^{RPJTY@^!n)b z{>r0GJHUp*(%;gj4q?-?M}DuWrp zfxkFkPEIZ;F3zs2i*LH&@s$xG zhJ!~$+O3dnnZc!jdz;u}#=<$3${Wv>wsH&>^A}2@9&K~#c7xtZ3J8kX1 zg7IdV2%`AGwVRQM-!(y*N@qk(L8h=i+cEgl>)m{9lp`Jn_n!Bw=q!-UM~O@)O$GB`PQ^G}1Aos;#Z{@6uEMp6#zKZ%F{z{(c-A zfP>^C{SoyiyZ}R-a;2lFihghYVy}y*z#W55C_i;mLp+V=acOvf*89wHW?VP!IvIPZ zUe&#?P%i&u5k#nyO?!!Il#yDEzvUvoPFSF3C3A(HHI=MGy1_@$BbWaEe%;aELde8i zQDB!Zd_J`t#(&XKu%Uv4FZg)mQRRWYsZm)~@BBK3KX zwPlZA6mZ$g2Vj?GhEJWyO`K3c&@+yVeGD6q8WyiSv|F^TYseHqoR=p|PAV=~ae^Rf z&|+L5mHSvyrKxK>SsY2h5WX4?t2x{HsT(GurA1O_IWar?<-_Cxt5}uKmv_SUQX(AzC?=4SmUfxAy=oN>M8k9EQ=OsMp ziDRd3)W(L<&Tcl!mbtmPso9&li+eMxJ}bm5`oW*FVeB$v#2QN zC>B<;J$*7nwifW^GvZDcpX|W4|Nec?#uf{L`FuF$pna45m0imRqST1|$TmbHfuj!Z3OB9zsMRKwwJS@OVo|z_!h&oOUS0u z`Q2wL_2Jps*-L#SECw{B%0EC4{z>OjeX?EC3q>lQ5m8qs+}_?cFgA{jixd0&8IFdA z#)U5(i{~Tas~4|cJO@cO&yP#AY4Sr=PDTa=zNo0c+~jv=n~!_}0fbBS9h`bvC4K0Z zcSB39?sL_~axIvQl$3s5UBYkPyumh*NT6wmfqf1f5z)bkaN)OVX{!89EdJN-btBIa zqv`a))r^NSiVkwiFzc<|1`iG4J8l+s&Tjh=Q)1DWKh+~$jx#%=O;ndpv=j(ZE0 zewNUB+uu!aFs=svgXywwl|@B<$u@nY*3gkZyXP?=f}ET<{NPY1czIJm?%a7jVxS3I zK;L6ZvE~Fii&724wpwB?r%ZPP!orA2>HM=C#R>sX=n@$P-3&f*{f^}uEAL;w)J$n4uL%3uSGf1dv^4dH zz&$oD!#G#)yI8SMKy^Pb&8Px`jlZ+IJJ;;Y1g*2ypZTlhFH8>7?ihCsmn&M8J#Om2 z%A-<}He*EG==1xh9UG6nwNs}poaYcdL|U~;Oim^=GdBlU1FEsp;nHw^h8N*-`p=(l z;u$phPSyvpv^7AEmLrE=ELYuF5bJvuKC`p4Tl21`53Q$?l@KCs}c>J-a7fjRiCA}I3kN_%S9PORr}Ih@9H0ei;kNAypcFNWivKwcN+MPP|Z`N zTsvhaI{t(hF7bu?d|`FFT-;iGO|?B&Hw-G=Y>g=v5r@U^%1Ra%1J=B5?c_>RZ^ndB zkYR6-ke+ts>gt-4bJh|dRCDdZKQ%Q)%w_XKsaUzvW>r|=W{Z-Rme$0|3NA}w@B0Ld zBKk{96%8k6D}dhA)L!-v5PniqBS5!&(_UT`v(M{P zVZSN+lsJ?ZdVVE0H*WEaS_5mnp@>)aztSBrDL;q3<>l3IrsEqcGndwpZR3|+U%ak@ z%OtaDWbnGEMbu=gD^S5o_Z@amy54f~W807S*aV8YcX9{s{d+HchKqM6kP zl8xfUcFDuV@0#&QgNC)-+8i=6;m;-~C!da}E0fvr+|gpLDxk0Lv(w>RciVs@C{Klj zg|GZNU4C;K^rIUMC3dPAr}Z}2!ukgXLpj?HB@ETX#-ut&Cpl2fq?~qQ5DP{PT{QfxV8hGplNdOPn{(nDH@33U-9v*7PHK(tgKBWE&wbjC@J+$ zmuje0=sthEf(WWM*pP$L3IGtmE~_?Nq{?v#32Y7~HLK36QNrUB<9`MQD9{l^MQ9)^ z_|$dwx@x74vLqM7{2pwJvjMeJ-4L`|13$<9b#-`>VpXZz2)Z_UdOA0}K9jevIyV61 zVFv)e!hMDjRZ*oH{54&oB_$&AGE*KQ65=U@5FZULX6p5u zvYRoYd0)B_`i-T~KGxBR#UhL#8EC?BMx^%uwWHaK5BFF0&ono=J4y>jV!tan?baeC zGp*mOs5)5tV$C4pXtrJuYI-%IHhrmbI~Hp=5)SWXnFh7wsr#U8iCZ}ehW zNg0{khKAs(Dmzdpnx`^8V1@wX)qAkme7)j_b_HF2=({~^m-F=G*N((XfQr4t?A&)^ z0}BQI{wo}C_6O-6IyS6ZVEHspS*|dQ;741J~+Y>aP+)`8LYsjpE8hW_YTB6gQ z&Y)2zuc87!DinLsv;C`1pY4M&I@WvX-wh31ONrVMXgju45FGuL>&tqUj?LAS4E`kI zjQr5@>bVL_s_OK`u5lij*q8^)XQ+7a0&jcgXZB}yoE7ba)#_ydvDnM_`dS6z598Wf zZqJCLQ>ikWq6H<8k88_ho*0gpn01`(0|P^DOG~P+uP>uYJE&$C2aAIdB)o~7)}&0W zXPBp(Baq#5OoUej0D^!9rXb55pw}H3osuH;^()5Q%j)B6&;7b$ONEn%9LtGw{ZdO_ zhDZojyTa<#G3+@)fH26}9={T>%bw!ZcqScLF5)MeCK3DIcFr)t78J{`nwrv9RtMyM z^z~2~A))85U%wtoWbp(1pvr2Ihr@bF8o)1yR88e(pQ?&kaLU##$8hnl*VictKuv9I zZ1fzT)rnwutzx%OY1!RvZMiYg>gaKL_cd&IxNCRkDkzU7uum7|(6h?-_M!Zz5{i|g z?}z)e9HjFm`vrUe4p|}iLRd&`bn^6IAqxgKv zA%L8lZkvYTv-pl>i^nD;%;Z)K;EOmq0x8K7R$9Nbb_^lQ&&qnyz8v`e#I zD~;4!gV0aY6*-p7-%)W844Rd;u=c%sWfr0DS6M-i_Yw|Ox1+sROdFAKRWr!HK5ojgZ@45WFq(?jy1VaQY^la0WapHOz`ABk5zH6tkuT1J zmTS#=(9DAXAD950b=-z9aDdz1}@v3j}QXh>E5r9_3Q?KjLJb(iPmn z(DjHQ;>y8Z7u9txz$HKxS_^MN!{O@zySvz%>xL)u=zv8E{^dPv@dJ=vlM&QaKX|k} zkPPGF-$2O#8|_`UTji47)w;n1AT3%>IEZ@wz4YrLYLaPi>s;<4h>VW`eoM21%^5{M17Lwz}e4r zCCjQ~Gvdy5hIHRM6+OI5T)6O%hUPc)j}z(yT8FWvvB)5^Y3P^x(2+7N-nkZ6HUNZl z0rqlfB~6Y0e4n50M*GxaC-UX;fB*ggGOfEUA&liI*8$d6F4dONhkBXk845zfGGJUT ze58GmWFr7syDcbcN07W$(fYko^oZ2JV8_S!O$*lwi6SC%$!QjkMf40Y3bxOu(;0Wx zg@~oOKlidbaQjE-yJk7TfEK>+ZVcV9%%Y;7hJy*!SFlKp>U0j74*j`cePYe{&`gn_ zCv5pSf_N@BUFoSYEVrd`F1h%D#s2IK#RKK^Ge82Srl!B*;uO@?g(V~;K0dQ=UYk;E zg*Hyh_wo!)l%Tf7jJ~-?AI{-1Pg=U53RzVc6yr+6#;SAGle$+f(JqRWVNQ(yShmlG zx;VgJr)4>_?9^*X#^pd=Dk6}~j4-zCS=nKFx-o3AR22|T#QCk(TW8QcD+G{>vepZA z#v6R*ycIpeyIgAB52*tkx+muLd4CD z2S7vp@f-;Q0|R2G>!FJCQ*n_&;6%d?8Q^ zX=t7kLu`7OB-|JIc2-`W8Tqsx8cfwv5zj@a@mr%>ci62uH3D>NG?LB_nu*WPpEv-A zz62c`&>Si%FfD41DEB%q21P4Nid-2SJe_ih?r}`7B3VJ-x|4HLc-_7{Ye{0Y;H}!p z=_!sb75D#Vn>OSg7u7r~6n_r(>-FY8hKuODLg|xNQ~T56daBxNgklxR>y_PFcm3I6 zC$TpwQzVhYlCV1nP0+%A7XU_q`!j`f*A0Snmo-FMI=Uyi&NU=K8EAjfAIrU+6Wc#B z5-GR1voik63Z6mBOTywNWqWQ7_4bou1ChKJZ4$JwT z`C4brGpr0(vjQxZS_mLcStyIVE3+J8 zy}ha457%!hViJ?9L;Jt3zJ^}eGz@~yrg;(6pFlJ+SyxwX!*xSs!jB&aqXrs8n6Ch6!o-nSvPAR4Ud9~k%!!d5z+FKK7z zsmeoc&Ue+Cofy3DU}UaWt3iMM{8^3Huv$d|BS_tA7eG#ZplEF5(9jARASxL zt^h?v#V5VJwSIn&`Kd`pi<9d01hf7XxTSwmXmqIvlr$ZEXv;+1`hxTda8`*&yz*R} zzOF5OPB*6$C?-H^UXRS&5A*gbUXLV#LC6GMH2Si72G?JJD3L`Fq(7f4SjzAV)og3Bc%m5< zcQnAzX+1BlG#u*QLe=W=9e#M)z4g=DTy!O7cDHu8t4^31-mol##2Q~Pf-Xm z5(3Kvt)=;}Ev0%yAZ1};L4-pS4bG2VyCv~***haJ-spg2KhP5T1TaDwn}R$2;CEBL z#K`{_GABIW);?(c9kyVV{|{A)qikbFN;(7YKwY#r{G=2icJUn9o}IbF?ER1MVt8}& zzHX#UiY2y(9D3Hj9jnrE3<++W%zvAGpDR;Yb+r--!7~lZGVW67yYPQZRoRnvx->a! z+OSWjZCjZWJA-S+eME>mYL_*OoW|>59%aQ=Bmdk;_`!}nMgl(rr!z<-PGUWCRNiUy zfj=dD723A{ui32mShg;4&pS@qcX4StEn1=^;+NBQBZlT3n`VzD_3Qh$++L~7om1J7 zU7T&1Peqf|<(XB35NuQ?JUo0TLofsI#q#@JkHLT@7*|eyuKh+BTN10`8-a}SiS+=CEXRDs0lmx@(Z=BE@%I?U6ac<*{ko26`_gH`hUecMX<3uWahr(r_nGEE6XLn`fZ zyhup5VS-j)n)A}1{%A_jowo1p*fI5bf8w-UZ=@)ysMK5kTdMMc*e3G1vTmk1YbUrh z|B22m^tD@%kUBj{QjnrKX=uKdBvK8n?K!9pwGj-oEqj~bQqMBh9GzJv^Qt5-UXn8Z zJidPMA@E=gd*q$h(nmk%)KBC7aE15+n}utG4~AjK^xfhLk(ZVCq(NV7Esa~O;q^Ue zJZM-WW|ASNn_p1fBRb@UzsXUt-7&3N%{^co6qcCs1v%!K9~NUH_bMkMwYYOlRmWx? z$PrlV_gIRpK?I%h@!^?7H1TZYY>=U7Z_3RoRE>>oeIEJY8aSM7ciopbGe@iag-58h z_-JwUTVOVXBdd-9D&xzOGv>&mXKMThU~n~EqSh_N)b>Szd6p5Qg5D;KoPi;Hdm`Wd zW;-7X;T4aTHUNWUo@W^C59j5sK*_4KB!bp)U;m4xVPZ1AI$8xYAE$|Dv8@3L7R1a^ zad8r>QRD`OMi{6;b1jB!@I|O+TlBk$drq9n^RfH8V*(_dK7c$voA{g9H%X%~+hT~c zJ6)MeW4J@HGMz7$(Gj5GAfztq?9t=D7&IUo0|3q`leMjFqW0W^vAc zCS!aYVQ)cKuG#TUaL4nP*-DZO zK1wB+)Y@_S(kbzB%DXi+s`YssQ~ZIdRC4B}GEhqn5{D>jjr-Wqgu>w}7*sUnLYm?9 zfqw8wOG#vbNdshDQ8In3-sC_t!hfmRMFZ+yedd0Z29Z?n&%CUW>iLfWtQ#wJ^0=0P zx#y3^k2R)vQ>+|)s>hIdhdGn&&8Zvt!QeVvo7&R%=8OFtN{W@<>c_|A%8o|gZ)H7- zzC+6l5O0_57Bnu~YB^P!3p0rqwu^>cYPXx?UCib$cE=18e#P63OHi|T-Z%idL6Y&N z!R7cn7#3Vz)VB}Mv|X?VA`^_4os7bcZ9rVI84h5_)2nqYG};#y7k5h{R3Jy}A9W!y z9RcibGo_^RRZ-EQpC$yDm2!0YD=n-p$~RBjobh#)&cF7470>h$0inRApxKZt(5(R} zIH7&nUHib`(ff$YJ)KTj*G^(P(PMMy?y+2*%;vj!yqt+yx5^_7BUxp8m}&e=I&?_M zz5l)cS>P!j{8h5IgjB8CrEHCs|1DPw4-Y-@PrlGby55n<x+hi0N;Xc6v z#gHd;YsuqOW@9)7&zjGYIG;+Y!iE@VG3zDOf7ihGPEFT^tz%E@So}-p0*2lQc@D)tqy3tI*&IhUXQoS0Ob$#N3UD@#;8W=WBL`U zUWt_G@f>d)s4|Sn2*;jrtZI00!v{zp3dy9*)D6wCAa_{?Hk6n%Bog>sFtNIig zg*;}xu2%wAD=# z&7pnKrv3u)H5LO>5ax*ZkhHB$L}yLsce3Z-juQXHW@m-3l`-taDyLi7ktb&pa`xv_D)vyz`mgHh^NbxQZmk|0Y#DlM zJs~X@^XByGHBny%7g3=%fKnC~6s+tNmjf!^vS|0Oe{?F~3mL(t)e1F@J&kiYcaf5K zLWfy$CsdrKg>chKS9w4q%&DBqmtnk_4OMTzT8({2F`Py4?2Z;$C6-ocl9-t96HiPMLA8~kmp4*tH8 zOW)Wz-7^u*m{i%1VrOdm5MVAyaw#!@q+8{9@DWVW_EEyy5D(+p!xKU3WOkP#SN7hIMP))8rha6c{S>1BHS` z_9KEt+HnGN-+mwrAytcnFSE?peAj}TgumL+cR4d*>cJclM?FnoAAzHt$7Bph92*Pv zBde1N)YT5#-;}`?y3quOCgcQ%l~lBPNt_qP^nS#}$M@m2Uy1!qwS7PGc1v!`{Wkr>Ap++> z`dS%Gyznt+vexHc@~S2No_62RYjTfbpM4wEpgkyBej~cV@BDk z{A%3G77Jq=D;L&@`{tV(XZ6G7AdJ52jbqQdp*Ioh3farwBrzwf$DVE^zs_oL;7P}6 zyT8@2IuVwV%Q9ig{q^gaistcaS>F8`RJuT*iGV2<5PLI#HuUS}=Eg;r-RsV!s=C^i zILL;Jc;w}Lpn|fv5RS43X(E81d3mg0as%G{5fHT{M&=!9pjuW|M%!>nf57glv-ItJ z3$FOs1$bD%49^99-KiN2tO2wkRccd>2M43s`R`Ax>)kP=poj-O%@-e)`XYYkUs#o; zbvf!whGH2!>IuIe#eR72JPIwzD&x2P@`gWl_o&pS57ucMkw2LB-l|gts#_Ypdf!I& zJu}boh~z!*o4fh2$f>a|leEA&BXs?07APm86frTR5*SR^*)pz?pOgjwQ|WcuQlGZj z4(xFr=fyhfpv&a|>2cAR%GrkT@jB!2PnF?d;^`f6k$Dl8h$7PehGY$^fkgN$-~DON zfUoRbIsM>j*{@52ZFPHTkyI0xTK}X%4rLhk5Wn#1l#4UJpzJ+-Zdtg5yL8o}c_YJA zR^{>c2iI49onpaKFPJbZlaDb3SU0!*xfcZ!h|-=9y}_a06Jf)RlZ6|ryORAD-WrOo z;Aw4BUU_9@gWNodW+U;fXbpXvT7|AjGbBWm0@QhVLHU7> z4)#kyRu2f$$ONo8-RDkI7%X#{DI=050jXG6)4X(R34{UGk%&(dsIEXY5?@kz`D?3MVG|m~ri+iHXU| zi1!0qC?1m)UJJhd7a57wWb<;vyQ^aoI5uEDc@p<{4;uV{#E*^|1ZLBqw8$Bb{sy6m zUdbz1zE$$Ap9@txBk~EMpZ?{I1SnlL|Kc@JVFCAkQeg*~+J~Ul?Zd$6VB4taFFdAQ zNE#Ug=KD`3D+!6G22)exz(?wAVa$T-FQNz15BO<-Cjod1dI36eyjJK_PgpHdBXPN&pz}A1T*L*C$I5-iJG+X(iRBxopPJTPnLPuA>DDT8 z+OAiH!L6!N3F$jgp!U^F8b_jTCc4f#wB(b(ek&?X zJ4it}I|Z4jVWsaL`h|wSu0AE_)o9Rv>CvU-BeXF7F-h`f#?l@I|dH8sEpZ0d+PSv#X~S6@xDEMt)B?k zvx=6ZQjn(;m{0>9V!;+d^2D@tnZI~x3xIxP&_7DfFtJ9lDEpEaLJr(NNK6e;1jfTA zGe>-Rz^wvq^k}boSNvy2%jJt1;Gd-&M2G#ObZ)Q2CBzqg&?O53+58w}0CGt4ZCJ`jv zJ@_m`Ue>0k4LVY5?ik3v4MNqAP=pjrZ~xZP6oKc3!Ys{CyF?=OM%J2V*A%{edmFVXRwaX5dJ->9ueTifmLYcWzVVy0KOtyTJM6Ye$mYBhw@NFhYNT#G}&tjx2 z$E0V;5mn#ww1NFwM`V}CmK^7#z{;-^(SjVU75jL){_6+VhKM;W;^w~h0W#4c9>jly zI9c(uz|#W)#ry~YVVPN3z97Uaw0?XK>)bp&t}XX`qRY;+8k-$HFZoXTDHoiVJ%OqK z$TN6sVgjGr?wwUVuN$O>)ItGRJMlDao@d-HYyxNJemWL1++3R{ulLPFhRa5h@zaIp z<$iVd@)7{!V{B9c;R`U`S!5*6cjWm^5+Zv0(Ggdohs?w?kR@=H8f7yt#rg?Z>}yx) zb;Ff4AMoHZsP~L!ipQBO(Lc8lw;}HT`)8x& z^shc}u+VGNg+&V71fPxwW;|X+KCaY3ttgq8m{!?=3*bNMG$H%X{PY{F+FEwR!)zcL zKisx^_kuNyOcp9kT^2rEo^(r+XBg`?gapu=L3*D)uncF9-uA{`M3BYSiibdckeC zv_uW)!5?}+dr0K6r2wu2eEr~Ar+QvPo``Pl&D8Gphu!wN6iLId%)Gon5d7iGz{~&i zCS7KPPr%Xy)KY9*Trd{1AqM_CDK6rJtB8Q0U--)Skky(A+07fGGHVqf!!SLday+Tp z^lDZ9Yir;0(pXT+A%jRvEgeAj`oPF|;KC5!3#1P4nf3saTFo388l*AVFgpa@- z31%8=?A<>#Kr-VIqw{2TRb}&EMo(WA6etPB4hqf!OR=-FIsj&#F_{NVk<2kk0?S188wod=p@p zE^i_OPAk!FZgLOQ`&^cT-i7-jGZv+;@=Z9P%6+!Gso9Ye#p|ZHDM|kTW^;h1>A3Ds z&7d~T*LQFYaqUXCJ)E2Kr0yt#p*|pS=AGe^Vh00|Kss~ zqBNS`_vdL#;iKzzwik28rvr449s38{t_IZ6GI%2KIeU+!Nb=3ixj`-idc_segZA{h z!TYhDUBH6?_$MF}1JoHFUBEuna56x5JwMad2Vw{qwxnI=KWTTUCgs@6gJFsf4K`%@~3eBsfHs9Z!IzlO;k*`P9L>) z(_~NaxyyC^*4%uIvvNfB_>E6l|C?2PXZOv~_8mD_+)Uapr_7XK#y~OAjHzy<0AW6W zURrK;ipN47um;(Yfpd$l{R9V$;MSak!eLxb#lhNhSHlC06o9@sFgQqHKR|PJ=Vbvs z_#PO=oR6g6`5I5N12`y>(0_49bOV>ibvM~V2W2FmFkmAQFnNH-B&g$9O-5JZKkq0v zH)E&U4bVs@u?9RL6JWPpId^-zYQc0LCsjGEcf7 za5b=id0$EVegqs+;FBZ|4mAA%YChF-#C{4)m+b!dR4wJLAb)p~+1jMVc2)St|J1Z+kXMnH!yk67}wdAPggyIS#+&Jf`D5|;|*IDb4zXqN^w z6AWK0C@`AWtFg4EZ;^0UxKKG4Z2Y?Do@7jwh(V)7bNll6)fET}soSYL_RGD>LD#It zR8R4@iGM1PRc-`&mUq1*8O_pSUCOUNrlX?iwH`aDhFAY}aaoq={E7VilRZJ~qVbX8 zBW<1=UW~BZ3!>1UM-JL`Y_VI5`eJm$N4jfDy2^JhjHsT0kF^tMZm%`>OAQi@sW4)f za-9RAJ8CPNE8Q$hR;^5wuT=4QVwXQgPELN>791b{2uE=*WVQpSDF5jY+uiAucU>xf z;~L&6uyXQRwfj}V`s#{#OwG*J3iH#~(%ldA9v|+3_0zVGz&I);1t|($y%tFh)iHDH z*bZvBY&4!DfmmKxRJ8WQw0+=7e-{KC1e9?x|il@a`c| zYh8KekfU$6y>LdEQ?3XqcdBM!4M_0CN;))cpcu08Zq>-fsNlCAEpg0Hsrce z!{rIO_0zu^y=qbn(f{$X!KM4}zx%$4hEZPMl$X}u_U6NiiE8;A|Duzcl8QTGDryic zeB=>PBO>jlZT^Ad1eZZ*-3;^YXec=R{dz-TRZ}}`VKUb-SA(Pd&lP3Bq?0?ZAgX5s z89c;+&cp7%#xYwhsCo$Ke2-J&EQi_K+b40`8wsNo0BiJr#-x8!fg=u@ zB{LR;mS0km`l(~tiuSuWa4wLwL)F1Ny)*AUaq}>8Aga;d+EV-z_!kQRD1?#nH+8Rb zva{RHXgRYuA84MOW;@IEWNG!P4Rr+|T_<>7C-}cf_5%-VjDpGAHme&>E^%z*4~8i@ zes9;yX61KINrL}ao1rcDQRjX^YL;spswB!WhK5G0`hCbxmKI>^y_y^G>H{-Em#xeo zE-<>r6dnN{MlXQI?3O(*D}mp@zx3wF5A8bbnxuZot(dml?Gr$mXxNQ}HkTD&uj`Jm z+vXfNiep;7HY~Gh9%zJ=<4q~|oeiHH(#ay zsqbJ>j+FJcfPMF~rS5#EiUCk50<~yA@tXh_(2uS+1s@4AzTq^Jjtq|&PNNw`XJmMf za>wfdrl9G*UkhXeVxU|BMJktyN|jLsA~Q0)%5)dbf{lk~59SMy!#p58g8J!k*}S|S zfT#VturP#NB#?RMU#!GPe{i~>1NKghG`mHT|LYDgQ455?I&F!8C(6{sg3rS5V#|Ro z2+S_D*o;}tCJRtA@IpjgfLQMefbG*WD0dCVs*J^s*CaW>IlK28un}Naa%y^b{t|BG z*aZR34-uuQ88_`697|rdN@98reXB8#7yP@uKdZE7O>xahv-i(=)8Cs=eROsH4P8~D zh8l16T{s`%V3igS^1ERJk@`s|sbBU=d8+adGVfH=i99N46_Sr8=ug__)gL*pp}z1G z%s%U}EHUsThE?0Ok&?C}u!={M;(ua0TNz*KgwSOkOOUBQ|J6W9u&Fi&o8i53WpE*= z7N(lK=q!(^!$+$Y&Nc9t{7{j{+&k4FDn~`Rz)NFSPTgBehPQf!2Fxg5NKtUG1WdRU zN|<-UH$N!psqIyZvo&vPrq{C}>||5M@QqKf!vPFLY7gAA#u=XR?f3iEh1AYC8_Mu5jE{26oUyE>ldPj`Xm=p?)QB=zrVQA<-Rl+D*13 z^;zmIPKYS5ls!%ZxQt140Ne!!^|)1Lvof z^YSpjojjg@K?)?Yo8tmAY9RnT=dM`AjXdDV(SC42s05m>t=hm?4pf7!nrQb+N6M4! z9Lz2G!R>Eo)w5OEX(PN0IaR@MQO929%A6cbdfduVZ^fw1ZPjOHoU>=33fKPOz)<`a zP<|?@_zJIkx3;TZrV|+1W1@Lk<9qVSAz%XfhI;iyqet41f;?0L~3b9WJRL_Oq z8V>MpUP6Wzk|f?+zWvfTvWxSDwLbNfdvj>v^xr8T8L8Kmo)=L4gX5XNV~q9CVgAbn z1u6`)Oji1}0p9?8UA)STY!izp2t?<4r1tP_QcU9Qb+!95Ia2a*+B=aVe4)zmKWs5c zrte~frIJImQ%mv2dh0^-ZctgzEj{)Qddq1~7|Ndibk{6S+%z3-5C7#BxvHK0QeTqk zpFM%`wSZcMO-biKLzQl)da*Zi&M%MSUc8pahuh~0Tj=1aATZ{%pc)^59UmQfKc-+% zeg>RH6g)_y)$G)Zv)L8Zr-wsowcN%J_*cR9A|TK}48wX#e{P2@bigmNfwh0ulB4~( zP4**Ck4jg70F_SM(S)1Kou2+>zHHQCZYn2W4T@6X8{)|-~I zos4^Wdndr-4WOvXcsTdm3fyFU5hQ~^=>?)! zrrJbajrlUE%mlN?*Zz!W3X}+c(8N}B0^TSwV2@TAN_`F|6#M=?YdekIVN3qW74<8| z;czJps9f!lO8DgOe7Ct?c;j?-oOiGL2qS=YgUhZVN%%RAKt=e<&&KaP$&!m z5n+I?{0J!C<0WcO{(CwdwvzJyho`R!sA}E9HBb=&=?3W*=|&Lg?vj*{k_IV3kdSVX z5^1ERK|)$uK%}I*TPfj=xzD}#E?9H^Bfk2@lUy%^4^JlH@j07 z^$_+SXPy0qam2jpU}t%4TFCO8wP4p72Vs$>og4g_BZrXqg2_fUoFb2i*aO% znj96Dc$V`j8!bJ3Ok!deg2ltug1lt1N|^Y8ataD&ziS;4tmEQrv(&FA^b*?apD9qp zIlyTDv)f(zz#pk$%bjaWeCocbB-Ez{@C+| zK%H~3jt(58g+8u-HpeKm#c-rwA*wWQ!}Pjs0!GgAaw0w-eY)WSQ0VM0c5zt^)50bK z9RUNh`hVj@)ucE%S1_9_9N*tS#Ni^7kM3*8eE})=aT_ zh2N68LunP~`tqMQkPxMZE@>adp->ys=VU4bl`#?L3Kf~W>*JL!i!k{T5}ugua?j?? z#MRor2+DnHbny-sk*n7!yf&dFn?=uRBB2|Swz0ckp03iYXgq&Toh2P*0WgQ{3|U>X z=lXKu_m`>id68+X2R+m!tUCM$<7~JbKTJ6-?X916F7daV@2Kn2#yziezyJC~2E}eU z@dx?m0=tLD|FT1dKMXwN)!WN0Rc*B38pRjfdv(sHH|dM=DW)mHg^DSk;h;?4;_!V9 ze$FqoFUIrZfhA81wY13KL*5VNM)($gGzOWvIZ%b5ZGFJi`xJFS-%IZTg<=Kbeb)*l zLgN}?qb6n(tW&r8eGz$%(LQ|G?>SiWj^!RfB&%DG8JZ?p zM1W9G1xE5j^J@AZ{NFjLGS6RA;u7)ieMZ6T(fIS-rJ6L!d%M@H&gBa!wsZ9$)7kYS zx$2cFn~_H|>f;Xa<=vbC?Rh92@2n*w$OEGMO6-5^MyVlO%nerpvVb zOB`MZ9qBLvEp+5{!2ZrC7-LLyd$%vOdt)fW%c$rjfri`r<{PV^YV}x*;A2OBy9(Jn{tZ1a2ZNj!S#W%es>Gb{{n;|v z`1u)Bt_^BhnSogU8$rsHH*6D%Ewdj*oknQpkr|Nr2|C}1<@tWA-eggk`J;E%O?i=R zkKVCpVNJd~X1~nh>IV;RPs%SWc^T0NOIwj$uKbpG@bZ2$F^29!@AHIk)8Ju744Q2m zRdiQdS|0H;%O6@oC&6j6|v+43AenqrbyD2<2oj30bzZqFC;{6C_M?{AU$Z5UXc4#b)=Dp08 zzB6opu0i37$#AwDJeyxd8D(YB;o;#boto@L_}Q=N_itYIF{317Ra%dRlG-y=r~f954C-B|YLfpFGtt4qNFIzyUf@y~+TZ{EJN%k% zFl_IqUtI(*Etvt<@KpSRvJqHN+~K`N`I=~5?dQ)7J*u9 z?_C?k`>)OWZm%3)_q%U^H_~1m-&u{1&OW)9^p&vG-Fe)gm(PbZ6t9?(ezK!*_(X}G zyz0^}n$6CQEjP|!pMi`lUcmNGl}TDDc5gVh3*K9@XKRZe9>m1av40=hkp7(B&xxHz7WY zmT}EgjESxSV6ow30S8u!^uR!ik*$7C zJV`6s)$l4392Dk0_56|7Gdg&_T7IB3Sn^t{Qks40zGp6OQZ#=R()4L>TP8w-=ouulg}skjk_adt``A`z?Qc3ZYc_>8QNppaYpI9@Y|F2F0U@6_=3|4HH2|Ul~e=r z=W6Mqv)>(fo^6edAKoDvc~)lJGKmNOTRdb+s3!OLV()3S?F7z25Y7+&8jh#Im=Tn( z{JSt8JWhpopM=kDi=h~uW*GCCL=|)BQFUnkpOvc#cJ!UvMa(!dW;Qm2lKuqt5ny9Y zE2c7IwWwH%(OhRV0&m~CYz9q$W`5j8NNa!oNG$nu#9;O{8Yw=QJ$F~?wQGD?2y zW$xQ$`fV)I*IBS}XLHZF)w?FMld~_YH(WKaC4$>4uXQ{)e1T=6>=rYVq05luZ&7de zs%%YJ*}F(z+x%6M1=XLHo;F}7WLBGHII;K2S;~Sw$fV5b&;o4dVoFMwHNTz0WN|j@ z2N3yM)77OH?8i11*8p6P8Z+`u1ErHRotLtkC9;OR!M0MKr_dbUmxSO)THW#F9qxF&{Fug3!!I6TI$ld0e-=`S<`XIq5bWu=pf)EG zY3`3!B=?R$iNeh6jd_qYY#$+&+^9rB>8Ru7=3%qvQgZQB|IA|j?@+i?w#ExEDw6V8 z_*yhR5{sQ#4kQb$gw-G2S4zE zwIwb)n>O1+1mE!^x(r^9$w@ao&7S9;Ptd$*7dyZ%`SFqj0RWH2DL!xr4gWw6MYb>najk2ngZTAXL2wM(2WW@fjui~r-h@p5Y@>kf=UrGF^k2V1RM z{g_o-`vFevi7C^+1gy9Fn)6@KI(5$!f9Fs+j*}y53O#=_u&p2+```Ir;FGr#H$IoS zr;NoD^{{u1v!kPb?i#$P`FeJ=DX zfu5@hDh%|Ti@%&vchl47je4&1jD_Q|le4hD#N)5TsacR-jA^N^))L=8^oHMe?swU7 zM)neLZHq8Be%1;th=0EGyUrU1UjTZzvXq$vx#N8eW!pq&v(YLiP}{pGX&^H*l?+UA zfYYPT`kcRPKVDo|K#H?t-9ldzX;2G5Go8siHbsAZ!(dyV|Neyf!@Sj?U4n=H4{YKy z>mB)%_}=3UoBJDYFkUIVa%5RmJ*>I0$0(G;LDFJFwDyZT@`k*&{XDI>8jj13ZAXiL zpec5m(D~Tf-D~NCT`BgB9Y94y2l=XR9}?@kO_;lEPx35``}^NO4)U*3qp05XY{$h; zzkVmnw>xpSgN~?PUq5IijHpGMS|X*wk(cYP8Z&rdH#u#`R|*DN=#FOQ=66u@u|8w_ zd^90i`AffUtU-&74&@tB4+E{Ky0{ONAQzyi3yX_iCbngmtfopH`kqhC7VwzzrC$kC zATDdb&t>J6qbS$WX=K)j{H|zZg_-E;OPn{0YqaF)iGx}|vep5u10v))=3TU?^~AsM zC}c8U5Vf%r@!IMn=)Pz-4V|Z;&${dEp#xj&2uwX*74qF@PYL-&IhU~%KmlH2e8Xh}yrKP2H z^z+Sa2&+}HoQ?IskjA3vD z0!Uyn;|v0(3(!mgRbp~q28HFdq&@@d6vM?U6Hz12^_-*63yqp`-S7a$%S>*9SBf!5 zMo#YeQC1f8&q7UMZP5Qg7nYNA;#A7x@=VF9l)qg*C>2Gah>RybIBKD%hpUH6amow( z?!f8u%4Sdg?m{(VINltKl4q;Soi{fxp)~82H2)Tti9Hzm8_-iTb?a`GmTUT}EEf9B=__;>aPG`I0~o0(2?^NF;;Xu!mOn z+;4S1Zp$Zo^acbzrZp1?l>#6ZV@?iLl!4+%Ey!%vA#p+W=V`-ZAN@-jcxV-mZ~iwC z(|qml9hnZ5Y%(?CB$5bfdhcZ*8l!vbNyatetbb`352~|dyvI@apEsmSbd}{g;E50R z)RjBLW|&GNqYXTypX&i@K~M!7U(I%G?tdVrgG~>yJ!J>WQYEM%$~0(Kpvy!IqU%nl zTte}FXsMftk&=UZ(FMB>hdqMh$o1)Jzyif-mKrV%2j*SbZ#}%;8DC_MRXf%jHcif&(6)cwd7*r z)LHf@M{KP6G! zDW*O*=+|D}EbP%W_8Zfzbi0+~qq<_s*sGw~;~y3te8rPt74CVY3bjw`-;5Fyl39^r z-xPx74{MU|MSN(orUel*=VnRkzn5;Lipjh!?C6g#PfRb*XRm^Y%b$vi-)IZM;99j& z$pmTQ#hs`w)7O|1TU-wTu+ibqM_KIgj3GCYV0eA(Shk+%;^N(Qb(+l8lA;;%Xa70{ z{sUaEnEZsf8<-S1AKOEm)B+w=-;p)1K=xEtF~AUzrIA22qG7|mb+`FyK>KiY@t&D6 zDoXLQnq1$jOY9|C6O#<9=#L*h$iVm-?V7)#J>}^DkR$J|4bko1#7EMo7}DPuHXJ@# zks#5-3M^asX>8if2@!x(1Aq9wS7eqTq@=9&xTZlB*jZzq^ z`5CRez#%F-l$eP=F$3@P^t8RJ>&drT;orfGF0&5y53~FG6@+2 z24G&)rn6dx-4Yh;T96}j_x93=h`fd6uWXBxrjL(FSW4=91(R{TFMxIC0cwM$qjr zSi34CBS%ZCiAizz>gDh~-G$9SM>1M2i-}*335GiBf9>cPW0*J$xKy{6@Eni&P`<98 z51CSy&?B^DNPjghz*qSe#fDoHx?jL=t_8q}m zmg3E}+=TvuR~9P7Cf*=|PcqTLLWl^oy-yqn9_qJ)My3ybx>I=LPob;UZI$<`$T_a( zByzq!J7TU#2RLV<;y-EA*PZ$>av`_qn3yM8TDe9+mYoC2O8IYuSvBY$sV#uQT?kx5 zY7dE5_ubt=P>M91q$J{Ta?Fa*PXe7m*i^?{7-I?H`PYbwhF2f#vQzh-WGCwS^XMY=7VE$mHNgtz3^dIl^Z^t6B2J=w!~7egByTmD zG4L&lD2jgNGiy5^#l6PIIDBdv6zj4z?&r1JNcgqULUf^_K^Rt&H(iTVA~ZC#%IHQm zx%*z@R~EwI-+N+PdZL3Q#&|%%4krxJxxmm6^iKPe%~;9N*3iwp*)HG-jxSCD%Njcq zX8+3wJ3IgLaax1l&nDBU2J~+WH0An@2lGp&<2iUy4ROFeizv=b93) z73iuC6ZrW4=c}27r;JKtHJKfFMrn$i`vGaekMVYhZ0PB2=x9If>F7QqP)cT?B2D#T z>q~jj(F$|xt(&(`##S67K{|^>cyRT~;*uaD(2$Uf7g#XNBF_t$$Fiwy#1WA#+mkM4 zuz82sPfW|JRxK1xT}He(s$Z@Z%o6|%C^#`*M^#*mm1hI*F!pHdP2ikO0k8Di2-HO< zbC;#7GgCZS&r3%zD61czt^-H40u`B-tHQ~3x3ZEO(1#A#(`8`SD5Q;n8SoTZ?td%= z*s-J4Vy74r+q2tMEc70BiCx1?j0>jldw1k(Mo1ic+97 zSmQs5KlXXsk;5=Ebj;d$tnH=l^5Lxs3};}fZ6g-1`*1oQEOmqHHwmiCo6?Dx>3&*T zS`egzc*_j_g+%~cco`LC2aqH@~| zwc97>S=dBt_gl2(HR!&^#ocXz@v*y2=WOC16JwLrWaVmr+MDs(6+v%fmnCA_l%~ku zBq`J?7MBajMfxB-1jLDEI+<;g+_?;zaX<;$*Il$cQel_^sC@<23B+vH89~ZrJ5FBJ zrJ>=cX(iY(%< zqalUyY})>BnaRiXSDkT>wo^;Vjo-#tJT$w<0Np(B|I?EXA_7n>M^T2(fW-Z*#3& zHh)DdEiExKf6!EXGH%O@hz?=xFQoevug0oV_6EFgbBCuO!8ZZtF1BO-XvNe=5pO7t zP!%BpM%~o4)^ZzpdWh2&x-+6!g~XTVBd#R8wldz{4a8Y0e_2u*rNiD~`6JSQK+-ff zv&;a#hqnsQO=>Et>vH{3@VX7Zk~Plrl2Y9Fm5Z%xq+=D`tL~3oEaYvj~(1N zpV%NNuP{UhCVbh zly#&Nw%F0&=8R3}8npFGMhee7-0U!vXS19)d!mGSv~dga*O3A#O7i;bWNgVX5?F%@ zQ|YFpIx38As9q0Z1inBd2y()J%aW0oFH}hVdDCtCH@JFOYQgJ_E&k(+skB}FI=yp+ zKB<|7#X&o+GCRDP%p%49I#t&A$h-u+q1g-bp=RH!5828vVkaRw_*lrz zK!zGX4E?eEi;9ZY0up3-etO@hLIJtI*-x4*g$Zqq1#u>y(`7<>#pHmoO*$5q-d&d5 z{2z&ye~U98GH+?BidZ>ir>|MRR`k6S)sw95cRuZNPL8DKNpZRD#f5Oa7%+oKAs~bo z90WI9F_>?4%XV+gRHQs=I`K!mBD~i#4M(`)6>5X}+RuyFngLYlr!Fb`k6=QhRe2|s z;qpbl{!Jf^lfj)(jiI#7G>?nvQz}7Mc^iD9?66@uUw$U7vKC9}J1=e8gNLb-dgBiU zF3#@sd|y!gJ+Jqqnlapwm8@uwx82!)unwOy;>p^0zE^=W>A70TVfFGYqqDpE9z*_t z>}!G$CidTI&uYA9e3dLb@Wr$X6O-V<5h+iWjGRKI?%@i(Y)1U^<(>o>!>9r%+1Lgi z5#wt9xQVUchbB@R7d7Fk_#(8PZ0%A*WF4=V0EB}Gp2r2C&mAvL#6si} zzf5g|0K##*Y7A72H8FZ`J(|=)A+696tO(y)W9@2nhti0z!I&VNTB4nmIZ5G@|{cDc%zUrn}-O@x-mRgxGC`;-$>d;wVWe za@mG!`q&(gFV}I0eNjqtByQJNT?&bK9`UcgE-5PThrPUOk8-@2d**MYnW!U&{*!sE z89DnFoEBkw+y;JX%MzZZk|K>r(V}L-&}JdN;n_b1eVn~GNxi}oa{=k3qNkjUE%;M+ z-ZIXYmfT7$On4@s4R~v+TQb68MczvFIdh!7#YDF`wmQG>*@tEdj*)DSiWO|LzKhdC zL@Wc5+Ef9$F{};Z$)}A4dtUej>_mdOt%EJIpMQ7&f((O>q0gy#gQ0J=4=~N9U6F`I z%4K6bBs*II@*YD%Lb&Z`QholNA;*v4kzpS&RVp-A%ZH~Y7l(s&pySqs7N?XK2V*Z3 zVmTOdHZQld)LOx;)*o>`>1vY%4r@qOfoWPDm<|v^dz^3A)GdUvAmY`%Q}2Wl+&*^5 z2cQR!r_|Vy*fJlnR*SNv#%LklU$LCWOmxX5hoIgW0!ijZH(Cx7@rFb-)R&yg_Mnaa z187yJn?t8@NVr7&Sv)6+^{!g1tU4!IKQeAs+PxOX(uPl6V93gr7;}ZP z^witi@~Hs`%K?0B)p3?mO-Sgfm>N_zV`E1HJAV=I3D8gjh|2#9rkS zO1MQOj|(hbrw7edvi~axLy$BT9UiZj<}|A4GM;V zC=Vt3t3OQkfCpMDeoxE)U&VGt{gnQakSV^)rTW=KmQT>OwqRJ}wB_ROprH0boRa}d zmGzlb8}rJa1$h;2@`Ogc{Grb;=Hzf=GX{?J*Hh(%b2z$sRAjwZtgzLyBl+SPBj}ni zT}H;RQ%IAjyQ)(;_URgQf2Ai$F`pM-u-s$d#KD=X-!xv5VId%3y-=;)5xmk99xx%g z4V!x>J380RELbD3QM#b6K)kCos-I%bsOO0OVyEt?Pwzi4wUs+9%Tf4VI3{yjJ&)CJ zEjG4*zYB&C@EOcQ_6y>vKS6KC_VV@?=1qz4jX2RRmJM`}VbX;dPS8fsQBhH`D*4=9 zfv<@1(cc&Egwm8^W8=O|*}~(#o!Hw^HD*Yfpi@3!tM59h360RHi~N;@Sz|yjE93Wj ziEDQ=hHC7MD7^PtNLXm#$^k_B3`;eZWk>+hu6Gff^ACZ-t3F&TzYS+92;aX~}aHYm{`k)`-Ou_D!HONp;6Zj_Y1 zMVLtvf(ZKUK37>SHrLE7`m83Vz*?LQ1fV{SLmSF zX;jdLg+kKk-!;Ck?_ngy5ea%6WLi-wbZFKp%O(1FpL>y68cp}PZ@9Kf_WEWmmx<7- zhu3~c9{FJe?H_9Xli;!TN*Jyk{!Ada-3iT5>FnJhgu#c53%lern`Et!%85oVK0uf( zlPxSPMlVMMwDRS$e-Iw7>`%X%bp&ywa~lSqLl09P(8oirjpf>ElpZk@+FVWFDydB zU_<$ZS|^*p^iOJ|QIck~Pes@3BO%r8(W59Jp+BJ5fLMpupzEdkH!MJxRa8{4aQD5a zTM%Vd{tHy&Xsy<|Kjh<)#^jHwXqhG-PflpXGKQLZdk;+*Yy8XjDp~(wce~$+EWLba ztVoTnDM);{uNs^D6-I=sbHSqX$1nf-taq)v*Iy1$C&|Pd;8M0UI|{s!z{3c5BKnN# z<`yY`N0YxhCb?Tfw%pRwS9x&-YBL1uOG2~6`WET0bhzW_F^9YN>9)zk>iR7^ccn~! zkz#mEr}Wb2JVSXOh3Vo?*__)89c9*2nh+fz)gU}q6`lY12v66u$7iW{;=g!1sKDQj|9;||0{<<8cLTG zm}a1Sa&q+o@MN{VzLuO`7ONJUoD2$*(?LPf@;m=G#4@;TKEI;fPTHr*D#(z0-*MV? zY#;I)zP#F=)kajgS9JFQz4TZNvQbDLbfBjG(Ave(uN?AHbr*Ck+5)bxNc~m zw|&DW436)_Hc+l2lj3?-N^O#y6!YZmPYt)7-^BYqPUUU?d$8QNx5tEhGb&84`_r7N zIGxxV8zyz$A2p+tH4ggvu<>Q#$xxyJ)9gBWaRBYhSH)ALdCSg0&J5FOY(d@nZX|VE z3Ej0{uA?O~GH6aoM>YjYe0d;Z@u;xp0sdxV<+x(oPQS)f9swvSb_t~XV9?@M{fQb>T zj&wsuT!+}*h_QGzPJrJLnTU%)mqkS&Gf^k%*Xc;RilKayr}eLPmEiAQXs)TLBN6LJ zloXgcAvuwvrYk}vW*+MGD2}jN4`Xci-l^JGkIz;lN_n_#>_A>Z#c%0@ZP*(RA0Nz- z{#}bteuYMV_BMi2Z~@^fkg`yuT0$HNB6wt7g?u|1)bFt@7SF7bgQo2mVA1_Ltpqvg zkJIl%vJMs&ZZKX1A3By=M@I*CdpsKH{p$o)_^HkRshdT!Gf(nNxyZL?Y1kv{{`|A3 zH1)!eT7H%jHa2}zhdGYgr}SptB-)R?c8NE%R;04Syc7vO@4Lw^w|DxQ;(laVMZ|m) z-qqFLK0(<=eT=dhKQhqtA?9q?h4I0+!kMi&&L#pSF?1s14$$=2Y?BkT77m?usE_(y zYR!E3wQwld8Y6V@J9b+*_fN#s8ZTPEbsk$8UQWA~D9tz_R zQ+QwYXlfg3z8GXZ6gaM#J=tr!f78t0&VohquLRnK*7ep0j-`WPUFwI<1=Dt`0OQSr zG21?{kI%$VuV!6X*oju#=(THv#g-fsfUvzF7lWuU1_)oqdJw2ajkURFp}l)xRx@Qa zlzA)Bz>OF|62W6MzU>w#rXiO!E4ugXrZ<50-~mbowQkDd^O`AyrT>pNKt&!i;CMnX z7w|5wH@h8q5YJdsp}2ib1N6@cMjs@VW+X?R5bCsofNfc^VQFHzRtaEH=@uY-AqtX^ zXr~Od(m2USeUkq8^@he1_F6VTS%9(i#@%zS$hu*}?Jf!p|IPKACm>>%g|U5z8|hg{ zjmlL>YK0s>mP%QP_F4UV;vX)xnLjuZvd7)4el^BnfF^2{TG*3sMtNAibhG|)nSd0ukJewVDC9T?lW?#;HS+~Z+M={`FB zapa|qRq@NmnPUVpYL&CKR68Pin>XECrav0R_s3%D@iz_C^XPlfHC#2}--AX^P||>- zS0!jtpJhdI!=$G=6Z=-n?5+MOQ?Ku;nvehGGxPq-ZE@MV3_#6$=Zq&K`9>w*VOx)g zalL*30%D#ZNh2G{@v#U<4`2DB13z^%s?O4=Gf=8aqV`i(IBb zwB~4d^iAixi{Mi5Dwg2~KMBM`BZ;<3c@_S_Nd;J&b1yaWP5r7Tw=o4>H@-vk-uhI{ zBg|kLYtdHURR?|q{#b`${pzppRaZf}w%;2-eVky8){;GfsSIoaa8pFg%bXx|8Y&0D zG)O}uMl6(|;+@`d)`X0gBauS|i~i&e(r9^w@h2l#t+Q)6*RtpVG(%~?~#>Du=;xJ(x3c}$4BbUQp zgI8m6vAOXJ(E3(E)h{Jy6>C>@i55+aI5J>g*@wIh4nUtsx<644VbiJr1>6bSiHudW z0t_c`UBS2Ig0s6Xfxt$B`tWw9ZREsl&Ya-sfIze_?Ofo2_uh4&X9KH8q*D@auXTF_HNbZH~4vOMf*ml-L( z8$rGDi?_q3O`Q{-*p8Dkrk<>SNXXrDFVKBzW;RZKrgWe09PW>*XBGzq6I7f3PT6}L zC0HFDmN0BP&o8w*^CLF`{|=m{+mn@kdhWN$wMmQ0&E* z=;I8`MR~0=T`3_YEn(?j3wl+J_G?~Dbg)_gW;qO0+W1HHgazQuk)X-7`esyxw8a4N zpC)bT(a~6Y3pm#+yaP)%5l1;F5vensOk5#2ceR@#eF?na{Ao(rYB7u0yqH6s-SCD& zusVnDr8isS(J;v_OO6?|x$p;~9}zD*Ou$Kx&5+>ef9L6KAS{;tyE9!#+l1aW2Psfy z)Ag?7H&L8A+`7yD+{+lMceOF}+UA)7y_4Xp9W`@$JqWE`?21~2@H{j4))Nes;iiyI zQhRQUmBMq#+yBo8_^iebl?tfAzK%{J6R8$uNl~W{Sj#*zw@-5=}|&j*Nyc`diuGidzoKpCiFN6Cka1| z-TRU)gC#_~boM~;xWaw(HQKDqDhC^Vl`$1sY}|a)x$$=_WRd*Ix6?e^yrM)2`}dCq z4`{J=Pi4HIld{P4Pyq=s`5rZ{RcpZ!j0^gB`9 z734dG?BG!gW=Ub2Q|}AVM_2%cwpJfmVs-s~j^*&T@AiRM0N3w<8i&(;Z;pg6l!F~Tg0*~^!i0V8CGM7URXUrnM} z8b|mCE@BfJqFPxYW|Dm6>>O|$r8-RGi2ulz#cdwcq`bGDXyg+@i2}7t3M@D(awaI{ z+1UlG!CcFpa+%aiP9|!ZXgIVf{ZfdWY$1fSUT^LrYZacYuB9OEnR-1EzL zZ%^V95=!emob7@qyRwqZY7#{u;tu!o0O!JSH!!&j654W=(l!$LCT)p^o)ej0_%HPX zV^#~GpY?K971Y3z+66Sg>Dyt!@9#?kJ>be9WXYXe*^VQs=xSTthZDT8VZc2K;dUc! zD^DdS@~I)Sbv51hBV58TDx-JnbrYCCWWnzP)0J)liaI3Feedq(sQBc+e|LZf+<%aE z6^E=#@G1_jg7TRdv>GE2nruAFE2GY;bhDmv$}>xIZA`A3(PnCCIt1d)8)wy15v&m4ikd(7co>sy>=Ny@4S z*0q7_<+2Nhp1M`mnmUr4&CP=_%w3ezw6S-;my4w#J~Py!8@wmP0LK{-5kQK)GXSrM zb`XX}%b)q!F!L`p{+%%eej1xL5X~+>>-)!d(IL0)-U<|#s-&r9iVAafggng9HsWWU z=K$EagFpd%deURZKRS(=w%okL)aVssvtnskWU#Ik>s1%UjH5D?02b)TPlk|m0PY3$ z^(lPYtw~5vH`+c$5J4#ZW?)>R-^Ww5mTo^eU4Aj9?~DP$pj_bncr1dMa&($~L=d47 zNTI$h?$0^!Q_Ez4)K>bau)u~n2jPnl_&3PplZ~fF2Z#v6T-=PWfSv z;<71$j!|hp+tjm2jv2gui=e*Lw`9VW*RJtc3tm$S&}RghuzUTnMO5fWs0Yd7F^mg` z5E3)*_7#LHx#5-YngJ2eO&fuX_dqX#zc>Haw@Wy8NW{a6``OiB(s6mZegWbpdh@_K z;sXAS(#Q`WG=EZAtXIX{hpR9I$L9l!vmQd@5oA7(aXHYKn6N!ebL)%I#$hn}`p6`; zhGVC#;r;yZmO67ViFLm&z==yET!Bz%ndDQqvQgq;?Pbj0wX;j2N#kZQg`jIJl53v@0 z^xR;I#fcM7GBkOK+R>rxWWZZ1;^+DQv;gi73A7U23q5uyl zI-Mz2E|||*N*ly|e43ax4?9*mkP@}45JlDl#4?=`(X1a|ylM_*Xb z*9Vb5cI+gm2g;Cnu&iog%Vq6HO9Au$+%dY0#(pjwC0n-DCjKg@_6vRxBxYWQiwz?yWU7tC zW8g!_2W2*DoiQ9&*kPW2I4VA~e#5*)gkNp-UTwHQlV!$p^C7r;ev>rjfY!QhJvZfo zew025r$O6{r$U85c2pDnEvZv*TMJw>BM06y1&-d{UgTN{xYWZ%@#(!2rN)!tFeJqW zZZnC4ombuE6Mwub{;%7_QWPLE980#m+XZ(wtqteg6Guf7gAtc5{RVJGEqHcM|4iAd$|Vs?%$s5pJ{SbS zh8~OW7YIXr51+YsNNm!9VipjvpXxkdveK8N`tNfl;u1m#NLU$?gEnCcw*X{aGu8*V zeO$QVE?no%HJD%o5vE8$0E|)2D}D1jUw19$zWKq*^>xygB?k{t7r_6|ah|y;2>Qzf zpjge|&vUhZ0U(RW|Ky&)#ug>=hp74ApOmDRx$)0I&w<$HAl`~o3{Pr6lsNuei)J1p z{id26B8nvGzq1R^L<-O~tCA| zjnKJIwmn-1haN2{lKa>Lo~qw&7@>L4`7XGN&i?5i z9bFMuf5MipGqyhLY%lVi$^UJK$>MdqBQk%DMtUCpp7kgd{!m|w$3;-C|!#+O%e1p))d&o9wdVa3Fm(3KW zI+#41fbD@hTe^X31ZrRuhwF*yF|TVrW?_6LoKyG@r~{CTXTY`*0MzvCQC;Y5z{4Rr zL&nh1@Y8IjlxWfI&!3SHPAHL0zi`SBUoI4{rpq%ML@iVpnlJmFupKN6?(>0!<2CjV z>Ps~1-{)E(R%kfh#gn!bp|n3i3oZKibP0DPMUJVX`eiehU^-@kf5t6|XH73CM-v}#b~Zn8Oc1ey(Os*&@_ii-hNdIlP`NGns%!URssNx`xQ8qNBNW0m&oSZvUY9tDn3}CZ17YFcz&SVl$B#RX^?2=DAjHjb ztb|;S3C8F_NQ-NSX$=Wwgi}!blP={kOSVwiE*NAREGmI^? zJM0A%t8j>ggvol&Mer1|YD{9az=k1B;SqqCFZSIPPl@H8lJX^M6mC3~#V2FCa=&Zd$)l4{O22L*S#AU2J|NCl z2O)y=A5nHnW_CWfwalQ<#(ug!0rH1~AlLz`g;w0uYtkx@MHXEnr}= z`09UqX^z1cc~@W`QsA_O4iEoSN_$3ZXyp+%l1cz7HWyIS0R-~Qt}YM>3&>MW*Xt^Q zV)UP?+7>Np9d1%`2aoSWjeYv50;!!;>5Z3|5B(^=+@e&KL-AZ;C^pM!RB{-)eVfs? ze{$Om-aWh+Vul*w88k3NcPg~H>KJ4 zdd?1wQ{nF9OH{kfTesO7m}97uy4h&rBBXfJ&p34oZOJa)q6XlgJUCZ7?HLPJuiVkM zzDl$E6Tv;oJCA36{YG}R@8n4I_fqsT7Fo9efkUAeJ*PXRl zH8S1%Y765EFQiMyMj*$fhoH?1uHj9Cexsq-_)qWsXT#-RW!&XQgb%+)C)9P1tsjBF z_pMw4Grk8I6f;1w?G}DK?r#*CAT&}5 zmn^c*w@Pjsv4&7eL}~(e02+JV?PxnkxY*r{|1LckL&p-^^}*axyE}8cC+O3?G$~WnE9K^u6Sx zA#uP>N+}TX>f2dkj3Ol2v0S4&mw(wnkL!DXRf4j%RQNdFpmwtC$+$QrBinTSL}|_) zl(=pQP#=%iI?}*{1_3l|bBMVL4*4tFqTU3SE`*kxZ5D;QvXZ3bZ_&R8eo>>;uu+GC z7z#x%9#*C8!iSamXAgs;6Si;0(yG=unS-fMYspckSPPkhcx}hu6cnJKps+(C4P4O) z+9G7ghbv&fP8R#r%yHUK%dn`(+EH>I-X|nS5Er;{rtY4m!kr)^_xiT>glST| z9j6>{C@6N2eh49IpT;)HN6*j>EXs9Qkx*!Ov_cKO-2J*bKe@ehtP|PNJYY~mJ8}W8lAXiipx>UiMV@w?sNmqLQ5B&4OM5@X*#tE_Kyak{j<3QtKPMQ%p` zk(rb#9wvpb3~Y=7uXe~nGm-29y(;U3(thCXRW$LV`mDd{-nwn3>n6x1PiX+^H%RO3 zb0fEubk7^rPtRy;Q*y4;z_@)A(vR{03|4BM1vU=|T=A-jAJ?+FWnhYs{O}2@1m(&? z17jfPvQbUk<{OV0MLT)Z?RF~MyyO#gj3?y*{3uviC~_9tl1ztb-MO+K{LE-$cd{5` z!`pQgYgYsf+5Dc#{Q04tp>dZ&B3z1yPYG4~MoSvIThTu~PqFWllw$>&@GYQnE7E|* z7!U{~&I!)l;)y=sFwkvog8k^5^}~i&^_h4g+>f9*1l}Wbf>Zf>8gHMtbYy+)YNJq6 zF5n{_Gji1+^e@0w!wpOGfJcOU1a!rbad>NB@}%Jzbya(pYG|T+V-ohJu%#gshXoM7 zp%aU{yYmaCMS}8!|G=QG)uLkm-``nhFfHGRb4gK<0{W&C0?yfq(DfTV697{fhWdG< ztLoLRA#gujtOQ}bOyxo>vOf#8qX3SX-`V+S?`2Vu1dDe(xGsglw8R`7*a19P*e5_r zm8z=h$>}M+4i%?OiO=~qU*3=U%(_0Km4L50+#>cJ6t+0U-^X zMvnvwM|&W&89Lbm*!ZM0HB0pCTERh@^!T+PSndYsJbCPXe*nUb%WMzHFGJ(`#fsf$ zQL)^(-@Q5x2+AwrzKTw|kNIbuxVqzk$kOp#s zY^w_pyIlZHtwj2okB<-VOw;?vvHJPk9u1l~+1Ui2TXliH0!bwW5GTEO-$SElW#M&zanh$eF zz_bANforX8ZK|3HoaQ}y5Rkvj;v*?7T>)_f`u=K4=(lfz1PHlsuePqvaebH$nNWcV zx<_XE{u>~0{lbL)K@y>!DDO2){pM-UuN-HZX!#(XSu)9UU`Ft^uY69aC-)TBGjKW1xrvN+>CU zfOJVnD&5UX3sREOol1y+pmcY?bT>#ziGY+e2uLH1q<8J_``sA#jNu>0A@H*I^Q@R_ z&bcD|!2}$ZUEo=*WX%3|E)1?J ztg?S}7Za;S*00$s-Oa(^_PaDXP8%H7rt3faVsVv~3F;l!MBwgh@g|gd2=v2e07#SX z%|}vBvoFZ1*4W(GeVpgGFJ)o~;cTg-*=C~|U|9Qq%FuAYA^1Hw2pzPiXJ!zsU!GE# zVR#t7(>ek4gUcl?m*s)Y(n<}^Z`Q|4{^YqHL)UX(`}6RcBY>PK0Lb0%5nYC|0_iA$ zdO4#q7fSvt)MkV83 z8JSCDX1cR!cN77=yD>~;1YiXk1{NzAghAC87#ByBJLloyhH!s?Zn}s$L<)=SnYAnF zl#+VT=vrVMwRz~6JdFEQ}DvlO8=fhz0!trRaiMrXvTX>AT5en zCkC>ZRp&5Ok!9d z0vU-Z>dOHHK4kO!S)I&LuR`=P!VvNXy9d zI)v#aVD9Yg86q2w;D-=}O0ekGe%V=Qp8(^2?nG?q2s#v+0CJ^s^FA8Qpbz~1Er-5l z3BmwiK1D#2{<|rRurnOLZ`mS}8j3)~USf4P7Z>6-2nGzwH0cU6Gnx7juOeu<_=A*T znv8GkUcU=Cs2av)B`E$rEo8WvPNr%lu0eQ)u+yKeAhNV^PhUh-FiGVNQ~*y1rJ z#d=K^15MF2cO1^ZSg|CP70Q-us15>f?o~pS1DXKf#_zt~BLiNWftcLGgu5L&^24H;g> z$>m%f;9+42gA_h}KaLao-`oF?u?51tJ#ZHUG}0J?A^<%p@SyL<5$FzcLRgIrG);>6 z^QpPHx$$hf0kRxkWKRbG)c<2r1slJ~SFG5GR>w<^jeVeA5DkZ{3@F)5_isltpfU`v7(~NgLJ9Hu_hoNr1|4EF6()q` z&HVG00WbeDi7ifGCO9wc9%i41hOhc9CstMe068_X7Ivohk zx?POHB3pb;ny~y9pua2gU>1x-be280IU~P z%LZh&!$T5DSw|SGq(YkNx}MKTmNA5gs(v8 zhSezO;n$iG)No!p01KjfnIqR+pE_z`aJW7Sr*n3<0j>$VVFxBSS!1zt6iNBmIXD||7mY?RF8AZ@eu%_L4&3H%siEN8#vK2_+0RnBOz&$vv3)5#{H*%-$C{)67`6!%6#fAL z0A4{=@;j7gb{;xjx8k0pmZSasMHs%#g9(;xd2Xkn0hEIZb*;u)Ia{iVppi1$Ed{MT>KYTG46@qdAtx>Z5vwN~oz4>BW2M+aWrP}~rJrZ25 zUcDN=_mk2zNFShjJF9s@aRUJQKq~ia&Ql(Pl6UJHO=D9NG59$_nZ>Hs0H+@OR0e^N zNJT}Zi^UX?1A(5buG!Pm6B7%|o;k_vXaSmS4(JDS8x1hR2`9M$jrY9J&Kc&Ve##Nb*o_ zp3tU@bB5;S(X9jrs_4mt`M17GgPEMwbZt1~GdywSTDN^#=;Tb{7%RBVI`WYN3V@uL zlSu}8`XFRO%(yl;}>S<#?EUB9C&5x7QcF zP&)|l;C1}FI^_3yrHzKgH!ZdoC=^m)z*4(+yiY&r#H2lQo_2*t*AdEr>u*c*Kh?|& zCEXOChZ3LeJKx1Df9hU&F?n}sQfa(UD-u|v&+IYHU)ZZ~K-gS@@YP2X4Cu;e3M)O` z-Pmn%JWZz8h$aSx>Wj~2`}_ODCA+uXPzSd14GLNCv`zZnBUTqs-zd>37Q-Rmg=ESB zm=46HeF72Ac_0))lz{nYwoEkDqeowsO$o)*<<>Y5?J<})6KF@NqM~8k6^>Y{LD5%F z7Q&+XwpVc#8vi48=da zFUy_A9Pz7TMWl$&A@~XbK7mXFpxmPascwp4A{vdn*op}`K**>G~0f; z7kIdQ`U9LO6!JY`%%{r2uGXu227Vwt8rOSeG*eNnN)gnMU0lKM1}-f*kenP2_J4)2 z@-e*nZD(T2|K^q=t0I!oTf8rl{87*zL&1c23X1|Ro{3U~60;Z4F>IQ39v@RN0UxVj>?Mu=52gBqQW&MLhzhjVhq{PLD4;0}mo z6qJ2*gYER-2@T&0bT}T8PG5Y*?uv(#r&~ZJg5)gZ(sjl57`ILJB?6yn7u1}H{O&5_ zzR3eka5j>Jra{-8!z3~8D!yT2|7$}-!~b>;_l+PqL-`&6f&R+pl$7tlu|hUxK}7xT zl~2OX&JOa@<*7h#B5m6Ln+u?jD;iJ_@hG_X6U@l}q8B7n6&&aAL2Pjb;lulPH{~pbOay*{PAxGBc z3)p*x{5xNH4&__>P|ED9G$64e4ay*82`)n~M7bxopDqm~r7GTfjAYsQcdz886524u zJNx_fxV92)9%=kge>Z<6!_i!`;zn;Yel^{TBus4VWnfL}0{ZHJ&v=i*Cj^)ZJCNA$ z$jCPTHZt{%6u(S_gN(ea@R)7wbLa+;(Y;!p(mSXOsiY$^OQ4p`q{D(fT3EG8?wy!O z1T}%UaLKq48`01j0pw*&%$-~y#WDSJK=zD z_hRjRgdRP53r4F;^pB(>I^fjq92^(}Yygw#D9=F349+lDpC}e={*VCCFA`Q$ zZcSF~y%Lv5>4e4z+3yBHA343yFhR0L@IffX!lR?}h z?7V`_Wr#&wX(=0l_yWXHIfw#K{7ngFVlffC>YAELgleGn&XAzbr2qsp2UpiCQsr`* zem7wBQaZSCDlG8>iyt4CE&HqIlaEMW>wD98^_U;)F+r@pPk?F4J?FauwdXt6U>sN~ zN;$|G3bj0v|AFD+$S++%J@km_V>F|NN{#i5ZsHMCreLbYCTaHaduRjogkTW>Hs2x2 zNCW(#nhG@VBmuXGot>BGmuKGKB8|Z1K K5F9yNQ0xPkyK{8JV*4~XBI19-Iozo4 z0LS_FzwYDFa@8{qkPUq(NFuHbkw32g^?|!rdQ(9;auPvUkKnFwBQoc&|B44ZIT>J$ zgu%f26ft9IPraqX_g+oWROpJ5uNak+n@gc zHAaT^FpT0Ah!zkOJOS>78RX^ku_<`CNCON7w@dt>E)}DGbB8FdUC*-I>fx(Ylrs>!`mO=YM`5< z{(x1_7|sJ2C=L9yLK`Ka&}s&dy*OdB%m?*=7nmBzAEf4XRl3_YO1>qwP%-doRmtFX zt6!Hd#?E@dBkot?wI*iBX9Y<0E+OIa&kud&azI7|;a>h@VMpgO${gtiP5m4$NclpJ#fulx65kORH<1WW*c^}Gzw=zjtCKO_a*I4e@UdVYQZw)Vo| zj0Gmiq96%}JdBj^05-g;n4s%b*8r@`<+S6`m*d0Nsd#lS%q@e!tci z5EEfLPR`>2E#Yct3J2f`-~eXqR9@El>J@-CkQQj?*ebwi4WZV-yX1u?C;e+a9oB%0 zXG-1vZi8kLv3u1Z2Y# zJXyi0xqNUDC?$wghxPUYmL(u)7T>;QT#Q$!gOW*OXTaOC>TI6cCmjlC4BU(Nel z;p$aC-@gz~kllp-W~iSY0e6eBB>ulI!m#X%$Dh_ht+xBVHv{b!=aXH;^a>22971fv zO0dbKbgh`QF3HQ8_PZZ{Dut(EF8yRHFj|`*)Bf%}h++vft*I9|j z$y8DItUoS?NG-$l|7}QH{e15h%e>8^CgS~(vLX5CZw)lk-x6=?>9yVO6oWDv6m$T< zt*X6Kj4+0NxD9G}5eN(pZ$26s8X~?HfI$M0m|3w^EP`5+P7US<03v6oWXq_b)q=i! zc@g;8q)Ho_Yov`n*)xGbdadIc9-^IQXAcL+1`_6QC6)iR`?P}$HLoD|%0b=b2BwBk zp5D3P*LqLn0X}{^OaX#Eel&)P3kFS@bTX7;l~zVQrkdqUMjY`7Qv+J^beIi=*_I=x z9N@z}=mjHVV$=&uy98Z_s3fJBlz$q32SDZb3H?>XmSS9BT=RU@tvF&s0gvqwQwL zhIdLtsS7K7-pvaOV`|WJgpv-HP*_dTT2cNz-dl=1+W9HskGi!d2`}~JHz^=-a#ox2 zPu^uhJ&H{JprQFaIh~u6Q*JfEfk3msFBB4a=XIPM{qZFVCF!aa^W{+=M+7ym2xi4; zJw9%Zp^eJZuJ{=tt)8VTU4OC2bcURs-VDxowi!+%1c5R)^-olx5E|MPh6kzj3%i!S%J-KT-Xy9nI1*N|7Hp*THy zFCT-6bqApq0Rfvi_GZwi1&x3}5(rZ9Aze`WF3qmb4Hx|;KX#R5Ub3@Qo@63UoUzU> z-J1EHOHG*`-Q5_ybZ+`UDTMY()F2(!gj@_!;xfvNJMrZQ_W(;dWdcA3kU|istKVpZ z0Q!ME*#-e&i+6x?bf~bQfgj`uhS1`QR5H+(v;EK11ZH0^)9g(&%dYO_TT$OVe@w4G z+gu%BhRUD|7J<;GS#3oOC%M}`$0Zzn4{i^9zj*F9B!Jc^bgen^fgGR*T7KjaTU%SR zv=?tT)%dDkWtO6uZBp-`&BCh`H8^W7!k9@nqnSxQ830d`dfbodwd}GkKjnV!qS27; zf!GgYP{M-I^)^|>z5%PZP3tDm8X&TK;GOr=2x&3hk73dhfj+a=bvI*Xn4v-o*Tuti z9!PfnW==Q%pO$Uj8lHZE20)&r0rPY3TAmkbo9IvZM$9Gka6oj2Qi(8%QDc^#lLg`h z2nk|gR)z$UmC$NxWzaRi4{3w6M6XdiJ=34=Fo2bA9lJIq)uaJ zLqPk#aKWS65b%h2P$D4U&wTx#m4LveWu3XIK}bWLaHL6!*O3;2IINEqZA5MK7?vRj zFCsIBiiqXY10V=MSU}9~R$)5~fN%lDk2I<6(x`RAgY!%X*6~>mGtR<^B^~S%wCxl& z!%kU_2&8CcGS!n_-zJN6UZL&Yw}tNku^qT%TL`fYz*jyWiQ#A7=g(j?VZ7jrp~-Y# zvZb6#6}SWHMcU%<-2HP=($fzDkdT1P+O|0q1+Fk^VDV#KP88~z>-YJ3w850*8^~g1 z;msvCashw?q{&QxKs`DCJO%{7o?Hj{cHj*RpXUegZG)Sw&m*fWw=6Yn2R>5=F`&@U zX1GJ0y-f|*MM77XY4Pi)O#K#bD787pcL5GWq)*!j;KP`V)($!bjbM8iykzg+2E9&39Cnjg1wlav?EFD$fz_~9#zoD`5zp6voX zIU!*u)O5srjzk*8x^`$XGl)-+Cn5Dg_3HkMRz0FmRgI8|NExMse0E%{4D|erH_zC7 zAM3($Yyg}WGNFHD^)5BBeE8E3I5;>o0q?L0bC2}@dp)pO~;5HgpDM$ zE=&UzjkLVbqwho04qlZDBNAmikpx_9TP!w$G=VtEm44LNm5mZ{bG7@YI%9tvE@v+H zn@V;x%W21gy^<_FrpNcgsiBqoB(R4GjW{I})1736l|7P4uz!wxBLC|k{wZ|aUA$6w za75V*eu=%uCSO6F7xDNNT7x+sg6(qu z+QZpg$@=Tb7Nax(Lj2FB@lNYhej7*!{>$> z{ViPF6#&8>adM92Ckx=eiv6G-5v{y7D`1=L;i;RhUAs7~JSiv?+aSCxcB)d`@;mvA zsU^ct1?)H@sO4z!VM>BDcS!9DaKQI$vuHqmeEyy3BM27Eq@k9=8DfzA3$EU=2w?=u zExe4;8BZrXTGzWN_JgNGG6z0J?!??b>NP3`4kZoqm;#qJRVDD~I9>6&RQ`$)AhVMOm_#Gm5zFrd_ZRD+6YY*uj2DpvQ_WYN1)jfP~ z>-YC;t|C{8Xqmz5bqTTCDRm)J{xPYYNe-!mO`qTXwkLsSg>(NqC`9dkbxUv%4 zKrU|tK@6G6BW*9tgfm_@gaK3yGD8SGrGPELe;uJ~)-Bw(@CAsb8{_3h4jsR(UyzEL zEZ;~!b@(S!Qqbb(B};;U-UkIGybDft~FEkKBId_WbOuEV3*q>Wq_!$XU+NrH^gx%*9eictRpD*?CtJx;qB zCm%Z2{3j#sHkjc-CPq+C`}r?dbWJ|&?(Jp392R-|h)W&}e_*)&Ih#M#gMQ;*mWWTn zdDcfmdMXSC+TziShJO-|2?aLs$o*q{{!At(2UACXv;8B{?4=b0+?*&@{S>$&woBdj z06Y4lQhN27+a5JcyP%%rJ_n~~9JG;ZVIrwyl<&3EpT9H{ z&s@ zCIBMAeV7-Mp-{_FumgMbVFrLjZslyEm)}lKMxI}itzT~# z?$)Q`7?OyI%)B%5x!-ZDbGHyrG0uwe+WG!h3uelp?VmMUm zyuV^rLY*!u$;3fO!?+bqZOHYQIhuNXjTK*ngWiIEz58_r*EM=xYu;_zfbHm}GLx)( zM&Y`%=?`!3rlsV5tur1?_teY2{7UQPxh?b!j8M@Io1>E7p53HRWDXSWHs`jnL?_t?8Di#J6}_=GF(3>mTI zIy}aX6zCZDMX7uN@Jl0D_w}_|+VPAO$$LH1(0o$lX_T1ciiAF^-cmk-Q5H#)W z6;-TiVRIL9){$FTme~3hZdxCtDuN`AqO8~au!tPJrSwc*B(c^x7ivMej?zX);&;?@O^ zy(9J=;3+=9D-7E|;_9v}ToNs*7p`Qn{4$fEAeRtR@Mn{VASi6~!c6IL90%=Fb32m5 zbAn=;+2*m^9)cG-M8nlNt~+zMyJ&T0vk^@0H&;W%e~Q`qh!u<$q>J99)V>(zb_^DQ z;uopXXJ=>q0|T!;H~+ZvUpxM=AN% zxQ}ES#f#Om)P5nX@b~@tVp05*=;(9tjmWHcTj1n|t6&b8wGlXoM?AH;h8;y95$~Ow zVoy@>`%~zh>8v|c-{j-tl@nDHE)wHjNgD->V^!w}NtP#l5BZw>S2q8n+;-oi<07@# zl3(xQU5hxmPjJSu(7lOXBW(LuXc68G zzi-{nqpQ2N;sT}NuW#_U2=3Mt?@WvtF$unF!WRFv7)z=m>*JS_DzUux(Y?@IOUQ%v z^E(Q1W6461`2X+ho=e|^kc_u_2wlTQ?Cqvn?eO_ZL& zGCTSe8;MfUF4KiKE(HaOXf*xC%z9v2g>;;p1ji2>wv|3FrsvtB*LttV{hj+Dsaa=m zvua&X?2JU%>@{K2&)4!y_W*weXKMb_U{e?*lqjo=$s){;I)=KWi4}PvizH zZ-?!Bcv?Rhevp#a(Lw93cln1tjJhviI~U}_&0FJf7#q!|L$_+kaOL8o+Y_kt`K9Lm zWv|dDN#O|$rx6%UjizVhhDCASUUlr~lHA?>JuGl6_f!C1VbB95&=XGuLY5UxKYivu=H z@1Hjw@@F`55$|ZCJPAKL-S`+BPjS3$GJ9KW&eQVQ_FKN&g3vzzO04f=l<) z2b5^6S<`A8K$9P_DEt(${j+l%$Yq)4WN8rM^DH6f9x-tb2wS_Lk^@b2UV=M6MKv{L z9|v@Vz$l6UQ&6COFAGdnfL|cMJU}mcYBMXKqN-9lX;mjyC~&*hseH0{OcR;oywLAjR8mZ`rVL1Acuo}8@u?p>p!%A^8>5XE#)sJt ziglvbrxYBhMo*sNj^4^jki-k2vPxSeKJX* zqPmBcQPcMq$K0XaQXgOUns%K$)^2%e?0PV2qbo%7U}ZtM!l{%MKEXEg;(O8Fz(RJ6Le^eRA-$ctS}j5Y7j>b8rklU)l9sDY)ZYm9y;J4U{q8gS2OE#j0+m}= z!%teh`Rgo_A-Eys-plpjVso!a@>`~&Q4gu}lv@LJG`@2j^tn<+wxXq@n3unMG_?`! zi*VfQ%eeL2F?97eYW19L^L%LH?E9SRz~bmN-qPc{L-)y@(0|!-CB3Zm1X@3H@$g2| zrAsRnWZ-7uhw03@+#>Y#vG8pVyXdK<3BypWKUfq+W)>^QCud=iu{fO?K zcFp^eW?#Lg`BNtY+}4>~M{^I?R~4_@?*F?A9V|@S5p@OBs81my9&OHwzUOUE$(2Mb zU13lT8e}#4uhjqs$KdOx(9_mZw{;aC;V=wFdd)rw04WC8Iq zgCC`mBS)Oel@tkUX!e1;jw}fQ3#23ljMq)W2_Ge3I?@l!c$n8!bsL?yYIFbJT!2HD z!KePW9?FSCCz_#I^>wb*27hCw&Iq3{JT%gkV787}i#FkV-;!e_VqvL1*~pvQqdQ0- z^fZv|@r73J`oDP;%~#5w-9OC=#rBlfRX^6K%}b7o@!7sqEj8yL!Q(eiFvXCB?roLZhnd+I~nXoJ3D!H*LO9TY(f4}QGatc&aJ zM&0DKmF?==Vo;t{YHY}(+Hhm)2+^{7!+nr3HyPw)UU!o>ejqcD^d(m!Hg%@xA znD41xfwzgz$cPzt(qOH{*oP{#Zhz_SRyt{89ea+HTQk;hjKY(rjb6-+OtHib>zt{n z%0Y59_u{P#hK_E_&ALaRM@e@qL_8N4|Jr#gP}WB%QUR;e}ypys_4cavAY;&yyDy1*NZ_x<2!2&RbM97VPh>+iiUi z75@Fjc)r{O+5T9i zFuh2Y+1Nk&^i|{cHAO+pmjpJ&Ev+_XB;(8sL$9OfzE2w-oVM4-7Pnh|s!idM_hVe+ zE<|6L2=TRlJ#i!q`TDY@3-h|$k3wS43$CR_49dGvn9+<=;|X_*E}ft%)vh#ag^AQa zFNzVlKQQJgOCRS1cu2RwIrMne;}i6~Y+#^)Y_Bv;L`XEp(+&s|2AKf@x$^3@tI*&E z;3n5+O6EijD2nqQc>9kmquxk5FkhYNXL#$fSS8+2QO7jNUx|}B^Z{UW=zD)dzxfEj zd$?Km@bDsX&A{W?9I?v)NggrmDTUF21GU~j+bcGu97l6xGgZ0QnKQC8f>Qg|f{g9+ zAmtgmSR~O6*Z=0azi{3~TfwR6`8cGwx_pru;8GltGb8q|HeTQ#1L9VN`+${frR^yb6OxSnp zY)#GjI9hceywuNlxAD;xolIR%f}iVK2tznI#E( z06~&9bXp5ykdC-zNz) z`X->qdR@O$oc$%rXy(rY_EU{CRe4$G?11mMv5(pLg7q^FT&5M3ZyoXMDQQtSQ}DJn zlP%;X+|a?1aNwlv*=_jsQE}MHpMtN$B{q3{zUc2A>#T&`LynlAqA!rY7wV0%{O&bV z8{Rvr_VkKS%~?5`1Wm0xmZvl1v-zLwir|djF*=yH9K*Zhty=SU|s7ANOLoetCDC&|sE&t$5Dp zy054C&%4bhUoZ9F&080hm9*Tq+|&nh3qOoF0av=TRl^?HI1Iz`e}#Kzs8-K8|cdiwF=?cDR} zANCS=mUi0gC#GNCoy%(T5@QJeT1h3X!IzBV*ZLNOV4zJw)-(Wl5~Re6AmIT z&`|+ByF<C>m$cjuvZ(|vL2 z8kn;Grk-Ell3Nf6_=?iOVA1X??bm*-xgogPz!lzp+=eV)C~C2p{wl6=ydz zZzTtPeEi;hgf6qK?w3^umo@F{SM5!4q;8?Be-CuKOpSk*nlotp8qpmniMqp>)}H5Z zdDLm>aFHe3TY8T$QqIfArx80}yrM_z?2pRu(+^J{Tn_DSH1~Pln{TaPewb|(a;JD& zYW`a`k++b$-y4llDw;wTIh`xng0~`=$(qf?o$UI0OY_J=6fzv#6a}J5r|pOS#orFv zY)KGqkjU0@6Nu5_zV#fJoEd#m;jto|!F*$#uq>6oljo=3BdtZ(C}Pq&ifV58c8ex2 z4)H2K<|&d&i|>|$=y#nt8QmO896{<_@{1CleAQ%LTAXY;Z&u3>QS5`E%5Ts=N*i%$ zsjblOQ5Bz~tlQ;dlsV?I6;nU0*nSMH#=fTMzz}2%Gkb$a1|B^Dm;9mzr>?PdmY8tq81`H8CX8dQvc?_@coIg_*XNwh?p1&MCk}hR^VDZqNeVUc6EYnb64lUoE}<>6=2GW0{N|M z(*D+K7+b4k%RY7cL5-;2f#%fFFM_Gz+k$T{_3e9JkA(wQ0{tycb!#P*PuQ)TEk_O> z11pKV#q#fes?#Ea(XkK0jB*#BkzE@Q{MDtgqM~$&H88@`nfjzHsQ2?A!I=c_^HF^j zW8+5!bpG!JiL0YllFu(Q&BZA~sie)~P0T_pB7c2Our}J`d2({l^;AG_CY3h&TAp4e zgCb{9XRA@*-vOSQTI+C`LT1!3$G+Y21;JPk-pOcusWxit!Y=3&zgmX)8mXs?xMXCY zjB3J$)idJ(MYU<~U>!RN)vb#)toX$;>dhS&%|jtQ_gNqMJE>8(J$E{9;mrI;iYL(| z7g_qFQI+M(u2zMXNqcyKoKNG`^jXP?*t8wb%yQVnvb%_JR#S(T7ae^>D-=$;>*m?+ zA^5H%>KR_h-LGgno8l+$(q1sA`ws~Dg+De}lz=)ldH;NWXGrpBTHW_1&hzKb5rrI# zpunT!DG*kcKyQ?XUeC*V-BV~q0?;}bqrxUCCQ$U|T8>)_oR@9{Ta+`Y0F!uyUmf_NA7N>v!ps)nX*J%VqncI0*yQ>^gTXMl1c3AXOUfS+eA) zR|+mot#7Xb7frj$suN~93IM&*y{vb9XBeGhPA2Fc1)R8Y@T&u1&-&lQ&R=9s>9J0K zZ~jGUX|VH@0cKn+FHC=TTsm*){vcq{y3n#Y?0S-U;|(_ZNA9BBkZdMWt68x{i8y=w z&fk%JtnKnD`31VvU)2-DsAV`#dD;v2T0}MChR}G}UY|Z|bAPZ%k!HGRe~%h|C%@d$ zZ%m@TpV8PLp{uf8WO9*k<$A=oT}CXk&(^B!B-95g+x({TKF3$_&F5IDrQ60uZRQ@W z{7o!L3=3{enSMqgnIX=YAir(Al}GX*f?4;&hu5R&X6FZ3147zi|13!*V)1n}ZNI4* zzVY(niZ@FPdyl$P_zasjd0;w}w&)g|IJ1|6Zv`80 z9cBz4#+I1Q(YFkm;oaZ^Lk)tCw^L^j zbE+8hLB6vHC$4Kp7gxB!NSJBkuDJ4NLJWBna7TgP8v?96C7^-A#Qrux!Iu=P3&brB zej2hl7tSl%!YpB5#~@}NS~e7LO64K$f%X!g$Il0os%!L`40ex*Qgy?}8y$};`tZ7$ z9shRUx3<8#$rS3^;rh1PKEWZwF)i|vBz;e-jd@gx&au*A;g3}Vj^3g#&D(8x10g=n?<(GQrl0{tpDx3 zZpyDa{OG%t(b+EPw2IT;+NrIL;>i40QF~221UW^dH9saiSi-v6*N3asa7erHW)d~h zQc}N_+#8oJT^0li0efnetGz-%YR==Po#I_2H|y{r-C(PTVOnB&y#r<=?^44`13?CoYe_lj_FdtJAkofeT*TLV6#xt)#*2H|N5Wee1!~tPH&k=S(1%B3v&-ex)lH*%O&`WG4(@NE zXv^jg-bM-EAjV)%zl(=y{D`dE@%P1fSOasoq(NeX56G;~j&-t^4FI^Vt*eO>E z!u!H2YNIRIIz07k|e36{mYTbPWg1GzBO zTx&kt9$wbaO$RZG836kc79Q1JZZj*+Q{(0}GtP4DtWU}J-q47@piCK`g?v^;1_vh(o0KToi96MrAolU6p*<6-`4#|&N%zYT3Hu*gqZXD_R3R45H6XHT-mMi7qax6crm&F^*hhwK4TfEzY%hyz#R=BP?+KFVR=(#EdiXNF? zewyh#9cPqWAL{9pIX_b-$9l%KZC`RZKv#0Kq_zO@g+{5T9J1juG*VT4>}TRydSBOj2PSG zD>PtBh4usb#u2-)clTf>-1<ripIGj(mKgtF z&Gi|&lI4~?X}M&&R?O_IqmpV4?vP@$frZO9t(Ts`SpK3<(O(hdw|qJmvKqa7c3}4| zJ7PX{8`I1fi&wySx) zihOwbg>sCzgLf$1?_fVw_GP`)r{@ZSgN};NnP*&5mv)tIk1$eF3WIBDHt=c@w8eOQyd1_*dI@1^Z0jsqR!B0jy0$9)4AbkXDyJD4C7VJ4v$Mq3uXo^OG zeRFv6$44J_(8A_U#B>&xeNZj0tn{rp_Ac1c5#8}F!f4~&)*^hu>RCN}x5(WdJ$(F) z&qd(jC#F6XHlnEUjZttnddK^arR-#HP`=6 z6dnx|N#CQP`|*ORoM}YED7k@)dCjIgD(_8exHj1~`i1DNV49|ig|uywA^mN9kIIAF z$K$h+*$-k?@^d)S_$6`2{uE1nHjq{hsQmY;IYm3JfajRss3p~9g?za2@XT=_kE!Y4 zv8Xa5y4*;za%r(BA=VUa^IkTS_0Vja#Unb%mO4Xf#LU@?hU?U`ubco&lCa2>>n@}A zz%i37MxC2GYf}1z#;1{k#0X=O_aX~eMRVoKE$3%;(aD=L!_x_#UM(%?+DeH(KI&Oh ztfcw5H?$d^D;Jh$Ty6S$tUL4k60bkSzHIx`dqPa&J!(FbU4|wr%Va*+6Uoka9MvG{ zup7Cr-nS3Ywy0k&FwFcrr4{66SDj0Ata?3TCG%blrCJtX@{rj@%wpGrX{ll!6xl(?T^^X7Y ztk;v3(7%)}&Qxu)v~e4or9UZt8;RQJTK`TeCtg&F6GO0GMWaKg&?5G{4xPxdrj~Y& zXIgPoGUV_xf@&eJE%c5E`uHye!{F=GhFORD{3$*a^~~JZ?GYm$oS{^2YSal$`#rVG z)~ka5#&BkwH_Wh`s9z?lKm1taSXWXiqJtxO(4V5*RGY8Dr9N{zNtA}meg3fD-SgL@ z3*$*w!6jUJ!)MFf7n6-1vEEI;@`6b=8tzwBni;+Odff8^*M>Y-skcGB*u?LJ# zo=5^2{v&KR0Jq^1KtnV^Xg>&&r)5Vzqs+3l1>49|H*Q9bauV;O2@~xxtxe6oEOXNX ztTK5X7m7Q)$zhRQy()AB>R@2S^=6XurN>rPZ+Ev6Sk05_UMUrVeJ)Tzqm&y zDjmPrbh>}3HR-Vwi#9(Um8FcEXHpn@f=mW30W60qOBzt zVX{0_=u$ay%SRvgNbX^C`|fEe5Naxr3-*_$rp(|Sw){J0c=K<`Yo?`5%`0Ss`29%x zqLTUmj*Kkl!9Y3I8`e*Gl_{g`WI5@r`43H86n_Nu%DiE~$!B{to+X(fDy=Sdc77yp zHg`SOv(>h#U?jP?IaF}YfTW(fHJ!>wGg|*PP7;pj#Y_&?`Mne7e2sCjri)i$v--`N zipBB~JVW{6O?-I`j2YyCj8z3~DyV~RmMU{@$}5-M26Rt5Gq=Bg*z+_X$I`~qj+o*c zVH`^DtAA3U5g{t+NjRxZ@}qS=)@Sy{QHzwK_fz5He|5D(O8N99D8?ZJvlqdV8t##V zIBJ%|1}#UUnz;vo7JqqC(j{~5jLlpVO*lG#_@*S!=RnD!dbo3OFs>43eJ$8xC7fYP z=-y~h-jXvB$!BA_5)Svt3t4*-lDLR8gO~V@g&8V|G9r9rIaCUEL~*X2T0I1KC*u zn}IKg`iemK#7IMvJuy{MSg59@6%YQ;RlvJ~s3XVkys2hl5+FAm)wQV1Nums^GM#A5 zg7VKzySJRXa%?21?#1WigS2H8mPe>5D1I*L{>x@cFEC742byz8`<|H3YmXq1K z=2?|lWRXh*dos4_x9c~+e3T8F?3og<*!eOq@`{Vq;pxD>ALL7sm<)>+P}NDcyvA=l+;zLc+`}i|;f#a#Y?yP|_UG_9Q(y3C_nIRbynyPLV{|y}m!z zSjoa-oj6b|Iy!o?!KDP;oQpwwWIo&Ic9=rp^oMcN99k$foIp_!v5&yIo^)`I&$(KS ze_ccVg{{-2d_!@|!;`NxGMr%>YyY$g#*eB8(o+N*s8@X`hzf9HS~B&E6_+}miP8rt z^k1|RVgHH98aVRIAinL%@QLls)jZr*6eCKLZpNpj(v!E!Y{0rv`E-#YTgWR9<9h*k zk1?>aDuz5@9fLguj^Oy?CXk{^QaFC&aRg<}RLU>HAh8kfp#`u=0sQJiL5`C3?c2Af zB;hDVGMs|?dTq!KXf#?ySveeb78JtHTJQ~1Pvr3-@vu@+iI|z53jQh)*4cN^o@1gG z`W)@7otpJ?M5vt&rK(Eq@d;$+%5a{5f?o{hGv8*;TSh@a9YmWW;5}TC;H;snoIX9R zyEWUS1A4q6kP=s!4m^y{5oO6$efKJ@^}@MMx6H_X&gEqcYr3zX7)4$ph#4|K>SPHS)4opk$dF~> zlvJ4HJ!L!R5}SO0$dM7PVoD*n>w-(SEHN?hI%u6j4wxbMq2cby%ZI{VU`H4PU|hYj z^|MSAIAR|#^gy0gvM&Iq>8RGkGh4ExX;ArZ0on7^W;g5)SLE&QZP{kbJJXqa3aRK?x19*mR87p z%AHV0*lUmgF?4+>GY|mh9I%Hl11*gyp>f9HW8~p6{`bNIu2wHfU8d!t=-}Ja zkqkk%1Ld`F$t)c5Jd9spHl<`>@XmSrTn;>us=>4VQD3>?a5D*x=jNqmm9S(HFi9=J zNxuHzhb;8u|5tlg{txx`#*d}0LZ~iX$rf6eQcS6c!nI@Drgd zwPwazWEsj@S2rQb7&W%2Xv_$ao#Fd@ru)PF6TUxuUoSt5*V)hWoacEy=bZQZ)L!sM5D_>@KL*Tr)9;Bw?_c6RmzS?*hT>4t0i1XW&oOp^EHoh%1TOPt>wZ8(j~g6 zzE{<0uR!i29KaiLi;AmFfh9W1ywp!ihBMI60Q;+otY$ldO&d2RrKXZCq?4DpAwNq> z7Ut(0I|onR0_<2Wux7lBDIlfpH@A&9rhLre3~9FabnV_@o=O2^B|A#B*a~Dh*5Mr{ zRGfNqa6NiU1hdQcJZqX$M86?sb7yu?EHrNXsgZAIjl^VI870SP>?H^5E>P%F0cdQR z0G}{mA|6q!SNuU+P0kL>iv8a|uUNSy6AeEpgo!t5Hr26KE z-?{PIjXc0za=Od&Y;0}2!00Kfs1#xpQE^4t9Tv_5EN1k~Rgqecs@^2e@J1JigA3rT zXc$XKgEK7NyKh*XV6-ZLryj2Q7qhH;!?A_QJ-%;x*}Oy1=SQ}`uZKbQ3CqqkG`$*d4LR_fa&l7A zPF-DR*vIRF%QY3C$4qy**rx3fY%BH67rm7m*G7o3TjR}~)1!8F=iE;1Dfa+I#!~rF zGs7HfEb2R&IZ+YouO&03bmg~KfcSZAWCAcW6F1-1iOX6;zBR7R2@NmhmKFhej}M;r z9v&LX4_#e>XI){579D=9CD^X;@7~^fvqNWrqUpAQ<{lmLO@J-c5O+8*SMu3=14Rii zXn+hfgNf;B2yO87^P2&NJrCFr^p_p=)`BSs$oKPQ*!Veci{@Su$|5wHH`Ee5Or6Fe z9}{psOI}-M-BlaamGq--=0yEE8dF^jSU$YVUxK2t+Na=r*%n}F$q5M$7z{@FhM}+v zndO2Gc}CuAko#Vt;+`j|((DD+7!rKclxS%xrY*m3Y^{5xo_tnIiz!4M1rHt^2PVu8 zSj_$aUf^wpR$jbqu9IX@%w%~p1bU0f9^Q; zKBvPyJ^oy{i#t460NO3-+G{eC*~QNr6Tc4ku6Z93N(i=4D*IVozb32#`o{{A$jU0Xyz z|92l)uPPl~`1r9E9*>W%Cwl5%j%e%{`3&nGc*tzs$V3Q$fS60E`?(XWLx`?B+1g{H ze2*la|BS%9&am_7(`*}}EG=?}z*^&ydXEx$A*6T2&4wSLumDbrD)sL>)S{AH$;nTg zBC6pXp+^X5-3I4`vUF;OHj-9?Nt}}&KH1miYUraS5NI&?QCK2oMNm)8ct4eukr*RZBaw zliBhzi>@yQL>JgRT)&2P_H|?QUc;4XB?!Pu;M<4uJ;QhEixV+-*?`O6m z>|3JY#Pgqe-!H9jC&nb)ybiLU9PSTHIS_8g#&}6w!i6`LMlLkh=4N`@A$nl__Cqo< zK~Oi}k1A@DRjH&-%G6Zl!itS8wPD$?dX^A_M~Eff)(sl+cIsv9-4E9sq}8*=>Z6YSlA}3D2ttORc|vK=JZqJz(X`W{CaozZKt^LL^w|VLAC~TkL=_VioC_a%68EEjAA^@m zr}wTZCf{vpTA}3jB$UkSAsg6v>cp0V{4_=2mhLA{adPc>A3qEq}RvO zxo5YM;Tx1&nUsF*Df#{@pBDyZqYw6U3hb>*`V6X? zrohwKVB-^s#H)tYpJTpPwe@->52PRehMt@@w&ObOQRoxsJbiG zoDb>M0x-F!?8KpSlX-=vi zXY0rdu3&?jKW61Z;>I6|p()C^GvqI-8|oKwFMn@KXznq9M3v>?wJ^wK4H44RZ|w9y z%Fs}*?%qS)8){W7RVw&V7M{qjju~R~s3jFoAFDbsJRGgN2j3-2N>0>oL&%*v&s>Kk zw=zp-cNR`hhsz~q?7=zmW?mI@hA8Zv+Y2F5hvF`c?~Do83#P(Srrq6)btud}h9~() zovwGgZTZT}>X!-jDO_5%et{T5o=68XnQyDNY00;xx~{JFJ+&FAX9%LO1J{2PbGDA> z4iM||p$ShRo&rMtPaAo~;FtJao@D?33ufSdZ8lbP=LGX7@O=|gSl>(xEewi}pS}KH DzX{47 literal 0 HcmV?d00001 From 94ab14effbe2b3230be57cbaf84b5b9cf467946e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 2 Dec 2020 23:10:01 -0800 Subject: [PATCH 207/539] Update to use new black compatibility image --- docs/configuration/black_compatibility.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/configuration/black_compatibility.md b/docs/configuration/black_compatibility.md index 35f6812bf..c81989a9f 100644 --- a/docs/configuration/black_compatibility.md +++ b/docs/configuration/black_compatibility.md @@ -1,3 +1,5 @@ +![isort loves black](https://raw.githubusercontent.com/pycqa/isort/develop/art/isort_loves_black.png) + Compatibility with black ======== From c61f6ffb23f20268e35d8ccab58ae70a8736b5b4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 2 Dec 2020 23:13:33 -0800 Subject: [PATCH 208/539] Fix test on windows --- tests/unit/test_main.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index e53a626dc..e1a459d1b 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -1,5 +1,4 @@ import json -import os import subprocess from datetime import datetime from io import BytesIO, TextIOWrapper @@ -1038,9 +1037,9 @@ def test_identify_imports_main(tmpdir, capsys): main.identify_imports_main([str(some_file)]) out, error = capsys.readouterr() - assert out == file_imports + assert out.replace("\r\n", "\n") == file_imports assert not error main.identify_imports_main(["-"], stdin=as_stream(file_content)) out, error = capsys.readouterr() - assert out == file_imports + assert out.replace("\r\n", "\n") == file_imports From 7fc229df01c6c54c6a6884182ad95a355758aee6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 2 Dec 2020 23:16:12 -0800 Subject: [PATCH 209/539] Add link to isort black compatibility guide to main readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f55c15883..564442824 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ quickly sort all your imports. It requires Python 3.6+ to run but supports formatting Python 2 code too. [Try isort now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try/) +[Using black? See the isort and black compatiblity guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility/) ![Example Usage](https://raw.github.com/pycqa/isort/develop/example.gif) From 7593b675996a2cd9ce5cda1e844e55e5718a689e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 2 Dec 2020 23:22:43 -0800 Subject: [PATCH 210/539] Improve formatting --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 564442824..7b4526700 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ editors](https://github.com/pycqa/isort/wiki/isort-Plugins) to quickly sort all your imports. It requires Python 3.6+ to run but supports formatting Python 2 code too. -[Try isort now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try/) -[Using black? See the isort and black compatiblity guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility/) +- [Try isort now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try/) +- [Using black? See the isort and black compatiblity guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility/) ![Example Usage](https://raw.github.com/pycqa/isort/develop/example.gif) From 05858f873cf3139e5b7c360fb7aef24288daa752 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 3 Dec 2020 23:30:25 -0800 Subject: [PATCH 211/539] Refactor test main cases to reuse as_stream --- tests/unit/test_main.py | 130 +++++++++++++--------------------------- 1 file changed, 43 insertions(+), 87 deletions(-) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index e1a459d1b..2af5d8143 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -363,7 +363,7 @@ def function(): """ def build_input_content(): - return UnseekableTextIOWrapper(BytesIO(input_text.encode("utf8"))) + return as_stream(input_text) main.main(["-"], stdin=build_input_content()) out, error = capsys.readouterr() @@ -462,13 +462,11 @@ def function(): def test_isort_with_stdin(capsys): # ensures that isort sorts stdin without any flags - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import b import a """ - ) ) main.main(["-"], stdin=input_content) @@ -481,14 +479,12 @@ def test_isort_with_stdin(capsys): """ ) - input_content_from = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content_from = as_stream( + """ import c import b from a import z, y, x """ - ) ) main.main(["-"], stdin=input_content_from) @@ -504,15 +500,13 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --fas flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import sys import pandas from z import abc from a import xyz """ - ) ) main.main(["-", "--fas"], stdin=input_content) @@ -530,12 +524,10 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --fass flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ from a import Path, abc """ - ) ) main.main(["-", "--fass"], stdin=input_content) @@ -549,14 +541,12 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --ff flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import b from c import x from a import y """ - ) ) main.main(["-", "--ff", "FROM_FIRST"], stdin=input_content) @@ -572,13 +562,11 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with -fss flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import b from a import a """ - ) ) main.main(["-", "--fss"], stdin=input_content) @@ -591,13 +579,11 @@ def test_isort_with_stdin(capsys): """ ) - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import a from b import c """ - ) ) main.main(["-", "--fss"], stdin=input_content) @@ -612,14 +598,12 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --ds flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import sys import pandas import a """ - ) ) main.main(["-", "--ds"], stdin=input_content) @@ -635,13 +619,11 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --cs flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ from a import b from a import * """ - ) ) main.main(["-", "--cs"], stdin=input_content) @@ -655,13 +637,11 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --ca flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ from a import x as X from a import y as Y """ - ) ) main.main(["-", "--ca"], stdin=input_content) @@ -675,14 +655,12 @@ def test_isort_with_stdin(capsys): # ensures that isort works consistently with check and ws flags - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import os import a import b """ - ) ) main.main(["-", "--check-only", "--ws"], stdin=input_content) @@ -692,13 +670,11 @@ def test_isort_with_stdin(capsys): # ensures that isort works consistently with check and diff flags - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import b import a """ - ) ) with pytest.raises(SystemExit): @@ -711,13 +687,11 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --ls flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import abcdef import x """ - ) ) main.main(["-", "--ls"], stdin=input_content) @@ -732,12 +706,10 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --nis flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ from z import b, c, a """ - ) ) main.main(["-", "--nis"], stdin=input_content) @@ -751,12 +723,10 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --sl flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ from z import b, c, a """ - ) ) main.main(["-", "--sl"], stdin=input_content) @@ -772,15 +742,12 @@ def test_isort_with_stdin(capsys): # ensures that isort correctly sorts stdin with --top flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import os import sys """ - ) ) - main.main(["-", "--top", "sys"], stdin=input_content) out, error = capsys.readouterr() @@ -793,17 +760,14 @@ def test_isort_with_stdin(capsys): # ensure that isort correctly sorts stdin with --os flag - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import sys import os import z from a import b, e, c """ - ) ) - main.main(["-", "--os"], stdin=input_content) out, error = capsys.readouterr() @@ -818,13 +782,11 @@ def test_isort_with_stdin(capsys): ) # ensures that isort warns with deprecated flags with stdin - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import sys import os """ - ) ) with pytest.warns(UserWarning): @@ -839,13 +801,11 @@ def test_isort_with_stdin(capsys): """ ) - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import sys import os """ - ) ) with pytest.warns(UserWarning): @@ -861,13 +821,11 @@ def test_isort_with_stdin(capsys): ) # ensures that only-modified flag works with stdin - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import a import b """ - ) ) main.main(["-", "--verbose", "--only-modified"], stdin=input_content) @@ -877,13 +835,11 @@ def test_isort_with_stdin(capsys): assert "else-type place_module for b returned THIRDPARTY" not in out # ensures that combine-straight-imports flag works with stdin - input_content = UnseekableTextIOWrapper( - BytesIO( - b""" + input_content = as_stream( + """ import a import b """ - ) ) main.main(["-", "--combine-straight-imports"], stdin=input_content) From e912fbf541ea1d722aeed7f1f01c4e565ad0196b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 4 Dec 2020 22:48:04 -0800 Subject: [PATCH 212/539] Improve coverage of identify imports --- tests/unit/test_main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 2af5d8143..5fab49ffc 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -999,3 +999,6 @@ def test_identify_imports_main(tmpdir, capsys): main.identify_imports_main(["-"], stdin=as_stream(file_content)) out, error = capsys.readouterr() assert out.replace("\r\n", "\n") == file_imports + + with pytest.raises(SystemExit): + main.identify_imports_main([str(tmpdir)]) From f32cf7724c071f9e84423f07271bfa70178d8000 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 5 Dec 2020 23:53:52 -0800 Subject: [PATCH 213/539] Improve test coverage with test for KeyError in main --- poetry.lock | 990 +++++++++++++++++++--------------------- pyproject.toml | 1 + tests/unit/test_main.py | 16 + 3 files changed, 475 insertions(+), 532 deletions(-) diff --git a/poetry.lock b/poetry.lock index d6e942661..21fa4fabc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,98 +1,97 @@ [[package]] -category = "main" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" optional = false python-versions = "*" -version = "1.4.4" [[package]] -category = "dev" -description = "Disable App Nap on OS X 10.9" -marker = "sys_platform == \"darwin\"" name = "appnope" +version = "0.1.0" +description = "Disable App Nap on OS X 10.9" +category = "dev" optional = false python-versions = "*" -version = "0.1.0" [[package]] -category = "dev" -description = "Better dates & times for Python" name = "arrow" +version = "0.16.0" +description = "Better dates & times for Python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.16.0" [package.dependencies] python-dateutil = ">=2.7.0" [[package]] -category = "dev" -description = "Atomic file writes." -marker = "sys_platform == \"win32\"" name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.4.0" [[package]] -category = "main" -description = "Classes Without Boilerplate" name = "attrs" +version = "20.1.0" +description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.1.0" [package.extras] -dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] [[package]] -category = "dev" -description = "Specifications for callback functions passed in to an API" name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +category = "dev" optional = false python-versions = "*" -version = "0.2.0" [[package]] -category = "dev" -description = "Security oriented static analyser for python code." name = "bandit" +version = "1.6.2" +description = "Security oriented static analyser for python code." +category = "dev" optional = false python-versions = "*" -version = "1.6.2" [package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} GitPython = ">=1.0.1" PyYAML = ">=3.13" -colorama = ">=0.3.9" six = ">=1.10.0" stevedore = ">=1.20.0" [[package]] -category = "dev" -description = "Ultra-lightweight pure Python package to check if a file is binary or text." name = "binaryornot" +version = "0.4.4" +description = "Ultra-lightweight pure Python package to check if a file is binary or text." +category = "dev" optional = false python-versions = "*" -version = "0.4.4" [package.dependencies] chardet = ">=3.0.2" [[package]] -category = "dev" -description = "The uncompromising code formatter." name = "black" +version = "20.8b1" +description = "The uncompromising code formatter." +category = "dev" optional = false python-versions = ">=3.6" -version = "20.8b1" [package.dependencies] appdirs = "*" click = ">=7.1.2" +dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} mypy-extensions = ">=0.4.3" pathspec = ">=0.6,<1" regex = ">=2020.1.8" @@ -100,114 +99,106 @@ toml = ">=0.10.1" typed-ast = ">=1.4.0" typing-extensions = ">=3.7.4" -[package.dependencies.dataclasses] -python = "<3.7" -version = ">=0.6" - [package.extras] colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] -category = "main" -description = "A decorator for caching properties in classes." name = "cached-property" +version = "1.5.1" +description = "A decorator for caching properties in classes." +category = "main" optional = false python-versions = "*" -version = "1.5.1" [[package]] -category = "main" -description = "Lightweight, extensible schema and data validation tool for Python dictionaries." name = "cerberus" +version = "1.3.2" +description = "Lightweight, extensible schema and data validation tool for Python dictionaries." +category = "main" optional = false python-versions = ">=2.7" -version = "1.3.2" - -[package.dependencies] -setuptools = "*" [[package]] -category = "main" -description = "Python package for providing Mozilla's CA Bundle." name = "certifi" +version = "2020.6.20" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = "*" -version = "2020.6.20" [[package]] -category = "main" -description = "Universal encoding detector for Python 2 and 3" name = "chardet" +version = "3.0.4" +description = "Universal encoding detector for Python 2 and 3" +category = "main" optional = false python-versions = "*" -version = "3.0.4" [[package]] -category = "dev" -description = "Composable command line interface toolkit" name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "7.1.2" [[package]] -category = "main" -description = "Cross-platform colored terminal text." name = "colorama" +version = "0.4.3" +description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.4.3" [[package]] -category = "dev" -description = "PEP 567 Backport" -marker = "python_version < \"3.7\"" name = "contextvars" +version = "2.4" +description = "PEP 567 Backport" +category = "dev" optional = false python-versions = "*" -version = "2.4" [package.dependencies] immutables = ">=0.9" [[package]] -category = "dev" -description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." name = "cookiecutter" +version = "1.7.2" +description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "1.7.2" [package.dependencies] -Jinja2 = "<3.0.0" -MarkupSafe = "<2.0.0" binaryornot = ">=0.4.4" click = ">=7.0" +Jinja2 = "<3.0.0" jinja2-time = ">=0.2.0" +MarkupSafe = "<2.0.0" poyo = ">=0.5.0" python-slugify = ">=4.0.0" requests = ">=2.23.0" six = ">=1.10" [[package]] -category = "dev" -description = "Code coverage measurement for Python" name = "coverage" +version = "5.2.1" +description = "Code coverage measurement for Python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "5.2.1" [package.extras] toml = ["toml"] [[package]] -category = "dev" -description = "Allows you to maintain all the necessary cruft for packaging and building projects separate from the code you intentionally write. Built on-top of CookieCutter." name = "cruft" +version = "2.3.0" +description = "Allows you to maintain all the necessary cruft for packaging and building projects separate from the code you intentionally write. Built on-top of CookieCutter." +category = "dev" optional = false python-versions = ">=3.6,<4.0" -version = "2.3.0" [package.dependencies] click = ">=7.1.2,<8.0.0" @@ -216,49 +207,48 @@ gitpython = ">=3.0,<4.0" typer = ">=0.3.1,<0.4.0" [package.extras] -examples = ["examples (>=1.0.2,<2.0.0)"] pyproject = ["toml (>=0.10,<0.11)"] +examples = ["examples (>=1.0.2,<2.0.0)"] [[package]] -category = "dev" -description = "A backport of the dataclasses module for Python 3.6" -marker = "python_version < \"3.7\"" name = "dataclasses" +version = "0.6" +description = "A backport of the dataclasses module for Python 3.6" +category = "dev" optional = false python-versions = "*" -version = "0.6" [[package]] -category = "dev" -description = "Decorators for Humans" name = "decorator" +version = "4.4.2" +description = "Decorators for Humans" +category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.4.2" [[package]] -category = "main" -description = "Distribution utilities" name = "distlib" +version = "0.3.1" +description = "Distribution utilities" +category = "main" optional = false python-versions = "*" -version = "0.3.1" [[package]] -category = "main" -description = "Pythonic argument parser, that will make you smile" name = "docopt" +version = "0.6.2" +description = "Pythonic argument parser, that will make you smile" +category = "main" optional = false python-versions = "*" -version = "0.6.2" [[package]] -category = "dev" -description = "A parser for Python dependency files" name = "dparse" +version = "0.5.1" +description = "A parser for Python dependency files" +category = "dev" optional = false python-versions = ">=3.5" -version = "0.5.1" [package.dependencies] packaging = "*" @@ -269,168 +259,165 @@ toml = "*" pipenv = ["pipenv"] [[package]] -category = "dev" -description = "An example plugin that modifies isort formatting using black." name = "example-isort-formatting-plugin" +version = "0.0.2" +description = "An example plugin that modifies isort formatting using black." +category = "dev" optional = false python-versions = ">=3.6,<4.0" -version = "0.0.2" [package.dependencies] black = ">=20.08b1,<21.0" isort = ">=5.1.4,<6.0.0" [[package]] -category = "dev" -description = "An example shared isort profile" name = "example-shared-isort-profile" +version = "0.0.1" +description = "An example shared isort profile" +category = "dev" optional = false python-versions = ">=3.6,<4.0" -version = "0.0.1" [[package]] -category = "dev" -description = "Tests and Documentation Done by Example." name = "examples" +version = "1.0.2" +description = "Tests and Documentation Done by Example." +category = "dev" optional = false python-versions = ">=3.6,<4.0" -version = "1.0.2" [package.dependencies] pydantic = ">=0.32.2" [[package]] -category = "dev" -description = "An unladen web framework for building APIs and app backends." name = "falcon" +version = "2.0.0" +description = "An unladen web framework for building APIs and app backends." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.0.0" [[package]] -category = "dev" -description = "the modular source code checker: pep8 pyflakes and co" name = "flake8" +version = "3.8.4" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "3.8.3" [package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} mccabe = ">=0.6.0,<0.7.0" pycodestyle = ">=2.6.0a1,<2.7.0" pyflakes = ">=2.2.0,<2.3.0" -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" - [[package]] -category = "dev" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." name = "flake8-bugbear" +version = "19.8.0" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +category = "dev" optional = false python-versions = ">=3.5" -version = "19.8.0" [package.dependencies] attrs = "*" flake8 = ">=3.0.0" [[package]] -category = "dev" -description = "Polyfill package for Flake8 plugins" name = "flake8-polyfill" +version = "1.0.2" +description = "Polyfill package for Flake8 plugins" +category = "dev" optional = false python-versions = "*" -version = "1.0.2" [package.dependencies] flake8 = "*" [[package]] -category = "dev" -description = "Clean single-source support for Python 3 and 2" name = "future" +version = "0.18.2" +description = "Clean single-source support for Python 3 and 2" +category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "0.18.2" [[package]] -category = "dev" -description = "Git Object Database" name = "gitdb" +version = "4.0.5" +description = "Git Object Database" +category = "dev" optional = false python-versions = ">=3.4" -version = "4.0.5" [package.dependencies] smmap = ">=3.0.1,<4" [[package]] -category = "dev" -description = "A mirror package for gitdb" name = "gitdb2" +version = "4.0.2" +description = "A mirror package for gitdb" +category = "dev" optional = false python-versions = "*" -version = "4.0.2" [package.dependencies] gitdb = ">=4.0.1" [[package]] -category = "dev" -description = "Python Git Library" name = "gitpython" +version = "3.1.7" +description = "Python Git Library" +category = "dev" optional = false python-versions = ">=3.4" -version = "3.1.7" [package.dependencies] gitdb = ">=4.0.1,<5" [[package]] -category = "dev" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" name = "h11" +version = "0.9.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "dev" optional = false python-versions = "*" -version = "0.9.0" [[package]] -category = "dev" -description = "HTTP/2 State-Machine based protocol implementation" name = "h2" +version = "3.2.0" +description = "HTTP/2 State-Machine based protocol implementation" +category = "dev" optional = false python-versions = "*" -version = "3.2.0" [package.dependencies] hpack = ">=3.0,<4" hyperframe = ">=5.2.0,<6" [[package]] -category = "dev" -description = "Pure-Python HPACK header compression" name = "hpack" +version = "3.0.0" +description = "Pure-Python HPACK header compression" +category = "dev" optional = false python-versions = "*" -version = "3.0.0" [[package]] -category = "dev" -description = "Chromium HSTS Preload list as a Python package and updated daily" name = "hstspreload" +version = "2020.8.25" +description = "Chromium HSTS Preload list as a Python package and updated daily" +category = "dev" optional = false python-versions = ">=3.6" -version = "2020.8.25" [[package]] -category = "dev" -description = "A minimal low-level HTTP client." name = "httpcore" +version = "0.9.1" +description = "A minimal low-level HTTP client." +category = "dev" optional = false python-versions = ">=3.6" -version = "0.9.1" [package.dependencies] h11 = ">=0.8,<0.10" @@ -438,12 +425,12 @@ h2 = ">=3.0.0,<4.0.0" sniffio = ">=1.0.0,<2.0.0" [[package]] -category = "dev" -description = "The next generation HTTP client." name = "httpx" +version = "0.13.3" +description = "The next generation HTTP client." +category = "dev" optional = false python-versions = ">=3.6" -version = "0.13.3" [package.dependencies] certifi = "*" @@ -455,32 +442,32 @@ rfc3986 = ">=1.3,<2" sniffio = "*" [[package]] -category = "dev" -description = "A Python framework that makes developing APIs as simple as possible, but no simpler." name = "hug" +version = "2.6.1" +description = "A Python framework that makes developing APIs as simple as possible, but no simpler." +category = "dev" optional = false python-versions = ">=3.5" -version = "2.6.1" [package.dependencies] falcon = "2.0.0" requests = "*" [[package]] -category = "dev" -description = "HTTP/2 framing layer for Python" name = "hyperframe" +version = "5.2.0" +description = "HTTP/2 framing layer for Python" +category = "dev" optional = false python-versions = "*" -version = "5.2.0" [[package]] -category = "dev" -description = "A library for property-based testing" name = "hypothesis" +version = "5.29.3" +description = "A library for property-based testing" +category = "dev" optional = false python-versions = ">=3.5.2" -version = "5.29.3" [package.dependencies] attrs = ">=19.2.0" @@ -500,12 +487,12 @@ pytest = ["pytest (>=4.3)"] pytz = ["pytz (>=2014.1)"] [[package]] -category = "dev" -description = "Extends Hypothesis to add fully automatic testing of type annotated functions" name = "hypothesis-auto" +version = "1.1.4" +description = "Extends Hypothesis to add fully automatic testing of type annotated functions" +category = "dev" optional = false python-versions = ">=3.6,<4.0" -version = "1.1.4" [package.dependencies] hypothesis = ">=4.36" @@ -515,12 +502,12 @@ pydantic = ">=0.32.2" pytest = ["pytest (>=4.0.0,<5.0.0)"] [[package]] -category = "dev" -description = "Hypothesis strategies for generating Python programs, something like CSmith" name = "hypothesmith" +version = "0.1.4" +description = "Hypothesis strategies for generating Python programs, something like CSmith" +category = "dev" optional = false python-versions = ">=3.6" -version = "0.1.4" [package.dependencies] hypothesis = ">=5.23.7" @@ -528,30 +515,28 @@ lark-parser = ">=0.7.2" libcst = ">=0.3.8" [[package]] -category = "main" -description = "Internationalized Domain Names in Applications (IDNA)" name = "idna" +version = "2.10" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.10" [[package]] -category = "dev" -description = "Immutable Collections" -marker = "python_version < \"3.7\"" name = "immutables" +version = "0.14" +description = "Immutable Collections" +category = "dev" optional = false python-versions = ">=3.5" -version = "0.14" [[package]] -category = "main" -description = "Read metadata from Python packages" -marker = "python_version < \"3.8\"" name = "importlib-metadata" +version = "1.7.0" +description = "Read metadata from Python packages" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "1.7.0" [package.dependencies] zipp = ">=0.5" @@ -561,24 +546,23 @@ docs = ["sphinx", "rst.linker"] testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] [[package]] -category = "dev" -description = "IPython: Productive Interactive Computing" name = "ipython" +version = "7.16.1" +description = "IPython: Productive Interactive Computing" +category = "dev" optional = false python-versions = ">=3.6" -version = "7.16.1" [package.dependencies] -appnope = "*" +appnope = {version = "*", markers = "sys_platform == \"darwin\""} backcall = "*" -colorama = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" jedi = ">=0.10" -pexpect = "*" +pexpect = {version = "*", markers = "sys_platform != \"win32\""} pickleshare = "*" prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" pygments = "*" -setuptools = ">=18.5" traitlets = ">=4.2" [package.extras] @@ -593,35 +577,35 @@ qtconsole = ["qtconsole"] test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] [[package]] -category = "dev" -description = "Vestigial utilities from IPython" name = "ipython-genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" +category = "dev" optional = false python-versions = "*" -version = "0.2.0" [[package]] -category = "dev" -description = "An autocompletion tool for Python that can be used for text editors." name = "jedi" +version = "0.17.2" +description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.17.2" [package.dependencies] parso = ">=0.7.0,<0.8.0" [package.extras] -qa = ["flake8 (3.7.9)"] +qa = ["flake8 (==3.7.9)"] testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] [[package]] -category = "dev" -description = "A very fast and expressive template engine." name = "jinja2" +version = "2.11.2" +description = "A very fast and expressive template engine." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.11.2" [package.dependencies] MarkupSafe = ">=0.23" @@ -630,99 +614,88 @@ MarkupSafe = ">=0.23" i18n = ["Babel (>=0.8)"] [[package]] -category = "dev" -description = "Jinja2 Extension for Dates and Times" name = "jinja2-time" +version = "0.2.0" +description = "Jinja2 Extension for Dates and Times" +category = "dev" optional = false python-versions = "*" -version = "0.2.0" [package.dependencies] arrow = "*" jinja2 = "*" [[package]] -category = "dev" -description = "Lightweight pipelining: using Python functions as pipeline jobs." -marker = "python_version > \"2.7\"" name = "joblib" +version = "0.16.0" +description = "Lightweight pipelining: using Python functions as pipeline jobs." +category = "dev" optional = false python-versions = ">=3.6" -version = "0.16.0" [[package]] -category = "dev" -description = "a modern parsing library" name = "lark-parser" +version = "0.9.0" +description = "a modern parsing library" +category = "dev" optional = false python-versions = "*" -version = "0.9.0" [package.extras] regex = ["regex"] [[package]] -category = "dev" -description = "A concrete syntax tree with AST-like properties for Python 3.5, 3.6, 3.7 and 3.8 programs." name = "libcst" +version = "0.3.10" +description = "A concrete syntax tree with AST-like properties for Python 3.5, 3.6, 3.7 and 3.8 programs." +category = "dev" optional = false python-versions = ">=3.6" -version = "0.3.10" [package.dependencies] +dataclasses = {version = "*", markers = "python_version < \"3.7\""} pyyaml = ">=5.2" typing-extensions = ">=3.7.4.2" typing-inspect = ">=0.4.0" -[package.dependencies.dataclasses] -python = "<3.7" -version = "*" - [package.extras] dev = ["black", "codecov", "coverage", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "isort", "flake8", "jupyter", "nbsphinx", "pyre-check", "sphinx", "sphinx-rtd-theme"] [[package]] -category = "dev" -description = "Python LiveReload is an awesome tool for web developers" name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +category = "dev" optional = false python-versions = "*" -version = "2.6.3" [package.dependencies] six = "*" - -[package.dependencies.tornado] -python = ">=2.8" -version = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} [[package]] -category = "dev" -description = "A Python implementation of Lunr.js" name = "lunr" +version = "0.5.8" +description = "A Python implementation of Lunr.js" +category = "dev" optional = false python-versions = "*" -version = "0.5.8" [package.dependencies] future = ">=0.16.0" +nltk = {version = ">=3.2.5", optional = true, markers = "python_version > \"2.7\" and extra == \"languages\""} six = ">=1.11.0" -[package.dependencies.nltk] -optional = true -python = ">=2.8" -version = ">=3.2.5" - [package.extras] languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] [[package]] -category = "dev" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." name = "mako" +version = "1.1.3" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.1.3" [package.dependencies] MarkupSafe = ">=0.9.2" @@ -732,98 +705,93 @@ babel = ["babel"] lingua = ["lingua"] [[package]] -category = "dev" -description = "Python implementation of Markdown." name = "markdown" +version = "3.2.2" +description = "Python implementation of Markdown." +category = "dev" optional = false python-versions = ">=3.5" -version = "3.2.2" [package.dependencies] -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [package.extras] testing = ["coverage", "pyyaml"] [[package]] -category = "dev" -description = "Safely add untrusted strings to HTML/XML markup." name = "markupsafe" +version = "1.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.1.1" [[package]] -category = "dev" -description = "McCabe checker, plugin for flake8" name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" optional = false python-versions = "*" -version = "0.6.1" [[package]] -category = "dev" -description = "Project documentation with Markdown." name = "mkdocs" +version = "1.1.2" +description = "Project documentation with Markdown." +category = "dev" optional = false python-versions = ">=3.5" -version = "1.1.2" [package.dependencies] +click = ">=3.3" Jinja2 = ">=2.10.1" +livereload = ">=2.5.1" +lunr = {version = "0.5.8", extras = ["languages"]} Markdown = ">=3.2.1" PyYAML = ">=3.10" -click = ">=3.3" -livereload = ">=2.5.1" tornado = ">=5.0" -[package.dependencies.lunr] -extras = ["languages"] -version = "0.5.8" - [[package]] -category = "dev" -description = "A Material Design theme for MkDocs" name = "mkdocs-material" +version = "5.5.9" +description = "A Material Design theme for MkDocs" +category = "dev" optional = false python-versions = "*" -version = "5.5.9" [package.dependencies] -Pygments = ">=2.4" markdown = ">=3.2" mkdocs = ">=1.1" mkdocs-material-extensions = ">=1.0" +Pygments = ">=2.4" pymdown-extensions = ">=7.0" [[package]] -category = "dev" -description = "Extension pack for Python Markdown." name = "mkdocs-material-extensions" +version = "1.0" +description = "Extension pack for Python Markdown." +category = "dev" optional = false python-versions = ">=3.5" -version = "1.0" [package.dependencies] mkdocs-material = ">=5.0.0" [[package]] -category = "dev" -description = "More routines for operating on iterables, beyond itertools" name = "more-itertools" +version = "8.4.0" +description = "More routines for operating on iterables, beyond itertools" +category = "dev" optional = false python-versions = ">=3.5" -version = "8.4.0" [[package]] -category = "dev" -description = "Optional static typing for Python" name = "mypy" +version = "0.761" +description = "Optional static typing for Python" +category = "dev" optional = false python-versions = ">=3.5" -version = "0.761" [package.dependencies] mypy-extensions = ">=0.4.3,<0.5.0" @@ -834,21 +802,20 @@ typing-extensions = ">=3.7.4" dmypy = ["psutil (>=4.0)"] [[package]] -category = "dev" -description = "Experimental type system extensions for programs checked with the mypy typechecker." name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" optional = false python-versions = "*" -version = "0.4.3" [[package]] -category = "dev" -description = "Natural Language Toolkit" -marker = "python_version > \"2.7\"" name = "nltk" +version = "3.5" +description = "Natural Language Toolkit" +category = "dev" optional = false python-versions = "*" -version = "3.5" [package.dependencies] click = "*" @@ -865,222 +832,204 @@ tgrep = ["pyparsing"] twitter = ["twython"] [[package]] -category = "dev" -description = "NumPy is the fundamental package for array computing with Python." name = "numpy" +version = "1.19.1" +description = "NumPy is the fundamental package for array computing with Python." +category = "dev" optional = false python-versions = ">=3.6" -version = "1.19.1" [[package]] -category = "main" -description = "Ordered Multivalue Dictionary" name = "orderedmultidict" +version = "1.0.1" +description = "Ordered Multivalue Dictionary" +category = "main" optional = false python-versions = "*" -version = "1.0.1" [package.dependencies] six = ">=1.8.0" [[package]] -category = "main" -description = "Core utilities for Python packages" name = "packaging" +version = "20.4" +description = "Core utilities for Python packages" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.4" [package.dependencies] pyparsing = ">=2.0.2" six = "*" [[package]] -category = "dev" -description = "A Python Parser" name = "parso" +version = "0.7.1" +description = "A Python Parser" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.7.1" [package.extras] testing = ["docopt", "pytest (>=3.0.7)"] [[package]] -category = "dev" -description = "Utility library for gitignore style pattern matching of file paths." name = "pathspec" +version = "0.8.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.8.0" [[package]] -category = "dev" -description = "Python Build Reasonableness" name = "pbr" +version = "5.4.5" +description = "Python Build Reasonableness" +category = "dev" optional = false python-versions = "*" -version = "5.4.5" [[package]] -category = "dev" -description = "A simple program and library to auto generate API documentation for Python modules." name = "pdocs" +version = "1.0.2" +description = "A simple program and library to auto generate API documentation for Python modules." +category = "dev" optional = false python-versions = ">=3.6,<4.0" -version = "1.0.2" [package.dependencies] +hug = ">=2.6,<3.0" Mako = ">=1.1,<2.0" Markdown = ">=3.0.0,<4.0.0" -hug = ">=2.6,<3.0" [[package]] -category = "main" -description = "Wrappers to build Python packages using PEP 517 hooks" name = "pep517" +version = "0.8.2" +description = "Wrappers to build Python packages using PEP 517 hooks" +category = "main" optional = false python-versions = "*" -version = "0.8.2" [package.dependencies] +importlib_metadata = {version = "*", markers = "python_version < \"3.8\""} toml = "*" - -[package.dependencies.importlib_metadata] -python = "<3.8" -version = "*" - -[package.dependencies.zipp] -python = "<3.8" -version = "*" +zipp = {version = "*", markers = "python_version < \"3.8\""} [[package]] -category = "dev" -description = "Check PEP-8 naming conventions, plugin for flake8" name = "pep8-naming" +version = "0.8.2" +description = "Check PEP-8 naming conventions, plugin for flake8" +category = "dev" optional = false python-versions = "*" -version = "0.8.2" [package.dependencies] flake8-polyfill = ">=1.0.2,<2" [[package]] -category = "dev" -description = "Pexpect allows easy control of interactive console applications." -marker = "sys_platform != \"win32\"" name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "dev" optional = false python-versions = "*" -version = "4.8.0" [package.dependencies] ptyprocess = ">=0.5" [[package]] -category = "dev" -description = "Tiny 'shelve'-like database with concurrency support" name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +category = "dev" optional = false python-versions = "*" -version = "0.7.5" [[package]] -category = "main" -description = "An unofficial, importable pip API" name = "pip-api" +version = "0.0.12" +description = "An unofficial, importable pip API" +category = "main" optional = false python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" -version = "0.0.12" - -[package.dependencies] -pip = "*" [[package]] -category = "main" -description = "Compatibility shims for pip versions 8 thru current." name = "pip-shims" +version = "0.5.3" +description = "Compatibility shims for pip versions 8 thru current." +category = "main" optional = false python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" -version = "0.5.3" [package.dependencies] packaging = "*" -pip = "*" -setuptools = "*" six = "*" -wheel = "*" [package.extras] dev = ["pre-commit", "isort", "flake8", "rope", "invoke", "parver", "towncrier", "wheel", "mypy", "flake8-bugbear", "black"] tests = ["pytest-timeout", "pytest (<5.0)", "pytest-xdist", "pytest-cov", "twine", "readme-renderer"] [[package]] -category = "dev" -description = "" name = "pipfile" +version = "0.0.2" +description = "" +category = "dev" optional = false python-versions = "*" -version = "0.0.2" [package.dependencies] toml = "*" [[package]] -category = "main" -description = "Pip requirements.txt generator based on imports in project" name = "pipreqs" +version = "0.4.10" +description = "Pip requirements.txt generator based on imports in project" +category = "main" optional = false python-versions = "*" -version = "0.4.10" [package.dependencies] docopt = "*" yarg = "*" [[package]] -category = "main" -description = "Structured Pipfile and Pipfile.lock models." name = "plette" +version = "0.2.3" +description = "Structured Pipfile and Pipfile.lock models." +category = "main" optional = false python-versions = ">=2.6,!=3.0,!=3.1,!=3.2,!=3.3" -version = "0.2.3" [package.dependencies] +cerberus = {version = "*", optional = true, markers = "extra == \"validation\""} six = "*" tomlkit = "*" -[package.dependencies.cerberus] -optional = true -version = "*" - [package.extras] tests = ["pytest", "pytest-xdist", "pytest-cov"] validation = ["cerberus"] [[package]] -category = "dev" -description = "plugin and hook calling mechanisms for python" name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.13.1" [package.dependencies] -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] [[package]] -category = "dev" -description = "Your Project with Great Documentation" name = "portray" +version = "1.4.0" +description = "Your Project with Great Documentation" +category = "dev" optional = false python-versions = ">=3.6,<4.0" -version = "1.4.0" [package.dependencies] GitPython = ">=3.0,<4.0" @@ -1093,101 +1042,98 @@ toml = ">=0.10.0,<0.11.0" yaspin = ">=0.15.0,<0.16.0" [[package]] -category = "dev" -description = "A lightweight YAML Parser for Python. 🐓" name = "poyo" +version = "0.5.0" +description = "A lightweight YAML Parser for Python. 🐓" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.5.0" [[package]] -category = "dev" -description = "Library for building powerful interactive command lines in Python" name = "prompt-toolkit" +version = "3.0.3" +description = "Library for building powerful interactive command lines in Python" +category = "dev" optional = false python-versions = ">=3.6" -version = "3.0.3" [package.dependencies] wcwidth = "*" [[package]] -category = "dev" -description = "Run a subprocess in a pseudo terminal" -marker = "sys_platform != \"win32\"" name = "ptyprocess" +version = "0.6.0" +description = "Run a subprocess in a pseudo terminal" +category = "dev" optional = false python-versions = "*" -version = "0.6.0" [[package]] -category = "dev" -description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" +version = "1.9.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.9.0" [[package]] -category = "dev" -description = "Python style guide checker" name = "pycodestyle" +version = "2.6.0" +description = "Python style guide checker" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.6.0" [[package]] -category = "dev" -description = "Data validation and settings management using python 3.6 type hinting" name = "pydantic" +version = "1.6.1" +description = "Data validation and settings management using python 3.6 type hinting" +category = "dev" optional = false python-versions = ">=3.6" -version = "1.6.1" [package.dependencies] -[package.dependencies.dataclasses] -python = "<3.7" -version = ">=0.6" +dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} [package.extras] dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] typing_extensions = ["typing-extensions (>=3.7.2)"] -[[package]] -category = "dev" -description = "Python docstring style checker" +[[package]] name = "pydocstyle" +version = "5.1.0" +description = "Python docstring style checker" +category = "dev" optional = false python-versions = ">=3.5" -version = "5.1.0" [package.dependencies] snowballstemmer = "*" [[package]] -category = "dev" -description = "passive checker of Python programs" name = "pyflakes" +version = "2.2.0" +description = "passive checker of Python programs" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.2.0" [[package]] -category = "dev" -description = "Pygments is a syntax highlighting package written in Python." name = "pygments" +version = "2.6.1" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" optional = false python-versions = ">=3.5" -version = "2.6.1" [[package]] -category = "dev" -description = "pylama -- Code audit tool for python" name = "pylama" +version = "7.7.1" +description = "pylama -- Code audit tool for python" +category = "dev" optional = false python-versions = "*" -version = "7.7.1" [package.dependencies] mccabe = ">=0.5.2" @@ -1196,72 +1142,69 @@ pydocstyle = ">=2.0.0" pyflakes = ">=1.5.0" [[package]] -category = "dev" -description = "Extension pack for Python Markdown." name = "pymdown-extensions" +version = "7.1" +description = "Extension pack for Python Markdown." +category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" -version = "7.1" [package.dependencies] Markdown = ">=3.2" [[package]] -category = "main" -description = "Python parsing module" name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.4.7" [[package]] -category = "dev" -description = "pytest: simple powerful testing with Python" name = "pytest" +version = "5.4.3" +description = "pytest: simple powerful testing with Python" +category = "dev" optional = false python-versions = ">=3.5" -version = "5.4.3" [package.dependencies] -atomicwrites = ">=1.0" +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=17.4.0" -colorama = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} more-itertools = ">=4.0.0" packaging = "*" pluggy = ">=0.12,<1.0" py = ">=1.5.0" wcwidth = "*" -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" - [package.extras] -checkqa-mypy = ["mypy (v0.761)"] +checkqa-mypy = ["mypy (==v0.761)"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] -category = "dev" -description = "Pytest plugin for measuring coverage." name = "pytest-cov" +version = "2.10.1" +description = "Pytest plugin for measuring coverage." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.10.1" [package.dependencies] coverage = ">=4.4" pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "pytest-xdist", "virtualenv"] +testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] [[package]] -category = "dev" -description = "Thin-wrapper around the mock package for easier use with py.test" name = "pytest-mock" +version = "1.13.0" +description = "Thin-wrapper around the mock package for easier use with py.test" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.13.0" [package.dependencies] pytest = ">=2.7" @@ -1270,23 +1213,23 @@ pytest = ">=2.7" dev = ["pre-commit", "tox"] [[package]] -category = "main" -description = "Extensions to the standard Python datetime module" name = "python-dateutil" +version = "2.8.1" +description = "Extensions to the standard Python datetime module" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -version = "2.8.1" [package.dependencies] six = ">=1.5" [[package]] -category = "dev" -description = "A Python Slugify application that handles Unicode" name = "python-slugify" +version = "4.0.1" +description = "A Python Slugify application that handles Unicode" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "4.0.1" [package.dependencies] text-unidecode = ">=1.3" @@ -1295,28 +1238,28 @@ text-unidecode = ">=1.3" unidecode = ["Unidecode (>=1.1.1)"] [[package]] -category = "dev" -description = "YAML parser and emitter for Python" name = "pyyaml" +version = "5.3.1" +description = "YAML parser and emitter for Python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.3.1" [[package]] -category = "dev" -description = "Alternative regular expression module, to replace re." name = "regex" +version = "2020.7.14" +description = "Alternative regular expression module, to replace re." +category = "dev" optional = false python-versions = "*" -version = "2020.7.14" [[package]] -category = "main" -description = "Python HTTP for Humans." name = "requests" +version = "2.24.0" +description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.24.0" [package.dependencies] certifi = ">=2017.4.17" @@ -1326,15 +1269,15 @@ urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] [[package]] -category = "main" -description = "A tool for converting between pip-style and pipfile requirements." name = "requirementslib" +version = "1.5.13" +description = "A tool for converting between pip-style and pipfile requirements." +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "1.5.13" [package.dependencies] appdirs = "*" @@ -1345,170 +1288,159 @@ orderedmultidict = "*" packaging = ">=19.0" pep517 = ">=0.5.0" pip-shims = ">=0.5.2" +plette = {version = "*", extras = ["validation"]} python-dateutil = "*" requests = "*" -setuptools = ">=40.8" six = ">=1.11.0" tomlkit = ">=0.5.3" vistir = ">=0.3.1" -[package.dependencies.plette] -extras = ["validation"] -version = "*" - [package.extras] dev = ["vulture", "flake8", "rope", "isort", "invoke", "twine", "pre-commit", "lxml", "towncrier", "parver", "flake8-bugbear", "black"] tests = ["mock", "pytest", "twine", "readme-renderer", "pytest-xdist", "pytest-cov", "pytest-timeout", "coverage", "hypothesis"] typing = ["typing", "mypy", "mypy-extensions", "mypytools", "pytype", "typed-ast", "monkeytype"] [[package]] -category = "dev" -description = "Validating URI References per RFC 3986" name = "rfc3986" +version = "1.4.0" +description = "Validating URI References per RFC 3986" +category = "dev" optional = false python-versions = "*" -version = "1.4.0" [package.extras] idna2008 = ["idna"] [[package]] -category = "dev" -description = "Checks installed dependencies for known vulnerabilities." name = "safety" +version = "1.9.0" +description = "Checks installed dependencies for known vulnerabilities." +category = "dev" optional = false python-versions = ">=3.5" -version = "1.9.0" [package.dependencies] Click = ">=6.0" dparse = ">=0.5.1" packaging = "*" requests = "*" -setuptools = "*" [[package]] -category = "main" -description = "Python 2 and 3 compatibility utilities" name = "six" +version = "1.15.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.15.0" [[package]] -category = "dev" -description = "A pure Python implementation of a sliding window memory map manager" name = "smmap" +version = "3.0.4" +description = "A pure Python implementation of a sliding window memory map manager" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.0.4" [[package]] -category = "dev" -description = "A mirror package for smmap" name = "smmap2" +version = "3.0.1" +description = "A mirror package for smmap" +category = "dev" optional = false python-versions = "*" -version = "3.0.1" [package.dependencies] smmap = ">=3.0.1" [[package]] -category = "dev" -description = "Sniff out which async library your code is running under" name = "sniffio" +version = "1.1.0" +description = "Sniff out which async library your code is running under" +category = "dev" optional = false python-versions = ">=3.5" -version = "1.1.0" [package.dependencies] -[package.dependencies.contextvars] -python = "<3.7" -version = ">=2.1" +contextvars = {version = ">=2.1", markers = "python_version < \"3.7\""} [[package]] -category = "dev" -description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms." name = "snowballstemmer" +version = "2.0.0" +description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms." +category = "dev" optional = false python-versions = "*" -version = "2.0.0" [[package]] -category = "dev" -description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" name = "sortedcontainers" +version = "2.2.2" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +category = "dev" optional = false python-versions = "*" -version = "2.2.2" [[package]] -category = "dev" -description = "Manage dynamic plugins for Python applications" name = "stevedore" +version = "3.2.0" +description = "Manage dynamic plugins for Python applications" +category = "dev" optional = false python-versions = ">=3.6" -version = "3.2.0" [package.dependencies] +importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} pbr = ">=2.0.0,<2.1.0 || >2.1.0" -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=1.7.0" - [[package]] -category = "dev" -description = "The most basic Text::Unidecode port" name = "text-unidecode" +version = "1.3" +description = "The most basic Text::Unidecode port" +category = "dev" optional = false python-versions = "*" -version = "1.3" [[package]] -category = "main" -description = "Python Library for Tom's Obvious, Minimal Language" name = "toml" +version = "0.10.1" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "main" optional = false python-versions = "*" -version = "0.10.1" [[package]] -category = "main" -description = "Style preserving TOML library" name = "tomlkit" +version = "0.7.0" +description = "Style preserving TOML library" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.7.0" [[package]] -category = "dev" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." name = "tornado" +version = "6.0.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "dev" optional = false python-versions = ">= 3.5" -version = "6.0.4" [[package]] -category = "dev" -description = "Fast, Extensible Progress Meter" -marker = "python_version > \"2.7\"" name = "tqdm" +version = "4.48.2" +description = "Fast, Extensible Progress Meter" +category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.48.2" [package.extras] dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] [[package]] -category = "dev" -description = "Traitlets Python config system" name = "traitlets" +version = "4.3.3" +description = "Traitlets Python config system" +category = "dev" optional = false python-versions = "*" -version = "4.3.3" [package.dependencies] decorator = "*" @@ -1519,70 +1451,70 @@ six = "*" test = ["pytest", "mock"] [[package]] -category = "dev" -description = "a fork of Python 2 and 3 ast modules with type comment support" name = "typed-ast" +version = "1.4.1" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" optional = false python-versions = "*" -version = "1.4.1" [[package]] -category = "dev" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." name = "typer" +version = "0.3.2" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +category = "dev" optional = false python-versions = ">=3.6" -version = "0.3.2" [package.dependencies] click = ">=7.1.1,<7.2.0" [package.extras] +test = ["pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.782)", "black (>=19.10b0,<20.0b0)", "isort (>=5.0.6,<6.0.0)", "shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)"] all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)"] -test = ["pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (0.782)", "black (>=19.10b0,<20.0b0)", "isort (>=5.0.6,<6.0.0)", "shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)"] [[package]] -category = "dev" -description = "Backported and Experimental Type Hints for Python 3.5+" name = "typing-extensions" +version = "3.7.4.3" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "dev" optional = false python-versions = "*" -version = "3.7.4.3" [[package]] -category = "dev" -description = "Runtime inspection utilities for typing module." name = "typing-inspect" +version = "0.6.0" +description = "Runtime inspection utilities for typing module." +category = "dev" optional = false python-versions = "*" -version = "0.6.0" [package.dependencies] mypy-extensions = ">=0.3.0" typing-extensions = ">=3.7.4" [[package]] -category = "main" -description = "HTTP library with thread-safe connection pooling, file post, and more." name = "urllib3" +version = "1.25.10" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "1.25.10" [package.extras] brotli = ["brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] -category = "main" -description = "Miscellaneous utilities for dealing with filesystems, paths, projects, subprocesses, and more." name = "vistir" +version = "0.5.2" +description = "Miscellaneous utilities for dealing with filesystems, paths, projects, subprocesses, and more." +category = "main" optional = false python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,>=2.7" -version = "0.5.2" [package.dependencies] colorama = ">=0.3.4,<0.4.2 || >0.4.2" @@ -1596,59 +1528,47 @@ tests = ["hypothesis", "hypothesis-fspaths", "pytest", "pytest-rerunfailures (<9 typing = ["typing", "mypy", "mypy-extensions", "mypytools", "pytype", "typed-ast"] [[package]] -category = "dev" -description = "Find dead code" name = "vulture" +version = "1.6" +description = "Find dead code" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "1.6" [[package]] -category = "dev" -description = "Measures the displayed width of unicode strings in a terminal" name = "wcwidth" -optional = false -python-versions = "*" version = "0.2.5" - -[[package]] -category = "main" -description = "A built-package format for Python" -name = "wheel" +description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "0.35.1" - -[package.extras] -test = ["pytest (>=3.0.0)", "pytest-cov"] +python-versions = "*" [[package]] -category = "main" -description = "A semi hard Cornish cheese, also queries PyPI (PyPI client)" name = "yarg" +version = "0.1.9" +description = "A semi hard Cornish cheese, also queries PyPI (PyPI client)" +category = "main" optional = false python-versions = "*" -version = "0.1.9" [package.dependencies] requests = "*" [[package]] -category = "dev" -description = "Yet Another Terminal Spinner" name = "yaspin" +version = "0.15.0" +description = "Yet Another Terminal Spinner" +category = "dev" optional = false python-versions = "*" -version = "0.15.0" [[package]] -category = "main" -description = "Backport of pathlib-compatible object wrapper for zip files" -marker = "python_version < \"3.8\"" name = "zipp" +version = "3.1.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" optional = false python-versions = ">=3.6" -version = "3.1.0" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] @@ -1660,9 +1580,9 @@ pipfile_deprecated_finder = ["pipreqs", "requirementslib"] requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] -content-hash = "b0253934829c50ca3694ad1b04e96761dd3122140ccf34b251e8b1b91dea4ca6" -lock-version = "1.0" +lock-version = "1.1" python-versions = "^3.6" +content-hash = "363f453324e3010e63a4f5281f2fadbf74d958296d7b61d22758d771b137f546" [metadata.files] appdirs = [ @@ -1698,7 +1618,6 @@ binaryornot = [ {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"}, ] black = [ - {file = "black-20.8b1-py3-none-any.whl", hash = "sha256:70b62ef1527c950db59062cda342ea224d772abdf6adc58b86a45421bab20a6b"}, {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] cached-property = [ @@ -1819,8 +1738,8 @@ falcon = [ {file = "falcon-2.0.0.tar.gz", hash = "sha256:eea593cf466b9c126ce667f6d30503624ef24459f118c75594a69353b6c3d5fc"}, ] flake8 = [ - {file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"}, - {file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"}, + {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, + {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, ] flake8-bugbear = [ {file = "flake8-bugbear-19.8.0.tar.gz", hash = "sha256:d8c466ea79d5020cb20bf9f11cf349026e09517a42264f313d3f6fddb83e0571"}, @@ -2223,6 +2142,8 @@ pyyaml = [ {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, + {file = "PyYAML-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a"}, + {file = "PyYAML-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e"}, {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] regex = [ @@ -2331,19 +2252,28 @@ typed-ast = [ {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f"}, {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298"}, {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d"}, {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, + {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c"}, + {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072"}, + {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91"}, + {file = "typed_ast-1.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d"}, + {file = "typed_ast-1.4.1-cp39-cp39-win32.whl", hash = "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395"}, + {file = "typed_ast-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c"}, {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, ] typer = [ @@ -2376,10 +2306,6 @@ wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] -wheel = [ - {file = "wheel-0.35.1-py2.py3-none-any.whl", hash = "sha256:497add53525d16c173c2c1c733b8f655510e909ea78cc0e29d374243544b77a2"}, - {file = "wheel-0.35.1.tar.gz", hash = "sha256:99a22d87add3f634ff917310a3d87e499f19e663413a52eb9232c447aa646c9f"}, -] yarg = [ {file = "yarg-0.1.9-py2.py3-none-any.whl", hash = "sha256:4f9cebdc00fac946c9bf2783d634e538a71c7d280a4d806d45fd4dc0ef441492"}, {file = "yarg-0.1.9.tar.gz", hash = "sha256:55695bf4d1e3e7f756496c36a69ba32c40d18f821e38f61d028f6049e5e15911"}, diff --git a/pyproject.toml b/pyproject.toml index 840c64916..122906ec7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,7 @@ gitdb2 = "^4.0.2" httpx = "^0.13.3" example_shared_isort_profile = "^0.0.1" example_isort_formatting_plugin = "^0.0.2" +flake8 = "^3.8.4" [tool.poetry.scripts] isort = "isort.main:main" diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 5fab49ffc..bd4c6ffa6 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -135,6 +135,22 @@ def test_show_files(capsys, tmpdir): main.main([str(tmpdir), "--show-files", "--show-config"]) +def test_missing_default_section(tmpdir): + config_file = tmpdir.join(".isort.cfg") + config_file.write( + """ +[settings] +sections=MADEUP +""" + ) + + python_file = tmpdir.join("file.py") + python_file.write("import os") + + with pytest.raises(SystemExit): + main.main([str(python_file)]) + + def test_main(capsys, tmpdir): base_args = [ "-sp", From 0566b4e23a2448c6b71c4f956f2b0006c11ed618 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 6 Dec 2020 22:53:38 -0800 Subject: [PATCH 214/539] Improve reporting of known errors in isort, reachieve 100% test coverage --- isort/exceptions.py | 17 +++++++++++++---- isort/main.py | 17 ++++------------- isort/parse.py | 5 +++++ 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/isort/exceptions.py b/isort/exceptions.py index b98454a2c..a73444ba5 100644 --- a/isort/exceptions.py +++ b/isort/exceptions.py @@ -163,9 +163,18 @@ def __init__(self, unsupported_settings: Dict[str, Dict[str, str]]): class UnsupportedEncoding(ISortError): """Raised when isort encounters an encoding error while trying to read a file""" - def __init__( - self, - filename: Union[str, Path], - ): + def __init__(self, filename: Union[str, Path]): super().__init__(f"Unknown or unsupported encoding in {filename}") self.filename = filename + + +class MissingSection(ISortError): + """Raised when isort encounters an import that matches a section that is not defined""" + + def __init__(self, import_module: str, section: str): + super().__init__( + f"Found {import_module} import while parsing, but {section} was not included " + "in the `sections` setting of your config. Please add it before continuing\n" + "See https://pycqa.github.io/isort/#custom-sections-and-ordering " + "for more info." + ) diff --git a/isort/main.py b/isort/main.py index 5352f64f8..ffc887871 100644 --- a/isort/main.py +++ b/isort/main.py @@ -11,11 +11,11 @@ from warnings import warn from . import __version__, api, sections -from .exceptions import FileSkipped, UnsupportedEncoding +from .exceptions import FileSkipped, ISortError, UnsupportedEncoding from .format import create_terminal_printer from .logo import ASCII_ART from .profiles import profiles -from .settings import DEFAULT_CONFIG, VALID_PY_TARGETS, Config, WrapModes +from .settings import VALID_PY_TARGETS, Config, WrapModes try: from .setuptools_commands import ISortCommand # noqa: F401 @@ -110,17 +110,8 @@ def sort_imports( if config.verbose: warn(f"Encoding not supported for {file_name}") return SortAttempt(incorrectly_sorted, skipped, False) - except KeyError as error: - if error.args[0] not in DEFAULT_CONFIG.sections: - _print_hard_fail(config, offending_file=file_name) - raise - msg = ( - f"Found {error} imports while parsing, but {error} was not included " - "in the `sections` setting of your config. Please add it before continuing\n" - "See https://pycqa.github.io/isort/#custom-sections-and-ordering " - "for more info." - ) - _print_hard_fail(config, message=msg) + except ISortError as error: + _print_hard_fail(config, message=str(error)) sys.exit(os.EX_CONFIG) except Exception: _print_hard_fail(config, offending_file=file_name) diff --git a/isort/parse.py b/isort/parse.py index 819c5cd85..d93f559e2 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -8,6 +8,7 @@ from . import place from .comments import parse as parse_comments from .deprecated.finders import FindersManager +from .exceptions import MissingSection from .settings import DEFAULT_CONFIG, Config if TYPE_CHECKING: @@ -524,6 +525,10 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte " Do you need to define a default section?" ) imports.setdefault("", {"straight": OrderedDict(), "from": OrderedDict()}) + + if placed_module and placed_module not in imports: + raise MissingSection(import_module=module, section=placed_module) + straight_import |= imports[placed_module][type_of_import].get( # type: ignore module, False ) From 347a6473a680acb5849427b9528918440a131118 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 7 Dec 2020 22:11:28 -0800 Subject: [PATCH 215/539] Exit 1 --- isort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index ffc887871..a0aedcd17 100644 --- a/isort/main.py +++ b/isort/main.py @@ -112,7 +112,7 @@ def sort_imports( return SortAttempt(incorrectly_sorted, skipped, False) except ISortError as error: _print_hard_fail(config, message=str(error)) - sys.exit(os.EX_CONFIG) + sys.exit(1) except Exception: _print_hard_fail(config, offending_file=file_name) raise From 576f2a5fe310158ad0039bd7e9d4ece82575cf09 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 7 Dec 2020 23:46:43 -0800 Subject: [PATCH 216/539] Add dedupe_imports setting --- isort/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/isort/settings.py b/isort/settings.py index f0bd14b2d..b4bc50df3 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -204,6 +204,7 @@ class _Config: auto_identify_namespace_packages: bool = True namespace_packages: FrozenSet[str] = frozenset() follow_links: bool = True + dedupe_imports: bool = True def __post_init__(self): py_version = self.py_version From e1741cd540ef9ad2e7b50217cc86daad6d061ce6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 7 Dec 2020 23:49:11 -0800 Subject: [PATCH 217/539] Add dedupe setting to CLI --- isort/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/isort/main.py b/isort/main.py index a0aedcd17..085cfccfa 100644 --- a/isort/main.py +++ b/isort/main.py @@ -660,6 +660,12 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="ext_format", help="Tells isort to format the given files according to an extensions formatting rules.", ) + output_group.add_argument( + "--dedupe-imports", + dest="dedupe_imports", + help="Tells isort to dedupe duplicated imports that are seen at the root across import blocks." + action="store_true" + ) section_group.add_argument( "--sd", From a0fdec008a685c05f37f91ad4c4e181f5b6922f7 Mon Sep 17 00:00:00 2001 From: Quentin Santos Date: Tue, 8 Dec 2020 18:23:13 +0100 Subject: [PATCH 218/539] Fix description of config lookup --- docs/configuration/config_files.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/configuration/config_files.md b/docs/configuration/config_files.md index 7cda9cd35..814b1ec63 100644 --- a/docs/configuration/config_files.md +++ b/docs/configuration/config_files.md @@ -5,6 +5,7 @@ isort supports various standard config formats to allow customizations to be int When applying configurations, isort looks for the closest supported config file, in the order files are listed below. You can manually specify the settings file or path by setting `--settings-path` from the command-line. Otherwise, isort will traverse up to 25 parent directories until it finds a suitable config file. +Note that isort will not leave a git or Mercurial repository (checking for a `.git` or `.hg` directory). As soon as it finds a file, it stops looking. The config file search is done relative to the current directory if `isort .` or a file stream is passed in, or relative to the first path passed in if multiple paths are passed in. isort **never** merges config files together due to the confusion it can cause. From 6c786d7729e208142237c87b7c8d53b5ab50e034 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 8 Dec 2020 23:21:49 -0800 Subject: [PATCH 219/539] Add missing comma --- isort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index 085cfccfa..ea9dc9386 100644 --- a/isort/main.py +++ b/isort/main.py @@ -663,7 +663,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: output_group.add_argument( "--dedupe-imports", dest="dedupe_imports", - help="Tells isort to dedupe duplicated imports that are seen at the root across import blocks." + help="Tells isort to dedupe duplicated imports that are seen at the root across import blocks.", action="store_true" ) From a6c694ba1df58dd6d629800bbd7054d2987a5816 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 8 Dec 2020 23:35:22 -0800 Subject: [PATCH 220/539] Remove comment lines from import identification --- isort/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/isort/core.py b/isort/core.py index 27e615414..d587f3d01 100644 --- a/isort/core.py +++ b/isort/core.py @@ -358,6 +358,7 @@ def write(self, *a, **kw): li for li in parsed_content.in_lines if li and li not in lines_without_imports_set + and not li.lstrip().startswith("#") ) sorted_import_section = output.sorted_imports( From c8afa9a4b1de5e8b2babb19162198dff9a9394e0 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 8 Dec 2020 23:38:46 -0800 Subject: [PATCH 221/539] Ensure line stays < 100 characters --- isort/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index ea9dc9386..632e171f3 100644 --- a/isort/main.py +++ b/isort/main.py @@ -663,8 +663,9 @@ def _build_arg_parser() -> argparse.ArgumentParser: output_group.add_argument( "--dedupe-imports", dest="dedupe_imports", - help="Tells isort to dedupe duplicated imports that are seen at the root across import blocks.", - action="store_true" + help="Tells isort to dedupe duplicated imports that are seen at the root across " + "import blocks.", + action="store_true", ) section_group.add_argument( From d1ce7d3f0c72cb3a84c512b79e2d33eddb929739 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 8 Dec 2020 23:38:55 -0800 Subject: [PATCH 222/539] Ensure line stays < 100 characters --- isort/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/isort/core.py b/isort/core.py index d587f3d01..93102e856 100644 --- a/isort/core.py +++ b/isort/core.py @@ -357,7 +357,8 @@ def write(self, *a, **kw): all_imports.extend( li for li in parsed_content.in_lines - if li and li not in lines_without_imports_set + if li + and li not in lines_without_imports_set and not li.lstrip().startswith("#") ) From 8647eb08fb52466620e3392969ab56cf79426a35 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 9 Dec 2020 23:18:08 -0800 Subject: [PATCH 223/539] Add test for #1604: allow toggling section header in indented imports --- tests/unit/test_ticketed_features.py | 48 ++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 7871e38c5..03998a474 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -923,3 +923,51 @@ def one(): import os """ ) + + +def test_indented_import_headings_issue_1604(): + """Test to ensure it is possible to toggle import headings on indented import sections + See: https://github.com/PyCQA/isort/issues/1604 + """ + assert ( + isort.code( + """ +import numpy as np + + +def function(): + import numpy as np +""", + import_heading_thirdparty="External imports", + ) + == """ +# External imports +import numpy as np + + +def function(): + # External imports + import numpy as np +""" + ) + assert ( + isort.code( + """ +import numpy as np + + +def function(): + import numpy as np +""", + import_heading_thirdparty="External imports", + indented_import_headings=False, + ) + == """ +# External imports +import numpy as np + + +def function(): + import numpy as np +""" + ) From 32dee154ce0eef0897de005101bed6d9175c8ee8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 9 Dec 2020 23:18:27 -0800 Subject: [PATCH 224/539] Add setting for #1604: allow toggling section header in indented imports --- isort/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/isort/settings.py b/isort/settings.py index b4bc50df3..8ef6dfa86 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -205,6 +205,7 @@ class _Config: namespace_packages: FrozenSet[str] = frozenset() follow_links: bool = True dedupe_imports: bool = True + indented_import_headings: bool = True def __post_init__(self): py_version = self.py_version From 1f52edd2ebd8e6061edfdb208d5514e5d51a5da0 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 10 Dec 2020 23:19:11 -0800 Subject: [PATCH 225/539] Dynamically toggle import_headings as configured in settings --- isort/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/isort/core.py b/isort/core.py index 93102e856..e53a8b87e 100644 --- a/isort/core.py +++ b/isort/core.py @@ -441,6 +441,7 @@ def _indented_config(config: Config, indent: str): line_length=max(config.line_length - len(indent), 0), wrap_length=max(config.wrap_length - len(indent), 0), lines_after_imports=1, + import_headings=config.import_headings if config.indented_import_headings else {}, ) From a28f6a155b2f52af5f38e5a14ef8c0a6a256ab94 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 10 Dec 2020 23:20:12 -0800 Subject: [PATCH 226/539] Resolved #1604: Added to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e81a6877..476038fe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1575: Support for automatically fixing mixed indentation of import sections. - Implemented #1582: Added a CLI option for skipping symlinks. - Implemented #1603: Support for disabling float_to_top from the command line. + - Implemented #1604: Allow toggling section comments on and off for indented import sections. ### 5.6.4 October 12, 2020 - Fixed #1556: Empty line added between imports that should be skipped. From d9c3f88c3852c2ef9e327c216507e218913a32e4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 11 Dec 2020 23:56:57 -0800 Subject: [PATCH 227/539] Mark 5.7.0 for december release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 476038fe3..ac6d209c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). -### 5.7.0 TBD +### 5.7.0 December TBD - Implemented #1596: Provide ways for extension formatting and file paths to be specified when using streaming input from CLI. - Implemented #1583: Ability to output and diff within a single API call to `isort.file`. - Implemented #1562, #1592 & #1593: Better more useful fatal error messages. From bd170de3c2a680570ab1e83edadf82944909bd25 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 12 Dec 2020 23:52:13 -0800 Subject: [PATCH 228/539] Fix datadog integration test --- tests/integration/test_projects_using_isort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 2a2abe398..6818addf5 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -126,7 +126,7 @@ def test_attrs(tmpdir): def test_datadog_integrations_core(tmpdir): git_clone("https://github.com/DataDog/integrations-core.git", tmpdir) - run_isort([str(tmpdir)]) + run_isort([str(tmpdir), '--skip', 'docs']) def test_pyramid(tmpdir): From 6b65b278960be551b972db57bd47607af02be0bd Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 13 Dec 2020 00:37:24 -0800 Subject: [PATCH 229/539] linting --- tests/integration/test_projects_using_isort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 6818addf5..2258cbfae 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -126,7 +126,7 @@ def test_attrs(tmpdir): def test_datadog_integrations_core(tmpdir): git_clone("https://github.com/DataDog/integrations-core.git", tmpdir) - run_isort([str(tmpdir), '--skip', 'docs']) + run_isort([str(tmpdir), "--skip", "docs"]) def test_pyramid(tmpdir): From 71b058d6751d26bec36223970494da075df5a1b8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 13 Dec 2020 18:24:50 -0800 Subject: [PATCH 230/539] Initial work to pull out just import identification from parse --- isort/identify.py | 213 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 isort/identify.py diff --git a/isort/identify.py b/isort/identify.py new file mode 100644 index 000000000..98e75b310 --- /dev/null +++ b/isort/identify.py @@ -0,0 +1,213 @@ +"""""" +from collections import OrderedDict, defaultdict +from functools import partial +from itertools import chain +from typing import TYPE_CHECKING, Any, Dict, List, NamedTuple, Optional, Tuple +from warnings import warn + +from . import place +from .comments import parse as parse_comments +from .deprecated.finders import FindersManager +from .exceptions import MissingSection +from .settings import DEFAULT_CONFIG, Config + + +from isort.parse import _infer_line_separator, _normalize_line, _strip_syntax, skip_line + + +def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: + """If the current line is an import line it will return its type (from or straight)""" + if line.startswith(("import ", "cimport ")): + return "straight" + if line.startswith("from "): + return "from" + return None + + +class ImportIdentified(NamedTuple): + from_file: Optional[Path] + line: int + + +def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[ImportIdentified]: + """Parses a python file taking out and categorizing imports.""" + in_quote = "" + + indexed_input = enumerate(input_stream) + for index, line in indexed_input + statement_index = index + (skipping_line, in_quote) = skip_line( + line, in_quote=in_quote, index=index, section_comments=config.section_comments + ) + + if skipping_line: + continue + + line, *end_of_line_comment = line.split("#", 1) + if ";" in line: + statements = [line.strip() for line in line.split(";")] + else: + statements = [line] + if end_of_line_comment: + statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" + + for statement in statements: + line, raw_line = _normalize_line(statement) + type_of_import = import_type(line, config) or "" + if not type_of_import: + out_lines.append(raw_line) + continue + + import_string, _ = parse_comments(line) + line_parts = [part for part in _strip_syntax(import_string).strip().split(" ") if part] + + if "(" in line.split("#", 1)[0]: + while not line.split("#")[0].strip().endswith(")"): + try: + index, next_line = next(indexed_input) + except StopIteration: + break + + line, _ = parse_comments(next_line) + stripped_line = _strip_syntax(line).strip() + import_string += line_separator + line + else: + while line.strip().endswith("\\"): + index, next_line = next(indexed_input) + line, _ = parse_comments(next_line) + + # Still need to check for parentheses after an escaped line + if ( + "(" in line.split("#")[0] + and ")" not in line.split("#")[0] + ): + stripped_line = _strip_syntax(line).strip() + import_string += line_separator + line + + while not line.split("#")[0].strip().endswith(")"): + try: + index, next_line = next(indexed_input) + except StopIteration: + break + line, _ = parse_comments(next_line) + stripped_line = _strip_syntax(line).strip() + import_string += line_separator + line + + stripped_line = _strip_syntax(line).strip() + if import_string.strip().endswith( + (" import", " cimport") + ) or line.strip().startswith(("import ", "cimport ")): + import_string += line_separator + line + else: + import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip() + + if type_of_import == "from": + cimports: bool + import_string = ( + import_string.replace("import(", "import (") + .replace("\\", " ") + .replace("\n", " ") + ) + if " cimport " in import_string: + parts = import_string.split(" cimport ") + cimports = True + + else: + parts = import_string.split(" import ") + cimports = False + + from_import = parts[0].split(" ") + import_string = (" cimport " if cimports else " import ").join( + [from_import[0] + " " + "".join(from_import[1:])] + parts[1:] + ) + + just_imports = [ + item.replace("{|", "{ ").replace("|}", " }") + for item in _strip_syntax(import_string).split() + ] + + direct_imports = just_imports[1:] + straight_import = True + top_level_module = "" + if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): + straight_import = False + while "as" in just_imports: + nested_module = None + as_index = just_imports.index("as") + if type_of_import == "from": + nested_module = just_imports[as_index - 1] + top_level_module = just_imports[0] + module = top_level_module + "." + nested_module + as_name = just_imports[as_index + 1] + direct_imports.remove(nested_module) + direct_imports.remove(as_name) + direct_imports.remove("as") + if nested_module == as_name and config.remove_redundant_aliases: + pass + elif as_name not in as_map["from"][module]: + as_map["from"][module].append(as_name) + + full_name = f"{nested_module} as {as_name}" + else: + module = just_imports[as_index - 1] + as_name = just_imports[as_index + 1] + if module == as_name and config.remove_redundant_aliases: + pass + elif as_name not in as_map["straight"][module]: + as_map["straight"][module].append(as_name) + + del just_imports[as_index : as_index + 2] + + if type_of_import == "from": + import_from = just_imports.pop(0) + placed_module = finder(import_from) + 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} --" + " Do you need to define a default section?" + ) + root = imports[placed_module][type_of_import] # type: ignore + + if import_from not in root: + root[import_from] = OrderedDict( + (module, module in direct_imports) for module in just_imports + ) + else: + root[import_from].update( + (module, root[import_from].get(module, False) or module in direct_imports) + for module in just_imports + ) + + else: + + for module in just_imports: + + placed_module = finder(module) + 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} --" + " Do you need to define a default section?" + ) + imports.setdefault("", {"straight": OrderedDict(), "from": OrderedDict()}) + + if placed_module and placed_module not in imports: + raise MissingSection(import_module=module, section=placed_module) + + straight_import |= imports[placed_module][type_of_import].get( # type: ignore + module, False + ) + imports[placed_module][type_of_import][module] = straight_import # type: ignore From 6fe8f0ce4a5c0c8e9a72e335693a5e5ae7c8152e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 13 Dec 2020 18:26:10 -0800 Subject: [PATCH 231/539] Remove pieces of parsing unrelated to import identification --- isort/identify.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 98e75b310..09383aa3a 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -188,7 +188,6 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I else: for module in just_imports: - placed_module = finder(module) if config.verbose and not config.only_modified: print(f"else-type place_module for {module} returned {placed_module}") @@ -203,10 +202,6 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I " Do you need to define a default section?" ) imports.setdefault("", {"straight": OrderedDict(), "from": OrderedDict()}) - - if placed_module and placed_module not in imports: - raise MissingSection(import_module=module, section=placed_module) - straight_import |= imports[placed_module][type_of_import].get( # type: ignore module, False ) From 8624101427f08b1ea6a5368040fa62b921ee224b Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Mon, 14 Dec 2020 14:53:12 -0800 Subject: [PATCH 232/539] Set pre-commit language_version to python3 For details on how pre-commit chooses a default language version, see: https://pre-commit.com/#overriding-language-version > For each language, they default to using the system installed language On some platforms this could be Python 2. As isort is Python-3-only, this should be enforced in the pre-commit hook. As this isn't currently listed, some projects have been forced to override it. --- .pre-commit-hooks.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 0027e49df..160016181 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -3,5 +3,6 @@ entry: isort require_serial: true language: python + language_version: python3 types: [python] args: ['--filter-files'] From d1d1d9d83b3bd31d0c12b5798fb29564df4f6931 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Mon, 14 Dec 2020 15:07:41 -0800 Subject: [PATCH 233/539] Simplify .pre-commit-config.yaml Run isort on all files to avoid listing them. --- .pre-commit-config.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ff5151ab..586a5edda 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,3 @@ repos: rev: 5.5.2 hooks: - id: isort - files: 'isort/.*' - - id: isort - files: 'tests/.*' From 10ef221e74d2ee4bc1ab8a8cfb7261d886e78263 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 14 Dec 2020 23:34:15 -0800 Subject: [PATCH 234/539] Support for yielding imports for straight forward case --- isort/identify.py | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 09383aa3a..3a7633a50 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -7,7 +7,6 @@ from . import place from .comments import parse as parse_comments -from .deprecated.finders import FindersManager from .exceptions import MissingSection from .settings import DEFAULT_CONFIG, Config @@ -25,9 +24,12 @@ def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: class ImportIdentified(NamedTuple): - from_file: Optional[Path] line: int - + module: str + import_type: str + alias: Optional[str] = None + src: Optional[Path] = None + def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[ImportIdentified]: """Parses a python file taking out and categorizing imports.""" @@ -186,23 +188,5 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I ) else: - for module in just_imports: - placed_module = finder(module) - 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} --" - " Do you need to define a default section?" - ) - imports.setdefault("", {"straight": OrderedDict(), "from": OrderedDict()}) - straight_import |= imports[placed_module][type_of_import].get( # type: ignore - module, False - ) - imports[placed_module][type_of_import][module] = straight_import # type: ignore + yield ImportIdentified(index, module, import_type) From 0ace9b3bcb3ff4f3f6b0c56f4ed150adbcabd3be Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 14 Dec 2020 23:44:59 -0800 Subject: [PATCH 235/539] Initial code cleanup and linting of identify module --- isort/identify.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 3a7633a50..de07dccf4 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -1,17 +1,13 @@ """""" -from collections import OrderedDict, defaultdict -from functools import partial -from itertools import chain -from typing import TYPE_CHECKING, Any, Dict, List, NamedTuple, Optional, Tuple +from typing import NamedTuple, Optional from warnings import warn - -from . import place from .comments import parse as parse_comments -from .exceptions import MissingSection from .settings import DEFAULT_CONFIG, Config +from pathlib import Path +from isort.parse import _normalize_line, _strip_syntax, skip_line -from isort.parse import _infer_line_separator, _normalize_line, _strip_syntax, skip_line +from typing import TextIO def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: @@ -36,7 +32,7 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I in_quote = "" indexed_input = enumerate(input_stream) - for index, line in indexed_input + for index, line in indexed_input: statement_index = index (skipping_line, in_quote) = skip_line( line, in_quote=in_quote, index=index, section_comments=config.section_comments @@ -129,10 +125,8 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I ] direct_imports = just_imports[1:] - straight_import = True top_level_module = "" if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): - straight_import = False while "as" in just_imports: nested_module = None as_index = just_imports.index("as") From 4eaec9fee426b06a5ecb008ae6c0c96c2c42c8f7 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 15 Dec 2020 20:49:37 -0800 Subject: [PATCH 236/539] Remove parsing aspects not needed for fast identification --- isort/identify.py | 48 ++++++++--------------------------------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index de07dccf4..3da348e64 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -1,13 +1,12 @@ """""" from typing import NamedTuple, Optional -from warnings import warn from .comments import parse as parse_comments from .settings import DEFAULT_CONFIG, Config from pathlib import Path from isort.parse import _normalize_line, _strip_syntax, skip_line -from typing import TextIO +from typing import TextIO, Iterator def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: @@ -25,7 +24,7 @@ class ImportIdentified(NamedTuple): import_type: str alias: Optional[str] = None src: Optional[Path] = None - + def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[ImportIdentified]: """Parses a python file taking out and categorizing imports.""" @@ -33,7 +32,6 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I indexed_input = enumerate(input_stream) for index, line in indexed_input: - statement_index = index (skipping_line, in_quote) = skip_line( line, in_quote=in_quote, index=index, section_comments=config.section_comments ) @@ -53,11 +51,9 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I line, raw_line = _normalize_line(statement) type_of_import = import_type(line, config) or "" if not type_of_import: - out_lines.append(raw_line) continue import_string, _ = parse_comments(line) - line_parts = [part for part in _strip_syntax(import_string).strip().split(" ") if part] if "(" in line.split("#", 1)[0]: while not line.split("#")[0].strip().endswith(")"): @@ -67,8 +63,7 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I break line, _ = parse_comments(next_line) - stripped_line = _strip_syntax(line).strip() - import_string += line_separator + line + import_string += "\n" + line else: while line.strip().endswith("\\"): index, next_line = next(indexed_input) @@ -79,8 +74,7 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I "(" in line.split("#")[0] and ")" not in line.split("#")[0] ): - stripped_line = _strip_syntax(line).strip() - import_string += line_separator + line + import_string += "\n" + line while not line.split("#")[0].strip().endswith(")"): try: @@ -88,14 +82,12 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I except StopIteration: break line, _ = parse_comments(next_line) - stripped_line = _strip_syntax(line).strip() - import_string += line_separator + line + import_string += "\n" + line - stripped_line = _strip_syntax(line).strip() if import_string.strip().endswith( (" import", " cimport") ) or line.strip().startswith(("import ", "cimport ")): - import_string += line_separator + line + import_string += "\n" + line else: import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip() @@ -143,7 +135,6 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I elif as_name not in as_map["from"][module]: as_map["from"][module].append(as_name) - full_name = f"{nested_module} as {as_name}" else: module = just_imports[as_index - 1] as_name = just_imports[as_index + 1] @@ -156,31 +147,8 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I if type_of_import == "from": import_from = just_imports.pop(0) - placed_module = finder(import_from) - 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} --" - " Do you need to define a default section?" - ) - root = imports[placed_module][type_of_import] # type: ignore - - if import_from not in root: - root[import_from] = OrderedDict( - (module, module in direct_imports) for module in just_imports - ) - else: - root[import_from].update( - (module, root[import_from].get(module, False) or module in direct_imports) - for module in just_imports - ) - + for import_part in just_imports: + yield ImportIdentified(index, f"{import_from}.{import_part}", import_type) else: for module in just_imports: yield ImportIdentified(index, module, import_type) From fffe8bf4174459b335617fb0d4ad72dac2ff5708 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 15 Dec 2020 21:27:07 -0800 Subject: [PATCH 237/539] Simplify output signature --- isort/identify.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 3da348e64..745489eb1 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -21,7 +21,7 @@ def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: class ImportIdentified(NamedTuple): line: int module: str - import_type: str + attribute: str = None alias: Optional[str] = None src: Optional[Path] = None @@ -146,9 +146,9 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I del just_imports[as_index : as_index + 2] if type_of_import == "from": - import_from = just_imports.pop(0) - for import_part in just_imports: - yield ImportIdentified(index, f"{import_from}.{import_part}", import_type) + module = just_imports.pop(0) + for attribute in just_imports: + yield ImportIdentified(index, module, attribute) else: for module in just_imports: - yield ImportIdentified(index, module, import_type) + yield ImportIdentified(index, module) From 06d1bddca5b754eb1bcc9f0c3358271f7e8df37e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 15 Dec 2020 21:48:07 -0800 Subject: [PATCH 238/539] Return aliases --- isort/identify.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 745489eb1..74bb0a3e1 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -24,6 +24,7 @@ class ImportIdentified(NamedTuple): attribute: str = None alias: Optional[str] = None src: Optional[Path] = None + cimport: bool = False def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[ImportIdentified]: @@ -120,20 +121,25 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I top_level_module = "" if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): while "as" in just_imports: - nested_module = None + attribute = None as_index = just_imports.index("as") if type_of_import == "from": - nested_module = just_imports[as_index - 1] + attribute = just_imports[as_index - 1] top_level_module = just_imports[0] - module = top_level_module + "." + nested_module - as_name = just_imports[as_index + 1] - direct_imports.remove(nested_module) - direct_imports.remove(as_name) + module = top_level_module + "." + attribute + alias = just_imports[as_index + 1] + direct_imports.remove(attribute) + direct_imports.remove(alias) direct_imports.remove("as") - if nested_module == as_name and config.remove_redundant_aliases: + if attribute == alias and config.remove_redundant_aliases: pass - elif as_name not in as_map["from"][module]: - as_map["from"][module].append(as_name) + else: + yield ImportIdentified( + index, + top_level_module, + attribute, + alias=alias + ) else: module = just_imports[as_index - 1] From e65a2b2c663154fcd14cce83691dffd7ecb58b1d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 16 Dec 2020 23:40:46 -0800 Subject: [PATCH 239/539] clean up alias import identification --- isort/identify.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 74bb0a3e1..7d2d4a567 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -143,18 +143,15 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I else: module = just_imports[as_index - 1] - as_name = just_imports[as_index + 1] - if module == as_name and config.remove_redundant_aliases: - pass - elif as_name not in as_map["straight"][module]: - as_map["straight"][module].append(as_name) - - del just_imports[as_index : as_index + 2] + alias = just_imports[as_index + 1] + if not (module == alias and config.remove_redundant_aliases): + yield ImportIdentified(index, module, alias) - if type_of_import == "from": - module = just_imports.pop(0) - for attribute in just_imports: - yield ImportIdentified(index, module, attribute) else: - for module in just_imports: - yield ImportIdentified(index, module) + if type_of_import == "from": + module = just_imports.pop(0) + for attribute in just_imports: + yield ImportIdentified(index, module, attribute) + else: + for module in just_imports: + yield ImportIdentified(index, module) From 334577d786f8fc8a839800d46cae3ac21cff7dc1 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 16 Dec 2020 23:53:56 -0800 Subject: [PATCH 240/539] Add quick cimport identification --- isort/identify.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 7d2d4a567..fa41309a1 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -19,7 +19,7 @@ def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: class ImportIdentified(NamedTuple): - line: int + line_number: int module: str attribute: str = None alias: Optional[str] = None @@ -55,6 +55,8 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I continue import_string, _ = parse_comments(line) + normalized_import_string = import_string.replace("import(", "import (").replace("\\", " ").replace("\n", " ") + cimports: bool = " cimport " in normalized_import_string or normalized_import_string.startswith("cimport") if "(" in line.split("#", 1)[0]: while not line.split("#")[0].strip().endswith(")"): @@ -93,19 +95,12 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip() if type_of_import == "from": - cimports: bool import_string = ( import_string.replace("import(", "import (") .replace("\\", " ") .replace("\n", " ") ) - if " cimport " in import_string: - parts = import_string.split(" cimport ") - cimports = True - - else: - parts = import_string.split(" import ") - cimports = False + parts = import_string.split(" cimport " if cimports else " import ") from_import = parts[0].split(" ") import_string = (" cimport " if cimports else " import ").join( @@ -138,14 +133,15 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I index, top_level_module, attribute, - alias=alias + alias=alias, + cimport=cimports, ) else: module = just_imports[as_index - 1] alias = just_imports[as_index + 1] if not (module == alias and config.remove_redundant_aliases): - yield ImportIdentified(index, module, alias) + yield ImportIdentified(index, module, alias, cimports) else: if type_of_import == "from": @@ -154,4 +150,4 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I yield ImportIdentified(index, module, attribute) else: for module in just_imports: - yield ImportIdentified(index, module) + yield ImportIdentified(index, module, cimport=cimports) From 333eaaa720f2c6b51d7744c0f283935324cfab59 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 16 Dec 2020 23:56:24 -0800 Subject: [PATCH 241/539] Autoformatting --- isort/identify.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index fa41309a1..6672ccf1e 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -1,12 +1,11 @@ """""" -from typing import NamedTuple, Optional -from .comments import parse as parse_comments -from .settings import DEFAULT_CONFIG, Config from pathlib import Path +from typing import Iterator, NamedTuple, Optional, TextIO from isort.parse import _normalize_line, _strip_syntax, skip_line -from typing import TextIO, Iterator +from .comments import parse as parse_comments +from .settings import DEFAULT_CONFIG, Config def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: @@ -55,8 +54,13 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I continue import_string, _ = parse_comments(line) - normalized_import_string = import_string.replace("import(", "import (").replace("\\", " ").replace("\n", " ") - cimports: bool = " cimport " in normalized_import_string or normalized_import_string.startswith("cimport") + normalized_import_string = ( + import_string.replace("import(", "import (").replace("\\", " ").replace("\n", " ") + ) + cimports: bool = ( + " cimport " in normalized_import_string + or normalized_import_string.startswith("cimport") + ) if "(" in line.split("#", 1)[0]: while not line.split("#")[0].strip().endswith(")"): @@ -73,10 +77,7 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I line, _ = parse_comments(next_line) # Still need to check for parentheses after an escaped line - if ( - "(" in line.split("#")[0] - and ")" not in line.split("#")[0] - ): + if "(" in line.split("#")[0] and ")" not in line.split("#")[0]: import_string += "\n" + line while not line.split("#")[0].strip().endswith(")"): From 31d35705c9e49ae5146fdfb6cfc36b97f350e602 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 16 Dec 2020 23:57:05 -0800 Subject: [PATCH 242/539] Autoformatting --- isort/identify.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 6672ccf1e..3c610c9d2 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -20,10 +20,10 @@ def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: class ImportIdentified(NamedTuple): line_number: int module: str - attribute: str = None + attribute: Optional[str] = None alias: Optional[str] = None src: Optional[Path] = None - cimport: bool = False + cimport: Optional[bool] = False def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[ImportIdentified]: From 76be45d844cf553040211aa2a254ae8baad9441b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 16 Dec 2020 23:59:00 -0800 Subject: [PATCH 243/539] Fix type signatures --- isort/identify.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 3c610c9d2..280e181c2 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -23,7 +23,7 @@ class ImportIdentified(NamedTuple): attribute: Optional[str] = None alias: Optional[str] = None src: Optional[Path] = None - cimport: Optional[bool] = False + cimport: bool = False def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[ImportIdentified]: @@ -142,7 +142,7 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I module = just_imports[as_index - 1] alias = just_imports[as_index + 1] if not (module == alias and config.remove_redundant_aliases): - yield ImportIdentified(index, module, alias, cimports) + yield ImportIdentified(index, module, alias, cimport=cimports) else: if type_of_import == "from": From 9f23d03f575694fd136f013377cc228c395c1ea2 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Dec 2020 21:54:20 -0800 Subject: [PATCH 244/539] Refactoring of direct imports --- isort/identify.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 280e181c2..af3bd45da 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -10,9 +10,9 @@ def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: """If the current line is an import line it will return its type (from or straight)""" - if line.startswith(("import ", "cimport ")): + if line.lstrip().startswith(("import ", "cimport ")): return "straight" - if line.startswith("from "): + if line.lstrip().startswith("from "): return "from" return None @@ -127,6 +127,7 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I direct_imports.remove(attribute) direct_imports.remove(alias) direct_imports.remove("as") + just_imports[1:] = direct_imports if attribute == alias and config.remove_redundant_aliases: pass else: From 151844cc7078fd53c7a7b95f8b4d5de850a114ab Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Dec 2020 21:54:54 -0800 Subject: [PATCH 245/539] Rename identified import class --- isort/identify.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index af3bd45da..22ce4b28d 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -17,7 +17,7 @@ def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: return None -class ImportIdentified(NamedTuple): +class IdentifiedImport(NamedTuple): line_number: int module: str attribute: Optional[str] = None @@ -26,7 +26,7 @@ class ImportIdentified(NamedTuple): cimport: bool = False -def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[ImportIdentified]: +def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[IdentifiedImport]: """Parses a python file taking out and categorizing imports.""" in_quote = "" @@ -131,7 +131,7 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I if attribute == alias and config.remove_redundant_aliases: pass else: - yield ImportIdentified( + yield IdentifiedImport( index, top_level_module, attribute, @@ -143,13 +143,13 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I module = just_imports[as_index - 1] alias = just_imports[as_index + 1] if not (module == alias and config.remove_redundant_aliases): - yield ImportIdentified(index, module, alias, cimport=cimports) + yield IdentifiedImport(index, module, alias, cimport=cimports) else: if type_of_import == "from": module = just_imports.pop(0) for attribute in just_imports: - yield ImportIdentified(index, module, attribute) + yield IdentifiedImport(index, module, attribute) else: for module in just_imports: - yield ImportIdentified(index, module, cimport=cimports) + yield IdentifiedImport(index, module, cimport=cimports) From 24593c0d6acfc9008da99bf85600f8e10ba35339 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Dec 2020 21:59:29 -0800 Subject: [PATCH 246/539] Add indented classifier to imports --- isort/identify.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 22ce4b28d..1813cb3a0 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -19,6 +19,7 @@ def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: class IdentifiedImport(NamedTuple): line_number: int + indented: bool module: str attribute: Optional[str] = None alias: Optional[str] = None @@ -40,10 +41,8 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I continue line, *end_of_line_comment = line.split("#", 1) - if ";" in line: - statements = [line.strip() for line in line.split(";")] - else: - statements = [line] + indented = line.startswith(" ") or line.startswith("\n") + statements = [line.strip() for line in line.split(";")] if end_of_line_comment: statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" @@ -133,6 +132,7 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I else: yield IdentifiedImport( index, + indented, top_level_module, attribute, alias=alias, @@ -143,13 +143,13 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I module = just_imports[as_index - 1] alias = just_imports[as_index + 1] if not (module == alias and config.remove_redundant_aliases): - yield IdentifiedImport(index, module, alias, cimport=cimports) + yield IdentifiedImport(index, indented, module, alias, cimport=cimports) else: if type_of_import == "from": module = just_imports.pop(0) for attribute in just_imports: - yield IdentifiedImport(index, module, attribute) + yield IdentifiedImport(index, indented, module, attribute) else: for module in just_imports: - yield IdentifiedImport(index, module, cimport=cimports) + yield IdentifiedImport(index, indented, module, cimport=cimports) From 7bd317143c36ca28860daf71eb9a65b86c57d368 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Dec 2020 22:02:24 -0800 Subject: [PATCH 247/539] Include file path --- isort/identify.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 1813cb3a0..ebb1fd3e6 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -23,11 +23,11 @@ class IdentifiedImport(NamedTuple): module: str attribute: Optional[str] = None alias: Optional[str] = None - src: Optional[Path] = None cimport: bool = False + file_path: Optional[Path] = None -def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[IdentifiedImport]: +def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Optional[Path]=None) -> Iterator[IdentifiedImport]: """Parses a python file taking out and categorizing imports.""" in_quote = "" @@ -137,19 +137,20 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG) -> Iterator[I attribute, alias=alias, cimport=cimports, + file_path=file_path, ) else: module = just_imports[as_index - 1] alias = just_imports[as_index + 1] if not (module == alias and config.remove_redundant_aliases): - yield IdentifiedImport(index, indented, module, alias, cimport=cimports) + yield IdentifiedImport(index, indented, module, alias, cimport=cimports, file_path=file_path,) else: if type_of_import == "from": module = just_imports.pop(0) for attribute in just_imports: - yield IdentifiedImport(index, indented, module, attribute) + yield IdentifiedImport(index, indented, module, attribute, file_path=file_path,) else: for module in just_imports: - yield IdentifiedImport(index, indented, module, cimport=cimports) + yield IdentifiedImport(index, indented, module, cimport=cimports, file_path=file_path,) From 53ff355317901406de3544cdb943269f57eb74a1 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Dec 2020 22:02:41 -0800 Subject: [PATCH 248/539] Reformatted with black+isort --- isort/identify.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index ebb1fd3e6..3e0779a06 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -27,7 +27,9 @@ class IdentifiedImport(NamedTuple): file_path: Optional[Path] = None -def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Optional[Path]=None) -> Iterator[IdentifiedImport]: +def imports( + input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None +) -> Iterator[IdentifiedImport]: """Parses a python file taking out and categorizing imports.""" in_quote = "" @@ -144,13 +146,32 @@ def imports(input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Op module = just_imports[as_index - 1] alias = just_imports[as_index + 1] if not (module == alias and config.remove_redundant_aliases): - yield IdentifiedImport(index, indented, module, alias, cimport=cimports, file_path=file_path,) + yield IdentifiedImport( + index, + indented, + module, + alias, + cimport=cimports, + file_path=file_path, + ) else: if type_of_import == "from": module = just_imports.pop(0) for attribute in just_imports: - yield IdentifiedImport(index, indented, module, attribute, file_path=file_path,) + yield IdentifiedImport( + index, + indented, + module, + attribute, + file_path=file_path, + ) else: for module in just_imports: - yield IdentifiedImport(index, indented, module, cimport=cimports, file_path=file_path,) + yield IdentifiedImport( + index, + indented, + module, + cimport=cimports, + file_path=file_path, + ) From 9cef6bdcf5b85fa477e1b0bd4fc55968bc62c5ac Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Dec 2020 22:07:04 -0800 Subject: [PATCH 249/539] Reuse identified import logic --- isort/identify.py | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 3e0779a06..77954aa9b 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -5,6 +5,7 @@ from isort.parse import _normalize_line, _strip_syntax, skip_line from .comments import parse as parse_comments +from functools import partial from .settings import DEFAULT_CONFIG, Config @@ -43,7 +44,7 @@ def imports( continue line, *end_of_line_comment = line.split("#", 1) - indented = line.startswith(" ") or line.startswith("\n") + identified_import = partial(IdentifiedImport, index, line.startswith(" ") or line.startswith("\n"), file_path=file_path) statements = [line.strip() for line in line.split(";")] if end_of_line_comment: statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" @@ -132,46 +133,34 @@ def imports( if attribute == alias and config.remove_redundant_aliases: pass else: - yield IdentifiedImport( - index, - indented, + yield identified_import( top_level_module, attribute, alias=alias, - cimport=cimports, - file_path=file_path, + cimport=cimports ) else: module = just_imports[as_index - 1] alias = just_imports[as_index + 1] if not (module == alias and config.remove_redundant_aliases): - yield IdentifiedImport( - index, - indented, + yield identified_import( module, alias, - cimport=cimports, - file_path=file_path, + cimport=cimports ) else: if type_of_import == "from": module = just_imports.pop(0) for attribute in just_imports: - yield IdentifiedImport( - index, - indented, + yield identified_import( module, - attribute, - file_path=file_path, + attribute ) else: for module in just_imports: - yield IdentifiedImport( - index, - indented, + yield identified_import( module, - cimport=cimports, - file_path=file_path, + cimport=cimports ) From 1e4270aa548fbebd5f3b4a5a8eb513f604f1165b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Dec 2020 22:07:19 -0800 Subject: [PATCH 250/539] black+isort formatting --- isort/identify.py | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 77954aa9b..a9281a0d3 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -1,11 +1,11 @@ """""" +from functools import partial from pathlib import Path from typing import Iterator, NamedTuple, Optional, TextIO from isort.parse import _normalize_line, _strip_syntax, skip_line from .comments import parse as parse_comments -from functools import partial from .settings import DEFAULT_CONFIG, Config @@ -44,7 +44,12 @@ def imports( continue line, *end_of_line_comment = line.split("#", 1) - identified_import = partial(IdentifiedImport, index, line.startswith(" ") or line.startswith("\n"), file_path=file_path) + identified_import = partial( + IdentifiedImport, + index, + line.startswith(" ") or line.startswith("\n"), + file_path=file_path, + ) statements = [line.strip() for line in line.split(";")] if end_of_line_comment: statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" @@ -134,33 +139,20 @@ def imports( pass else: yield identified_import( - top_level_module, - attribute, - alias=alias, - cimport=cimports + top_level_module, attribute, alias=alias, cimport=cimports ) else: module = just_imports[as_index - 1] alias = just_imports[as_index + 1] if not (module == alias and config.remove_redundant_aliases): - yield identified_import( - module, - alias, - cimport=cimports - ) + yield identified_import(module, alias, cimport=cimports) else: if type_of_import == "from": module = just_imports.pop(0) for attribute in just_imports: - yield identified_import( - module, - attribute - ) + yield identified_import(module, attribute) else: for module in just_imports: - yield identified_import( - module, - cimport=cimports - ) + yield identified_import(module, cimport=cimports) From 9a706c5caec184d939b621a1cef81269f064cd4e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Dec 2020 22:15:11 -0800 Subject: [PATCH 251/539] Include statement --- isort/identify.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index a9281a0d3..94ee56f64 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -21,6 +21,7 @@ def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: class IdentifiedImport(NamedTuple): line_number: int indented: bool + statement: str module: str attribute: Optional[str] = None alias: Optional[str] = None @@ -44,12 +45,6 @@ def imports( continue line, *end_of_line_comment = line.split("#", 1) - identified_import = partial( - IdentifiedImport, - index, - line.startswith(" ") or line.startswith("\n"), - file_path=file_path, - ) statements = [line.strip() for line in line.split(";")] if end_of_line_comment: statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" @@ -68,6 +63,14 @@ def imports( " cimport " in normalized_import_string or normalized_import_string.startswith("cimport") ) + identified_import = partial( + IdentifiedImport, + index, + line.startswith(" ") or line.startswith("\n"), + statement, + cimport=cimports, + file_path=file_path, + ) if "(" in line.split("#", 1)[0]: while not line.split("#")[0].strip().endswith(")"): @@ -139,20 +142,20 @@ def imports( pass else: yield identified_import( - top_level_module, attribute, alias=alias, cimport=cimports + top_level_module, attribute, alias=alias ) else: module = just_imports[as_index - 1] alias = just_imports[as_index + 1] if not (module == alias and config.remove_redundant_aliases): - yield identified_import(module, alias, cimport=cimports) + yield identified_import(module, alias) - else: + if just_imports: if type_of_import == "from": module = just_imports.pop(0) for attribute in just_imports: yield identified_import(module, attribute) else: for module in just_imports: - yield identified_import(module, cimport=cimports) + yield identified_import(module) From 8d8e737a35add11f1f8e95b5bed8f2d2d1ae5ee0 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 18 Dec 2020 00:56:44 -0800 Subject: [PATCH 252/539] Simplify import type identification --- isort/identify.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 94ee56f64..83865a140 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -9,15 +9,6 @@ from .settings import DEFAULT_CONFIG, Config -def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]: - """If the current line is an import line it will return its type (from or straight)""" - if line.lstrip().startswith(("import ", "cimport ")): - return "straight" - if line.lstrip().startswith("from "): - return "from" - return None - - class IdentifiedImport(NamedTuple): line_number: int indented: bool @@ -51,8 +42,11 @@ def imports( for statement in statements: line, raw_line = _normalize_line(statement) - type_of_import = import_type(line, config) or "" - if not type_of_import: + if line.lstrip().startswith(("import ", "cimport ")): + type_of_import = "straight" + if line.lstrip().startswith("from "): + type_of_import = "from" + else: continue import_string, _ = parse_comments(line) From 55307009e8bb92a5508e6b56a155adc709af0a96 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 18 Dec 2020 00:58:17 -0800 Subject: [PATCH 253/539] Add doc string for identify.py --- isort/identify.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/isort/identify.py b/isort/identify.py index 83865a140..339d6e894 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -1,4 +1,6 @@ -"""""" +"""Fast stream based import identification. +Eventually this will likely replace parse.py +""" from functools import partial from pathlib import Path from typing import Iterator, NamedTuple, Optional, TextIO From 66fde254430a0499486109cd2192cf5b24fdb328 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 18 Dec 2020 01:07:51 -0800 Subject: [PATCH 254/539] Add importidentification str support --- isort/identify.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 339d6e894..56a58d338 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -21,6 +21,16 @@ class IdentifiedImport(NamedTuple): cimport: bool = False file_path: Optional[Path] = None + def __str__(self): + full_path = ".".join(self.module.split(".") + self.attribute.split(".")) + if self.alias: + full_path = f"{full_path} as {self.alias}" + return ( + f"{self.file_path or ''}:{self.line_number} " + f"{'indented ' if self.indented else ''}" + f"{'cimport' if self.cimport else 'import'} {full_path}" + ) + def imports( input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None @@ -137,9 +147,7 @@ def imports( if attribute == alias and config.remove_redundant_aliases: pass else: - yield identified_import( - top_level_module, attribute, alias=alias - ) + yield identified_import(top_level_module, attribute, alias=alias) else: module = just_imports[as_index - 1] From 5e32a4f986a6e1d587de0a7108bc80a13add349c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 18 Dec 2020 01:49:06 -0800 Subject: [PATCH 255/539] Improved identify imports stringification --- isort/identify.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 56a58d338..79055c005 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -22,9 +22,11 @@ class IdentifiedImport(NamedTuple): file_path: Optional[Path] = None def __str__(self): - full_path = ".".join(self.module.split(".") + self.attribute.split(".")) + full_path = self.module + if self.attribute: + full_path += f".{self.attribute}" if self.alias: - full_path = f"{full_path} as {self.alias}" + full_path += " as {self.alias}" return ( f"{self.file_path or ''}:{self.line_number} " f"{'indented ' if self.indented else ''}" From 73827c0389de1c248863197f36670c499ad23075 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 18 Dec 2020 23:51:42 -0800 Subject: [PATCH 256/539] Remove statement from initial identified import contract --- isort/identify.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 79055c005..4e4e81ca7 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -14,7 +14,6 @@ class IdentifiedImport(NamedTuple): line_number: int indented: bool - statement: str module: str attribute: Optional[str] = None alias: Optional[str] = None @@ -75,7 +74,6 @@ def imports( IdentifiedImport, index, line.startswith(" ") or line.startswith("\n"), - statement, cimport=cimports, file_path=file_path, ) From d86e3a38cff78a845293a5ae4e990992eeb80bc3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 18 Dec 2020 23:59:19 -0800 Subject: [PATCH 257/539] Move file identification to standalone module' --- isort/main.py | 44 +++----------------------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/isort/main.py b/isort/main.py index 632e171f3..58071c451 100644 --- a/isort/main.py +++ b/isort/main.py @@ -7,10 +7,10 @@ from gettext import gettext as _ from io import TextIOWrapper from pathlib import Path -from typing import Any, Dict, Iterable, Iterator, List, Optional, Sequence, Set +from typing import Any, Dict, List, Optional, Sequence from warnings import warn -from . import __version__, api, sections +from . import __version__, api, sections, files from .exceptions import FileSkipped, ISortError, UnsupportedEncoding from .format import create_terminal_printer from .logo import ASCII_ART @@ -131,44 +131,6 @@ def _print_hard_fail( printer.error(message) -def iter_source_code( - paths: Iterable[str], config: Config, skipped: List[str], broken: List[str] -) -> Iterator[str]: - """Iterate over all Python source files defined in paths.""" - visited_dirs: Set[Path] = set() - - for path in paths: - if os.path.isdir(path): - for dirpath, dirnames, filenames in os.walk( - path, topdown=True, followlinks=config.follow_links - ): - base_path = Path(dirpath) - for dirname in list(dirnames): - full_path = base_path / dirname - resolved_path = full_path.resolve() - if config.is_skipped(full_path): - skipped.append(dirname) - dirnames.remove(dirname) - else: - if resolved_path in visited_dirs: # pragma: no cover - if not config.quiet: - warn(f"Likely recursive symlink detected to {resolved_path}") - dirnames.remove(dirname) - visited_dirs.add(resolved_path) - - for filename in filenames: - filepath = os.path.join(dirpath, filename) - if config.is_supported_filetype(filepath): - if config.is_skipped(Path(filepath)): - skipped.append(filename) - else: - yield filepath - elif not os.path.exists(path): - broken.append(path) - else: - yield path - - def _build_arg_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description="Sort Python import definitions alphabetically " @@ -1017,7 +979,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = filtered_files.append(file_name) file_names = filtered_files - file_names = iter_source_code(file_names, config, skipped, broken) + file_names = files.find(file_names, config, skipped, broken) if show_files: for file_name in file_names: print(file_name) From a1d004255aa2a9b2c1668af0776d2af22aae10a6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 18 Dec 2020 23:59:58 -0800 Subject: [PATCH 258/539] Add files module --- isort/files.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 isort/files.py diff --git a/isort/files.py b/isort/files.py new file mode 100644 index 000000000..dfb326e55 --- /dev/null +++ b/isort/files.py @@ -0,0 +1,43 @@ +import os +from typing import Iterable,List,Iterator,Set +from isort.settings import Config +from pathlib import Path + + +def find( + paths: Iterable[str], config: Config, skipped: List[str], broken: List[str] +) -> Iterator[str]: + """Fines and provides an iterator for all Python source files defined in paths.""" + visited_dirs: Set[Path] = set() + + for path in paths: + if os.path.isdir(path): + for dirpath, dirnames, filenames in os.walk( + path, topdown=True, followlinks=config.follow_links + ): + base_path = Path(dirpath) + for dirname in list(dirnames): + full_path = base_path / dirname + resolved_path = full_path.resolve() + if config.is_skipped(full_path): + skipped.append(dirname) + dirnames.remove(dirname) + else: + if resolved_path in visited_dirs: # pragma: no cover + if not config.quiet: + warn(f"Likely recursive symlink detected to {resolved_path}") + dirnames.remove(dirname) + visited_dirs.add(resolved_path) + + for filename in filenames: + filepath = os.path.join(dirpath, filename) + if config.is_supported_filetype(filepath): + if config.is_skipped(Path(filepath)): + skipped.append(filename) + else: + yield filepath + elif not os.path.exists(path): + broken.append(path) + else: + yield path + From f106209fa681248f1eb1e8f62a7830a6d0de5bc7 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 19 Dec 2020 00:00:13 -0800 Subject: [PATCH 259/539] isort+black --- isort/files.py | 6 +++--- isort/main.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/isort/files.py b/isort/files.py index dfb326e55..224215315 100644 --- a/isort/files.py +++ b/isort/files.py @@ -1,7 +1,8 @@ import os -from typing import Iterable,List,Iterator,Set -from isort.settings import Config from pathlib import Path +from typing import Iterable, Iterator, List, Set + +from isort.settings import Config def find( @@ -40,4 +41,3 @@ def find( broken.append(path) else: yield path - diff --git a/isort/main.py b/isort/main.py index 58071c451..7d1bc3d9b 100644 --- a/isort/main.py +++ b/isort/main.py @@ -10,7 +10,7 @@ from typing import Any, Dict, List, Optional, Sequence from warnings import warn -from . import __version__, api, sections, files +from . import __version__, api, files, sections from .exceptions import FileSkipped, ISortError, UnsupportedEncoding from .format import create_terminal_printer from .logo import ASCII_ART From eb7b9163b18469fd2770af1de28c7a886ad1c03d Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Sat, 19 Dec 2020 15:23:14 -0800 Subject: [PATCH 260/539] Add pyi and cython file support to .pre-commit-hooks.yaml Since pre-commit 2.9.0 (2020-11-21), the types_or key can be used to match multiple disparate file types. For more upstream details, see: https://github.com/pre-commit/pre-commit/issues/607 Fixes #402 --- .pre-commit-hooks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 160016181..773b505fd 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -4,5 +4,5 @@ require_serial: true language: python language_version: python3 - types: [python] + types_or: [cython, pyi, python] args: ['--filter-files'] From 52b90b33e3843185df38038cf65311460ee22779 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 19 Dec 2020 21:19:56 -0800 Subject: [PATCH 261/539] identify.IdentifiedImport -> identify.Import --- isort/identify.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 4e4e81ca7..696cb3718 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -11,7 +11,7 @@ from .settings import DEFAULT_CONFIG, Config -class IdentifiedImport(NamedTuple): +class Import(NamedTuple): line_number: int indented: bool module: str @@ -35,7 +35,7 @@ def __str__(self): def imports( input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None -) -> Iterator[IdentifiedImport]: +) -> Iterator[Import]: """Parses a python file taking out and categorizing imports.""" in_quote = "" @@ -71,7 +71,7 @@ def imports( or normalized_import_string.startswith("cimport") ) identified_import = partial( - IdentifiedImport, + Import, index, line.startswith(" ") or line.startswith("\n"), cimport=cimports, From f6a18f40527abf9a0e1e16c16c8f1e7019e19bb5 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 19 Dec 2020 22:07:29 -0800 Subject: [PATCH 262/539] Add statement method to IdentifiedImport --- isort/api.py | 82 ++++++++++++++++++++--------------------------- isort/identify.py | 8 +++-- 2 files changed, 40 insertions(+), 50 deletions(-) diff --git a/isort/api.py b/isort/api.py index 502994b66..a178758a7 100644 --- a/isort/api.py +++ b/isort/api.py @@ -2,12 +2,12 @@ import sys from io import StringIO from pathlib import Path -from typing import Optional, TextIO, Union, cast +from typing import Optional, TextIO, Union, cast, Iterator from warnings import warn from isort import core -from . import io +from . import io, identify from .exceptions import ( ExistingSyntaxErrors, FileSkipComment, @@ -394,86 +394,74 @@ def sort_file( return changed -def get_imports_string( +def imports_in_code_string( code: str, - extension: Optional[str] = None, config: Config = DEFAULT_CONFIG, - file_path: Optional[Path] = None, + file_path: Opitonal[Path] = None, + unique: bool = False, **config_kwargs, -) -> str: - """Finds all imports within the provided code string, returning a new string with them. +) -> Iterator[identify.Import]: + """Finds and returns all imports within the provided code string. - **code**: The string of code with imports that need to be sorted. - - **extension**: The file extension that contains imports. Defaults to filename extension or py. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. + - **unique**: If True, only the first instance of an import is returned. - ****config_kwargs**: Any config modifications. """ - input_stream = StringIO(code) - output_stream = StringIO() - config = _config(path=file_path, config=config, **config_kwargs) - get_imports_stream( - input_stream, - output_stream, - extension=extension, - config=config, - file_path=file_path, - ) - output_stream.seek(0) - return output_stream.read() + yield from imports_in_stream(input_stream=StringIO(code), config=config, file_path=file_path, unique=unique, **config_kwargs) -def get_imports_stream( +def imports_in_stream( input_stream: TextIO, - output_stream: TextIO, - extension: Optional[str] = None, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, + unique: bool = False, **config_kwargs, -) -> None: - """Finds all imports within the provided code stream, outputs to the provided output stream. +) -> Iterator[identify.Import]: + """Finds and returns all imports within the provided code stream. - **input_stream**: The stream of code with imports that need to be sorted. - - **output_stream**: The stream where sorted imports should be written to. - - **extension**: The file extension that contains imports. Defaults to filename extension or py. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. + - **unique**: If True, only the first instance of an import is returned. - ****config_kwargs**: Any config modifications. """ config = _config(path=file_path, config=config, **config_kwargs) - core.process( - input_stream, - output_stream, - extension=extension or (file_path and file_path.suffix.lstrip(".")) or "py", - config=config, - imports_only=True, - ) + identified_imports = identify.imports(input_stream, config=config, file_path=file_path) + if not unique: + yield from identified_imports + + seen = set() + for identified_import in identified_imports: + key = identified_import.statement() + if key not in seen: + seen.add(key) + yield identified_import -def get_imports_file( +def imports_in_file( filename: Union[str, Path], - output_stream: TextIO, - extension: Optional[str] = None, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, + unique: bool = False, **config_kwargs, -) -> None: - """Finds all imports within the provided file, outputs to the provided output stream. +) -> Iterator[identify.Import]: + """Finds and returns all imports within the provided source file. - - **filename**: The name or Path of the file to check. - - **output_stream**: The stream where sorted imports should be written to. + - **filename**: The name or Path of the file to look for imports in. - **extension**: The file extension that contains imports. Defaults to filename extension or py. - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. + - **unique**: If True, only the first instance of an import is returned. - ****config_kwargs**: Any config modifications. """ with io.File.read(filename) as source_file: - get_imports_stream( - source_file.stream, - output_stream, - extension, - config, - file_path, + yield from imports_in_stream( + input_stream=source_file.stream, + config=config, + file_path=file_path, + unique=unique, **config_kwargs, ) diff --git a/isort/identify.py b/isort/identify.py index 696cb3718..558f46937 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -20,16 +20,18 @@ class Import(NamedTuple): cimport: bool = False file_path: Optional[Path] = None - def __str__(self): + def statement(self) -> str: full_path = self.module if self.attribute: full_path += f".{self.attribute}" if self.alias: full_path += " as {self.alias}" + return f"{'cimport' if self.cimport else 'import'} {full_path}" + + def __str__(self): return ( f"{self.file_path or ''}:{self.line_number} " - f"{'indented ' if self.indented else ''}" - f"{'cimport' if self.cimport else 'import'} {full_path}" + f"{'indented ' if self.indented else ''}{self.statement()}" ) From 39b8f7528792e1126ead4fbe2e65bbb07bc431ea Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 19 Dec 2020 22:08:44 -0800 Subject: [PATCH 263/539] Fix typo with Optional --- isort/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/api.py b/isort/api.py index a178758a7..d4b4e739f 100644 --- a/isort/api.py +++ b/isort/api.py @@ -397,7 +397,7 @@ def sort_file( def imports_in_code_string( code: str, config: Config = DEFAULT_CONFIG, - file_path: Opitonal[Path] = None, + file_path: Optional[Path] = None, unique: bool = False, **config_kwargs, ) -> Iterator[identify.Import]: From a4ec137db50720b817186bbc3f43d0345eb77db2 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 19 Dec 2020 22:11:01 -0800 Subject: [PATCH 264/539] Export imports_in methods to __init__ --- isort/__init__.py | 6 +++--- isort/api.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/isort/__init__.py b/isort/__init__.py index b800a03de..0277199cc 100644 --- a/isort/__init__.py +++ b/isort/__init__.py @@ -5,9 +5,9 @@ from .api import ( check_file, check_stream, - get_imports_file, - get_imports_stream, - get_imports_string, + imports_in_file, + imports_in_stream, + imports_in_code, place_module, place_module_with_reason, ) diff --git a/isort/api.py b/isort/api.py index d4b4e739f..af5ead881 100644 --- a/isort/api.py +++ b/isort/api.py @@ -394,7 +394,7 @@ def sort_file( return changed -def imports_in_code_string( +def imports_in_code( code: str, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, From b0c92df90f467535908c8c7551acefe0f144ee71 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 19 Dec 2020 22:12:51 -0800 Subject: [PATCH 265/539] Missing f for f'string' --- isort/identify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/identify.py b/isort/identify.py index 558f46937..9f6cc9467 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -25,7 +25,7 @@ def statement(self) -> str: if self.attribute: full_path += f".{self.attribute}" if self.alias: - full_path += " as {self.alias}" + full_path += f" as {self.alias}" return f"{'cimport' if self.cimport else 'import'} {full_path}" def __str__(self): From 74527afdbd6541cd30de5706a23ea0141f975800 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 19 Dec 2020 22:23:20 -0800 Subject: [PATCH 266/539] isort+black --- isort/__init__.py | 2 +- isort/api.py | 16 +++++++++++----- isort/files.py | 1 + isort/main.py | 7 +++++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/isort/__init__.py b/isort/__init__.py index 0277199cc..dbe2c895e 100644 --- a/isort/__init__.py +++ b/isort/__init__.py @@ -5,9 +5,9 @@ from .api import ( check_file, check_stream, + imports_in_code, imports_in_file, imports_in_stream, - imports_in_code, place_module, place_module_with_reason, ) diff --git a/isort/api.py b/isort/api.py index af5ead881..c1b609248 100644 --- a/isort/api.py +++ b/isort/api.py @@ -2,12 +2,12 @@ import sys from io import StringIO from pathlib import Path -from typing import Optional, TextIO, Union, cast, Iterator +from typing import Iterator, Optional, Set, TextIO, Union, cast from warnings import warn from isort import core -from . import io, identify +from . import identify, io from .exceptions import ( ExistingSyntaxErrors, FileSkipComment, @@ -409,7 +409,13 @@ def imports_in_code( - **unique**: If True, only the first instance of an import is returned. - ****config_kwargs**: Any config modifications. """ - yield from imports_in_stream(input_stream=StringIO(code), config=config, file_path=file_path, unique=unique, **config_kwargs) + yield from imports_in_stream( + input_stream=StringIO(code), + config=config, + file_path=file_path, + unique=unique, + **config_kwargs, + ) def imports_in_stream( @@ -432,7 +438,7 @@ def imports_in_stream( if not unique: yield from identified_imports - seen = set() + seen: Set[str] = set() for identified_import in identified_imports: key = identified_import.statement() if key not in seen: @@ -460,7 +466,7 @@ def imports_in_file( yield from imports_in_stream( input_stream=source_file.stream, config=config, - file_path=file_path, + file_path=file_path or source_file.path, unique=unique, **config_kwargs, ) diff --git a/isort/files.py b/isort/files.py index 224215315..692c2011c 100644 --- a/isort/files.py +++ b/isort/files.py @@ -1,6 +1,7 @@ import os from pathlib import Path from typing import Iterable, Iterator, List, Set +from warnings import warn from isort.settings import Config diff --git a/isort/main.py b/isort/main.py index 7d1bc3d9b..262a9eeb3 100644 --- a/isort/main.py +++ b/isort/main.py @@ -871,11 +871,14 @@ def identify_imports_main( file_name = arguments.file if file_name == "-": - api.get_imports_stream(sys.stdin if stdin is None else stdin, sys.stdout) + identified_imports = api.imports_in_stream(sys.stdin if stdin is None else stdin) else: if os.path.isdir(file_name): sys.exit("Path must be a file, not a directory") - api.get_imports_file(file_name, sys.stdout) + identified_imports = api.imports_in_file(file_name) + + for identified_import in identified_imports: + print(str(identified_import)) def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = None) -> None: From 0c1ed9cd04896baa7926aefd057050cc34f62505 Mon Sep 17 00:00:00 2001 From: Marco Gorelli Date: Sun, 20 Dec 2020 07:37:53 +0000 Subject: [PATCH 267/539] Put minimum pre-commit version in hook (seeing as you're now using `types_or` - else the error messages people get may be a bit mysterious) --- .pre-commit-hooks.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 773b505fd..fc6906aae 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -6,3 +6,4 @@ language_version: python3 types_or: [cython, pyi, python] args: ['--filter-files'] + minimum_pre_commit_version: '2.9.0' From aa464877a34fd812fa9f1ba5f6741e7e7f26e0bc Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Dec 2020 17:25:01 -0800 Subject: [PATCH 268/539] Fix logic error in type of import identification --- isort/identify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/identify.py b/isort/identify.py index 9f6cc9467..f57f3a3be 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -59,7 +59,7 @@ def imports( line, raw_line = _normalize_line(statement) if line.lstrip().startswith(("import ", "cimport ")): type_of_import = "straight" - if line.lstrip().startswith("from "): + elif line.lstrip().startswith("from "): type_of_import = "from" else: continue From f6cc79450f2c50923e98bd4e56614dd6c8446de3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Dec 2020 17:55:26 -0800 Subject: [PATCH 269/539] Rename import finding methods to be more intuitive directly from isort (isort.find_imports_in_file > isort.imports_in_file=) --- isort/__init__.py | 6 +++--- isort/api.py | 10 +++++----- isort/main.py | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/isort/__init__.py b/isort/__init__.py index dbe2c895e..afb67e3ae 100644 --- a/isort/__init__.py +++ b/isort/__init__.py @@ -5,9 +5,9 @@ from .api import ( check_file, check_stream, - imports_in_code, - imports_in_file, - imports_in_stream, + find_imports_in_code, + find_imports_in_file, + find_imports_in_stream, place_module, place_module_with_reason, ) diff --git a/isort/api.py b/isort/api.py index c1b609248..583856f2f 100644 --- a/isort/api.py +++ b/isort/api.py @@ -394,7 +394,7 @@ def sort_file( return changed -def imports_in_code( +def find_find_imports_in_code( code: str, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, @@ -409,7 +409,7 @@ def imports_in_code( - **unique**: If True, only the first instance of an import is returned. - ****config_kwargs**: Any config modifications. """ - yield from imports_in_stream( + yield from find_imports_in_stream( input_stream=StringIO(code), config=config, file_path=file_path, @@ -418,7 +418,7 @@ def imports_in_code( ) -def imports_in_stream( +def find_imports_in_stream( input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, @@ -446,7 +446,7 @@ def imports_in_stream( yield identified_import -def imports_in_file( +def find_imports_in_file( filename: Union[str, Path], config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, @@ -463,7 +463,7 @@ def imports_in_file( - ****config_kwargs**: Any config modifications. """ with io.File.read(filename) as source_file: - yield from imports_in_stream( + yield from find_imports_in_stream( input_stream=source_file.stream, config=config, file_path=file_path or source_file.path, diff --git a/isort/main.py b/isort/main.py index 262a9eeb3..40f62f6f9 100644 --- a/isort/main.py +++ b/isort/main.py @@ -871,11 +871,11 @@ def identify_imports_main( file_name = arguments.file if file_name == "-": - identified_imports = api.imports_in_stream(sys.stdin if stdin is None else stdin) + identified_imports = api.find_imports_in_stream(sys.stdin if stdin is None else stdin) else: if os.path.isdir(file_name): sys.exit("Path must be a file, not a directory") - identified_imports = api.imports_in_file(file_name) + identified_imports = api.find_imports_in_file(file_name) for identified_import in identified_imports: print(str(identified_import)) From 75d6d9f14637a1bc0a77f4d7582d14e346a09bc8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Dec 2020 17:58:53 -0800 Subject: [PATCH 270/539] Fix double find typo --- isort/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/api.py b/isort/api.py index 583856f2f..ec4152184 100644 --- a/isort/api.py +++ b/isort/api.py @@ -394,7 +394,7 @@ def sort_file( return changed -def find_find_imports_in_code( +def find_imports_in_code( code: str, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, From bf42692e255697767cfd30271bc669587fb9b398 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Dec 2020 18:22:43 -0800 Subject: [PATCH 271/539] Fix infinite loop --- isort/identify.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/isort/identify.py b/isort/identify.py index f57f3a3be..6e0b606da 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -154,6 +154,9 @@ def imports( else: module = just_imports[as_index - 1] alias = just_imports[as_index + 1] + direct_imports.remove(alias) + direct_imports.remove("as") + just_imports[1:] = direct_imports if not (module == alias and config.remove_redundant_aliases): yield identified_import(module, alias) From 10fb87eb4ff0585ae48862af5be0c07543e406b5 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Dec 2020 18:36:53 -0800 Subject: [PATCH 272/539] All tests now passingd --- tests/unit/test_api.py | 8 ++--- tests/unit/test_isort.py | 64 ++++++++++++++++++---------------------- tests/unit/test_main.py | 10 ++----- 3 files changed, 34 insertions(+), 48 deletions(-) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 4ee19bc43..20c2fdae6 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -1,6 +1,5 @@ """Tests the isort API module""" import os -import sys from io import StringIO from unittest.mock import MagicMock, patch @@ -84,7 +83,6 @@ def test_sort_code_string_mixed_newlines(): assert api.sort_code_string("import A\n\r\nimportA\n\n") == "import A\r\n\r\nimportA\r\n\n" -def test_get_import_file(imperfect, capsys): - api.get_imports_file(imperfect, sys.stdout) - out, _ = capsys.readouterr() - assert out == imperfect_content.replace("\n", os.linesep) +def test_find_imports_in_file(imperfect): + found_imports = list(api.find_imports_in_file(imperfect)) + assert "b" in [found_import.module for found_import in found_imports] diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index aea2c74b6..aca087092 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -14,7 +14,7 @@ import py import pytest import isort -from isort import main, api, sections +from isort import api, sections, files from isort.settings import WrapModes, Config from isort.utils import exists_case_sensitive from isort.exceptions import FileSkipped, ExistingSyntaxErrors @@ -3270,12 +3270,11 @@ def test_safety_skips(tmpdir, enabled: bool) -> None: skipped: List[str] = [] broken: List[str] = [] codes = [str(tmpdir)] - main.iter_source_code(codes, config, skipped, broken) + files.find(codes, config, skipped, broken) # if enabled files within nested unsafe directories should be skipped file_names = { - os.path.relpath(f, str(tmpdir)) - for f in main.iter_source_code([str(tmpdir)], config, skipped, broken) + os.path.relpath(f, str(tmpdir)) for f in files.find([str(tmpdir)], config, skipped, broken) } if enabled: assert file_names == {"victim.py"} @@ -3292,9 +3291,7 @@ def test_safety_skips(tmpdir, enabled: bool) -> None: # directly pointing to files within unsafe directories shouldn't skip them either way file_names = { os.path.relpath(f, str(toxdir)) - for f in main.iter_source_code( - [str(toxdir)], Config(directory=str(toxdir)), skipped, broken - ) + for f in files.find([str(toxdir)], Config(directory=str(toxdir)), skipped, broken) } assert file_names == {"verysafe.py"} @@ -3318,7 +3315,7 @@ def test_skip_glob(tmpdir, skip_glob_assert: Tuple[List[str], int, Set[str]]) -> broken: List[str] = [] file_names = { os.path.relpath(f, str(base_dir)) - for f in main.iter_source_code([str(base_dir)], config, skipped, broken) + for f in files.find([str(base_dir)], config, skipped, broken) } assert len(skipped) == skipped_count assert file_names == file_names_expected @@ -3332,7 +3329,7 @@ def test_broken(tmpdir) -> None: broken: List[str] = [] file_names = { os.path.relpath(f, str(base_dir)) - for f in main.iter_source_code(["not-exist"], config, skipped, broken) + for f in files.find(["not-exist"], config, skipped, broken) } assert len(broken) == 1 assert file_names == set() @@ -4916,7 +4913,7 @@ def test_combine_straight_imports() -> None: ) -def test_get_imports_string() -> None: +def test_find_imports_in_code() -> None: test_input = ( "import first_straight\n" "\n" @@ -4943,24 +4940,25 @@ def test_get_imports_string() -> None: "\n" "import needed_in_end\n" ) - result = api.get_imports_string(test_input) - assert result == ( - "import first_straight\n" - "import second_straight\n" - "from first_from import first_from_function_1, first_from_function_2\n" - "import bad_name as good_name\n" - "from parent.some_bad_defs import bad_name_1 as ok_name_1, bad_name_2 as ok_name_2\n" - "import needed_in_bla_2\n" - "import needed_in_bla\n" - "import needed_in_bla_bla\n" - "import needed_in_end\n" - ) - - -def test_get_imports_stdout() -> None: - """Ensure that get_imports_stream can work with nonseekable streams like STDOUT""" - - global_output = [] + identified_imports = list(map(str, api.find_imports_in_code(test_input))) + assert identified_imports == [ + ":0 import first_straight", + ":2 import second_straight", + ":3 import first_from.first_from_function_1", + ":3 import first_from.first_from_function_2", + ":4 import bad_name.good_name", + ":4 import bad_name", + ":5 import parent.some_bad_defs.bad_name_1 as ok_name_1", + ":5 import parent.some_bad_defs.bad_name_2 as ok_name_2", + ":11 import needed_in_bla_2", + ":14 import needed_in_bla", + ":17 import needed_in_bla_bla", + ":21 import needed_in_end", + ] + + +def test_find_imports_in_stream() -> None: + """Ensure that find_imports_in_stream can work with nonseekable streams like STDOUT""" class NonSeekableTestStream(StringIO): def seek(self, position): @@ -4969,10 +4967,6 @@ def seek(self, position): def seekable(self): return False - def write(self, s, *a, **kw): - global_output.append(s) - - test_input = StringIO("import m2\n" "import m1\n" "not_import = 7") - test_output = NonSeekableTestStream() - api.get_imports_stream(test_input, test_output) - assert "".join(global_output) == "import m2\nimport m1\n" + test_input = NonSeekableTestStream("import m2\n" "import m1\n" "not_import = 7") + identified_imports = list(map(str, api.find_imports_in_stream(test_input))) + assert identified_imports == [":0 import m2", ":1 import m1"] diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index bd4c6ffa6..67adad34b 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -40,12 +40,6 @@ def test_fuzz_sort_imports(file_name, config, check, ask_to_apply, write_to_stdo ) -def test_iter_source_code(tmpdir): - tmp_file = tmpdir.join("file.py") - tmp_file.write("import os, sys\n") - assert tuple(main.iter_source_code((tmp_file,), DEFAULT_CONFIG, [], [])) == (tmp_file,) - - def test_sort_imports(tmpdir): tmp_file = tmpdir.join("file.py") tmp_file.write("import os, sys\n") @@ -1002,9 +996,9 @@ def test_only_modified_flag(tmpdir, capsys): def test_identify_imports_main(tmpdir, capsys): file_content = "import mod2\n" "a = 1\n" "import mod1\n" - file_imports = "import mod2\n" "import mod1\n" some_file = tmpdir.join("some_file.py") some_file.write(file_content) + file_imports = f"{some_file}:0 import mod2\n{some_file}:2 import mod1\n" main.identify_imports_main([str(some_file)]) @@ -1014,7 +1008,7 @@ def test_identify_imports_main(tmpdir, capsys): main.identify_imports_main(["-"], stdin=as_stream(file_content)) out, error = capsys.readouterr() - assert out.replace("\r\n", "\n") == file_imports + assert out.replace("\r\n", "\n") == file_imports.replace(str(some_file), "") with pytest.raises(SystemExit): main.identify_imports_main([str(tmpdir)]) From c5b335500ab326b225626927ebadf5c584f58ac4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Dec 2020 18:37:20 -0800 Subject: [PATCH 273/539] Add test for new files module --- tests/unit/test_files.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/unit/test_files.py diff --git a/tests/unit/test_files.py b/tests/unit/test_files.py new file mode 100644 index 000000000..7ee6acf40 --- /dev/null +++ b/tests/unit/test_files.py @@ -0,0 +1,8 @@ +from isort import files +from isort.settings import DEFAULT_CONFIG + + +def test_find(tmpdir): + tmp_file = tmpdir.join("file.py") + tmp_file.write("import os, sys\n") + assert tuple(files.find((tmp_file,), DEFAULT_CONFIG, [], [])) == (tmp_file,) From bded231bfe679761856f819cf2a7fe29b955378e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Dec 2020 23:42:08 -0800 Subject: [PATCH 274/539] Hide unused variable --- isort/identify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/identify.py b/isort/identify.py index 6e0b606da..3f1a58045 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -56,7 +56,7 @@ def imports( statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" for statement in statements: - line, raw_line = _normalize_line(statement) + line, _raw_line = _normalize_line(statement) if line.lstrip().startswith(("import ", "cimport ")): type_of_import = "straight" elif line.lstrip().startswith("from "): From c4560d657c10b03f6f712026b7335f6c8ee512ee Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Dec 2020 01:05:57 -0800 Subject: [PATCH 275/539] identify imports directory support --- isort/main.py | 23 ++++++++++++++++------- tests/unit/test_main.py | 3 +-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/isort/main.py b/isort/main.py index 40f62f6f9..4d59b2a88 100644 --- a/isort/main.py +++ b/isort/main.py @@ -6,6 +6,7 @@ import sys from gettext import gettext as _ from io import TextIOWrapper +from itertools import chain from pathlib import Path from typing import Any, Dict, List, Optional, Sequence from warnings import warn @@ -15,7 +16,7 @@ from .format import create_terminal_printer from .logo import ASCII_ART from .profiles import profiles -from .settings import VALID_PY_TARGETS, Config, WrapModes +from .settings import DEFAULT_CONFIG, VALID_PY_TARGETS, Config, WrapModes try: from .setuptools_commands import ISortCommand # noqa: F401 @@ -866,16 +867,24 @@ def identify_imports_main( description="Get all import definitions from a given file." "Use `-` as the first argument to represent stdin." ) - parser.add_argument("file", help="Python source file to get imports from.") + parser.add_argument( + "files", nargs="*", help="One or more Python source files that need their imports sorted." + ) arguments = parser.parse_args(argv) - file_name = arguments.file - if file_name == "-": + file_names = arguments.files + if file_names == ["-"]: identified_imports = api.find_imports_in_stream(sys.stdin if stdin is None else stdin) else: - if os.path.isdir(file_name): - sys.exit("Path must be a file, not a directory") - identified_imports = api.find_imports_in_file(file_name) + skipped: List[str] = [] + broken: List[str] = [] + config = DEFAULT_CONFIG + identified_imports = chain( + *( + api.find_imports_in_file(file_name) + for file_name in files.find(file_names, config, skipped, broken) + ) + ) for identified_import in identified_imports: print(str(identified_import)) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 67adad34b..a13f5badc 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -1010,5 +1010,4 @@ def test_identify_imports_main(tmpdir, capsys): out, error = capsys.readouterr() assert out.replace("\r\n", "\n") == file_imports.replace(str(some_file), "") - with pytest.raises(SystemExit): - main.identify_imports_main([str(tmpdir)]) + main.identify_imports_main([str(tmpdir)]) From d93bbc693a2f61b48e4765c525b01aeeeeff3bb8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Dec 2020 22:34:34 -0800 Subject: [PATCH 276/539] Expose unique option for identifying imports to cli --- isort/main.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index 4d59b2a88..2e84a6645 100644 --- a/isort/main.py +++ b/isort/main.py @@ -870,18 +870,22 @@ def identify_imports_main( parser.add_argument( "files", nargs="*", help="One or more Python source files that need their imports sorted." ) + parser.add_argument( + "--unique", action="store_true", default=False, + help="If true, isort will only identify unique imports." + ) arguments = parser.parse_args(argv) file_names = arguments.files if file_names == ["-"]: - identified_imports = api.find_imports_in_stream(sys.stdin if stdin is None else stdin) + identified_imports = api.find_imports_in_stream(sys.stdin if stdin is None else stdin, unique=arguments.unique) else: skipped: List[str] = [] broken: List[str] = [] config = DEFAULT_CONFIG identified_imports = chain( *( - api.find_imports_in_file(file_name) + api.find_imports_in_file(file_name, unique=arguments.unique) for file_name in files.find(file_names, config, skipped, broken) ) ) From 128e1da3042906a53a239d65568d5abb90844e8c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Dec 2020 22:36:47 -0800 Subject: [PATCH 277/539] isort + black --- isort/main.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/isort/main.py b/isort/main.py index 2e84a6645..b63642ba6 100644 --- a/isort/main.py +++ b/isort/main.py @@ -871,14 +871,18 @@ def identify_imports_main( "files", nargs="*", help="One or more Python source files that need their imports sorted." ) parser.add_argument( - "--unique", action="store_true", default=False, - help="If true, isort will only identify unique imports." + "--unique", + action="store_true", + default=False, + help="If true, isort will only identify unique imports.", ) arguments = parser.parse_args(argv) file_names = arguments.files if file_names == ["-"]: - identified_imports = api.find_imports_in_stream(sys.stdin if stdin is None else stdin, unique=arguments.unique) + identified_imports = api.find_imports_in_stream( + sys.stdin if stdin is None else stdin, unique=arguments.unique + ) else: skipped: List[str] = [] broken: List[str] = [] From 54f5bce948bf5f9cc5190efd6e81d533c35d513e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 22 Dec 2020 22:54:46 -0800 Subject: [PATCH 278/539] remove unecesary elif --- isort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index b63642ba6..889f3b623 100644 --- a/isort/main.py +++ b/isort/main.py @@ -958,7 +958,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = if show_config: print(json.dumps(config.__dict__, indent=4, separators=(",", ": "), default=_preconvert)) return - elif file_names == ["-"]: + if file_names == ["-"]: file_path = Path(stream_filename) if stream_filename else None if show_files: sys.exit("Error: can't show files for streaming input.") From 7addd4fc5154fedd90c6c4c32d8f5baf497c1468 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 22 Dec 2020 22:55:41 -0800 Subject: [PATCH 279/539] remove unecesary elif~ --- isort/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/isort/settings.py b/isort/settings.py index 8ef6dfa86..a1e9067f8 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -387,7 +387,8 @@ def __init__( for section in combined_config.get("sections", ()): if section in SECTION_DEFAULTS: continue - elif not section.lower() in known_other: + + if not section.lower() in known_other: config_keys = ", ".join(known_other.keys()) warn( f"`sections` setting includes {section}, but no known_{section.lower()} " From 1e6db95820f4341712fb7fd9e993597543480a0b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 22 Dec 2020 22:58:24 -0800 Subject: [PATCH 280/539] Remove unecesary variable definition --- isort/output.py | 4 +--- isort/settings.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/isort/output.py b/isort/output.py index 45faa6468..1d64785bc 100644 --- a/isort/output.py +++ b/isort/output.py @@ -229,8 +229,6 @@ def _with_from_imports( if not config.no_inline_sort or ( config.force_single_line and module not in config.single_line_exclusions ): - ignore_case = config.force_alphabetical_sort_within_sections - if not config.only_sections: from_imports = sorting.naturally( from_imports, @@ -238,7 +236,7 @@ def _with_from_imports( key, config, True, - ignore_case, + config.force_alphabetical_sort_within_sections, section_name=section, ), ) diff --git a/isort/settings.py b/isort/settings.py index a1e9067f8..03d90335e 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -387,7 +387,7 @@ def __init__( for section in combined_config.get("sections", ()): if section in SECTION_DEFAULTS: continue - + if not section.lower() in known_other: config_keys = ", ".join(known_other.keys()) warn( From 805323439dfd9e208e42c5690e88528f0da547b0 Mon Sep 17 00:00:00 2001 From: anirudnits Date: Wed, 23 Dec 2020 18:41:47 +0530 Subject: [PATCH 281/539] Specify the config file example corresponds to pyproject.toml --- docs/configuration/black_compatibility.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/configuration/black_compatibility.md b/docs/configuration/black_compatibility.md index c81989a9f..a57754252 100644 --- a/docs/configuration/black_compatibility.md +++ b/docs/configuration/black_compatibility.md @@ -11,6 +11,8 @@ All that's required to use isort alongside black is to set the isort profile to For projects that officially use both isort and black, we recommend setting the black profile in a config file at the root of your project's repository. This way independent to how users call isort (pre-commit, CLI, or editor integration) the black profile will automatically be applied. +For instance, your _pyproject.toml_ file would look something like + ```ini [tool.isort] profile = "black" From 40d657c252799511c4804a2a75952f6da3a14023 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 23 Dec 2020 22:52:34 -0800 Subject: [PATCH 282/539] Call super for colorama printer --- isort/format.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/isort/format.py b/isort/format.py index 46bb15695..22c506f06 100644 --- a/isort/format.py +++ b/isort/format.py @@ -110,7 +110,8 @@ def diff_line(self, line: str) -> None: class ColoramaPrinter(BasicPrinter): def __init__(self, output: Optional[TextIO] = None): - self.output = output or sys.stdout + super().__init__(output=output) + # Note: this constants are instance variables instead ofs class variables # because they refer to colorama which might not be installed. self.ERROR = self.style_text("ERROR", colorama.Fore.RED) From 3f82273dbd513a6fce756da315bb6affd3a8c8ab Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 23 Dec 2020 22:56:20 -0800 Subject: [PATCH 283/539] Add ignore line for deepsource rule --- isort/format.py | 2 +- isort/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/format.py b/isort/format.py index 22c506f06..d08a6a513 100644 --- a/isort/format.py +++ b/isort/format.py @@ -111,7 +111,7 @@ def diff_line(self, line: str) -> None: class ColoramaPrinter(BasicPrinter): def __init__(self, output: Optional[TextIO] = None): super().__init__(output=output) - + # Note: this constants are instance variables instead ofs class variables # because they refer to colorama which might not be installed. self.ERROR = self.style_text("ERROR", colorama.Fore.RED) diff --git a/isort/settings.py b/isort/settings.py index 03d90335e..4ea8c0d0b 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -506,7 +506,7 @@ def is_skipped(self, file_path: Path) -> bool: if file_path.name == ".git": # pragma: no cover return True - result = subprocess.run( # nosec + result = subprocess.run( # nosec # skipcq: PYL-W1510 ["git", "-C", str(file_path.parent), "check-ignore", "--quiet", os_path] ) if result.returncode == 0: From 800bfd91f6cde599fca1c45e5566db0379a7a8b8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 23 Dec 2020 22:57:36 -0800 Subject: [PATCH 284/539] Ignore scripts in deepsource config --- .deepsource.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/.deepsource.toml b/.deepsource.toml index cfbbec30a..2cd579f78 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -3,6 +3,7 @@ version = 1 test_patterns = ["tests/**"] exclude_patterns = [ "tests/**", + "scripts/**", "isort/_future/**", "isort/_vendored/**", ] From d37e4142a3ce8c4ecd66c104f8b861975ddf16d2 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 23 Dec 2020 23:01:44 -0800 Subject: [PATCH 285/539] Handle stop iteration case --- isort/identify.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/isort/identify.py b/isort/identify.py index 3f1a58045..09f3cabd9 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -91,7 +91,11 @@ def imports( import_string += "\n" + line else: while line.strip().endswith("\\"): - index, next_line = next(indexed_input) + try: + index, next_line = next(indexed_input) + except StopIteration: + break + line, _ = parse_comments(next_line) # Still need to check for parentheses after an escaped line From aab64a1004613f3dad5b2879d968a8d900f32482 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 23 Dec 2020 23:20:26 -0800 Subject: [PATCH 286/539] Remove unecesary blank lines --- isort/hooks.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/isort/hooks.py b/isort/hooks.py index acccede59..dfd7eb3dc 100644 --- a/isort/hooks.py +++ b/isort/hooks.py @@ -12,8 +12,7 @@ def get_output(command: List[str]) -> str: - """ - Run a command and return raw output + """Run a command and return raw output :param str command: the command to run :returns: the stdout output of the command @@ -23,8 +22,7 @@ def get_output(command: List[str]) -> str: def get_lines(command: List[str]) -> List[str]: - """ - Run a command and return lines of output + """Run a command and return lines of output :param str command: the command to run :returns: list of whitespace-stripped lines output by command @@ -36,8 +34,7 @@ def get_lines(command: List[str]) -> List[str]: def git_hook( strict: bool = False, modify: bool = False, lazy: bool = False, settings_file: str = "" ) -> int: - """ - Git pre-commit hook to check staged files for isort errors + """Git pre-commit hook to check staged files for isort errors :param bool strict - if True, return number of errors on exit, causing the hook to fail. If False, return zero so it will From f1c908ac9a79622893e066f8e48154073e04ded2 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 24 Dec 2020 14:41:50 -0800 Subject: [PATCH 287/539] Add failing test for issue #1621: Showing that double comma does indeed apear. --- tests/unit/test_regressions.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index c25ed19c7..ef3d84797 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1485,3 +1485,19 @@ def test_isort_losing_imports_vertical_prefix_from_module_import_wrap_mode_issue show_diff=True, multi_line_output=9, ) + + +def test_isort_adding_second_comma_issue_1621(): + """Ensure isort doesnt add a second comma when very long comment is present + See: https://github.com/PyCQA/isort/issues/1621. + """ + assert isort.code( + """from .test import ( + TestTestTestTestTestTest2 as TestTestTestTestTestTest1 # Some really long comment bla bla bla bla bla +) +""", + profile="black", + ) == """from .test import ( + TestTestTestTestTestTest2 as TestTestTestTestTestTest1, # Some really long comment bla bla bla bla bla +) +""" From 7a84253b9f9f8f6d1d251e3caac1de4698e17b6f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 24 Dec 2020 14:43:00 -0800 Subject: [PATCH 288/539] Expand test to capture case where comma is already present --- tests/unit/test_regressions.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index ef3d84797..910525432 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1491,6 +1491,14 @@ def test_isort_adding_second_comma_issue_1621(): """Ensure isort doesnt add a second comma when very long comment is present See: https://github.com/PyCQA/isort/issues/1621. """ + assert isort.check_code( + """from .test import ( + TestTestTestTestTestTest2 as TestTestTestTestTestTest1, # Some really long comment bla bla bla bla bla +) +""", + profile="black", + show_diff=True, + ) assert isort.code( """from .test import ( TestTestTestTestTestTest2 as TestTestTestTestTestTest1 # Some really long comment bla bla bla bla bla @@ -1501,3 +1509,4 @@ def test_isort_adding_second_comma_issue_1621(): TestTestTestTestTestTest2 as TestTestTestTestTestTest1, # Some really long comment bla bla bla bla bla ) """ + From 8b828fb9b42bc129e10f88b166b69f2271c76a7d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 24 Dec 2020 23:31:06 -0800 Subject: [PATCH 289/539] Remove uneeded line --- tests/unit/test_regressions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 910525432..10bd3bf81 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1509,4 +1509,3 @@ def test_isort_adding_second_comma_issue_1621(): TestTestTestTestTestTest2 as TestTestTestTestTestTest1, # Some really long comment bla bla bla bla bla ) """ - From 59f635ab33c32925f0faa92835ee3e94fd785ad6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 25 Dec 2020 00:00:22 -0800 Subject: [PATCH 290/539] Fix comma behavior --- isort/wrap.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/isort/wrap.py b/isort/wrap.py index 11542fa07..e993ae0f3 100644 --- a/isort/wrap.py +++ b/isort/wrap.py @@ -77,7 +77,13 @@ def line(content: str, line_separator: str, config: Config = DEFAULT_CONFIG) -> line_parts = re.split(exp, line_without_comment) if comment and not (config.use_parentheses and "noqa" in comment): _comma_maybe = ( - "," if (config.include_trailing_comma and config.use_parentheses) else "" + "," + if ( + config.include_trailing_comma + and config.use_parentheses + and not line_without_comment.rstrip().endswith(",") + ) + else "" ) line_parts[ -1 @@ -92,13 +98,16 @@ def line(content: str, line_separator: str, config: Config = DEFAULT_CONFIG) -> content = next_line.pop() cont_line = _wrap_line( - config.indent + splitter.join(next_line).lstrip(), line_separator, config + config.indent + splitter.join(next_line).lstrip(), + line_separator, + config, ) if config.use_parentheses: if splitter == "as ": output = f"{content}{splitter}{cont_line.lstrip()}" else: _comma = "," if config.include_trailing_comma and not comment else "" + if wrap_mode in ( Modes.VERTICAL_HANGING_INDENT, # type: ignore Modes.VERTICAL_GRID_GROUPED, # type: ignore From ddf0d394653db7c19a7f9a78357caf080c36f11b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 25 Dec 2020 00:00:29 -0800 Subject: [PATCH 291/539] Fix test line lengths --- tests/unit/test_regressions.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 10bd3bf81..89fa09274 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1493,19 +1493,25 @@ def test_isort_adding_second_comma_issue_1621(): """ assert isort.check_code( """from .test import ( - TestTestTestTestTestTest2 as TestTestTestTestTestTest1, # Some really long comment bla bla bla bla bla + TestTestTestTestTestTest2 as TestTestTestTestTestTest1, """ + """# Some really long comment bla bla bla bla bla ) """, profile="black", show_diff=True, ) - assert isort.code( - """from .test import ( - TestTestTestTestTestTest2 as TestTestTestTestTestTest1 # Some really long comment bla bla bla bla bla + assert ( + isort.code( + """from .test import ( + TestTestTestTestTestTest2 as TestTestTestTestTestTest1 """ + """# Some really long comment bla bla bla bla bla ) """, - profile="black", - ) == """from .test import ( - TestTestTestTestTestTest2 as TestTestTestTestTestTest1, # Some really long comment bla bla bla bla bla + profile="black", + ) + == """from .test import ( + TestTestTestTestTestTest2 as TestTestTestTestTestTest1, """ + """# Some really long comment bla bla bla bla bla ) """ + ) From 18ec2a06146b97022deccd8c90ea8725c7a91f28 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 25 Dec 2020 00:02:18 -0800 Subject: [PATCH 292/539] Fixed #1612: In rare circumstances an extra comma is added after import and before comment. marked in changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac6d209c3..7e377fd21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). ### 5.7.0 December TBD + - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. - Implemented #1596: Provide ways for extension formatting and file paths to be specified when using streaming input from CLI. - Implemented #1583: Ability to output and diff within a single API call to `isort.file`. - Implemented #1562, #1592 & #1593: Better more useful fatal error messages. From c48fd911e4afd8f542f561490b16aeaaaabe9fae Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 26 Dec 2020 23:04:40 -0800 Subject: [PATCH 293/539] Expose path based import finding via API --- isort/__init__.py | 1 + isort/api.py | 28 +++++++++++++++++++++++++++- isort/main.py | 13 ++----------- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/isort/__init__.py b/isort/__init__.py index afb67e3ae..03223665d 100644 --- a/isort/__init__.py +++ b/isort/__init__.py @@ -7,6 +7,7 @@ check_stream, find_imports_in_code, find_imports_in_file, + find_imports_in_paths, find_imports_in_stream, place_module, place_module_with_reason, diff --git a/isort/api.py b/isort/api.py index ec4152184..ca5fc301d 100644 --- a/isort/api.py +++ b/isort/api.py @@ -1,13 +1,14 @@ import shutil import sys from io import StringIO +from itertools import chain from pathlib import Path from typing import Iterator, Optional, Set, TextIO, Union, cast from warnings import warn from isort import core -from . import identify, io +from . import files, identify, io from .exceptions import ( ExistingSyntaxErrors, FileSkipComment, @@ -472,6 +473,31 @@ def find_imports_in_file( ) +def find_imports_in_paths( + paths: Iterator[Union[str, Path]], + config: Config = DEFAULT_CONFIG, + file_path: Optional[Path] = None, + unique: bool = False, + **config_kwargs, +) -> Iterator[identify.Import]: + """Finds and returns all imports within the provided source paths. + + - **paths**: A collection of paths to recursively look for imports within. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **unique**: If True, only the first instance of an import is returned. + - ****config_kwargs**: Any config modifications. + """ + config = _config(path=file_path, config=config, **config_kwargs) + yield from chain( + *( + find_imports_in_file(file_name, unique=unique, config=config) + for file_name in files.find(map(str, paths), config, [], []) + ) + ) + + def _config( path: Optional[Path] = None, config: Config = DEFAULT_CONFIG, **config_kwargs ) -> Config: diff --git a/isort/main.py b/isort/main.py index 889f3b623..ba4f3538b 100644 --- a/isort/main.py +++ b/isort/main.py @@ -6,7 +6,6 @@ import sys from gettext import gettext as _ from io import TextIOWrapper -from itertools import chain from pathlib import Path from typing import Any, Dict, List, Optional, Sequence from warnings import warn @@ -16,7 +15,7 @@ from .format import create_terminal_printer from .logo import ASCII_ART from .profiles import profiles -from .settings import DEFAULT_CONFIG, VALID_PY_TARGETS, Config, WrapModes +from .settings import VALID_PY_TARGETS, Config, WrapModes try: from .setuptools_commands import ISortCommand # noqa: F401 @@ -884,15 +883,7 @@ def identify_imports_main( sys.stdin if stdin is None else stdin, unique=arguments.unique ) else: - skipped: List[str] = [] - broken: List[str] = [] - config = DEFAULT_CONFIG - identified_imports = chain( - *( - api.find_imports_in_file(file_name, unique=arguments.unique) - for file_name in files.find(file_names, config, skipped, broken) - ) - ) + identified_imports = api.find_imports_in_paths(file_names) for identified_import in identified_imports: print(str(identified_import)) From f607723c88ac3fb6983b192ab40e2034442ec60b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 27 Dec 2020 23:51:45 -0800 Subject: [PATCH 294/539] Remove no longer needed imports_only functionality in core.py --- isort/core.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/isort/core.py b/isort/core.py index e53a8b87e..a37b707e4 100644 --- a/isort/core.py +++ b/isort/core.py @@ -30,7 +30,6 @@ def process( output_stream: TextIO, extension: str = "py", config: Config = DEFAULT_CONFIG, - imports_only: bool = False, ) -> bool: """Parses stream identifying sections of contiguous imports and sorting them @@ -69,16 +68,6 @@ def process( stripped_line: str = "" end_of_file: bool = False verbose_output: List[str] = [] - all_imports: List[str] = [] - - _output_stream = output_stream # Used if imports_only == True - if imports_only: - - class DevNull(StringIO): - def write(self, *a, **kw): - pass - - output_stream = DevNull() if config.float_to_top: new_input = "" @@ -352,15 +341,6 @@ def write(self, *a, **kw): parsed_content = parse.file_contents(import_section, config=config) verbose_output += parsed_content.verbose_output - if imports_only: - lines_without_imports_set = set(parsed_content.lines_without_imports) - all_imports.extend( - li - for li in parsed_content.in_lines - if li - and li not in lines_without_imports_set - and not li.lstrip().startswith("#") - ) sorted_import_section = output.sorted_imports( parsed_content, @@ -425,10 +405,6 @@ def write(self, *a, **kw): for output_str in verbose_output: print(output_str) - if imports_only: - result = line_separator.join(all_imports) + line_separator - _output_stream.write(result) - return made_changes From 41302ffb6f08607596fd5ce1fd176e30ff2ad7e7 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 28 Dec 2020 01:49:56 -0800 Subject: [PATCH 295/539] Add testing for unique --- isort/main.py | 2 +- tests/unit/test_main.py | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/isort/main.py b/isort/main.py index ba4f3538b..22164f1af 100644 --- a/isort/main.py +++ b/isort/main.py @@ -883,7 +883,7 @@ def identify_imports_main( sys.stdin if stdin is None else stdin, unique=arguments.unique ) else: - identified_imports = api.find_imports_in_paths(file_names) + identified_imports = api.find_imports_in_paths(file_names, unique=arguments.unique) for identified_import in identified_imports: print(str(identified_import)) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index a13f5badc..8de3e2bee 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -995,19 +995,30 @@ def test_only_modified_flag(tmpdir, capsys): def test_identify_imports_main(tmpdir, capsys): - file_content = "import mod2\n" "a = 1\n" "import mod1\n" + file_content = "import mod2\n import mod2\n" "a = 1\n" "import mod1\n" some_file = tmpdir.join("some_file.py") some_file.write(file_content) - file_imports = f"{some_file}:0 import mod2\n{some_file}:2 import mod1\n" - - main.identify_imports_main([str(some_file)]) + file_imports = f"{some_file}:0 import mod2\n{some_file}:3 import mod1\n" + file_imports_with_dupes = ( + f"{some_file}:0 import mod2\n{some_file}:1 import mod2\n" f"{some_file}:3 import mod1\n" + ) + main.identify_imports_main([str(some_file), "--unique"]) out, error = capsys.readouterr() assert out.replace("\r\n", "\n") == file_imports assert not error - main.identify_imports_main(["-"], stdin=as_stream(file_content)) + main.identify_imports_main([str(some_file)]) + out, error = capsys.readouterr() + assert out.replace("\r\n", "\n") == file_imports_with_dupes + assert not error + + main.identify_imports_main(["-", "--unique"], stdin=as_stream(file_content)) out, error = capsys.readouterr() assert out.replace("\r\n", "\n") == file_imports.replace(str(some_file), "") + main.identify_imports_main(["-"], stdin=as_stream(file_content)) + out, error = capsys.readouterr() + assert out.replace("\r\n", "\n") == file_imports_with_dupes.replace(str(some_file), "") + main.identify_imports_main([str(tmpdir)]) From e387e4b7e03aa7451873fde1c4b51af8d2c28c59 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 28 Dec 2020 16:47:33 -0800 Subject: [PATCH 296/539] Allow unique flag across files --- isort/api.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/isort/api.py b/isort/api.py index ca5fc301d..a8f1ba376 100644 --- a/isort/api.py +++ b/isort/api.py @@ -424,6 +424,7 @@ def find_imports_in_stream( config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, unique: bool = False, + _seen: Optional[Set[str]] = None, **config_kwargs, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided code stream. @@ -432,6 +433,7 @@ def find_imports_in_stream( - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **unique**: If True, only the first instance of an import is returned. + - **_seen**: An optional set of imports already seen. Generally meant only for internal use. - ****config_kwargs**: Any config modifications. """ config = _config(path=file_path, config=config, **config_kwargs) @@ -439,7 +441,7 @@ def find_imports_in_stream( if not unique: yield from identified_imports - seen: Set[str] = set() + seen: Set[str] = set() if _seen is None else _seen for identified_import in identified_imports: key = identified_import.statement() if key not in seen: @@ -490,9 +492,10 @@ def find_imports_in_paths( - ****config_kwargs**: Any config modifications. """ config = _config(path=file_path, config=config, **config_kwargs) + seen: Set[str] = set() if unique else None yield from chain( *( - find_imports_in_file(file_name, unique=unique, config=config) + find_imports_in_file(file_name, unique=unique, config=config, _seen=seen) for file_name in files.find(map(str, paths), config, [], []) ) ) From e99a4786d60b4f05a854c689df037ff1e3c64d41 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 29 Dec 2020 13:57:37 -0800 Subject: [PATCH 297/539] Fix typing error --- isort/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/api.py b/isort/api.py index a8f1ba376..7e597ed9c 100644 --- a/isort/api.py +++ b/isort/api.py @@ -492,7 +492,7 @@ def find_imports_in_paths( - ****config_kwargs**: Any config modifications. """ config = _config(path=file_path, config=config, **config_kwargs) - seen: Set[str] = set() if unique else None + seen: Optional[Set[str]] = set() if unique else None yield from chain( *( find_imports_in_file(file_name, unique=unique, config=config, _seen=seen) From 4762ea4cb594e65d31b7c950d0ab638385be54d4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 29 Dec 2020 13:57:59 -0800 Subject: [PATCH 298/539] Update tests to enforce import identifaction line numbers use 1 base indexing --- tests/unit/test_isort.py | 26 +++++++++++++------------- tests/unit/test_main.py | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index aca087092..9655d9f66 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -4942,18 +4942,18 @@ def test_find_imports_in_code() -> None: ) identified_imports = list(map(str, api.find_imports_in_code(test_input))) assert identified_imports == [ - ":0 import first_straight", - ":2 import second_straight", - ":3 import first_from.first_from_function_1", - ":3 import first_from.first_from_function_2", - ":4 import bad_name.good_name", - ":4 import bad_name", - ":5 import parent.some_bad_defs.bad_name_1 as ok_name_1", - ":5 import parent.some_bad_defs.bad_name_2 as ok_name_2", - ":11 import needed_in_bla_2", - ":14 import needed_in_bla", - ":17 import needed_in_bla_bla", - ":21 import needed_in_end", + ":1 import first_straight", + ":3 import second_straight", + ":4 import first_from.first_from_function_1", + ":4 import first_from.first_from_function_2", + ":5 import bad_name.good_name", + ":5 import bad_name", + ":6 import parent.some_bad_defs.bad_name_1 as ok_name_1", + ":6 import parent.some_bad_defs.bad_name_2 as ok_name_2", + ":12 import needed_in_bla_2", + ":15 import needed_in_bla", + ":18 import needed_in_bla_bla", + ":22 import needed_in_end", ] @@ -4969,4 +4969,4 @@ def seekable(self): test_input = NonSeekableTestStream("import m2\n" "import m1\n" "not_import = 7") identified_imports = list(map(str, api.find_imports_in_stream(test_input))) - assert identified_imports == [":0 import m2", ":1 import m1"] + assert identified_imports == [":1 import m2", ":2 import m1"] diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 8de3e2bee..7291cb5fe 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -998,9 +998,9 @@ def test_identify_imports_main(tmpdir, capsys): file_content = "import mod2\n import mod2\n" "a = 1\n" "import mod1\n" some_file = tmpdir.join("some_file.py") some_file.write(file_content) - file_imports = f"{some_file}:0 import mod2\n{some_file}:3 import mod1\n" + file_imports = f"{some_file}:1 import mod2\n{some_file}:4 import mod1\n" file_imports_with_dupes = ( - f"{some_file}:0 import mod2\n{some_file}:1 import mod2\n" f"{some_file}:3 import mod1\n" + f"{some_file}:1 import mod2\n{some_file}:2 import mod2\n" f"{some_file}:4 import mod1\n" ) main.identify_imports_main([str(some_file), "--unique"]) From db0a7c96a13e3210caf2fcda790b03d65e430030 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 29 Dec 2020 13:58:11 -0800 Subject: [PATCH 299/539] Update import identification line numbers to use 1 based indexing --- isort/identify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/identify.py b/isort/identify.py index 09f3cabd9..156d866a7 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -74,7 +74,7 @@ def imports( ) identified_import = partial( Import, - index, + index + 1, # line numbers use 1 based indexing line.startswith(" ") or line.startswith("\n"), cimport=cimports, file_path=file_path, From 1e9b8af7ce5e7245046a95066bac9f6749b52aff Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 00:55:44 -0800 Subject: [PATCH 300/539] Add support for multiple ways of identifying an import as unique --- isort/api.py | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/isort/api.py b/isort/api.py index 7e597ed9c..16e92c975 100644 --- a/isort/api.py +++ b/isort/api.py @@ -1,5 +1,6 @@ import shutil import sys +from enum import Enum from io import StringIO from itertools import chain from pathlib import Path @@ -22,6 +23,32 @@ from .settings import DEFAULT_CONFIG, Config +class ImportKey(Enum): + """Defines how to key an individual import, generally for deduping. + + Import keys are defined from less to more specific: + + from x.y import z as a + ______| | | | + | | | | + PACKAGE | | | + ________| | | + | | | + MODULE | | + _________________| | + | | + ATTRIBUTE | + ______________________| + | + ALIAS + """ + + PACKAGE = 1 + MODULE = 2 + ATTRIBUTE = 3 + ALIAS = 4 + + def sort_code_string( code: str, extension: Optional[str] = None, @@ -399,7 +426,7 @@ def find_imports_in_code( code: str, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, - unique: bool = False, + unique: Union[bool, ImportKey] = False, **config_kwargs, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided code string. @@ -423,7 +450,7 @@ def find_imports_in_stream( input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, - unique: bool = False, + unique: Union[bool, ImportKey] = False, _seen: Optional[Set[str]] = None, **config_kwargs, ) -> Iterator[identify.Import]: @@ -443,8 +470,16 @@ def find_imports_in_stream( seen: Set[str] = set() if _seen is None else _seen for identified_import in identified_imports: - key = identified_import.statement() - if key not in seen: + if unique in (True, ImportKey.ALIAS): + key = identified_import.statement() + elif unique == ImportKey.ATTRIBUTE: + key = f"{identified_import.module}.{identified_import.attribute}" + elif unique == ImportKey.MODULE: + key = identified_import.module + elif unique == ImportKey.PACKAGE: + key = identified_import.module.split(".")[0] + + if key and key not in seen: seen.add(key) yield identified_import @@ -453,7 +488,7 @@ def find_imports_in_file( filename: Union[str, Path], config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, - unique: bool = False, + unique: Union[bool, ImportKey] = False, **config_kwargs, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided source file. @@ -479,7 +514,7 @@ def find_imports_in_paths( paths: Iterator[Union[str, Path]], config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, - unique: bool = False, + unique: Union[bool, ImportKey] = False, **config_kwargs, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided source paths. From 228266772fb4466fd716ad56d6025442f693a077 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 01:33:08 -0800 Subject: [PATCH 301/539] Add quick support from CLI to most uniqueness keys --- isort/main.py | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index 22164f1af..09f3c64e4 100644 --- a/isort/main.py +++ b/isort/main.py @@ -869,12 +869,39 @@ def identify_imports_main( parser.add_argument( "files", nargs="*", help="One or more Python source files that need their imports sorted." ) - parser.add_argument( + + uniqueness = parser.add_mutually_exclusive_group() + uniqueness.add_argument( "--unique", action="store_true", default=False, help="If true, isort will only identify unique imports.", ) + uniqueness.add_argument( + "--packages", + dest="unique", + action="store_const", + const=api.ImportKey.PACKAGE, + default=False, + help="If true, isort will only identify the unique top level modules imported.", + ) + uniqueness.add_argument( + "--modules", + dest="unique", + action="store_const", + const=api.ImportKey.MODULE, + default=False, + help="If true, isort will only identify the unique modules imported.", + ) + uniqueness.add_argument( + "--attributes", + dest="unique", + action="store_const", + const=api.ImportKey.ATTRIBUTE, + default=False, + help="If true, isort will only identify the unique attributes imported.", + ) + arguments = parser.parse_args(argv) file_names = arguments.files @@ -886,7 +913,14 @@ def identify_imports_main( identified_imports = api.find_imports_in_paths(file_names, unique=arguments.unique) for identified_import in identified_imports: - print(str(identified_import)) + if arguments.unique == api.ImportKey.PACKAGE: + print(identified_import.module.split(".")[0]) + elif arguments.unique == api.ImportKey.MODULE: + print(identified_import.module) + elif arguments.unique == api.ImportKey.ATTRIBUTE: + print(f"{identified_import.module}.{identified_import.attribute}") + else: + print(str(identified_import)) def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = None) -> None: From b1e676312930029c3974cadb5bdffcdaaf7d02ba Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 01:50:40 -0800 Subject: [PATCH 302/539] Add support for quick identification of just the top-level imports, before functions and classes. --- isort/identify.py | 11 +++++++++-- isort/output.py | 3 +-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 156d866a7..46e3df5f4 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -3,13 +3,15 @@ """ from functools import partial from pathlib import Path -from typing import Iterator, NamedTuple, Optional, TextIO +from typing import Iterator, NamedTuple, Optional, TextIO, Tuple from isort.parse import _normalize_line, _strip_syntax, skip_line from .comments import parse as parse_comments from .settings import DEFAULT_CONFIG, Config +STATEMENT_DECLARATIONS: Tuple[str, ...] = ("def ", "cdef ", "cpdef ", "class ", "@", "async def") + class Import(NamedTuple): line_number: int @@ -36,7 +38,10 @@ def __str__(self): def imports( - input_stream: TextIO, config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None + input_stream: TextIO, + config: Config = DEFAULT_CONFIG, + file_path: Optional[Path] = None, + top_only: bool = False, ) -> Iterator[Import]: """Parses a python file taking out and categorizing imports.""" in_quote = "" @@ -48,6 +53,8 @@ def imports( ) if skipping_line: + if top_only and not in_quote and line.startswith(STATEMENT_DECLARATIONS): + break continue line, *end_of_line_comment = line.split("#", 1) diff --git a/isort/output.py b/isort/output.py index 1d64785bc..e0855de67 100644 --- a/isort/output.py +++ b/isort/output.py @@ -7,10 +7,9 @@ from . import parse, sorting, wrap from .comments import add_to_line as with_comments +from .identify import STATEMENT_DECLARATIONS from .settings import DEFAULT_CONFIG, Config -STATEMENT_DECLARATIONS: Tuple[str, ...] = ("def ", "cdef ", "cpdef ", "class ", "@", "async def") - def sorted_imports( parsed: parse.ParsedContent, From 95474d38a8cc3edb5ff2eea278b8d8957a2c41be Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 01:51:01 -0800 Subject: [PATCH 303/539] Expand quick identification support of just top import section to isort API --- isort/api.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/isort/api.py b/isort/api.py index 16e92c975..a2bc86981 100644 --- a/isort/api.py +++ b/isort/api.py @@ -427,6 +427,7 @@ def find_imports_in_code( config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, unique: Union[bool, ImportKey] = False, + top_only: bool = False, **config_kwargs, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided code string. @@ -435,6 +436,7 @@ def find_imports_in_code( - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **unique**: If True, only the first instance of an import is returned. + - **top_only**: If True, only return imports that occur before the first function or class. - ****config_kwargs**: Any config modifications. """ yield from find_imports_in_stream( @@ -442,6 +444,7 @@ def find_imports_in_code( config=config, file_path=file_path, unique=unique, + top_only=top_only, **config_kwargs, ) @@ -451,6 +454,7 @@ def find_imports_in_stream( config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, unique: Union[bool, ImportKey] = False, + top_only: bool = False, _seen: Optional[Set[str]] = None, **config_kwargs, ) -> Iterator[identify.Import]: @@ -460,6 +464,7 @@ def find_imports_in_stream( - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **unique**: If True, only the first instance of an import is returned. + - **top_only**: If True, only return imports that occur before the first function or class. - **_seen**: An optional set of imports already seen. Generally meant only for internal use. - ****config_kwargs**: Any config modifications. """ @@ -489,6 +494,7 @@ def find_imports_in_file( config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, unique: Union[bool, ImportKey] = False, + top_only: bool = False, **config_kwargs, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided source file. @@ -498,6 +504,7 @@ def find_imports_in_file( - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **unique**: If True, only the first instance of an import is returned. + - **top_only**: If True, only return imports that occur before the first function or class. - ****config_kwargs**: Any config modifications. """ with io.File.read(filename) as source_file: @@ -515,6 +522,7 @@ def find_imports_in_paths( config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, unique: Union[bool, ImportKey] = False, + top_only: bool = False, **config_kwargs, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided source paths. @@ -524,6 +532,7 @@ def find_imports_in_paths( - **config**: The config object to use when sorting imports. - **file_path**: The disk location where the code string was pulled from. - **unique**: If True, only the first instance of an import is returned. + - **top_only**: If True, only return imports that occur before the first function or class. - ****config_kwargs**: Any config modifications. """ config = _config(path=file_path, config=config, **config_kwargs) From fc3a1ec9daa1fab9c80ba43c2f902203a1ccd7b3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 02:04:14 -0800 Subject: [PATCH 304/539] Expose top-only identification functionality to CLI --- isort/main.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index 09f3c64e4..c9f339647 100644 --- a/isort/main.py +++ b/isort/main.py @@ -869,6 +869,12 @@ def identify_imports_main( parser.add_argument( "files", nargs="*", help="One or more Python source files that need their imports sorted." ) + parser.add_argument( + "--top-only", + action="store_true", + default=False, + help="Only identify imports that occur in before functions or classes.", + ) uniqueness = parser.add_mutually_exclusive_group() uniqueness.add_argument( @@ -907,10 +913,14 @@ def identify_imports_main( file_names = arguments.files if file_names == ["-"]: identified_imports = api.find_imports_in_stream( - sys.stdin if stdin is None else stdin, unique=arguments.unique + sys.stdin if stdin is None else stdin, + unique=arguments.unique, + top_only=arguments.top_only, ) else: - identified_imports = api.find_imports_in_paths(file_names, unique=arguments.unique) + identified_imports = api.find_imports_in_paths( + file_names, unique=arguments.unique, top_only=arguments.top_only + ) for identified_import in identified_imports: if arguments.unique == api.ImportKey.PACKAGE: From 00f6db125235b1bfb4bd5aecefd1f89256aa043d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 02:13:19 -0800 Subject: [PATCH 305/539] Add support even faster identification of imports if only interested in top of file --- isort/api.py | 9 +++++++-- isort/identify.py | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/isort/api.py b/isort/api.py index a2bc86981..6c2011a5f 100644 --- a/isort/api.py +++ b/isort/api.py @@ -469,7 +469,9 @@ def find_imports_in_stream( - ****config_kwargs**: Any config modifications. """ config = _config(path=file_path, config=config, **config_kwargs) - identified_imports = identify.imports(input_stream, config=config, file_path=file_path) + identified_imports = identify.imports( + input_stream, config=config, file_path=file_path, top_only=top_only + ) if not unique: yield from identified_imports @@ -513,6 +515,7 @@ def find_imports_in_file( config=config, file_path=file_path or source_file.path, unique=unique, + top_only=top_only, **config_kwargs, ) @@ -539,7 +542,9 @@ def find_imports_in_paths( seen: Optional[Set[str]] = set() if unique else None yield from chain( *( - find_imports_in_file(file_name, unique=unique, config=config, _seen=seen) + find_imports_in_file( + file_name, unique=unique, config=config, top_only=top_only, _seen=seen + ) for file_name in files.find(map(str, paths), config, [], []) ) ) diff --git a/isort/identify.py b/isort/identify.py index 46e3df5f4..7a9093066 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -52,9 +52,9 @@ def imports( line, in_quote=in_quote, index=index, section_comments=config.section_comments ) + if top_only and not in_quote and line.startswith(STATEMENT_DECLARATIONS): + break if skipping_line: - if top_only and not in_quote and line.startswith(STATEMENT_DECLARATIONS): - break continue line, *end_of_line_comment = line.split("#", 1) From fe27db934b475985b6120aa8fab046a6777097dc Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 02:15:17 -0800 Subject: [PATCH 306/539] Require at least one file or path for import identification CLI --- isort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index c9f339647..c3190349e 100644 --- a/isort/main.py +++ b/isort/main.py @@ -867,7 +867,7 @@ def identify_imports_main( "Use `-` as the first argument to represent stdin." ) parser.add_argument( - "files", nargs="*", help="One or more Python source files that need their imports sorted." + "files", nargs="+", help="One or more Python source files that need their imports sorted." ) parser.add_argument( "--top-only", From 6e5414cb72ee822d351968626bb9a37e10425857 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 02:54:21 -0800 Subject: [PATCH 307/539] Add link following support (and lack thereof) to import identification CLI --- isort/main.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index c3190349e..27866286e 100644 --- a/isort/main.py +++ b/isort/main.py @@ -876,6 +876,14 @@ def identify_imports_main( help="Only identify imports that occur in before functions or classes.", ) + target_group = parser.add_argument_group("target options") + target_group.add_argument( + "--follow-links", + action="store_true", + default=False, + help="Tells isort to follow symlinks that are encountered when running recursively.", + ) + uniqueness = parser.add_mutually_exclusive_group() uniqueness.add_argument( "--unique", @@ -916,10 +924,14 @@ def identify_imports_main( sys.stdin if stdin is None else stdin, unique=arguments.unique, top_only=arguments.top_only, + follow_links=arguments.follow_links, ) else: identified_imports = api.find_imports_in_paths( - file_names, unique=arguments.unique, top_only=arguments.top_only + file_names, + unique=arguments.unique, + top_only=arguments.top_only, + follow_links=arguments.follow_links, ) for identified_import in identified_imports: From 07768d11bbd21ed7760622a7f4644ed0488efa79 Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Wed, 30 Dec 2020 18:52:39 +0100 Subject: [PATCH 308/539] Make force_sort_within_sections respect case force_sort_within_sections only looked at the order_by_type option to determine how to order imports with different case in a section. Whether you order import names by type or not also affected the order of the modules. When force_sort_within_sections is used: * ignore case on the module name if case_sensitive is false, * ignore case on the imported names if order_by_type is false. --- isort/output.py | 1 + isort/sorting.py | 14 ++++++++++++-- tests/unit/test_isort.py | 30 +++++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/isort/output.py b/isort/output.py index e0855de67..2e6e6c1eb 100644 --- a/isort/output.py +++ b/isort/output.py @@ -96,6 +96,7 @@ def sorted_imports( new_section_output, key=partial( sorting.section_key, + case_sensitive=config.case_sensitive, order_by_type=config.order_by_type, force_to_top=config.force_to_top, lexicographical=config.lexicographical, diff --git a/isort/sorting.py b/isort/sorting.py index b614abe79..60401f8fe 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -53,6 +53,7 @@ def module_key( def section_key( line: str, + case_sensitive: bool, order_by_type: bool, force_to_top: List[str], lexicographical: bool = False, @@ -76,8 +77,17 @@ def section_key( line = re.sub("^import ", "", line) if line.split(" ")[0] in force_to_top: section = "A" - if not order_by_type: - line = line.lower() + if not case_sensitive or not order_by_type: + split_module = line.split(" import ", 1) + if len(split_module) > 1: + module_name, names = split_module + if not case_sensitive: + module_name = module_name.lower() + if not order_by_type: + names = names.lower() + line = " import ".join([module_name, names]) + elif not case_sensitive: + line = line.lower() return f"{section}{len(line) if length_sort else ''}{line}" diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 9655d9f66..9d2d410cf 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -2358,10 +2358,10 @@ def test_alphabetic_sorting_no_newlines() -> None: def test_sort_within_section() -> None: """Test to ensure its possible to force isort to sort within sections""" test_input = ( - "from Foob import ar\n" "import foo\n" "from foo import bar\n" "from foo.bar import Quux, baz\n" + "from Foob import ar\n" ) test_output = isort.code(test_input, force_sort_within_sections=True) assert test_output == test_input @@ -2381,6 +2381,34 @@ def test_sort_within_section() -> None: ) assert test_output == test_input + test_input = ( + "from Foob import ar\n" + "import foo\n" + "from foo import bar\n" + "from foo.bar import baz\n" + "from foo.bar import Quux\n" + ) + test_output = isort.code( + code=test_input, + case_sensitive=True, + force_sort_within_sections=True, + order_by_type=False, + force_single_line=True, + ) + assert test_output == test_input + + test_input = ( + "from Foob import ar\n" "import foo\n" "from foo import Quux\n" "from foo import baz\n" + ) + test_output = isort.code( + code=test_input, + case_sensitive=True, + force_sort_within_sections=True, + order_by_type=True, + force_single_line=True, + ) + assert test_output == test_input + def test_sorting_with_two_top_comments() -> None: """Test to ensure isort will sort files that contain 2 top comments""" From 721cfd62f487ff7e5ded42a32c3c083bc4d172c1 Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Wed, 30 Dec 2020 19:18:53 +0100 Subject: [PATCH 309/539] Fix Gitter link in Contributing guide doc --- docs/contributing/1.-contributing-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/1.-contributing-guide.md b/docs/contributing/1.-contributing-guide.md index e51e852d8..2ba296f8d 100644 --- a/docs/contributing/1.-contributing-guide.md +++ b/docs/contributing/1.-contributing-guide.md @@ -60,7 +60,7 @@ Congrats! You're now ready to make a contribution! Use the following as a guide 1. Check the [issues page](https://github.com/pycqa/isort/issues) on GitHub to see if the task you want to complete is listed there. - If it's listed there, write a comment letting others know you are working on it. - If it's not listed in GitHub issues, go ahead and log a new issue. Then add a comment letting everyone know you have it under control. - - If you're not sure if it's something that is good for the main isort project and want immediate feedback, you can discuss it [here](https://gitter.im/pycqa/isort). + - If you're not sure if it's something that is good for the main isort project and want immediate feedback, you can discuss it [here](https://gitter.im/timothycrosley/isort). 2. Create an issue branch for your local work `git checkout -b issue/$ISSUE-NUMBER`. 3. Do your magic here. 4. Ensure your code matches the [HOPE-8 Coding Standard](https://github.com/hugapi/HOPE/blob/master/all/HOPE-8--Style-Guide-for-Hug-Code.md#hope-8----style-guide-for-hug-code) used by the project. From fd7a2dccbb867d23ae5c98d48261de517b83731d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 13:49:00 -0800 Subject: [PATCH 310/539] Config path should never be auto determined for import identification CLI --- isort/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/api.py b/isort/api.py index 6c2011a5f..024b75ac5 100644 --- a/isort/api.py +++ b/isort/api.py @@ -468,7 +468,7 @@ def find_imports_in_stream( - **_seen**: An optional set of imports already seen. Generally meant only for internal use. - ****config_kwargs**: Any config modifications. """ - config = _config(path=file_path, config=config, **config_kwargs) + config = _config(config=config, **config_kwargs) identified_imports = identify.imports( input_stream, config=config, file_path=file_path, top_only=top_only ) @@ -538,7 +538,7 @@ def find_imports_in_paths( - **top_only**: If True, only return imports that occur before the first function or class. - ****config_kwargs**: Any config modifications. """ - config = _config(path=file_path, config=config, **config_kwargs) + config = _config(config=config, **config_kwargs) seen: Optional[Set[str]] = set() if unique else None yield from chain( *( From 570b66e42523e424f217e2e850c55b3d63b0f9c0 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 14:35:49 -0800 Subject: [PATCH 311/539] Undo skip gitignore for black profile --- isort/profiles.py | 1 - 1 file changed, 1 deletion(-) diff --git a/isort/profiles.py b/isort/profiles.py index 523b1ec66..cb8cb5688 100644 --- a/isort/profiles.py +++ b/isort/profiles.py @@ -8,7 +8,6 @@ "use_parentheses": True, "ensure_newline_before_comments": True, "line_length": 88, - "skip_gitignore": True, } django = { "combine_as_imports": True, From 5d0f7e1658a6aa7e1f3bb8d54dc9487218307fb5 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 14:42:32 -0800 Subject: [PATCH 312/539] Updadte changelog to include fix for #1593 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e377fd21..aa65c4df2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.7.0 December TBD - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. + - Fixed #1593: isort encounters bug in Python 3.6.0. - Implemented #1596: Provide ways for extension formatting and file paths to be specified when using streaming input from CLI. - Implemented #1583: Ability to output and diff within a single API call to `isort.file`. - Implemented #1562, #1592 & #1593: Better more useful fatal error messages. From 0b072150304d582ffd67b9343f1dd30a73043625 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 15:05:16 -0800 Subject: [PATCH 313/539] Add initial unit testing for identify - with focuses on yield and raise edge cases currently handled by import sorting core --- tests/unit/test_identify.py | 139 ++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 tests/unit/test_identify.py diff --git a/tests/unit/test_identify.py b/tests/unit/test_identify.py new file mode 100644 index 000000000..08a7e7269 --- /dev/null +++ b/tests/unit/test_identify.py @@ -0,0 +1,139 @@ +from io import StringIO +from typing import List + +from isort import identify + + +def imports_in_code(code: str, **kwargs) -> List[identify.Import]: + return list(identify.imports(StringIO(code, **kwargs))) + + +def test_yield_edge_cases(): + assert not imports_in_code( + """ +raise SomeException("Blah") \\ + from exceptionsInfo.popitem()[1] +""" + ) + assert not imports_in_code( + """ +def generator_function(): + yield \\ + from other_function()[1] +""" + ) + assert ( + len( + imports_in_code( + """ +# one + +# two + + +def function(): + # three \\ + import b + import a +""" + ) + ) + == 2 + ) + assert ( + len( + imports_in_code( + """ +# one + +# two + + +def function(): + raise \\ + import b + import a +""" + ) + ) + == 1 + ) + assert not imports_in_code( + """ +def generator_function(): + ( + yield + from other_function()[1] + ) +""" + ) + assert not imports_in_code( + """ +def generator_function(): + ( + ( + (((( + ((((( + (( + ((( + yield + + + + from other_function()[1] + ))))))))))))) + ))) +""" + ) + assert ( + len( + imports_in_code( + """ +def generator_function(): + import os + + yield \\ + from other_function()[1] +""" + ) + ) + == 1 + ) + + assert not imports_in_code( + """ +def generator_function(): + ( + ( + (((( + ((((( + (( + ((( + yield +""" + ) + assert not imports_in_code( + """ +def generator_function(): + ( + ( + (((( + ((((( + (( + ((( + raise ( +""" + ) + assert not imports_in_code( + """ +def generator_function(): + ( + ( + (((( + ((((( + (( + ((( + raise \\ + from \\ +""" + ) From 8b83d56588e9d4d0cf2e37c893de3f20ef78c3f8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 15:05:27 -0800 Subject: [PATCH 314/539] Fix handling of yield and raise statements in import identification --- isort/identify.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/isort/identify.py b/isort/identify.py index 7a9093066..1f8185cbe 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -57,6 +57,25 @@ def imports( if skipping_line: continue + stripped_line = line.strip().split("#")[0] + if stripped_line.startswith("raise") or stripped_line.startswith("yield"): + if stripped_line == "yield": + while not stripped_line or stripped_line == "yield": + try: + index, next_line = next(indexed_input) + except StopIteration: + break + + stripped_line = next_line.strip().split("#")[0] + while stripped_line.endswith("\\"): + try: + index, next_line = next(indexed_input) + except StopIteration: + break + + stripped_line = next_line.strip().split("#")[0] + continue + line, *end_of_line_comment = line.split("#", 1) statements = [line.strip() for line in line.split(";")] if end_of_line_comment: From 69a89c0b8224895e8d524116e46ac8cd965a181f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 15:31:39 -0800 Subject: [PATCH 315/539] Add additional identification test cases --- tests/unit/test_identify.py | 65 +++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_identify.py b/tests/unit/test_identify.py index 08a7e7269..8515d459e 100644 --- a/tests/unit/test_identify.py +++ b/tests/unit/test_identify.py @@ -5,10 +5,49 @@ def imports_in_code(code: str, **kwargs) -> List[identify.Import]: - return list(identify.imports(StringIO(code, **kwargs))) + return list(identify.imports(StringIO(code), **kwargs)) -def test_yield_edge_cases(): +def test_top_only(): + imports_in_function = """ +import abc + +def xyz(): + import defg +""" + assert len(imports_in_code(imports_in_function)) == 2 + assert len(imports_in_code(imports_in_function, top_only=True)) == 1 + + imports_after_class = """ +import abc + +class MyObject: + pass + +import defg +""" + assert len(imports_in_code(imports_after_class)) == 2 + assert len(imports_in_code(imports_after_class, top_only=True)) == 1 + + +def test_top_doc_string(): + assert ( + len( + imports_in_code( + ''' +#! /bin/bash import x +"""import abc +from y import z +""" +import abc +''' + ) + ) + == 1 + ) + + +def test_yield_and_raise_edge_cases(): assert not imports_in_code( """ raise SomeException("Blah") \\ @@ -137,3 +176,25 @@ def generator_function(): from \\ """ ) + assert ( + len( + imports_in_code( + """ +def generator_function(): + ( + ( + (((( + ((((( + (( + ((( + raise \\ + from \\ + import c + + import abc + import xyz +""" + ) + ) + == 2 + ) From 15502c875c46d0c8761fb3f7c2b6c1ef4b518a49 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 15:38:59 -0800 Subject: [PATCH 316/539] Expose ImportKey from main isort import --- isort/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/isort/__init__.py b/isort/__init__.py index 03223665d..fdb1d6e93 100644 --- a/isort/__init__.py +++ b/isort/__init__.py @@ -1,6 +1,7 @@ """Defines the public isort interface""" from . import settings from ._version import __version__ +from .api import ImportKey from .api import check_code_string as check_code from .api import ( check_file, From 0383c3668d289db2d41a43165b564f75bb5e64ff Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 16:26:36 -0800 Subject: [PATCH 317/539] Fix indented identification isort --- isort/identify.py | 30 +++++++++++----------- tests/unit/test_identify.py | 50 ++++++++++++++++++++++++++++++++++++- tests/unit/test_isort.py | 9 +++---- tests/unit/test_main.py | 2 +- 4 files changed, 70 insertions(+), 21 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index 1f8185cbe..dbab7f6ae 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -47,17 +47,17 @@ def imports( in_quote = "" indexed_input = enumerate(input_stream) - for index, line in indexed_input: + for index, raw_line in indexed_input: (skipping_line, in_quote) = skip_line( - line, in_quote=in_quote, index=index, section_comments=config.section_comments + raw_line, in_quote=in_quote, index=index, section_comments=config.section_comments ) - if top_only and not in_quote and line.startswith(STATEMENT_DECLARATIONS): + if top_only and not in_quote and raw_line.startswith(STATEMENT_DECLARATIONS): break if skipping_line: continue - stripped_line = line.strip().split("#")[0] + stripped_line = raw_line.strip().split("#")[0] if stripped_line.startswith("raise") or stripped_line.startswith("yield"): if stripped_line == "yield": while not stripped_line or stripped_line == "yield": @@ -76,16 +76,16 @@ def imports( stripped_line = next_line.strip().split("#")[0] continue - line, *end_of_line_comment = line.split("#", 1) + line, *end_of_line_comment = raw_line.split("#", 1) statements = [line.strip() for line in line.split(";")] if end_of_line_comment: statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" for statement in statements: line, _raw_line = _normalize_line(statement) - if line.lstrip().startswith(("import ", "cimport ")): + if line.startswith(("import ", "cimport ")): type_of_import = "straight" - elif line.lstrip().startswith("from "): + elif line.startswith("from "): type_of_import = "from" else: continue @@ -101,7 +101,7 @@ def imports( identified_import = partial( Import, index + 1, # line numbers use 1 based indexing - line.startswith(" ") or line.startswith("\n"), + raw_line.startswith((" ", "\t")), cimport=cimports, file_path=file_path, ) @@ -177,18 +177,20 @@ def imports( direct_imports.remove("as") just_imports[1:] = direct_imports if attribute == alias and config.remove_redundant_aliases: - pass + yield identified_import(top_level_module, attribute) else: yield identified_import(top_level_module, attribute, alias=alias) else: module = just_imports[as_index - 1] alias = just_imports[as_index + 1] - direct_imports.remove(alias) - direct_imports.remove("as") - just_imports[1:] = direct_imports - if not (module == alias and config.remove_redundant_aliases): - yield identified_import(module, alias) + just_imports.remove(alias) + just_imports.remove("as") + just_imports.remove(module) + if module == alias and config.remove_redundant_aliases: + yield identified_import(module) + else: + yield identified_import(module, alias=alias) if just_imports: if type_of_import == "from": diff --git a/tests/unit/test_identify.py b/tests/unit/test_identify.py index 8515d459e..64c9f28a9 100644 --- a/tests/unit/test_identify.py +++ b/tests/unit/test_identify.py @@ -1,7 +1,7 @@ from io import StringIO from typing import List -from isort import identify +from isort import Config, identify def imports_in_code(code: str, **kwargs) -> List[identify.Import]: @@ -198,3 +198,51 @@ def generator_function(): ) == 2 ) + + +def test_complex_examples(): + assert ( + len( + imports_in_code( + """ +import a, b, c; import n + +x = ( + 1, + 2, + 3 +) + +import x +from os \\ + import path +from os ( + import path +) +from os import ( \\""" + ) + ) + == 7 + ) + assert not imports_in_code("from os import \\") + + +def test_aliases(): + assert imports_in_code("import os as os")[0].alias == "os" + assert not imports_in_code( + "import os as os", + config=Config( + remove_redundant_aliases=True, + ), + )[0].alias + + assert imports_in_code("from os import path as path")[0].alias == "path" + assert not imports_in_code( + "from os import path as path", config=Config(remove_redundant_aliases=True) + )[0].alias + + +def test_indented(): + assert not imports_in_code("import os")[0].indented + assert imports_in_code(" import os")[0].indented + assert imports_in_code("\timport os")[0].indented diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 9655d9f66..125bd0dfa 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -4946,13 +4946,12 @@ def test_find_imports_in_code() -> None: ":3 import second_straight", ":4 import first_from.first_from_function_1", ":4 import first_from.first_from_function_2", - ":5 import bad_name.good_name", - ":5 import bad_name", + ":5 import bad_name as good_name", ":6 import parent.some_bad_defs.bad_name_1 as ok_name_1", ":6 import parent.some_bad_defs.bad_name_2 as ok_name_2", - ":12 import needed_in_bla_2", - ":15 import needed_in_bla", - ":18 import needed_in_bla_bla", + ":12 indented import needed_in_bla_2", + ":15 indented import needed_in_bla", + ":18 indented import needed_in_bla_bla", ":22 import needed_in_end", ] diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 7291cb5fe..d1ae50214 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -995,7 +995,7 @@ def test_only_modified_flag(tmpdir, capsys): def test_identify_imports_main(tmpdir, capsys): - file_content = "import mod2\n import mod2\n" "a = 1\n" "import mod1\n" + file_content = "import mod2\nimport mod2\n" "a = 1\n" "import mod1\n" some_file = tmpdir.join("some_file.py") some_file.write(file_content) file_imports = f"{some_file}:1 import mod2\n{some_file}:4 import mod1\n" From 8e70db8d0092c333c112f0685bc9e416caab9d80 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 16:51:48 -0800 Subject: [PATCH 318/539] 100% test coverage for new identify module --- isort/identify.py | 18 ++++++++++-------- tests/unit/test_identify.py | 28 +++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/isort/identify.py b/isort/identify.py index dbab7f6ae..ff0282443 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -74,7 +74,7 @@ def imports( break stripped_line = next_line.strip().split("#")[0] - continue + continue # pragma: no cover line, *end_of_line_comment = raw_line.split("#", 1) statements = [line.strip() for line in line.split(";")] @@ -88,7 +88,7 @@ def imports( elif line.startswith("from "): type_of_import = "from" else: - continue + continue # pragma: no cover import_string, _ = parse_comments(line) normalized_import_string = ( @@ -135,13 +135,15 @@ def imports( break line, _ = parse_comments(next_line) import_string += "\n" + line - - if import_string.strip().endswith( - (" import", " cimport") - ) or line.strip().startswith(("import ", "cimport ")): - import_string += "\n" + line else: - import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip() + if import_string.strip().endswith( + (" import", " cimport") + ) or line.strip().startswith(("import ", "cimport ")): + import_string += "\n" + line + else: + import_string = ( + import_string.rstrip().rstrip("\\") + " " + line.lstrip() + ) if type_of_import == "from": import_string = ( diff --git a/tests/unit/test_identify.py b/tests/unit/test_identify.py index 64c9f28a9..c2918b529 100644 --- a/tests/unit/test_identify.py +++ b/tests/unit/test_identify.py @@ -2,6 +2,7 @@ from typing import List from isort import Config, identify +from isort.identify import Import def imports_in_code(code: str, **kwargs) -> List[identify.Import]: @@ -219,12 +220,37 @@ def test_complex_examples(): from os ( import path ) +from os import \\ + path +from os \\ + import ( + path + ) from os import ( \\""" ) ) - == 7 + == 9 ) assert not imports_in_code("from os import \\") + assert ( + imports_in_code( + """ +from os \\ + import ( + system""" + ) + == [ + Import( + line_number=2, + indented=False, + module="os", + attribute="system", + alias=None, + cimport=False, + file_path=None, + ) + ] + ) def test_aliases(): From 3eb14eb975509a34843aa6384a19374854f5979f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 17:06:58 -0800 Subject: [PATCH 319/539] 100% test coverage --- tests/unit/test_api.py | 17 ++++++++++++++++- tests/unit/test_main.py | 12 ++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 20c2fdae6..2247d8fc5 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -5,7 +5,7 @@ import pytest -from isort import api +from isort import ImportKey, api from isort.settings import Config imperfect_content = "import b\nimport a\n" @@ -86,3 +86,18 @@ def test_sort_code_string_mixed_newlines(): def test_find_imports_in_file(imperfect): found_imports = list(api.find_imports_in_file(imperfect)) assert "b" in [found_import.module for found_import in found_imports] + + +def test_find_imports_in_code(): + code = """ +from x.y import z as a +from x.y import z as a +from x.y import z +import x.y +import x +""" + assert len(list(api.find_imports_in_code(code))) == 5 + assert len(list(api.find_imports_in_code(code, unique=True))) == 4 + assert len(list(api.find_imports_in_code(code, unique=ImportKey.ATTRIBUTE))) == 3 + assert len(list(api.find_imports_in_code(code, unique=ImportKey.MODULE))) == 2 + assert len(list(api.find_imports_in_code(code, unique=ImportKey.PACKAGE))) == 1 diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index d1ae50214..9a78cbad8 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -1022,3 +1022,15 @@ def test_identify_imports_main(tmpdir, capsys): assert out.replace("\r\n", "\n") == file_imports_with_dupes.replace(str(some_file), "") main.identify_imports_main([str(tmpdir)]) + + main.identify_imports_main(["-", "--packages"], stdin=as_stream(file_content)) + out, error = capsys.readouterr() + len(out.split("\n")) == 2 + + main.identify_imports_main(["-", "--modules"], stdin=as_stream(file_content)) + out, error = capsys.readouterr() + len(out.split("\n")) == 2 + + main.identify_imports_main(["-", "--attributes"], stdin=as_stream(file_content)) + out, error = capsys.readouterr() + len(out.split("\n")) == 2 From 8372d71147334e5a4677197fe1fc1a61c5080435 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 17:07:50 -0800 Subject: [PATCH 320/539] Bump to version 5.7.0 --- CHANGELOG.md | 2 +- isort/_version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa65c4df2..dcfeb972d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). -### 5.7.0 December TBD +### 5.7.0 December 30th 2020 - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. - Fixed #1593: isort encounters bug in Python 3.6.0. - Implemented #1596: Provide ways for extension formatting and file paths to be specified when using streaming input from CLI. diff --git a/isort/_version.py b/isort/_version.py index f0b716b28..7f4ce2d4c 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.6.4" +__version__ = "5.7.0" diff --git a/pyproject.toml b/pyproject.toml index 122906ec7..0889cc4f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.6.4" +version = "5.7.0" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" From 681b26c766f863a790bd50eb9d2e0a80022f1343 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 17:11:21 -0800 Subject: [PATCH 321/539] Add @dwanderson-intel, Quentin Santos (@qsantos), and @gofr to acknowledgements --- docs/contributing/4.-acknowledgements.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 48f7d8cf0..03ddc1e67 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -212,6 +212,9 @@ Code Contributors - Tamara (@infinityxxx) - Akihiro Nitta (@akihironitta) - Samuel Gaist (@sgaist) +- @dwanderson-intel +- Quentin Santos (@qsantos) +- @gofr Documenters =================== From a8f4ff3e85b7a26cad2c0af499028caa94f5febf Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 30 Dec 2020 17:12:43 -0800 Subject: [PATCH 322/539] Regenerate config option docs --- docs/configuration/options.md | 20 ++++++++++++++++++++ isort/main.py | 7 ------- isort/settings.py | 1 - 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index e6d0e8058..0b5de2306 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -1010,6 +1010,15 @@ Combines all the bare straight imports of the same section in a single line. Won **Python & Config File Name:** follow_links **CLI Flags:** **Not Supported** +## Indented Import Headings + +**No Description** + +**Type:** Bool +**Default:** `True` +**Python & Config File Name:** indented_import_headings +**CLI Flags:** **Not Supported** + ## Show Version Displays the currently installed version of isort. @@ -1169,6 +1178,17 @@ Provide the filename associated with a stream. - --filename +## Dont Float To Top + +Forces --float-to-top setting off. See --float-to-top for more information. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** **Not Supported** +**CLI Flags:** + +- --dont-float-to-top + ## Dont Order By Type Don't order imports by type, which is determined by case, in addition to alphabetically. diff --git a/isort/main.py b/isort/main.py index 27866286e..5a7e1b179 100644 --- a/isort/main.py +++ b/isort/main.py @@ -622,13 +622,6 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="ext_format", help="Tells isort to format the given files according to an extensions formatting rules.", ) - output_group.add_argument( - "--dedupe-imports", - dest="dedupe_imports", - help="Tells isort to dedupe duplicated imports that are seen at the root across " - "import blocks.", - action="store_true", - ) section_group.add_argument( "--sd", diff --git a/isort/settings.py b/isort/settings.py index 4ea8c0d0b..f9c041478 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -204,7 +204,6 @@ class _Config: auto_identify_namespace_packages: bool = True namespace_packages: FrozenSet[str] = frozenset() follow_links: bool = True - dedupe_imports: bool = True indented_import_headings: bool = True def __post_init__(self): From e94a8a94f99fb7022ea3c6eb243c6ce3737fb4a9 Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Thu, 31 Dec 2020 17:34:30 +0100 Subject: [PATCH 323/539] Move fix behind a flag --- isort/main.py | 8 ++++++ isort/output.py | 1 + isort/settings.py | 1 + isort/sorting.py | 9 +++++- tests/unit/test_isort.py | 62 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 79 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index 27866286e..811bdaf65 100644 --- a/isort/main.py +++ b/isort/main.py @@ -667,6 +667,14 @@ def _build_arg_parser() -> argparse.ArgumentParser: "(like from itertools import groupby). Instead, sort the imports by module, " "independent of import style.", ) + section_group.add_argument( + "--hcss", + "--honor-case-in-force-sorted-sections", + action="store_true", + dest="honor_case_in_force_sorted_sections", + help="Honor `--case-sensitive` when `--force-sort-within-sections` is being used. " + "Without this option set, `--order-by-type` decides module name ordering too.", + ) section_group.add_argument( "--fass", "--force-alphabetical-sort-within-sections", diff --git a/isort/output.py b/isort/output.py index 2e6e6c1eb..54253884b 100644 --- a/isort/output.py +++ b/isort/output.py @@ -97,6 +97,7 @@ def sorted_imports( key=partial( sorting.section_key, case_sensitive=config.case_sensitive, + honor_case_in_force_sorted_sections=config.honor_case_in_force_sorted_sections, order_by_type=config.order_by_type, force_to_top=config.force_to_top, lexicographical=config.lexicographical, diff --git a/isort/settings.py b/isort/settings.py index 4ea8c0d0b..44484e218 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -206,6 +206,7 @@ class _Config: follow_links: bool = True dedupe_imports: bool = True indented_import_headings: bool = True + honor_case_in_force_sorted_sections: bool = False def __post_init__(self): py_version = self.py_version diff --git a/isort/sorting.py b/isort/sorting.py index 60401f8fe..9a4336b8e 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -54,6 +54,7 @@ def module_key( def section_key( line: str, case_sensitive: bool, + honor_case_in_force_sorted_sections: bool, order_by_type: bool, force_to_top: List[str], lexicographical: bool = False, @@ -77,7 +78,11 @@ def section_key( line = re.sub("^import ", "", line) if line.split(" ")[0] in force_to_top: section = "A" - if not case_sensitive or not order_by_type: + # * If honor_case_in_force_sorted_sections is true, and case_sensitive and + # order_by_type are different, only ignore case in part of the line. + # * Otherwise, let order_by_type decide the sorting of the whole line. This + # is only "correct" if case_sensitive and order_by_type have the same value. + if honor_case_in_force_sorted_sections and case_sensitive != order_by_type: split_module = line.split(" import ", 1) if len(split_module) > 1: module_name, names = split_module @@ -88,6 +93,8 @@ def section_key( line = " import ".join([module_name, names]) elif not case_sensitive: line = line.lower() + elif not order_by_type: + line = line.lower() return f"{section}{len(line) if length_sort else ''}{line}" diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 9d2d410cf..f057cd81d 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -2358,10 +2358,10 @@ def test_alphabetic_sorting_no_newlines() -> None: def test_sort_within_section() -> None: """Test to ensure its possible to force isort to sort within sections""" test_input = ( + "from Foob import ar\n" "import foo\n" "from foo import bar\n" "from foo.bar import Quux, baz\n" - "from Foob import ar\n" ) test_output = isort.code(test_input, force_sort_within_sections=True) assert test_output == test_input @@ -2381,6 +2381,64 @@ def test_sort_within_section() -> None: ) assert test_output == test_input + test_input = ( + "import foo\n" + "from foo import bar\n" + "from foo.bar import baz\n" + "from foo.bar import Quux\n" + "from Foob import ar\n" + ) + test_output = isort.code( + code=test_input, + case_sensitive=True, + force_sort_within_sections=True, + order_by_type=False, + force_single_line=True, + ) + assert test_output == test_input + + test_input = ( + "from Foob import ar\n" "import foo\n" "from foo import Quux\n" "from foo import baz\n" + ) + test_output = isort.code( + code=test_input, + case_sensitive=True, + force_sort_within_sections=True, + order_by_type=True, + force_single_line=True, + ) + assert test_output == test_input + + +def test_sort_within_section_case_honored() -> None: + """Ensure isort can do partial case-sensitive sorting in force-sorted sections""" + test_input = ( + "import foo\n" + "from foo import bar\n" + "from foo.bar import Quux, baz\n" + "from Foob import ar\n" + ) + test_output = isort.code( + test_input, force_sort_within_sections=True, honor_case_in_force_sorted_sections=True + ) + assert test_output == test_input + + test_input = ( + "import foo\n" + "from foo import bar\n" + "from foo.bar import baz\n" + "from foo.bar import Quux\n" + "from Foob import ar\n" + ) + test_output = isort.code( + code=test_input, + force_sort_within_sections=True, + honor_case_in_force_sorted_sections=True, + order_by_type=False, + force_single_line=True, + ) + assert test_output == test_input + test_input = ( "from Foob import ar\n" "import foo\n" @@ -2392,6 +2450,7 @@ def test_sort_within_section() -> None: code=test_input, case_sensitive=True, force_sort_within_sections=True, + honor_case_in_force_sorted_sections=True, order_by_type=False, force_single_line=True, ) @@ -2404,6 +2463,7 @@ def test_sort_within_section() -> None: code=test_input, case_sensitive=True, force_sort_within_sections=True, + honor_case_in_force_sorted_sections=True, order_by_type=True, force_single_line=True, ) From 05d8542b0450fecb47d7b5068136e03e72739a89 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 1 Jan 2021 14:24:52 -0800 Subject: [PATCH 324/539] Update pytest --- poetry.lock | 726 +++++++++++++++++++++++++++---------------------- pyproject.toml | 2 +- 2 files changed, 407 insertions(+), 321 deletions(-) diff --git a/poetry.lock b/poetry.lock index 21fa4fabc..526dbbe38 100644 --- a/poetry.lock +++ b/poetry.lock @@ -8,15 +8,15 @@ python-versions = "*" [[package]] name = "appnope" -version = "0.1.0" -description = "Disable App Nap on OS X 10.9" +version = "0.1.2" +description = "Disable App Nap on macOS >= 10.9" category = "dev" optional = false python-versions = "*" [[package]] name = "arrow" -version = "0.16.0" +version = "0.17.0" description = "Better dates & times for Python" category = "dev" optional = false @@ -35,16 +35,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "20.1.0" +version = "20.3.0" description = "Classes Without Boilerplate" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] -docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] [[package]] name = "backcall" @@ -56,16 +57,16 @@ python-versions = "*" [[package]] name = "bandit" -version = "1.6.2" +version = "1.7.0" description = "Security oriented static analyser for python code." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} GitPython = ">=1.0.1" -PyYAML = ">=3.13" +PyYAML = ">=5.3.1" six = ">=1.10.0" stevedore = ">=1.20.0" @@ -105,7 +106,7 @@ d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] name = "cached-property" -version = "1.5.1" +version = "1.5.2" description = "A decorator for caching properties in classes." category = "main" optional = false @@ -121,7 +122,7 @@ python-versions = ">=2.7" [[package]] name = "certifi" -version = "2020.6.20" +version = "2020.12.5" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -145,7 +146,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "colorama" -version = "0.4.3" +version = "0.4.4" description = "Cross-platform colored terminal text." category = "main" optional = false @@ -183,7 +184,7 @@ six = ">=1.10" [[package]] name = "coverage" -version = "5.2.1" +version = "5.3.1" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -194,7 +195,7 @@ toml = ["toml"] [[package]] name = "cruft" -version = "2.3.0" +version = "2.6.0" description = "Allows you to maintain all the necessary cruft for packaging and building projects separate from the code you intentionally write. Built on-top of CookieCutter." category = "dev" optional = false @@ -212,11 +213,11 @@ examples = ["examples (>=1.0.2,<2.0.0)"] [[package]] name = "dataclasses" -version = "0.6" +version = "0.8" description = "A backport of the dataclasses module for Python 3.6" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6, <3.7" [[package]] name = "decorator" @@ -366,7 +367,7 @@ gitdb = ">=4.0.1" [[package]] name = "gitpython" -version = "3.1.7" +version = "3.1.11" description = "Python Git Library" category = "dev" optional = false @@ -405,8 +406,8 @@ python-versions = "*" [[package]] name = "hstspreload" -version = "2020.8.25" -description = "Chromium HSTS Preload list as a Python package and updated daily" +version = "2020.12.22" +description = "Chromium HSTS Preload list as a Python package" category = "dev" optional = false python-versions = ">=3.6" @@ -463,18 +464,18 @@ python-versions = "*" [[package]] name = "hypothesis" -version = "5.29.3" +version = "5.43.5" description = "A library for property-based testing" category = "dev" optional = false -python-versions = ">=3.5.2" +python-versions = ">=3.6" [package.dependencies] attrs = ">=19.2.0" sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] -all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "numpy (>=1.9.0)", "pandas (>=0.19)", "pytest (>=4.3)", "python-dateutil (>=1.4)", "pytz (>=2014.1)"] +all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.3)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] cli = ["click (>=7.0)", "black (>=19.10b0)"] dateutil = ["python-dateutil (>=1.4)"] django = ["pytz (>=2014.1)", "django (>=2.2)"] @@ -482,9 +483,11 @@ dpcontracts = ["dpcontracts (>=0.4)"] ghostwriter = ["black (>=19.10b0)"] lark = ["lark-parser (>=0.6.5)"] numpy = ["numpy (>=1.9.0)"] -pandas = ["pandas (>=0.19)"] +pandas = ["pandas (>=0.25)"] pytest = ["pytest (>=4.3)"] pytz = ["pytz (>=2014.1)"] +redis = ["redis (>=3.0.0)"] +zoneinfo = ["importlib-resources (>=3.3.0)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] [[package]] name = "hypothesis-auto" @@ -503,14 +506,14 @@ pytest = ["pytest (>=4.0.0,<5.0.0)"] [[package]] name = "hypothesmith" -version = "0.1.4" +version = "0.1.7" description = "Hypothesis strategies for generating Python programs, something like CSmith" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -hypothesis = ">=5.23.7" +hypothesis = ">=5.41.0" lark-parser = ">=0.7.2" libcst = ">=0.3.8" @@ -532,18 +535,27 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "1.7.0" +version = "3.3.0" description = "Read metadata from Python packages" category = "main" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] +docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" [[package]] name = "ipython" @@ -586,18 +598,18 @@ python-versions = "*" [[package]] name = "jedi" -version = "0.17.2" +version = "0.18.0" description = "An autocompletion tool for Python that can be used for text editors." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [package.dependencies] -parso = ">=0.7.0,<0.8.0" +parso = ">=0.8.0,<0.9.0" [package.extras] -qa = ["flake8 (==3.7.9)"] -testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"] [[package]] name = "jinja2" @@ -627,39 +639,40 @@ jinja2 = "*" [[package]] name = "joblib" -version = "0.16.0" -description = "Lightweight pipelining: using Python functions as pipeline jobs." +version = "1.0.0" +description = "Lightweight pipelining with Python functions" category = "dev" optional = false python-versions = ">=3.6" [[package]] name = "lark-parser" -version = "0.9.0" +version = "0.11.1" description = "a modern parsing library" category = "dev" optional = false python-versions = "*" [package.extras] +nearley = ["js2py"] regex = ["regex"] [[package]] name = "libcst" -version = "0.3.10" +version = "0.3.16" description = "A concrete syntax tree with AST-like properties for Python 3.5, 3.6, 3.7 and 3.8 programs." category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -dataclasses = {version = "*", markers = "python_version < \"3.7\""} +dataclasses = {version = ">=0.6.0", markers = "python_version < \"3.7\""} pyyaml = ">=5.2" typing-extensions = ">=3.7.4.2" typing-inspect = ">=0.4.0" [package.extras] -dev = ["black", "codecov", "coverage", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "isort", "flake8", "jupyter", "nbsphinx", "pyre-check", "sphinx", "sphinx-rtd-theme"] +dev = ["black (==20.8b1)", "codecov (>=2.1.4)", "coverage (>=4.5.4)", "fixit (==0.1.1)", "flake8 (>=3.7.8)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "isort (==5.5.3)", "jupyter (>=1.0.0)", "nbsphinx (>=0.4.2)", "pyre-check (==0.0.41)", "sphinx-rtd-theme (>=0.4.3)", "prompt-toolkit (>=2.0.9)", "tox (>=3.18.1)"] [[package]] name = "livereload" @@ -706,11 +719,11 @@ lingua = ["lingua"] [[package]] name = "markdown" -version = "3.2.2" +version = "3.3.3" description = "Python implementation of Markdown." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} @@ -753,7 +766,7 @@ tornado = ">=5.0" [[package]] name = "mkdocs-material" -version = "5.5.9" +version = "5.5.14" description = "A Material Design theme for MkDocs" category = "dev" optional = false @@ -768,7 +781,7 @@ pymdown-extensions = ">=7.0" [[package]] name = "mkdocs-material-extensions" -version = "1.0" +version = "1.0.1" description = "Extension pack for Python Markdown." category = "dev" optional = false @@ -777,14 +790,6 @@ python-versions = ">=3.5" [package.dependencies] mkdocs-material = ">=5.0.0" -[[package]] -name = "more-itertools" -version = "8.4.0" -description = "More routines for operating on iterables, beyond itertools" -category = "dev" -optional = false -python-versions = ">=3.5" - [[package]] name = "mypy" version = "0.761" @@ -833,7 +838,7 @@ twitter = ["twython"] [[package]] name = "numpy" -version = "1.19.1" +version = "1.19.4" description = "NumPy is the fundamental package for array computing with Python." category = "dev" optional = false @@ -852,7 +857,7 @@ six = ">=1.8.0" [[package]] name = "packaging" -version = "20.4" +version = "20.8" description = "Core utilities for Python packages" category = "main" optional = false @@ -860,22 +865,22 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] pyparsing = ">=2.0.2" -six = "*" [[package]] name = "parso" -version = "0.7.1" +version = "0.8.1" description = "A Python Parser" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.extras] -testing = ["docopt", "pytest (>=3.0.7)"] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] [[package]] name = "pathspec" -version = "0.8.0" +version = "0.8.1" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false @@ -883,11 +888,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pbr" -version = "5.4.5" +version = "5.5.1" description = "Python Build Reasonableness" category = "dev" optional = false -python-versions = "*" +python-versions = ">=2.6" [[package]] name = "pdocs" @@ -904,7 +909,7 @@ Markdown = ">=3.0.0,<4.0.0" [[package]] name = "pep517" -version = "0.8.2" +version = "0.9.1" description = "Wrappers to build Python packages using PEP 517 hooks" category = "main" optional = false @@ -1062,7 +1067,7 @@ wcwidth = "*" [[package]] name = "ptyprocess" -version = "0.6.0" +version = "0.7.0" description = "Run a subprocess in a pseudo terminal" category = "dev" optional = false @@ -1070,7 +1075,7 @@ python-versions = "*" [[package]] name = "py" -version = "1.9.0" +version = "1.10.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" category = "dev" optional = false @@ -1086,7 +1091,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pydantic" -version = "1.6.1" +version = "1.7.3" description = "Data validation and settings management using python 3.6 type hinting" category = "dev" optional = false @@ -1102,7 +1107,7 @@ typing_extensions = ["typing-extensions (>=3.7.2)"] [[package]] name = "pydocstyle" -version = "5.1.0" +version = "5.1.1" description = "Python docstring style checker" category = "dev" optional = false @@ -1121,7 +1126,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.6.1" +version = "2.7.3" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -1162,25 +1167,24 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pytest" -version = "5.4.3" +version = "6.2.1" description = "pytest: simple powerful testing with Python" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=17.4.0" +attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -more-itertools = ">=4.0.0" +iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<1.0" -py = ">=1.5.0" -wcwidth = "*" +pluggy = ">=0.12,<1.0.0a1" +py = ">=1.8.2" +toml = "*" [package.extras] -checkqa-mypy = ["mypy (==v0.761)"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] @@ -1247,7 +1251,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "regex" -version = "2020.7.14" +version = "2020.11.13" description = "Alternative regular expression module, to replace re." category = "dev" optional = false @@ -1255,7 +1259,7 @@ python-versions = "*" [[package]] name = "requests" -version = "2.24.0" +version = "2.25.1" description = "Python HTTP for Humans." category = "main" optional = false @@ -1263,9 +1267,9 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" +chardet = ">=3.0.2,<5" idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +urllib3 = ">=1.21.1,<1.27" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] @@ -1273,7 +1277,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] [[package]] name = "requirementslib" -version = "1.5.13" +version = "1.5.16" description = "A tool for converting between pip-style and pipfile requirements." category = "main" optional = false @@ -1281,7 +1285,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] appdirs = "*" -attrs = ">=18.2" +attrs = ">=19.2" cached-property = "*" distlib = ">=0.2.8" orderedmultidict = "*" @@ -1313,7 +1317,7 @@ idna2008 = ["idna"] [[package]] name = "safety" -version = "1.9.0" +version = "1.10.0" description = "Checks installed dependencies for known vulnerabilities." category = "dev" optional = false @@ -1354,7 +1358,7 @@ smmap = ">=3.0.1" [[package]] name = "sniffio" -version = "1.1.0" +version = "1.2.0" description = "Sniff out which async library your code is running under" category = "dev" optional = false @@ -1373,7 +1377,7 @@ python-versions = "*" [[package]] name = "sortedcontainers" -version = "2.2.2" +version = "2.3.0" description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" category = "dev" optional = false @@ -1381,7 +1385,7 @@ python-versions = "*" [[package]] name = "stevedore" -version = "3.2.0" +version = "3.3.0" description = "Manage dynamic plugins for Python applications" category = "dev" optional = false @@ -1401,11 +1405,11 @@ python-versions = "*" [[package]] name = "toml" -version = "0.10.1" +version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" category = "main" optional = false -python-versions = "*" +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tomlkit" @@ -1417,7 +1421,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "tornado" -version = "6.0.4" +version = "6.1" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." category = "dev" optional = false @@ -1425,14 +1429,15 @@ python-versions = ">= 3.5" [[package]] name = "tqdm" -version = "4.48.2" +version = "4.55.0" description = "Fast, Extensible Progress Meter" category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" [package.extras] -dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] +dev = ["py-make (>=0.1.0)", "twine", "wheel"] +telegram = ["requests"] [[package]] name = "traitlets" @@ -1452,7 +1457,7 @@ test = ["pytest", "mock"] [[package]] name = "typed-ast" -version = "1.4.1" +version = "1.4.2" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false @@ -1479,7 +1484,7 @@ doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown- name = "typing-extensions" version = "3.7.4.3" description = "Backported and Experimental Type Hints for Python 3.5+" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -1497,7 +1502,7 @@ typing-extensions = ">=3.7.4" [[package]] name = "urllib3" -version = "1.25.10" +version = "1.26.2" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -1505,7 +1510,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] @@ -1564,7 +1569,7 @@ python-versions = "*" [[package]] name = "zipp" -version = "3.1.0" +version = "3.4.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false @@ -1572,7 +1577,7 @@ python-versions = ">=3.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["jaraco.itertools", "func-timeout"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [extras] colors = ["colorama"] @@ -1582,7 +1587,7 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "363f453324e3010e63a4f5281f2fadbf74d958296d7b61d22758d771b137f546" +content-hash = "8d92b325ce222b42dfd7e2a8e6e5abd4f213cba6903e6b36edd322b8d9878fdf" [metadata.files] appdirs = [ @@ -1590,28 +1595,28 @@ appdirs = [ {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] appnope = [ - {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, - {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, + {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, + {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, ] arrow = [ - {file = "arrow-0.16.0-py2.py3-none-any.whl", hash = "sha256:98184d8dd3e5d30b96c2df4596526f7de679ccb467f358b82b0f686436f3a6b8"}, - {file = "arrow-0.16.0.tar.gz", hash = "sha256:92aac856ea5175c804f7ccb96aca4d714d936f1c867ba59d747a8096ec30e90a"}, + {file = "arrow-0.17.0-py2.py3-none-any.whl", hash = "sha256:e098abbd9af3665aea81bdd6c869e93af4feb078e98468dd351c383af187aac5"}, + {file = "arrow-0.17.0.tar.gz", hash = "sha256:ff08d10cda1d36c68657d6ad20d74fbea493d980f8b2d45344e00d6ed2bf6ed4"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-20.1.0-py2.py3-none-any.whl", hash = "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff"}, - {file = "attrs-20.1.0.tar.gz", hash = "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a"}, + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] backcall = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] bandit = [ - {file = "bandit-1.6.2-py2.py3-none-any.whl", hash = "sha256:336620e220cf2d3115877685e264477ff9d9abaeb0afe3dc7264f55fa17a3952"}, - {file = "bandit-1.6.2.tar.gz", hash = "sha256:41e75315853507aa145d62a78a2a6c5e3240fe14ee7c601459d0df9418196065"}, + {file = "bandit-1.7.0-py3-none-any.whl", hash = "sha256:216be4d044209fa06cf2a3e51b319769a51be8318140659719aa7a115c35ed07"}, + {file = "bandit-1.7.0.tar.gz", hash = "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"}, ] binaryornot = [ {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"}, @@ -1621,15 +1626,15 @@ black = [ {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] cached-property = [ - {file = "cached-property-1.5.1.tar.gz", hash = "sha256:9217a59f14a5682da7c4b8829deadbfc194ac22e9908ccf7c8820234e80a1504"}, - {file = "cached_property-1.5.1-py2.py3-none-any.whl", hash = "sha256:3a026f1a54135677e7da5ce819b0c690f156f37976f3e30c5430740725203d7f"}, + {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, + {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, ] cerberus = [ {file = "Cerberus-1.3.2.tar.gz", hash = "sha256:302e6694f206dd85cb63f13fd5025b31ab6d38c99c50c6d769f8fa0b0f299589"}, ] certifi = [ - {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, - {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] chardet = [ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, @@ -1640,8 +1645,8 @@ click = [ {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] colorama = [ - {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, - {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] contextvars = [ {file = "contextvars-2.4.tar.gz", hash = "sha256:f38c908aaa59c14335eeea12abea5f443646216c4e29380d7bf34d2018e2c39e"}, @@ -1651,48 +1656,63 @@ cookiecutter = [ {file = "cookiecutter-1.7.2.tar.gz", hash = "sha256:efb6b2d4780feda8908a873e38f0e61778c23f6a2ea58215723bcceb5b515dac"}, ] coverage = [ - {file = "coverage-5.2.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4"}, - {file = "coverage-5.2.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01"}, - {file = "coverage-5.2.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8"}, - {file = "coverage-5.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59"}, - {file = "coverage-5.2.1-cp27-cp27m-win32.whl", hash = "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3"}, - {file = "coverage-5.2.1-cp27-cp27m-win_amd64.whl", hash = "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f"}, - {file = "coverage-5.2.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd"}, - {file = "coverage-5.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651"}, - {file = "coverage-5.2.1-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b"}, - {file = "coverage-5.2.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d"}, - {file = "coverage-5.2.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3"}, - {file = "coverage-5.2.1-cp35-cp35m-win32.whl", hash = "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0"}, - {file = "coverage-5.2.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962"}, - {file = "coverage-5.2.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082"}, - {file = "coverage-5.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716"}, - {file = "coverage-5.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb"}, - {file = "coverage-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d"}, - {file = "coverage-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546"}, - {file = "coverage-5.2.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811"}, - {file = "coverage-5.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258"}, - {file = "coverage-5.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034"}, - {file = "coverage-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46"}, - {file = "coverage-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8"}, - {file = "coverage-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0"}, - {file = "coverage-5.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd"}, - {file = "coverage-5.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b"}, - {file = "coverage-5.2.1-cp38-cp38-win32.whl", hash = "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd"}, - {file = "coverage-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d"}, - {file = "coverage-5.2.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3"}, - {file = "coverage-5.2.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4"}, - {file = "coverage-5.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4"}, - {file = "coverage-5.2.1-cp39-cp39-win32.whl", hash = "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89"}, - {file = "coverage-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b"}, - {file = "coverage-5.2.1.tar.gz", hash = "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b"}, + {file = "coverage-5.3.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fabeeb121735d47d8eab8671b6b031ce08514c86b7ad8f7d5490a7b6dcd6267d"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7e4d159021c2029b958b2363abec4a11db0ce8cd43abb0d9ce44284cb97217e7"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:378ac77af41350a8c6b8801a66021b52da8a05fd77e578b7380e876c0ce4f528"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e448f56cfeae7b1b3b5bcd99bb377cde7c4eb1970a525c770720a352bc4c8044"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:cc44e3545d908ecf3e5773266c487ad1877be718d9dc65fc7eb6e7d14960985b"}, + {file = "coverage-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:08b3ba72bd981531fd557f67beee376d6700fba183b167857038997ba30dd297"}, + {file = "coverage-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:8dacc4073c359f40fcf73aede8428c35f84639baad7e1b46fce5ab7a8a7be4bb"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ee2f1d1c223c3d2c24e3afbb2dd38be3f03b1a8d6a83ee3d9eb8c36a52bee899"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9a9d4ff06804920388aab69c5ea8a77525cf165356db70131616acd269e19b36"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:782a5c7df9f91979a7a21792e09b34a658058896628217ae6362088b123c8500"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:fda29412a66099af6d6de0baa6bd7c52674de177ec2ad2630ca264142d69c6c7"}, + {file = "coverage-5.3.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:f2c6888eada180814b8583c3e793f3f343a692fc802546eed45f40a001b1169f"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8f33d1156241c43755137288dea619105477961cfa7e47f48dbf96bc2c30720b"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b239711e774c8eb910e9b1ac719f02f5ae4bf35fa0420f438cdc3a7e4e7dd6ec"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:f54de00baf200b4539a5a092a759f000b5f45fd226d6d25a76b0dff71177a714"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:be0416074d7f253865bb67630cf7210cbc14eb05f4099cc0f82430135aaa7a3b"}, + {file = "coverage-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:c46643970dff9f5c976c6512fd35768c4a3819f01f61169d8cdac3f9290903b7"}, + {file = "coverage-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9a4f66259bdd6964d8cf26142733c81fb562252db74ea367d9beb4f815478e72"}, + {file = "coverage-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6e5174f8ca585755988bc278c8bb5d02d9dc2e971591ef4a1baabdf2d99589b"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3911c2ef96e5ddc748a3c8b4702c61986628bb719b8378bf1e4a6184bbd48fe4"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c5ec71fd4a43b6d84ddb88c1df94572479d9a26ef3f150cef3dacefecf888105"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f51dbba78d68a44e99d484ca8c8f604f17e957c1ca09c3ebc2c7e3bbd9ba0448"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a2070c5affdb3a5e751f24208c5c4f3d5f008fa04d28731416e023c93b275277"}, + {file = "coverage-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:535dc1e6e68fad5355f9984d5637c33badbdc987b0c0d303ee95a6c979c9516f"}, + {file = "coverage-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:a4857f7e2bc6921dbd487c5c88b84f5633de3e7d416c4dc0bb70256775551a6c"}, + {file = "coverage-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fac3c432851038b3e6afe086f777732bcf7f6ebbfd90951fa04ee53db6d0bcdd"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cd556c79ad665faeae28020a0ab3bda6cd47d94bec48e36970719b0b86e4dcf4"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a66ca3bdf21c653e47f726ca57f46ba7fc1f260ad99ba783acc3e58e3ebdb9ff"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ab110c48bc3d97b4d19af41865e14531f300b482da21783fdaacd159251890e8"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e52d3d95df81c8f6b2a1685aabffadf2d2d9ad97203a40f8d61e51b70f191e4e"}, + {file = "coverage-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:fa10fee7e32213f5c7b0d6428ea92e3a3fdd6d725590238a3f92c0de1c78b9d2"}, + {file = "coverage-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ce6f3a147b4b1a8b09aae48517ae91139b1b010c5f36423fa2b866a8b23df879"}, + {file = "coverage-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:93a280c9eb736a0dcca19296f3c30c720cb41a71b1f9e617f341f0a8e791a69b"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3102bb2c206700a7d28181dbe04d66b30780cde1d1c02c5f3c165cf3d2489497"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8ffd4b204d7de77b5dd558cdff986a8274796a1e57813ed005b33fd97e29f059"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a607ae05b6c96057ba86c811d9c43423f35e03874ffb03fbdcd45e0637e8b631"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:3a3c3f8863255f3c31db3889f8055989527173ef6192a283eb6f4db3c579d830"}, + {file = "coverage-5.3.1-cp38-cp38-win32.whl", hash = "sha256:ff1330e8bc996570221b450e2d539134baa9465f5cb98aff0e0f73f34172e0ae"}, + {file = "coverage-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:3498b27d8236057def41de3585f317abae235dd3a11d33e01736ffedb2ef8606"}, + {file = "coverage-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb499d2b3d1d7b7ba23abe8bf26df5f06ba8c71127f188333dddcf356b4b63f"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3b14b1da110ea50c8bcbadc3b82c3933974dbeea1832e814aab93ca1163cd4c1"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:76b2775dda7e78680d688daabcb485dc87cf5e3184a0b3e012e1d40e38527cc8"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:cef06fb382557f66d81d804230c11ab292d94b840b3cb7bf4450778377b592f4"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f61319e33222591f885c598e3e24f6a4be3533c1d70c19e0dc59e83a71ce27d"}, + {file = "coverage-5.3.1-cp39-cp39-win32.whl", hash = "sha256:cc6f8246e74dd210d7e2b56c76ceaba1cc52b025cd75dbe96eb48791e0250e98"}, + {file = "coverage-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:2757fa64e11ec12220968f65d086b7a29b6583d16e9a544c889b22ba98555ef1"}, + {file = "coverage-5.3.1-pp36-none-any.whl", hash = "sha256:723d22d324e7997a651478e9c5a3120a0ecbc9a7e94071f7e1954562a8806cf3"}, + {file = "coverage-5.3.1-pp37-none-any.whl", hash = "sha256:c89b558f8a9a5a6f2cfc923c304d49f0ce629c3bd85cb442ca258ec20366394c"}, + {file = "coverage-5.3.1.tar.gz", hash = "sha256:38f16b1317b8dd82df67ed5daa5f5e7c959e46579840d77a67a4ceb9cef0a50b"}, ] cruft = [ - {file = "cruft-2.3.0-py3-none-any.whl", hash = "sha256:ca973c1ca9e4add9893483dbce02cd8930e105f8940afe0d087a14b70c6068de"}, - {file = "cruft-2.3.0.tar.gz", hash = "sha256:7c0f7682765e76fcf31adf877ea6f74372a0ab9554d8f8d6766e8e0413730e52"}, + {file = "cruft-2.6.0-py3-none-any.whl", hash = "sha256:80e32a2fd8103f3c57c96af1c25d96d748072727a8563c58a65eece56a02b441"}, + {file = "cruft-2.6.0.tar.gz", hash = "sha256:bbeea9fb69812afd74a8140cca5ae7fdab761d4df50b137f2437fab9e72c4580"}, ] dataclasses = [ - {file = "dataclasses-0.6-py3-none-any.whl", hash = "sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f"}, - {file = "dataclasses-0.6.tar.gz", hash = "sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"}, + {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, + {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, ] decorator = [ {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, @@ -1761,8 +1781,8 @@ gitdb2 = [ {file = "gitdb2-4.0.2.tar.gz", hash = "sha256:0986cb4003de743f2b3aba4c828edd1ab58ce98e1c4a8acf72ef02760d4beb4e"}, ] gitpython = [ - {file = "GitPython-3.1.7-py3-none-any.whl", hash = "sha256:fa3b92da728a457dd75d62bb5f3eb2816d99a7fe6c67398e260637a40e3fafb5"}, - {file = "GitPython-3.1.7.tar.gz", hash = "sha256:2db287d71a284e22e5c2846042d0602465c7434d910406990d5b74df4afb0858"}, + {file = "GitPython-3.1.11-py3-none-any.whl", hash = "sha256:6eea89b655917b500437e9668e4a12eabdcf00229a0df1762aabd692ef9b746b"}, + {file = "GitPython-3.1.11.tar.gz", hash = "sha256:befa4d101f91bad1b632df4308ec64555db684c360bd7d2130b4807d49ce86b8"}, ] h11 = [ {file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"}, @@ -1777,8 +1797,8 @@ hpack = [ {file = "hpack-3.0.0.tar.gz", hash = "sha256:8eec9c1f4bfae3408a3f30500261f7e6a65912dc138526ea054f9ad98892e9d2"}, ] hstspreload = [ - {file = "hstspreload-2020.8.25-py3-none-any.whl", hash = "sha256:c96401eca4669340b423abd711d2d5d03ddf0685461f95e9cfe500d5e9acf3d2"}, - {file = "hstspreload-2020.8.25.tar.gz", hash = "sha256:3129613419c13ea62411ec7375d79840e28004cbb6a585909ddcbeee401bea14"}, + {file = "hstspreload-2020.12.22-py3-none-any.whl", hash = "sha256:f09afa7015257d33ead35137ddfee3604040b7f9eefbe995dacb4d04744f4034"}, + {file = "hstspreload-2020.12.22.tar.gz", hash = "sha256:8bd8cdf180627e6289805efad5399a55bedf2e707fdcbb243b74298b95db48c6"}, ] httpcore = [ {file = "httpcore-0.9.1-py3-none-any.whl", hash = "sha256:9850fe97a166a794d7e920590d5ec49a05488884c9fc8b5dba8561effab0c2a0"}, @@ -1797,16 +1817,16 @@ hyperframe = [ {file = "hyperframe-5.2.0.tar.gz", hash = "sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"}, ] hypothesis = [ - {file = "hypothesis-5.29.3-py3-none-any.whl", hash = "sha256:07b865184494a64cf2e18090ecfb876c97d303973c2f97139a07be361b0c3a28"}, - {file = "hypothesis-5.29.3.tar.gz", hash = "sha256:e6cf92a94a5108d326e45df5a2b256dc0d57f9663d13efdebcadcfbad9accc31"}, + {file = "hypothesis-5.43.5-py3-none-any.whl", hash = "sha256:546db914a7a7be1ccacbd408cf4cec4fa958b96b4015a2216f8187e4f0ec7eaa"}, + {file = "hypothesis-5.43.5.tar.gz", hash = "sha256:9377cd796a5bca3c0ae74ef1c592aa231d3a04cde948467bace9344148ee75cb"}, ] hypothesis-auto = [ {file = "hypothesis-auto-1.1.4.tar.gz", hash = "sha256:5e2c2fb09dc09842512d80630bb792359a1d33d2c0473ad47ee23da0be9e32b1"}, {file = "hypothesis_auto-1.1.4-py3-none-any.whl", hash = "sha256:fea8560c4522c0fd490ed8cc17e420b95dabebb11b9b334c59bf2d768839015f"}, ] hypothesmith = [ - {file = "hypothesmith-0.1.4-py3-none-any.whl", hash = "sha256:bc45f45808078d2bbe6c3806af3b3604bde35624964fcc6b849cecadf254d3a9"}, - {file = "hypothesmith-0.1.4.tar.gz", hash = "sha256:5628fb1a06233c70751105635bc3cee789c82358041b4518c2cab5300e73cd65"}, + {file = "hypothesmith-0.1.7-py3-none-any.whl", hash = "sha256:f37d0b55fec60d31c4fff271e6ae38100fce4c622c584aae0037f08163fea93f"}, + {file = "hypothesmith-0.1.7.tar.gz", hash = "sha256:97802ad8136033e65db748bbb5a7c9193b9e4df85028f074484db993e8548019"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, @@ -1827,8 +1847,12 @@ immutables = [ {file = "immutables-0.14.tar.gz", hash = "sha256:a0a1cc238b678455145bae291d8426f732f5255537ed6a5b7645949704c70a78"}, ] importlib-metadata = [ - {file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"}, - {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, + {file = "importlib_metadata-3.3.0-py3-none-any.whl", hash = "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"}, + {file = "importlib_metadata-3.3.0.tar.gz", hash = "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] ipython = [ {file = "ipython-7.16.1-py3-none-any.whl", hash = "sha256:2dbcc8c27ca7d3cfe4fcdff7f45b27f9a8d3edfa70ff8024a71c7a8eb5f09d64"}, @@ -1839,8 +1863,8 @@ ipython-genutils = [ {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, ] jedi = [ - {file = "jedi-0.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"}, - {file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"}, + {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"}, + {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, ] jinja2 = [ {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, @@ -1851,15 +1875,16 @@ jinja2-time = [ {file = "jinja2_time-0.2.0-py2.py3-none-any.whl", hash = "sha256:d3eab6605e3ec8b7a0863df09cc1d23714908fa61aa6986a845c20ba488b4efa"}, ] joblib = [ - {file = "joblib-0.16.0-py3-none-any.whl", hash = "sha256:d348c5d4ae31496b2aa060d6d9b787864dd204f9480baaa52d18850cb43e9f49"}, - {file = "joblib-0.16.0.tar.gz", hash = "sha256:8f52bf24c64b608bf0b2563e0e47d6fcf516abc8cfafe10cfd98ad66d94f92d6"}, + {file = "joblib-1.0.0-py3-none-any.whl", hash = "sha256:75ead23f13484a2a414874779d69ade40d4fa1abe62b222a23cd50d4bc822f6f"}, + {file = "joblib-1.0.0.tar.gz", hash = "sha256:7ad866067ac1fdec27d51c8678ea760601b70e32ff1881d4dc8e1171f2b64b24"}, ] lark-parser = [ - {file = "lark-parser-0.9.0.tar.gz", hash = "sha256:9e7589365d6b6de1cca40b0eaec31104a3fb96a37a11a9dfd5098e95b50aa6cd"}, + {file = "lark-parser-0.11.1.tar.gz", hash = "sha256:20bdefdf1b6e9bcb38165ea5cc4f27921a99c6f4c35264a3a953fd60335f1f8c"}, + {file = "lark_parser-0.11.1-py2.py3-none-any.whl", hash = "sha256:8b747e1f544dcc2789e3feaddd2a50c6a73bed69d62e9c69760c1e1f7d23495f"}, ] libcst = [ - {file = "libcst-0.3.10-py3-none-any.whl", hash = "sha256:e9395d952a490e6fc160f2bea8df139bdf1fdcb3fe4c01b88893da279eff00de"}, - {file = "libcst-0.3.10.tar.gz", hash = "sha256:b0dccbfc1cff7bfa3214980e1d2d90b4e00b2fed002d4b276a8a411217738df3"}, + {file = "libcst-0.3.16-py3-none-any.whl", hash = "sha256:2c9e40245b8cb49b5219c76b36fe7037effa7594b9e6d5a092be99f8083d2415"}, + {file = "libcst-0.3.16.tar.gz", hash = "sha256:99c200004b6e845642eea7a433844d144994767f9ed50705171720b76d28cf7e"}, ] livereload = [ {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, @@ -1873,8 +1898,8 @@ mako = [ {file = "Mako-1.1.3.tar.gz", hash = "sha256:8195c8c1400ceb53496064314c6736719c6f25e7479cd24c77be3d9361cddc27"}, ] markdown = [ - {file = "Markdown-3.2.2-py3-none-any.whl", hash = "sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59"}, - {file = "Markdown-3.2.2.tar.gz", hash = "sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17"}, + {file = "Markdown-3.3.3-py3-none-any.whl", hash = "sha256:c109c15b7dc20a9ac454c9e6025927d44460b85bd039da028d85e2b6d0bcc328"}, + {file = "Markdown-3.3.3.tar.gz", hash = "sha256:5d9f2b5ca24bc4c7a390d22323ca4bad200368612b5aaa7796babf971d2b2f18"}, ] markupsafe = [ {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, @@ -1920,16 +1945,12 @@ mkdocs = [ {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, ] mkdocs-material = [ - {file = "mkdocs-material-5.5.9.tar.gz", hash = "sha256:37d60947993b939318945c170c7b3a153646976badf57648fd70befc3b54c830"}, - {file = "mkdocs_material-5.5.9-py2.py3-none-any.whl", hash = "sha256:c8cb3c8c44bf10ed7ac1eb568d93a4346efe03fee2994b6a80e96559421cec49"}, + {file = "mkdocs-material-5.5.14.tar.gz", hash = "sha256:9f3237df1a72f91e0330a5e3b3711cb7aaa0d5705f9585e6ce6fbacaa16e777f"}, + {file = "mkdocs_material-5.5.14-py2.py3-none-any.whl", hash = "sha256:a0b3b3e67606e04d13e777d13f3195402ea09e0c3ce279abc3666cac2c5b3a6d"}, ] mkdocs-material-extensions = [ - {file = "mkdocs-material-extensions-1.0.tar.gz", hash = "sha256:17d7491e189af75700310b7ec33c6c48a22060b8b445001deca040cb60471cde"}, - {file = "mkdocs_material_extensions-1.0-py3-none-any.whl", hash = "sha256:09569c3694b5acc1e8334c9730e52b4bcde65fc9d613cc20e49af131ef1a9ca0"}, -] -more-itertools = [ - {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"}, - {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"}, + {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, + {file = "mkdocs_material_extensions-1.0.1-py3-none-any.whl", hash = "sha256:d90c807a88348aa6d1805657ec5c0b2d8d609c110e62b9dce4daf7fa981fa338"}, ] mypy = [ {file = "mypy-0.761-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:7f672d02fffcbace4db2b05369142e0506cdcde20cea0e07c7c2171c4fd11dd6"}, @@ -1955,60 +1976,68 @@ nltk = [ {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, ] numpy = [ - {file = "numpy-1.19.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1cca51512299841bf69add3b75361779962f9cee7d9ee3bb446d5982e925b69"}, - {file = "numpy-1.19.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c9591886fc9cbe5532d5df85cb8e0cc3b44ba8ce4367bd4cf1b93dc19713da72"}, - {file = "numpy-1.19.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:cf1347450c0b7644ea142712619533553f02ef23f92f781312f6a3553d031fc7"}, - {file = "numpy-1.19.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:ed8a311493cf5480a2ebc597d1e177231984c818a86875126cfd004241a73c3e"}, - {file = "numpy-1.19.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3673c8b2b29077f1b7b3a848794f8e11f401ba0b71c49fbd26fb40b71788b132"}, - {file = "numpy-1.19.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:56ef7f56470c24bb67fb43dae442e946a6ce172f97c69f8d067ff8550cf782ff"}, - {file = "numpy-1.19.1-cp36-cp36m-win32.whl", hash = "sha256:aaf42a04b472d12515debc621c31cf16c215e332242e7a9f56403d814c744624"}, - {file = "numpy-1.19.1-cp36-cp36m-win_amd64.whl", hash = "sha256:082f8d4dd69b6b688f64f509b91d482362124986d98dc7dc5f5e9f9b9c3bb983"}, - {file = "numpy-1.19.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e4f6d3c53911a9d103d8ec9518190e52a8b945bab021745af4939cfc7c0d4a9e"}, - {file = "numpy-1.19.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5b6885c12784a27e957294b60f97e8b5b4174c7504665333c5e94fbf41ae5d6a"}, - {file = "numpy-1.19.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1bc0145999e8cb8aed9d4e65dd8b139adf1919e521177f198529687dbf613065"}, - {file = "numpy-1.19.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:5a936fd51049541d86ccdeef2833cc89a18e4d3808fe58a8abeb802665c5af93"}, - {file = "numpy-1.19.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ef71a1d4fd4858596ae80ad1ec76404ad29701f8ca7cdcebc50300178db14dfc"}, - {file = "numpy-1.19.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b9792b0ac0130b277536ab8944e7b754c69560dac0415dd4b2dbd16b902c8954"}, - {file = "numpy-1.19.1-cp37-cp37m-win32.whl", hash = "sha256:b12e639378c741add21fbffd16ba5ad25c0a1a17cf2b6fe4288feeb65144f35b"}, - {file = "numpy-1.19.1-cp37-cp37m-win_amd64.whl", hash = "sha256:8343bf67c72e09cfabfab55ad4a43ce3f6bf6e6ced7acf70f45ded9ebb425055"}, - {file = "numpy-1.19.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e45f8e981a0ab47103181773cc0a54e650b2aef8c7b6cd07405d0fa8d869444a"}, - {file = "numpy-1.19.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:667c07063940e934287993366ad5f56766bc009017b4a0fe91dbd07960d0aba7"}, - {file = "numpy-1.19.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:480fdd4dbda4dd6b638d3863da3be82873bba6d32d1fc12ea1b8486ac7b8d129"}, - {file = "numpy-1.19.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:935c27ae2760c21cd7354402546f6be21d3d0c806fffe967f745d5f2de5005a7"}, - {file = "numpy-1.19.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:309cbcfaa103fc9a33ec16d2d62569d541b79f828c382556ff072442226d1968"}, - {file = "numpy-1.19.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7ed448ff4eaffeb01094959b19cbaf998ecdee9ef9932381420d514e446601cd"}, - {file = "numpy-1.19.1-cp38-cp38-win32.whl", hash = "sha256:de8b4a9b56255797cbddb93281ed92acbc510fb7b15df3f01bd28f46ebc4edae"}, - {file = "numpy-1.19.1-cp38-cp38-win_amd64.whl", hash = "sha256:92feb989b47f83ebef246adabc7ff3b9a59ac30601c3f6819f8913458610bdcc"}, - {file = "numpy-1.19.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:e1b1dc0372f530f26a03578ac75d5e51b3868b9b76cd2facba4c9ee0eb252ab1"}, - {file = "numpy-1.19.1.zip", hash = "sha256:b8456987b637232602ceb4d663cb34106f7eb780e247d51a260b84760fd8f491"}, + {file = "numpy-1.19.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9b30d4bd69498fc0c3fe9db5f62fffbb06b8eb9321f92cc970f2969be5e3949"}, + {file = "numpy-1.19.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fedbd128668ead37f33917820b704784aff695e0019309ad446a6d0b065b57e4"}, + {file = "numpy-1.19.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8ece138c3a16db8c1ad38f52eb32be6086cc72f403150a79336eb2045723a1ad"}, + {file = "numpy-1.19.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:64324f64f90a9e4ef732be0928be853eee378fd6a01be21a0a8469c4f2682c83"}, + {file = "numpy-1.19.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ad6f2ff5b1989a4899bf89800a671d71b1612e5ff40866d1f4d8bcf48d4e5764"}, + {file = "numpy-1.19.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d6c7bb82883680e168b55b49c70af29b84b84abb161cbac2800e8fcb6f2109b6"}, + {file = "numpy-1.19.4-cp36-cp36m-win32.whl", hash = "sha256:13d166f77d6dc02c0a73c1101dd87fdf01339febec1030bd810dcd53fff3b0f1"}, + {file = "numpy-1.19.4-cp36-cp36m-win_amd64.whl", hash = "sha256:448ebb1b3bf64c0267d6b09a7cba26b5ae61b6d2dbabff7c91b660c7eccf2bdb"}, + {file = "numpy-1.19.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:27d3f3b9e3406579a8af3a9f262f5339005dd25e0ecf3cf1559ff8a49ed5cbf2"}, + {file = "numpy-1.19.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:16c1b388cc31a9baa06d91a19366fb99ddbe1c7b205293ed072211ee5bac1ed2"}, + {file = "numpy-1.19.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e5b6ed0f0b42317050c88022349d994fe72bfe35f5908617512cd8c8ef9da2a9"}, + {file = "numpy-1.19.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:18bed2bcb39e3f758296584337966e68d2d5ba6aab7e038688ad53c8f889f757"}, + {file = "numpy-1.19.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:fe45becb4c2f72a0907c1d0246ea6449fe7a9e2293bb0e11c4e9a32bb0930a15"}, + {file = "numpy-1.19.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:6d7593a705d662be5bfe24111af14763016765f43cb6923ed86223f965f52387"}, + {file = "numpy-1.19.4-cp37-cp37m-win32.whl", hash = "sha256:6ae6c680f3ebf1cf7ad1d7748868b39d9f900836df774c453c11c5440bc15b36"}, + {file = "numpy-1.19.4-cp37-cp37m-win_amd64.whl", hash = "sha256:9eeb7d1d04b117ac0d38719915ae169aa6b61fca227b0b7d198d43728f0c879c"}, + {file = "numpy-1.19.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cb1017eec5257e9ac6209ac172058c430e834d5d2bc21961dceeb79d111e5909"}, + {file = "numpy-1.19.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:edb01671b3caae1ca00881686003d16c2209e07b7ef8b7639f1867852b948f7c"}, + {file = "numpy-1.19.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f29454410db6ef8126c83bd3c968d143304633d45dc57b51252afbd79d700893"}, + {file = "numpy-1.19.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:ec149b90019852266fec2341ce1db513b843e496d5a8e8cdb5ced1923a92faab"}, + {file = "numpy-1.19.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1aeef46a13e51931c0b1cf8ae1168b4a55ecd282e6688fdb0a948cc5a1d5afb9"}, + {file = "numpy-1.19.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:08308c38e44cc926bdfce99498b21eec1f848d24c302519e64203a8da99a97db"}, + {file = "numpy-1.19.4-cp38-cp38-win32.whl", hash = "sha256:5734bdc0342aba9dfc6f04920988140fb41234db42381cf7ccba64169f9fe7ac"}, + {file = "numpy-1.19.4-cp38-cp38-win_amd64.whl", hash = "sha256:09c12096d843b90eafd01ea1b3307e78ddd47a55855ad402b157b6c4862197ce"}, + {file = "numpy-1.19.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e452dc66e08a4ce642a961f134814258a082832c78c90351b75c41ad16f79f63"}, + {file = "numpy-1.19.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a5d897c14513590a85774180be713f692df6fa8ecf6483e561a6d47309566f37"}, + {file = "numpy-1.19.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:a09f98011236a419ee3f49cedc9ef27d7a1651df07810ae430a6b06576e0b414"}, + {file = "numpy-1.19.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:50e86c076611212ca62e5a59f518edafe0c0730f7d9195fec718da1a5c2bb1fc"}, + {file = "numpy-1.19.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f0d3929fe88ee1c155129ecd82f981b8856c5d97bcb0d5f23e9b4242e79d1de3"}, + {file = "numpy-1.19.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:c42c4b73121caf0ed6cd795512c9c09c52a7287b04d105d112068c1736d7c753"}, + {file = "numpy-1.19.4-cp39-cp39-win32.whl", hash = "sha256:8cac8790a6b1ddf88640a9267ee67b1aee7a57dfa2d2dd33999d080bc8ee3a0f"}, + {file = "numpy-1.19.4-cp39-cp39-win_amd64.whl", hash = "sha256:4377e10b874e653fe96985c05feed2225c912e328c8a26541f7fc600fb9c637b"}, + {file = "numpy-1.19.4-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:2a2740aa9733d2e5b2dfb33639d98a64c3b0f24765fed86b0fd2aec07f6a0a08"}, + {file = "numpy-1.19.4.zip", hash = "sha256:141ec3a3300ab89c7f2b0775289954d193cc8edb621ea05f99db9cb181530512"}, ] orderedmultidict = [ {file = "orderedmultidict-1.0.1-py2.py3-none-any.whl", hash = "sha256:43c839a17ee3cdd62234c47deca1a8508a3f2ca1d0678a3bf791c87cf84adbf3"}, {file = "orderedmultidict-1.0.1.tar.gz", hash = "sha256:04070bbb5e87291cc9bfa51df413677faf2141c73c61d2a5f7b26bea3cd882ad"}, ] packaging = [ - {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, - {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, + {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, + {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, ] parso = [ - {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"}, - {file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"}, + {file = "parso-0.8.1-py2.py3-none-any.whl", hash = "sha256:15b00182f472319383252c18d5913b69269590616c947747bc50bf4ac768f410"}, + {file = "parso-0.8.1.tar.gz", hash = "sha256:8519430ad07087d4c997fda3a7918f7cfa27cb58972a8c89c2a0295a1c940e9e"}, ] pathspec = [ - {file = "pathspec-0.8.0-py2.py3-none-any.whl", hash = "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0"}, - {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"}, + {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, + {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, ] pbr = [ - {file = "pbr-5.4.5-py2.py3-none-any.whl", hash = "sha256:579170e23f8e0c2f24b0de612f71f648eccb79fb1322c814ae6b3c07b5ba23e8"}, - {file = "pbr-5.4.5.tar.gz", hash = "sha256:07f558fece33b05caf857474a366dfcc00562bca13dd8b47b2b3e22d9f9bf55c"}, + {file = "pbr-5.5.1-py2.py3-none-any.whl", hash = "sha256:b236cde0ac9a6aedd5e3c34517b423cd4fd97ef723849da6b0d2231142d89c00"}, + {file = "pbr-5.5.1.tar.gz", hash = "sha256:5fad80b613c402d5b7df7bd84812548b2a61e9977387a80a5fc5c396492b13c9"}, ] pdocs = [ {file = "pdocs-1.0.2-py3-none-any.whl", hash = "sha256:4d5ff87babcd0c46f12b76c887d53225bddb389dee7c6b338dbe281c729fc035"}, {file = "pdocs-1.0.2.tar.gz", hash = "sha256:2e32432bd2736fd678ac1ce4447cd508deb62b5a12f7ba3bf0e3a374063221e2"}, ] pep517 = [ - {file = "pep517-0.8.2-py2.py3-none-any.whl", hash = "sha256:576c480be81f3e1a70a16182c762311eb80d1f8a7b0d11971e5234967d7a342c"}, - {file = "pep517-0.8.2.tar.gz", hash = "sha256:8e6199cf1288d48a0c44057f112acf18aa5ebabbf73faa242f598fbe145ba29e"}, + {file = "pep517-0.9.1-py2.py3-none-any.whl", hash = "sha256:3985b91ebf576883efe5fa501f42a16de2607684f3797ddba7202b71b7d0da51"}, + {file = "pep517-0.9.1.tar.gz", hash = "sha256:aeb78601f2d1aa461960b43add204cc7955667687fbcf9cdb5170f00556f117f"}, ] pep8-naming = [ {file = "pep8-naming-0.8.2.tar.gz", hash = "sha256:01cb1dab2f3ce9045133d08449f1b6b93531dceacb9ef04f67087c11c723cea9"}, @@ -2058,47 +2087,52 @@ prompt-toolkit = [ {file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"}, ] ptyprocess = [ - {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, - {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] py = [ - {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, - {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] pycodestyle = [ {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, ] pydantic = [ - {file = "pydantic-1.6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:418b84654b60e44c0cdd5384294b0e4bc1ebf42d6e873819424f3b78b8690614"}, - {file = "pydantic-1.6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4900b8820b687c9a3ed753684337979574df20e6ebe4227381d04b3c3c628f99"}, - {file = "pydantic-1.6.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:b49c86aecde15cde33835d5d6360e55f5e0067bb7143a8303bf03b872935c75b"}, - {file = "pydantic-1.6.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:2de562a456c4ecdc80cf1a8c3e70c666625f7d02d89a6174ecf63754c734592e"}, - {file = "pydantic-1.6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f769141ab0abfadf3305d4fcf36660e5cf568a666dd3efab7c3d4782f70946b1"}, - {file = "pydantic-1.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2dc946b07cf24bee4737ced0ae77e2ea6bc97489ba5a035b603bd1b40ad81f7e"}, - {file = "pydantic-1.6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:36dbf6f1be212ab37b5fda07667461a9219c956181aa5570a00edfb0acdfe4a1"}, - {file = "pydantic-1.6.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:1783c1d927f9e1366e0e0609ae324039b2479a1a282a98ed6a6836c9ed02002c"}, - {file = "pydantic-1.6.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:cf3933c98cb5e808b62fae509f74f209730b180b1e3c3954ee3f7949e083a7df"}, - {file = "pydantic-1.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f8af9b840a9074e08c0e6dc93101de84ba95df89b267bf7151d74c553d66833b"}, - {file = "pydantic-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:40d765fa2d31d5be8e29c1794657ad46f5ee583a565c83cea56630d3ae5878b9"}, - {file = "pydantic-1.6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3fa799f3cfff3e5f536cbd389368fc96a44bb30308f258c94ee76b73bd60531d"}, - {file = "pydantic-1.6.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:6c3f162ba175678218629f446a947e3356415b6b09122dcb364e58c442c645a7"}, - {file = "pydantic-1.6.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:eb75dc1809875d5738df14b6566ccf9fd9c0bcde4f36b72870f318f16b9f5c20"}, - {file = "pydantic-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:530d7222a2786a97bc59ee0e0ebbe23728f82974b1f1ad9a11cd966143410633"}, - {file = "pydantic-1.6.1-py36.py37.py38-none-any.whl", hash = "sha256:b5b3489cb303d0f41ad4a7390cf606a5f2c7a94dcba20c051cd1c653694cb14d"}, - {file = "pydantic-1.6.1.tar.gz", hash = "sha256:54122a8ed6b75fe1dd80797f8251ad2063ea348a03b77218d73ea9fe19bd4e73"}, + {file = "pydantic-1.7.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c59ea046aea25be14dc22d69c97bee629e6d48d2b2ecb724d7fe8806bf5f61cd"}, + {file = "pydantic-1.7.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a4143c8d0c456a093387b96e0f5ee941a950992904d88bc816b4f0e72c9a0009"}, + {file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:d8df4b9090b595511906fa48deda47af04e7d092318bfb291f4d45dfb6bb2127"}, + {file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:514b473d264671a5c672dfb28bdfe1bf1afd390f6b206aa2ec9fed7fc592c48e"}, + {file = "pydantic-1.7.3-cp36-cp36m-win_amd64.whl", hash = "sha256:dba5c1f0a3aeea5083e75db9660935da90216f8a81b6d68e67f54e135ed5eb23"}, + {file = "pydantic-1.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:59e45f3b694b05a69032a0d603c32d453a23f0de80844fb14d55ab0c6c78ff2f"}, + {file = "pydantic-1.7.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5b24e8a572e4b4c18f614004dda8c9f2c07328cb5b6e314d6e1bbd536cb1a6c1"}, + {file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:b2b054d095b6431cdda2f852a6d2f0fdec77686b305c57961b4c5dd6d863bf3c"}, + {file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:025bf13ce27990acc059d0c5be46f416fc9b293f45363b3d19855165fee1874f"}, + {file = "pydantic-1.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6e3874aa7e8babd37b40c4504e3a94cc2023696ced5a0500949f3347664ff8e2"}, + {file = "pydantic-1.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e682f6442ebe4e50cb5e1cfde7dda6766fb586631c3e5569f6aa1951fd1a76ef"}, + {file = "pydantic-1.7.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:185e18134bec5ef43351149fe34fda4758e53d05bb8ea4d5928f0720997b79ef"}, + {file = "pydantic-1.7.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:f5b06f5099e163295b8ff5b1b71132ecf5866cc6e7f586d78d7d3fd6e8084608"}, + {file = "pydantic-1.7.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:24ca47365be2a5a3cc3f4a26dcc755bcdc9f0036f55dcedbd55663662ba145ec"}, + {file = "pydantic-1.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:d1fe3f0df8ac0f3a9792666c69a7cd70530f329036426d06b4f899c025aca74e"}, + {file = "pydantic-1.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f6864844b039805add62ebe8a8c676286340ba0c6d043ae5dea24114b82a319e"}, + {file = "pydantic-1.7.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ecb54491f98544c12c66ff3d15e701612fc388161fd455242447083350904730"}, + {file = "pydantic-1.7.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:ffd180ebd5dd2a9ac0da4e8b995c9c99e7c74c31f985ba090ee01d681b1c4b95"}, + {file = "pydantic-1.7.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8d72e814c7821125b16f1553124d12faba88e85405b0864328899aceaad7282b"}, + {file = "pydantic-1.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:475f2fa134cf272d6631072554f845d0630907fce053926ff634cc6bc45bf1af"}, + {file = "pydantic-1.7.3-py3-none-any.whl", hash = "sha256:38be427ea01a78206bcaf9a56f835784afcba9e5b88fbdce33bbbfbcd7841229"}, + {file = "pydantic-1.7.3.tar.gz", hash = "sha256:213125b7e9e64713d16d988d10997dabc6a1f73f3991e1ff8e35ebb1409c7dc9"}, ] pydocstyle = [ - {file = "pydocstyle-5.1.0-py3-none-any.whl", hash = "sha256:08374b9d4d2b7164bae50b71bb24eb0d74a56b309029d5d502264092fa7db0c3"}, - {file = "pydocstyle-5.1.0.tar.gz", hash = "sha256:4ca3c7736d36f92bb215dd74ef84ac3d6c146edd795c7afc5154c10f1eb1f65a"}, + {file = "pydocstyle-5.1.1-py3-none-any.whl", hash = "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678"}, + {file = "pydocstyle-5.1.1.tar.gz", hash = "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325"}, ] pyflakes = [ {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, ] pygments = [ - {file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"}, - {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, + {file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"}, + {file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"}, ] pylama = [ {file = "pylama-7.7.1-py2.py3-none-any.whl", hash = "sha256:fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15"}, @@ -2113,8 +2147,8 @@ pyparsing = [ {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ - {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, - {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, + {file = "pytest-6.2.1-py3-none-any.whl", hash = "sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8"}, + {file = "pytest-6.2.1.tar.gz", hash = "sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"}, ] pytest-cov = [ {file = "pytest-cov-2.10.1.tar.gz", hash = "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"}, @@ -2147,43 +2181,63 @@ pyyaml = [ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] regex = [ - {file = "regex-2020.7.14-cp27-cp27m-win32.whl", hash = "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"}, - {file = "regex-2020.7.14-cp27-cp27m-win_amd64.whl", hash = "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88"}, - {file = "regex-2020.7.14-cp36-cp36m-win32.whl", hash = "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4"}, - {file = "regex-2020.7.14-cp36-cp36m-win_amd64.whl", hash = "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89"}, - {file = "regex-2020.7.14-cp37-cp37m-win32.whl", hash = "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6"}, - {file = "regex-2020.7.14-cp37-cp37m-win_amd64.whl", hash = "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a"}, - {file = "regex-2020.7.14-cp38-cp38-win32.whl", hash = "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341"}, - {file = "regex-2020.7.14-cp38-cp38-win_amd64.whl", hash = "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840"}, - {file = "regex-2020.7.14.tar.gz", hash = "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb"}, + {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, + {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, + {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, + {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, + {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, + {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, + {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, + {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, + {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, + {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, + {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, + {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, + {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, ] requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, ] requirementslib = [ - {file = "requirementslib-1.5.13-py2.py3-none-any.whl", hash = "sha256:cdf8aa652ac52216d156cee2b89c3c9ee53373dded0035184d0b9af569a0f10c"}, - {file = "requirementslib-1.5.13.tar.gz", hash = "sha256:fd98ea873effaede6b3394725a232bcbd3fe3985987e226109a841c85a69e2e3"}, + {file = "requirementslib-1.5.16-py2.py3-none-any.whl", hash = "sha256:50d20f27e4515a2393695b0d886219598302163438ae054253147b2bad9b4a44"}, + {file = "requirementslib-1.5.16.tar.gz", hash = "sha256:9c1e8666ca4512724cdd1739adcc7df19ec7ad2ed21f0e748f9631ad6b54f321"}, ] rfc3986 = [ {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"}, {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"}, ] safety = [ - {file = "safety-1.9.0-py2.py3-none-any.whl", hash = "sha256:86c1c4a031fe35bd624fce143fbe642a0234d29f7cbf7a9aa269f244a955b087"}, - {file = "safety-1.9.0.tar.gz", hash = "sha256:23bf20690d4400edc795836b0c983c2b4cbbb922233108ff925b7dd7750f00c9"}, + {file = "safety-1.10.0-py2.py3-none-any.whl", hash = "sha256:69437acf5dd617abd7086ccd0d50e813e67aa969bb9ca90f1847d5fbea047dcc"}, + {file = "safety-1.10.0.tar.gz", hash = "sha256:2ebc71b44666588d7898905d86d575933fcd5fa3c92d301ed12482602b1e928a"}, ] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, @@ -2198,83 +2252,115 @@ smmap2 = [ {file = "smmap2-3.0.1.tar.gz", hash = "sha256:44cc8bdaf96442dbb9a8e2e14377d074b3d0eea292eee3c95c8c449b6c92c557"}, ] sniffio = [ - {file = "sniffio-1.1.0-py3-none-any.whl", hash = "sha256:20ed6d5b46f8ae136d00b9dcb807615d83ed82ceea6b2058cecb696765246da5"}, - {file = "sniffio-1.1.0.tar.gz", hash = "sha256:8e3810100f69fe0edd463d02ad407112542a11ffdc29f67db2bf3771afb87a21"}, + {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, + {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, ] snowballstemmer = [ {file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"}, {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"}, ] sortedcontainers = [ - {file = "sortedcontainers-2.2.2-py2.py3-none-any.whl", hash = "sha256:c633ebde8580f241f274c1f8994a665c0e54a17724fecd0cae2f079e09c36d3f"}, - {file = "sortedcontainers-2.2.2.tar.gz", hash = "sha256:4e73a757831fc3ca4de2859c422564239a31d8213d09a2a666e375807034d2ba"}, + {file = "sortedcontainers-2.3.0-py2.py3-none-any.whl", hash = "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f"}, + {file = "sortedcontainers-2.3.0.tar.gz", hash = "sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1"}, ] stevedore = [ - {file = "stevedore-3.2.0-py3-none-any.whl", hash = "sha256:c8f4f0ebbc394e52ddf49de8bcc3cf8ad2b4425ebac494106bbc5e3661ac7633"}, - {file = "stevedore-3.2.0.tar.gz", hash = "sha256:38791aa5bed922b0a844513c5f9ed37774b68edc609e5ab8ab8d8fe0ce4315e5"}, + {file = "stevedore-3.3.0-py3-none-any.whl", hash = "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"}, + {file = "stevedore-3.3.0.tar.gz", hash = "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee"}, ] text-unidecode = [ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, ] toml = [ - {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, - {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tomlkit = [ {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"}, {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"}, ] tornado = [ - {file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"}, - {file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"}, - {file = "tornado-6.0.4-cp36-cp36m-win32.whl", hash = "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673"}, - {file = "tornado-6.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a"}, - {file = "tornado-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6"}, - {file = "tornado-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b"}, - {file = "tornado-6.0.4-cp38-cp38-win32.whl", hash = "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52"}, - {file = "tornado-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9"}, - {file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"}, + {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, + {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, + {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, + {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, + {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, + {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, + {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, + {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, + {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, + {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, + {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, + {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, + {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, + {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, + {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, + {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, + {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, + {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, ] tqdm = [ - {file = "tqdm-4.48.2-py2.py3-none-any.whl", hash = "sha256:1a336d2b829be50e46b84668691e0a2719f26c97c62846298dd5ae2937e4d5cf"}, - {file = "tqdm-4.48.2.tar.gz", hash = "sha256:564d632ea2b9cb52979f7956e093e831c28d441c11751682f84c86fc46e4fd21"}, + {file = "tqdm-4.55.0-py2.py3-none-any.whl", hash = "sha256:0cd81710de29754bf17b6fee07bdb86f956b4fa20d3078f02040f83e64309416"}, + {file = "tqdm-4.55.0.tar.gz", hash = "sha256:f4f80b96e2ceafea69add7bf971b8403b9cba8fb4451c1220f91c79be4ebd208"}, ] traitlets = [ {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, ] typed-ast = [ - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, - {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, - {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, - {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d"}, - {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, - {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, - {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, - {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c"}, - {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072"}, - {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91"}, - {file = "typed_ast-1.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d"}, - {file = "typed_ast-1.4.1-cp39-cp39-win32.whl", hash = "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395"}, - {file = "typed_ast-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c"}, - {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412"}, + {file = "typed_ast-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400"}, + {file = "typed_ast-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606"}, + {file = "typed_ast-1.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a"}, + {file = "typed_ast-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151"}, + {file = "typed_ast-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3"}, + {file = "typed_ast-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37"}, + {file = "typed_ast-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd"}, + {file = "typed_ast-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496"}, + {file = "typed_ast-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787"}, + {file = "typed_ast-1.4.2-cp38-cp38-win32.whl", hash = "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2"}, + {file = "typed_ast-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937"}, + {file = "typed_ast-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d"}, + {file = "typed_ast-1.4.2-cp39-cp39-win32.whl", hash = "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b"}, + {file = "typed_ast-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440"}, + {file = "typed_ast-1.4.2.tar.gz", hash = "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a"}, ] typer = [ {file = "typer-0.3.2-py3-none-any.whl", hash = "sha256:ba58b920ce851b12a2d790143009fa00ac1d05b3ff3257061ff69dbdfc3d161b"}, @@ -2291,8 +2377,8 @@ typing-inspect = [ {file = "typing_inspect-0.6.0.tar.gz", hash = "sha256:8f1b1dd25908dbfd81d3bebc218011531e7ab614ba6e5bf7826d887c834afab7"}, ] urllib3 = [ - {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, - {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, + {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, + {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, ] vistir = [ {file = "vistir-0.5.2-py2.py3-none-any.whl", hash = "sha256:a37079cdbd85d31a41cdd18457fe521e15ec08b255811e81aa061fd5f48a20fb"}, @@ -2315,6 +2401,6 @@ yaspin = [ {file = "yaspin-0.15.0.tar.gz", hash = "sha256:5a938bdc7bab353fd8942d0619d56c6b5159a80997dc1c387a479b39e6dc9391"}, ] zipp = [ - {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, - {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, + {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, + {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, ] diff --git a/pyproject.toml b/pyproject.toml index 0889cc4f2..086dadfd3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ flake8-bugbear = "^19.8" black = {version = "^20.08b1", allow-prereleases = true} mypy = "^0.761.0" ipython = "^7.7" -pytest = "^5.0" +pytest = "^6.0" pytest-cov = "^2.7" pytest-mock = "^1.10" pep8-naming = "^0.8.2" From 2f9fe8621eb9e29255db64fe77ab0d0aae293f9d Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Sat, 2 Jan 2021 12:46:10 +0100 Subject: [PATCH 325/539] Simplify section_key helper function arguments sorting.section_key() is only used once and all its arguments come from Config. Make it take a single config parameter instead of a bunch of individual settings. --- isort/output.py | 12 +----------- isort/sorting.py | 32 +++++++++++--------------------- 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/isort/output.py b/isort/output.py index 54253884b..1d51171d3 100644 --- a/isort/output.py +++ b/isort/output.py @@ -94,17 +94,7 @@ def sorted_imports( # only_sections options is not imposed if force_sort_within_sections is True new_section_output = sorting.naturally( new_section_output, - key=partial( - sorting.section_key, - case_sensitive=config.case_sensitive, - honor_case_in_force_sorted_sections=config.honor_case_in_force_sorted_sections, - order_by_type=config.order_by_type, - force_to_top=config.force_to_top, - lexicographical=config.lexicographical, - length_sort=config.length_sort, - reverse_relative=config.reverse_relative, - group_by_package=config.group_by_package, - ), + key=partial(sorting.section_key, config=config), ) # uncollapse comments diff --git a/isort/sorting.py b/isort/sorting.py index 9a4336b8e..2a333f2c5 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -51,52 +51,42 @@ def module_key( return f"{module_name in config.force_to_top and 'A' or 'B'}{prefix}{_length_sort_maybe}" -def section_key( - line: str, - case_sensitive: bool, - honor_case_in_force_sorted_sections: bool, - order_by_type: bool, - force_to_top: List[str], - lexicographical: bool = False, - length_sort: bool = False, - reverse_relative: bool = False, - group_by_package: bool = False, -) -> str: +def section_key(line: str, config: Config) -> str: section = "B" - if reverse_relative and line.startswith("from ."): + if config.reverse_relative and line.startswith("from ."): match = re.match(r"^from (\.+)\s*(.*)", line) if match: # pragma: no cover - regex always matches if line starts with "from ." line = f"from {' '.join(match.groups())}" - if group_by_package and line.strip().startswith("from"): + if config.group_by_package and line.strip().startswith("from"): line = line.split(" import", 1)[0] - if lexicographical: + if config.lexicographical: line = _import_line_intro_re.sub("", _import_line_midline_import_re.sub(".", line)) else: line = re.sub("^from ", "", line) line = re.sub("^import ", "", line) - if line.split(" ")[0] in force_to_top: + if line.split(" ")[0] in config.force_to_top: section = "A" # * If honor_case_in_force_sorted_sections is true, and case_sensitive and # order_by_type are different, only ignore case in part of the line. # * Otherwise, let order_by_type decide the sorting of the whole line. This # is only "correct" if case_sensitive and order_by_type have the same value. - if honor_case_in_force_sorted_sections and case_sensitive != order_by_type: + if config.honor_case_in_force_sorted_sections and config.case_sensitive != config.order_by_type: split_module = line.split(" import ", 1) if len(split_module) > 1: module_name, names = split_module - if not case_sensitive: + if not config.case_sensitive: module_name = module_name.lower() - if not order_by_type: + if not config.order_by_type: names = names.lower() line = " import ".join([module_name, names]) - elif not case_sensitive: + elif not config.case_sensitive: line = line.lower() - elif not order_by_type: + elif not config.order_by_type: line = line.lower() - return f"{section}{len(line) if length_sort else ''}{line}" + return f"{section}{len(line) if config.length_sort else ''}{line}" def naturally(to_sort: Iterable[str], key: Optional[Callable[[str], Any]] = None) -> List[str]: From b27a5dcf039a72ff335fd81e7a313a51aa3b0593 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 10 Jan 2021 23:19:03 -0800 Subject: [PATCH 326/539] Add test case for issue #1631 --- tests/unit/test_regressions.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 89fa09274..a4d98cce3 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1515,3 +1515,13 @@ def test_isort_adding_second_comma_issue_1621(): ) """ ) + + +def test_isort_shouldnt_duplicate_comments_issue_1631(): + assert isort.check_code( + """ +import a # a comment +import a as b # b comment +""", + show_diff=True, + ) From cc69e6115174b1103b5b3c372e467e8cde0e229d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 11 Jan 2021 22:06:08 -0800 Subject: [PATCH 327/539] Update output to take into account difference between as and non as import statements --- isort/parse.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/isort/parse.py b/isort/parse.py index d93f559e2..f4844d506 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -410,9 +410,14 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte f"{top_level_module}.__combined_as__", [] ) else: - attach_comments_to = categorized_comments["straight"].setdefault( - module, [] - ) + if config.remove_redundant_aliases and as_name == module.split(".")[-1]: + attach_comments_to = categorized_comments["straight"].setdefault( + module, [] + ) + else: + attach_comments_to = categorized_comments["straight"].setdefault( + f"{module} as {as_name}" , [] + ) del just_imports[as_index : as_index + 2] if type_of_import == "from": From 433e3b0dcca0d6b9e05910f3c0430ddc46e38a1d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 12 Jan 2021 22:08:40 -0800 Subject: [PATCH 328/539] Update output to respect as import comment location --- isort/output.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/isort/output.py b/isort/output.py index 1d51171d3..017ae4d4e 100644 --- a/isort/output.py +++ b/isort/output.py @@ -543,25 +543,25 @@ def _with_straight_imports( import_definition = [] if module in parsed.as_map["straight"]: if parsed.imports[section]["straight"][module]: - import_definition.append(f"{import_type} {module}") + import_definition.append((f"{import_type} {module}", module)) import_definition.extend( - f"{import_type} {module} as {as_import}" + (f"{import_type} {module} as {as_import}", f"{module} as {as_import}") for as_import in parsed.as_map["straight"][module] ) else: - import_definition.append(f"{import_type} {module}") + import_definition.append((f"{import_type} {module}", module)) comments_above = parsed.categorized_comments["above"]["straight"].pop(module, None) if comments_above: output.extend(comments_above) output.extend( with_comments( - parsed.categorized_comments["straight"].get(module), + parsed.categorized_comments["straight"].get(imodule), idef, removed=config.ignore_comments, comment_prefix=config.comment_prefix, ) - for idef in import_definition + for idef, imodule in import_definition ) return output From 804329d7776f3b97b8f248f44d314afb00ce753e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 12 Jan 2021 22:09:02 -0800 Subject: [PATCH 329/539] Update test to use correct spacing for comments --- tests/unit/test_regressions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index a4d98cce3..3d586b2c8 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1520,8 +1520,8 @@ def test_isort_adding_second_comma_issue_1621(): def test_isort_shouldnt_duplicate_comments_issue_1631(): assert isort.check_code( """ -import a # a comment -import a as b # b comment +import a # a comment +import a as b # b comment """, show_diff=True, ) From c2133b3194e48ad7ba0730648bbbae62b12ec5bd Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 12 Jan 2021 22:21:14 -0800 Subject: [PATCH 330/539] Add test for duplicate alias case --- tests/unit/test_regressions.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 3d586b2c8..7ba1da770 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1525,3 +1525,15 @@ def test_isort_shouldnt_duplicate_comments_issue_1631(): """, show_diff=True, ) + assert ( + isort.code( + """ +import a # a comment +import a as a # b comment +""", + remove_redundant_aliases=True, + ) + == """ +import a # a comment; b comment +""" + ) From 836302fed1b6bc8720d4248529c989adcc41bbd5 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 12 Jan 2021 22:21:36 -0800 Subject: [PATCH 331/539] Clarify it effects straight imports only --- isort/parse.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/isort/parse.py b/isort/parse.py index f4844d506..307015e74 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -410,13 +410,15 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte f"{top_level_module}.__combined_as__", [] ) else: - if config.remove_redundant_aliases and as_name == module.split(".")[-1]: + if type_of_import == "from" or ( + config.remove_redundant_aliases and as_name == module.split(".")[-1] + ): attach_comments_to = categorized_comments["straight"].setdefault( module, [] ) else: attach_comments_to = categorized_comments["straight"].setdefault( - f"{module} as {as_name}" , [] + f"{module} as {as_name}", [] ) del just_imports[as_index : as_index + 2] From 47882c5d3252227783d84ed029691fab0aa6d3cd Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 12 Jan 2021 22:52:52 -0800 Subject: [PATCH 332/539] Fix integration test --- tests/integration/test_projects_using_isort.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 2258cbfae..17cea29c4 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -61,7 +61,19 @@ def test_habitat_lab(tmpdir): def test_tmuxp(tmpdir): git_clone("https://github.com/tmux-python/tmuxp.git", tmpdir) - run_isort([str(tmpdir), "--skip", "cli.py", "--skip", "test_workspacebuilder.py"]) + run_isort( + [ + str(tmpdir), + "--skip", + "cli.py", + "--skip", + "test_workspacebuilder.py", + "--skip", + "test_cli.py", + "--skip", + "workspacebuilder.py", + ] + ) def test_websockets(tmpdir): From 6230dc3086c8189e8dc873a444d0e26ada068581 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 13 Jan 2021 23:20:10 -0800 Subject: [PATCH 333/539] Add Fixed #1631: as import comments can in some cases be duplicated. to changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcfeb972d..550314067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). +### 5.8.0 TBD + - Fixed #1631: as import comments can in some cases be duplicated. + ### 5.7.0 December 30th 2020 - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. - Fixed #1593: isort encounters bug in Python 3.6.0. From 2ec47fd8b0e660d12df05b571fe1f23d1d3228c3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 25 Jan 2021 22:59:40 -0800 Subject: [PATCH 334/539] Update project cruft --- .cruft.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cruft.json b/.cruft.json index dcbd6c8eb..e1aa3cc33 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/timothycrosley/cookiecutter-python/", - "commit": "ff6836bfaa247c65ff50b39c520ed12d91bf5a20", + "commit": "70bd5343b7321f49ee8d699a94481c5a73d4e380", "context": { "cookiecutter": { "full_name": "Timothy Crosley", From 957321f90835a6d1f1fcceeeffb76f91364bd056 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 30 Jan 2021 22:05:31 -0800 Subject: [PATCH 335/539] Add documentation page for pre-commit --- docs/configuration/pre-commit.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docs/configuration/pre-commit.md diff --git a/docs/configuration/pre-commit.md b/docs/configuration/pre-commit.md new file mode 100644 index 000000000..70eaded47 --- /dev/null +++ b/docs/configuration/pre-commit.md @@ -0,0 +1,32 @@ +Using isort with pre-commit +======== + +isort provides official support for [pre-commit](https://pre-commit.com/). + +### isort pre-commit step + +To use isort's official pre-commit integration add the following config: + +``` + - repo: https://github.com/pycqa/isort + rev: 5.6.3 + hooks: + - id: isort + name: isort (python) + - id: isort + name: isort (cython) + types: [cython] + - id: isort + name: isort (pyi) + types: [pyi] +``` + +under the `repos` section of your projects `.pre-commit-config.yaml` file. + +### seed-isort-config + +Older versions of isort used a lot of magic to determine import placement, that could easily break when running on CI/CD. +To fix this, a utilitiy called `seed-isort-config` was created. Since isort 5 however, the project has drastically improved its placement +logic and ensured a good level of consistency across environments. +If you have a step in your pre-commit config called `seed-isort-config` or similar, it is highly recommend that you remove this. +It is guaranteed to slow things down, and can conflict with isort's own module placement logic. From fdb15cdce7e235ad2181563637c5268f84f0b092 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 30 Jan 2021 22:09:23 -0800 Subject: [PATCH 336/539] Add link to pre-commit-docs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7b4526700..8d2937523 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ supports formatting Python 2 code too. - [Try isort now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try/) - [Using black? See the isort and black compatiblity guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility/) +- [isort has official support for pre-commit!](https://pycqa.github.io/isort/docs/configuration/pre-commit/) ![Example Usage](https://raw.github.com/pycqa/isort/develop/example.gif) From 6017f6ef2bd070bac20dce294ab3e2a596b7aff4 Mon Sep 17 00:00:00 2001 From: Pavel Savchenko Date: Wed, 3 Feb 2021 16:05:33 +0000 Subject: [PATCH 337/539] Indicate type hint support with a py.typed file fix #1648 --- isort/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 isort/py.typed diff --git a/isort/py.typed b/isort/py.typed new file mode 100644 index 000000000..e69de29bb From d10aeefb1bb6355d3ef14cdaa0ee645be9132c5b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 3 Feb 2021 22:33:27 -0800 Subject: [PATCH 338/539] Update cruft --- .cruft.json | 2 +- scripts/lint.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.cruft.json b/.cruft.json index e1aa3cc33..6d044f389 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/timothycrosley/cookiecutter-python/", - "commit": "70bd5343b7321f49ee8d699a94481c5a73d4e380", + "commit": "9be01014cde7ec06bd52415655d52ca30a9e9bcc", "context": { "cookiecutter": { "full_name": "Timothy Crosley", diff --git a/scripts/lint.sh b/scripts/lint.sh index b8eb14da2..992027865 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -7,5 +7,5 @@ poetry run black --target-version py36 --check . poetry run isort --profile hug --check --diff isort/ tests/ poetry run isort --profile hug --check --diff example_isort_formatting_plugin/ poetry run flake8 isort/ tests/ -poetry run safety check +poetry run safety check -i 39462 poetry run bandit -r isort/ -x isort/_vendored From fe4b8c2e915346b7f1b8eb3891df6ddd8b9e7e1c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 3 Feb 2021 23:33:34 -0800 Subject: [PATCH 339/539] Add - Pavel Savchenko (@asfaltboy) to acknowledgements --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 03ddc1e67..bb41e41f4 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -215,6 +215,7 @@ Code Contributors - @dwanderson-intel - Quentin Santos (@qsantos) - @gofr +- Pavel Savchenko (@asfaltboy) Documenters =================== From 1111614a9354f445ebd63e1eb0c34abcbaf2de12 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 3 Feb 2021 23:34:53 -0800 Subject: [PATCH 340/539] Update changelog to mention py.typed addition --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 550314067..b169ff6f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.8.0 TBD - Fixed #1631: as import comments can in some cases be duplicated. + - Implemented #1648: Export MyPY type hints. ### 5.7.0 December 30th 2020 - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. From 8d6f0b994b0f1917d7c9a195be0b1fc2c17e7bf8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 3 Feb 2021 23:47:10 -0800 Subject: [PATCH 341/539] Fix broken integration test --- tests/integration/test_projects_using_isort.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 17cea29c4..5c172f07c 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -113,7 +113,9 @@ def test_poetry(tmpdir): def test_hypothesis(tmpdir): git_clone("https://github.com/HypothesisWorks/hypothesis.git", tmpdir) - run_isort((str(tmpdir), "--skip", "tests")) + run_isort( + (str(tmpdir), "--skip", "tests", "--profile", "black", "--ca", "--project", "hypothesis") + ) def test_pillow(tmpdir): From f89dbc9405e391b02d2bea9dce6eb162bff814b3 Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Fri, 5 Feb 2021 12:15:51 +0100 Subject: [PATCH 342/539] Unit test behavior of vertical grids close to the line length The three vertical grid multi line output modes 4, 5 and 6 use a shared function that makes them all wrap slightly differently when the import lines are right around the line length limit. Add tests to document the behavior and catch regressions. --- tests/unit/test_wrap_modes.py | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/unit/test_wrap_modes.py b/tests/unit/test_wrap_modes.py index 29ecfd0df..d43d4956d 100644 --- a/tests/unit/test_wrap_modes.py +++ b/tests/unit/test_wrap_modes.py @@ -1,3 +1,4 @@ +import pytest from hypothesis import given, reject from hypothesis import strategies as st @@ -93,6 +94,51 @@ def test_backslash_grid(): ) +@pytest.mark.parametrize("include_trailing_comma", (False, True)) +@pytest.mark.parametrize("line_length", (18, 19)) +@pytest.mark.parametrize("multi_line_output", (4, 5, 6)) +def test_vertical_grid_size_near_line_length( + multi_line_output: int, + line_length: int, + include_trailing_comma: bool, +): + separator = " " + # Cases where the input should be wrapped: + if ( + # Mode 4 always adds a closing ")", making the imports line 19 chars, + # if include_trailing_comma is True that becomes 20 chars. + (multi_line_output == 4 and line_length < 19 + int(include_trailing_comma)) + # Mode 5 always makes space for a final "," even if include_trailing_comma is False, + # (issue #1634) making the line (seem) 19 chars. + or (multi_line_output == 5 and line_length < 19) + # Mode 6 never makes space for a final "," even if include_trailing_comma is True, + # (issue #1634) making the line (seem) 18 chars, so this doesn't wrap. + ): + separator = "\n " + + test_input = f"from foo import (\n aaaa, bbb,{separator}ccc" + if include_trailing_comma: + test_input += "," + if multi_line_output != 4: + test_input += "\n" + test_input += ")\n" + + try: + assert ( + isort.code( + test_input, + multi_line_output=multi_line_output, + line_length=line_length, + include_trailing_comma=include_trailing_comma, + ) + == test_input + ) + except AssertionError: + if multi_line_output == 4 and include_trailing_comma and line_length == 19: + pytest.xfail("issue #1640") + raise + + # This test code was written by the `hypothesis.extra.ghostwriter` module # and is provided under the Creative Commons Zero public domain dedication. From 5e9ad2b867c8bb061bee2ba844a1b26e7fa0dcab Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Sat, 9 Jan 2021 19:10:53 +0100 Subject: [PATCH 343/539] Respect line_length in vertical grid modes and deprecate mode 6 In the three vertical grid multi line modes (4, 5 and 6), each mode had a bug where the line length would be overcounted or undercounted by one character when used in combination with an unexpected value for the include_trailing_comma setting. This could cause import lines to exceed the line_length or be wrapped at one less than the specified line_length. Count the trailing characters correctly. This also deprecates mode 6, since the only difference between this mode and mode 5 is in how it handles include_trailing_comma. The distinction is no longer relevant, since mode 5 can now handle both possible values of include_trailing_comma. For backwards compatibility, make mode 6 an alias for mode 5. --- README.md | 15 +------- isort/main.py | 2 +- isort/settings.py | 2 + isort/wrap_modes.py | 18 ++++----- tests/unit/test_isort.py | 2 +- tests/unit/test_settings.py | 4 ++ tests/unit/test_wrap_modes.py | 72 ++++++----------------------------- 7 files changed, 31 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 8d2937523..960901953 100644 --- a/README.md +++ b/README.md @@ -221,20 +221,9 @@ from third_party import ( ) ``` -**6 - Hanging Grid Grouped, No Trailing Comma** +**6 - Hanging Grid Grouped** -In Mode 5 isort leaves a single extra space to maintain consistency of -output when a comma is added at the end. Mode 6 is the same - except -that no extra space is maintained leading to the possibility of lines -one character longer. You can enforce a trailing comma by using this in -conjunction with `-tc` or `include_trailing_comma: True`. - -```python -from third_party import ( - lib1, lib2, lib3, lib4, - lib5 -) -``` +Same as Mode 5. Deprecated. **7 - NOQA** diff --git a/isort/main.py b/isort/main.py index 39dbf19b4..1a7eb8046 100644 --- a/isort/main.py +++ b/isort/main.py @@ -472,7 +472,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: + [str(mode.value) for mode in WrapModes.__members__.values()], type=str, help="Multi line output (0-grid, 1-vertical, 2-hanging, 3-vert-hanging, 4-vert-grid, " - "5-vert-grid-grouped, 6-vert-grid-grouped-no-comma, 7-noqa, " + "5-vert-grid-grouped, 6-deprecated-alias-for-5, 7-noqa, " "8-vertical-hanging-indent-bracket, 9-vertical-prefix-from-module-import, " "10-hanging-indent-with-parentheses).", ) diff --git a/isort/settings.py b/isort/settings.py index b4a29647b..c2c79cb63 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -234,6 +234,8 @@ def __post_init__(self): self, "known_standard_library", frozenset(getattr(stdlibs, self.py_version).stdlib) ) + if self.multi_line_output == WrapModes.VERTICAL_GRID_GROUPED_NO_COMMA: + object.__setattr__(self, "multi_line_output", WrapModes.VERTICAL_GRID_GROUPED) if self.force_alphabetical_sort: object.__setattr__(self, "force_alphabetical_sort_within_sections", True) object.__setattr__(self, "no_sections", True) diff --git a/isort/wrap_modes.py b/isort/wrap_modes.py index 5c2695263..5ced3713c 100644 --- a/isort/wrap_modes.py +++ b/isort/wrap_modes.py @@ -201,9 +201,11 @@ def _vertical_grid_common(need_trailing_char: bool, **interface): next_import = interface["imports"].pop(0) next_statement = f"{interface['statement']}, {next_import}" current_line_length = len(next_statement.split(interface["line_separator"])[-1]) - if interface["imports"] or need_trailing_char: - # If we have more interface["imports"] we need to account for a comma after this import - # We might also need to account for a closing ) we're going to add. + if interface["imports"] or interface["include_trailing_comma"]: + # We need to account for a comma after this import. + current_line_length += 1 + if not interface["imports"] and need_trailing_char: + # We need to account for a closing ) we're going to add. current_line_length += 1 if current_line_length > interface["line_length"]: next_statement = ( @@ -224,7 +226,7 @@ def vertical_grid(**interface) -> str: @_wrap_mode def vertical_grid_grouped(**interface): return ( - _vertical_grid_common(need_trailing_char=True, **interface) + _vertical_grid_common(need_trailing_char=False, **interface) + interface["line_separator"] + ")" ) @@ -232,11 +234,9 @@ def vertical_grid_grouped(**interface): @_wrap_mode def vertical_grid_grouped_no_comma(**interface): - return ( - _vertical_grid_common(need_trailing_char=False, **interface) - + interface["line_separator"] - + ")" - ) + # This is a deprecated alias for vertical_grid_grouped above. This function + # needs to exist for backwards compatibility but should never get called. + raise NotImplementedError @_wrap_mode diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 0a9d187a7..e5ab86d31 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -481,7 +481,7 @@ def test_output_modes() -> None: test_case = isort.code( code=SINGLE_LINE_LONG_IMPORT, - multi_line_output=WrapModes.VERTICAL_GRID_GROUPED_NO_COMMA, + multi_line_output=WrapModes.VERTICAL_GRID_GROUPED, line_length=40, indent=" ", ) diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index be8b5020b..157a087f4 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -6,6 +6,7 @@ from isort import exceptions, settings from isort.settings import Config +from isort.wrap_modes import WrapModes class TestConfig: @@ -91,6 +92,9 @@ def test_src_paths_are_combined_and_deduplicated(self): src_full_paths = (Path(os.getcwd()) / f for f in src_paths) assert Config(src_paths=src_paths * 2).src_paths == tuple(src_full_paths) + def test_deprecated_multi_line_output(self): + assert Config(multi_line_output=6).multi_line_output == WrapModes.VERTICAL_GRID_GROUPED + def test_as_list(): assert settings._as_list([" one "]) == ["one"] diff --git a/tests/unit/test_wrap_modes.py b/tests/unit/test_wrap_modes.py index d43d4956d..c21db9049 100644 --- a/tests/unit/test_wrap_modes.py +++ b/tests/unit/test_wrap_modes.py @@ -96,7 +96,7 @@ def test_backslash_grid(): @pytest.mark.parametrize("include_trailing_comma", (False, True)) @pytest.mark.parametrize("line_length", (18, 19)) -@pytest.mark.parametrize("multi_line_output", (4, 5, 6)) +@pytest.mark.parametrize("multi_line_output", (4, 5)) def test_vertical_grid_size_near_line_length( multi_line_output: int, line_length: int, @@ -108,11 +108,9 @@ def test_vertical_grid_size_near_line_length( # Mode 4 always adds a closing ")", making the imports line 19 chars, # if include_trailing_comma is True that becomes 20 chars. (multi_line_output == 4 and line_length < 19 + int(include_trailing_comma)) - # Mode 5 always makes space for a final "," even if include_trailing_comma is False, - # (issue #1634) making the line (seem) 19 chars. - or (multi_line_output == 5 and line_length < 19) - # Mode 6 never makes space for a final "," even if include_trailing_comma is True, - # (issue #1634) making the line (seem) 18 chars, so this doesn't wrap. + # Modes 5 and 6 only add a comma, if include_trailing_comma is True, + # so their lines are 18 or 19 chars long. + or (multi_line_output != 4 and line_length < 18 + int(include_trailing_comma)) ): separator = "\n " @@ -123,20 +121,15 @@ def test_vertical_grid_size_near_line_length( test_input += "\n" test_input += ")\n" - try: - assert ( - isort.code( - test_input, - multi_line_output=multi_line_output, - line_length=line_length, - include_trailing_comma=include_trailing_comma, - ) - == test_input + assert ( + isort.code( + test_input, + multi_line_output=multi_line_output, + line_length=line_length, + include_trailing_comma=include_trailing_comma, ) - except AssertionError: - if multi_line_output == 4 and include_trailing_comma and line_length == 19: - pytest.xfail("issue #1640") - raise + == test_input + ) # This test code was written by the `hypothesis.extra.ghostwriter` module @@ -471,47 +464,6 @@ def test_fuzz_vertical_grid_grouped( reject() -@given( - statement=st.text(), - imports=st.lists(st.text()), - white_space=st.text(), - indent=st.text(), - line_length=st.integers(), - comments=st.lists(st.text()), - line_separator=st.text(), - comment_prefix=st.text(), - include_trailing_comma=st.booleans(), - remove_comments=st.booleans(), -) -def test_fuzz_vertical_grid_grouped_no_comma( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): - try: - isort.wrap_modes.vertical_grid_grouped_no_comma( - statement=statement, - imports=imports, - white_space=white_space, - indent=indent, - line_length=line_length, - comments=comments, - line_separator=line_separator, - comment_prefix=comment_prefix, - include_trailing_comma=include_trailing_comma, - remove_comments=remove_comments, - ) - except ValueError: - reject() - - @given( statement=st.text(), imports=st.lists(st.text()), From 32f5c2c30dc3dd67f74fa6ebe75aec362b580203 Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Fri, 5 Feb 2021 16:56:56 +0100 Subject: [PATCH 344/539] fixup! Respect line_length in vertical grid modes and deprecate mode 6 --- isort/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index c2c79cb63..584165b99 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -234,8 +234,8 @@ def __post_init__(self): self, "known_standard_library", frozenset(getattr(stdlibs, self.py_version).stdlib) ) - if self.multi_line_output == WrapModes.VERTICAL_GRID_GROUPED_NO_COMMA: - object.__setattr__(self, "multi_line_output", WrapModes.VERTICAL_GRID_GROUPED) + if self.multi_line_output == WrapModes.VERTICAL_GRID_GROUPED_NO_COMMA: # type: ignore + object.__setattr__(self, "multi_line_output", WrapModes.VERTICAL_GRID_GROUPED) # type: ignore if self.force_alphabetical_sort: object.__setattr__(self, "force_alphabetical_sort_within_sections", True) object.__setattr__(self, "no_sections", True) From bd8bd5fb2e7210c06e6a6c87f3c6aa05d48a2409 Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Fri, 5 Feb 2021 17:10:41 +0100 Subject: [PATCH 345/539] squash! fixup! Respect line_length in vertical grid modes and deprecate mode 6 Linting work-around --- isort/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/isort/settings.py b/isort/settings.py index 584165b99..62b5f54cd 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -235,7 +235,8 @@ def __post_init__(self): ) if self.multi_line_output == WrapModes.VERTICAL_GRID_GROUPED_NO_COMMA: # type: ignore - object.__setattr__(self, "multi_line_output", WrapModes.VERTICAL_GRID_GROUPED) # type: ignore + vertical_grid_grouped = WrapModes.VERTICAL_GRID_GROUPED # type: ignore + object.__setattr__(self, "multi_line_output", vertical_grid_grouped) if self.force_alphabetical_sort: object.__setattr__(self, "force_alphabetical_sort_within_sections", True) object.__setattr__(self, "no_sections", True) From e7f5e306a400971a6db21892198ebdbffa4249f4 Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Fri, 5 Feb 2021 17:33:39 +0100 Subject: [PATCH 346/539] Don't print skipped/broken files with quiet config setting When using --quiet, no warnings are printed about skipped and broken paths. But you do get warnings when using the "quiet" setting in a config file. Be consistent and don't print the warnings then either. --- isort/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index 39dbf19b4..599913994 100644 --- a/isort/main.py +++ b/isort/main.py @@ -1110,7 +1110,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = is_no_attempt = False num_skipped += len(skipped) - if num_skipped and not arguments.get("quiet", False): + if num_skipped and not config.quiet: if config.verbose: for was_skipped in skipped: warn( @@ -1120,7 +1120,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = print(f"Skipped {num_skipped} files") num_broken += len(broken) - if num_broken and not arguments.get("quiet", False): + if num_broken and not config.quiet: if config.verbose: for was_broken in broken: warn(f"{was_broken} was broken path, make sure it exists correctly") From 60dec54fc534e9085e3193c2ca9f75d97bcd19ef Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 6 Feb 2021 15:00:30 -0800 Subject: [PATCH 347/539] Fix coding error found by deepsource --- isort/settings.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index 62b5f54cd..bc0782c55 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -672,11 +672,15 @@ def _get_config_data(file_path: str, sections: Tuple[str]) -> Dict[str, Any]: if section.startswith("*.{") and section.endswith("}"): extension = section[len("*.{") : -1] for config_key in config.keys(): - if config_key.startswith("*.{") and config_key.endswith("}"): - if extension in map( + if ( + config_key.startswith("*.{") + and config_key.endswith("}") + and extension + in map( lambda text: text.strip(), config_key[len("*.{") : -1].split(",") - ): - settings.update(config.items(config_key)) + ) + ): + settings.update(config.items(config_key)) elif config.has_section(section): settings.update(config.items(section)) From 929ee15fe8f2e40480434bcf641c1681c7f2ead6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 7 Feb 2021 22:55:11 -0800 Subject: [PATCH 348/539] Made identified imports .statement() runnable code --- CHANGELOG.md | 1 + isort/identify.py | 10 ++++++---- tests/unit/test_isort.py | 8 ++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b169ff6f0..483a13928 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.8.0 TBD - Fixed #1631: as import comments can in some cases be duplicated. - Implemented #1648: Export MyPY type hints. + - Implemented #1641: Identified import statements now return runnable code. ### 5.7.0 December 30th 2020 - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. diff --git a/isort/identify.py b/isort/identify.py index ff0282443..6a4f6d7d8 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -23,12 +23,14 @@ class Import(NamedTuple): file_path: Optional[Path] = None def statement(self) -> str: - full_path = self.module + import_cmd = "cimport" if self.cimport else "import" if self.attribute: - full_path += f".{self.attribute}" + import_string = f"from {self.module} {import_cmd} {self.attribute}" + else: + import_string = f"{import_cmd} {self.module}" if self.alias: - full_path += f" as {self.alias}" - return f"{'cimport' if self.cimport else 'import'} {full_path}" + import_string += f" as {self.alias}" + return import_string def __str__(self): return ( diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index e5ab86d31..39a396b8b 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -5032,11 +5032,11 @@ def test_find_imports_in_code() -> None: assert identified_imports == [ ":1 import first_straight", ":3 import second_straight", - ":4 import first_from.first_from_function_1", - ":4 import first_from.first_from_function_2", + ":4 from first_from import first_from_function_1", + ":4 from first_from import first_from_function_2", ":5 import bad_name as good_name", - ":6 import parent.some_bad_defs.bad_name_1 as ok_name_1", - ":6 import parent.some_bad_defs.bad_name_2 as ok_name_2", + ":6 from parent.some_bad_defs import bad_name_1 as ok_name_1", + ":6 from parent.some_bad_defs import bad_name_2 as ok_name_2", ":12 indented import needed_in_bla_2", ":15 indented import needed_in_bla", ":18 indented import needed_in_bla_bla", From 3fcfdd250e2bd5cbf3d7a91427ac5f2adffd5cbf Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 8 Feb 2021 23:36:20 -0800 Subject: [PATCH 349/539] Move multi line output documentation to a dedicated documentation page --- README.md | 119 +----------------------------------------------------- 1 file changed, 1 insertion(+), 118 deletions(-) diff --git a/README.md b/README.md index 960901953..85cc8e990 100644 --- a/README.md +++ b/README.md @@ -165,124 +165,7 @@ notified. You will notice above the \"multi\_line\_output\" setting. This setting defines how from imports wrap when they extend past the line\_length -limit and has 12 possible settings: - -**0 - Grid** - -```python -from third_party import (lib1, lib2, lib3, - lib4, lib5, ...) -``` - -**1 - Vertical** - -```python -from third_party import (lib1, - lib2, - lib3 - lib4, - lib5, - ...) -``` - -**2 - Hanging Indent** - -```python -from third_party import \ - lib1, lib2, lib3, \ - lib4, lib5, lib6 -``` - -**3 - Vertical Hanging Indent** - -```python -from third_party import ( - lib1, - lib2, - lib3, - lib4, -) -``` - -**4 - Hanging Grid** - -```python -from third_party import ( - lib1, lib2, lib3, lib4, - lib5, ...) -``` - -**5 - Hanging Grid Grouped** - -```python -from third_party import ( - lib1, lib2, lib3, lib4, - lib5, ... -) -``` - -**6 - Hanging Grid Grouped** - -Same as Mode 5. Deprecated. - -**7 - NOQA** - -```python -from third_party import lib1, lib2, lib3, ... # NOQA -``` - -Alternatively, you can set `force_single_line` to `True` (`-sl` on the -command line) and every import will appear on its own line: - -```python -from third_party import lib1 -from third_party import lib2 -from third_party import lib3 -... -``` - -**8 - Vertical Hanging Indent Bracket** - -Same as Mode 3 - _Vertical Hanging Indent_ but the closing parentheses -on the last line is indented. - -```python -from third_party import ( - lib1, - lib2, - lib3, - lib4, - ) -``` - -**9 - Vertical Prefix From Module Import** - -Starts a new line with the same `from MODULE import ` prefix when lines are longer than the line length limit. - -```python -from third_party import lib1, lib2, lib3 -from third_party import lib4, lib5, lib6 -``` - -**10 - Hanging Indent With Parentheses** - -Same as Mode 2 - _Hanging Indent_ but uses parentheses instead of backslash -for wrapping long lines. - -```python -from third_party import ( - lib1, lib2, lib3, - lib4, lib5, lib6) -``` - -**11 - Backslash Grid** - -Same as Mode 0 - _Grid_ but uses backslashes instead of parentheses to group imports. - -```python -from third_party import lib1, lib2, lib3, \ - lib4, lib5 -``` +limit and has [12 possible settings](https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes/). ## Indentation From 0c8bdf3c5fc8ebffe95cc07aca3ab164f4629c9a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 9 Feb 2021 23:22:59 -0800 Subject: [PATCH 350/539] Add wemake profile --- CHANGELOG.md | 1 + docs/configuration/profiles.md | 8 +++ isort/profiles.py | 7 +++ tests/unit/profiles/test_wemake.py | 80 ++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 tests/unit/profiles/test_wemake.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 483a13928..710eccb1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1631: as import comments can in some cases be duplicated. - Implemented #1648: Export MyPY type hints. - Implemented #1641: Identified import statements now return runnable code. + - Implemented #1661: Added "wemake" profile. ### 5.7.0 December 30th 2020 - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. diff --git a/docs/configuration/profiles.md b/docs/configuration/profiles.md index 9de48e0f7..073f7d60d 100644 --- a/docs/configuration/profiles.md +++ b/docs/configuration/profiles.md @@ -76,3 +76,11 @@ To use any of the listed profiles, use `isort --profile PROFILE_NAME` from the c - **force_grid_wrap**: `0` - **use_parentheses**: `True` - **line_length**: `100` + +#wemake + + + - **multi_line_output**: `3` + - **include_trailing_comma**: `True` + - **use_parentheses**: `True` + - **line_length**: `80` diff --git a/isort/profiles.py b/isort/profiles.py index cb8cb5688..00ebe418e 100644 --- a/isort/profiles.py +++ b/isort/profiles.py @@ -55,6 +55,12 @@ "use_parentheses": True, "line_length": 100, } +wemake = { + "multi_line_output": 3, + "include_trailing_comma": True, + "use_parentheses": True, + "line_length": 80, +} profiles: Dict[str, Dict[str, Any]] = { "black": black, @@ -65,4 +71,5 @@ "plone": plone, "attrs": attrs, "hug": hug, + "wemake": wemake, } diff --git a/tests/unit/profiles/test_wemake.py b/tests/unit/profiles/test_wemake.py new file mode 100644 index 000000000..5e17a6ad6 --- /dev/null +++ b/tests/unit/profiles/test_wemake.py @@ -0,0 +1,80 @@ +"""A set of test cases for the wemake isort profile. + +Snippets are taken directly from the wemake-python-styleguide project here: +https://github.com/wemake-services/wemake-python-styleguide +""" +from functools import partial + +from ..utils import isort_test + +wemake_isort_test = partial(isort_test, profile="wemake", known_first_party=["wemake_python_styleguide"]) + + +def test_wemake_snippet_one(): + wemake_isort_test(""" +import ast +import tokenize +import traceback +from typing import ClassVar, Iterator, Sequence, Type + +from flake8.options.manager import OptionManager +from typing_extensions import final + +from wemake_python_styleguide import constants, types +from wemake_python_styleguide import version as pkg_version +from wemake_python_styleguide.options.config import Configuration +from wemake_python_styleguide.options.validation import validate_options +from wemake_python_styleguide.presets.types import file_tokens as tokens_preset +from wemake_python_styleguide.presets.types import filename as filename_preset +from wemake_python_styleguide.presets.types import tree as tree_preset +from wemake_python_styleguide.transformations.ast_tree import transform +from wemake_python_styleguide.violations import system +from wemake_python_styleguide.visitors import base + +VisitorClass = Type[base.BaseVisitor] +""" + ) + + +def test_wemake_snippet_two(): + wemake_isort_test(""" +from collections import defaultdict +from typing import ClassVar, DefaultDict, List + +from flake8.formatting.base import BaseFormatter +from flake8.statistics import Statistics +from flake8.style_guide import Violation +from pygments import highlight +from pygments.formatters import TerminalFormatter +from pygments.lexers import PythonLexer +from typing_extensions import Final + +from wemake_python_styleguide.version import pkg_version + +#: That url is generated and hosted by Sphinx. +DOCS_URL_TEMPLATE: Final = ( + 'https://wemake-python-stylegui.de/en/{0}/pages/usage/violations/' +) +""") + + +def test_wemake_snippet_three(): + wemake_isort_test(""" +import ast + +from pep8ext_naming import NamingChecker +from typing_extensions import final + +from wemake_python_styleguide.transformations.ast.bugfixes import ( + fix_async_offset, + fix_line_number, +) +from wemake_python_styleguide.transformations.ast.enhancements import ( + set_if_chain, + set_node_context, +) + + +@final +class _ClassVisitor(ast.NodeVisitor): +""") From 146dfe6e9468a784c1177ae7fdce3235c8805d4f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 9 Feb 2021 23:26:02 -0800 Subject: [PATCH 351/539] Fix formatting --- tests/unit/profiles/test_wemake.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/unit/profiles/test_wemake.py b/tests/unit/profiles/test_wemake.py index 5e17a6ad6..ad9530131 100644 --- a/tests/unit/profiles/test_wemake.py +++ b/tests/unit/profiles/test_wemake.py @@ -7,11 +7,14 @@ from ..utils import isort_test -wemake_isort_test = partial(isort_test, profile="wemake", known_first_party=["wemake_python_styleguide"]) +wemake_isort_test = partial( + isort_test, profile="wemake", known_first_party=["wemake_python_styleguide"] +) def test_wemake_snippet_one(): - wemake_isort_test(""" + wemake_isort_test( + """ import ast import tokenize import traceback @@ -37,7 +40,8 @@ def test_wemake_snippet_one(): def test_wemake_snippet_two(): - wemake_isort_test(""" + wemake_isort_test( + """ from collections import defaultdict from typing import ClassVar, DefaultDict, List @@ -55,11 +59,13 @@ def test_wemake_snippet_two(): DOCS_URL_TEMPLATE: Final = ( 'https://wemake-python-stylegui.de/en/{0}/pages/usage/violations/' ) -""") +""" + ) def test_wemake_snippet_three(): - wemake_isort_test(""" + wemake_isort_test( + """ import ast from pep8ext_naming import NamingChecker @@ -77,4 +83,5 @@ def test_wemake_snippet_three(): @final class _ClassVisitor(ast.NodeVisitor): -""") +""" + ) From 05cf7ae995f88c10f0c3ea44a23a1f47d0412ef2 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 9 Feb 2021 23:27:35 -0800 Subject: [PATCH 352/539] Add multi line output modes documentation dedicated file --- docs/configuration/multi_line_output_modes.md | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 docs/configuration/multi_line_output_modes.md diff --git a/docs/configuration/multi_line_output_modes.md b/docs/configuration/multi_line_output_modes.md new file mode 100644 index 000000000..aed2897e9 --- /dev/null +++ b/docs/configuration/multi_line_output_modes.md @@ -0,0 +1,121 @@ +# Multi Line Output Modes + +This [config option](https://pycqa.github.io/isort/docs/configuration/options/#multi-line-output) defines how from imports wrap when they extend past the line\_length +limit and has 12 possible settings: + +## 0 - Grid + +```python +from third_party import (lib1, lib2, lib3, + lib4, lib5, ...) +``` + +## 1 - Vertical + +```python +from third_party import (lib1, + lib2, + lib3 + lib4, + lib5, + ...) +``` + +## 2 - Hanging Indent + +```python +from third_party import \ + lib1, lib2, lib3, \ + lib4, lib5, lib6 +``` + +## 3 - Vertical Hanging Indent + +```python +from third_party import ( + lib1, + lib2, + lib3, + lib4, +) +``` + +## 4 - Hanging Grid + +```python +from third_party import ( + lib1, lib2, lib3, lib4, + lib5, ...) +``` + +## 5 - Hanging Grid Grouped + +```python +from third_party import ( + lib1, lib2, lib3, lib4, + lib5, ... +) +``` + +## 6 - Hanging Grid Grouped + +Same as Mode 5. Deprecated. + +## 7 - NOQA + +```python +from third_party import lib1, lib2, lib3, ... # NOQA +``` + +Alternatively, you can set `force_single_line` to `True` (`-sl` on the +command line) and every import will appear on its own line: + +```python +from third_party import lib1 +from third_party import lib2 +from third_party import lib3 +... +``` + +## 8 - Vertical Hanging Indent Bracket + +Same as Mode 3 - _Vertical Hanging Indent_ but the closing parentheses +on the last line is indented. + +```python +from third_party import ( + lib1, + lib2, + lib3, + lib4, + ) +``` + +## 9 - Vertical Prefix From Module Import + +Starts a new line with the same `from MODULE import ` prefix when lines are longer than the line length limit. + +```python +from third_party import lib1, lib2, lib3 +from third_party import lib4, lib5, lib6 +``` + +## 10 - Hanging Indent With Parentheses + +Same as Mode 2 - _Hanging Indent_ but uses parentheses instead of backslash +for wrapping long lines. + +```python +from third_party import ( + lib1, lib2, lib3, + lib4, lib5, lib6) +``` + +## 11 - Backslash Grid + +Same as Mode 0 - _Grid_ but uses backslashes instead of parentheses to group imports. + +```python +from third_party import lib1, lib2, lib3, \ + lib4, lib5 +``` From 22d2cc776d19f188db7a223aa75507ba676b1f22 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 9 Feb 2021 23:33:24 -0800 Subject: [PATCH 353/539] Make last snippet valid code --- tests/unit/profiles/test_wemake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/profiles/test_wemake.py b/tests/unit/profiles/test_wemake.py index ad9530131..2d1fb22df 100644 --- a/tests/unit/profiles/test_wemake.py +++ b/tests/unit/profiles/test_wemake.py @@ -82,6 +82,6 @@ def test_wemake_snippet_three(): @final -class _ClassVisitor(ast.NodeVisitor): +class _ClassVisitor(ast.NodeVisitor): ... """ ) From 0d1b8ef6e353a37721814fbedfe7cb78ec57178f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 10 Feb 2021 21:26:12 -0800 Subject: [PATCH 354/539] Move custom sections and ordering to a dedicated documentation page --- README.md | 128 +---------------- .../custom_sections_and_ordering.md | 131 ++++++++++++++++++ 2 files changed, 134 insertions(+), 125 deletions(-) create mode 100644 docs/configuration/custom_sections_and_ordering.md diff --git a/README.md b/README.md index 85cc8e990..c42741ef2 100644 --- a/README.md +++ b/README.md @@ -215,132 +215,10 @@ the `-e` option into the command line utility. ## Custom Sections and Ordering -You can change the section order with `sections` option from the default -of: +isort provides configuration options to change almost every aspect of how +imports are organized, ordered, or grouped together in sections. -```ini -FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER -``` - -to your preference (if defined, omitting a default section may cause errors): - -```ini -sections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER -``` - -You also can define your own sections and their order. - -Example: - -```ini -known_django=django -known_pandas=pandas,numpy -sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,PANDAS,FIRSTPARTY,LOCALFOLDER -``` - -would create two new sections with the specified known modules. - -The `no_lines_before` option will prevent the listed sections from being -split from the previous section by an empty line. - -Example: - -```ini -sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER -no_lines_before=LOCALFOLDER -``` - -would produce a section with both FIRSTPARTY and LOCALFOLDER modules -combined. - -**IMPORTANT NOTE**: It is very important to know when setting `known` sections that the naming -does not directly map for historical reasons. For custom settings, the only difference is -capitalization (`known_custom=custom` VS `sections=CUSTOM,...`) for all others reference the -following mapping: - - - `known_standard_library` : `STANDARD_LIBRARY` - - `extra_standard_library` : `STANDARD_LIBRARY` # Like known standard library but appends instead of replacing - - `known_future_library` : `FUTURE` - - `known_first_party`: `FIRSTPARTY` - - `known_third_party`: `THIRDPARTY` - - `known_local_folder`: `LOCALFOLDER` - -This will likely be changed in isort 6.0.0+ in a backwards compatible way. - -## Auto-comment import sections - -Some projects prefer to have import sections uniquely titled to aid in -identifying the sections quickly when visually scanning. isort can -automate this as well. To do this simply set the -`import_heading_{section_name}` setting for each section you wish to -have auto commented - to the desired comment. - -For Example: - -```ini -import_heading_stdlib=Standard Library -import_heading_firstparty=My Stuff -``` - -Would lead to output looking like the following: - -```python -# Standard Library -import os -import sys - -import django.settings - -# My Stuff -import myproject.test -``` - -## Ordering by import length - -isort also makes it easy to sort your imports by length, simply by -setting the `length_sort` option to `True`. This will result in the -following output style: - -```python -from evn.util import ( - Pool, - Dict, - Options, - Constant, - DecayDict, - UnexpectedCodePath, -) -``` - -It is also possible to opt-in to sorting imports by length for only -specific sections by using `length_sort_` followed by the section name -as a configuration item, e.g.: - - length_sort_stdlib=1 - -## Controlling how isort sections `from` imports - -By default isort places straight (`import y`) imports above from imports (`from x import y`): - -```python -import b -from a import a # This will always appear below because it is a from import. -``` - -However, if you prefer to keep strict alphabetical sorting you can set [force sort within sections](https://pycqa.github.io/isort/docs/configuration/options/#force-sort-within-sections) to true. Resulting in: - - -```python -from a import a # This will now appear at top because a appears in the alphabet before b -import b -``` - -You can even tell isort to always place from imports on top, instead of the default of placing them on bottom, using [from first](https://pycqa.github.io/isort/docs/configuration/options/#from-first). - -```python -from b import b # If from first is set to True, all from imports will be placed before non-from imports. -import a -``` +[Click here](https://pycqa.github.io/isort/docs/configuration/custom_sections_and_ordering/) for an overview of all these options. ## Skip processing of imports (outside of configuration) diff --git a/docs/configuration/custom_sections_and_ordering.md b/docs/configuration/custom_sections_and_ordering.md new file mode 100644 index 000000000..df145e889 --- /dev/null +++ b/docs/configuration/custom_sections_and_ordering.md @@ -0,0 +1,131 @@ +# Custom Sections and Ordering + +isort provides lots of features to enable configuring how it sections imports +and how it sorts imports within those sections. +You can change the section order with `sections` option from the default +of: + +```ini +FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER +``` + +to your preference (if defined, omitting a default section may cause errors): + +```ini +sections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER +``` + +You also can define your own sections and their order. + +Example: + +```ini +known_django=django +known_pandas=pandas,numpy +sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,PANDAS,FIRSTPARTY,LOCALFOLDER +``` + +would create two new sections with the specified known modules. + +The `no_lines_before` option will prevent the listed sections from being +split from the previous section by an empty line. + +Example: + +```ini +sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER +no_lines_before=LOCALFOLDER +``` + +would produce a section with both FIRSTPARTY and LOCALFOLDER modules +combined. + +**IMPORTANT NOTE**: It is very important to know when setting `known` sections that the naming +does not directly map for historical reasons. For custom settings, the only difference is +capitalization (`known_custom=custom` VS `sections=CUSTOM,...`) for all others reference the +following mapping: + + - `known_standard_library` : `STANDARD_LIBRARY` + - `extra_standard_library` : `STANDARD_LIBRARY` # Like known standard library but appends instead of replacing + - `known_future_library` : `FUTURE` + - `known_first_party`: `FIRSTPARTY` + - `known_third_party`: `THIRDPARTY` + - `known_local_folder`: `LOCALFOLDER` + +This will likely be changed in isort 6.0.0+ in a backwards compatible way. + + +## Auto-comment import sections + +Some projects prefer to have import sections uniquely titled to aid in +identifying the sections quickly when visually scanning. isort can +automate this as well. To do this simply set the +`import_heading_{section_name}` setting for each section you wish to +have auto commented - to the desired comment. + +For Example: + +```ini +import_heading_stdlib=Standard Library +import_heading_firstparty=My Stuff +``` + +Would lead to output looking like the following: + +```python +# Standard Library +import os +import sys + +import django.settings + +# My Stuff +import myproject.test +``` + +## Ordering by import length + +isort also makes it easy to sort your imports by length, simply by +setting the `length_sort` option to `True`. This will result in the +following output style: + +```python +from evn.util import ( + Pool, + Dict, + Options, + Constant, + DecayDict, + UnexpectedCodePath, +) +``` + +It is also possible to opt-in to sorting imports by length for only +specific sections by using `length_sort_` followed by the section name +as a configuration item, e.g.: + + length_sort_stdlib=1 + +## Controlling how isort sections `from` imports + +By default isort places straight (`import y`) imports above from imports (`from x import y`): + +```python +import b +from a import a # This will always appear below because it is a from import. +``` + +However, if you prefer to keep strict alphabetical sorting you can set [force sort within sections](https://pycqa.github.io/isort/docs/configuration/options/#force-sort-within-sections) to true. Resulting in: + + +```python +from a import a # This will now appear at top because a appears in the alphabet before b +import b +``` + +You can even tell isort to always place from imports on top, instead of the default of placing them on bottom, using [from first](https://pycqa.github.io/isort/docs/configuration/options/#from-first). + +```python +from b import b # If from first is set to True, all from imports will be placed before non-from imports. +import a +``` From 5f8c5fae9c638bf01e0a8fc61115786a39315e47 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 10 Feb 2021 21:33:51 -0800 Subject: [PATCH 355/539] Move githook documentation to dedicated page --- README.md | 30 +----------------------------- docs/configuration/git_hook.md | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 docs/configuration/git_hook.md diff --git a/README.md b/README.md index c42741ef2..6c0f403ba 100644 --- a/README.md +++ b/README.md @@ -310,35 +310,7 @@ Git hook isort provides a hook function that can be integrated into your Git pre-commit script to check Python code before committing. -To cause the commit to fail if there are isort errors (strict mode), -include the following in `.git/hooks/pre-commit`: - -```python -#!/usr/bin/env python -import sys -from isort.hooks import git_hook - -sys.exit(git_hook(strict=True, modify=True, lazy=True, settings_file="")) -``` - -If you just want to display warnings, but allow the commit to happen -anyway, call `git_hook` without the strict parameter. If you want to -display warnings, but not also fix the code, call `git_hook` without the -modify parameter. -The `lazy` argument is to support users who are "lazy" to add files -individually to the index and tend to use `git commit -a` instead. -Set it to `True` to ensure all tracked files are properly isorted, -leave it out or set it to `False` to check only files added to your -index. - -If you want to use a specific configuration file for the hook, you can pass its -path to settings_file. If no path is specifically requested, `git_hook` will -search for the configuration file starting at the directory containing the first -staged file, as per `git diff-index` ordering, and going upward in the directory -structure until a valid configuration file is found or -[`MAX_CONFIG_SEARCH_DEPTH`](src/config.py:35) directories are checked. -The settings_file parameter is used to support users who keep their configuration -file in a directory that might not be a parent of all the other files. +[More info here.](https://pycqa.github.io/isort/docs/configuration/git_hook/) ## Setuptools integration diff --git a/docs/configuration/git_hook.md b/docs/configuration/git_hook.md new file mode 100644 index 000000000..6fb514421 --- /dev/null +++ b/docs/configuration/git_hook.md @@ -0,0 +1,34 @@ +# Git Hook + +isort provides a hook function that can be integrated into your Git +pre-commit script to check Python code before committing. + +To cause the commit to fail if there are isort errors (strict mode), +include the following in `.git/hooks/pre-commit`: + +```python +#!/usr/bin/env python +import sys +from isort.hooks import git_hook + +sys.exit(git_hook(strict=True, modify=True, lazy=True, settings_file="")) +``` + +If you just want to display warnings, but allow the commit to happen +anyway, call `git_hook` without the strict parameter. If you want to +display warnings, but not also fix the code, call `git_hook` without the +modify parameter. +The `lazy` argument is to support users who are "lazy" to add files +individually to the index and tend to use `git commit -a` instead. +Set it to `True` to ensure all tracked files are properly isorted, +leave it out or set it to `False` to check only files added to your +index. + +If you want to use a specific configuration file for the hook, you can pass its +path to settings_file. If no path is specifically requested, `git_hook` will +search for the configuration file starting at the directory containing the first +staged file, as per `git diff-index` ordering, and going upward in the directory +structure until a valid configuration file is found or +[`MAX_CONFIG_SEARCH_DEPTH`](src/config.py:35) directories are checked. +The settings_file parameter is used to support users who keep their configuration +file in a directory that might not be a parent of all the other files. From 8a9ad28563403edec26d1eced173e4e679cc6439 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 10 Feb 2021 21:40:28 -0800 Subject: [PATCH 356/539] Move add or remove imports to dedicated documentation page --- README.md | 29 +++------------------ docs/configuration/add_or_remove_imports.md | 28 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 docs/configuration/add_or_remove_imports.md diff --git a/README.md b/README.md index 6c0f403ba..dd25c66ac 100644 --- a/README.md +++ b/README.md @@ -251,34 +251,11 @@ import b import a ``` -## Adding an import to multiple files +## Adding or removing an import from multiple files -isort makes it easy to add an import statement across multiple files, -while being assured it's correctly placed. +isort can be ran or configured to add / remove imports automatically. -To add an import to all files: - -```bash -isort -a "from __future__ import print_function" *.py -``` - -To add an import only to files that already have imports: - -```bash -isort -a "from __future__ import print_function" --append-only *.py -``` - - -## Removing an import from multiple files - -isort also makes it easy to remove an import from multiple files, -without having to be concerned with how it was originally formatted. - -From the command line: - -```bash -isort --rm "os.system" *.py -``` +[See a complete guide here.]((https://pycqa.github.io/isort/docs/configuration/add_or_remove_imports/) ## Using isort to verify code diff --git a/docs/configuration/add_or_remove_imports.md b/docs/configuration/add_or_remove_imports.md new file mode 100644 index 000000000..d43286e95 --- /dev/null +++ b/docs/configuration/add_or_remove_imports.md @@ -0,0 +1,28 @@ + +## Adding an import to multiple files +isort makes it easy to add an import statement across multiple files, +while being assured it's correctly placed. + +To add an import to all files: + +```bash +isort -a "from __future__ import print_function" *.py +``` + +To add an import only to files that already have imports: + +```bash +isort -a "from __future__ import print_function" --append-only *.py +``` + + +## Removing an import from multiple files + +isort also makes it easy to remove an import from multiple files, +without having to be concerned with how it was originally formatted. + +From the command line: + +```bash +isort --rm "os.system" *.py +``` From 2d13d5ad07ecff5a15e905a50cdbe5413921ebf9 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 10 Feb 2021 21:42:26 -0800 Subject: [PATCH 357/539] Move setuptools integration to dedicated documentation page --- README.md | 26 ++----------------- docs/configuration/setuptools_integration.md | 27 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 docs/configuration/setuptools_integration.md diff --git a/README.md b/README.md index dd25c66ac..54b6d324f 100644 --- a/README.md +++ b/README.md @@ -281,8 +281,7 @@ this one by \@acdha: This can help to ensure a certain level of code quality throughout a project. -Git hook --------- +## Git hook isort provides a hook function that can be integrated into your Git pre-commit script to check Python code before committing. @@ -294,28 +293,7 @@ pre-commit script to check Python code before committing. Upon installation, isort enables a `setuptools` command that checks Python files declared by your project. -Running `python setup.py isort` on the command line will check the files -listed in your `py_modules` and `packages`. If any warning is found, the -command will exit with an error code: - -```bash -$ python setup.py isort -``` - -Also, to allow users to be able to use the command without having to -install isort themselves, add isort to the setup\_requires of your -`setup()` like so: - -```python -setup( - name="project", - packages=["project"], - - setup_requires=[ - "isort" - ] -) -``` +[More info here.](https://pycqa.github.io/isort/docs/configuration/setuptools_integration/) ## Spread the word diff --git a/docs/configuration/setuptools_integration.md b/docs/configuration/setuptools_integration.md new file mode 100644 index 000000000..ca2dbe568 --- /dev/null +++ b/docs/configuration/setuptools_integration.md @@ -0,0 +1,27 @@ +# Setuptools integration + +Upon installation, isort enables a `setuptools` command that checks +Python files declared by your project. + +Running `python setup.py isort` on the command line will check the files +listed in your `py_modules` and `packages`. If any warning is found, the +command will exit with an error code: + +```bash +$ python setup.py isort +``` + +Also, to allow users to be able to use the command without having to +install isort themselves, add isort to the setup\_requires of your +`setup()` like so: + +```python +setup( + name="project", + packages=["project"], + + setup_requires=[ + "isort" + ] +) +``` From 783f3e85545684d970d584a8690c0c18d4b355bf Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Thu, 11 Feb 2021 17:42:11 +0100 Subject: [PATCH 358/539] Sort relative imports correctly with force_sort_within_sections Relative import sort order when using force_sort_within_sections was inconsistent with the order without that setting. Change the force_sort_within_sections sort order to match. This fixes the relative import ordering issues noted in #1659. --- isort/sorting.py | 6 ++---- tests/unit/test_isort.py | 33 +++++++++++++++++++++++++++++++++ tests/unit/test_regressions.py | 8 ++++---- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/isort/sorting.py b/isort/sorting.py index 2a333f2c5..3c61955f8 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -54,10 +54,6 @@ def module_key( def section_key(line: str, config: Config) -> str: section = "B" - if config.reverse_relative and line.startswith("from ."): - match = re.match(r"^from (\.+)\s*(.*)", line) - if match: # pragma: no cover - regex always matches if line starts with "from ." - line = f"from {' '.join(match.groups())}" if config.group_by_package and line.strip().startswith("from"): line = line.split(" import", 1)[0] @@ -66,6 +62,8 @@ def section_key(line: str, config: Config) -> str: else: line = re.sub("^from ", "", line) line = re.sub("^import ", "", line) + sep = " " if config.reverse_relative else "_" + line = re.sub(r"^(\.+)", fr"\1{sep}", line) if line.split(" ")[0] in config.force_to_top: section = "A" # * If honor_case_in_force_sorted_sections is true, and case_sensitive and diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 39a396b8b..6ba716d91 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -2914,6 +2914,39 @@ def test_sort_within_sections_with_force_to_top_issue_473() -> None: ) +def test_force_sort_within_sections_with_relative_imports_issue_1659() -> None: + """Ensure relative imports are sorted within sections""" + assert isort.check_code( + """from .. import a +from ..alpha.beta import b +from ..omega import c +import . +from . import foo +import .apple as bar +from .mango import baz +""", + show_diff=True, + force_sort_within_sections=True, + ) + + +def test_force_sort_within_sections_with_reverse_relative_imports_issue_1659() -> None: + """Ensure reverse ordered relative imports are sorted within sections""" + assert isort.check_code( + """import . +from . import foo +import .apple as bar +from .mango import baz +from .. import a +from ..alpha.beta import b +from ..omega import c +""", + show_diff=True, + force_sort_within_sections=True, + reverse_relative=True, + ) + + def test_correct_number_of_new_lines_with_comment_issue_435() -> None: """Test to ensure that injecting a comment in-between imports doesn't mess up the new line spacing diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 7ba1da770..eb571c218 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -331,18 +331,18 @@ def test_isort_shouldnt_add_extra_new_line_when_fass_and_n_issue_1315(): assert ( isort.code( """ -from . import foo -# Comment canary from .. import foo +# Comment canary +from . import foo """, ensure_newline_before_comments=True, force_sort_within_sections=True, ) == """ -from . import foo +from .. import foo # Comment canary -from .. import foo +from . import foo """ ) From d8394e74692a078c479dedd3a303079ff89c3c87 Mon Sep 17 00:00:00 2001 From: hirosassa Date: Sat, 13 Feb 2021 05:08:05 +0900 Subject: [PATCH 359/539] fix typo on README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54b6d324f..d9c86ff67 100644 --- a/README.md +++ b/README.md @@ -255,7 +255,7 @@ import a isort can be ran or configured to add / remove imports automatically. -[See a complete guide here.]((https://pycqa.github.io/isort/docs/configuration/add_or_remove_imports/) +[See a complete guide here.](https://pycqa.github.io/isort/docs/configuration/add_or_remove_imports/) ## Using isort to verify code From 6e74fe8c91a8b5e989e402838415a6879c250d65 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 12 Feb 2021 22:47:19 -0800 Subject: [PATCH 360/539] Add - @hirosassa to acknowledgements --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index bb41e41f4..b1fc86c9e 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -239,6 +239,7 @@ Documenters - Abtin (@abtinmo) - @scottwedge - Hasan Ramezani (@hramezani) +- @hirosassa -------------------------------------------- From f89e80277868f135ed1da70f757c41efc14e530c Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Sun, 14 Feb 2021 12:39:01 +0100 Subject: [PATCH 361/539] Add new option for sorting relative imports in force-sorted sections Add --sort-relative-in-force-sorted-sections to make sorting of --force-sort-within-sections consistent with the way imports are sorted without force-sorted sections. Add tests for both the old and new behaviors. --- isort/main.py | 8 ++++++++ isort/settings.py | 1 + isort/sorting.py | 13 +++++++++++-- tests/unit/test_isort.py | 39 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/isort/main.py b/isort/main.py index 6824bae73..14fcd487c 100644 --- a/isort/main.py +++ b/isort/main.py @@ -668,6 +668,14 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Honor `--case-sensitive` when `--force-sort-within-sections` is being used. " "Without this option set, `--order-by-type` decides module name ordering too.", ) + section_group.add_argument( + "--srss", + "--sort-relative-in-force-sorted-sections", + action="store_true", + dest="sort_relative_in_force_sorted_sections", + help="When using `--force-sort-within-sections`, sort relative imports the same " + "way as they are sorted when not using that setting.", + ) section_group.add_argument( "--fass", "--force-alphabetical-sort-within-sections", diff --git a/isort/settings.py b/isort/settings.py index bc0782c55..dd6536128 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -206,6 +206,7 @@ class _Config: follow_links: bool = True indented_import_headings: bool = True honor_case_in_force_sorted_sections: bool = False + sort_relative_in_force_sorted_sections: bool = False def __post_init__(self): py_version = self.py_version diff --git a/isort/sorting.py b/isort/sorting.py index 3c61955f8..2cfe60bf5 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -54,6 +54,14 @@ def module_key( def section_key(line: str, config: Config) -> str: section = "B" + if ( + not config.sort_relative_in_force_sorted_sections + and config.reverse_relative + and line.startswith("from .") + ): + match = re.match(r"^from (\.+)\s*(.*)", line) + if match: # pragma: no cover - regex always matches if line starts with "from ." + line = f"from {' '.join(match.groups())}" if config.group_by_package and line.strip().startswith("from"): line = line.split(" import", 1)[0] @@ -62,8 +70,9 @@ def section_key(line: str, config: Config) -> str: else: line = re.sub("^from ", "", line) line = re.sub("^import ", "", line) - sep = " " if config.reverse_relative else "_" - line = re.sub(r"^(\.+)", fr"\1{sep}", line) + if config.sort_relative_in_force_sorted_sections: + sep = " " if config.reverse_relative else "_" + line = re.sub(r"^(\.+)", fr"\1{sep}", line) if line.split(" ")[0] in config.force_to_top: section = "A" # * If honor_case_in_force_sorted_sections is true, and case_sensitive and diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 6ba716d91..19452db6d 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -2914,7 +2914,40 @@ def test_sort_within_sections_with_force_to_top_issue_473() -> None: ) -def test_force_sort_within_sections_with_relative_imports_issue_1659() -> None: +def test_force_sort_within_sections_with_relative_imports() -> None: + """Test sorting of relative imports with force_sort_within_sections=True""" + assert isort.check_code( + """import . +from . import foo +from .. import a +from ..alpha.beta import b +from ..omega import c +import .apple as bar +from .mango import baz +""", + show_diff=True, + force_sort_within_sections=True, + ) + + +def test_force_sort_within_sections_with_reverse_relative_imports() -> None: + """Test reverse sorting of relative imports with force_sort_within_sections=True""" + assert isort.check_code( + """import . +from . import foo +from .mango import baz +from ..alpha.beta import b +from .. import a +from ..omega import c +import .apple as bar +""", + show_diff=True, + force_sort_within_sections=True, + reverse_relative=True, + ) + + +def test_sort_relative_in_force_sorted_sections_issue_1659() -> None: """Ensure relative imports are sorted within sections""" assert isort.check_code( """from .. import a @@ -2927,10 +2960,11 @@ def test_force_sort_within_sections_with_relative_imports_issue_1659() -> None: """, show_diff=True, force_sort_within_sections=True, + sort_relative_in_force_sorted_sections=True, ) -def test_force_sort_within_sections_with_reverse_relative_imports_issue_1659() -> None: +def test_reverse_sort_relative_in_force_sorted_sections_issue_1659() -> None: """Ensure reverse ordered relative imports are sorted within sections""" assert isort.check_code( """import . @@ -2943,6 +2977,7 @@ def test_force_sort_within_sections_with_reverse_relative_imports_issue_1659() - """, show_diff=True, force_sort_within_sections=True, + sort_relative_in_force_sorted_sections=True, reverse_relative=True, ) From c26296fee8dc386e7673eaeb7acf0efff0013a8d Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Sun, 14 Feb 2021 13:05:00 +0100 Subject: [PATCH 362/539] squash! Add new option for sorting relative imports in force-sorted sections Restore unrelated test to original --- tests/unit/test_regressions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index eb571c218..7ba1da770 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -331,18 +331,18 @@ def test_isort_shouldnt_add_extra_new_line_when_fass_and_n_issue_1315(): assert ( isort.code( """ -from .. import foo -# Comment canary from . import foo +# Comment canary +from .. import foo """, ensure_newline_before_comments=True, force_sort_within_sections=True, ) == """ -from .. import foo +from . import foo # Comment canary -from . import foo +from .. import foo """ ) From 0e892a85ab76968dca754487f2f6370dbd11caff Mon Sep 17 00:00:00 2001 From: David Poznik Date: Tue, 16 Feb 2021 15:55:45 -0800 Subject: [PATCH 363/539] Add clarifying note on globstar to README.md --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d9c86ff67..fa6e26ea3 100644 --- a/README.md +++ b/README.md @@ -104,37 +104,40 @@ pip install isort[requirements_deprecated_finder,pipfile_deprecated_finder] **From the command line**: +To run on specific files: + ```bash isort mypythonfile.py mypythonfile2.py ``` -or recursively: +To apply recursively: ```bash isort . ``` -*which is equivalent to:* +If [globstar](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) +is enabled, `isort .` is equivalent to: ```bash isort **/*.py ``` -or to see the proposed changes without applying them: +To view proposed changes without applying them: ```bash isort mypythonfile.py --diff ``` Finally, to atomically run isort against a project, only applying -changes if they don't introduce syntax errors do: +changes if they don't introduce syntax errors: ```bash isort --atomic . ``` -(Note: this is disabled by default as it keeps isort from being able to -run against code written using a different version of Python) +(Note: this is disabled by default, as it prevents isort from +running against code written using a different version of Python.) **From within Python**: From c1ebff5d8603985f3819ac97ade756b6510d1ebe Mon Sep 17 00:00:00 2001 From: Marco Lam Date: Wed, 17 Feb 2021 11:03:18 +0800 Subject: [PATCH 364/539] Fix not replacing the source file if only literals are changed but not the imports --- isort/core.py | 50 +++++++++++++++++----------- tests/unit/test_ticketed_features.py | 39 +++++++++++++++++++++- 2 files changed, 68 insertions(+), 21 deletions(-) diff --git a/isort/core.py b/isort/core.py index a37b707e4..ae32a1dd0 100644 --- a/isort/core.py +++ b/isort/core.py @@ -125,17 +125,22 @@ def process( line_separator = "\n" if code_sorting and code_sorting_section: - output_stream.write( - textwrap.indent( - isort.literal.assignment( - code_sorting_section, - str(code_sorting), - extension, - config=_indented_config(config, indent), - ), - code_sorting_indent, - ) + sorted_code = textwrap.indent( + isort.literal.assignment( + code_sorting_section, + str(code_sorting), + extension, + config=_indented_config(config, indent), + ), + code_sorting_indent, ) + made_changes = made_changes or _has_changed( + before=code_sorting_section, + after=sorted_code, + line_separator=line_separator, + ignore_whitespace=config.ignore_whitespace, + ) + output_stream.write(sorted_code) else: stripped_line = line.strip() if stripped_line and not line_separator: @@ -198,17 +203,22 @@ def process( not_imports = True elif code_sorting: if not stripped_line: - output_stream.write( - textwrap.indent( - isort.literal.assignment( - code_sorting_section, - str(code_sorting), - extension, - config=_indented_config(config, indent), - ), - code_sorting_indent, - ) + sorted_code = textwrap.indent( + isort.literal.assignment( + code_sorting_section, + str(code_sorting), + extension, + config=_indented_config(config, indent), + ), + code_sorting_indent, + ) + made_changes = made_changes or _has_changed( + before=code_sorting_section, + after=sorted_code, + line_separator=line_separator, + ignore_whitespace=config.ignore_whitespace, ) + output_stream.write(sorted_code) not_imports = True code_sorting = False code_sorting_section = "" diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 03998a474..130ef67c7 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -7,7 +7,7 @@ import pytest import isort -from isort import Config, exceptions +from isort import Config, api, exceptions def test_semicolon_ignored_for_dynamic_lines_after_import_issue_1178(): @@ -483,6 +483,43 @@ def method(): # isort: dict y = {"b": "c", "z": "z"}""" ) + assert api.sort_stream( + input_stream=StringIO( + """ +import a +import x + +# isort: list +__all__ = ["b", "a", "b"] + +# isort: unique-list +__all__ = ["b", "a", "b"] + +# isort: tuple +__all__ = ("b", "a", "b") + +# isort: unique-tuple +__all__ = ("b", "a", "b") + +# isort: set +__all__ = {"b", "a", "b"} + + +def method(): + # isort: list + x = ["b", "a"] + + +# isort: assignments +d = 1 +b = 2 +a = 3 + +# isort: dict +y = {"z": "z", "b": "b", "b": "c"}""", + ), + output_stream=StringIO(), + ) def test_isort_allows_setting_import_types_issue_1181(): From 5f15873c0b77be6f71b56324becd9832b6dae800 Mon Sep 17 00:00:00 2001 From: Marco Lam Date: Wed, 17 Feb 2021 13:13:24 +0800 Subject: [PATCH 365/539] Merge if statments --- isort/core.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/isort/core.py b/isort/core.py index ae32a1dd0..598789984 100644 --- a/isort/core.py +++ b/isort/core.py @@ -159,10 +159,11 @@ def process( and stripped_line not in config.section_comments ): in_top_comment = True - elif in_top_comment: - if not line.startswith("#") or stripped_line in config.section_comments: - in_top_comment = False - first_comment_index_end = index - 1 + elif in_top_comment and ( + not line.startswith("#") or stripped_line in config.section_comments + ): + in_top_comment = False + first_comment_index_end = index - 1 was_in_quote = bool(in_quote) if (not stripped_line.startswith("#") or in_quote) and '"' in line or "'" in line: From c025ad91b70fcf59fe494e0576539a28aafc0875 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 16 Feb 2021 22:09:10 -0800 Subject: [PATCH 366/539] David Poznik (@dpoznik) to contributors --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index b1fc86c9e..15fff2a12 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -240,6 +240,7 @@ Documenters - @scottwedge - Hasan Ramezani (@hramezani) - @hirosassa +- David Poznik (@dpoznik) -------------------------------------------- From 25d1095de4d5ed139831cffa7ded533dbc2f861b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 17 Feb 2021 23:56:32 -0800 Subject: [PATCH 367/539] Add test case for issue #1670 --- tests/unit/test_regressions.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 7ba1da770..749bf6307 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1537,3 +1537,34 @@ def test_isort_shouldnt_duplicate_comments_issue_1631(): import a # a comment; b comment """ ) + + +def test_isort_shouldnt_add_extra_new_lines_with_import_heading_issue_1670(): + snippet = """#!/usr/bin/python3 -ttu +# Standard Library +import argparse +import datetime + +import attr +import requests + + +def foo() -> int: + print("Hello world") + return 0 + + +def spam(): + + + # Standard Library + import collections + import logging +""" + assert ( + isort.code( + snippet, + import_heading_stdlib="Standard Library", + ) + == snippet + ) From f903034a584c5ef3c67cae5aa0aef1b7cc2db194 Mon Sep 17 00:00:00 2001 From: dongfangtianyu <7629022+dongfangtianyu@users.noreply.github.com> Date: Wed, 24 Feb 2021 13:41:30 +0800 Subject: [PATCH 368/539] fix :Failed to pull configuration information from pyproject.toml --- isort/settings.py | 2 +- tests/unit/test_settings.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/isort/settings.py b/isort/settings.py index bc0782c55..1ea12179e 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -647,7 +647,7 @@ def _find_config(path: str) -> Tuple[str, Dict[str, Any]]: def _get_config_data(file_path: str, sections: Tuple[str]) -> Dict[str, Any]: settings: Dict[str, Any] = {} - with open(file_path) as config_file: + with open(file_path, encoding="utf-8") as config_file: if file_path.endswith(".toml"): config = toml.load(config_file) for section in sections: diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index 157a087f4..6ef8f70ec 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -189,6 +189,31 @@ def test_editorconfig_without_sections(tmpdir): assert not loaded_settings +def test_get_config_data_with_toml_and_utf8(tmpdir): + test_config = tmpdir.join("pyproject.toml") + # Exception: UnicodeDecodeError: 'gbk' codec can't decode byte 0x84 in position 57 + test_config.write_text( + """ +[tool.poetry] + +description = "基于FastAPI + Mysql的 TodoList" # Exception: UnicodeDecodeError +name = "TodoList" +version = "0.1.0" + +[tool.isort] + +multi_line_output = 3 + +""", + "utf8", + ) + loaded_settings = settings._get_config_data( + str(test_config), sections=settings.CONFIG_SECTIONS["pyproject.toml"] + ) + assert loaded_settings + assert str(tmpdir) in loaded_settings["source"] + + def test_as_bool(): assert settings._as_bool("TrUe") is True assert settings._as_bool("true") is True From fb6e0a9da6d2010805916682adc2df4b00b5cc0b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 26 Feb 2021 21:14:25 -0800 Subject: [PATCH 369/539] Resolved issue #1669: Parallel now defaults to number of CPU cores if no value is provided --- CHANGELOG.md | 1 + isort/main.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 710eccb1f..72b8caeb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1648: Export MyPY type hints. - Implemented #1641: Identified import statements now return runnable code. - Implemented #1661: Added "wemake" profile. + - implemented #1669: Parallel (`-j`) now defaults to number of CPU cores if no value is provided. ### 5.7.0 December 30th 2020 - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. diff --git a/isort/main.py b/isort/main.py index 6824bae73..c6f6737b5 100644 --- a/isort/main.py +++ b/isort/main.py @@ -2,6 +2,7 @@ import argparse import functools import json +import multiprocessing import os import sys from gettext import gettext as _ @@ -268,7 +269,13 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Use the old deprecated finder logic that relies on environment introspection magic.", ) general_group.add_argument( - "-j", "--jobs", help="Number of files to process in parallel.", dest="jobs", type=int + "-j", + "--jobs", + help="Number of files to process in parallel.", + dest="jobs", + type=int, + nargs="?", + const=multiprocessing.cpu_count(), ) general_group.add_argument( "--ac", @@ -844,6 +851,7 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> Dict[str, Any]: arguments["multi_line_output"] = WrapModes(int(multi_line_output)) else: arguments["multi_line_output"] = WrapModes[multi_line_output] + return arguments From 3df2ed45ea05a9817f0a099ee436561b28092934 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 27 Feb 2021 03:15:45 -0800 Subject: [PATCH 370/539] Fix errors found by deepsource --- isort/main.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/isort/main.py b/isort/main.py index c6f6737b5..11da105b8 100644 --- a/isort/main.py +++ b/isort/main.py @@ -2,7 +2,6 @@ import argparse import functools import json -import multiprocessing import os import sys from gettext import gettext as _ @@ -275,7 +274,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="jobs", type=int, nargs="?", - const=multiprocessing.cpu_count(), + const=-1, ) general_group.add_argument( "--ac", @@ -993,7 +992,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = config_dict = arguments.copy() ask_to_apply = config_dict.pop("ask_to_apply", False) - jobs = config_dict.pop("jobs", ()) + jobs = config_dict.pop("jobs", None) check = config_dict.pop("check", False) show_diff = config_dict.pop("show_diff", False) write_to_stdout = config_dict.pop("write_to_stdout", False) @@ -1069,7 +1068,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = if jobs: import multiprocessing - executor = multiprocessing.Pool(jobs) + executor = multiprocessing.Pool(jobs if jobs > 0 else multiprocessing.cpu_count()) attempt_iterator = executor.imap( functools.partial( sort_imports, From f6b3fef93e2e382242ab99421ed3de240d9334ad Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 28 Feb 2021 23:59:41 -0800 Subject: [PATCH 371/539] Fix no / safety --- isort/main.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/isort/main.py b/isort/main.py index 11da105b8..648efd012 100644 --- a/isort/main.py +++ b/isort/main.py @@ -347,6 +347,12 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="filename", help="Provide the filename associated with a stream.", ) + target_group.add_argument( + "--no-preserve-root", + action="store_true", + default=False, + help="Tells isort not to treat / specially, allowing it to be ran on the root dir.", + ) output_group.add_argument( "-a", @@ -1000,6 +1006,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = remapped_deprecated_args = config_dict.pop("remapped_deprecated_args", False) stream_filename = config_dict.pop("filename", None) ext_format = config_dict.pop("ext_format", None) + no_preserve_root = config_dict.pop("no_preserve_root", None) wrong_sorted_files = False all_attempt_broken = False no_valid_encodings = False @@ -1037,11 +1044,18 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = file_path=file_path, extension=ext_format, ) + elif "/" in file_names and not no_preserve_root: + printer = create_terminal_printer(color=config.color_output) + printer.error("it is dangerous to operate recursively on '/'") + printer.error("use --no-preserve-root to override this failsafe") + sys.exit(1) else: if stream_filename: printer = create_terminal_printer(color=config.color_output) printer.error("Filename override is intended only for stream (-) sorting.") sys.exit(1) + if file_names == ["/"]: + input("You've requested to run isort against your entire computer (/) are you sure? ") skipped: List[str] = [] broken: List[str] = [] From 8fb0fca0bd192c2aa79c869d115445ccca8d0363 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 1 Mar 2021 00:00:50 -0800 Subject: [PATCH 372/539] Fixed #1668 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72b8caeb2..6b325025a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1648: Export MyPY type hints. - Implemented #1641: Identified import statements now return runnable code. - Implemented #1661: Added "wemake" profile. - - implemented #1669: Parallel (`-j`) now defaults to number of CPU cores if no value is provided. + - Implemented #1669: Parallel (`-j`) now defaults to number of CPU cores if no value is provided. + - Implemented #1668: Added a safeguard against accidental usage against /. ### 5.7.0 December 30th 2020 - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. From 747a08c280028fea66ff6ab8463e7fb05527e8d8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 1 Mar 2021 00:05:45 -0800 Subject: [PATCH 373/539] Update dependencies --- poetry.lock | 433 +++++++++++++++++++++++++++++----------------------- 1 file changed, 240 insertions(+), 193 deletions(-) diff --git a/poetry.lock b/poetry.lock index 526dbbe38..03bcbfd59 100644 --- a/poetry.lock +++ b/poetry.lock @@ -16,14 +16,15 @@ python-versions = "*" [[package]] name = "arrow" -version = "0.17.0" +version = "1.0.2" description = "Better dates & times for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [package.dependencies] python-dateutil = ">=2.7.0" +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "atomicwrites" @@ -184,7 +185,7 @@ six = ">=1.10" [[package]] name = "coverage" -version = "5.3.1" +version = "5.5" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -195,7 +196,7 @@ toml = ["toml"] [[package]] name = "cruft" -version = "2.6.0" +version = "2.7.0" description = "Allows you to maintain all the necessary cruft for packaging and building projects separate from the code you intentionally write. Built on-top of CookieCutter." category = "dev" optional = false @@ -213,7 +214,7 @@ examples = ["examples (>=1.0.2,<2.0.0)"] [[package]] name = "dataclasses" -version = "0.8" +version = "0.7" description = "A backport of the dataclasses module for Python 3.6" category = "dev" optional = false @@ -243,6 +244,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "docstring-parser" +version = "0.7.3" +description = "" +category = "dev" +optional = false +python-versions = "~=3.5" + [[package]] name = "dparse" version = "0.5.1" @@ -367,7 +376,7 @@ gitdb = ">=4.0.1" [[package]] name = "gitpython" -version = "3.1.11" +version = "3.1.14" description = "Python Git Library" category = "dev" optional = false @@ -464,7 +473,7 @@ python-versions = "*" [[package]] name = "hypothesis" -version = "5.43.5" +version = "6.3.4" description = "A library for property-based testing" category = "dev" optional = false @@ -475,8 +484,9 @@ attrs = ">=19.2.0" sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] -all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.3)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] +all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] cli = ["click (>=7.0)", "black (>=19.10b0)"] +codemods = ["libcst (>=0.3.16)"] dateutil = ["python-dateutil (>=1.4)"] django = ["pytz (>=2014.1)", "django (>=2.2)"] dpcontracts = ["dpcontracts (>=0.4)"] @@ -484,7 +494,7 @@ ghostwriter = ["black (>=19.10b0)"] lark = ["lark-parser (>=0.6.5)"] numpy = ["numpy (>=1.9.0)"] pandas = ["pandas (>=0.25)"] -pytest = ["pytest (>=4.3)"] +pytest = ["pytest (>=4.6)"] pytz = ["pytz (>=2014.1)"] redis = ["redis (>=3.0.0)"] zoneinfo = ["importlib-resources (>=3.3.0)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] @@ -506,7 +516,7 @@ pytest = ["pytest (>=4.0.0,<5.0.0)"] [[package]] name = "hypothesmith" -version = "0.1.7" +version = "0.1.8" description = "Hypothesis strategies for generating Python programs, something like CSmith" category = "dev" optional = false @@ -527,15 +537,18 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "immutables" -version = "0.14" +version = "0.15" description = "Immutable Collections" category = "dev" optional = false python-versions = ">=3.5" +[package.extras] +test = ["flake8 (>=3.8.4,<3.9.0)", "pycodestyle (>=2.6.0,<2.7.0)"] + [[package]] name = "importlib-metadata" -version = "3.3.0" +version = "3.7.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -546,8 +559,8 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -613,7 +626,7 @@ testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"] [[package]] name = "jinja2" -version = "2.11.2" +version = "2.11.3" description = "A very fast and expressive template engine." category = "dev" optional = false @@ -639,7 +652,7 @@ jinja2 = "*" [[package]] name = "joblib" -version = "1.0.0" +version = "1.0.1" description = "Lightweight pipelining with Python functions" category = "dev" optional = false @@ -647,7 +660,7 @@ python-versions = ">=3.6" [[package]] name = "lark-parser" -version = "0.11.1" +version = "0.11.2" description = "a modern parsing library" category = "dev" optional = false @@ -659,7 +672,7 @@ regex = ["regex"] [[package]] name = "libcst" -version = "0.3.16" +version = "0.3.17" description = "A concrete syntax tree with AST-like properties for Python 3.5, 3.6, 3.7 and 3.8 programs." category = "dev" optional = false @@ -704,7 +717,7 @@ languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] [[package]] name = "mako" -version = "1.1.3" +version = "1.1.4" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." category = "dev" optional = false @@ -719,7 +732,7 @@ lingua = ["lingua"] [[package]] name = "markdown" -version = "3.3.3" +version = "3.3.4" description = "Python implementation of Markdown." category = "dev" optional = false @@ -838,7 +851,7 @@ twitter = ["twython"] [[package]] name = "numpy" -version = "1.19.4" +version = "1.19.5" description = "NumPy is the fundamental package for array computing with Python." category = "dev" optional = false @@ -857,7 +870,7 @@ six = ">=1.8.0" [[package]] name = "packaging" -version = "20.8" +version = "20.9" description = "Core utilities for Python packages" category = "main" optional = false @@ -896,13 +909,15 @@ python-versions = ">=2.6" [[package]] name = "pdocs" -version = "1.0.2" +version = "1.1.1" description = "A simple program and library to auto generate API documentation for Python modules." category = "dev" optional = false python-versions = ">=3.6,<4.0" [package.dependencies] +dataclasses = {version = ">=0.7,<0.8", markers = "python_version >= \"3.6\" and python_version < \"3.7\""} +docstring_parser = ">=0.7.2,<0.8.0" hug = ">=2.6,<3.0" Mako = ">=1.1,<2.0" Markdown = ">=3.0.0,<4.0.0" @@ -1030,7 +1045,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "portray" -version = "1.4.0" +version = "1.5.2" description = "Your Project with Great Documentation" category = "dev" optional = false @@ -1039,9 +1054,10 @@ python-versions = ">=3.6,<4.0" [package.dependencies] GitPython = ">=3.0,<4.0" hug = ">=2.6,<3.0" +livereload = ">=2.6.3,<3.0.0" mkdocs = ">=1.0,<2.0" mkdocs-material = ">=5.0,<6.0" -pdocs = ">=1.0.2,<2.0.0" +pdocs = ">=1.1.1,<2.0.0" pymdown-extensions = ">=7.0,<8.0" toml = ">=0.10.0,<0.11.0" yaspin = ">=0.15.0,<0.16.0" @@ -1126,7 +1142,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.7.3" +version = "2.8.0" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -1167,7 +1183,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pytest" -version = "6.2.1" +version = "6.2.2" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -1189,14 +1205,14 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm [[package]] name = "pytest-cov" -version = "2.10.1" +version = "2.11.1" description = "Pytest plugin for measuring coverage." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -coverage = ">=4.4" +coverage = ">=5.2.1" pytest = ">=4.6" [package.extras] @@ -1243,11 +1259,11 @@ unidecode = ["Unidecode (>=1.1.1)"] [[package]] name = "pyyaml" -version = "5.3.1" +version = "5.4.1" description = "YAML parser and emitter for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "regex" @@ -1317,7 +1333,7 @@ idna2008 = ["idna"] [[package]] name = "safety" -version = "1.10.0" +version = "1.10.3" description = "Checks installed dependencies for known vulnerabilities." category = "dev" optional = false @@ -1339,7 +1355,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "smmap" -version = "3.0.4" +version = "3.0.5" description = "A pure Python implementation of a sliding window memory map manager" category = "dev" optional = false @@ -1369,8 +1385,8 @@ contextvars = {version = ">=2.1", markers = "python_version < \"3.7\""} [[package]] name = "snowballstemmer" -version = "2.0.0" -description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms." +version = "2.1.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." category = "dev" optional = false python-versions = "*" @@ -1429,7 +1445,7 @@ python-versions = ">= 3.5" [[package]] name = "tqdm" -version = "4.55.0" +version = "4.58.0" description = "Fast, Extensible Progress Meter" category = "dev" optional = false @@ -1502,7 +1518,7 @@ typing-extensions = ">=3.7.4" [[package]] name = "urllib3" -version = "1.26.2" +version = "1.26.3" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -1599,8 +1615,8 @@ appnope = [ {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, ] arrow = [ - {file = "arrow-0.17.0-py2.py3-none-any.whl", hash = "sha256:e098abbd9af3665aea81bdd6c869e93af4feb078e98468dd351c383af187aac5"}, - {file = "arrow-0.17.0.tar.gz", hash = "sha256:ff08d10cda1d36c68657d6ad20d74fbea493d980f8b2d45344e00d6ed2bf6ed4"}, + {file = "arrow-1.0.2-py3-none-any.whl", hash = "sha256:cb1b7bc3a07eb1c1e98ccc740627460c9891636642bcf03c4097b71d8bc5ca1d"}, + {file = "arrow-1.0.2.tar.gz", hash = "sha256:5df8e632e9158c48f42f68a742068bcfc1c0181cbe7543e4cda6089bb287a305"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, @@ -1656,63 +1672,63 @@ cookiecutter = [ {file = "cookiecutter-1.7.2.tar.gz", hash = "sha256:efb6b2d4780feda8908a873e38f0e61778c23f6a2ea58215723bcceb5b515dac"}, ] coverage = [ - {file = "coverage-5.3.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fabeeb121735d47d8eab8671b6b031ce08514c86b7ad8f7d5490a7b6dcd6267d"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7e4d159021c2029b958b2363abec4a11db0ce8cd43abb0d9ce44284cb97217e7"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:378ac77af41350a8c6b8801a66021b52da8a05fd77e578b7380e876c0ce4f528"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e448f56cfeae7b1b3b5bcd99bb377cde7c4eb1970a525c770720a352bc4c8044"}, - {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:cc44e3545d908ecf3e5773266c487ad1877be718d9dc65fc7eb6e7d14960985b"}, - {file = "coverage-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:08b3ba72bd981531fd557f67beee376d6700fba183b167857038997ba30dd297"}, - {file = "coverage-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:8dacc4073c359f40fcf73aede8428c35f84639baad7e1b46fce5ab7a8a7be4bb"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ee2f1d1c223c3d2c24e3afbb2dd38be3f03b1a8d6a83ee3d9eb8c36a52bee899"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9a9d4ff06804920388aab69c5ea8a77525cf165356db70131616acd269e19b36"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:782a5c7df9f91979a7a21792e09b34a658058896628217ae6362088b123c8500"}, - {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:fda29412a66099af6d6de0baa6bd7c52674de177ec2ad2630ca264142d69c6c7"}, - {file = "coverage-5.3.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:f2c6888eada180814b8583c3e793f3f343a692fc802546eed45f40a001b1169f"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8f33d1156241c43755137288dea619105477961cfa7e47f48dbf96bc2c30720b"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b239711e774c8eb910e9b1ac719f02f5ae4bf35fa0420f438cdc3a7e4e7dd6ec"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:f54de00baf200b4539a5a092a759f000b5f45fd226d6d25a76b0dff71177a714"}, - {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:be0416074d7f253865bb67630cf7210cbc14eb05f4099cc0f82430135aaa7a3b"}, - {file = "coverage-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:c46643970dff9f5c976c6512fd35768c4a3819f01f61169d8cdac3f9290903b7"}, - {file = "coverage-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9a4f66259bdd6964d8cf26142733c81fb562252db74ea367d9beb4f815478e72"}, - {file = "coverage-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6e5174f8ca585755988bc278c8bb5d02d9dc2e971591ef4a1baabdf2d99589b"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3911c2ef96e5ddc748a3c8b4702c61986628bb719b8378bf1e4a6184bbd48fe4"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c5ec71fd4a43b6d84ddb88c1df94572479d9a26ef3f150cef3dacefecf888105"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f51dbba78d68a44e99d484ca8c8f604f17e957c1ca09c3ebc2c7e3bbd9ba0448"}, - {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a2070c5affdb3a5e751f24208c5c4f3d5f008fa04d28731416e023c93b275277"}, - {file = "coverage-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:535dc1e6e68fad5355f9984d5637c33badbdc987b0c0d303ee95a6c979c9516f"}, - {file = "coverage-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:a4857f7e2bc6921dbd487c5c88b84f5633de3e7d416c4dc0bb70256775551a6c"}, - {file = "coverage-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fac3c432851038b3e6afe086f777732bcf7f6ebbfd90951fa04ee53db6d0bcdd"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cd556c79ad665faeae28020a0ab3bda6cd47d94bec48e36970719b0b86e4dcf4"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a66ca3bdf21c653e47f726ca57f46ba7fc1f260ad99ba783acc3e58e3ebdb9ff"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ab110c48bc3d97b4d19af41865e14531f300b482da21783fdaacd159251890e8"}, - {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e52d3d95df81c8f6b2a1685aabffadf2d2d9ad97203a40f8d61e51b70f191e4e"}, - {file = "coverage-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:fa10fee7e32213f5c7b0d6428ea92e3a3fdd6d725590238a3f92c0de1c78b9d2"}, - {file = "coverage-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ce6f3a147b4b1a8b09aae48517ae91139b1b010c5f36423fa2b866a8b23df879"}, - {file = "coverage-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:93a280c9eb736a0dcca19296f3c30c720cb41a71b1f9e617f341f0a8e791a69b"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3102bb2c206700a7d28181dbe04d66b30780cde1d1c02c5f3c165cf3d2489497"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8ffd4b204d7de77b5dd558cdff986a8274796a1e57813ed005b33fd97e29f059"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a607ae05b6c96057ba86c811d9c43423f35e03874ffb03fbdcd45e0637e8b631"}, - {file = "coverage-5.3.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:3a3c3f8863255f3c31db3889f8055989527173ef6192a283eb6f4db3c579d830"}, - {file = "coverage-5.3.1-cp38-cp38-win32.whl", hash = "sha256:ff1330e8bc996570221b450e2d539134baa9465f5cb98aff0e0f73f34172e0ae"}, - {file = "coverage-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:3498b27d8236057def41de3585f317abae235dd3a11d33e01736ffedb2ef8606"}, - {file = "coverage-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb499d2b3d1d7b7ba23abe8bf26df5f06ba8c71127f188333dddcf356b4b63f"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3b14b1da110ea50c8bcbadc3b82c3933974dbeea1832e814aab93ca1163cd4c1"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:76b2775dda7e78680d688daabcb485dc87cf5e3184a0b3e012e1d40e38527cc8"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:cef06fb382557f66d81d804230c11ab292d94b840b3cb7bf4450778377b592f4"}, - {file = "coverage-5.3.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f61319e33222591f885c598e3e24f6a4be3533c1d70c19e0dc59e83a71ce27d"}, - {file = "coverage-5.3.1-cp39-cp39-win32.whl", hash = "sha256:cc6f8246e74dd210d7e2b56c76ceaba1cc52b025cd75dbe96eb48791e0250e98"}, - {file = "coverage-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:2757fa64e11ec12220968f65d086b7a29b6583d16e9a544c889b22ba98555ef1"}, - {file = "coverage-5.3.1-pp36-none-any.whl", hash = "sha256:723d22d324e7997a651478e9c5a3120a0ecbc9a7e94071f7e1954562a8806cf3"}, - {file = "coverage-5.3.1-pp37-none-any.whl", hash = "sha256:c89b558f8a9a5a6f2cfc923c304d49f0ce629c3bd85cb442ca258ec20366394c"}, - {file = "coverage-5.3.1.tar.gz", hash = "sha256:38f16b1317b8dd82df67ed5daa5f5e7c959e46579840d77a67a4ceb9cef0a50b"}, + {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, + {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, + {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, + {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, + {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, + {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, + {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, + {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, + {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, + {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, + {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, + {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, + {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, + {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, + {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, + {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, + {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, + {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, + {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, + {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, + {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, ] cruft = [ - {file = "cruft-2.6.0-py3-none-any.whl", hash = "sha256:80e32a2fd8103f3c57c96af1c25d96d748072727a8563c58a65eece56a02b441"}, - {file = "cruft-2.6.0.tar.gz", hash = "sha256:bbeea9fb69812afd74a8140cca5ae7fdab761d4df50b137f2437fab9e72c4580"}, + {file = "cruft-2.7.0-py3-none-any.whl", hash = "sha256:f4b8d93276a07fedd4b6bc7880c9f7ec72ce35fca61effe049bd1e951c601776"}, + {file = "cruft-2.7.0.tar.gz", hash = "sha256:a47b84387f3133181da9f82f4711246147363eff78f13bd2b031400a33ee24d1"}, ] dataclasses = [ - {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, - {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, + {file = "dataclasses-0.7-py3-none-any.whl", hash = "sha256:3459118f7ede7c8bea0fe795bff7c6c2ce287d01dd226202f7c9ebc0610a7836"}, + {file = "dataclasses-0.7.tar.gz", hash = "sha256:494a6dcae3b8bcf80848eea2ef64c0cc5cd307ffc263e17cdf42f3e5420808e6"}, ] decorator = [ {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, @@ -1725,6 +1741,9 @@ distlib = [ docopt = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] +docstring-parser = [ + {file = "docstring_parser-0.7.3.tar.gz", hash = "sha256:cde5fbf8b846433dfbde1e0f96b7f909336a634d5df34a38cb75050c7346734a"}, +] dparse = [ {file = "dparse-0.5.1-py3-none-any.whl", hash = "sha256:e953a25e44ebb60a5c6efc2add4420c177f1d8404509da88da9729202f306994"}, {file = "dparse-0.5.1.tar.gz", hash = "sha256:a1b5f169102e1c894f9a7d5ccf6f9402a836a5d24be80a986c7ce9eaed78f367"}, @@ -1781,8 +1800,8 @@ gitdb2 = [ {file = "gitdb2-4.0.2.tar.gz", hash = "sha256:0986cb4003de743f2b3aba4c828edd1ab58ce98e1c4a8acf72ef02760d4beb4e"}, ] gitpython = [ - {file = "GitPython-3.1.11-py3-none-any.whl", hash = "sha256:6eea89b655917b500437e9668e4a12eabdcf00229a0df1762aabd692ef9b746b"}, - {file = "GitPython-3.1.11.tar.gz", hash = "sha256:befa4d101f91bad1b632df4308ec64555db684c360bd7d2130b4807d49ce86b8"}, + {file = "GitPython-3.1.14-py3-none-any.whl", hash = "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b"}, + {file = "GitPython-3.1.14.tar.gz", hash = "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"}, ] h11 = [ {file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"}, @@ -1817,38 +1836,41 @@ hyperframe = [ {file = "hyperframe-5.2.0.tar.gz", hash = "sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"}, ] hypothesis = [ - {file = "hypothesis-5.43.5-py3-none-any.whl", hash = "sha256:546db914a7a7be1ccacbd408cf4cec4fa958b96b4015a2216f8187e4f0ec7eaa"}, - {file = "hypothesis-5.43.5.tar.gz", hash = "sha256:9377cd796a5bca3c0ae74ef1c592aa231d3a04cde948467bace9344148ee75cb"}, + {file = "hypothesis-6.3.4-py3-none-any.whl", hash = "sha256:69f7d32270f52a14f853cb3673eae65fc2f057601f6e7b5502e5c905985d8c69"}, + {file = "hypothesis-6.3.4.tar.gz", hash = "sha256:26771ce7f21c2ed08e93f62bdd63755dab051c163cc9d54544269b5b8b550eac"}, ] hypothesis-auto = [ {file = "hypothesis-auto-1.1.4.tar.gz", hash = "sha256:5e2c2fb09dc09842512d80630bb792359a1d33d2c0473ad47ee23da0be9e32b1"}, {file = "hypothesis_auto-1.1.4-py3-none-any.whl", hash = "sha256:fea8560c4522c0fd490ed8cc17e420b95dabebb11b9b334c59bf2d768839015f"}, ] hypothesmith = [ - {file = "hypothesmith-0.1.7-py3-none-any.whl", hash = "sha256:f37d0b55fec60d31c4fff271e6ae38100fce4c622c584aae0037f08163fea93f"}, - {file = "hypothesmith-0.1.7.tar.gz", hash = "sha256:97802ad8136033e65db748bbb5a7c9193b9e4df85028f074484db993e8548019"}, + {file = "hypothesmith-0.1.8-py3-none-any.whl", hash = "sha256:6248c3d0e0dc934e5352e3f7d79290560ab5861847ca6701e410f9a287461216"}, + {file = "hypothesmith-0.1.8.tar.gz", hash = "sha256:f9ff047b15c4ed312ce3da57ea27570f86d6b53ce12af9f25e59e6576a00410a"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, ] immutables = [ - {file = "immutables-0.14-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:860666fab142401a5535bf65cbd607b46bc5ed25b9d1eb053ca8ed9a1a1a80d6"}, - {file = "immutables-0.14-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ce01788878827c3f0331c254a4ad8d9721489a5e65cc43e19c80040b46e0d297"}, - {file = "immutables-0.14-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:8797eed4042f4626b0bc04d9cf134208918eb0c937a8193a2c66df5041e62d2e"}, - {file = "immutables-0.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:33ce2f977da7b5e0dddd93744862404bdb316ffe5853ec853e53141508fa2e6a"}, - {file = "immutables-0.14-cp36-cp36m-win_amd64.whl", hash = "sha256:6c8eace4d98988c72bcb37c05e79aae756832738305ae9497670482a82db08bc"}, - {file = "immutables-0.14-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:ab6c18b7b2b2abc83e0edc57b0a38bf0915b271582a1eb8c7bed1c20398f8040"}, - {file = "immutables-0.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c099212fd6504513a50e7369fe281007c820cf9d7bb22a336486c63d77d6f0b2"}, - {file = "immutables-0.14-cp37-cp37m-win_amd64.whl", hash = "sha256:714aedbdeba4439d91cb5e5735cb10631fc47a7a69ea9cc8ecbac90322d50a4a"}, - {file = "immutables-0.14-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:1c11050c49e193a1ec9dda1747285333f6ba6a30bbeb2929000b9b1192097ec0"}, - {file = "immutables-0.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c453e12b95e1d6bb4909e8743f88b7f5c0c97b86a8bc0d73507091cb644e3c1e"}, - {file = "immutables-0.14-cp38-cp38-win_amd64.whl", hash = "sha256:ef9da20ec0f1c5853b5c8f8e3d9e1e15b8d98c259de4b7515d789a606af8745e"}, - {file = "immutables-0.14.tar.gz", hash = "sha256:a0a1cc238b678455145bae291d8426f732f5255537ed6a5b7645949704c70a78"}, + {file = "immutables-0.15-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:6728f4392e3e8e64b593a5a0cd910a1278f07f879795517e09f308daed138631"}, + {file = "immutables-0.15-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f0836cd3bdc37c8a77b192bbe5f41dbcc3ce654db048ebbba89bdfe6db7a1c7a"}, + {file = "immutables-0.15-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:8703d8abfd8687932f2a05f38e7de270c3a6ca3bd1c1efb3c938656b3f2f985a"}, + {file = "immutables-0.15-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b8ad986f9b532c026f19585289384b0769188fcb68b37c7f0bd0df9092a6ca54"}, + {file = "immutables-0.15-cp36-cp36m-win_amd64.whl", hash = "sha256:6f117d9206165b9dab8fd81c5129db757d1a044953f438654236ed9a7a4224ae"}, + {file = "immutables-0.15-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b75ade826920c4e490b1bb14cf967ac14e61eb7c5562161c5d7337d61962c226"}, + {file = "immutables-0.15-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:b7e13c061785e34f73c4f659861f1b3e4a5fd918e4395c84b21c4e3d449ebe27"}, + {file = "immutables-0.15-cp37-cp37m-win_amd64.whl", hash = "sha256:3035849accee4f4e510ed7c94366a40e0f5fef9069fbe04a35f4787b13610a4a"}, + {file = "immutables-0.15-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:b04fa69174e0c8f815f9c55f2a43fc9e5a68452fab459a08e904a74e8471639f"}, + {file = "immutables-0.15-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:141c2e9ea515a3a815007a429f0b47a578ebeb42c831edaec882a245a35fffca"}, + {file = "immutables-0.15-cp38-cp38-win_amd64.whl", hash = "sha256:cbe8c64640637faa5535d539421b293327f119c31507c33ca880bd4f16035eb6"}, + {file = "immutables-0.15-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a0a4e4417d5ef4812d7f99470cd39347b58cb927365dd2b8da9161040d260db0"}, + {file = "immutables-0.15-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3b15c08c71c59e5b7c2470ef949d49ff9f4263bb77f488422eaa157da84d6999"}, + {file = "immutables-0.15-cp39-cp39-win_amd64.whl", hash = "sha256:2283a93c151566e6830aee0e5bee55fc273455503b43aa004356b50f9182092b"}, + {file = "immutables-0.15.tar.gz", hash = "sha256:3713ab1ebbb6946b7ce1387bb9d1d7f5e09c45add58c2a2ee65f963c171e746b"}, ] importlib-metadata = [ - {file = "importlib_metadata-3.3.0-py3-none-any.whl", hash = "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"}, - {file = "importlib_metadata-3.3.0.tar.gz", hash = "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed"}, + {file = "importlib_metadata-3.7.0-py3-none-any.whl", hash = "sha256:c6af5dbf1126cd959c4a8d8efd61d4d3c83bddb0459a17e554284a077574b614"}, + {file = "importlib_metadata-3.7.0.tar.gz", hash = "sha256:24499ffde1b80be08284100393955842be4a59c7c16bbf2738aad0e464a8e0aa"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -1867,24 +1889,23 @@ jedi = [ {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, ] jinja2 = [ - {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, - {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, + {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, + {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, ] jinja2-time = [ {file = "jinja2-time-0.2.0.tar.gz", hash = "sha256:d14eaa4d315e7688daa4969f616f226614350c48730bfa1692d2caebd8c90d40"}, {file = "jinja2_time-0.2.0-py2.py3-none-any.whl", hash = "sha256:d3eab6605e3ec8b7a0863df09cc1d23714908fa61aa6986a845c20ba488b4efa"}, ] joblib = [ - {file = "joblib-1.0.0-py3-none-any.whl", hash = "sha256:75ead23f13484a2a414874779d69ade40d4fa1abe62b222a23cd50d4bc822f6f"}, - {file = "joblib-1.0.0.tar.gz", hash = "sha256:7ad866067ac1fdec27d51c8678ea760601b70e32ff1881d4dc8e1171f2b64b24"}, + {file = "joblib-1.0.1-py3-none-any.whl", hash = "sha256:feeb1ec69c4d45129954f1b7034954241eedfd6ba39b5e9e4b6883be3332d5e5"}, + {file = "joblib-1.0.1.tar.gz", hash = "sha256:9c17567692206d2f3fb9ecf5e991084254fe631665c450b443761c4186a613f7"}, ] lark-parser = [ - {file = "lark-parser-0.11.1.tar.gz", hash = "sha256:20bdefdf1b6e9bcb38165ea5cc4f27921a99c6f4c35264a3a953fd60335f1f8c"}, - {file = "lark_parser-0.11.1-py2.py3-none-any.whl", hash = "sha256:8b747e1f544dcc2789e3feaddd2a50c6a73bed69d62e9c69760c1e1f7d23495f"}, + {file = "lark-parser-0.11.2.tar.gz", hash = "sha256:ef610461ebf2b243502f337d9d49879e39f9add846a4749e88c8dcdc1378bb6b"}, ] libcst = [ - {file = "libcst-0.3.16-py3-none-any.whl", hash = "sha256:2c9e40245b8cb49b5219c76b36fe7037effa7594b9e6d5a092be99f8083d2415"}, - {file = "libcst-0.3.16.tar.gz", hash = "sha256:99c200004b6e845642eea7a433844d144994767f9ed50705171720b76d28cf7e"}, + {file = "libcst-0.3.17-py3-none-any.whl", hash = "sha256:4638e4e8f166f4c74df399222d347ce3e1d316e206b550d8a6254d51b4cf7275"}, + {file = "libcst-0.3.17.tar.gz", hash = "sha256:2766671c107263daa3fc34e39d55134a6fe253701564d7670586f30eee2c201c"}, ] livereload = [ {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, @@ -1894,12 +1915,11 @@ lunr = [ {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, ] mako = [ - {file = "Mako-1.1.3-py2.py3-none-any.whl", hash = "sha256:93729a258e4ff0747c876bd9e20df1b9758028946e976324ccd2d68245c7b6a9"}, - {file = "Mako-1.1.3.tar.gz", hash = "sha256:8195c8c1400ceb53496064314c6736719c6f25e7479cd24c77be3d9361cddc27"}, + {file = "Mako-1.1.4.tar.gz", hash = "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab"}, ] markdown = [ - {file = "Markdown-3.3.3-py3-none-any.whl", hash = "sha256:c109c15b7dc20a9ac454c9e6025927d44460b85bd039da028d85e2b6d0bcc328"}, - {file = "Markdown-3.3.3.tar.gz", hash = "sha256:5d9f2b5ca24bc4c7a390d22323ca4bad200368612b5aaa7796babf971d2b2f18"}, + {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, + {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, ] markupsafe = [ {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, @@ -1920,20 +1940,39 @@ markupsafe = [ {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, ] mccabe = [ @@ -1976,48 +2015,48 @@ nltk = [ {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, ] numpy = [ - {file = "numpy-1.19.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9b30d4bd69498fc0c3fe9db5f62fffbb06b8eb9321f92cc970f2969be5e3949"}, - {file = "numpy-1.19.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fedbd128668ead37f33917820b704784aff695e0019309ad446a6d0b065b57e4"}, - {file = "numpy-1.19.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8ece138c3a16db8c1ad38f52eb32be6086cc72f403150a79336eb2045723a1ad"}, - {file = "numpy-1.19.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:64324f64f90a9e4ef732be0928be853eee378fd6a01be21a0a8469c4f2682c83"}, - {file = "numpy-1.19.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ad6f2ff5b1989a4899bf89800a671d71b1612e5ff40866d1f4d8bcf48d4e5764"}, - {file = "numpy-1.19.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d6c7bb82883680e168b55b49c70af29b84b84abb161cbac2800e8fcb6f2109b6"}, - {file = "numpy-1.19.4-cp36-cp36m-win32.whl", hash = "sha256:13d166f77d6dc02c0a73c1101dd87fdf01339febec1030bd810dcd53fff3b0f1"}, - {file = "numpy-1.19.4-cp36-cp36m-win_amd64.whl", hash = "sha256:448ebb1b3bf64c0267d6b09a7cba26b5ae61b6d2dbabff7c91b660c7eccf2bdb"}, - {file = "numpy-1.19.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:27d3f3b9e3406579a8af3a9f262f5339005dd25e0ecf3cf1559ff8a49ed5cbf2"}, - {file = "numpy-1.19.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:16c1b388cc31a9baa06d91a19366fb99ddbe1c7b205293ed072211ee5bac1ed2"}, - {file = "numpy-1.19.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e5b6ed0f0b42317050c88022349d994fe72bfe35f5908617512cd8c8ef9da2a9"}, - {file = "numpy-1.19.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:18bed2bcb39e3f758296584337966e68d2d5ba6aab7e038688ad53c8f889f757"}, - {file = "numpy-1.19.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:fe45becb4c2f72a0907c1d0246ea6449fe7a9e2293bb0e11c4e9a32bb0930a15"}, - {file = "numpy-1.19.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:6d7593a705d662be5bfe24111af14763016765f43cb6923ed86223f965f52387"}, - {file = "numpy-1.19.4-cp37-cp37m-win32.whl", hash = "sha256:6ae6c680f3ebf1cf7ad1d7748868b39d9f900836df774c453c11c5440bc15b36"}, - {file = "numpy-1.19.4-cp37-cp37m-win_amd64.whl", hash = "sha256:9eeb7d1d04b117ac0d38719915ae169aa6b61fca227b0b7d198d43728f0c879c"}, - {file = "numpy-1.19.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cb1017eec5257e9ac6209ac172058c430e834d5d2bc21961dceeb79d111e5909"}, - {file = "numpy-1.19.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:edb01671b3caae1ca00881686003d16c2209e07b7ef8b7639f1867852b948f7c"}, - {file = "numpy-1.19.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f29454410db6ef8126c83bd3c968d143304633d45dc57b51252afbd79d700893"}, - {file = "numpy-1.19.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:ec149b90019852266fec2341ce1db513b843e496d5a8e8cdb5ced1923a92faab"}, - {file = "numpy-1.19.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1aeef46a13e51931c0b1cf8ae1168b4a55ecd282e6688fdb0a948cc5a1d5afb9"}, - {file = "numpy-1.19.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:08308c38e44cc926bdfce99498b21eec1f848d24c302519e64203a8da99a97db"}, - {file = "numpy-1.19.4-cp38-cp38-win32.whl", hash = "sha256:5734bdc0342aba9dfc6f04920988140fb41234db42381cf7ccba64169f9fe7ac"}, - {file = "numpy-1.19.4-cp38-cp38-win_amd64.whl", hash = "sha256:09c12096d843b90eafd01ea1b3307e78ddd47a55855ad402b157b6c4862197ce"}, - {file = "numpy-1.19.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e452dc66e08a4ce642a961f134814258a082832c78c90351b75c41ad16f79f63"}, - {file = "numpy-1.19.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a5d897c14513590a85774180be713f692df6fa8ecf6483e561a6d47309566f37"}, - {file = "numpy-1.19.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:a09f98011236a419ee3f49cedc9ef27d7a1651df07810ae430a6b06576e0b414"}, - {file = "numpy-1.19.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:50e86c076611212ca62e5a59f518edafe0c0730f7d9195fec718da1a5c2bb1fc"}, - {file = "numpy-1.19.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f0d3929fe88ee1c155129ecd82f981b8856c5d97bcb0d5f23e9b4242e79d1de3"}, - {file = "numpy-1.19.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:c42c4b73121caf0ed6cd795512c9c09c52a7287b04d105d112068c1736d7c753"}, - {file = "numpy-1.19.4-cp39-cp39-win32.whl", hash = "sha256:8cac8790a6b1ddf88640a9267ee67b1aee7a57dfa2d2dd33999d080bc8ee3a0f"}, - {file = "numpy-1.19.4-cp39-cp39-win_amd64.whl", hash = "sha256:4377e10b874e653fe96985c05feed2225c912e328c8a26541f7fc600fb9c637b"}, - {file = "numpy-1.19.4-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:2a2740aa9733d2e5b2dfb33639d98a64c3b0f24765fed86b0fd2aec07f6a0a08"}, - {file = "numpy-1.19.4.zip", hash = "sha256:141ec3a3300ab89c7f2b0775289954d193cc8edb621ea05f99db9cb181530512"}, + {file = "numpy-1.19.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff"}, + {file = "numpy-1.19.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aeb9ed923be74e659984e321f609b9ba54a48354bfd168d21a2b072ed1e833ea"}, + {file = "numpy-1.19.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8b5e972b43c8fc27d56550b4120fe6257fdc15f9301914380b27f74856299fea"}, + {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:43d4c81d5ffdff6bae58d66a3cd7f54a7acd9a0e7b18d97abb255defc09e3140"}, + {file = "numpy-1.19.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a4646724fba402aa7504cd48b4b50e783296b5e10a524c7a6da62e4a8ac9698d"}, + {file = "numpy-1.19.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2e55195bc1c6b705bfd8ad6f288b38b11b1af32f3c8289d6c50d47f950c12e76"}, + {file = "numpy-1.19.5-cp36-cp36m-win32.whl", hash = "sha256:39b70c19ec771805081578cc936bbe95336798b7edf4732ed102e7a43ec5c07a"}, + {file = "numpy-1.19.5-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827"}, + {file = "numpy-1.19.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:603aa0706be710eea8884af807b1b3bc9fb2e49b9f4da439e76000f3b3c6ff0f"}, + {file = "numpy-1.19.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cae865b1cae1ec2663d8ea56ef6ff185bad091a5e33ebbadd98de2cfa3fa668f"}, + {file = "numpy-1.19.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:36674959eed6957e61f11c912f71e78857a8d0604171dfd9ce9ad5cbf41c511c"}, + {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:06fab248a088e439402141ea04f0fffb203723148f6ee791e9c75b3e9e82f080"}, + {file = "numpy-1.19.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6149a185cece5ee78d1d196938b2a8f9d09f5a5ebfbba66969302a778d5ddd1d"}, + {file = "numpy-1.19.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:50a4a0ad0111cc1b71fa32dedd05fa239f7fb5a43a40663269bb5dc7877cfd28"}, + {file = "numpy-1.19.5-cp37-cp37m-win32.whl", hash = "sha256:d051ec1c64b85ecc69531e1137bb9751c6830772ee5c1c426dbcfe98ef5788d7"}, + {file = "numpy-1.19.5-cp37-cp37m-win_amd64.whl", hash = "sha256:a12ff4c8ddfee61f90a1633a4c4afd3f7bcb32b11c52026c92a12e1325922d0d"}, + {file = "numpy-1.19.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf2402002d3d9f91c8b01e66fbb436a4ed01c6498fffed0e4c7566da1d40ee1e"}, + {file = "numpy-1.19.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1ded4fce9cfaaf24e7a0ab51b7a87be9038ea1ace7f34b841fe3b6894c721d1c"}, + {file = "numpy-1.19.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:012426a41bc9ab63bb158635aecccc7610e3eff5d31d1eb43bc099debc979d94"}, + {file = "numpy-1.19.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:759e4095edc3c1b3ac031f34d9459fa781777a93ccc633a472a5468587a190ff"}, + {file = "numpy-1.19.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:a9d17f2be3b427fbb2bce61e596cf555d6f8a56c222bd2ca148baeeb5e5c783c"}, + {file = "numpy-1.19.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:99abf4f353c3d1a0c7a5f27699482c987cf663b1eac20db59b8c7b061eabd7fc"}, + {file = "numpy-1.19.5-cp38-cp38-win32.whl", hash = "sha256:384ec0463d1c2671170901994aeb6dce126de0a95ccc3976c43b0038a37329c2"}, + {file = "numpy-1.19.5-cp38-cp38-win_amd64.whl", hash = "sha256:811daee36a58dc79cf3d8bdd4a490e4277d0e4b7d103a001a4e73ddb48e7e6aa"}, + {file = "numpy-1.19.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c843b3f50d1ab7361ca4f0b3639bf691569493a56808a0b0c54a051d260b7dbd"}, + {file = "numpy-1.19.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d6631f2e867676b13026e2846180e2c13c1e11289d67da08d71cacb2cd93d4aa"}, + {file = "numpy-1.19.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7fb43004bce0ca31d8f13a6eb5e943fa73371381e53f7074ed21a4cb786c32f8"}, + {file = "numpy-1.19.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2ea52bd92ab9f768cc64a4c3ef8f4b2580a17af0a5436f6126b08efbd1838371"}, + {file = "numpy-1.19.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:400580cbd3cff6ffa6293df2278c75aef2d58d8d93d3c5614cd67981dae68ceb"}, + {file = "numpy-1.19.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60"}, + {file = "numpy-1.19.5-cp39-cp39-win32.whl", hash = "sha256:ab83f24d5c52d60dbc8cd0528759532736b56db58adaa7b5f1f76ad551416a1e"}, + {file = "numpy-1.19.5-cp39-cp39-win_amd64.whl", hash = "sha256:0eef32ca3132a48e43f6a0f5a82cb508f22ce5a3d6f67a8329c81c8e226d3f6e"}, + {file = "numpy-1.19.5-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:a0d53e51a6cb6f0d9082decb7a4cb6dfb33055308c4c44f53103c073f649af73"}, + {file = "numpy-1.19.5.zip", hash = "sha256:a76f502430dd98d7546e1ea2250a7360c065a5fdea52b2dffe8ae7180909b6f4"}, ] orderedmultidict = [ {file = "orderedmultidict-1.0.1-py2.py3-none-any.whl", hash = "sha256:43c839a17ee3cdd62234c47deca1a8508a3f2ca1d0678a3bf791c87cf84adbf3"}, {file = "orderedmultidict-1.0.1.tar.gz", hash = "sha256:04070bbb5e87291cc9bfa51df413677faf2141c73c61d2a5f7b26bea3cd882ad"}, ] packaging = [ - {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, - {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] parso = [ {file = "parso-0.8.1-py2.py3-none-any.whl", hash = "sha256:15b00182f472319383252c18d5913b69269590616c947747bc50bf4ac768f410"}, @@ -2032,8 +2071,8 @@ pbr = [ {file = "pbr-5.5.1.tar.gz", hash = "sha256:5fad80b613c402d5b7df7bd84812548b2a61e9977387a80a5fc5c396492b13c9"}, ] pdocs = [ - {file = "pdocs-1.0.2-py3-none-any.whl", hash = "sha256:4d5ff87babcd0c46f12b76c887d53225bddb389dee7c6b338dbe281c729fc035"}, - {file = "pdocs-1.0.2.tar.gz", hash = "sha256:2e32432bd2736fd678ac1ce4447cd508deb62b5a12f7ba3bf0e3a374063221e2"}, + {file = "pdocs-1.1.1-py3-none-any.whl", hash = "sha256:4f5116cf5ce0fa9f13171cd74db224636d4d71370115eefce22d8945526fcfc0"}, + {file = "pdocs-1.1.1.tar.gz", hash = "sha256:f148034970220c9e05d2e04d8eb3fcec3575cf480af0966123ef9d6621b46e4f"}, ] pep517 = [ {file = "pep517-0.9.1-py2.py3-none-any.whl", hash = "sha256:3985b91ebf576883efe5fa501f42a16de2607684f3797ddba7202b71b7d0da51"}, @@ -2075,8 +2114,8 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] portray = [ - {file = "portray-1.4.0-py3-none-any.whl", hash = "sha256:a6a06042a6b7fcb876b1e6cdcaee5adaeb6751388cb292fc05b2f31b1a4c3fb2"}, - {file = "portray-1.4.0.tar.gz", hash = "sha256:ea2271c5e3fbe956070a6f8b1aee6dc3d6a66c18c11907e878db8faa6fd2c449"}, + {file = "portray-1.5.2-py3-none-any.whl", hash = "sha256:40c6dfbff392f16a5c56c93aa79356b4ee25aa2d00579d61b560445ec4d3dc47"}, + {file = "portray-1.5.2.tar.gz", hash = "sha256:c8a3d489cfc95df6922868971996bb23584d37e46aa55e7ab9b6d29aa49f109d"}, ] poyo = [ {file = "poyo-0.5.0-py2.py3-none-any.whl", hash = "sha256:3e2ca8e33fdc3c411cd101ca395668395dd5dc7ac775b8e809e3def9f9fe041a"}, @@ -2131,8 +2170,8 @@ pyflakes = [ {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, ] pygments = [ - {file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"}, - {file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"}, + {file = "Pygments-2.8.0-py3-none-any.whl", hash = "sha256:b21b072d0ccdf29297a82a2363359d99623597b8a265b8081760e4d0f7153c88"}, + {file = "Pygments-2.8.0.tar.gz", hash = "sha256:37a13ba168a02ac54cc5891a42b1caec333e59b66addb7fa633ea8a6d73445c0"}, ] pylama = [ {file = "pylama-7.7.1-py2.py3-none-any.whl", hash = "sha256:fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15"}, @@ -2147,12 +2186,12 @@ pyparsing = [ {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ - {file = "pytest-6.2.1-py3-none-any.whl", hash = "sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8"}, - {file = "pytest-6.2.1.tar.gz", hash = "sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"}, + {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"}, + {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"}, ] pytest-cov = [ - {file = "pytest-cov-2.10.1.tar.gz", hash = "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"}, - {file = "pytest_cov-2.10.1-py2.py3-none-any.whl", hash = "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191"}, + {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, + {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, ] pytest-mock = [ {file = "pytest-mock-1.13.0.tar.gz", hash = "sha256:e24a911ec96773022ebcc7030059b57cd3480b56d4f5d19b7c370ec635e6aed5"}, @@ -2166,19 +2205,27 @@ python-slugify = [ {file = "python-slugify-4.0.1.tar.gz", hash = "sha256:69a517766e00c1268e5bbfc0d010a0a8508de0b18d30ad5a1ff357f8ae724270"}, ] pyyaml = [ - {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, - {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, - {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, - {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, - {file = "PyYAML-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a"}, - {file = "PyYAML-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e"}, - {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] regex = [ {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, @@ -2236,16 +2283,16 @@ rfc3986 = [ {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"}, ] safety = [ - {file = "safety-1.10.0-py2.py3-none-any.whl", hash = "sha256:69437acf5dd617abd7086ccd0d50e813e67aa969bb9ca90f1847d5fbea047dcc"}, - {file = "safety-1.10.0.tar.gz", hash = "sha256:2ebc71b44666588d7898905d86d575933fcd5fa3c92d301ed12482602b1e928a"}, + {file = "safety-1.10.3-py2.py3-none-any.whl", hash = "sha256:5f802ad5df5614f9622d8d71fedec2757099705c2356f862847c58c6dfe13e84"}, + {file = "safety-1.10.3.tar.gz", hash = "sha256:30e394d02a20ac49b7f65292d19d38fa927a8f9582cdfd3ad1adbbc66c641ad5"}, ] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] smmap = [ - {file = "smmap-3.0.4-py2.py3-none-any.whl", hash = "sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4"}, - {file = "smmap-3.0.4.tar.gz", hash = "sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24"}, + {file = "smmap-3.0.5-py2.py3-none-any.whl", hash = "sha256:7bfcf367828031dc893530a29cb35eb8c8f2d7c8f2d0989354d75d24c8573714"}, + {file = "smmap-3.0.5.tar.gz", hash = "sha256:84c2751ef3072d4f6b2785ec7ee40244c6f45eb934d9e543e2c51f1bd3d54c50"}, ] smmap2 = [ {file = "smmap2-3.0.1-py3-none-any.whl", hash = "sha256:0cb6ea470b1ad9a65a02ca7f4c7ae601861f7dd24a43812ca51cfca2892bb524"}, @@ -2256,8 +2303,8 @@ sniffio = [ {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, ] snowballstemmer = [ - {file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"}, - {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"}, + {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, + {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] sortedcontainers = [ {file = "sortedcontainers-2.3.0-py2.py3-none-any.whl", hash = "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f"}, @@ -2323,8 +2370,8 @@ tornado = [ {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, ] tqdm = [ - {file = "tqdm-4.55.0-py2.py3-none-any.whl", hash = "sha256:0cd81710de29754bf17b6fee07bdb86f956b4fa20d3078f02040f83e64309416"}, - {file = "tqdm-4.55.0.tar.gz", hash = "sha256:f4f80b96e2ceafea69add7bf971b8403b9cba8fb4451c1220f91c79be4ebd208"}, + {file = "tqdm-4.58.0-py2.py3-none-any.whl", hash = "sha256:2c44efa73b8914dba7807aefd09653ac63c22b5b4ea34f7a80973f418f1a3089"}, + {file = "tqdm-4.58.0.tar.gz", hash = "sha256:c23ac707e8e8aabb825e4d91f8e17247f9cc14b0d64dd9e97be0781e9e525bba"}, ] traitlets = [ {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, @@ -2377,8 +2424,8 @@ typing-inspect = [ {file = "typing_inspect-0.6.0.tar.gz", hash = "sha256:8f1b1dd25908dbfd81d3bebc218011531e7ab614ba6e5bf7826d887c834afab7"}, ] urllib3 = [ - {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, - {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, + {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, + {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"}, ] vistir = [ {file = "vistir-0.5.2-py2.py3-none-any.whl", hash = "sha256:a37079cdbd85d31a41cdd18457fe521e15ec08b255811e81aa061fd5f48a20fb"}, From 3de261f784dfea5729a06c6d8a4dddf82a715caa Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 1 Mar 2021 00:12:35 -0800 Subject: [PATCH 374/539] Add test for allowing root, switch to allow root command --- isort/main.py | 12 +++++------- tests/unit/test_main.py | 5 +++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/isort/main.py b/isort/main.py index 648efd012..0dedcd408 100644 --- a/isort/main.py +++ b/isort/main.py @@ -348,10 +348,10 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Provide the filename associated with a stream.", ) target_group.add_argument( - "--no-preserve-root", + "--allow-root", action="store_true", default=False, - help="Tells isort not to treat / specially, allowing it to be ran on the root dir.", + help="Tells isort not to treat / specially, allowing it to be ran against the root dir.", ) output_group.add_argument( @@ -1006,7 +1006,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = remapped_deprecated_args = config_dict.pop("remapped_deprecated_args", False) stream_filename = config_dict.pop("filename", None) ext_format = config_dict.pop("ext_format", None) - no_preserve_root = config_dict.pop("no_preserve_root", None) + allow_root = config_dict.pop("allow_root", None) wrong_sorted_files = False all_attempt_broken = False no_valid_encodings = False @@ -1044,18 +1044,16 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = file_path=file_path, extension=ext_format, ) - elif "/" in file_names and not no_preserve_root: + elif "/" in file_names and not allow_root: printer = create_terminal_printer(color=config.color_output) printer.error("it is dangerous to operate recursively on '/'") - printer.error("use --no-preserve-root to override this failsafe") + printer.error("use --allow-root to override this failsafe") sys.exit(1) else: if stream_filename: printer = create_terminal_printer(color=config.color_output) printer.error("Filename override is intended only for stream (-) sorting.") sys.exit(1) - if file_names == ["/"]: - input("You've requested to run isort against your entire computer (/) are you sure? ") skipped: List[str] = [] broken: List[str] = [] diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 9a78cbad8..133a94883 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -145,6 +145,11 @@ def test_missing_default_section(tmpdir): main.main([str(python_file)]) +def test_ran_against_root(): + with pytest.raises(SystemExit): + main.main(["/"]) + + def test_main(capsys, tmpdir): base_args = [ "-sp", From 4acbb6da9480ed35a22cd097949eb01c8c13fd4a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 2 Mar 2021 23:03:50 -0800 Subject: [PATCH 375/539] Create failing test for issue #1667 --- tests/unit/test_regressions.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 749bf6307..a823d398e 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1568,3 +1568,20 @@ def spam(): ) == snippet ) + + +def test_isort_shouldnt_add_extra_line_float_to_top_issue_1667(): + assert isort.check_code( + """ + import sys + +sys.path.insert(1, 'path/containing/something_else/..') + +import something_else # isort:skip + +# Some constant +SOME_CONSTANT = 4 +""", + show_diff=True, + float_to_top=True, + ) From dd1059e91e426ab87aee2d0ac8cfb4c6a727714f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 3 Mar 2021 23:19:13 -0800 Subject: [PATCH 376/539] Fix indentation --- tests/unit/test_regressions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index a823d398e..7d311f3c2 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1573,7 +1573,7 @@ def spam(): def test_isort_shouldnt_add_extra_line_float_to_top_issue_1667(): assert isort.check_code( """ - import sys +import sys sys.path.insert(1, 'path/containing/something_else/..') From 7508a0afac727b3ccda9e038b354de0c913ba4cb Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 3 Mar 2021 23:35:19 -0800 Subject: [PATCH 377/539] Don't add newlines if there are is no output --- isort/output.py | 77 +++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/isort/output.py b/isort/output.py index 017ae4d4e..958a81336 100644 --- a/isort/output.py +++ b/isort/output.py @@ -148,43 +148,46 @@ def sorted_imports( output_at = parsed.import_index formatted_output[output_at:0] = output - imports_tail = output_at + len(output) - while [ - character.strip() for character in formatted_output[imports_tail : imports_tail + 1] - ] == [""]: - formatted_output.pop(imports_tail) - - if len(formatted_output) > imports_tail: - next_construct = "" - tail = formatted_output[imports_tail:] - - for index, line in enumerate(tail): - should_skip, in_quote, *_ = parse.skip_line( - line, - in_quote="", - index=len(formatted_output), - section_comments=config.section_comments, - needs_import=False, - ) - if not should_skip and line.strip(): - if ( - line.strip().startswith("#") - and len(tail) > (index + 1) - and tail[index + 1].strip() - ): - continue - next_construct = line - break - if in_quote: - next_construct = line - break - - 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(STATEMENT_DECLARATIONS): - formatted_output[imports_tail:0] = ["", ""] - else: - formatted_output[imports_tail:0] = [""] + if output: + imports_tail = output_at + len(output) + while [ + character.strip() for character in formatted_output[imports_tail : imports_tail + 1] + ] == [""]: + formatted_output.pop(imports_tail) + + if len(formatted_output) > imports_tail: + next_construct = "" + tail = formatted_output[imports_tail:] + + for index, line in enumerate(tail): + should_skip, in_quote, *_ = parse.skip_line( + line, + in_quote="", + index=len(formatted_output), + section_comments=config.section_comments, + needs_import=False, + ) + if not should_skip and line.strip(): + if ( + line.strip().startswith("#") + and len(tail) > (index + 1) + and tail[index + 1].strip() + ): + continue + next_construct = line + break + if in_quote: + next_construct = line + break + + 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(STATEMENT_DECLARATIONS): + formatted_output[imports_tail:0] = ["", ""] + else: + formatted_output[imports_tail:0] = [""] if parsed.place_imports: new_out_lines = [] From f62e6be92d0055750011b14a166c1fb49e381cad Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 3 Mar 2021 23:36:27 -0800 Subject: [PATCH 378/539] Fixed #1667: Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b325025a..dd6f8163a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.8.0 TBD - Fixed #1631: as import comments can in some cases be duplicated. + - Fixed #1667: extra newline added with float-to-top, after skip, in some cases. - Implemented #1648: Export MyPY type hints. - Implemented #1641: Identified import statements now return runnable code. - Implemented #1661: Added "wemake" profile. From aed861e07711996317f049be2e7ea5fc8fc31c76 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 3 Mar 2021 23:42:12 -0800 Subject: [PATCH 379/539] Merge if statements per deepsource --- isort/output.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/isort/output.py b/isort/output.py index 958a81336..6b3401089 100644 --- a/isort/output.py +++ b/isort/output.py @@ -220,20 +220,20 @@ 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 and module not in config.single_line_exclusions - ): - if not config.only_sections: - from_imports = sorting.naturally( - from_imports, - key=lambda key: sorting.module_key( - key, - config, - True, - config.force_alphabetical_sort_within_sections, - section_name=section, - ), - ) + if ( + not config.no_inline_sort + or (config.force_single_line and module not in config.single_line_exclusions) + ) and not config.only_sections: + from_imports = sorting.naturally( + from_imports, + key=lambda key: sorting.module_key( + key, + config, + True, + config.force_alphabetical_sort_within_sections, + section_name=section, + ), + ) if remove_imports: from_imports = [ line for line in from_imports if f"{module}.{line}" not in remove_imports From e09bbbcf07bf600c7d67cf794868e629681e3cca Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 4 Mar 2021 20:05:19 -0800 Subject: [PATCH 380/539] Implemented #1638 / #1644: Provide a flag to ensure same file handle is used after sorting. --- CHANGELOG.md | 1 + isort/api.py | 5 ++++- isort/main.py | 8 ++++++++ isort/settings.py | 1 + tests/unit/test_api.py | 5 +++++ tests/unit/test_main.py | 1 + 6 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd6f8163a..03d415f50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1661: Added "wemake" profile. - Implemented #1669: Parallel (`-j`) now defaults to number of CPU cores if no value is provided. - Implemented #1668: Added a safeguard against accidental usage against /. + - Implemented #1638 / #1644: Provide a flag `--overwrite-in-place` to ensure same file handle is used after sorting. ### 5.7.0 December 30th 2020 - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. diff --git a/isort/api.py b/isort/api.py index 024b75ac5..0cf70845f 100644 --- a/isort/api.py +++ b/isort/api.py @@ -384,7 +384,10 @@ def sort_file( ): return False source_file.stream.close() - tmp_file.replace(source_file.path) + if config.overwrite_in_place: + source_file.path.write_bytes(tmp_file.read_bytes()) + else: + tmp_file.replace(source_file.path) if not config.quiet: print(f"Fixing {source_file.path}") finally: diff --git a/isort/main.py b/isort/main.py index 0dedcd408..bdf6a89dd 100644 --- a/isort/main.py +++ b/isort/main.py @@ -207,6 +207,14 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="write_to_stdout", action="store_true", ) + general_group.add_argument( + "--overwrite-in-place", + help="Tells isort to overwrite in place using the same file handle." + "Comes at a performance and memory usage penalty over it's standard " + "approach but ensures all file flags and modes stay unchanged.", + dest="overwrite_in_place", + action="store_true", + ) general_group.add_argument( "--show-config", dest="show_config", diff --git a/isort/settings.py b/isort/settings.py index 1ea12179e..c7effa5d5 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -206,6 +206,7 @@ class _Config: follow_links: bool = True indented_import_headings: bool = True honor_case_in_force_sorted_sections: bool = False + overwrite_in_place: bool = False def __post_init__(self): py_version = self.py_version diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 2247d8fc5..bffa7fca2 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -34,6 +34,11 @@ def test_sort_file(imperfect) -> None: assert imperfect.read() == fixed_content +def test_sort_file_in_place(imperfect) -> None: + assert api.sort_file(imperfect, overwrite_in_place=True) + assert imperfect.read() == fixed_content + + def test_sort_file_to_stdout(capsys, imperfect) -> None: assert api.sort_file(imperfect, write_to_stdout=True) out, _ = capsys.readouterr() diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 133a94883..eb1970901 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -78,6 +78,7 @@ def test_parse_args(): assert main.parse_args(["--csi"]) == {"combine_straight_imports": True} assert main.parse_args(["--combine-straight-imports"]) == {"combine_straight_imports": True} assert main.parse_args(["--dont-follow-links"]) == {"follow_links": False} + assert main.parse_args(["--overwrite-in-place"]) == {"overwrite_in_place": True} def test_ascii_art(capsys): From 6b0b85f6bd2f18d8e507dc530e8730a381586f72 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 5 Mar 2021 23:54:47 -0800 Subject: [PATCH 381/539] Merge combinabele if statements as recommended by deepsource --- isort/api.py | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/isort/api.py b/isort/api.py index 0cf70845f..a861da122 100644 --- a/isort/api.py +++ b/isort/api.py @@ -165,9 +165,8 @@ def sort_stream( config = _config(path=file_path, config=config, **config_kwargs) content_source = str(file_path or "Passed in content") - if not disregard_skip: - if file_path and config.is_skipped(file_path): - raise FileSkipSetting(content_source) + if not disregard_skip and file_path and config.is_skipped(file_path): + raise FileSkipSetting(content_source) _internal_output = output_stream @@ -404,17 +403,16 @@ def sort_file( disregard_skip=disregard_skip, extension=extension, ) - if changed: - if show_diff: - source_file.stream.seek(0) - output.seek(0) - show_unified_diff( - file_input=source_file.stream.read(), - file_output=output.read(), - file_path=actual_file_path, - output=None if show_diff is True else cast(TextIO, show_diff), - color_output=config.color_output, - ) + if changed and show_diff: + source_file.stream.seek(0) + output.seek(0) + show_unified_diff( + file_input=source_file.stream.read(), + file_output=output.read(), + file_path=actual_file_path, + output=None if show_diff is True else cast(TextIO, show_diff), + color_output=config.color_output, + ) source_file.stream.close() except ExistingSyntaxErrors: @@ -556,13 +554,12 @@ def find_imports_in_paths( def _config( path: Optional[Path] = None, config: Config = DEFAULT_CONFIG, **config_kwargs ) -> Config: - if path: - if ( - config is DEFAULT_CONFIG - and "settings_path" not in config_kwargs - and "settings_file" not in config_kwargs - ): - config_kwargs["settings_path"] = path + if path and ( + config is DEFAULT_CONFIG + and "settings_path" not in config_kwargs + and "settings_file" not in config_kwargs + ): + config_kwargs["settings_path"] = path if config_kwargs: if config is not DEFAULT_CONFIG: From fce310f4157b3d185d9c8076cb191a7de1506daf Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Thu, 11 Mar 2021 17:37:40 +0100 Subject: [PATCH 382/539] Clarify documentation for --skip option The --skip option only handles full file paths or names of individual path components. For example, "foo/bar/baz.py" can be used to skip just that file. And "foo" would skip any files with that name and any files nested in directories with that name. On the other hand, "foo/bar" does *not* skip everything in the "foo/bar" directory. --skip-glob can be used to achieve this. Attempt to clarify the documentation to say that. --- isort/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index 66df2be51..f759429ee 100644 --- a/isort/main.py +++ b/isort/main.py @@ -312,7 +312,9 @@ def _build_arg_parser() -> argparse.ArgumentParser: "-s", "--skip", help="Files that sort imports should skip over. If you want to skip multiple " - "files you should specify twice: --skip file1 --skip file2.", + "files you should specify twice: --skip file1 --skip file2. Values can be ", + "file names, directory names or file paths. To skip all files in a nested path " + "use --skip-glob." dest="skip", action="append", ) From 6d885b5e3770e2e2bfb69522a97b4800c6bf3558 Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Thu, 11 Mar 2021 17:50:52 +0100 Subject: [PATCH 383/539] fixup! Clarify documentation for --skip option --- isort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index f759429ee..32fd4604b 100644 --- a/isort/main.py +++ b/isort/main.py @@ -314,7 +314,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: help="Files that sort imports should skip over. If you want to skip multiple " "files you should specify twice: --skip file1 --skip file2. Values can be ", "file names, directory names or file paths. To skip all files in a nested path " - "use --skip-glob." + "use --skip-glob.", dest="skip", action="append", ) From 6fb82c3c802d6e8d25783987eb93fe9c5b9dddba Mon Sep 17 00:00:00 2001 From: gofr <32750931+gofr@users.noreply.github.com> Date: Thu, 11 Mar 2021 17:59:08 +0100 Subject: [PATCH 384/539] fixup! fixup! Clarify documentation for --skip option --- isort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index 32fd4604b..38d9a4bbb 100644 --- a/isort/main.py +++ b/isort/main.py @@ -312,7 +312,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: "-s", "--skip", help="Files that sort imports should skip over. If you want to skip multiple " - "files you should specify twice: --skip file1 --skip file2. Values can be ", + "files you should specify twice: --skip file1 --skip file2. Values can be " "file names, directory names or file paths. To skip all files in a nested path " "use --skip-glob.", dest="skip", From c4216b10473c2f9073052a32a43e93048fee96cc Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 11 Mar 2021 22:03:38 -0800 Subject: [PATCH 385/539] Update config option docs --- docs/configuration/options.md | 63 +++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index a44e2b30d..5ecb6c5ea 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -33,7 +33,7 @@ Force specific imports to the top of their appropriate section. ## Skip -Files that sort imports should skip over. If you want to skip multiple files you should specify twice: --skip file1 --skip file2. +Files that sort imports should skip over. If you want to skip multiple files you should specify twice: --skip file1 --skip file2. Values can be file names, directory names or file paths. To skip all files in a nested path use --skip-glob. **Type:** Frozenset **Default:** `('.bzr', '.direnv', '.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.svn', '.tox', '.venv', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv')` @@ -226,7 +226,7 @@ known_airflow = ['airflow'] ## Multi Line Output -Multi line output (0-grid, 1-vertical, 2-hanging, 3-vert-hanging, 4-vert-grid, 5-vert-grid-grouped, 6-vert-grid-grouped-no-comma, 7-noqa, 8-vertical-hanging-indent-bracket, 9-vertical-prefix-from-module-import, 10-hanging-indent-with-parentheses). +Multi line output (0-grid, 1-vertical, 2-hanging, 3-vert-hanging, 4-vert-grid, 5-vert-grid-grouped, 6-deprecated-alias-for-5, 7-noqa, 8-vertical-hanging-indent-bracket, 9-vertical-prefix-from-module-import, 10-hanging-indent-with-parentheses). **Type:** Wrapmodes **Default:** `WrapModes.GRID` @@ -752,7 +752,7 @@ Inserts a blank line before a comment following an import. ## Profile -Base profile type to use for configuration. Profiles include: black, django, pycharm, google, open_stack, plone, attrs, hug. As well as any shared profiles. +Base profile type to use for configuration. Profiles include: black, django, pycharm, google, open_stack, plone, attrs, hug, wemake. As well as any shared profiles. **Type:** String **Default:** `` @@ -1019,6 +1019,41 @@ Combines all the bare straight imports of the same section in a single line. Won **Python & Config File Name:** indented_import_headings **CLI Flags:** **Not Supported** +## Honor Case In Force Sorted Sections + +Honor `--case-sensitive` when `--force-sort-within-sections` is being used. Without this option set, `--order-by-type` decides module name ordering too. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** honor_case_in_force_sorted_sections +**CLI Flags:** + +- --hcss +- --honor-case-in-force-sorted-sections + +## Sort Relative In Force Sorted Sections + +When using `--force-sort-within-sections`, sort relative imports the same way as they are sorted when not using that setting. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** sort_relative_in_force_sorted_sections +**CLI Flags:** + +- --srss +- --sort-relative-in-force-sorted-sections + +## Overwrite In Place + +Tells isort to overwrite in place using the same file handle.Comes at a performance and memory usage penalty over it's standard approach but ensures all file flags and modes stay unchanged. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** overwrite_in_place +**CLI Flags:** + +- --overwrite-in-place + ## Show Version Displays the currently installed version of isort. @@ -1178,6 +1213,17 @@ Provide the filename associated with a stream. - --filename +## Allow Root + +Tells isort not to treat / specially, allowing it to be ran against the root dir. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** **Not Supported** +**CLI Flags:** + +- --allow-root + ## Dont Float To Top Forces --float-to-top setting off. See --float-to-top for more information. @@ -1215,17 +1261,6 @@ Tells isort to format the given files according to an extensions formatting rule - --ext-format -## Show Files - -See the files isort will be ran against with the current config options. - -**Type:** Bool -**Default:** `False` -**Python & Config File Name:** **Not Supported** -**CLI Flags:** - -- --show-files - ## Deprecated Flags ==SUPPRESS== From 7d0e94caf4250b12148b28bf5a09540bf00798f1 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 11 Mar 2021 22:05:22 -0800 Subject: [PATCH 386/539] Add - @dongfangtianyu to acknowledgements file --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 15fff2a12..09e000a7c 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -216,6 +216,7 @@ Code Contributors - Quentin Santos (@qsantos) - @gofr - Pavel Savchenko (@asfaltboy) +- @dongfangtianyu Documenters =================== From 31e4fde04d00627977a27b8e083d562b61e2e608 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 12 Mar 2021 23:50:07 -0800 Subject: [PATCH 387/539] Added changelog entry for #1685 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03d415f50..4ce9f0224 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1669: Parallel (`-j`) now defaults to number of CPU cores if no value is provided. - Implemented #1668: Added a safeguard against accidental usage against /. - Implemented #1638 / #1644: Provide a flag `--overwrite-in-place` to ensure same file handle is used after sorting. + - Documented #1685: Skip doesn't support plain directory names, but skip_glob does. ### 5.7.0 December 30th 2020 - Fixed #1612: In rare circumstances an extra comma is added after import and before comment. From d09acd0974847314278e18c92db13950555e0ae9 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 13 Mar 2021 18:41:15 -0800 Subject: [PATCH 388/539] Resolve #1684: Add support for --extend-skip and --extend-skip-glob --- CHANGELOG.md | 1 + isort/main.py | 20 ++++++++++++++++++-- isort/settings.py | 28 +++++++++++++++++++++++++--- tests/unit/test_main.py | 2 +- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ce9f0224..b0107444b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1669: Parallel (`-j`) now defaults to number of CPU cores if no value is provided. - Implemented #1668: Added a safeguard against accidental usage against /. - Implemented #1638 / #1644: Provide a flag `--overwrite-in-place` to ensure same file handle is used after sorting. + - Implemented #1684: Added support for extending skips with `--extend-skip` and `--extend-skip-glob`. - Documented #1685: Skip doesn't support plain directory names, but skip_glob does. ### 5.7.0 December 30th 2020 diff --git a/isort/main.py b/isort/main.py index 38d9a4bbb..5ed22cc26 100644 --- a/isort/main.py +++ b/isort/main.py @@ -311,17 +311,33 @@ def _build_arg_parser() -> argparse.ArgumentParser: target_group.add_argument( "-s", "--skip", - help="Files that sort imports should skip over. If you want to skip multiple " + help="Files that isort should skip over. If you want to skip multiple " "files you should specify twice: --skip file1 --skip file2. Values can be " "file names, directory names or file paths. To skip all files in a nested path " "use --skip-glob.", dest="skip", action="append", ) + target_group.add_argument( + "--extend-skip", + help="Extends --skip to add additional files that isort should skip over. " + "If you want to skip multiple " + "files you should specify twice: --skip file1 --skip file2. Values can be " + "file names, directory names or file paths. To skip all files in a nested path " + "use --skip-glob.", + dest="extend_skip", + action="append", + ) target_group.add_argument( "--sg", "--skip-glob", - help="Files that sort imports should skip over.", + help="Files that isort should skip over.", + dest="skip_glob", + action="append", + ) + target_group.add_argument( + "--extend-skip-glob", + help="Additional files that isort should skip over (extending --skip-glob).", dest="skip_glob", action="append", ) diff --git a/isort/settings.py b/isort/settings.py index e52937721..a62c27bf6 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -121,7 +121,9 @@ class _Config: py_version: str = "3" force_to_top: FrozenSet[str] = frozenset() skip: FrozenSet[str] = DEFAULT_SKIP + extend_skip: FrozenSet[str] = frozenset() skip_glob: FrozenSet[str] = frozenset() + extend_skip_glob: FrozenSet[str] = frozenset() skip_gitignore: bool = False line_length: int = 79 wrap_length: int = 0 @@ -267,6 +269,8 @@ def __init__( ): self._known_patterns: Optional[List[Tuple[Pattern[str], str]]] = None self._section_comments: Optional[Tuple[str, ...]] = None + self._skips: Optional[FrozenSet[str]] = None + self._skip_globs: Optional[FrozenSet[str]] = None if config: config_vars = vars(config).copy() @@ -274,6 +278,8 @@ def __init__( config_vars["py_version"] = config_vars["py_version"].replace("py", "") config_vars.pop("_known_patterns") config_vars.pop("_section_comments") + config_vars.pop("_skips") + config_vars.pop("_skip_globs") super().__init__(**config_vars) # type: ignore return @@ -521,7 +527,7 @@ def is_skipped(self, file_path: Path) -> bool: if normalized_path[1:2] == ":": normalized_path = normalized_path[2:] - for skip_path in self.skip: + for skip_path in self.skips: if posixpath.abspath(normalized_path) == posixpath.abspath( skip_path.replace("\\", "/") ): @@ -529,11 +535,11 @@ def is_skipped(self, file_path: Path) -> bool: position = os.path.split(file_name) while position[1]: - if position[1] in self.skip: + if position[1] in self.skips: return True position = os.path.split(position[0]) - for glob in self.skip_glob: + for glob in self.skip_globs: if fnmatch.fnmatch(file_name, glob) or fnmatch.fnmatch("/" + file_name, glob): return True @@ -574,6 +580,22 @@ def section_comments(self) -> Tuple[str, ...]: self._section_comments = tuple(f"# {heading}" for heading in self.import_headings.values()) return self._section_comments + @property + def skips(self) -> FrozenSet[str]: + if self._skips is not None: + return self._skips + + self._skips = self.skip.union(self.extend_skip) + return self._skips + + @property + def skip_globs(self) -> FrozenSet[str]: + if self._skip_globs is not None: + return self._skip_globs + + self._skip_globs = self.skip_glob.union(self.extend_skip_glob) + return self._skip_globs + def _parse_known_pattern(self, pattern: str) -> List[str]: """Expand pattern if identified as a directory and return found sub packages""" if pattern.endswith(os.path.sep): diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index eb1970901..86f62db3b 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -326,7 +326,7 @@ def test_main(capsys, tmpdir): import b """ ) - main.main([str(tmpdir), "--skip", "skip.py", "--check"]) + main.main([str(tmpdir), "--extend-skip", "skip.py", "--check"]) # without filter options passed in should successfully sort files main.main([str(python_file), str(should_skip), "--verbose", "--atomic"]) From 3945b82808023f76227a29e2a5b5f451ccb4ad3f Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 14 Mar 2021 20:42:49 +0100 Subject: [PATCH 389/539] GitHub Actions: Upgrade actions/cache https://github.com/actions/cache/releases --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 06780574e..857fb209f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - name: pip cache - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cache/pip key: lint-pip-${{ hashFiles('**/pyproject.toml') }} From 215ee2097e25202e699f6ecd8ba1056ff08b3049 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 14 Mar 2021 20:43:35 +0100 Subject: [PATCH 390/539] setup-python --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 857fb209f..af9158bc9 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,7 +21,7 @@ jobs: lint-pip- - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} From 4299df9a8025846bef685324cb9c2fbbfd4ff999 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 14 Mar 2021 20:46:35 +0100 Subject: [PATCH 391/539] Update test.yml --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef93a1eea..1238f7ff7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Ubuntu cache - uses: actions/cache@v1 + uses: actions/cache@v2 if: startsWith(matrix.os, 'ubuntu') with: path: ~/.cache/pip @@ -24,7 +24,7 @@ jobs: ${{ matrix.os }}-${{ matrix.python-version }}- - name: macOS cache - uses: actions/cache@v1 + uses: actions/cache@v2 if: startsWith(matrix.os, 'macOS') with: path: ~/Library/Caches/pip @@ -34,7 +34,7 @@ jobs: ${{ matrix.os }}-${{ matrix.python-version }}- - name: Windows cache - uses: actions/cache@v1 + uses: actions/cache@v2 if: startsWith(matrix.os, 'windows') with: path: c:\users\runneradmin\appdata\local\pip\cache @@ -44,7 +44,7 @@ jobs: ${{ matrix.os }}-${{ matrix.python-version }}- - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} From 748cbe461fb949ee833a934b65d3e413e850b40d Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 14 Mar 2021 20:46:58 +0100 Subject: [PATCH 392/539] Update integration.yml --- .github/workflows/integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 283103118..3f66a7ef2 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - name: pip cache - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cache/pip key: integration-pip-${{ hashFiles('**/pyproject.toml') }} @@ -21,7 +21,7 @@ jobs: integration-pip- - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} From ec3ac4dc2f2b77937321015393fac09173711c00 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 15 Mar 2021 22:40:49 -0700 Subject: [PATCH 393/539] Add entry to changelog for issue #1688 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0107444b..53271c104 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1668: Added a safeguard against accidental usage against /. - Implemented #1638 / #1644: Provide a flag `--overwrite-in-place` to ensure same file handle is used after sorting. - Implemented #1684: Added support for extending skips with `--extend-skip` and `--extend-skip-glob`. + - Implemented #1688: Auto identification and skipping of some invalid import statements. - Documented #1685: Skip doesn't support plain directory names, but skip_glob does. ### 5.7.0 December 30th 2020 From 2bce0f2c197a6c7b70c7ae2a6c9c0cbded2a6600 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 15 Mar 2021 23:04:37 -0700 Subject: [PATCH 394/539] Add test case to ensure incorrectly formatted from statements dont disapear --- tests/unit/test_ticketed_features.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 130ef67c7..dddb31bc2 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -1008,3 +1008,24 @@ def function(): import numpy as np """ ) + + +def test_isort_auto_detects_and_ignores_invalid_from_imports_issue_1688(): + """isort should automatically detect and ignore incorrectly written from import statements + see: https://github.com/PyCQA/isort/issues/1688 + """ + assert ( + isort.code( + """ +from package1 import alright +from package2 imprt and_its_gone +from package3 import also_ok +""" + ) + == """ +from package1 import alright + +from package2 imprt and_its_gone +from package3 import also_ok +""" + ) From 9c18cbb630749925b71f9904e4581a8657c33d3a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 15 Mar 2021 23:04:57 -0700 Subject: [PATCH 395/539] Fix issue #1688: from statements removed when invalid --- isort/core.py | 67 ++++++++++++++++++++++++++++---------------------- isort/parse.py | 8 ++++++ 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/isort/core.py b/isort/core.py index 598789984..d763ad469 100644 --- a/isort/core.py +++ b/isort/core.py @@ -246,9 +246,6 @@ def process( ): import_section += line elif stripped_line.startswith(IMPORT_START_IDENTIFIERS): - did_contain_imports = contains_imports - contains_imports = True - new_indent = line[: -len(line.lstrip())] import_statement = line stripped_line = line.strip().split("#")[0] @@ -266,37 +263,47 @@ def process( stripped_line = line.strip().split("#")[0] import_statement += line - cimport_statement: bool = False if ( - import_statement.lstrip().startswith(CIMPORT_IDENTIFIERS) - or " cimport " in import_statement - or " cimport*" in import_statement - or " cimport(" in import_statement - or ".cimport" in import_statement - ): - cimport_statement = True - - if cimport_statement != cimports or ( - new_indent != indent - and import_section - and (not did_contain_imports or len(new_indent) < len(indent)) + import_statement.lstrip().startswith("from") + and "import" not in import_statement ): - indent = new_indent - if import_section: - next_cimports = cimport_statement - next_import_section = import_statement - import_statement = "" - not_imports = True - line = "" - else: - cimports = cimport_statement + line = import_statement + not_imports = True else: - if new_indent != indent: - if import_section and did_contain_imports: - import_statement = indent + import_statement.lstrip() + did_contain_imports = contains_imports + contains_imports = True + + cimport_statement: bool = False + if ( + import_statement.lstrip().startswith(CIMPORT_IDENTIFIERS) + or " cimport " in import_statement + or " cimport*" in import_statement + or " cimport(" in import_statement + or ".cimport" in import_statement + ): + cimport_statement = True + + if cimport_statement != cimports or ( + new_indent != indent + and import_section + and (not did_contain_imports or len(new_indent) < len(indent)) + ): + indent = new_indent + if import_section: + next_cimports = cimport_statement + next_import_section = import_statement + import_statement = "" + not_imports = True + line = "" else: - indent = new_indent - import_section += import_statement + cimports = cimport_statement + else: + if new_indent != indent: + if import_section and did_contain_imports: + import_statement = indent + import_statement.lstrip() + else: + indent = new_indent + import_section += import_statement else: not_imports = True diff --git a/isort/parse.py b/isort/parse.py index 307015e74..714e39abd 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -260,6 +260,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte for statement in statements: line, raw_line = _normalize_line(statement) type_of_import = import_type(line, config) or "" + raw_lines = [raw_line] if not type_of_import: out_lines.append(raw_line) continue @@ -288,6 +289,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte ): nested_comments[stripped_line] = comments[-1] import_string += line_separator + line + raw_lines.append(line) else: while line.strip().endswith("\\"): line, new_comment = parse_comments(in_lines[index]) @@ -310,6 +312,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte ): nested_comments[stripped_line] = comments[-1] import_string += line_separator + line + raw_lines.append(line) while not line.split("#")[0].strip().endswith(")") and index < line_count: line, new_comment = parse_comments(in_lines[index]) @@ -325,6 +328,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte ): nested_comments[stripped_line] = comments[-1] import_string += line_separator + line + raw_lines.append(line) stripped_line = _strip_syntax(line).strip() if ( @@ -348,6 +352,10 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte .replace("\\", " ") .replace("\n", " ") ) + if "import " not in import_string: + out_lines.extend(raw_lines) + continue + if " cimport " in import_string: parts = import_string.split(" cimport ") cimports = True From d4907b936e03a313c2b0305f63fe57a6c333cb52 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 16 Mar 2021 21:03:38 -0700 Subject: [PATCH 396/539] Add config option for reversing sort order --- isort/main.py | 6 ++++++ isort/settings.py | 1 + 2 files changed, 7 insertions(+) diff --git a/isort/main.py b/isort/main.py index 5ed22cc26..d16626f5f 100644 --- a/isort/main.py +++ b/isort/main.py @@ -562,6 +562,12 @@ def _build_arg_parser() -> argparse.ArgumentParser: action="store_true", help="Reverse order of relative imports.", ) + output_group.add_argument( + "--reverse-sort", + dest="reverse_sort", + action="store_true", + help="Reverses the ordering of imports.", + ) inline_args_group.add_argument( "--sl", "--force-single-line-imports", diff --git a/isort/settings.py b/isort/settings.py index a62c27bf6..dc3754ef8 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -210,6 +210,7 @@ class _Config: honor_case_in_force_sorted_sections: bool = False sort_relative_in_force_sorted_sections: bool = False overwrite_in_place: bool = False + reverse_sort: bool = False def __post_init__(self): py_version = self.py_version From 080501975cb6cf200d4fff6ae17e08eb82c8f65f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 16 Mar 2021 21:04:38 -0700 Subject: [PATCH 397/539] Add to changelog Implemented #1645: Ability to reverse the import sorting order. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53271c104..72bebd8c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1638 / #1644: Provide a flag `--overwrite-in-place` to ensure same file handle is used after sorting. - Implemented #1684: Added support for extending skips with `--extend-skip` and `--extend-skip-glob`. - Implemented #1688: Auto identification and skipping of some invalid import statements. + - Implemented #1645: Ability to reverse the import sorting order. - Documented #1685: Skip doesn't support plain directory names, but skip_glob does. ### 5.7.0 December 30th 2020 From 7802b4f88e92a4a9534b9044527cd1f37c965755 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 17 Mar 2021 00:31:46 -0700 Subject: [PATCH 398/539] Add test case for issue #1645: Allowing sort order to be reversed --- tests/unit/test_ticketed_features.py | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index dddb31bc2..6bed59a66 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -1029,3 +1029,33 @@ def test_isort_auto_detects_and_ignores_invalid_from_imports_issue_1688(): from package3 import also_ok """ ) + + +def test_isort_allows_reversing_sort_order_issue_1645(): + """isort allows reversing the sort order for those who prefer Z or longer imports first. + see: https://github.com/PyCQA/isort/issues/1688 + """ + assert ( + isort.code( + """ +from xxx import ( + g, + hi, + def, + abcd, +) +""", + profile="black", + reverse_sort=True, + length_sort=True, + line_length=20, + ) + == """ +from xxx import ( + abcd, + def, + hi, + g, +) +""" + ) From cb896cd47962034f681171d3eced45a82f0ddf0b Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 17 Mar 2021 00:32:36 -0700 Subject: [PATCH 399/539] Add support for reversing import sort --- isort/output.py | 6 +++++- isort/sorting.py | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/isort/output.py b/isort/output.py index 6b3401089..ee7406ca8 100644 --- a/isort/output.py +++ b/isort/output.py @@ -54,12 +54,15 @@ def sorted_imports( key=lambda key: sorting.module_key( key, config, section_name=section, straight_import=True ), + reverse=config.reverse_sort, ) from_modules = parsed.imports[section]["from"] if not config.only_sections: from_modules = sorting.naturally( - from_modules, key=lambda key: sorting.module_key(key, config, section_name=section) + from_modules, + key=lambda key: sorting.module_key(key, config, section_name=section), + reverse=config.reverse_sort, ) straight_imports = _with_straight_imports( @@ -233,6 +236,7 @@ def _with_from_imports( config.force_alphabetical_sort_within_sections, section_name=section, ), + reverse=config.reverse_sort, ) if remove_imports: from_imports = [ diff --git a/isort/sorting.py b/isort/sorting.py index 2cfe60bf5..e8c355bab 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -96,7 +96,9 @@ def section_key(line: str, config: Config) -> str: return f"{section}{len(line) if config.length_sort else ''}{line}" -def naturally(to_sort: Iterable[str], key: Optional[Callable[[str], Any]] = None) -> List[str]: +def naturally( + to_sort: Iterable[str], key: Optional[Callable[[str], Any]] = None, reverse: bool = False +) -> List[str]: """Returns a naturally sorted list""" if key is None: key_callback = _natural_keys @@ -105,7 +107,7 @@ def naturally(to_sort: Iterable[str], key: Optional[Callable[[str], Any]] = None def key_callback(text: str) -> List[Any]: return _natural_keys(key(text)) # type: ignore - return sorted(to_sort, key=key_callback) + return sorted(to_sort, key=key_callback, reverse=reverse) def _atoi(text: str) -> Any: From 33b4cc3abb700cd89a52ef5fd27080bf32c991a4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 17 Mar 2021 22:22:22 -0700 Subject: [PATCH 400/539] Resolved #1504: Added ability to push star imports to the top to avoid overriding explicitly defined imports. --- CHANGELOG.md | 1 + isort/main.py | 6 ++++++ isort/output.py | 10 ++++++++++ isort/settings.py | 1 + tests/unit/test_ticketed_features.py | 23 +++++++++++++++++++++++ 5 files changed, 41 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72bebd8c1..2de5058ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1684: Added support for extending skips with `--extend-skip` and `--extend-skip-glob`. - Implemented #1688: Auto identification and skipping of some invalid import statements. - Implemented #1645: Ability to reverse the import sorting order. + - Implemented #1504: Ability to push star imports to the top to avoid overriding explicitly defined imports. - Documented #1685: Skip doesn't support plain directory names, but skip_glob does. ### 5.7.0 December 30th 2020 diff --git a/isort/main.py b/isort/main.py index d16626f5f..b6954d249 100644 --- a/isort/main.py +++ b/isort/main.py @@ -666,6 +666,12 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="ext_format", help="Tells isort to format the given files according to an extensions formatting rules.", ) + output_group.add_argument( + "--star-first", + help="Forces star imports above others to avoid overriding directly imported variables.", + dest="star_first", + action="store_true", + ) section_group.add_argument( "--sd", diff --git a/isort/output.py b/isort/output.py index ee7406ca8..01abbe27d 100644 --- a/isort/output.py +++ b/isort/output.py @@ -65,6 +65,16 @@ def sorted_imports( reverse=config.reverse_sort, ) + if config.star_first: + star_modules = [] + other_modules = [] + for module in from_modules: + if "*" in parsed.imports[section]["from"][module]: + star_modules.append(module) + else: + other_modules.append(module) + from_modules = star_modules + other_modules + straight_imports = _with_straight_imports( parsed, config, straight_modules, section, remove_imports, import_type ) diff --git a/isort/settings.py b/isort/settings.py index dc3754ef8..c2cd9624a 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -211,6 +211,7 @@ class _Config: sort_relative_in_force_sorted_sections: bool = False overwrite_in_place: bool = False reverse_sort: bool = False + star_first: bool = False def __post_init__(self): py_version = self.py_version diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 6bed59a66..a0e5e1640 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -1059,3 +1059,26 @@ def test_isort_allows_reversing_sort_order_issue_1645(): ) """ ) + + +def test_isort_can_push_star_imports_above_others_issue_1504(): + """isort should provide a way to push star imports above other imports to avoid explicit + imports from being overwritten. + see: https://github.com/PyCQA/isort/issues/1504 + """ + assert ( + ( + isort.code( + """ +from ._bar import Any, All, Not +from ._foo import a, * +""", + star_first=True, + ) + ) + == """ +from ._foo import * +from ._foo import a +from ._bar import All, Any, Not +""" + ) From 5a0ebba5174d67210b6b23f8f644f1b0bc03de73 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 18 Mar 2021 23:23:02 -0700 Subject: [PATCH 401/539] Fixed #1594: incorrect placement of noqa comments with multiple from imports. --- CHANGELOG.md | 3 ++- isort/output.py | 12 ++++++++---- tests/unit/test_regressions.py | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2de5058ad..4d0a99b2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.8.0 TBD - Fixed #1631: as import comments can in some cases be duplicated. - Fixed #1667: extra newline added with float-to-top, after skip, in some cases. + - Fixed #1594: incorrect placement of noqa comments with multiple from imports. - Implemented #1648: Export MyPY type hints. - Implemented #1641: Identified import statements now return runnable code. - Implemented #1661: Added "wemake" profile. @@ -16,7 +17,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1684: Added support for extending skips with `--extend-skip` and `--extend-skip-glob`. - Implemented #1688: Auto identification and skipping of some invalid import statements. - Implemented #1645: Ability to reverse the import sorting order. - - Implemented #1504: Ability to push star imports to the top to avoid overriding explicitly defined imports. + - Implemented #1504: Added ability to push star imports to the top to avoid overriding explicitly defined imports. - Documented #1685: Skip doesn't support plain directory names, but skip_glob does. ### 5.7.0 December 30th 2020 diff --git a/isort/output.py b/isort/output.py index 01abbe27d..13f72a3d5 100644 --- a/isort/output.py +++ b/isort/output.py @@ -429,18 +429,22 @@ def _with_from_imports( parsed.categorized_comments["nested"].get(module, {}).pop(from_import, None) ) if comment: + from_imports.remove(from_import) + if from_imports: + use_comments = [] + else: + use_comments = comments + comments = None single_import_line = with_comments( - comments, + use_comments, import_start + from_import, removed=config.ignore_comments, comment_prefix=config.comment_prefix, ) single_import_line += ( - f"{comments and ';' or config.comment_prefix} " f"{comment}" + f"{use_comments and ';' or config.comment_prefix} " f"{comment}" ) output.append(wrap.line(single_import_line, parsed.line_separator, config)) - from_imports.remove(from_import) - comments = None from_import_section = [] while from_imports and ( diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 7d311f3c2..9f1755254 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1585,3 +1585,25 @@ def test_isort_shouldnt_add_extra_line_float_to_top_issue_1667(): show_diff=True, float_to_top=True, ) + + +def test_isort_shouldnt_move_noqa_comment_issue_1594(): + assert ( + isort.code( + """ +from .test import TestTestTestTestTestTest1 # noqa: F401 +from .test import TestTestTestTestTestTest2, TestTestTestTestTestTest3, """ + """TestTestTestTestTestTest4, TestTestTestTestTestTest5 # noqa: F401 +""", + profile="black", + ) + == """ +from .test import TestTestTestTestTestTest1 # noqa: F401 +from .test import ( # noqa: F401 + TestTestTestTestTestTest2, + TestTestTestTestTestTest3, + TestTestTestTestTestTest4, + TestTestTestTestTestTest5, +) +""" + ) From a1f2782e4d825a54b2eec698b3520e30526a343f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 19 Mar 2021 23:33:56 -0700 Subject: [PATCH 402/539] Create failing test for issue #1566 --- tests/unit/test_regressions.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 9f1755254..4cd8a3fac 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1607,3 +1607,15 @@ def test_isort_shouldnt_move_noqa_comment_issue_1594(): ) """ ) + + +def test_isort_correctly_handles_unix_vs_linux_newlines_issue_1566(): + IMPORT_STATEMENT = ( + "from impacket.smb3structs import (\n" + "SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2_IL_IMPERSONATION, " + "SMB2_OPLOCK_LEVEL_NONE, SMB2Create," + "\nSMB2Create_Response, SMB2Packet)\n" + ) + assert isort.code(IMPORT_STATEMENT, line_length=120) == isort.code( + IMPORT_STATEMENT.replace("\n", "\r\n"), line_length=120 + ) From 844c85c36108937ec1962a94d54dba634d568d00 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 19 Mar 2021 23:34:38 -0700 Subject: [PATCH 403/539] Add changelog entry for issue #1566 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d0a99b2c..4a805715c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1631: as import comments can in some cases be duplicated. - Fixed #1667: extra newline added with float-to-top, after skip, in some cases. - Fixed #1594: incorrect placement of noqa comments with multiple from imports. + - Fixed #1566: in some cases different length limits for dos based line endings. - Implemented #1648: Export MyPY type hints. - Implemented #1641: Identified import statements now return runnable code. - Implemented #1661: Added "wemake" profile. From 0ee873365c722d5eea95e0e838b771f4b06a61e1 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 20 Mar 2021 00:06:02 -0700 Subject: [PATCH 404/539] Fix test to ensure output is the same --- tests/unit/test_regressions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 4cd8a3fac..26517598c 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1610,12 +1610,12 @@ def test_isort_shouldnt_move_noqa_comment_issue_1594(): def test_isort_correctly_handles_unix_vs_linux_newlines_issue_1566(): - IMPORT_STATEMENT = ( + import_statement = ( "from impacket.smb3structs import (\n" "SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2_IL_IMPERSONATION, " "SMB2_OPLOCK_LEVEL_NONE, SMB2Create," "\nSMB2Create_Response, SMB2Packet)\n" ) - assert isort.code(IMPORT_STATEMENT, line_length=120) == isort.code( - IMPORT_STATEMENT.replace("\n", "\r\n"), line_length=120 - ) + assert isort.code(import_statement, line_length=120) == isort.code( + import_statement.replace("\n", "\r\n"), line_length=120 + ).replace("\r\n", "\n") From 404d809a692d68db0f36016b91171befec48be81 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 20 Mar 2021 00:06:38 -0700 Subject: [PATCH 405/539] Fix issue #1566: Fixed single location parsed line separator isn't used --- isort/output.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/isort/output.py b/isort/output.py index 13f72a3d5..61c2e30ec 100644 --- a/isort/output.py +++ b/isort/output.py @@ -504,7 +504,13 @@ def _with_from_imports( config=config, multi_line_output=wrap.Modes.VERTICAL_GRID, # type: ignore ) - if max(len(x) for x in import_statement.split("\n")) > config.line_length: + if ( + max( + len(import_line) + for import_line in import_statement.split(parsed.line_separator) + ) + > config.line_length + ): import_statement = other_import_statement if not do_multiline_reformat and len(import_statement) > config.line_length: import_statement = wrap.line(import_statement, parsed.line_separator, config) From a6222a8a125ec719724e628a5d3d0d5c60923281 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 20 Mar 2021 22:25:02 -0700 Subject: [PATCH 406/539] Bump version to 5.8.0 --- CHANGELOG.md | 2 +- isort/_version.py | 2 +- isort/settings.py | 1 + pyproject.toml | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a805715c..aaeae67a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). -### 5.8.0 TBD +### 5.8.0 March 20th 2021 - Fixed #1631: as import comments can in some cases be duplicated. - Fixed #1667: extra newline added with float-to-top, after skip, in some cases. - Fixed #1594: incorrect placement of noqa comments with multiple from imports. diff --git a/isort/_version.py b/isort/_version.py index 7f4ce2d4c..e0b752771 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.7.0" +__version__ = "5.8.0" diff --git a/isort/settings.py b/isort/settings.py index c2cd9624a..d1fba4c20 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -212,6 +212,7 @@ class _Config: overwrite_in_place: bool = False reverse_sort: bool = False star_first: bool = False + import_dependencies = Dict[str, str] def __post_init__(self): py_version = self.py_version diff --git a/pyproject.toml b/pyproject.toml index 086dadfd3..b4813b780 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.7.0" +version = "5.8.0" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" From faa829351c9589d1a1607e5b8dd0f1a139c68036 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 20 Mar 2021 23:08:02 -0700 Subject: [PATCH 407/539] Add - Christian Clauss (@cclauss) to acknowledgements --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 09e000a7c..3011a88fa 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -217,6 +217,7 @@ Code Contributors - @gofr - Pavel Savchenko (@asfaltboy) - @dongfangtianyu +- Christian Clauss (@cclauss) Documenters =================== From 9665722d4c4acf6fa4c95d4e7966793d03f3821d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 20 Mar 2021 23:08:36 -0700 Subject: [PATCH 408/539] Update documentation --- docs/configuration/options.md | 49 ++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index 5ecb6c5ea..ed68cf2c1 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -33,7 +33,7 @@ Force specific imports to the top of their appropriate section. ## Skip -Files that sort imports should skip over. If you want to skip multiple files you should specify twice: --skip file1 --skip file2. Values can be file names, directory names or file paths. To skip all files in a nested path use --skip-glob. +Files that isort should skip over. If you want to skip multiple files you should specify twice: --skip file1 --skip file2. Values can be file names, directory names or file paths. To skip all files in a nested path use --skip-glob. **Type:** Frozenset **Default:** `('.bzr', '.direnv', '.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.svn', '.tox', '.venv', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv')` @@ -43,17 +43,36 @@ Files that sort imports should skip over. If you want to skip multiple files you - -s - --skip +## Extend Skip + +Extends --skip to add additional files that isort should skip over. If you want to skip multiple files you should specify twice: --skip file1 --skip file2. Values can be file names, directory names or file paths. To skip all files in a nested path use --skip-glob. + +**Type:** Frozenset +**Default:** `frozenset()` +**Python & Config File Name:** extend_skip +**CLI Flags:** + +- --extend-skip + ## Skip Glob -Files that sort imports should skip over. +Additional files that isort should skip over (extending --skip-glob). **Type:** Frozenset **Default:** `frozenset()` **Python & Config File Name:** skip_glob **CLI Flags:** -- --sg -- --skip-glob +- --extend-skip-glob + +## Extend Skip Glob + +**No Description** + +**Type:** Frozenset +**Default:** `frozenset()` +**Python & Config File Name:** extend_skip_glob +**CLI Flags:** **Not Supported** ## Skip Gitignore @@ -1054,6 +1073,28 @@ Tells isort to overwrite in place using the same file handle.Comes at a performa - --overwrite-in-place +## Reverse Sort + +Reverses the ordering of imports. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** reverse_sort +**CLI Flags:** + +- --reverse-sort + +## Star First + +Forces star imports above others to avoid overriding directly imported variables. + +**Type:** Bool +**Default:** `False` +**Python & Config File Name:** star_first +**CLI Flags:** + +- --star-first + ## Show Version Displays the currently installed version of isort. From 2ccb49776957dbcb8b05ac4cc5eb9ef20a54a25d Mon Sep 17 00:00:00 2001 From: Jon Banafato Date: Mon, 22 Mar 2021 13:24:15 -0400 Subject: [PATCH 409/539] Fix bug regarding multiline docstrings If a multiline docstring has its closing quotes at the end of a line instead of on a separate line, isort fails to properly add imports using the `add_imports` config option; instead, it adds the desired imports into the middle of the docstring as illustrated below. While PEP 257 (and other guides) advises that closing quotes appear on their own line, `isort` should not fail here. This change adds a check for closing docstrings at the end of a line in addition to the existing line start check for all comment indicators. A new section of the `test_add_imports` test explicitly tests multiline imports and this failure scenario specifically. --- A working example: ```python """My module. Provides example functionality. """ print("hello, world") ``` Running `isort --add-import "from __future__ import annotations"` produces the following as expected: ```python """My module. Provides example functionality. """ from __future__ import annotations print("hello, world") ``` --- The failure behavior described: ```python """My module. Provides example functionality.""" print("hello, world") ``` Running `isort --add-import "from __future__ import annotations"` as above produces the following result: ```python """My module. from __future__ import annotations Provides example functionality.""" print("hello, world") ``` Subsequent executions add more import lines into the docstring. This behavior occurs even if the file already has the desired imports. --- isort/core.py | 4 +++- tests/unit/test_isort.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/isort/core.py b/isort/core.py index d763ad469..9f577dac6 100644 --- a/isort/core.py +++ b/isort/core.py @@ -13,7 +13,8 @@ CIMPORT_IDENTIFIERS = ("cimport ", "cimport*", "from.cimport") IMPORT_START_IDENTIFIERS = ("from ", "from.import", "import ", "import*") + CIMPORT_IDENTIFIERS -COMMENT_INDICATORS = ('"""', "'''", "'", '"', "#") +DOCSTRING_INDICATORS = ('"""', "'''") +COMMENT_INDICATORS = DOCSTRING_INDICATORS + ("'", '"', "#") CODE_SORT_COMMENTS = ( "# isort: list", "# isort: dict", @@ -317,6 +318,7 @@ def process( and not in_quote and not import_section and not line.lstrip().startswith(COMMENT_INDICATORS) + and not line.rstrip().endswith(DOCSTRING_INDICATORS) ): import_section = line_separator.join(add_imports) + line_separator if end_of_file and index != 0: diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 19452db6d..7a02884ba 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -873,6 +873,41 @@ def test_add_imports() -> None: " pass\n" ) + # On a file that has no pre-existing imports and a multiline docstring + test_input = ( + '"""Module docstring\n\nWith a second line\n"""\n' "class MyClass(object):\n pass\n" + ) + test_output = isort.code(code=test_input, add_imports=["from __future__ import print_function"]) + assert test_output == ( + '"""Module docstring\n' + "\n" + "With a second line\n" + '"""\n' + "from __future__ import print_function\n" + "\n" + "\n" + "class MyClass(object):\n" + " pass\n" + ) + + # On a file that has no pre-existing imports and a multiline docstring. + # In this example, the closing quotes for the docstring are on the final + # line rather than a separate one. + test_input = ( + '"""Module docstring\n\nWith a second line"""\n' "class MyClass(object):\n pass\n" + ) + test_output = isort.code(code=test_input, add_imports=["from __future__ import print_function"]) + assert test_output == ( + '"""Module docstring\n' + "\n" + 'With a second line"""\n' + "from __future__ import print_function\n" + "\n" + "\n" + "class MyClass(object):\n" + " pass\n" + ) + # On a file that has no pre-existing imports, and no doc-string test_input = "class MyClass(object):\n pass\n" test_output = isort.code(code=test_input, add_imports=["from __future__ import print_function"]) From 5d3aa873fef384a03a0b125de92d4362be305960 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 22 Mar 2021 23:32:07 -0700 Subject: [PATCH 410/539] Add changelog entry for fix from @jonafato --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaeae67a0..49dddb2d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). +### TBD + - Fixed (https://github.com/PyCQA/isort/pull/1695) added imports being added to doc string in some cases. + ### 5.8.0 March 20th 2021 - Fixed #1631: as import comments can in some cases be duplicated. - Fixed #1667: extra newline added with float-to-top, after skip, in some cases. From a07bdf3cf7484dfb7570b4eec076fc0bc483b79a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 22 Mar 2021 23:33:04 -0700 Subject: [PATCH 411/539] Add - Jon Banafato (@jonafato) to contributors list --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 3011a88fa..90b4bc59b 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -218,6 +218,7 @@ Code Contributors - Pavel Savchenko (@asfaltboy) - @dongfangtianyu - Christian Clauss (@cclauss) +- Jon Banafato (@jonafato) Documenters =================== From dbd5a9e2708a4ba0063228f9570b59e13d6394b0 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 23 Mar 2021 20:53:24 -0700 Subject: [PATCH 412/539] Implemented #1697: Provisional support for PEP 582: skip directories by default. --- CHANGELOG.md | 3 ++- isort/settings.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49dddb2d1..155c3d88c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,9 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). -### TBD +### 5.9.0 TBD - Fixed (https://github.com/PyCQA/isort/pull/1695) added imports being added to doc string in some cases. + - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. ### 5.8.0 March 20th 2021 - Fixed #1631: as import comments can in some cases be duplicated. diff --git a/isort/settings.py b/isort/settings.py index d1fba4c20..bd8e631db 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -68,6 +68,7 @@ ".pants.d", ".direnv", "node_modules", + "__pypackages__", } ) From bf715a4ee3fa5935da3f1db2e592844aa5674e16 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 24 Mar 2021 20:59:26 -0700 Subject: [PATCH 413/539] master -> main --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b4813b780..4927f3fe4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ classifiers = [ "Topic :: Software Development :: Libraries", "Topic :: Utilities", ] -urls = { Changelog = "https://github.com/pycqa/isort/blob/master/CHANGELOG.md" } +urls = { Changelog = "https://github.com/pycqa/isort/blob/main/CHANGELOG.md" } packages = [ { include = "isort" }, ] From f01a8e02dee3549d8942ebd117a20eb6d219d7e8 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 24 Mar 2021 21:01:47 -0700 Subject: [PATCH 414/539] develop -> main --- README.md | 6 +++--- docs/configuration/black_compatibility.md | 2 +- docs/major_releases/introducing_isort_5.md | 2 +- pyproject.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fa6e26ea3..bdd86c020 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -[![isort - isort your imports, so you don't have to.](https://raw.githubusercontent.com/pycqa/isort/develop/art/logo_large.png)](https://pycqa.github.io/isort/) +[![isort - isort your imports, so you don't have to.](https://raw.githubusercontent.com/pycqa/isort/main/art/logo_large.png)](https://pycqa.github.io/isort/) ------------------------------------------------------------------------ [![PyPI version](https://badge.fury.io/py/isort.svg)](https://badge.fury.io/py/isort) [![Test Status](https://github.com/pycqa/isort/workflows/Test/badge.svg?branch=develop)](https://github.com/pycqa/isort/actions?query=workflow%3ATest) [![Lint Status](https://github.com/pycqa/isort/workflows/Lint/badge.svg?branch=develop)](https://github.com/pycqa/isort/actions?query=workflow%3ALint) -[![Code coverage Status](https://codecov.io/gh/pycqa/isort/branch/develop/graph/badge.svg)](https://codecov.io/gh/pycqa/isort) +[![Code coverage Status](https://codecov.io/gh/pycqa/isort/branch/main/graph/badge.svg)](https://codecov.io/gh/pycqa/isort) [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://pypi.org/project/isort/) [![Join the chat at https://gitter.im/timothycrosley/isort](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/timothycrosley/isort?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Downloads](https://pepy.tech/badge/isort)](https://pepy.tech/project/isort) @@ -30,7 +30,7 @@ supports formatting Python 2 code too. - [Using black? See the isort and black compatiblity guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility/) - [isort has official support for pre-commit!](https://pycqa.github.io/isort/docs/configuration/pre-commit/) -![Example Usage](https://raw.github.com/pycqa/isort/develop/example.gif) +![Example Usage](https://raw.github.com/pycqa/isort/main/example.gif) Before isort: diff --git a/docs/configuration/black_compatibility.md b/docs/configuration/black_compatibility.md index a57754252..6e1058ef8 100644 --- a/docs/configuration/black_compatibility.md +++ b/docs/configuration/black_compatibility.md @@ -1,4 +1,4 @@ -![isort loves black](https://raw.githubusercontent.com/pycqa/isort/develop/art/isort_loves_black.png) +![isort loves black](https://raw.githubusercontent.com/pycqa/isort/main/art/isort_loves_black.png) Compatibility with black ======== diff --git a/docs/major_releases/introducing_isort_5.md b/docs/major_releases/introducing_isort_5.md index a5cbdda93..4f6463ea1 100644 --- a/docs/major_releases/introducing_isort_5.md +++ b/docs/major_releases/introducing_isort_5.md @@ -1,6 +1,6 @@ # Introducing isort 5 -[![isort 5 - the best version of isort yet](https://raw.githubusercontent.com/pycqa/isort/develop/art/logo_5.png)](https://pycqa.github.io/isort/) +[![isort 5 - the best version of isort yet](https://raw.githubusercontent.com/pycqa/isort/main/art/logo_5.png)](https://pycqa.github.io/isort/) isort 5.0.0 is the first major release of isort in over five years and the first significant refactoring of isort since it was conceived more than ten years ago. It's also the first version to require Python 3 (Python 3.6+ at that!) to run - though it can still be run on source files from any version of Python. diff --git a/pyproject.toml b/pyproject.toml index 4927f3fe4..c4c600126 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -93,7 +93,7 @@ isort = "isort.main:ISortCommand" isort = "isort.pylama_isort:Linter" [tool.portray.mkdocs] -edit_uri = "https://github.com/pycqa/isort/edit/develop/" +edit_uri = "https://github.com/pycqa/isort/edit/main/" extra_css = ["art/stylesheets/extra.css"] [tool.portray.mkdocs.theme] From ead8c7c8ab73991796f2561605cc65d213b1c72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ni=20Gauffier?= Date: Wed, 31 Mar 2021 00:08:25 +0000 Subject: [PATCH 415/539] Fix --gitignore flag not working on relative subdir files --- isort/files.py | 2 +- tests/unit/test_main.py | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/isort/files.py b/isort/files.py index 692c2011c..313d16b70 100644 --- a/isort/files.py +++ b/isort/files.py @@ -34,7 +34,7 @@ def find( for filename in filenames: filepath = os.path.join(dirpath, filename) if config.is_supported_filetype(filepath): - if config.is_skipped(Path(filepath)): + if config.is_skipped(Path(os.path.abspath(filepath))): skipped.append(filename) else: yield filepath diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 86f62db3b..581b3a568 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -1,4 +1,5 @@ import json +import os import subprocess from datetime import datetime from io import BytesIO, TextIOWrapper @@ -352,7 +353,26 @@ def test_main(capsys, tmpdir): assert "Skipped" not in out main.main([str(python_file), "--skip-gitignore", "--filter-files"]) out, error = capsys.readouterr() - assert "Skipped" in out + assert "Skipped" in out and "has_imports.py" not in out + + tmpdir.join(".gitignore").remove() + + currentdir = os.getcwd() + os.chdir(tmpdir) + + tmpdir.join(".gitignore").write("nested_dir/has_imports.py") + subpython_file = tmpdir.join("nested_dir/has_imports.py") + subpython_file.write( + """ +import b +import a +""" + ) + main.main([".", "--skip-gitignore", "--filter-files"]) + out, error = capsys.readouterr() + assert "nested_dir/has_imports.py" not in out + + os.chdir(currentdir) # warnings should be displayed if old flags are used with pytest.warns(UserWarning): From 3896dae9df9fb084634763f62485d7cd5808c2b2 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 1 Apr 2021 20:43:18 -0700 Subject: [PATCH 416/539] Add optional setuptools installation --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index c4c600126..6cd4f6af2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,11 +44,13 @@ pipreqs = {version = "*", optional = true} requirementslib = {version = "*", optional = true} pip-api = {version = "*", optional = true} colorama = {version = "^0.4.3", optional = true} +setuptools = {version = "*", optional = true} [tool.poetry.extras] pipfile_deprecated_finder = ["pipreqs", "requirementslib", "pip-shims<=0.3.4"] requirements_deprecated_finder = ["pipreqs", "pip-api"] colors = ["colorama"] +plugins = ["setuptools"] [tool.poetry.dev-dependencies] vulture = "^1.0" From 8a9dd4c6717d2fe0fb74f47bfca29e9a8dfd9c06 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 1 Apr 2021 21:08:28 -0700 Subject: [PATCH 417/539] Bump dependencies --- poetry.lock | 224 ++++++++++++++++++++++++++-------------------------- 1 file changed, 113 insertions(+), 111 deletions(-) diff --git a/poetry.lock b/poetry.lock index 03bcbfd59..cf5a17155 100644 --- a/poetry.lock +++ b/poetry.lock @@ -16,7 +16,7 @@ python-versions = "*" [[package]] name = "arrow" -version = "1.0.2" +version = "1.0.3" description = "Better dates & times for Python" category = "dev" optional = false @@ -196,7 +196,7 @@ toml = ["toml"] [[package]] name = "cruft" -version = "2.7.0" +version = "2.8.0" description = "Allows you to maintain all the necessary cruft for packaging and building projects separate from the code you intentionally write. Built on-top of CookieCutter." category = "dev" optional = false @@ -309,17 +309,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "flake8" -version = "3.8.4" +version = "3.9.0" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.6.0a1,<2.7.0" -pyflakes = ">=2.2.0,<2.3.0" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" [[package]] name = "flake8-bugbear" @@ -354,14 +354,14 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "gitdb" -version = "4.0.5" +version = "4.0.7" description = "Git Object Database" category = "dev" optional = false python-versions = ">=3.4" [package.dependencies] -smmap = ">=3.0.1,<4" +smmap = ">=3.0.1,<5" [[package]] name = "gitdb2" @@ -473,7 +473,7 @@ python-versions = "*" [[package]] name = "hypothesis" -version = "6.3.4" +version = "6.8.4" description = "A library for property-based testing" category = "dev" optional = false @@ -548,7 +548,7 @@ test = ["flake8 (>=3.8.4,<3.9.0)", "pycodestyle (>=2.6.0,<2.7.0)"] [[package]] name = "importlib-metadata" -version = "3.7.0" +version = "3.10.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -560,7 +560,7 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -672,7 +672,7 @@ regex = ["regex"] [[package]] name = "libcst" -version = "0.3.17" +version = "0.3.18" description = "A concrete syntax tree with AST-like properties for Python 3.5, 3.6, 3.7 and 3.8 programs." category = "dev" optional = false @@ -779,7 +779,7 @@ tornado = ">=5.0" [[package]] name = "mkdocs-material" -version = "5.5.14" +version = "7.1.0" description = "A Material Design theme for MkDocs" category = "dev" optional = false @@ -881,7 +881,7 @@ pyparsing = ">=2.0.2" [[package]] name = "parso" -version = "0.8.1" +version = "0.8.2" description = "A Python Parser" category = "dev" optional = false @@ -924,7 +924,7 @@ Markdown = ">=3.0.0,<4.0.0" [[package]] name = "pep517" -version = "0.9.1" +version = "0.10.0" description = "Wrappers to build Python packages using PEP 517 hooks" category = "main" optional = false @@ -1045,7 +1045,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "portray" -version = "1.5.2" +version = "1.6.0" description = "Your Project with Great Documentation" category = "dev" optional = false @@ -1056,7 +1056,7 @@ GitPython = ">=3.0,<4.0" hug = ">=2.6,<3.0" livereload = ">=2.6.3,<3.0.0" mkdocs = ">=1.0,<2.0" -mkdocs-material = ">=5.0,<6.0" +mkdocs-material = ">=7.0,<8.0" pdocs = ">=1.1.1,<2.0.0" pymdown-extensions = ">=7.0,<8.0" toml = ">=0.10.0,<0.11.0" @@ -1099,7 +1099,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pycodestyle" -version = "2.6.0" +version = "2.7.0" description = "Python style guide checker" category = "dev" optional = false @@ -1123,18 +1123,18 @@ typing_extensions = ["typing-extensions (>=3.7.2)"] [[package]] name = "pydocstyle" -version = "5.1.1" +version = "6.0.0" description = "Python docstring style checker" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] snowballstemmer = "*" [[package]] name = "pyflakes" -version = "2.2.0" +version = "2.3.1" description = "passive checker of Python programs" category = "dev" optional = false @@ -1142,7 +1142,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.8.0" +version = "2.8.1" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -1267,7 +1267,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "regex" -version = "2020.11.13" +version = "2021.3.17" description = "Alternative regular expression module, to replace re." category = "dev" optional = false @@ -1355,11 +1355,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "smmap" -version = "3.0.5" +version = "4.0.0" description = "A pure Python implementation of a sliding window memory map manager" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.5" [[package]] name = "smmap2" @@ -1445,7 +1445,7 @@ python-versions = ">= 3.5" [[package]] name = "tqdm" -version = "4.58.0" +version = "4.59.0" description = "Fast, Extensible Progress Meter" category = "dev" optional = false @@ -1453,6 +1453,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" [package.extras] dev = ["py-make (>=0.1.0)", "twine", "wheel"] +notebook = ["ipywidgets (>=6)"] telegram = ["requests"] [[package]] @@ -1518,16 +1519,16 @@ typing-extensions = ">=3.7.4" [[package]] name = "urllib3" -version = "1.26.3" +version = "1.26.4" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] -brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotlipy (>=0.6.0)"] [[package]] name = "vistir" @@ -1585,25 +1586,26 @@ python-versions = "*" [[package]] name = "zipp" -version = "3.4.0" +version = "3.4.1" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.6" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [extras] colors = ["colorama"] pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +plugins = ["setuptools"] requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "8d92b325ce222b42dfd7e2a8e6e5abd4f213cba6903e6b36edd322b8d9878fdf" +content-hash = "b9cfee23dcfb20d64a10ae181e449fa542a0d7a59f49305bf106e93f7f097948" [metadata.files] appdirs = [ @@ -1615,8 +1617,8 @@ appnope = [ {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, ] arrow = [ - {file = "arrow-1.0.2-py3-none-any.whl", hash = "sha256:cb1b7bc3a07eb1c1e98ccc740627460c9891636642bcf03c4097b71d8bc5ca1d"}, - {file = "arrow-1.0.2.tar.gz", hash = "sha256:5df8e632e9158c48f42f68a742068bcfc1c0181cbe7543e4cda6089bb287a305"}, + {file = "arrow-1.0.3-py3-none-any.whl", hash = "sha256:3515630f11a15c61dcb4cdd245883270dd334c83f3e639824e65a4b79cc48543"}, + {file = "arrow-1.0.3.tar.gz", hash = "sha256:399c9c8ae732270e1aa58ead835a79a40d7be8aa109c579898eb41029b5a231d"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, @@ -1723,8 +1725,8 @@ coverage = [ {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, ] cruft = [ - {file = "cruft-2.7.0-py3-none-any.whl", hash = "sha256:f4b8d93276a07fedd4b6bc7880c9f7ec72ce35fca61effe049bd1e951c601776"}, - {file = "cruft-2.7.0.tar.gz", hash = "sha256:a47b84387f3133181da9f82f4711246147363eff78f13bd2b031400a33ee24d1"}, + {file = "cruft-2.8.0-py3-none-any.whl", hash = "sha256:986bfc6b907133284882f48708a8ea9c7a90591b45f02eddd01beeb973fa0c83"}, + {file = "cruft-2.8.0.tar.gz", hash = "sha256:29e6c95ed46bda8d26b9666e78bdcd625f3a2352620fc52c44dc4ff64a298ce0"}, ] dataclasses = [ {file = "dataclasses-0.7-py3-none-any.whl", hash = "sha256:3459118f7ede7c8bea0fe795bff7c6c2ce287d01dd226202f7c9ebc0610a7836"}, @@ -1777,8 +1779,8 @@ falcon = [ {file = "falcon-2.0.0.tar.gz", hash = "sha256:eea593cf466b9c126ce667f6d30503624ef24459f118c75594a69353b6c3d5fc"}, ] flake8 = [ - {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, - {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, + {file = "flake8-3.9.0-py2.py3-none-any.whl", hash = "sha256:12d05ab02614b6aee8df7c36b97d1a3b2372761222b19b58621355e82acddcff"}, + {file = "flake8-3.9.0.tar.gz", hash = "sha256:78873e372b12b093da7b5e5ed302e8ad9e988b38b063b61ad937f26ca58fc5f0"}, ] flake8-bugbear = [ {file = "flake8-bugbear-19.8.0.tar.gz", hash = "sha256:d8c466ea79d5020cb20bf9f11cf349026e09517a42264f313d3f6fddb83e0571"}, @@ -1792,8 +1794,8 @@ future = [ {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, ] gitdb = [ - {file = "gitdb-4.0.5-py3-none-any.whl", hash = "sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac"}, - {file = "gitdb-4.0.5.tar.gz", hash = "sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9"}, + {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"}, + {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"}, ] gitdb2 = [ {file = "gitdb2-4.0.2-py3-none-any.whl", hash = "sha256:a1c974e5fab8c2c90192c1367c81cbc54baec04244bda1816e9c8ab377d1cba3"}, @@ -1836,8 +1838,8 @@ hyperframe = [ {file = "hyperframe-5.2.0.tar.gz", hash = "sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"}, ] hypothesis = [ - {file = "hypothesis-6.3.4-py3-none-any.whl", hash = "sha256:69f7d32270f52a14f853cb3673eae65fc2f057601f6e7b5502e5c905985d8c69"}, - {file = "hypothesis-6.3.4.tar.gz", hash = "sha256:26771ce7f21c2ed08e93f62bdd63755dab051c163cc9d54544269b5b8b550eac"}, + {file = "hypothesis-6.8.4-py3-none-any.whl", hash = "sha256:2caa49d4daf77538ec79731b8b3baaa9cfaf83abd9c7703f05b0d046814654f1"}, + {file = "hypothesis-6.8.4.tar.gz", hash = "sha256:665486c0c823ae217be96e611882249f6b060799ded7b2393d451fa488b62fcb"}, ] hypothesis-auto = [ {file = "hypothesis-auto-1.1.4.tar.gz", hash = "sha256:5e2c2fb09dc09842512d80630bb792359a1d33d2c0473ad47ee23da0be9e32b1"}, @@ -1869,8 +1871,8 @@ immutables = [ {file = "immutables-0.15.tar.gz", hash = "sha256:3713ab1ebbb6946b7ce1387bb9d1d7f5e09c45add58c2a2ee65f963c171e746b"}, ] importlib-metadata = [ - {file = "importlib_metadata-3.7.0-py3-none-any.whl", hash = "sha256:c6af5dbf1126cd959c4a8d8efd61d4d3c83bddb0459a17e554284a077574b614"}, - {file = "importlib_metadata-3.7.0.tar.gz", hash = "sha256:24499ffde1b80be08284100393955842be4a59c7c16bbf2738aad0e464a8e0aa"}, + {file = "importlib_metadata-3.10.0-py3-none-any.whl", hash = "sha256:d2d46ef77ffc85cbf7dac7e81dd663fde71c45326131bea8033b9bad42268ebe"}, + {file = "importlib_metadata-3.10.0.tar.gz", hash = "sha256:c9db46394197244adf2f0b08ec5bc3cf16757e9590b02af1fca085c16c0d600a"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -1904,8 +1906,8 @@ lark-parser = [ {file = "lark-parser-0.11.2.tar.gz", hash = "sha256:ef610461ebf2b243502f337d9d49879e39f9add846a4749e88c8dcdc1378bb6b"}, ] libcst = [ - {file = "libcst-0.3.17-py3-none-any.whl", hash = "sha256:4638e4e8f166f4c74df399222d347ce3e1d316e206b550d8a6254d51b4cf7275"}, - {file = "libcst-0.3.17.tar.gz", hash = "sha256:2766671c107263daa3fc34e39d55134a6fe253701564d7670586f30eee2c201c"}, + {file = "libcst-0.3.18-py3-none-any.whl", hash = "sha256:da89cc1a37702caa6fe7207b1257fad58f0d4643597279733106ca902b4fdbad"}, + {file = "libcst-0.3.18.tar.gz", hash = "sha256:30154cd0aaede8f3adfc4bdead23fe022a57e88898b9993cc3fea3bfbaf780d2"}, ] livereload = [ {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, @@ -1984,8 +1986,8 @@ mkdocs = [ {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, ] mkdocs-material = [ - {file = "mkdocs-material-5.5.14.tar.gz", hash = "sha256:9f3237df1a72f91e0330a5e3b3711cb7aaa0d5705f9585e6ce6fbacaa16e777f"}, - {file = "mkdocs_material-5.5.14-py2.py3-none-any.whl", hash = "sha256:a0b3b3e67606e04d13e777d13f3195402ea09e0c3ce279abc3666cac2c5b3a6d"}, + {file = "mkdocs-material-7.1.0.tar.gz", hash = "sha256:1afaa5b174265eaa4a886f73187bb0e302a9596e9bfedb5aa2cb260d8b1d994e"}, + {file = "mkdocs_material-7.1.0-py2.py3-none-any.whl", hash = "sha256:13e73b3571d36f7e4a7dc11093323cff92095f4f219a00ba19c77a5e53aa6c55"}, ] mkdocs-material-extensions = [ {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, @@ -2059,8 +2061,8 @@ packaging = [ {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] parso = [ - {file = "parso-0.8.1-py2.py3-none-any.whl", hash = "sha256:15b00182f472319383252c18d5913b69269590616c947747bc50bf4ac768f410"}, - {file = "parso-0.8.1.tar.gz", hash = "sha256:8519430ad07087d4c997fda3a7918f7cfa27cb58972a8c89c2a0295a1c940e9e"}, + {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"}, + {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"}, ] pathspec = [ {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, @@ -2075,8 +2077,8 @@ pdocs = [ {file = "pdocs-1.1.1.tar.gz", hash = "sha256:f148034970220c9e05d2e04d8eb3fcec3575cf480af0966123ef9d6621b46e4f"}, ] pep517 = [ - {file = "pep517-0.9.1-py2.py3-none-any.whl", hash = "sha256:3985b91ebf576883efe5fa501f42a16de2607684f3797ddba7202b71b7d0da51"}, - {file = "pep517-0.9.1.tar.gz", hash = "sha256:aeb78601f2d1aa461960b43add204cc7955667687fbcf9cdb5170f00556f117f"}, + {file = "pep517-0.10.0-py2.py3-none-any.whl", hash = "sha256:eba39d201ef937584ad3343df3581069085bacc95454c80188291d5b3ac7a249"}, + {file = "pep517-0.10.0.tar.gz", hash = "sha256:ac59f3f6b9726a49e15a649474539442cf76e0697e39df4869d25e68e880931b"}, ] pep8-naming = [ {file = "pep8-naming-0.8.2.tar.gz", hash = "sha256:01cb1dab2f3ce9045133d08449f1b6b93531dceacb9ef04f67087c11c723cea9"}, @@ -2114,8 +2116,8 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] portray = [ - {file = "portray-1.5.2-py3-none-any.whl", hash = "sha256:40c6dfbff392f16a5c56c93aa79356b4ee25aa2d00579d61b560445ec4d3dc47"}, - {file = "portray-1.5.2.tar.gz", hash = "sha256:c8a3d489cfc95df6922868971996bb23584d37e46aa55e7ab9b6d29aa49f109d"}, + {file = "portray-1.6.0-py3-none-any.whl", hash = "sha256:592d6f0851dc585b49044d7a61bb6b5b26b3e9693ac06d97e27b86d6e00e9113"}, + {file = "portray-1.6.0.tar.gz", hash = "sha256:7d4b7b58c4964833c704eb5fbdf801aede6f72ea26603d6c2b58e16448fe93d9"}, ] poyo = [ {file = "poyo-0.5.0-py2.py3-none-any.whl", hash = "sha256:3e2ca8e33fdc3c411cd101ca395668395dd5dc7ac775b8e809e3def9f9fe041a"}, @@ -2134,8 +2136,8 @@ py = [ {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] pycodestyle = [ - {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, - {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] pydantic = [ {file = "pydantic-1.7.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c59ea046aea25be14dc22d69c97bee629e6d48d2b2ecb724d7fe8806bf5f61cd"}, @@ -2162,16 +2164,16 @@ pydantic = [ {file = "pydantic-1.7.3.tar.gz", hash = "sha256:213125b7e9e64713d16d988d10997dabc6a1f73f3991e1ff8e35ebb1409c7dc9"}, ] pydocstyle = [ - {file = "pydocstyle-5.1.1-py3-none-any.whl", hash = "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678"}, - {file = "pydocstyle-5.1.1.tar.gz", hash = "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325"}, + {file = "pydocstyle-6.0.0-py3-none-any.whl", hash = "sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d"}, + {file = "pydocstyle-6.0.0.tar.gz", hash = "sha256:164befb520d851dbcf0e029681b91f4f599c62c5cd8933fd54b1bfbd50e89e1f"}, ] pyflakes = [ - {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, - {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pygments = [ - {file = "Pygments-2.8.0-py3-none-any.whl", hash = "sha256:b21b072d0ccdf29297a82a2363359d99623597b8a265b8081760e4d0f7153c88"}, - {file = "Pygments-2.8.0.tar.gz", hash = "sha256:37a13ba168a02ac54cc5891a42b1caec333e59b66addb7fa633ea8a6d73445c0"}, + {file = "Pygments-2.8.1-py3-none-any.whl", hash = "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"}, + {file = "Pygments-2.8.1.tar.gz", hash = "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94"}, ] pylama = [ {file = "pylama-7.7.1-py2.py3-none-any.whl", hash = "sha256:fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15"}, @@ -2228,47 +2230,47 @@ pyyaml = [ {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] regex = [ - {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, - {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, - {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, - {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, - {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, - {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, - {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, - {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, - {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, - {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, - {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, - {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, - {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, + {file = "regex-2021.3.17-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b97ec5d299c10d96617cc851b2e0f81ba5d9d6248413cd374ef7f3a8871ee4a6"}, + {file = "regex-2021.3.17-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:cb4ee827857a5ad9b8ae34d3c8cc51151cb4a3fe082c12ec20ec73e63cc7c6f0"}, + {file = "regex-2021.3.17-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:633497504e2a485a70a3268d4fc403fe3063a50a50eed1039083e9471ad0101c"}, + {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:a59a2ee329b3de764b21495d78c92ab00b4ea79acef0f7ae8c1067f773570afa"}, + {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f85d6f41e34f6a2d1607e312820971872944f1661a73d33e1e82d35ea3305e14"}, + {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:4651f839dbde0816798e698626af6a2469eee6d9964824bb5386091255a1694f"}, + {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:39c44532d0e4f1639a89e52355b949573e1e2c5116106a395642cbbae0ff9bcd"}, + {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3d9a7e215e02bd7646a91fb8bcba30bc55fd42a719d6b35cf80e5bae31d9134e"}, + {file = "regex-2021.3.17-cp36-cp36m-win32.whl", hash = "sha256:159fac1a4731409c830d32913f13f68346d6b8e39650ed5d704a9ce2f9ef9cb3"}, + {file = "regex-2021.3.17-cp36-cp36m-win_amd64.whl", hash = "sha256:13f50969028e81765ed2a1c5fcfdc246c245cf8d47986d5172e82ab1a0c42ee5"}, + {file = "regex-2021.3.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9d8d286c53fe0cbc6d20bf3d583cabcd1499d89034524e3b94c93a5ab85ca90"}, + {file = "regex-2021.3.17-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:201e2619a77b21a7780580ab7b5ce43835e242d3e20fef50f66a8df0542e437f"}, + {file = "regex-2021.3.17-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d47d359545b0ccad29d572ecd52c9da945de7cd6cf9c0cfcb0269f76d3555689"}, + {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ea2f41445852c660ba7c3ebf7d70b3779b20d9ca8ba54485a17740db49f46932"}, + {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:486a5f8e11e1f5bbfcad87f7c7745eb14796642323e7e1829a331f87a713daaa"}, + {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:18e25e0afe1cf0f62781a150c1454b2113785401ba285c745acf10c8ca8917df"}, + {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:a2ee026f4156789df8644d23ef423e6194fad0bc53575534101bb1de5d67e8ce"}, + {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:4c0788010a93ace8a174d73e7c6c9d3e6e3b7ad99a453c8ee8c975ddd9965643"}, + {file = "regex-2021.3.17-cp37-cp37m-win32.whl", hash = "sha256:575a832e09d237ae5fedb825a7a5bc6a116090dd57d6417d4f3b75121c73e3be"}, + {file = "regex-2021.3.17-cp37-cp37m-win_amd64.whl", hash = "sha256:8e65e3e4c6feadf6770e2ad89ad3deb524bcb03d8dc679f381d0568c024e0deb"}, + {file = "regex-2021.3.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a0df9a0ad2aad49ea3c7f65edd2ffb3d5c59589b85992a6006354f6fb109bb18"}, + {file = "regex-2021.3.17-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b98bc9db003f1079caf07b610377ed1ac2e2c11acc2bea4892e28cc5b509d8d5"}, + {file = "regex-2021.3.17-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:808404898e9a765e4058bf3d7607d0629000e0a14a6782ccbb089296b76fa8fe"}, + {file = "regex-2021.3.17-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:5770a51180d85ea468234bc7987f5597803a4c3d7463e7323322fe4a1b181578"}, + {file = "regex-2021.3.17-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:976a54d44fd043d958a69b18705a910a8376196c6b6ee5f2596ffc11bff4420d"}, + {file = "regex-2021.3.17-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:63f3ca8451e5ff7133ffbec9eda641aeab2001be1a01878990f6c87e3c44b9d5"}, + {file = "regex-2021.3.17-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bcd945175c29a672f13fce13a11893556cd440e37c1b643d6eeab1988c8b209c"}, + {file = "regex-2021.3.17-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:3d9356add82cff75413bec360c1eca3e58db4a9f5dafa1f19650958a81e3249d"}, + {file = "regex-2021.3.17-cp38-cp38-win32.whl", hash = "sha256:f5d0c921c99297354cecc5a416ee4280bd3f20fd81b9fb671ca6be71499c3fdf"}, + {file = "regex-2021.3.17-cp38-cp38-win_amd64.whl", hash = "sha256:14de88eda0976020528efc92d0a1f8830e2fb0de2ae6005a6fc4e062553031fa"}, + {file = "regex-2021.3.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4c2e364491406b7888c2ad4428245fc56c327e34a5dfe58fd40df272b3c3dab3"}, + {file = "regex-2021.3.17-cp39-cp39-manylinux1_i686.whl", hash = "sha256:8bd4f91f3fb1c9b1380d6894bd5b4a519409135bec14c0c80151e58394a4e88a"}, + {file = "regex-2021.3.17-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:882f53afe31ef0425b405a3f601c0009b44206ea7f55ee1c606aad3cc213a52c"}, + {file = "regex-2021.3.17-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:07ef35301b4484bce843831e7039a84e19d8d33b3f8b2f9aab86c376813d0139"}, + {file = "regex-2021.3.17-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:360a01b5fa2ad35b3113ae0c07fb544ad180603fa3b1f074f52d98c1096fa15e"}, + {file = "regex-2021.3.17-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:709f65bb2fa9825f09892617d01246002097f8f9b6dde8d1bb4083cf554701ba"}, + {file = "regex-2021.3.17-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:c66221e947d7207457f8b6f42b12f613b09efa9669f65a587a2a71f6a0e4d106"}, + {file = "regex-2021.3.17-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:c782da0e45aff131f0bed6e66fbcfa589ff2862fc719b83a88640daa01a5aff7"}, + {file = "regex-2021.3.17-cp39-cp39-win32.whl", hash = "sha256:dc9963aacb7da5177e40874585d7407c0f93fb9d7518ec58b86e562f633f36cd"}, + {file = "regex-2021.3.17-cp39-cp39-win_amd64.whl", hash = "sha256:a0d04128e005142260de3733591ddf476e4902c0c23c1af237d9acf3c96e1b38"}, + {file = "regex-2021.3.17.tar.gz", hash = "sha256:4b8a1fb724904139149a43e172850f35aa6ea97fb0545244dc0b805e0154ed68"}, ] requests = [ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, @@ -2291,8 +2293,8 @@ six = [ {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] smmap = [ - {file = "smmap-3.0.5-py2.py3-none-any.whl", hash = "sha256:7bfcf367828031dc893530a29cb35eb8c8f2d7c8f2d0989354d75d24c8573714"}, - {file = "smmap-3.0.5.tar.gz", hash = "sha256:84c2751ef3072d4f6b2785ec7ee40244c6f45eb934d9e543e2c51f1bd3d54c50"}, + {file = "smmap-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"}, + {file = "smmap-4.0.0.tar.gz", hash = "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182"}, ] smmap2 = [ {file = "smmap2-3.0.1-py3-none-any.whl", hash = "sha256:0cb6ea470b1ad9a65a02ca7f4c7ae601861f7dd24a43812ca51cfca2892bb524"}, @@ -2370,8 +2372,8 @@ tornado = [ {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, ] tqdm = [ - {file = "tqdm-4.58.0-py2.py3-none-any.whl", hash = "sha256:2c44efa73b8914dba7807aefd09653ac63c22b5b4ea34f7a80973f418f1a3089"}, - {file = "tqdm-4.58.0.tar.gz", hash = "sha256:c23ac707e8e8aabb825e4d91f8e17247f9cc14b0d64dd9e97be0781e9e525bba"}, + {file = "tqdm-4.59.0-py2.py3-none-any.whl", hash = "sha256:9fdf349068d047d4cfbe24862c425883af1db29bcddf4b0eeb2524f6fbdb23c7"}, + {file = "tqdm-4.59.0.tar.gz", hash = "sha256:d666ae29164da3e517fcf125e41d4fe96e5bb375cd87ff9763f6b38b5592fe33"}, ] traitlets = [ {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, @@ -2424,8 +2426,8 @@ typing-inspect = [ {file = "typing_inspect-0.6.0.tar.gz", hash = "sha256:8f1b1dd25908dbfd81d3bebc218011531e7ab614ba6e5bf7826d887c834afab7"}, ] urllib3 = [ - {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, - {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"}, + {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, + {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, ] vistir = [ {file = "vistir-0.5.2-py2.py3-none-any.whl", hash = "sha256:a37079cdbd85d31a41cdd18457fe521e15ec08b255811e81aa061fd5f48a20fb"}, @@ -2448,6 +2450,6 @@ yaspin = [ {file = "yaspin-0.15.0.tar.gz", hash = "sha256:5a938bdc7bab353fd8942d0619d56c6b5159a80997dc1c387a479b39e6dc9391"}, ] zipp = [ - {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, - {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, + {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, + {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, ] From 07cf92116d1d880aa54f8e664804d9e8ffdfeb2a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 1 Apr 2021 21:12:45 -0700 Subject: [PATCH 418/539] Update pre-commit example to use latest isort version --- docs/configuration/pre-commit.md | 2 +- docs/upgrade_guides/5.0.0.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/pre-commit.md b/docs/configuration/pre-commit.md index 70eaded47..be495d2d7 100644 --- a/docs/configuration/pre-commit.md +++ b/docs/configuration/pre-commit.md @@ -9,7 +9,7 @@ To use isort's official pre-commit integration add the following config: ``` - repo: https://github.com/pycqa/isort - rev: 5.6.3 + rev: 5.8.0 hooks: - id: isort name: isort (python) diff --git a/docs/upgrade_guides/5.0.0.md b/docs/upgrade_guides/5.0.0.md index c5e806069..ba8e8288e 100644 --- a/docs/upgrade_guides/5.0.0.md +++ b/docs/upgrade_guides/5.0.0.md @@ -82,7 +82,7 @@ isort now includes an optimized precommit configuration in the repo itself. To u ``` - repo: https://github.com/pycqa/isort - rev: 5.6.3 + rev: 5.8.0 hooks: - id: isort name: isort (python) From 92a23f47ed0ec1c6777d7361690b7e2e2dbb492a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 2 Apr 2021 23:57:13 -0700 Subject: [PATCH 419/539] Cruft update --- .cruft.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cruft.json b/.cruft.json index 6d044f389..76987d0b0 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/timothycrosley/cookiecutter-python/", - "commit": "9be01014cde7ec06bd52415655d52ca30a9e9bcc", + "commit": "71f82f3600df4189ccef75556f706e0303ed8e5b", "context": { "cookiecutter": { "full_name": "Timothy Crosley", From 33e36237b7e7bd8e7c477fdcb7bef6437545784f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 3 Apr 2021 00:06:26 -0700 Subject: [PATCH 420/539] Update deps --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index cf5a17155..36a829e7f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -222,11 +222,11 @@ python-versions = ">=3.6, <3.7" [[package]] name = "decorator" -version = "4.4.2" +version = "5.0.3" description = "Decorators for Humans" category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" +python-versions = ">=3.5" [[package]] name = "distlib" @@ -1733,8 +1733,8 @@ dataclasses = [ {file = "dataclasses-0.7.tar.gz", hash = "sha256:494a6dcae3b8bcf80848eea2ef64c0cc5cd307ffc263e17cdf42f3e5420808e6"}, ] decorator = [ - {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, - {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, + {file = "decorator-5.0.3-py3-none-any.whl", hash = "sha256:e3f6d0eb6f1bf4580f872cb4313cb9a2067183e92bb1fbc4e5309285342d3420"}, + {file = "decorator-5.0.3.tar.gz", hash = "sha256:566f7d4563486f27d3c9fd201fd6f6f95eaf7cf4ad6f6ebbcffe82563b7bf06c"}, ] distlib = [ {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, From f074239e5e39c2dd9e4d373e3c6ce528dae3f78c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 3 Apr 2021 00:06:52 -0700 Subject: [PATCH 421/539] Cruft update --- .cruft.json | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.cruft.json b/.cruft.json index 76987d0b0..5d7ba98bd 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,16 +1,17 @@ { - "template": "https://github.com/timothycrosley/cookiecutter-python/", - "commit": "71f82f3600df4189ccef75556f706e0303ed8e5b", - "context": { - "cookiecutter": { - "full_name": "Timothy Crosley", - "email": "timothy.crosley@gmail.com", - "github_username": "pycqa", - "project_name": "isort", - "description": "A Python utility / library to sort Python imports.", - "version": "4.3.21", - "_template": "https://github.com/timothycrosley/cookiecutter-python/" - } - }, - "directory": "" -} \ No newline at end of file + "template": "https://github.com/timothycrosley/cookiecutter-python/", + "commit": "3958ca2ed0cad4d65c15912f6ab714720eb528a1", + "context": { + "cookiecutter": { + "full_name": "Timothy Crosley", + "email": "timothy.crosley@gmail.com", + "github_username": "pycqa", + "project_name": "isort", + "description": "A Python utility / library to sort Python imports.", + "version": "4.3.21", + "_template": "https://github.com/timothycrosley/cookiecutter-python/" + } + }, + "directory": "", + "checkout": null +} From 927cf7efed0c526e8aee9ce5e1ff7a7682ec7d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ni=20Gauffier?= Date: Wed, 7 Apr 2021 20:08:06 +0000 Subject: [PATCH 422/539] Faster gitignore --- isort/settings.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index bd8e631db..4108fd2fb 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -4,6 +4,7 @@ """ import configparser import fnmatch +import glob import os import posixpath import re @@ -214,6 +215,8 @@ class _Config: reverse_sort: bool = False star_first: bool = False import_dependencies = Dict[str, str] + git_folders: Set[Path] = field(default_factory=set) + git_ignore_files: Set[Path] = field(default_factory=set) def __post_init__(self): py_version = self.py_version @@ -521,10 +524,26 @@ def is_skipped(self, file_path: Path) -> bool: if file_path.name == ".git": # pragma: no cover return True - result = subprocess.run( # nosec # skipcq: PYL-W1510 - ["git", "-C", str(file_path.parent), "check-ignore", "--quiet", os_path] - ) - if result.returncode == 0: + for folder in self.git_folders: + if folder in file_path.parents: + break + else: + topfolder_result = subprocess.run([ + "git", "-C", str(file_path.parent), "rev-parse", "--show-toplevel" + ], capture_output=True) + if topfolder_result.returncode == 0: + git_folder = Path( + topfolder_result.stdout.decode('utf-8').split("\n")[0] + ) + files = glob.glob(str(git_folder)+"/**/*", recursive=True) + files_result = subprocess.run( + ["git", "-C", git_folder, "check-ignore", *files], capture_output=True + ).stdout.decode('utf-8').split("\n") + files_result = files_result[:-1] if files_result else files_result + self.git_ignore_files.update({Path(f) for f in files_result}) + self.git_folders.add(git_folder) + + if file_path in self.git_ignore_files: return True normalized_path = os_path.replace("\\", "/") @@ -543,8 +562,8 @@ def is_skipped(self, file_path: Path) -> bool: return True position = os.path.split(position[0]) - for glob in self.skip_globs: - if fnmatch.fnmatch(file_name, glob) or fnmatch.fnmatch("/" + file_name, glob): + for sglob in self.skip_globs: + if fnmatch.fnmatch(file_name, sglob) or fnmatch.fnmatch("/" + file_name, sglob): return True if not (os.path.isfile(os_path) or os.path.isdir(os_path) or os.path.islink(os_path)): From 4f32c446f9de8bb10a402a58420a6b6b32e57f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ni=20Gauffier?= Date: Mon, 12 Apr 2021 19:52:45 +0000 Subject: [PATCH 423/539] tests ok --- isort/settings.py | 51 ++++++++------ tests/unit/test_main.py | 143 ++++++++++++++++++++++++++++++---------- 2 files changed, 142 insertions(+), 52 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index 4108fd2fb..28df48754 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -215,8 +215,7 @@ class _Config: reverse_sort: bool = False star_first: bool = False import_dependencies = Dict[str, str] - git_folders: Set[Path] = field(default_factory=set) - git_ignore_files: Set[Path] = field(default_factory=set) + git_ignore: Dict[Path, Set[Path]] = field(default_factory=dict) def __post_init__(self): py_version = self.py_version @@ -511,6 +510,30 @@ def is_supported_filetype(self, file_name: str): else: return bool(_SHEBANG_RE.match(line)) + def _check_folder_gitignore(self, folder: str) -> Optional[Path]: + try: + topfolder_result = subprocess.check_output( # nosec # skipcq: PYL-W1510 + ["git", "-C", folder, "rev-parse", "--show-toplevel"] + ) + git_folder = Path(topfolder_result.decode("utf-8").split("\n")[0]) + + files = glob.glob(str(git_folder) + "/**/*", recursive=True) + files_result = ( + subprocess.check_output( # nosec # skipcq: PYL-W1510 + ["git", "-C", git_folder, "check-ignore", *files] + ) + .decode("utf-8") + .split("\n") + ) + files_result = files_result[:-1] if files_result else files_result + + self.git_ignore[git_folder] = {Path(f) for f in files_result} + + return git_folder + + except subprocess.CalledProcessError: + return None + def is_skipped(self, file_path: Path) -> bool: """Returns True if the file and/or folder should be skipped based on current settings.""" if self.directory and Path(self.directory) in file_path.resolve().parents: @@ -524,26 +547,16 @@ def is_skipped(self, file_path: Path) -> bool: if file_path.name == ".git": # pragma: no cover return True - for folder in self.git_folders: + git_folder = None + + for folder in self.git_ignore: if folder in file_path.parents: + git_folder = folder break else: - topfolder_result = subprocess.run([ - "git", "-C", str(file_path.parent), "rev-parse", "--show-toplevel" - ], capture_output=True) - if topfolder_result.returncode == 0: - git_folder = Path( - topfolder_result.stdout.decode('utf-8').split("\n")[0] - ) - files = glob.glob(str(git_folder)+"/**/*", recursive=True) - files_result = subprocess.run( - ["git", "-C", git_folder, "check-ignore", *files], capture_output=True - ).stdout.decode('utf-8').split("\n") - files_result = files_result[:-1] if files_result else files_result - self.git_ignore_files.update({Path(f) for f in files_result}) - self.git_folders.add(git_folder) - - if file_path in self.git_ignore_files: + git_folder = self._check_folder_gitignore(str(file_path.parent)) + + if git_folder and file_path in self.git_ignore[git_folder]: return True normalized_path = os_path.replace("\\", "/") diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 581b3a568..2849b64cc 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -4,6 +4,7 @@ from datetime import datetime from io import BytesIO, TextIOWrapper +import py import pytest from hypothesis import given from hypothesis import strategies as st @@ -341,39 +342,6 @@ def test_main(capsys, tmpdir): out, error = capsys.readouterr() assert "Broken" in out - # should respect gitignore if requested. - out, error = capsys.readouterr() # clear sysoutput before tests - subprocess.run(["git", "init", str(tmpdir)]) - main.main([str(python_file), "--skip-gitignore", "--filter-files"]) - out, error = capsys.readouterr() - assert "Skipped" not in out - tmpdir.join(".gitignore").write("has_imports.py") - main.main([str(python_file)]) - out, error = capsys.readouterr() - assert "Skipped" not in out - main.main([str(python_file), "--skip-gitignore", "--filter-files"]) - out, error = capsys.readouterr() - assert "Skipped" in out and "has_imports.py" not in out - - tmpdir.join(".gitignore").remove() - - currentdir = os.getcwd() - os.chdir(tmpdir) - - tmpdir.join(".gitignore").write("nested_dir/has_imports.py") - subpython_file = tmpdir.join("nested_dir/has_imports.py") - subpython_file.write( - """ -import b -import a -""" - ) - main.main([".", "--skip-gitignore", "--filter-files"]) - out, error = capsys.readouterr() - assert "nested_dir/has_imports.py" not in out - - os.chdir(currentdir) - # warnings should be displayed if old flags are used with pytest.warns(UserWarning): main.main([str(python_file), "--recursive", "-fss"]) @@ -1060,3 +1028,112 @@ def test_identify_imports_main(tmpdir, capsys): main.identify_imports_main(["-", "--attributes"], stdin=as_stream(file_content)) out, error = capsys.readouterr() len(out.split("\n")) == 2 + + +def test_gitignore(capsys: pytest.CaptureFixture, tmpdir: py.path.local): + + import_content = """ +import b +import a +""" + + def main_check(args): + try: + main.main(args) + except SystemExit: + pass + return capsys.readouterr() + + subprocess.run(["git", "init", str(tmpdir)]) + python_file = tmpdir.join("has_imports.py") + python_file.write(import_content) + tmpdir.join("no_imports.py").write("...") + + out, error = main_check([str(python_file), "--skip-gitignore", "--filter-files", "--check"]) + assert "has_imports.py" in error and "no_imports.py" not in error + + tmpdir.join(".gitignore").write("has_imports.py") + + out, error = main_check([str(python_file), "--check"]) + assert "has_imports.py" in error and "no_imports.py" not in error + + out, error = main_check([str(python_file), "--skip-gitignore", "--filter-files", "--check"]) + assert "Skipped" in out + + # Should work with nested directories + tmpdir.mkdir("nested_dir") + tmpdir.join(".gitignore").write("nested_dir/has_imports.py") + subfolder_file = tmpdir.join("nested_dir/has_imports.py") + subfolder_file.write(import_content) + + out, error = main_check([str(tmpdir), "--skip-gitignore", "--filter-files", "--check"]) + assert "has_imports.py" in error and "nested_dir/has_imports.py" not in error + + # Should work with relative path + currentdir = os.getcwd() + os.chdir(tmpdir) + + out, error = main_check([".", "--skip-gitignore", "--filter-files", "--check"]) + assert "has_imports.py" in error and "nested_dir/has_imports.py" not in error + + tmpdir.join(".gitignore").write( + """ +nested_dir/has_imports.py +has_imports.py +""" + ) + out, error = main_check([".", "--skip-gitignore", "--filter-files", "--check"]) + assert "Skipped" in out + + os.chdir(currentdir) + + # Should work with multiple git projects + + tmpdir.join(".git").remove() + tmpdir.join(".gitignore").remove() + + # git_project0 + # | has_imports_ignored.py ignored + # | has_imports.py should check + git_project0 = tmpdir.mkdir("git_project0") + subprocess.run(["git", "init", str(git_project0)]) + git_project0.join(".gitignore").write("has_imports_ignored.py") + git_project0.join("has_imports_ignored.py").write(import_content) + git_project0.join("has_imports.py").write(import_content) + + # git_project1 + # | has_imports.py should check + # | nested_dir + # | has_imports_ignored.py ignored + # | has_imports.py should check + # | nested_dir_ignored ignored + # | has_imports.py ignored from folder + git_project1 = tmpdir.mkdir("git_project1") + subprocess.run(["git", "init", str(git_project1)]) + git_project1.join(".gitignore").write( + """ +nested_dir/has_imports_ignored.py +nested_dir_ignored +""" + ) + git_project1.join("has_imports.py").write(import_content) + nested_dir = git_project1.mkdir("nested_dir") + nested_dir.join("has_imports.py").write(import_content) + nested_dir.join("has_imports_ignored.py").write(import_content) + git_project1.mkdir("nested_dir_ignored").join("has_imports.py").write(import_content) + + should_check = [ + "has_imports.py", + "nested_dir/has_imports.py", + "git_project0/has_imports.py", + "git_project1/has_imports.py", + "git_project1/nested_dir/has_imports.py", + ] + + out, error = main_check([str(tmpdir), "--skip-gitignore", "--filter-files", "--check"]) + + assert all(f"{str(tmpdir)}/{file}" in error for file in should_check) + + out, error = main_check([str(tmpdir), "--skip-gitignore", "--filter-files"]) + + assert all(f"{str(tmpdir)}/{file}" in out for file in should_check) From 9647353ed5ca0ae5c746c221a8ec1c95a5be9875 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 16 Apr 2021 23:43:52 -0700 Subject: [PATCH 424/539] Better skip_file behavior for streams --- isort/api.py | 3 +++ isort/core.py | 10 ++++++++-- isort/main.py | 1 + tests/unit/test_main.py | 25 +++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/isort/api.py b/isort/api.py index a861da122..9deb2c2c2 100644 --- a/isort/api.py +++ b/isort/api.py @@ -125,6 +125,7 @@ def sort_stream( file_path: Optional[Path] = None, disregard_skip: bool = False, show_diff: Union[bool, TextIO] = False, + raise_on_skip: bool = True, **config_kwargs, ) -> bool: """Sorts any imports within the provided code stream, outputs to the provided output stream. @@ -150,6 +151,7 @@ def sort_stream( config=config, file_path=file_path, disregard_skip=disregard_skip, + raise_on_skip=raise_on_skip, **config_kwargs, ) _output_stream.seek(0) @@ -187,6 +189,7 @@ def sort_stream( _internal_output, extension=extension or (file_path and file_path.suffix.lstrip(".")) or "py", config=config, + raise_on_skip=raise_on_skip, ) except FileSkipComment: raise FileSkipComment(content_source) diff --git a/isort/core.py b/isort/core.py index 9f577dac6..f8f386cda 100644 --- a/isort/core.py +++ b/isort/core.py @@ -30,6 +30,7 @@ def process( input_stream: TextIO, output_stream: TextIO, extension: str = "py", + raise_on_skip: bool = True, config: Config = DEFAULT_CONFIG, ) -> bool: """Parses stream identifying sections of contiguous imports and sorting them @@ -61,6 +62,7 @@ def process( first_import_section: bool = True indent: str = "" isort_off: bool = False + skip_file: bool = False code_sorting: Union[bool, str] = False code_sorting_section: str = "" code_sorting_indent: str = "" @@ -149,7 +151,11 @@ def process( for file_skip_comment in FILE_SKIP_COMMENTS: if file_skip_comment in line: - raise FileSkipComment("Passed in content") + if raise_on_skip: + raise FileSkipComment("Passed in content") + else: + isort_off = True + skip_file = True if not in_quote and stripped_line == "# isort: off": isort_off = True @@ -195,7 +201,7 @@ def process( not_imports = bool(in_quote) or was_in_quote or in_top_comment or isort_off if not (in_quote or was_in_quote or in_top_comment): if isort_off: - if stripped_line == "# isort: on": + if not skip_file and stripped_line == "# isort: on": isort_off = False elif stripped_line.endswith("# isort: split"): not_imports = True diff --git a/isort/main.py b/isort/main.py index b6954d249..93bd48534 100644 --- a/isort/main.py +++ b/isort/main.py @@ -1089,6 +1089,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = show_diff=show_diff, file_path=file_path, extension=ext_format, + raise_on_skip=False, ) elif "/" in file_names and not allow_root: printer = create_terminal_printer(color=config.color_output) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 581b3a568..e7d139406 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -918,6 +918,31 @@ def test_unsupported_encodings(tmpdir, capsys): out, error = capsys.readouterr() +def test_stream_skip_file(tmpdir, capsys): + input_with_skip = """ +# isort: skip_file +import b +import a +""" + stream_with_skip = as_stream(input_with_skip) + main.main(["-"], stdin=stream_with_skip) + out, error = capsys.readouterr() + assert out == input_with_skip + + input_without_skip = input_with_skip.replace("isort: skip_file", "generic comment") + stream_without_skip = as_stream(input_without_skip) + main.main(["-"], stdin=stream_without_skip) + out, error = capsys.readouterr() + assert ( + out + == """ +# generic comment +import a +import b +""" + ) + + def test_only_modified_flag(tmpdir, capsys): # ensures there is no verbose output for correct files with only-modified flag From 5592f424c13ff16bc688832cab176dd1af33e41c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 16 Apr 2021 23:52:25 -0700 Subject: [PATCH 425/539] Implemented #1705: More intuitive handling of isort:skip_file comments on streams. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 155c3d88c..fb41a693c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.9.0 TBD - Fixed (https://github.com/PyCQA/isort/pull/1695) added imports being added to doc string in some cases. - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. + - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. ### 5.8.0 March 20th 2021 - Fixed #1631: as import comments can in some cases be duplicated. From fab05f271b19b702351e33dda94802a4eb95f2b6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 16 Apr 2021 23:53:08 -0700 Subject: [PATCH 426/539] Small typo fix a vs an --- isort/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/exceptions.py b/isort/exceptions.py index a73444ba5..3a54d6429 100644 --- a/isort/exceptions.py +++ b/isort/exceptions.py @@ -56,7 +56,7 @@ class FileSkipComment(FileSkipped): def __init__(self, file_path: str): super().__init__( - f"{file_path} contains an file skip comment and was skipped.", file_path=file_path + f"{file_path} contains a file skip comment and was skipped.", file_path=file_path ) From 008d0a6e9042dd2387bf785431f8973adec21ecf Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 17 Apr 2021 00:00:14 -0700 Subject: [PATCH 427/539] Fix mergeable if statement found by deep source --- isort/wrap.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/isort/wrap.py b/isort/wrap.py index e993ae0f3..5fb4631f7 100644 --- a/isort/wrap.py +++ b/isort/wrap.py @@ -130,9 +130,8 @@ def line(content: str, line_separator: str, config: Config = DEFAULT_CONFIG) -> lines[-1] = content + ")" + config.comment_prefix + comment[:-1] return line_separator.join(lines) return f"{content}{splitter}\\{line_separator}{cont_line}" - elif len(content) > config.line_length and wrap_mode == Modes.NOQA: # type: ignore - if "# NOQA" not in content: - return f"{content}{config.comment_prefix} NOQA" + elif len(content) > config.line_length and wrap_mode == Modes.NOQA and "# NOQA" not in content: # type: ignore + return f"{content}{config.comment_prefix} NOQA" return content From a5ce76fd400da73be0d56609274d8473904f845d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 17 Apr 2021 00:01:15 -0700 Subject: [PATCH 428/539] Remove unnecessary else found by deep source --- isort/core.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/isort/core.py b/isort/core.py index f8f386cda..cc9d7c810 100644 --- a/isort/core.py +++ b/isort/core.py @@ -153,9 +153,8 @@ def process( if file_skip_comment in line: if raise_on_skip: raise FileSkipComment("Passed in content") - else: - isort_off = True - skip_file = True + isort_off = True + skip_file = True if not in_quote and stripped_line == "# isort: off": isort_off = True From bedc20739a7bf48f6beb79af90a42d143ec030b4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 17 Apr 2021 00:03:13 -0700 Subject: [PATCH 429/539] Skip issue for intentionally blank function --- isort/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/io.py b/isort/io.py index 2f30be0c7..7b107ab02 100644 --- a/isort/io.py +++ b/isort/io.py @@ -66,7 +66,7 @@ def read(filename: Union[str, Path]) -> Iterator["File"]: class _EmptyIO(StringIO): - def write(self, *args, **kwargs): + def write(self, *args, **kwargs): # skipcq: PTC-W0049 pass From 3dff8058f09d323517a5e47b5454c8d981342938 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 17 Apr 2021 00:04:12 -0700 Subject: [PATCH 430/539] Remove blank line after doc string found by deep source --- isort/hooks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/isort/hooks.py b/isort/hooks.py index dfd7eb3dc..244e7ea11 100644 --- a/isort/hooks.py +++ b/isort/hooks.py @@ -53,7 +53,6 @@ def git_hook( :return number of errors if in strict mode, 0 otherwise. """ - # Get list of files modified and staged diff_cmd = ["git", "diff-index", "--cached", "--name-only", "--diff-filter=ACMRTUXB", "HEAD"] if lazy: From 98b601953428fb4c77eafb4e06a018c4bb2b4391 Mon Sep 17 00:00:00 2001 From: ruro Date: Tue, 20 Apr 2021 15:19:37 +0300 Subject: [PATCH 431/539] Remove "recursive symlink detected" UserWarning --- isort/files.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/isort/files.py b/isort/files.py index 313d16b70..28a916cbb 100644 --- a/isort/files.py +++ b/isort/files.py @@ -1,7 +1,6 @@ import os from pathlib import Path from typing import Iterable, Iterator, List, Set -from warnings import warn from isort.settings import Config @@ -26,8 +25,6 @@ def find( dirnames.remove(dirname) else: if resolved_path in visited_dirs: # pragma: no cover - if not config.quiet: - warn(f"Likely recursive symlink detected to {resolved_path}") dirnames.remove(dirname) visited_dirs.add(resolved_path) From 098f71427e72187cf980daa084ded07fc6539a57 Mon Sep 17 00:00:00 2001 From: ruro Date: Tue, 20 Apr 2021 15:20:59 +0300 Subject: [PATCH 432/539] Use print instead of warn for "X was skipped" --- isort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index 93bd48534..3e3e1d134 100644 --- a/isort/main.py +++ b/isort/main.py @@ -1179,7 +1179,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = if num_skipped and not config.quiet: if config.verbose: for was_skipped in skipped: - warn( + print( f"{was_skipped} was skipped as it's listed in 'skip' setting" " or matches a glob in 'skip_glob' setting" ) From d059d602224f6883b68b911c0633d268dc0f34f6 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 20 Apr 2021 22:53:37 -0700 Subject: [PATCH 433/539] =?UTF-8?q?Add=20-=20ruro=20(@RuRo)=20-=20L=C3=A9n?= =?UTF-8?q?i=20(@legau)=20to=20contributors=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/contributing/4.-acknowledgements.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 90b4bc59b..f70173a0b 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -219,6 +219,8 @@ Code Contributors - @dongfangtianyu - Christian Clauss (@cclauss) - Jon Banafato (@jonafato) +- ruro (@RuRo) +- Léni (@legau) Documenters =================== From 8701c58cb4cf4c9b77944001d8d0fcb8d7412492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ni=20Gauffier?= Date: Wed, 21 Apr 2021 14:17:48 +0000 Subject: [PATCH 434/539] windows test --- isort/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/settings.py b/isort/settings.py index 28df48754..8ebaef668 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -520,7 +520,7 @@ def _check_folder_gitignore(self, folder: str) -> Optional[Path]: files = glob.glob(str(git_folder) + "/**/*", recursive=True) files_result = ( subprocess.check_output( # nosec # skipcq: PYL-W1510 - ["git", "-C", git_folder, "check-ignore", *files] + ["git", "-C", str(git_folder), "check-ignore", *files] ) .decode("utf-8") .split("\n") From 90ab857147966ead2e8d5832abe6e68d4934d997 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 21 Apr 2021 22:05:14 -0700 Subject: [PATCH 435/539] Add test case for issue #1711 --- tests/unit/test_regressions.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 26517598c..4d9b9b2b3 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1619,3 +1619,35 @@ def test_isort_correctly_handles_unix_vs_linux_newlines_issue_1566(): assert isort.code(import_statement, line_length=120) == isort.code( import_statement.replace("\n", "\r\n"), line_length=120 ).replace("\r\n", "\n") + + +def test_isort_treats_src_paths_same_as_from_config_as_cli_issue_1711(tmpdir): + assert isort.check_code( + """ +import mymodule +import sqlalchemy +""", + show_diff=True, + ) + + config_file = tmpdir.join(".isort.cfg") + config_file.write( + """ +[settings] +src_paths= + api +""" + ) + api_dir = tmpdir.mkdir("api") + api_dir.join("mymodule.py").write("# comment") + + config = isort.settings.Config(str(config_file)) + assert isort.check_code( + """ +import sqlalchemy + +import mymodule +""", + show_diff=True, + config=config, + ) From 123c97e3d5e4e81a90956ee8ef244ad196cad3fc Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 22 Apr 2021 12:43:28 -0400 Subject: [PATCH 436/539] fix typo in stream API docs --- docs/quick_start/3.-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quick_start/3.-api.md b/docs/quick_start/3.-api.md index 1583357f7..a327fc73a 100644 --- a/docs/quick_start/3.-api.md +++ b/docs/quick_start/3.-api.md @@ -13,7 +13,7 @@ Highlights include: - `isort.code` - Takes a string containing code, and returns it with imports sorted. - `isort.check_code` - Takes a string containing code, and returns `True` if all imports are sorted correctly, otherwise, `False`. - `isort.stream` - Takes an input stream containing Python code and an output stream. Outputs code to output stream with all imports sorted. -- `isort.stream` - Takes an input stream containing Python code and returns `True` if all imports in the stream are sorted correctly, otherwise, `False`. +- `isort.check_stream` - Takes an input stream containing Python code and returns `True` if all imports in the stream are sorted correctly, otherwise, `False`. - `isort.file` - Takes the path of a Python source file and sorts the imports in-place. - `isort.check_file` - Takes the path of a Python source file and returns `True` if all imports contained within are sorted correctly, otherwise, `False`. - `isort.place_module` - Takes the name of a module as a string and returns the categorization determined for it. From c617ddc059bbf434a0295baea1eb76dc5149d276 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 22 Apr 2021 20:53:12 -0700 Subject: [PATCH 437/539] Add - Mike Frysinger (@vapier) to contributor list --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index f70173a0b..66a7d3cab 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -246,6 +246,7 @@ Documenters - Hasan Ramezani (@hramezani) - @hirosassa - David Poznik (@dpoznik) +- Mike Frysinger (@vapier) -------------------------------------------- From 2cd28e9bdde6fbe859478fa14d7fd33eaf5fb30f Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Tue, 27 Apr 2021 20:38:51 +0530 Subject: [PATCH 438/539] Modidy docs to make --only-sections flag more clear --- isort/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index 3e3e1d134..a4e69823e 100644 --- a/isort/main.py +++ b/isort/main.py @@ -684,8 +684,9 @@ def _build_arg_parser() -> argparse.ArgumentParser: "--os", dest="only_sections", action="store_true", - help="Causes imports to be sorted only based on their sections like STDLIB,THIRDPARTY etc. " - "Imports are unaltered and keep their relative positions within the different sections.", + help="Causes imports to be sorted based on their sections like STDLIB,THIRDPARTY etc. " + "Imports are ordered based on their import style and keep their relative positions within " + "the different sections with the same style.", ) section_group.add_argument( "--ds", From 78aac6e21bc324b59f6a812ea9ad23b0c5824ad9 Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Tue, 27 Apr 2021 22:54:43 +0530 Subject: [PATCH 439/539] Modify docs to make only sections option more clear --- docs/configuration/options.md | 3 +-- isort/main.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index ed68cf2c1..0a0cb8ead 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -968,8 +968,7 @@ Tells isort to only show an identical custom import heading comment once, even i ## Only Sections -Causes imports to be sorted only based on their sections like STDLIB,THIRDPARTY etc. Imports are unaltered and keep their relative positions within the different sections. - +Causes imports to be sorted based on their sections like STDLIB,THIRDPARTY etc. Within the sections, the imports are ordered by their import style and the imports with the same style maintain their relative positions. **Type:** Bool **Default:** `False` **Python & Config File Name:** only_sections diff --git a/isort/main.py b/isort/main.py index a4e69823e..a8255c7d8 100644 --- a/isort/main.py +++ b/isort/main.py @@ -685,8 +685,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="only_sections", action="store_true", help="Causes imports to be sorted based on their sections like STDLIB,THIRDPARTY etc. " - "Imports are ordered based on their import style and keep their relative positions within " - "the different sections with the same style.", + "Within sections, the imports are ordered by their import style and the imports with " + "same style maintain their relative positions." ) section_group.add_argument( "--ds", From 03ac14055d5f7fa4f03582f08f1f0c4bb7291286 Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Wed, 28 Apr 2021 00:30:09 +0530 Subject: [PATCH 440/539] Correct linting issue --- isort/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index a8255c7d8..ae1ab60e0 100644 --- a/isort/main.py +++ b/isort/main.py @@ -685,8 +685,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="only_sections", action="store_true", help="Causes imports to be sorted based on their sections like STDLIB,THIRDPARTY etc. " - "Within sections, the imports are ordered by their import style and the imports with " - "same style maintain their relative positions." + "Within sections, the imports are ordered by their import style and the imports with " + "same style maintain their relative positions.", ) section_group.add_argument( "--ds", From 337ca6b896f334cfe489e4a22edae659687ff809 Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Thu, 29 Apr 2021 22:33:13 +0530 Subject: [PATCH 441/539] Correct the multiline tab bug as raised in issue #1714 --- isort/parse.py | 1 + 1 file changed, 1 insertion(+) diff --git a/isort/parse.py b/isort/parse.py index 714e39abd..2a15c90fd 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -293,6 +293,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte else: while line.strip().endswith("\\"): line, new_comment = parse_comments(in_lines[index]) + line = line.lstrip() index += 1 if new_comment: comments.append(new_comment) From 2ca426103f058260bb77b49fa9c85a33cc01e776 Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Thu, 29 Apr 2021 22:33:40 +0530 Subject: [PATCH 442/539] Add test for issue#1714 --- tests/unit/test_isort.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 7a02884ba..8f0368d70 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -4095,6 +4095,14 @@ def test_isort_keeps_comments_issue_691() -> None: assert isort.code(test_input) == expected_output +def test_isort_multiline_with_tab_issue_1714() -> None: + test_input = "from sys \\ \n" "\timport version\n" "print(version)\n" + + expected_output = "from sys import version\n" "\n" "print(version)\n" + + assert isort.code(test_input) == expected_output + + def test_isort_ensures_blank_line_between_import_and_comment() -> None: config = { "ensure_newline_before_comments": True, From 60d8e2e2450d4977e18e4a706996b6fb791dc010 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 30 Apr 2021 22:09:57 -0700 Subject: [PATCH 443/539] Update changelog to mention resolution of issue #1714 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb41a693c..8a67e349b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.9.0 TBD - Fixed (https://github.com/PyCQA/isort/pull/1695) added imports being added to doc string in some cases. + - Fixed (https://github.com/PyCQA/isort/pull/1714) in rare case line continuation combined with tab can output invalid code. - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. From 6efb3acde018380ea1ef43b9e1d1f68c6b65eb02 Mon Sep 17 00:00:00 2001 From: keno Date: Mon, 3 May 2021 01:41:22 +0900 Subject: [PATCH 444/539] fix: Should import vendered version --- isort/_vendored/toml/encoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/_vendored/toml/encoder.py b/isort/_vendored/toml/encoder.py index 68ec60f9f..c601afdde 100644 --- a/isort/_vendored/toml/encoder.py +++ b/isort/_vendored/toml/encoder.py @@ -276,7 +276,7 @@ def _dump_int(self, v): class TomlPreserveCommentEncoder(TomlEncoder): def __init__(self, _dict=dict, preserve=False): - from toml.decoder import CommentValue + from .decoder import CommentValue super(TomlPreserveCommentEncoder, self).__init__(_dict, preserve) self.dump_funcs[CommentValue] = lambda v: v.dump(self.dump_value) From 8c46082b504fb869fc37738291101731a0e67998 Mon Sep 17 00:00:00 2001 From: keno Date: Mon, 3 May 2021 03:07:41 +0900 Subject: [PATCH 445/539] fix: Indirect import --- poetry.lock | 24 ++++++++++++++++++------ pyproject.toml | 4 ++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 36a829e7f..2f5739161 100644 --- a/poetry.lock +++ b/poetry.lock @@ -473,7 +473,7 @@ python-versions = "*" [[package]] name = "hypothesis" -version = "6.8.4" +version = "6.10.1" description = "A library for property-based testing" category = "dev" optional = false @@ -484,8 +484,8 @@ attrs = ">=19.2.0" sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] -all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] -cli = ["click (>=7.0)", "black (>=19.10b0)"] +all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata (>=3.6)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] +cli = ["click (>=7.0)", "black (>=19.10b0)", "rich (>=9.0.0)"] codemods = ["libcst (>=0.3.16)"] dateutil = ["python-dateutil (>=1.4)"] django = ["pytz (>=2014.1)", "django (>=2.2)"] @@ -1605,7 +1605,7 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "b9cfee23dcfb20d64a10ae181e449fa542a0d7a59f49305bf106e93f7f097948" +content-hash = "ec62167e76408cb378bb244664d70f1c35814d52124a0e8bbce385cc513dfe10" [metadata.files] appdirs = [ @@ -1685,6 +1685,9 @@ coverage = [ {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, + {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, + {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, + {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, @@ -1838,8 +1841,8 @@ hyperframe = [ {file = "hyperframe-5.2.0.tar.gz", hash = "sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"}, ] hypothesis = [ - {file = "hypothesis-6.8.4-py3-none-any.whl", hash = "sha256:2caa49d4daf77538ec79731b8b3baaa9cfaf83abd9c7703f05b0d046814654f1"}, - {file = "hypothesis-6.8.4.tar.gz", hash = "sha256:665486c0c823ae217be96e611882249f6b060799ded7b2393d451fa488b62fcb"}, + {file = "hypothesis-6.10.1-py3-none-any.whl", hash = "sha256:1d65f58d82d1cbd35b6441bda3bb11cb1adc879d6b2af191aea388fa412171b1"}, + {file = "hypothesis-6.10.1.tar.gz", hash = "sha256:586b6c46e90878c2546743afbed348bca51e1f30e3461fa701fad58c2c47c650"}, ] hypothesis-auto = [ {file = "hypothesis-auto-1.1.4.tar.gz", hash = "sha256:5e2c2fb09dc09842512d80630bb792359a1d33d2c0473ad47ee23da0be9e32b1"}, @@ -1917,6 +1920,7 @@ lunr = [ {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, ] mako = [ + {file = "Mako-1.1.4-py2.py3-none-any.whl", hash = "sha256:aea166356da44b9b830c8023cd9b557fa856bd8b4035d6de771ca027dfc5cc6e"}, {file = "Mako-1.1.4.tar.gz", hash = "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab"}, ] markdown = [ @@ -2213,18 +2217,26 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, diff --git a/pyproject.toml b/pyproject.toml index 6cd4f6af2..0ded72144 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,6 +83,10 @@ httpx = "^0.13.3" example_shared_isort_profile = "^0.0.1" example_isort_formatting_plugin = "^0.0.2" flake8 = "^3.8.4" +hypothesis = "^6.10.1" +libcst = "^0.3.18" +mypy-extensions = "^0.4.3" +py = "^1.10.0" [tool.poetry.scripts] isort = "isort.main:main" From c4e2c02abd1b765854b41aa4f6c1b1fe9125a26e Mon Sep 17 00:00:00 2001 From: keno Date: Mon, 3 May 2021 03:09:51 +0900 Subject: [PATCH 446/539] refactor: Add `toml` to dev-dependencies and assume always exists --- poetry.lock | 2 +- pyproject.toml | 1 + tests/unit/test_isort.py | 6 +----- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2f5739161..029593195 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1605,7 +1605,7 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "ec62167e76408cb378bb244664d70f1c35814d52124a0e8bbce385cc513dfe10" +content-hash = "4a46585da4f8437ec280cf081865b4d6c6696a951f88d980c9a0089fdbd4f9e1" [metadata.files] appdirs = [ diff --git a/pyproject.toml b/pyproject.toml index 0ded72144..94bc45968 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,7 @@ hypothesis = "^6.10.1" libcst = "^0.3.18" mypy-extensions = "^0.4.3" py = "^1.10.0" +toml = "^0.10.2" [tool.poetry.scripts] isort = "isort.main:main" diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 8f0368d70..f847f097f 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -13,17 +13,13 @@ import py import pytest +import toml import isort from isort import api, sections, files from isort.settings import WrapModes, Config from isort.utils import exists_case_sensitive from isort.exceptions import FileSkipped, ExistingSyntaxErrors -try: - import toml -except ImportError: - toml = None - TEST_DEFAULT_CONFIG = """ [*.{py,pyi}] max_line_length = 120 From 7745d815f9064985dc19c1d581588614844c1031 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 3 May 2021 22:08:47 -0700 Subject: [PATCH 447/539] Update deps --- poetry.lock | 257 +++++++++++++++++++++++++------------------------ pyproject.toml | 2 +- 2 files changed, 132 insertions(+), 127 deletions(-) diff --git a/poetry.lock b/poetry.lock index 36a829e7f..98f640fb4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -16,7 +16,7 @@ python-versions = "*" [[package]] name = "arrow" -version = "1.0.3" +version = "1.1.0" description = "Better dates & times for Python" category = "dev" optional = false @@ -115,7 +115,7 @@ python-versions = "*" [[package]] name = "cerberus" -version = "1.3.2" +version = "1.3.3" description = "Lightweight, extensible schema and data validation tool for Python dictionaries." category = "main" optional = false @@ -222,7 +222,7 @@ python-versions = ">=3.6, <3.7" [[package]] name = "decorator" -version = "5.0.3" +version = "5.0.7" description = "Decorators for Humans" category = "dev" optional = false @@ -309,7 +309,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "flake8" -version = "3.9.0" +version = "3.9.1" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false @@ -376,14 +376,15 @@ gitdb = ">=4.0.1" [[package]] name = "gitpython" -version = "3.1.14" +version = "3.1.15" description = "Python Git Library" category = "dev" optional = false -python-versions = ">=3.4" +python-versions = ">=3.5" [package.dependencies] gitdb = ">=4.0.1,<5" +typing-extensions = ">=3.7.4.0" [[package]] name = "h11" @@ -473,7 +474,7 @@ python-versions = "*" [[package]] name = "hypothesis" -version = "6.8.4" +version = "6.10.1" description = "A library for property-based testing" category = "dev" optional = false @@ -484,8 +485,8 @@ attrs = ">=19.2.0" sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] -all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] -cli = ["click (>=7.0)", "black (>=19.10b0)"] +all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata (>=3.6)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"] +cli = ["click (>=7.0)", "black (>=19.10b0)", "rich (>=9.0.0)"] codemods = ["libcst (>=0.3.16)"] dateutil = ["python-dateutil (>=1.4)"] django = ["pytz (>=2014.1)", "django (>=2.2)"] @@ -548,7 +549,7 @@ test = ["flake8 (>=3.8.4,<3.9.0)", "pycodestyle (>=2.6.0,<2.7.0)"] [[package]] name = "importlib-metadata" -version = "3.10.0" +version = "4.0.1" description = "Read metadata from Python packages" category = "main" optional = false @@ -660,13 +661,14 @@ python-versions = ">=3.6" [[package]] name = "lark-parser" -version = "0.11.2" +version = "0.11.3" description = "a modern parsing library" category = "dev" optional = false python-versions = "*" [package.extras] +atomic_cache = ["atomicwrites"] nearley = ["js2py"] regex = ["regex"] @@ -779,7 +781,7 @@ tornado = ">=5.0" [[package]] name = "mkdocs-material" -version = "7.1.0" +version = "7.1.3" description = "A Material Design theme for MkDocs" category = "dev" optional = false @@ -829,11 +831,11 @@ python-versions = "*" [[package]] name = "nltk" -version = "3.5" +version = "3.6.2" description = "Natural Language Toolkit" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5.*" [package.dependencies] click = "*" @@ -842,9 +844,9 @@ regex = "*" tqdm = "*" [package.extras] -all = ["requests", "numpy", "python-crfsuite", "scikit-learn", "twython", "pyparsing", "scipy", "matplotlib", "gensim"] +all = ["matplotlib", "twython", "scipy", "numpy", "gensim (<4.0.0)", "python-crfsuite", "pyparsing", "scikit-learn", "requests"] corenlp = ["requests"] -machine_learning = ["gensim", "numpy", "python-crfsuite", "scikit-learn", "scipy"] +machine_learning = ["gensim (<4.0.0)", "numpy", "python-crfsuite", "scikit-learn", "scipy"] plot = ["matplotlib"] tgrep = ["pyparsing"] twitter = ["twython"] @@ -901,7 +903,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pbr" -version = "5.5.1" +version = "5.6.0" description = "Python Build Reasonableness" category = "dev" optional = false @@ -1142,7 +1144,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.8.1" +version = "2.9.0" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -1183,7 +1185,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pytest" -version = "6.2.2" +version = "6.2.3" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -1245,11 +1247,11 @@ six = ">=1.5" [[package]] name = "python-slugify" -version = "4.0.1" +version = "5.0.0" description = "A Python Slugify application that handles Unicode" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.dependencies] text-unidecode = ">=1.3" @@ -1267,7 +1269,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "regex" -version = "2021.3.17" +version = "2021.4.4" description = "Alternative regular expression module, to replace re." category = "dev" optional = false @@ -1445,7 +1447,7 @@ python-versions = ">= 3.5" [[package]] name = "tqdm" -version = "4.59.0" +version = "4.60.0" description = "Fast, Extensible Progress Meter" category = "dev" optional = false @@ -1474,7 +1476,7 @@ test = ["pytest", "mock"] [[package]] name = "typed-ast" -version = "1.4.2" +version = "1.4.3" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false @@ -1499,7 +1501,7 @@ doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown- [[package]] name = "typing-extensions" -version = "3.7.4.3" +version = "3.10.0.0" description = "Backported and Experimental Type Hints for Python 3.5+" category = "main" optional = false @@ -1605,7 +1607,7 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "b9cfee23dcfb20d64a10ae181e449fa542a0d7a59f49305bf106e93f7f097948" +content-hash = "91c626526441a35437807b313392813ef86a5cad36ecb166c1337a7a2f30d35f" [metadata.files] appdirs = [ @@ -1617,8 +1619,8 @@ appnope = [ {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, ] arrow = [ - {file = "arrow-1.0.3-py3-none-any.whl", hash = "sha256:3515630f11a15c61dcb4cdd245883270dd334c83f3e639824e65a4b79cc48543"}, - {file = "arrow-1.0.3.tar.gz", hash = "sha256:399c9c8ae732270e1aa58ead835a79a40d7be8aa109c579898eb41029b5a231d"}, + {file = "arrow-1.1.0-py3-none-any.whl", hash = "sha256:8cbe6a629b1c54ae11b52d6d9e70890089241958f63bc59467e277e34b7a5378"}, + {file = "arrow-1.1.0.tar.gz", hash = "sha256:b8fe13abf3517abab315e09350c903902d1447bd311afbc17547ba1cb3ff5bd8"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, @@ -1648,7 +1650,8 @@ cached-property = [ {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, ] cerberus = [ - {file = "Cerberus-1.3.2.tar.gz", hash = "sha256:302e6694f206dd85cb63f13fd5025b31ab6d38c99c50c6d769f8fa0b0f299589"}, + {file = "Cerberus-1.3.3-py3-none-any.whl", hash = "sha256:7aff49bc793e58a88ac14bffc3eca0f67e077881d3c62c621679a621294dd174"}, + {file = "Cerberus-1.3.3.tar.gz", hash = "sha256:eec10585c33044fb7c69650bc5b68018dac0443753337e2b07684ee0f3c83329"}, ] certifi = [ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, @@ -1733,8 +1736,8 @@ dataclasses = [ {file = "dataclasses-0.7.tar.gz", hash = "sha256:494a6dcae3b8bcf80848eea2ef64c0cc5cd307ffc263e17cdf42f3e5420808e6"}, ] decorator = [ - {file = "decorator-5.0.3-py3-none-any.whl", hash = "sha256:e3f6d0eb6f1bf4580f872cb4313cb9a2067183e92bb1fbc4e5309285342d3420"}, - {file = "decorator-5.0.3.tar.gz", hash = "sha256:566f7d4563486f27d3c9fd201fd6f6f95eaf7cf4ad6f6ebbcffe82563b7bf06c"}, + {file = "decorator-5.0.7-py3-none-any.whl", hash = "sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"}, + {file = "decorator-5.0.7.tar.gz", hash = "sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060"}, ] distlib = [ {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, @@ -1779,8 +1782,8 @@ falcon = [ {file = "falcon-2.0.0.tar.gz", hash = "sha256:eea593cf466b9c126ce667f6d30503624ef24459f118c75594a69353b6c3d5fc"}, ] flake8 = [ - {file = "flake8-3.9.0-py2.py3-none-any.whl", hash = "sha256:12d05ab02614b6aee8df7c36b97d1a3b2372761222b19b58621355e82acddcff"}, - {file = "flake8-3.9.0.tar.gz", hash = "sha256:78873e372b12b093da7b5e5ed302e8ad9e988b38b063b61ad937f26ca58fc5f0"}, + {file = "flake8-3.9.1-py2.py3-none-any.whl", hash = "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a"}, + {file = "flake8-3.9.1.tar.gz", hash = "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378"}, ] flake8-bugbear = [ {file = "flake8-bugbear-19.8.0.tar.gz", hash = "sha256:d8c466ea79d5020cb20bf9f11cf349026e09517a42264f313d3f6fddb83e0571"}, @@ -1802,8 +1805,8 @@ gitdb2 = [ {file = "gitdb2-4.0.2.tar.gz", hash = "sha256:0986cb4003de743f2b3aba4c828edd1ab58ce98e1c4a8acf72ef02760d4beb4e"}, ] gitpython = [ - {file = "GitPython-3.1.14-py3-none-any.whl", hash = "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b"}, - {file = "GitPython-3.1.14.tar.gz", hash = "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"}, + {file = "GitPython-3.1.15-py3-none-any.whl", hash = "sha256:a77824e516d3298b04fb36ec7845e92747df8fcfee9cacc32dd6239f9652f867"}, + {file = "GitPython-3.1.15.tar.gz", hash = "sha256:05af150f47a5cca3f4b0af289b73aef8cf3c4fe2385015b06220cbcdee48bb6e"}, ] h11 = [ {file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"}, @@ -1838,8 +1841,8 @@ hyperframe = [ {file = "hyperframe-5.2.0.tar.gz", hash = "sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"}, ] hypothesis = [ - {file = "hypothesis-6.8.4-py3-none-any.whl", hash = "sha256:2caa49d4daf77538ec79731b8b3baaa9cfaf83abd9c7703f05b0d046814654f1"}, - {file = "hypothesis-6.8.4.tar.gz", hash = "sha256:665486c0c823ae217be96e611882249f6b060799ded7b2393d451fa488b62fcb"}, + {file = "hypothesis-6.10.1-py3-none-any.whl", hash = "sha256:1d65f58d82d1cbd35b6441bda3bb11cb1adc879d6b2af191aea388fa412171b1"}, + {file = "hypothesis-6.10.1.tar.gz", hash = "sha256:586b6c46e90878c2546743afbed348bca51e1f30e3461fa701fad58c2c47c650"}, ] hypothesis-auto = [ {file = "hypothesis-auto-1.1.4.tar.gz", hash = "sha256:5e2c2fb09dc09842512d80630bb792359a1d33d2c0473ad47ee23da0be9e32b1"}, @@ -1871,8 +1874,8 @@ immutables = [ {file = "immutables-0.15.tar.gz", hash = "sha256:3713ab1ebbb6946b7ce1387bb9d1d7f5e09c45add58c2a2ee65f963c171e746b"}, ] importlib-metadata = [ - {file = "importlib_metadata-3.10.0-py3-none-any.whl", hash = "sha256:d2d46ef77ffc85cbf7dac7e81dd663fde71c45326131bea8033b9bad42268ebe"}, - {file = "importlib_metadata-3.10.0.tar.gz", hash = "sha256:c9db46394197244adf2f0b08ec5bc3cf16757e9590b02af1fca085c16c0d600a"}, + {file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, + {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -1903,7 +1906,7 @@ joblib = [ {file = "joblib-1.0.1.tar.gz", hash = "sha256:9c17567692206d2f3fb9ecf5e991084254fe631665c450b443761c4186a613f7"}, ] lark-parser = [ - {file = "lark-parser-0.11.2.tar.gz", hash = "sha256:ef610461ebf2b243502f337d9d49879e39f9add846a4749e88c8dcdc1378bb6b"}, + {file = "lark-parser-0.11.3.tar.gz", hash = "sha256:e29ca814a98bb0f81674617d878e5f611cb993c19ea47f22c80da3569425f9bd"}, ] libcst = [ {file = "libcst-0.3.18-py3-none-any.whl", hash = "sha256:da89cc1a37702caa6fe7207b1257fad58f0d4643597279733106ca902b4fdbad"}, @@ -1986,8 +1989,8 @@ mkdocs = [ {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, ] mkdocs-material = [ - {file = "mkdocs-material-7.1.0.tar.gz", hash = "sha256:1afaa5b174265eaa4a886f73187bb0e302a9596e9bfedb5aa2cb260d8b1d994e"}, - {file = "mkdocs_material-7.1.0-py2.py3-none-any.whl", hash = "sha256:13e73b3571d36f7e4a7dc11093323cff92095f4f219a00ba19c77a5e53aa6c55"}, + {file = "mkdocs-material-7.1.3.tar.gz", hash = "sha256:e34bba93ad1a0e6f9afc371f4ef55bedabbf13b9a786b013b0ce26ac55ec2932"}, + {file = "mkdocs_material-7.1.3-py2.py3-none-any.whl", hash = "sha256:437638b0de7a9113d7f1c9ddc93c0a29a3b808c71c3606713d8c1fa437697a3e"}, ] mkdocs-material-extensions = [ {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, @@ -2014,7 +2017,8 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] nltk = [ - {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, + {file = "nltk-3.6.2-py3-none-any.whl", hash = "sha256:240e23ab1ab159ef9940777d30c7c72d7e76d91877099218a7585370c11f6b9e"}, + {file = "nltk-3.6.2.zip", hash = "sha256:57d556abed621ab9be225cc6d2df1edce17572efb67a3d754630c9f8381503eb"}, ] numpy = [ {file = "numpy-1.19.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff"}, @@ -2069,8 +2073,8 @@ pathspec = [ {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, ] pbr = [ - {file = "pbr-5.5.1-py2.py3-none-any.whl", hash = "sha256:b236cde0ac9a6aedd5e3c34517b423cd4fd97ef723849da6b0d2231142d89c00"}, - {file = "pbr-5.5.1.tar.gz", hash = "sha256:5fad80b613c402d5b7df7bd84812548b2a61e9977387a80a5fc5c396492b13c9"}, + {file = "pbr-5.6.0-py2.py3-none-any.whl", hash = "sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"}, + {file = "pbr-5.6.0.tar.gz", hash = "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd"}, ] pdocs = [ {file = "pdocs-1.1.1-py3-none-any.whl", hash = "sha256:4f5116cf5ce0fa9f13171cd74db224636d4d71370115eefce22d8945526fcfc0"}, @@ -2172,8 +2176,8 @@ pyflakes = [ {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pygments = [ - {file = "Pygments-2.8.1-py3-none-any.whl", hash = "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"}, - {file = "Pygments-2.8.1.tar.gz", hash = "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94"}, + {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"}, + {file = "Pygments-2.9.0.tar.gz", hash = "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f"}, ] pylama = [ {file = "pylama-7.7.1-py2.py3-none-any.whl", hash = "sha256:fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15"}, @@ -2188,8 +2192,8 @@ pyparsing = [ {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ - {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"}, - {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"}, + {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, + {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, ] pytest-cov = [ {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, @@ -2204,7 +2208,8 @@ python-dateutil = [ {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, ] python-slugify = [ - {file = "python-slugify-4.0.1.tar.gz", hash = "sha256:69a517766e00c1268e5bbfc0d010a0a8508de0b18d30ad5a1ff357f8ae724270"}, + {file = "python-slugify-5.0.0.tar.gz", hash = "sha256:744cd5d42b381687657f3b652d1d9b183c0870ec43de23be682b6d83d69c6962"}, + {file = "python_slugify-5.0.0-py2.py3-none-any.whl", hash = "sha256:2a497206413bebbab1d9004c44443b71f65a402653446af7246904ad1c0927d3"}, ] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, @@ -2230,47 +2235,47 @@ pyyaml = [ {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] regex = [ - {file = "regex-2021.3.17-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b97ec5d299c10d96617cc851b2e0f81ba5d9d6248413cd374ef7f3a8871ee4a6"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:cb4ee827857a5ad9b8ae34d3c8cc51151cb4a3fe082c12ec20ec73e63cc7c6f0"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:633497504e2a485a70a3268d4fc403fe3063a50a50eed1039083e9471ad0101c"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:a59a2ee329b3de764b21495d78c92ab00b4ea79acef0f7ae8c1067f773570afa"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f85d6f41e34f6a2d1607e312820971872944f1661a73d33e1e82d35ea3305e14"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:4651f839dbde0816798e698626af6a2469eee6d9964824bb5386091255a1694f"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:39c44532d0e4f1639a89e52355b949573e1e2c5116106a395642cbbae0ff9bcd"}, - {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3d9a7e215e02bd7646a91fb8bcba30bc55fd42a719d6b35cf80e5bae31d9134e"}, - {file = "regex-2021.3.17-cp36-cp36m-win32.whl", hash = "sha256:159fac1a4731409c830d32913f13f68346d6b8e39650ed5d704a9ce2f9ef9cb3"}, - {file = "regex-2021.3.17-cp36-cp36m-win_amd64.whl", hash = "sha256:13f50969028e81765ed2a1c5fcfdc246c245cf8d47986d5172e82ab1a0c42ee5"}, - {file = "regex-2021.3.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9d8d286c53fe0cbc6d20bf3d583cabcd1499d89034524e3b94c93a5ab85ca90"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:201e2619a77b21a7780580ab7b5ce43835e242d3e20fef50f66a8df0542e437f"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d47d359545b0ccad29d572ecd52c9da945de7cd6cf9c0cfcb0269f76d3555689"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ea2f41445852c660ba7c3ebf7d70b3779b20d9ca8ba54485a17740db49f46932"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:486a5f8e11e1f5bbfcad87f7c7745eb14796642323e7e1829a331f87a713daaa"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:18e25e0afe1cf0f62781a150c1454b2113785401ba285c745acf10c8ca8917df"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:a2ee026f4156789df8644d23ef423e6194fad0bc53575534101bb1de5d67e8ce"}, - {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:4c0788010a93ace8a174d73e7c6c9d3e6e3b7ad99a453c8ee8c975ddd9965643"}, - {file = "regex-2021.3.17-cp37-cp37m-win32.whl", hash = "sha256:575a832e09d237ae5fedb825a7a5bc6a116090dd57d6417d4f3b75121c73e3be"}, - {file = "regex-2021.3.17-cp37-cp37m-win_amd64.whl", hash = "sha256:8e65e3e4c6feadf6770e2ad89ad3deb524bcb03d8dc679f381d0568c024e0deb"}, - {file = "regex-2021.3.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a0df9a0ad2aad49ea3c7f65edd2ffb3d5c59589b85992a6006354f6fb109bb18"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b98bc9db003f1079caf07b610377ed1ac2e2c11acc2bea4892e28cc5b509d8d5"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:808404898e9a765e4058bf3d7607d0629000e0a14a6782ccbb089296b76fa8fe"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:5770a51180d85ea468234bc7987f5597803a4c3d7463e7323322fe4a1b181578"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:976a54d44fd043d958a69b18705a910a8376196c6b6ee5f2596ffc11bff4420d"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:63f3ca8451e5ff7133ffbec9eda641aeab2001be1a01878990f6c87e3c44b9d5"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bcd945175c29a672f13fce13a11893556cd440e37c1b643d6eeab1988c8b209c"}, - {file = "regex-2021.3.17-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:3d9356add82cff75413bec360c1eca3e58db4a9f5dafa1f19650958a81e3249d"}, - {file = "regex-2021.3.17-cp38-cp38-win32.whl", hash = "sha256:f5d0c921c99297354cecc5a416ee4280bd3f20fd81b9fb671ca6be71499c3fdf"}, - {file = "regex-2021.3.17-cp38-cp38-win_amd64.whl", hash = "sha256:14de88eda0976020528efc92d0a1f8830e2fb0de2ae6005a6fc4e062553031fa"}, - {file = "regex-2021.3.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4c2e364491406b7888c2ad4428245fc56c327e34a5dfe58fd40df272b3c3dab3"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux1_i686.whl", hash = "sha256:8bd4f91f3fb1c9b1380d6894bd5b4a519409135bec14c0c80151e58394a4e88a"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:882f53afe31ef0425b405a3f601c0009b44206ea7f55ee1c606aad3cc213a52c"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:07ef35301b4484bce843831e7039a84e19d8d33b3f8b2f9aab86c376813d0139"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:360a01b5fa2ad35b3113ae0c07fb544ad180603fa3b1f074f52d98c1096fa15e"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:709f65bb2fa9825f09892617d01246002097f8f9b6dde8d1bb4083cf554701ba"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:c66221e947d7207457f8b6f42b12f613b09efa9669f65a587a2a71f6a0e4d106"}, - {file = "regex-2021.3.17-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:c782da0e45aff131f0bed6e66fbcfa589ff2862fc719b83a88640daa01a5aff7"}, - {file = "regex-2021.3.17-cp39-cp39-win32.whl", hash = "sha256:dc9963aacb7da5177e40874585d7407c0f93fb9d7518ec58b86e562f633f36cd"}, - {file = "regex-2021.3.17-cp39-cp39-win_amd64.whl", hash = "sha256:a0d04128e005142260de3733591ddf476e4902c0c23c1af237d9acf3c96e1b38"}, - {file = "regex-2021.3.17.tar.gz", hash = "sha256:4b8a1fb724904139149a43e172850f35aa6ea97fb0545244dc0b805e0154ed68"}, + {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"}, + {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"}, + {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"}, + {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"}, + {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"}, + {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"}, + {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"}, + {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"}, + {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"}, + {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"}, + {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"}, + {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"}, + {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"}, ] requests = [ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, @@ -2372,53 +2377,53 @@ tornado = [ {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, ] tqdm = [ - {file = "tqdm-4.59.0-py2.py3-none-any.whl", hash = "sha256:9fdf349068d047d4cfbe24862c425883af1db29bcddf4b0eeb2524f6fbdb23c7"}, - {file = "tqdm-4.59.0.tar.gz", hash = "sha256:d666ae29164da3e517fcf125e41d4fe96e5bb375cd87ff9763f6b38b5592fe33"}, + {file = "tqdm-4.60.0-py2.py3-none-any.whl", hash = "sha256:daec693491c52e9498632dfbe9ccfc4882a557f5fa08982db1b4d3adbe0887c3"}, + {file = "tqdm-4.60.0.tar.gz", hash = "sha256:ebdebdb95e3477ceea267decfc0784859aa3df3e27e22d23b83e9b272bf157ae"}, ] traitlets = [ {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, ] typed-ast = [ - {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"}, - {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487"}, - {file = "typed_ast-1.4.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412"}, - {file = "typed_ast-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400"}, - {file = "typed_ast-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606"}, - {file = "typed_ast-1.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64"}, - {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07"}, - {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc"}, - {file = "typed_ast-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a"}, - {file = "typed_ast-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151"}, - {file = "typed_ast-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3"}, - {file = "typed_ast-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41"}, - {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f"}, - {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581"}, - {file = "typed_ast-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37"}, - {file = "typed_ast-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd"}, - {file = "typed_ast-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496"}, - {file = "typed_ast-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc"}, - {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"}, - {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea"}, - {file = "typed_ast-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787"}, - {file = "typed_ast-1.4.2-cp38-cp38-win32.whl", hash = "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2"}, - {file = "typed_ast-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937"}, - {file = "typed_ast-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1"}, - {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6"}, - {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166"}, - {file = "typed_ast-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d"}, - {file = "typed_ast-1.4.2-cp39-cp39-win32.whl", hash = "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b"}, - {file = "typed_ast-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440"}, - {file = "typed_ast-1.4.2.tar.gz", hash = "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, + {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, + {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, + {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, + {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, + {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, + {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, + {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, + {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, + {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typer = [ {file = "typer-0.3.2-py3-none-any.whl", hash = "sha256:ba58b920ce851b12a2d790143009fa00ac1d05b3ff3257061ff69dbdfc3d161b"}, {file = "typer-0.3.2.tar.gz", hash = "sha256:5455d750122cff96745b0dec87368f56d023725a7ebc9d2e54dd23dc86816303"}, ] typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, - {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, - {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, + {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, + {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, + {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, ] typing-inspect = [ {file = "typing_inspect-0.6.0-py2-none-any.whl", hash = "sha256:de08f50a22955ddec353876df7b2545994d6df08a2f45d54ac8c05e530372ca0"}, diff --git a/pyproject.toml b/pyproject.toml index 6cd4f6af2..e1ab84f9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,7 +75,7 @@ pipreqs = "^0.4.9" pip_api = "^0.0.12" numpy = "^1.16.0" pylama = "^7.7" -pip = "^20.0.2" +pip = "^21.1.1" pip-shims = "^0.5.2" smmap2 = "^3.0.1" gitdb2 = "^4.0.2" From a44885a4a243182e0ecc43d516b06e16f3903f21 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 3 May 2021 22:35:33 -0700 Subject: [PATCH 448/539] Bump to latest cruft template --- .cruft.json | 2 +- scripts/lint.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.cruft.json b/.cruft.json index 5d7ba98bd..b471661a3 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/timothycrosley/cookiecutter-python/", - "commit": "3958ca2ed0cad4d65c15912f6ab714720eb528a1", + "commit": "4f2a3601ad12239bf6cbeedba8ecf297690ab07f", "context": { "cookiecutter": { "full_name": "Timothy Crosley", diff --git a/scripts/lint.sh b/scripts/lint.sh index 992027865..ece33be7a 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -7,5 +7,5 @@ poetry run black --target-version py36 --check . poetry run isort --profile hug --check --diff isort/ tests/ poetry run isort --profile hug --check --diff example_isort_formatting_plugin/ poetry run flake8 isort/ tests/ -poetry run safety check -i 39462 +poetry run safety check -i 39462 -i 40291 poetry run bandit -r isort/ -x isort/_vendored From 532741001264cd3370fcdc226bc84f1768df184f Mon Sep 17 00:00:00 2001 From: DanielFEvans <41120183+DanielFEvans@users.noreply.github.com> Date: Fri, 7 May 2021 08:48:53 +0100 Subject: [PATCH 449/539] Update options.md Add note that --atomic is not compatible with Cython. --- docs/configuration/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index 0a0cb8ead..c9d665aeb 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -468,7 +468,7 @@ Order imports by type, which is determined by case, in addition to alphabeticall ## Atomic -Ensures the output doesn't save if the resulting file contains syntax errors. +Ensures the output doesn't save if the resulting file contains syntax errors. This option is not compatible with Cython code. **Type:** Bool **Default:** `False` From b840a33e4802048fecb1ee6a23c3fdc8ce2bec0c Mon Sep 17 00:00:00 2001 From: ruro Date: Sun, 9 May 2021 18:48:06 +0300 Subject: [PATCH 450/539] don't strip colors from non-tty stdout/stderr --- isort/format.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/format.py b/isort/format.py index d08a6a513..ba00f8651 100644 --- a/isort/format.py +++ b/isort/format.py @@ -11,7 +11,7 @@ colorama_unavailable = True else: colorama_unavailable = False - colorama.init() + colorama.init(strip=False) ADDED_LINE_PATTERN = re.compile(r"\+[^+]") From fb36ca3d3ccd59ff844f6b3bff05925666fcd621 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 11 May 2021 22:24:24 -0700 Subject: [PATCH 451/539] Fix issue #1726: isort ignores reverse_sort when force_sort_within_sections is true. --- CHANGELOG.md | 3 ++- isort/output.py | 1 + tests/unit/test_ticketed_features.py | 33 ++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a67e349b..217e2bef9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.9.0 TBD - Fixed (https://github.com/PyCQA/isort/pull/1695) added imports being added to doc string in some cases. - - Fixed (https://github.com/PyCQA/isort/pull/1714) in rare case line continuation combined with tab can output invalid code. + - Fixed (https://github.com/PyCQA/isort/pull/1714) in rare cases line continuation combined with tabs can output invalid code. + - Fixed (https://github.com/PyCQA/isort/pull/1726) isort ignores reverse_sort when force_sort_within_sections is true. - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. diff --git a/isort/output.py b/isort/output.py index 61c2e30ec..fb74ef735 100644 --- a/isort/output.py +++ b/isort/output.py @@ -108,6 +108,7 @@ def sorted_imports( new_section_output = sorting.naturally( new_section_output, key=partial(sorting.section_key, config=config), + reverse=config.reverse_sort, ) # uncollapse comments diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index a0e5e1640..17aaf166b 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -1082,3 +1082,36 @@ def test_isort_can_push_star_imports_above_others_issue_1504(): from ._bar import All, Any, Not """ ) + + +def test_isort_can_combine_reverse_sort_with_force_sort_within_sections_issue_1726(): + """isort should support reversing import order even with force sort within sections turned on. + See: https://github.com/PyCQA/isort/issues/1726 + """ + assert ( + isort.code( + """ +import blaaa +from bl4aaaaaaaaaaaaaaaa import r +import blaaaaaaaaaaaa +import bla +import blaaaaaaa +from bl1aaaaaaaaaaaaaa import this_is_1 +from bl2aaaaaaa import THIIIIIIIIIIIISS_is_2 +from bl3aaaaaa import less +""", + length_sort=True, + reverse_sort=True, + force_sort_within_sections=True, + ) + == """ +from bl2aaaaaaa import THIIIIIIIIIIIISS_is_2 +from bl1aaaaaaaaaaaaaa import this_is_1 +from bl4aaaaaaaaaaaaaaaa import r +from bl3aaaaaa import less +import blaaaaaaaaaaaa +import blaaaaaaa +import blaaa +import bla +""" + ) From 65b6075276a05b96d406a50ec792c43d0ce37110 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 13 May 2021 23:52:21 -0700 Subject: [PATCH 452/539] Add keno (Ken Okada) (@kenoss) and @DanielFEvans to contributors list --- docs/contributing/4.-acknowledgements.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 66a7d3cab..ecb2a5eaf 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -221,6 +221,7 @@ Code Contributors - Jon Banafato (@jonafato) - ruro (@RuRo) - Léni (@legau) +- keno (Ken Okada) (@kenoss) Documenters =================== @@ -247,6 +248,7 @@ Documenters - @hirosassa - David Poznik (@dpoznik) - Mike Frysinger (@vapier) +- @DanielFEvans -------------------------------------------- From 89e1e2cc07e7bf524f5af2c8f1cd3757eced3dfa Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 16 May 2021 21:58:44 -0700 Subject: [PATCH 453/539] Add initial appnexus profile --- isort/profiles.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/isort/profiles.py b/isort/profiles.py index 00ebe418e..21d064630 100644 --- a/isort/profiles.py +++ b/isort/profiles.py @@ -61,6 +61,16 @@ "use_parentheses": True, "line_length": 80, } +appnexus = { + **black, + "force_sort_within_sections": True, + "order_by_type": False, + "case_sensitive": False, + "reverse_relative": True, + "sort_relative_in_force_sorted_sections": True, + "sections": ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "APPLICATION", "LOCALFOLDER"], + "no_lines_before": "LOCALFOLDER", +} profiles: Dict[str, Dict[str, Any]] = { "black": black, @@ -72,4 +82,5 @@ "attrs": attrs, "hug": hug, "wemake": wemake, + "appnexus": appnexus, } From 2f089f44bf5996d0049afa9fdbe0695e585bfafd Mon Sep 17 00:00:00 2001 From: Yusuke Hayashi Date: Sat, 22 May 2021 11:21:16 +0900 Subject: [PATCH 454/539] Fix typo: compatiblity -> compatibility --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bdd86c020..d1005b6ca 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ quickly sort all your imports. It requires Python 3.6+ to run but supports formatting Python 2 code too. - [Try isort now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try/) -- [Using black? See the isort and black compatiblity guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility/) +- [Using black? See the isort and black compatibility guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility/) - [isort has official support for pre-commit!](https://pycqa.github.io/isort/docs/configuration/pre-commit/) ![Example Usage](https://raw.github.com/pycqa/isort/main/example.gif) From d9616916c33bd70b7d481884b161dba06e9b39ff Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 21 May 2021 22:08:52 -0700 Subject: [PATCH 455/539] Fix airflow integration test --- tests/integration/test_projects_using_isort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index 5c172f07c..e9e85cb6d 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -83,7 +83,7 @@ def test_websockets(tmpdir): def test_airflow(tmpdir): git_clone("https://github.com/apache/airflow.git", tmpdir) - run_isort([str(tmpdir)]) + run_isort([str(tmpdir), "--skip-glob", "*/_vendor/*"]) def test_typeshed(tmpdir): From 445be39b3e6a5be35f5889502af0b0e8a27ae4cb Mon Sep 17 00:00:00 2001 From: Tonye Jack Date: Tue, 25 May 2021 15:51:18 -0400 Subject: [PATCH 456/539] Fixed typo --- docs/configuration/black_compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/black_compatibility.md b/docs/configuration/black_compatibility.md index 6e1058ef8..c07b43cf0 100644 --- a/docs/configuration/black_compatibility.md +++ b/docs/configuration/black_compatibility.md @@ -9,7 +9,7 @@ All that's required to use isort alongside black is to set the isort profile to ## Using a config file (such as .isort.cfg) For projects that officially use both isort and black, we recommend setting the black profile in a config file at the root of your project's repository. -This way independent to how users call isort (pre-commit, CLI, or editor integration) the black profile will automatically be applied. +That way it's independent of how users call isort (pre-commit, CLI, or editor integration) the black profile will automatically be applied. For instance, your _pyproject.toml_ file would look something like From 290605491c4b3b342f9dae4a56715ad5a1910106 Mon Sep 17 00:00:00 2001 From: shotat Date: Sun, 30 May 2021 15:32:35 +0900 Subject: [PATCH 457/539] avoid creating tmp files when overwrite-in-place is enabled --- isort/api.py | 65 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/isort/api.py b/isort/api.py index 9deb2c2c2..0561bcf92 100644 --- a/isort/api.py +++ b/isort/api.py @@ -1,3 +1,4 @@ +import contextlib import shutil import sys from enum import Enum @@ -17,7 +18,7 @@ IntroducedSyntaxErrors, ) from .format import ask_whether_to_apply_changes_to_file, create_terminal_printer, show_unified_diff -from .io import Empty +from .io import Empty, File from .place import module as place_module # noqa: F401 from .place import module_with_reason as place_module_with_reason # noqa: F401 from .settings import DEFAULT_CONFIG, Config @@ -305,6 +306,23 @@ def check_file( ) +def _tmp_file(source_file: File) -> Path: + return source_file.path.with_suffix(source_file.path.suffix + ".isorted") + + +@contextlib.contextmanager +def _in_memory_output_stream_context() -> Iterator[TextIO]: + yield StringIO() + + +@contextlib.contextmanager +def _file_output_stream_context(filename: Union[str, Path], source_file: File) -> Iterator[TextIO]: + tmp_file = _tmp_file(source_file) + with tmp_file.open("w+", encoding=source_file.encoding, newline="") as output_stream: + shutil.copymode(filename, tmp_file) + yield output_stream + + def sort_file( filename: Union[str, Path], extension: Optional[str] = None, @@ -349,12 +367,14 @@ def sort_file( ) else: if output is None: - tmp_file = source_file.path.with_suffix(source_file.path.suffix + ".isorted") try: - with tmp_file.open( - "w", encoding=source_file.encoding, newline="" - ) as output_stream: - shutil.copymode(filename, tmp_file) + if config.overwrite_in_place: + output_stream_context = _in_memory_output_stream_context() + else: + output_stream_context = _file_output_stream_context( + filename, source_file + ) + with output_stream_context as output_stream: changed = sort_stream( input_stream=source_file.stream, output_stream=output_stream, @@ -363,15 +383,13 @@ def sort_file( disregard_skip=disregard_skip, extension=extension, ) - if changed: - if show_diff or ask_to_apply: - source_file.stream.seek(0) - with tmp_file.open( - encoding=source_file.encoding, newline="" - ) as tmp_out: + output_stream.seek(0) + if changed: + if show_diff or ask_to_apply: + source_file.stream.seek(0) show_unified_diff( file_input=source_file.stream.read(), - file_output=tmp_out.read(), + file_output=output_stream.read(), file_path=actual_file_path, output=None if show_diff is True @@ -385,16 +403,21 @@ def sort_file( ) ): return False - source_file.stream.close() - if config.overwrite_in_place: - source_file.path.write_bytes(tmp_file.read_bytes()) - else: - tmp_file.replace(source_file.path) - if not config.quiet: - print(f"Fixing {source_file.path}") + source_file.stream.close() + if config.overwrite_in_place: + output_stream.seek(0) + with source_file.path.open("w") as fs: + shutil.copyfileobj(output_stream, fs) + else: + tmp_file = _tmp_file(source_file) + tmp_file.replace(source_file.path) + if not config.quiet: + print(f"Fixing {source_file.path}") finally: try: # Python 3.8+: use `missing_ok=True` instead of try except. - tmp_file.unlink() + if not config.overwrite_in_place: + tmp_file = _tmp_file(source_file) + tmp_file.unlink() except FileNotFoundError: pass # pragma: no cover else: From b4254e7f91afb6863c13c6b4b1914e6f5e287a3c Mon Sep 17 00:00:00 2001 From: shotat Date: Sun, 30 May 2021 20:57:40 +0900 Subject: [PATCH 458/539] add benchmark --- poetry.lock | 62 ++++++++++++++++++++----------------- pyproject.toml | 1 + tests/benchmark/test_api.py | 28 +++++++++++++++++ 3 files changed, 62 insertions(+), 29 deletions(-) create mode 100644 tests/benchmark/test_api.py diff --git a/poetry.lock b/poetry.lock index f20d6b24d..68cf5f563 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1099,6 +1099,14 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "py-cpuinfo" +version = "8.0.0" +description = "Get CPU info with pure Python 2 & 3" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "pycodestyle" version = "2.7.0" @@ -1205,6 +1213,23 @@ toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +[[package]] +name = "pytest-benchmark" +version = "3.4.1" +description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +py-cpuinfo = "*" +pytest = ">=3.8" + +[package.extras] +aspect = ["aspectlib"] +elasticsearch = ["elasticsearch"] +histogram = ["pygal", "pygaljs"] + [[package]] name = "pytest-cov" version = "2.11.1" @@ -1607,7 +1632,7 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "4a46585da4f8437ec280cf081865b4d6c6696a951f88d980c9a0089fdbd4f9e1" +content-hash = "28c343388225d1b49e03dec8079b13e081d14de35bf6ca7d0ec8e3a23e7503bc" [metadata.files] appdirs = [ @@ -1923,7 +1948,6 @@ lunr = [ {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, ] mako = [ - {file = "Mako-1.1.4-py2.py3-none-any.whl", hash = "sha256:aea166356da44b9b830c8023cd9b557fa856bd8b4035d6de771ca027dfc5cc6e"}, {file = "Mako-1.1.4.tar.gz", hash = "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab"}, ] markdown = [ @@ -1949,39 +1973,20 @@ markupsafe = [ {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, ] mccabe = [ @@ -2143,6 +2148,9 @@ py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] +py-cpuinfo = [ + {file = "py-cpuinfo-8.0.0.tar.gz", hash = "sha256:5f269be0e08e33fd959de96b34cd4aeeeacac014dd8305f70eb28d06de2345c5"}, +] pycodestyle = [ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, @@ -2199,6 +2207,10 @@ pytest = [ {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, ] +pytest-benchmark = [ + {file = "pytest-benchmark-3.4.1.tar.gz", hash = "sha256:40e263f912de5a81d891619032983557d62a3d85843f9a9f30b98baea0cd7b47"}, + {file = "pytest_benchmark-3.4.1-py2.py3-none-any.whl", hash = "sha256:36d2b08c4882f6f997fd3126a3d6dfd70f3249cde178ed8bbc0b73db7c20f809"}, +] pytest-cov = [ {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, @@ -2222,26 +2234,18 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, - {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, - {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, - {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, - {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, diff --git a/pyproject.toml b/pyproject.toml index 3be1ec6eb..d0a55026a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,6 +88,7 @@ libcst = "^0.3.18" mypy-extensions = "^0.4.3" py = "^1.10.0" toml = "^0.10.2" +pytest-benchmark = "^3.4.1" [tool.poetry.scripts] isort = "isort.main:main" diff --git a/tests/benchmark/test_api.py b/tests/benchmark/test_api.py new file mode 100644 index 000000000..11698cb02 --- /dev/null +++ b/tests/benchmark/test_api.py @@ -0,0 +1,28 @@ +import pytest +from isort import api + +imperfect_content = "import b\nimport a\n" +fixed_content = "import a\nimport b\n" + + +@pytest.fixture +def imperfect(tmpdir) -> None: + imperfect_file = tmpdir.join("test_needs_changes.py") + imperfect_file.write_text(imperfect_content, "utf8") + return imperfect_file + + +def test_sort_file(benchmark, imperfect) -> None: + def sort_file(): + api.sort_file(imperfect) + + benchmark.pedantic(sort_file, iterations=10, rounds=100) + assert imperfect.read() == fixed_content + + +def test_sort_file_in_place(benchmark, imperfect) -> None: + def sort_file(): + api.sort_file(imperfect, overwrite_in_place=True) + + benchmark.pedantic(sort_file, iterations=10, rounds=100) + assert imperfect.read() == fixed_content From c724fd7fcde55087e088f3db3e5b68bd84ac9e48 Mon Sep 17 00:00:00 2001 From: Timothy Edmund Crosley Date: Mon, 31 May 2021 22:35:32 -0700 Subject: [PATCH 459/539] Update test_api.py --- tests/benchmark/test_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/benchmark/test_api.py b/tests/benchmark/test_api.py index 11698cb02..0d2432d30 100644 --- a/tests/benchmark/test_api.py +++ b/tests/benchmark/test_api.py @@ -1,4 +1,5 @@ import pytest + from isort import api imperfect_content = "import b\nimport a\n" From e34aa0432f415c107f466688b56ed910d1fdac95 Mon Sep 17 00:00:00 2001 From: shotat Date: Tue, 1 Jun 2021 15:40:53 +0900 Subject: [PATCH 460/539] split reader and writer to avoid permission errors --- isort/api.py | 64 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/isort/api.py b/isort/api.py index 0561bcf92..7cc97c987 100644 --- a/isort/api.py +++ b/isort/api.py @@ -310,17 +310,41 @@ def _tmp_file(source_file: File) -> Path: return source_file.path.with_suffix(source_file.path.suffix + ".isorted") -@contextlib.contextmanager -def _in_memory_output_stream_context() -> Iterator[TextIO]: - yield StringIO() +class InMemoryOutputStream: + """Defines output stream using in-memory buffers.""" + def __init__(self) -> None: + self._buffer = StringIO() -@contextlib.contextmanager -def _file_output_stream_context(filename: Union[str, Path], source_file: File) -> Iterator[TextIO]: - tmp_file = _tmp_file(source_file) - with tmp_file.open("w+", encoding=source_file.encoding, newline="") as output_stream: - shutil.copymode(filename, tmp_file) - yield output_stream + @contextlib.contextmanager + def writer(self) -> Iterator[TextIO]: + yield self._buffer + + @contextlib.contextmanager + def reader(self) -> Iterator[TextIO]: + self._buffer.seek(0) + yield self._buffer + + +class TmpFileOutputStream: + """Defines output stream using a tmp file.""" + + def __init__(self, filename: Union[str, Path], source_file: File) -> None: + self._filename = filename + self._source_file = source_file + + @contextlib.contextmanager + def writer(self) -> Iterator[TextIO]: + tmp_file = _tmp_file(self._source_file) + with tmp_file.open("w", encoding=self._source_file.encoding, newline="") as output_stream: + shutil.copymode(self._filename, tmp_file) + yield output_stream + + @contextlib.contextmanager + def reader(self) -> Iterator[TextIO]: + tmp_file = _tmp_file(self._source_file) + with tmp_file.open(encoding=self._source_file.encoding, newline="") as output_stream: + yield output_stream def sort_file( @@ -368,28 +392,27 @@ def sort_file( else: if output is None: try: - if config.overwrite_in_place: - output_stream_context = _in_memory_output_stream_context() - else: - output_stream_context = _file_output_stream_context( - filename, source_file - ) - with output_stream_context as output_stream: + output_stream: Union[InMemoryOutputStream, TmpFileOutputStream] = ( + InMemoryOutputStream() + if config.overwrite_in_place + else TmpFileOutputStream(filename, source_file) + ) + with output_stream.writer() as output_stream_writer: changed = sort_stream( input_stream=source_file.stream, - output_stream=output_stream, + output_stream=output_stream_writer, config=config, file_path=actual_file_path, disregard_skip=disregard_skip, extension=extension, ) - output_stream.seek(0) + with output_stream.reader() as output_stream_reader: if changed: if show_diff or ask_to_apply: source_file.stream.seek(0) show_unified_diff( file_input=source_file.stream.read(), - file_output=output_stream.read(), + file_output=output_stream_reader.read(), file_path=actual_file_path, output=None if show_diff is True @@ -405,9 +428,8 @@ def sort_file( return False source_file.stream.close() if config.overwrite_in_place: - output_stream.seek(0) with source_file.path.open("w") as fs: - shutil.copyfileobj(output_stream, fs) + shutil.copyfileobj(output_stream_reader, fs) else: tmp_file = _tmp_file(source_file) tmp_file.replace(source_file.path) From 29185544f2da5df3c8c627670ea3eaad0770542b Mon Sep 17 00:00:00 2001 From: shotat Date: Tue, 1 Jun 2021 16:28:01 +0900 Subject: [PATCH 461/539] Revert "split reader and writer to avoid permission errors" This reverts commit e34aa0432f415c107f466688b56ed910d1fdac95. --- isort/api.py | 64 +++++++++++++++++----------------------------------- 1 file changed, 21 insertions(+), 43 deletions(-) diff --git a/isort/api.py b/isort/api.py index 7cc97c987..0561bcf92 100644 --- a/isort/api.py +++ b/isort/api.py @@ -310,41 +310,17 @@ def _tmp_file(source_file: File) -> Path: return source_file.path.with_suffix(source_file.path.suffix + ".isorted") -class InMemoryOutputStream: - """Defines output stream using in-memory buffers.""" +@contextlib.contextmanager +def _in_memory_output_stream_context() -> Iterator[TextIO]: + yield StringIO() - def __init__(self) -> None: - self._buffer = StringIO() - @contextlib.contextmanager - def writer(self) -> Iterator[TextIO]: - yield self._buffer - - @contextlib.contextmanager - def reader(self) -> Iterator[TextIO]: - self._buffer.seek(0) - yield self._buffer - - -class TmpFileOutputStream: - """Defines output stream using a tmp file.""" - - def __init__(self, filename: Union[str, Path], source_file: File) -> None: - self._filename = filename - self._source_file = source_file - - @contextlib.contextmanager - def writer(self) -> Iterator[TextIO]: - tmp_file = _tmp_file(self._source_file) - with tmp_file.open("w", encoding=self._source_file.encoding, newline="") as output_stream: - shutil.copymode(self._filename, tmp_file) - yield output_stream - - @contextlib.contextmanager - def reader(self) -> Iterator[TextIO]: - tmp_file = _tmp_file(self._source_file) - with tmp_file.open(encoding=self._source_file.encoding, newline="") as output_stream: - yield output_stream +@contextlib.contextmanager +def _file_output_stream_context(filename: Union[str, Path], source_file: File) -> Iterator[TextIO]: + tmp_file = _tmp_file(source_file) + with tmp_file.open("w+", encoding=source_file.encoding, newline="") as output_stream: + shutil.copymode(filename, tmp_file) + yield output_stream def sort_file( @@ -392,27 +368,28 @@ def sort_file( else: if output is None: try: - output_stream: Union[InMemoryOutputStream, TmpFileOutputStream] = ( - InMemoryOutputStream() - if config.overwrite_in_place - else TmpFileOutputStream(filename, source_file) - ) - with output_stream.writer() as output_stream_writer: + if config.overwrite_in_place: + output_stream_context = _in_memory_output_stream_context() + else: + output_stream_context = _file_output_stream_context( + filename, source_file + ) + with output_stream_context as output_stream: changed = sort_stream( input_stream=source_file.stream, - output_stream=output_stream_writer, + output_stream=output_stream, config=config, file_path=actual_file_path, disregard_skip=disregard_skip, extension=extension, ) - with output_stream.reader() as output_stream_reader: + output_stream.seek(0) if changed: if show_diff or ask_to_apply: source_file.stream.seek(0) show_unified_diff( file_input=source_file.stream.read(), - file_output=output_stream_reader.read(), + file_output=output_stream.read(), file_path=actual_file_path, output=None if show_diff is True @@ -428,8 +405,9 @@ def sort_file( return False source_file.stream.close() if config.overwrite_in_place: + output_stream.seek(0) with source_file.path.open("w") as fs: - shutil.copyfileobj(output_stream_reader, fs) + shutil.copyfileobj(output_stream, fs) else: tmp_file = _tmp_file(source_file) tmp_file.replace(source_file.path) From 2ce07238477ea4cd1fd9aa0e3c9552940bedff92 Mon Sep 17 00:00:00 2001 From: shotat Date: Tue, 1 Jun 2021 16:39:47 +0900 Subject: [PATCH 462/539] replace swap files after closing stream --- isort/api.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/isort/api.py b/isort/api.py index 0561bcf92..f718323d0 100644 --- a/isort/api.py +++ b/isort/api.py @@ -408,11 +408,11 @@ def sort_file( output_stream.seek(0) with source_file.path.open("w") as fs: shutil.copyfileobj(output_stream, fs) - else: - tmp_file = _tmp_file(source_file) - tmp_file.replace(source_file.path) - if not config.quiet: - print(f"Fixing {source_file.path}") + if not config.overwrite_in_place: + tmp_file = _tmp_file(source_file) + tmp_file.replace(source_file.path) + if changed and not config.quiet: + print(f"Fixing {source_file.path}") finally: try: # Python 3.8+: use `missing_ok=True` instead of try except. if not config.overwrite_in_place: From 9db6244d41ad761c2ed8e4d62b0666b9009e3790 Mon Sep 17 00:00:00 2001 From: shotat Date: Tue, 1 Jun 2021 20:32:15 +0900 Subject: [PATCH 463/539] bump pydantic for security --- poetry.lock | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/poetry.lock b/poetry.lock index 68cf5f563..3255f2f56 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1117,7 +1117,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pydantic" -version = "1.7.3" +version = "1.7.4" description = "Data validation and settings management using python 3.6 type hinting" category = "dev" optional = false @@ -2156,28 +2156,28 @@ pycodestyle = [ {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] pydantic = [ - {file = "pydantic-1.7.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c59ea046aea25be14dc22d69c97bee629e6d48d2b2ecb724d7fe8806bf5f61cd"}, - {file = "pydantic-1.7.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a4143c8d0c456a093387b96e0f5ee941a950992904d88bc816b4f0e72c9a0009"}, - {file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:d8df4b9090b595511906fa48deda47af04e7d092318bfb291f4d45dfb6bb2127"}, - {file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:514b473d264671a5c672dfb28bdfe1bf1afd390f6b206aa2ec9fed7fc592c48e"}, - {file = "pydantic-1.7.3-cp36-cp36m-win_amd64.whl", hash = "sha256:dba5c1f0a3aeea5083e75db9660935da90216f8a81b6d68e67f54e135ed5eb23"}, - {file = "pydantic-1.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:59e45f3b694b05a69032a0d603c32d453a23f0de80844fb14d55ab0c6c78ff2f"}, - {file = "pydantic-1.7.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5b24e8a572e4b4c18f614004dda8c9f2c07328cb5b6e314d6e1bbd536cb1a6c1"}, - {file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:b2b054d095b6431cdda2f852a6d2f0fdec77686b305c57961b4c5dd6d863bf3c"}, - {file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:025bf13ce27990acc059d0c5be46f416fc9b293f45363b3d19855165fee1874f"}, - {file = "pydantic-1.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6e3874aa7e8babd37b40c4504e3a94cc2023696ced5a0500949f3347664ff8e2"}, - {file = "pydantic-1.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e682f6442ebe4e50cb5e1cfde7dda6766fb586631c3e5569f6aa1951fd1a76ef"}, - {file = "pydantic-1.7.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:185e18134bec5ef43351149fe34fda4758e53d05bb8ea4d5928f0720997b79ef"}, - {file = "pydantic-1.7.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:f5b06f5099e163295b8ff5b1b71132ecf5866cc6e7f586d78d7d3fd6e8084608"}, - {file = "pydantic-1.7.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:24ca47365be2a5a3cc3f4a26dcc755bcdc9f0036f55dcedbd55663662ba145ec"}, - {file = "pydantic-1.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:d1fe3f0df8ac0f3a9792666c69a7cd70530f329036426d06b4f899c025aca74e"}, - {file = "pydantic-1.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f6864844b039805add62ebe8a8c676286340ba0c6d043ae5dea24114b82a319e"}, - {file = "pydantic-1.7.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ecb54491f98544c12c66ff3d15e701612fc388161fd455242447083350904730"}, - {file = "pydantic-1.7.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:ffd180ebd5dd2a9ac0da4e8b995c9c99e7c74c31f985ba090ee01d681b1c4b95"}, - {file = "pydantic-1.7.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8d72e814c7821125b16f1553124d12faba88e85405b0864328899aceaad7282b"}, - {file = "pydantic-1.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:475f2fa134cf272d6631072554f845d0630907fce053926ff634cc6bc45bf1af"}, - {file = "pydantic-1.7.3-py3-none-any.whl", hash = "sha256:38be427ea01a78206bcaf9a56f835784afcba9e5b88fbdce33bbbfbcd7841229"}, - {file = "pydantic-1.7.3.tar.gz", hash = "sha256:213125b7e9e64713d16d988d10997dabc6a1f73f3991e1ff8e35ebb1409c7dc9"}, + {file = "pydantic-1.7.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3c60039e84552442defbcb5d56711ef0e057028ca7bfc559374917408a88d84e"}, + {file = "pydantic-1.7.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:6e7e314acb170e143c6f3912f93f2ec80a96aa2009ee681356b7ce20d57e5c62"}, + {file = "pydantic-1.7.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:8ef77cd17b73b5ba46788d040c0e820e49a2d80cfcd66fda3ba8be31094fd146"}, + {file = "pydantic-1.7.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:115d8aa6f257a1d469c66b6bfc7aaf04cd87c25095f24542065c68ebcb42fe63"}, + {file = "pydantic-1.7.4-cp36-cp36m-win_amd64.whl", hash = "sha256:66757d4e1eab69a3cfd3114480cc1d72b6dd847c4d30e676ae838c6740fdd146"}, + {file = "pydantic-1.7.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4c92863263e4bd89e4f9cf1ab70d918170c51bd96305fe7b00853d80660acb26"}, + {file = "pydantic-1.7.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3b8154babf30a5e0fa3aa91f188356763749d9b30f7f211fafb247d4256d7877"}, + {file = "pydantic-1.7.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:80cc46378505f7ff202879dcffe4bfbf776c15675028f6e08d1d10bdfbb168ac"}, + {file = "pydantic-1.7.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:dda60d7878a5af2d8560c55c7c47a8908344aa78d32ec1c02d742ede09c534df"}, + {file = "pydantic-1.7.4-cp37-cp37m-win_amd64.whl", hash = "sha256:4c1979d5cc3e14b35f0825caddea5a243dd6085e2a7539c006bc46997ef7a61a"}, + {file = "pydantic-1.7.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8857576600c32aa488f18d30833aa833b54a48e3bab3adb6de97e463af71f8f8"}, + {file = "pydantic-1.7.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1f86d4da363badb39426a0ff494bf1d8510cd2f7274f460eee37bdbf2fd495ec"}, + {file = "pydantic-1.7.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:3ea1256a9e782149381e8200119f3e2edea7cd6b123f1c79ab4bbefe4d9ba2c9"}, + {file = "pydantic-1.7.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:e28455b42a0465a7bf2cde5eab530389226ce7dc779de28d17b8377245982b1e"}, + {file = "pydantic-1.7.4-cp38-cp38-win_amd64.whl", hash = "sha256:47c5b1d44934375a3311891cabd450c150a31cf5c22e84aa172967bf186718be"}, + {file = "pydantic-1.7.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:00250e5123dd0b123ff72be0e1b69140e0b0b9e404d15be3846b77c6f1b1e387"}, + {file = "pydantic-1.7.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d24aa3f7f791a023888976b600f2f389d3713e4f23b7a4c88217d3fce61cdffc"}, + {file = "pydantic-1.7.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:2c44a9afd4c4c850885436a4209376857989aaf0853c7b118bb2e628d4b78c4e"}, + {file = "pydantic-1.7.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:e87edd753da0ca1d44e308a1b1034859ffeab1f4a4492276bff9e1c3230db4fe"}, + {file = "pydantic-1.7.4-cp39-cp39-win_amd64.whl", hash = "sha256:a3026ee105b5360855e500b4abf1a1d0b034d88e75a2d0d66a4c35e60858e15b"}, + {file = "pydantic-1.7.4-py3-none-any.whl", hash = "sha256:a82385c6d5a77e3387e94612e3e34b77e13c39ff1295c26e3ba664e7b98073e2"}, + {file = "pydantic-1.7.4.tar.gz", hash = "sha256:0a1abcbd525fbb52da58c813d54c2ec706c31a91afdb75411a73dd1dec036595"}, ] pydocstyle = [ {file = "pydocstyle-6.0.0-py3-none-any.whl", hash = "sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d"}, From 9d007f158deaebfec18f1e933bde55a3a31bd389 Mon Sep 17 00:00:00 2001 From: shotat Date: Tue, 1 Jun 2021 20:02:10 +0900 Subject: [PATCH 464/539] fix for windows --- isort/api.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/isort/api.py b/isort/api.py index f718323d0..047434632 100644 --- a/isort/api.py +++ b/isort/api.py @@ -312,7 +312,7 @@ def _tmp_file(source_file: File) -> Path: @contextlib.contextmanager def _in_memory_output_stream_context() -> Iterator[TextIO]: - yield StringIO() + yield StringIO(newline=None) @contextlib.contextmanager @@ -408,11 +408,12 @@ def sort_file( output_stream.seek(0) with source_file.path.open("w") as fs: shutil.copyfileobj(output_stream, fs) - if not config.overwrite_in_place: - tmp_file = _tmp_file(source_file) - tmp_file.replace(source_file.path) - if changed and not config.quiet: - print(f"Fixing {source_file.path}") + if changed: + if not config.overwrite_in_place: + tmp_file = _tmp_file(source_file) + tmp_file.replace(source_file.path) + if not config.quiet: + print(f"Fixing {source_file.path}") finally: try: # Python 3.8+: use `missing_ok=True` instead of try except. if not config.overwrite_in_place: From d76cf991e9d3b6dab9ee39e1ac338b5b7bf228fc Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 2 Jun 2021 00:29:30 -0700 Subject: [PATCH 465/539] Work around integration issue with plone test --- tests/integration/test_projects_using_isort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index e9e85cb6d..207f38c40 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -32,7 +32,7 @@ def test_django(tmpdir): def test_plone(tmpdir): git_clone("https://github.com/plone/plone.app.multilingualindexes.git", tmpdir) - run_isort([str(tmpdir / "src")]) + run_isort([str(tmpdir / "src"), "--skip", "languagefallback.py"]) def test_pandas(tmpdir): From bfbbdb638d99d3d0a7ef52a0f0c6d0dc907cf3b4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 10 Jun 2021 01:37:08 -0700 Subject: [PATCH 466/539] Fixed #1741 --- CHANGELOG.md | 7 +- isort/wrap_modes.py | 132 +++++++++++++++++++++++---------- tests/unit/test_isort.py | 11 +-- tests/unit/test_regressions.py | 71 ++++++++++++++++++ 4 files changed, 174 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 217e2bef9..cedf1b9af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,10 @@ NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). ### 5.9.0 TBD - - Fixed (https://github.com/PyCQA/isort/pull/1695) added imports being added to doc string in some cases. - - Fixed (https://github.com/PyCQA/isort/pull/1714) in rare cases line continuation combined with tabs can output invalid code. - - Fixed (https://github.com/PyCQA/isort/pull/1726) isort ignores reverse_sort when force_sort_within_sections is true. + - Fixed (https://github.com/PyCQA/isort/pull/1695): added imports being added to doc string in some cases. + - Fixed (https://github.com/PyCQA/isort/pull/1714): in rare cases line continuation combined with tabs can output invalid code. + - Fixed (https://github.com/PyCQA/isort/pull/1726): isort ignores reverse_sort when force_sort_within_sections is true. + - Fixed (https://github.com/PyCQA/isort/issues/1741): comments in hanging indent modes can lead to invalid code. - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. diff --git a/isort/wrap_modes.py b/isort/wrap_modes.py index 5ced3713c..20ce0b6f7 100644 --- a/isort/wrap_modes.py +++ b/isort/wrap_modes.py @@ -106,64 +106,67 @@ def vertical(**interface): return f"{interface['statement']}({first_import}{_imports}{_comma_maybe})" -def _hanging_indent_common(use_parentheses=False, **interface): +def _hanging_indent_end_line(line: str) -> str: + if not line.endswith(" "): + line += " " + return line + "\\" + + +@_wrap_mode +def hanging_indent(**interface): if not interface["imports"]: return "" - line_length_limit = interface["line_length"] - (1 if use_parentheses else 3) - def end_line(line): - if use_parentheses: - return line - if not line.endswith(" "): - line += " " - return line + "\\" + line_length_limit = interface["line_length"] - 3 - if use_parentheses: - interface["statement"] += "(" next_import = interface["imports"].pop(0) next_statement = interface["statement"] + next_import # Check for first import if len(next_statement) > line_length_limit: next_statement = ( - isort.comments.add_to_line( - interface["comments"], - end_line(interface["statement"]), - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - + f"{interface['line_separator']}{interface['indent']}{next_import}" + _hanging_indent_end_line(interface["statement"]) + + interface["line_separator"] + + interface["indent"] + + next_import ) - interface["comments"] = [] + interface["statement"] = next_statement while interface["imports"]: next_import = interface["imports"].pop(0) - next_statement = isort.comments.add_to_line( + next_statement = interface["statement"] + ", " + next_import + if len(next_statement.split(interface["line_separator"])[-1]) > line_length_limit: + next_statement = ( + _hanging_indent_end_line(interface["statement"] + ",") + + f"{interface['line_separator']}{interface['indent']}{next_import}" + ) + interface["statement"] = next_statement + + interface[ + "statement" + ] = f"{interface['statement']}{',' if interface['include_trailing_comma'] else ''}" + if interface["comments"]: + statement_with_comments = isort.comments.add_to_line( interface["comments"], - interface["statement"] + ", " + next_import, + interface["statement"], removed=interface["remove_comments"], comment_prefix=interface["comment_prefix"], ) - current_line = next_statement.split(interface["line_separator"])[-1] - if len(current_line) > line_length_limit: - next_statement = ( - isort.comments.add_to_line( + if len(statement_with_comments.split(interface["line_separator"])[-1]) <= ( + line_length_limit + 2 + ): + return statement_with_comments + else: + return ( + _hanging_indent_end_line(interface["statement"]) + + interface["line_separator"] + + isort.comments.add_to_line( interface["comments"], - end_line(interface["statement"] + ","), + interface["indent"], removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], + comment_prefix=interface["comment_prefix"].lstrip(), ) - + f"{interface['line_separator']}{interface['indent']}{next_import}" ) - interface["comments"] = [] - interface["statement"] = next_statement - _comma_maybe = "," if interface["include_trailing_comma"] else "" - _close_parentheses_maybe = ")" if use_parentheses else "" - return interface["statement"] + _comma_maybe + _close_parentheses_maybe - - -@_wrap_mode -def hanging_indent(**interface): - return _hanging_indent_common(use_parentheses=False, **interface) + return interface["statement"] @_wrap_mode @@ -309,13 +312,64 @@ def vertical_prefix_from_module_import(**interface): @_wrap_mode def hanging_indent_with_parentheses(**interface): - return _hanging_indent_common(use_parentheses=True, **interface) + if not interface["imports"]: + return "" + + line_length_limit = interface["line_length"] - 1 + + interface["statement"] += "(" + next_import = interface["imports"].pop(0) + next_statement = interface["statement"] + next_import + # Check for first import + if len(next_statement) > line_length_limit: + next_statement = ( + isort.comments.add_to_line( + interface["comments"], + interface["statement"], + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + + f"{interface['line_separator']}{interface['indent']}{next_import}" + ) + interface["comments"] = [] + interface["statement"] = next_statement + while interface["imports"]: + next_import = interface["imports"].pop(0) + if ( + not interface["line_separator"] in interface["statement"] + and "#" in interface["statement"] + ): + line, comments = interface["statement"].split("#", 1) + next_statement = ( + f"{line.rstrip()}, {next_import}{interface['comment_prefix']}{comments}" + ) + else: + next_statement = isort.comments.add_to_line( + interface["comments"], + interface["statement"] + ", " + next_import, + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + current_line = next_statement.split(interface["line_separator"])[-1] + if len(current_line) > line_length_limit: + next_statement = ( + isort.comments.add_to_line( + interface["comments"], + interface["statement"] + ",", + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + + f"{interface['line_separator']}{interface['indent']}{next_import}" + ) + interface["comments"] = [] + interface["statement"] = next_statement + return f"{interface['statement']}{',' if interface['include_trailing_comma'] else ''})" @_wrap_mode def backslash_grid(**interface): interface["indent"] = interface["white_space"][:-1] - return _hanging_indent_common(use_parentheses=False, **interface) + return hanging_indent(**interface) WrapModes = enum.Enum( # type: ignore diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index f847f097f..2cbc93a43 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -338,11 +338,12 @@ def test_output_modes() -> None: indent=" ", ) assert comment_output_hanging_indent == ( - "from third_party import lib1, \\ # comment\n" - " lib2, lib3, lib4, lib5, lib6, \\\n" - " lib7, lib8, lib9, lib10, lib11, \\\n" - " lib12, lib13, lib14, lib15, lib16, \\\n" - " lib17, lib18, lib20, lib21, lib22\n" + "from third_party import lib1, lib2, \\\n" + " lib3, lib4, lib5, lib6, lib7, \\\n" + " lib8, lib9, lib10, lib11, lib12, \\\n" + " lib13, lib14, lib15, lib16, lib17, \\\n" + " lib18, lib20, lib21, lib22 \\\n" + " # comment\n" ) test_output_vertical_indent = isort.code( diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 4d9b9b2b3..615c78875 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -2,6 +2,7 @@ from io import StringIO import isort +import pytest def test_isort_duplicating_comments_issue_1264(): @@ -1651,3 +1652,73 @@ def test_isort_treats_src_paths_same_as_from_config_as_cli_issue_1711(tmpdir): show_diff=True, config=config, ) + + +def test_isort_should_never_quietly_remove_imports_in_hanging_line_mode_issue_1741(): + assert ( + isort.code( + """ +from src import abcd, qwerty, efg, xyz # some comment +""", + line_length=50, + multi_line_output=2, + ) + == """ +from src import abcd, efg, qwerty, xyz \\ + # some comment +""" + ) + assert ( + isort.code( + """ +from src import abcd, qwerty, efg, xyz # some comment +""", + line_length=54, + multi_line_output=2, + ) + == """ +from src import abcd, efg, qwerty, xyz # some comment +""" + ) + assert ( + isort.code( + """ +from src import abcd, qwerty, efg, xyz # some comment +""", + line_length=53, + multi_line_output=2, + ) + == """ +from src import abcd, efg, qwerty, xyz \\ + # some comment +""" + ) + assert ( + isort.code( + """ +from src import abcd, qwerty, efg, xyz # some comment +""", + line_length=30, + multi_line_output=2, + ) + == """ +from src import abcd, efg, \\ + qwerty, xyz \\ + # some comment +""" + ) + + +@pytest.mark.parametrize("multi_line_output", range(12)) +def test_isort_should_never_quietly_remove_imports_in_any_hangin_mode_issue_1741(multi_line_output: int): + sorted_code = isort.code( + """ +from src import abcd, qwerty, efg, xyz # some comment +""", + line_length=30, + multi_line_output=multi_line_output, + ) + assert "abcd" in sorted_code + assert "qwerty" in sorted_code + assert "efg" in sorted_code + assert "xyz" in sorted_code From b2d5c47de6d24cd10b7e985aa6b6ce6c1abc3e10 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 10 Jun 2021 01:49:59 -0700 Subject: [PATCH 467/539] Formatting --- isort/settings.py | 2 +- tests/unit/test_regressions.py | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index bd8e631db..0c16356b7 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -17,7 +17,7 @@ from . import stdlibs from ._future import dataclass, field -from ._vendored import toml +from ._vendored import toml # type: ignore from .exceptions import ( FormattingPluginDoesNotExist, InvalidSettingsPath, diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 615c78875..ea6571518 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1,9 +1,10 @@ """A growing set of tests designed to ensure isort doesn't have regressions in new versions""" from io import StringIO -import isort import pytest +import isort + def test_isort_duplicating_comments_issue_1264(): """Ensure isort doesn't duplicate comments when force_sort_within_sections is set to `True` @@ -1710,14 +1711,16 @@ def test_isort_should_never_quietly_remove_imports_in_hanging_line_mode_issue_17 @pytest.mark.parametrize("multi_line_output", range(12)) -def test_isort_should_never_quietly_remove_imports_in_any_hangin_mode_issue_1741(multi_line_output: int): +def test_isort_should_never_quietly_remove_imports_in_any_hangin_mode_issue_1741( + multi_line_output: int, +): sorted_code = isort.code( - """ + """ from src import abcd, qwerty, efg, xyz # some comment """, - line_length=30, - multi_line_output=multi_line_output, - ) + line_length=30, + multi_line_output=multi_line_output, + ) assert "abcd" in sorted_code assert "qwerty" in sorted_code assert "efg" in sorted_code From 9a4b7f294c543f5306f1a5595044830f85d33b37 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 10 Jun 2021 21:52:15 -0700 Subject: [PATCH 468/539] Bump to latest version of mypy --- isort/output.py | 4 +- poetry.lock | 572 +++++++++++++++++++++--------------------------- pyproject.toml | 3 +- scripts/lint.sh | 2 +- setup.cfg | 4 + 5 files changed, 264 insertions(+), 321 deletions(-) diff --git a/isort/output.py b/isort/output.py index fb74ef735..04db46d06 100644 --- a/isort/output.py +++ b/isort/output.py @@ -608,7 +608,9 @@ def _normalize_empty_lines(lines: List[str]) -> List[str]: class _LineWithComments(str): - def __new__(cls, value, comments): + comments: List[str] + + def __new__(cls, value: str, comments: List[str]): instance = super().__new__(cls, value) # type: ignore instance.comments = comments return instance diff --git a/poetry.lock b/poetry.lock index 3255f2f56..6703e1ff4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -36,17 +36,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "20.3.0" +version = "21.2.0" description = "Classes Without Boilerplate" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] [[package]] name = "backcall" @@ -115,7 +115,7 @@ python-versions = "*" [[package]] name = "cerberus" -version = "1.3.3" +version = "1.3.4" description = "Lightweight, extensible schema and data validation tool for Python dictionaries." category = "main" optional = false @@ -123,7 +123,7 @@ python-versions = ">=2.7" [[package]] name = "certifi" -version = "2020.12.5" +version = "2021.5.30" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -166,7 +166,7 @@ immutables = ">=0.9" [[package]] name = "cookiecutter" -version = "1.7.2" +version = "1.7.3" description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." category = "dev" optional = false @@ -175,9 +175,8 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] binaryornot = ">=0.4.4" click = ">=7.0" -Jinja2 = "<3.0.0" +Jinja2 = ">=2.7,<4.0.0" jinja2-time = ">=0.2.0" -MarkupSafe = "<2.0.0" poyo = ">=0.5.0" python-slugify = ">=4.0.0" requests = ">=2.23.0" @@ -214,7 +213,7 @@ examples = ["examples (>=1.0.2,<2.0.0)"] [[package]] name = "dataclasses" -version = "0.7" +version = "0.8" description = "A backport of the dataclasses module for Python 3.6" category = "dev" optional = false @@ -222,7 +221,7 @@ python-versions = ">=3.6, <3.7" [[package]] name = "decorator" -version = "5.0.7" +version = "5.0.9" description = "Decorators for Humans" category = "dev" optional = false @@ -230,7 +229,7 @@ python-versions = ">=3.5" [[package]] name = "distlib" -version = "0.3.1" +version = "0.3.2" description = "Distribution utilities" category = "main" optional = false @@ -244,14 +243,6 @@ category = "main" optional = false python-versions = "*" -[[package]] -name = "docstring-parser" -version = "0.7.3" -description = "" -category = "dev" -optional = false -python-versions = "~=3.5" - [[package]] name = "dparse" version = "0.5.1" @@ -309,7 +300,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "flake8" -version = "3.9.1" +version = "3.9.2" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false @@ -345,12 +336,18 @@ python-versions = "*" flake8 = "*" [[package]] -name = "future" -version = "0.18.2" -description = "Clean single-source support for Python 3 and 2" +name = "ghp-import" +version = "2.0.1" +description = "Copy your docs directly to the gh-pages branch." category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "*" + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["twine", "markdown", "flake8"] [[package]] name = "gitdb" @@ -376,7 +373,7 @@ gitdb = ">=4.0.1" [[package]] name = "gitpython" -version = "3.1.15" +version = "3.1.17" description = "Python Git Library" category = "dev" optional = false @@ -384,7 +381,7 @@ python-versions = ">=3.5" [package.dependencies] gitdb = ">=4.0.1,<5" -typing-extensions = ">=3.7.4.0" +typing-extensions = {version = ">=3.7.4.0", markers = "python_version < \"3.8\""} [[package]] name = "h11" @@ -474,7 +471,7 @@ python-versions = "*" [[package]] name = "hypothesis" -version = "6.10.1" +version = "6.14.0" description = "A library for property-based testing" category = "dev" optional = false @@ -549,7 +546,7 @@ test = ["flake8 (>=3.8.4,<3.9.0)", "pycodestyle (>=2.6.0,<2.7.0)"] [[package]] name = "importlib-metadata" -version = "4.0.1" +version = "4.5.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -627,17 +624,17 @@ testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"] [[package]] name = "jinja2" -version = "2.11.3" +version = "3.0.1" description = "A very fast and expressive template engine." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [package.dependencies] -MarkupSafe = ">=0.23" +MarkupSafe = ">=2.0" [package.extras] -i18n = ["Babel (>=0.8)"] +i18n = ["Babel (>=2.7)"] [[package]] name = "jinja2-time" @@ -651,14 +648,6 @@ python-versions = "*" arrow = "*" jinja2 = "*" -[[package]] -name = "joblib" -version = "1.0.1" -description = "Lightweight pipelining with Python functions" -category = "dev" -optional = false -python-versions = ">=3.6" - [[package]] name = "lark-parser" version = "0.11.3" @@ -674,7 +663,7 @@ regex = ["regex"] [[package]] name = "libcst" -version = "0.3.18" +version = "0.3.19" description = "A concrete syntax tree with AST-like properties for Python 3.5, 3.6, 3.7 and 3.8 programs." category = "dev" optional = false @@ -689,34 +678,6 @@ typing-inspect = ">=0.4.0" [package.extras] dev = ["black (==20.8b1)", "codecov (>=2.1.4)", "coverage (>=4.5.4)", "fixit (==0.1.1)", "flake8 (>=3.7.8)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "isort (==5.5.3)", "jupyter (>=1.0.0)", "nbsphinx (>=0.4.2)", "pyre-check (==0.0.41)", "sphinx-rtd-theme (>=0.4.3)", "prompt-toolkit (>=2.0.9)", "tox (>=3.18.1)"] -[[package]] -name = "livereload" -version = "2.6.3" -description = "Python LiveReload is an awesome tool for web developers" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" -tornado = {version = "*", markers = "python_version > \"2.7\""} - -[[package]] -name = "lunr" -version = "0.5.8" -description = "A Python implementation of Lunr.js" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -future = ">=0.16.0" -nltk = {version = ">=3.2.5", optional = true, markers = "python_version > \"2.7\" and extra == \"languages\""} -six = ">=1.11.0" - -[package.extras] -languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] - [[package]] name = "mako" version = "1.1.4" @@ -748,11 +709,11 @@ testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" -version = "1.1.1" +version = "2.0.1" description = "Safely add untrusted strings to HTML/XML markup." category = "dev" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +python-versions = ">=3.6" [[package]] name = "mccabe" @@ -762,26 +723,40 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "mkdocs" -version = "1.1.2" +version = "1.2.1" description = "Project documentation with Markdown." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] click = ">=3.3" +ghp-import = ">=1.0" +importlib-metadata = ">=3.10" Jinja2 = ">=2.10.1" -livereload = ">=2.5.1" -lunr = {version = "0.5.8", extras = ["languages"]} Markdown = ">=3.2.1" +mergedeep = ">=1.3.4" +packaging = ">=20.5" PyYAML = ">=3.10" -tornado = ">=5.0" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] [[package]] name = "mkdocs-material" -version = "7.1.3" +version = "5.5.14" description = "A Material Design theme for MkDocs" category = "dev" optional = false @@ -807,7 +782,7 @@ mkdocs-material = ">=5.0.0" [[package]] name = "mypy" -version = "0.761" +version = "0.901" description = "Optional static typing for Python" category = "dev" optional = false @@ -815,11 +790,13 @@ python-versions = ">=3.5" [package.dependencies] mypy-extensions = ">=0.4.3,<0.5.0" -typed-ast = ">=1.4.0,<1.5.0" +toml = "*" +typed-ast = {version = ">=1.4.0,<1.5.0", markers = "python_version < \"3.8\""} typing-extensions = ">=3.7.4" [package.extras] dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<1.5.0)"] [[package]] name = "mypy-extensions" @@ -829,28 +806,6 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "nltk" -version = "3.6.2" -description = "Natural Language Toolkit" -category = "dev" -optional = false -python-versions = ">=3.5.*" - -[package.dependencies] -click = "*" -joblib = "*" -regex = "*" -tqdm = "*" - -[package.extras] -all = ["matplotlib", "twython", "scipy", "numpy", "gensim (<4.0.0)", "python-crfsuite", "pyparsing", "scikit-learn", "requests"] -corenlp = ["requests"] -machine_learning = ["gensim (<4.0.0)", "numpy", "python-crfsuite", "scikit-learn", "scipy"] -plot = ["matplotlib"] -tgrep = ["pyparsing"] -twitter = ["twython"] - [[package]] name = "numpy" version = "1.19.5" @@ -911,15 +866,13 @@ python-versions = ">=2.6" [[package]] name = "pdocs" -version = "1.1.1" +version = "1.0.2" description = "A simple program and library to auto generate API documentation for Python modules." category = "dev" optional = false python-versions = ">=3.6,<4.0" [package.dependencies] -dataclasses = {version = ">=0.7,<0.8", markers = "python_version >= \"3.6\" and python_version < \"3.7\""} -docstring_parser = ">=0.7.2,<0.8.0" hug = ">=2.6,<3.0" Mako = ">=1.1,<2.0" Markdown = ">=3.0.0,<4.0.0" @@ -1047,7 +1000,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "portray" -version = "1.6.0" +version = "1.4.0" description = "Your Project with Great Documentation" category = "dev" optional = false @@ -1056,10 +1009,9 @@ python-versions = ">=3.6,<4.0" [package.dependencies] GitPython = ">=3.0,<4.0" hug = ">=2.6,<3.0" -livereload = ">=2.6.3,<3.0.0" mkdocs = ">=1.0,<2.0" -mkdocs-material = ">=7.0,<8.0" -pdocs = ">=1.1.1,<2.0.0" +mkdocs-material = ">=5.0,<6.0" +pdocs = ">=1.0.2,<2.0.0" pymdown-extensions = ">=7.0,<8.0" toml = ">=0.10.0,<0.11.0" yaspin = ">=0.15.0,<0.16.0" @@ -1133,7 +1085,7 @@ typing_extensions = ["typing-extensions (>=3.7.2)"] [[package]] name = "pydocstyle" -version = "6.0.0" +version = "6.1.1" description = "Python docstring style checker" category = "dev" optional = false @@ -1142,6 +1094,9 @@ python-versions = ">=3.6" [package.dependencies] snowballstemmer = "*" +[package.extras] +toml = ["toml"] + [[package]] name = "pyflakes" version = "2.3.1" @@ -1193,7 +1148,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pytest" -version = "6.2.3" +version = "6.2.4" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -1232,7 +1187,7 @@ histogram = ["pygal", "pygaljs"] [[package]] name = "pytest-cov" -version = "2.11.1" +version = "2.12.1" description = "Pytest plugin for measuring coverage." category = "dev" optional = false @@ -1241,9 +1196,10 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] coverage = ">=5.2.1" pytest = ">=4.6" +toml = "*" [package.extras] -testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-mock" @@ -1272,11 +1228,11 @@ six = ">=1.5" [[package]] name = "python-slugify" -version = "5.0.0" +version = "5.0.2" description = "A Python Slugify application that handles Unicode" category = "dev" optional = false -python-versions = "!=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.6" [package.dependencies] text-unidecode = ">=1.3" @@ -1292,6 +1248,17 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyyaml = "*" + [[package]] name = "regex" version = "2021.4.4" @@ -1349,7 +1316,7 @@ typing = ["typing", "mypy", "mypy-extensions", "mypytools", "pytype", "typed-ast [[package]] name = "rfc3986" -version = "1.4.0" +version = "1.5.0" description = "Validating URI References per RFC 3986" category = "dev" optional = false @@ -1374,7 +1341,7 @@ requests = "*" [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "main" optional = false @@ -1420,7 +1387,7 @@ python-versions = "*" [[package]] name = "sortedcontainers" -version = "2.3.0" +version = "2.4.0" description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" category = "dev" optional = false @@ -1456,33 +1423,12 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tomlkit" -version = "0.7.0" +version = "0.7.2" description = "Style preserving TOML library" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -[[package]] -name = "tornado" -version = "6.1" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "dev" -optional = false -python-versions = ">= 3.5" - -[[package]] -name = "tqdm" -version = "4.60.0" -description = "Fast, Extensible Progress Meter" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" - -[package.extras] -dev = ["py-make (>=0.1.0)", "twine", "wheel"] -notebook = ["ipywidgets (>=6)"] -telegram = ["requests"] - [[package]] name = "traitlets" version = "4.3.3" @@ -1524,6 +1470,14 @@ all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)"] +[[package]] +name = "types-pkg-resources" +version = "0.1.2" +description = "Typing stubs for pkg_resources" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "typing-extensions" version = "3.10.0.0" @@ -1534,7 +1488,7 @@ python-versions = "*" [[package]] name = "typing-inspect" -version = "0.6.0" +version = "0.7.1" description = "Runtime inspection utilities for typing module." category = "dev" optional = false @@ -1546,16 +1500,16 @@ typing-extensions = ">=3.7.4" [[package]] name = "urllib3" -version = "1.26.4" +version = "1.26.5" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] +brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -brotli = ["brotlipy (>=0.6.0)"] [[package]] name = "vistir" @@ -1584,6 +1538,17 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "watchdog" +version = "2.1.2" +description = "Filesystem events monitoring" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +watchmedo = ["PyYAML (>=3.10)", "argh (>=0.24.1)"] + [[package]] name = "wcwidth" version = "0.2.5" @@ -1632,7 +1597,7 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "28c343388225d1b49e03dec8079b13e081d14de35bf6ca7d0ec8e3a23e7503bc" +content-hash = "d1f7b33a4c7b1c6ae5a9787923d50801c745b0f3ed01248d6025214f413f79b5" [metadata.files] appdirs = [ @@ -1652,8 +1617,8 @@ atomicwrites = [ {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, - {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] backcall = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, @@ -1675,12 +1640,11 @@ cached-property = [ {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, ] cerberus = [ - {file = "Cerberus-1.3.3-py3-none-any.whl", hash = "sha256:7aff49bc793e58a88ac14bffc3eca0f67e077881d3c62c621679a621294dd174"}, - {file = "Cerberus-1.3.3.tar.gz", hash = "sha256:eec10585c33044fb7c69650bc5b68018dac0443753337e2b07684ee0f3c83329"}, + {file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"}, ] certifi = [ - {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, - {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, + {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, + {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, ] chardet = [ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, @@ -1698,8 +1662,8 @@ contextvars = [ {file = "contextvars-2.4.tar.gz", hash = "sha256:f38c908aaa59c14335eeea12abea5f443646216c4e29380d7bf34d2018e2c39e"}, ] cookiecutter = [ - {file = "cookiecutter-1.7.2-py2.py3-none-any.whl", hash = "sha256:430eb882d028afb6102c084bab6cf41f6559a77ce9b18dc6802e3bc0cc5f4a30"}, - {file = "cookiecutter-1.7.2.tar.gz", hash = "sha256:efb6b2d4780feda8908a873e38f0e61778c23f6a2ea58215723bcceb5b515dac"}, + {file = "cookiecutter-1.7.3-py2.py3-none-any.whl", hash = "sha256:f8671531fa96ab14339d0c59b4f662a4f12a2ecacd94a0f70a3500843da588e2"}, + {file = "cookiecutter-1.7.3.tar.gz", hash = "sha256:6b9a4d72882e243be077a7397d0f1f76fe66cf3df91f3115dbb5330e214fa457"}, ] coverage = [ {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, @@ -1713,9 +1677,6 @@ coverage = [ {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, - {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, - {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, - {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, @@ -1760,23 +1721,20 @@ cruft = [ {file = "cruft-2.8.0.tar.gz", hash = "sha256:29e6c95ed46bda8d26b9666e78bdcd625f3a2352620fc52c44dc4ff64a298ce0"}, ] dataclasses = [ - {file = "dataclasses-0.7-py3-none-any.whl", hash = "sha256:3459118f7ede7c8bea0fe795bff7c6c2ce287d01dd226202f7c9ebc0610a7836"}, - {file = "dataclasses-0.7.tar.gz", hash = "sha256:494a6dcae3b8bcf80848eea2ef64c0cc5cd307ffc263e17cdf42f3e5420808e6"}, + {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, + {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, ] decorator = [ - {file = "decorator-5.0.7-py3-none-any.whl", hash = "sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"}, - {file = "decorator-5.0.7.tar.gz", hash = "sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060"}, + {file = "decorator-5.0.9-py3-none-any.whl", hash = "sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323"}, + {file = "decorator-5.0.9.tar.gz", hash = "sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5"}, ] distlib = [ - {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, - {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, + {file = "distlib-0.3.2-py2.py3-none-any.whl", hash = "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"}, + {file = "distlib-0.3.2.zip", hash = "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736"}, ] docopt = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] -docstring-parser = [ - {file = "docstring_parser-0.7.3.tar.gz", hash = "sha256:cde5fbf8b846433dfbde1e0f96b7f909336a634d5df34a38cb75050c7346734a"}, -] dparse = [ {file = "dparse-0.5.1-py3-none-any.whl", hash = "sha256:e953a25e44ebb60a5c6efc2add4420c177f1d8404509da88da9729202f306994"}, {file = "dparse-0.5.1.tar.gz", hash = "sha256:a1b5f169102e1c894f9a7d5ccf6f9402a836a5d24be80a986c7ce9eaed78f367"}, @@ -1810,8 +1768,8 @@ falcon = [ {file = "falcon-2.0.0.tar.gz", hash = "sha256:eea593cf466b9c126ce667f6d30503624ef24459f118c75594a69353b6c3d5fc"}, ] flake8 = [ - {file = "flake8-3.9.1-py2.py3-none-any.whl", hash = "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a"}, - {file = "flake8-3.9.1.tar.gz", hash = "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378"}, + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] flake8-bugbear = [ {file = "flake8-bugbear-19.8.0.tar.gz", hash = "sha256:d8c466ea79d5020cb20bf9f11cf349026e09517a42264f313d3f6fddb83e0571"}, @@ -1821,8 +1779,8 @@ flake8-polyfill = [ {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, ] -future = [ - {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, +ghp-import = [ + {file = "ghp-import-2.0.1.tar.gz", hash = "sha256:753de2eace6e0f7d4edfb3cce5e3c3b98cd52aadb80163303d1d036bda7b4483"}, ] gitdb = [ {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"}, @@ -1833,8 +1791,8 @@ gitdb2 = [ {file = "gitdb2-4.0.2.tar.gz", hash = "sha256:0986cb4003de743f2b3aba4c828edd1ab58ce98e1c4a8acf72ef02760d4beb4e"}, ] gitpython = [ - {file = "GitPython-3.1.15-py3-none-any.whl", hash = "sha256:a77824e516d3298b04fb36ec7845e92747df8fcfee9cacc32dd6239f9652f867"}, - {file = "GitPython-3.1.15.tar.gz", hash = "sha256:05af150f47a5cca3f4b0af289b73aef8cf3c4fe2385015b06220cbcdee48bb6e"}, + {file = "GitPython-3.1.17-py3-none-any.whl", hash = "sha256:29fe82050709760081f588dd50ce83504feddbebdc4da6956d02351552b1c135"}, + {file = "GitPython-3.1.17.tar.gz", hash = "sha256:ee24bdc93dce357630764db659edaf6b8d664d4ff5447ccfeedd2dc5c253f41e"}, ] h11 = [ {file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"}, @@ -1869,8 +1827,8 @@ hyperframe = [ {file = "hyperframe-5.2.0.tar.gz", hash = "sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"}, ] hypothesis = [ - {file = "hypothesis-6.10.1-py3-none-any.whl", hash = "sha256:1d65f58d82d1cbd35b6441bda3bb11cb1adc879d6b2af191aea388fa412171b1"}, - {file = "hypothesis-6.10.1.tar.gz", hash = "sha256:586b6c46e90878c2546743afbed348bca51e1f30e3461fa701fad58c2c47c650"}, + {file = "hypothesis-6.14.0-py3-none-any.whl", hash = "sha256:27aa2af763af06b8b61ce65c09626cf1da6d3a6ff155900f3c581837b453313a"}, + {file = "hypothesis-6.14.0.tar.gz", hash = "sha256:9bdee01ae260329b16117e9b0229a839b4a77747a985922653f595bd2a6a541a"}, ] hypothesis-auto = [ {file = "hypothesis-auto-1.1.4.tar.gz", hash = "sha256:5e2c2fb09dc09842512d80630bb792359a1d33d2c0473ad47ee23da0be9e32b1"}, @@ -1902,8 +1860,8 @@ immutables = [ {file = "immutables-0.15.tar.gz", hash = "sha256:3713ab1ebbb6946b7ce1387bb9d1d7f5e09c45add58c2a2ee65f963c171e746b"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, - {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, + {file = "importlib_metadata-4.5.0-py3-none-any.whl", hash = "sha256:833b26fb89d5de469b24a390e9df088d4e52e4ba33b01dc5e0e4f41b81a16c00"}, + {file = "importlib_metadata-4.5.0.tar.gz", hash = "sha256:b142cc1dd1342f31ff04bb7d022492b09920cb64fed867cd3ea6f80fe3ebd139"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -1922,30 +1880,19 @@ jedi = [ {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, ] jinja2 = [ - {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, - {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, + {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"}, + {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"}, ] jinja2-time = [ {file = "jinja2-time-0.2.0.tar.gz", hash = "sha256:d14eaa4d315e7688daa4969f616f226614350c48730bfa1692d2caebd8c90d40"}, {file = "jinja2_time-0.2.0-py2.py3-none-any.whl", hash = "sha256:d3eab6605e3ec8b7a0863df09cc1d23714908fa61aa6986a845c20ba488b4efa"}, ] -joblib = [ - {file = "joblib-1.0.1-py3-none-any.whl", hash = "sha256:feeb1ec69c4d45129954f1b7034954241eedfd6ba39b5e9e4b6883be3332d5e5"}, - {file = "joblib-1.0.1.tar.gz", hash = "sha256:9c17567692206d2f3fb9ecf5e991084254fe631665c450b443761c4186a613f7"}, -] lark-parser = [ {file = "lark-parser-0.11.3.tar.gz", hash = "sha256:e29ca814a98bb0f81674617d878e5f611cb993c19ea47f22c80da3569425f9bd"}, ] libcst = [ - {file = "libcst-0.3.18-py3-none-any.whl", hash = "sha256:da89cc1a37702caa6fe7207b1257fad58f0d4643597279733106ca902b4fdbad"}, - {file = "libcst-0.3.18.tar.gz", hash = "sha256:30154cd0aaede8f3adfc4bdead23fe022a57e88898b9993cc3fea3bfbaf780d2"}, -] -livereload = [ - {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, -] -lunr = [ - {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"}, - {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, + {file = "libcst-0.3.19-py3-none-any.whl", hash = "sha256:9e26313ded6e17605658b93319299bce43cc8d7e24dafc12d6f782f758a3faf4"}, + {file = "libcst-0.3.19.tar.gz", hash = "sha256:4876239db55164acaf034ee01f56a7db0a2f90cacea24b183d8aa69efc11b067"}, ] mako = [ {file = "Mako-1.1.4.tar.gz", hash = "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab"}, @@ -1955,80 +1902,90 @@ markdown = [ {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, ] markupsafe = [ - {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, - {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, + {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] +mergedeep = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] mkdocs = [ - {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, - {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, + {file = "mkdocs-1.2.1-py3-none-any.whl", hash = "sha256:11141126e5896dd9d279b3e4814eb488e409a0990fb638856255020406a8e2e7"}, + {file = "mkdocs-1.2.1.tar.gz", hash = "sha256:6e0ea175366e3a50d334597b0bc042b8cebd512398cdd3f6f34842d0ef524905"}, ] mkdocs-material = [ - {file = "mkdocs-material-7.1.3.tar.gz", hash = "sha256:e34bba93ad1a0e6f9afc371f4ef55bedabbf13b9a786b013b0ce26ac55ec2932"}, - {file = "mkdocs_material-7.1.3-py2.py3-none-any.whl", hash = "sha256:437638b0de7a9113d7f1c9ddc93c0a29a3b808c71c3606713d8c1fa437697a3e"}, + {file = "mkdocs-material-5.5.14.tar.gz", hash = "sha256:9f3237df1a72f91e0330a5e3b3711cb7aaa0d5705f9585e6ce6fbacaa16e777f"}, + {file = "mkdocs_material-5.5.14-py2.py3-none-any.whl", hash = "sha256:a0b3b3e67606e04d13e777d13f3195402ea09e0c3ce279abc3666cac2c5b3a6d"}, ] mkdocs-material-extensions = [ {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, {file = "mkdocs_material_extensions-1.0.1-py3-none-any.whl", hash = "sha256:d90c807a88348aa6d1805657ec5c0b2d8d609c110e62b9dce4daf7fa981fa338"}, ] mypy = [ - {file = "mypy-0.761-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:7f672d02fffcbace4db2b05369142e0506cdcde20cea0e07c7c2171c4fd11dd6"}, - {file = "mypy-0.761-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:87c556fb85d709dacd4b4cb6167eecc5bbb4f0a9864b69136a0d4640fdc76a36"}, - {file = "mypy-0.761-cp35-cp35m-win_amd64.whl", hash = "sha256:c6d27bd20c3ba60d5b02f20bd28e20091d6286a699174dfad515636cb09b5a72"}, - {file = "mypy-0.761-cp36-cp36m-macosx_10_6_x86_64.whl", hash = "sha256:4b9365ade157794cef9685791032521233729cb00ce76b0ddc78749abea463d2"}, - {file = "mypy-0.761-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:634aef60b4ff0f650d3e59d4374626ca6153fcaff96ec075b215b568e6ee3cb0"}, - {file = "mypy-0.761-cp36-cp36m-win_amd64.whl", hash = "sha256:53ea810ae3f83f9c9b452582261ea859828a9ed666f2e1ca840300b69322c474"}, - {file = "mypy-0.761-cp37-cp37m-macosx_10_6_x86_64.whl", hash = "sha256:0a9a45157e532da06fe56adcfef8a74629566b607fa2c1ac0122d1ff995c748a"}, - {file = "mypy-0.761-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:7eadc91af8270455e0d73565b8964da1642fe226665dd5c9560067cd64d56749"}, - {file = "mypy-0.761-cp37-cp37m-win_amd64.whl", hash = "sha256:e2bb577d10d09a2d8822a042a23b8d62bc3b269667c9eb8e60a6edfa000211b1"}, - {file = "mypy-0.761-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c35cae79ceb20d47facfad51f952df16c2ae9f45db6cb38405a3da1cf8fc0a7"}, - {file = "mypy-0.761-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f97a605d7c8bc2c6d1172c2f0d5a65b24142e11a58de689046e62c2d632ca8c1"}, - {file = "mypy-0.761-cp38-cp38-win_amd64.whl", hash = "sha256:a6bd44efee4dc8c3324c13785a9dc3519b3ee3a92cada42d2b57762b7053b49b"}, - {file = "mypy-0.761-py3-none-any.whl", hash = "sha256:7e396ce53cacd5596ff6d191b47ab0ea18f8e0ec04e15d69728d530e86d4c217"}, - {file = "mypy-0.761.tar.gz", hash = "sha256:85baab8d74ec601e86134afe2bcccd87820f79d2f8d5798c889507d1088287bf"}, + {file = "mypy-0.901-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:91211acf1485a1db0b1261bc5f9ed450cba3c0dfd8da0a6680e94827591e34d7"}, + {file = "mypy-0.901-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c8bc628961cca4335ac7d1f2ed59b7125d9252fe4c78c3d66d30b50162359c99"}, + {file = "mypy-0.901-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:4a622faa3be76114cdce009f8ec173401494cf9e8f22713e7ae75fee9d906ab3"}, + {file = "mypy-0.901-cp35-cp35m-win_amd64.whl", hash = "sha256:8183561bfd950e93eeab8379ae5ec65873c856f5b58498d23aa8691f74c86030"}, + {file = "mypy-0.901-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:da914faaa80c25f463913da6db12adba703822a768f452f29f75b40bb4357139"}, + {file = "mypy-0.901-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:307a6c047596d768c3d689734307e47a91596eb9dbb67cfdf7d1fd9117b27f13"}, + {file = "mypy-0.901-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a85c6759dcc6a9884131fa06a037bd34352aa3947e7f5d9d5a35652cc3a44bcd"}, + {file = "mypy-0.901-cp36-cp36m-win_amd64.whl", hash = "sha256:9941b685807b60c58020bb67b3217c9df47820dcd00425f55cdf71f31d3c42d9"}, + {file = "mypy-0.901-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:08cf1f31029612e1008a9432337ca4b1fbac989ff7c8200e2c9ec42705cd4c7b"}, + {file = "mypy-0.901-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bc61153eb4df769538bb4a6e1045f59c2e6119339690ec719feeacbfc3809e89"}, + {file = "mypy-0.901-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:1cd241966a35036f936d4739bd71a1c64e15f02bf7d12bb2815cccfb2993a9de"}, + {file = "mypy-0.901-cp37-cp37m-win_amd64.whl", hash = "sha256:97be0e8ed116f7f79472a49cf06dd45dd806771142401f684d4f13ee652a63c0"}, + {file = "mypy-0.901-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79beb6741df15395908ecc706b3a593a98804c1d5b5b6bd0c5b03b67c7ac03a0"}, + {file = "mypy-0.901-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf347c327c48d963bdef5bf365215d3e98b5fddbe5069fc796cec330e8235a20"}, + {file = "mypy-0.901-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:053b92ebae901fc7954677949049f70133f2f63e3e83dc100225c26d6a46fe95"}, + {file = "mypy-0.901-cp38-cp38-win_amd64.whl", hash = "sha256:f208cc967e566698c4e30a1f65843fc88d8da05a8693bac8b975417e0aee9ced"}, + {file = "mypy-0.901-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c86e3f015bfe7958646825d41c0691c6e5a5cd4015e3409b5c29c18a3c712534"}, + {file = "mypy-0.901-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8577d30daf1b7b6582020f539f76e78ee1ed64a0323b28c8e0333c45db9369f"}, + {file = "mypy-0.901-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:5ddd8f4096d5fc2e7d7bb3924ac22758862163ad2c1cdc902c4b85568160e90a"}, + {file = "mypy-0.901-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:4b54518e399c3f4dc53380d4252c83276b2e60623cfc5274076eb8aae57572ac"}, + {file = "mypy-0.901-cp39-cp39-win_amd64.whl", hash = "sha256:7845ad3a31407bfbd64c76d032c16ab546d282930f747023bf07c17b054bebc5"}, + {file = "mypy-0.901-py3-none-any.whl", hash = "sha256:61b10ba18a01d05fc46adbf4f18b0e92178f6b5fd0f45926ffc2a408b5419728"}, + {file = "mypy-0.901.tar.gz", hash = "sha256:18753a8bb9bcf031ff10009852bd48d781798ecbccf45be5449892e6af4e3f9f"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] -nltk = [ - {file = "nltk-3.6.2-py3-none-any.whl", hash = "sha256:240e23ab1ab159ef9940777d30c7c72d7e76d91877099218a7585370c11f6b9e"}, - {file = "nltk-3.6.2.zip", hash = "sha256:57d556abed621ab9be225cc6d2df1edce17572efb67a3d754630c9f8381503eb"}, -] numpy = [ {file = "numpy-1.19.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff"}, {file = "numpy-1.19.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aeb9ed923be74e659984e321f609b9ba54a48354bfd168d21a2b072ed1e833ea"}, @@ -2086,8 +2043,8 @@ pbr = [ {file = "pbr-5.6.0.tar.gz", hash = "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd"}, ] pdocs = [ - {file = "pdocs-1.1.1-py3-none-any.whl", hash = "sha256:4f5116cf5ce0fa9f13171cd74db224636d4d71370115eefce22d8945526fcfc0"}, - {file = "pdocs-1.1.1.tar.gz", hash = "sha256:f148034970220c9e05d2e04d8eb3fcec3575cf480af0966123ef9d6621b46e4f"}, + {file = "pdocs-1.0.2-py3-none-any.whl", hash = "sha256:4d5ff87babcd0c46f12b76c887d53225bddb389dee7c6b338dbe281c729fc035"}, + {file = "pdocs-1.0.2.tar.gz", hash = "sha256:2e32432bd2736fd678ac1ce4447cd508deb62b5a12f7ba3bf0e3a374063221e2"}, ] pep517 = [ {file = "pep517-0.10.0-py2.py3-none-any.whl", hash = "sha256:eba39d201ef937584ad3343df3581069085bacc95454c80188291d5b3ac7a249"}, @@ -2129,8 +2086,8 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] portray = [ - {file = "portray-1.6.0-py3-none-any.whl", hash = "sha256:592d6f0851dc585b49044d7a61bb6b5b26b3e9693ac06d97e27b86d6e00e9113"}, - {file = "portray-1.6.0.tar.gz", hash = "sha256:7d4b7b58c4964833c704eb5fbdf801aede6f72ea26603d6c2b58e16448fe93d9"}, + {file = "portray-1.4.0-py3-none-any.whl", hash = "sha256:a6a06042a6b7fcb876b1e6cdcaee5adaeb6751388cb292fc05b2f31b1a4c3fb2"}, + {file = "portray-1.4.0.tar.gz", hash = "sha256:ea2271c5e3fbe956070a6f8b1aee6dc3d6a66c18c11907e878db8faa6fd2c449"}, ] poyo = [ {file = "poyo-0.5.0-py2.py3-none-any.whl", hash = "sha256:3e2ca8e33fdc3c411cd101ca395668395dd5dc7ac775b8e809e3def9f9fe041a"}, @@ -2180,8 +2137,8 @@ pydantic = [ {file = "pydantic-1.7.4.tar.gz", hash = "sha256:0a1abcbd525fbb52da58c813d54c2ec706c31a91afdb75411a73dd1dec036595"}, ] pydocstyle = [ - {file = "pydocstyle-6.0.0-py3-none-any.whl", hash = "sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d"}, - {file = "pydocstyle-6.0.0.tar.gz", hash = "sha256:164befb520d851dbcf0e029681b91f4f599c62c5cd8933fd54b1bfbd50e89e1f"}, + {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, + {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, ] pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, @@ -2204,16 +2161,16 @@ pyparsing = [ {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ - {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, - {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, + {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, + {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, ] pytest-benchmark = [ {file = "pytest-benchmark-3.4.1.tar.gz", hash = "sha256:40e263f912de5a81d891619032983557d62a3d85843f9a9f30b98baea0cd7b47"}, {file = "pytest_benchmark-3.4.1-py2.py3-none-any.whl", hash = "sha256:36d2b08c4882f6f997fd3126a3d6dfd70f3249cde178ed8bbc0b73db7c20f809"}, ] pytest-cov = [ - {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, - {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, + {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, + {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"}, ] pytest-mock = [ {file = "pytest-mock-1.13.0.tar.gz", hash = "sha256:e24a911ec96773022ebcc7030059b57cd3480b56d4f5d19b7c370ec635e6aed5"}, @@ -2224,8 +2181,8 @@ python-dateutil = [ {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, ] python-slugify = [ - {file = "python-slugify-5.0.0.tar.gz", hash = "sha256:744cd5d42b381687657f3b652d1d9b183c0870ec43de23be682b6d83d69c6962"}, - {file = "python_slugify-5.0.0-py2.py3-none-any.whl", hash = "sha256:2a497206413bebbab1d9004c44443b71f65a402653446af7246904ad1c0927d3"}, + {file = "python-slugify-5.0.2.tar.gz", hash = "sha256:f13383a0b9fcbe649a1892b9c8eb4f8eab1d6d84b84bb7a624317afa98159cab"}, + {file = "python_slugify-5.0.2-py2.py3-none-any.whl", hash = "sha256:6d8c5df75cd4a7c3a2d21e257633de53f52ab0265cd2d1dc62a730e8194a7380"}, ] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, @@ -2250,6 +2207,10 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] +pyyaml-env-tag = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] regex = [ {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"}, {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"}, @@ -2302,16 +2263,16 @@ requirementslib = [ {file = "requirementslib-1.5.16.tar.gz", hash = "sha256:9c1e8666ca4512724cdd1739adcc7df19ec7ad2ed21f0e748f9631ad6b54f321"}, ] rfc3986 = [ - {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"}, - {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"}, + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, ] safety = [ {file = "safety-1.10.3-py2.py3-none-any.whl", hash = "sha256:5f802ad5df5614f9622d8d71fedec2757099705c2356f862847c58c6dfe13e84"}, {file = "safety-1.10.3.tar.gz", hash = "sha256:30e394d02a20ac49b7f65292d19d38fa927a8f9582cdfd3ad1adbbc66c641ad5"}, ] six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] smmap = [ {file = "smmap-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"}, @@ -2330,8 +2291,8 @@ snowballstemmer = [ {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] sortedcontainers = [ - {file = "sortedcontainers-2.3.0-py2.py3-none-any.whl", hash = "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f"}, - {file = "sortedcontainers-2.3.0.tar.gz", hash = "sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1"}, + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] stevedore = [ {file = "stevedore-3.3.0-py3-none-any.whl", hash = "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"}, @@ -2346,55 +2307,8 @@ toml = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tomlkit = [ - {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"}, - {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"}, -] -tornado = [ - {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, - {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, - {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, - {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, - {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, - {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, - {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, - {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, - {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, - {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, - {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, - {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, - {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, - {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, - {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, - {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, - {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, - {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, -] -tqdm = [ - {file = "tqdm-4.60.0-py2.py3-none-any.whl", hash = "sha256:daec693491c52e9498632dfbe9ccfc4882a557f5fa08982db1b4d3adbe0887c3"}, - {file = "tqdm-4.60.0.tar.gz", hash = "sha256:ebdebdb95e3477ceea267decfc0784859aa3df3e27e22d23b83e9b272bf157ae"}, + {file = "tomlkit-0.7.2-py2.py3-none-any.whl", hash = "sha256:173ad840fa5d2aac140528ca1933c29791b79a374a0861a80347f42ec9328117"}, + {file = "tomlkit-0.7.2.tar.gz", hash = "sha256:d7a454f319a7e9bd2e249f239168729327e4dd2d27b17dc68be264ad1ce36754"}, ] traitlets = [ {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, @@ -2436,19 +2350,22 @@ typer = [ {file = "typer-0.3.2-py3-none-any.whl", hash = "sha256:ba58b920ce851b12a2d790143009fa00ac1d05b3ff3257061ff69dbdfc3d161b"}, {file = "typer-0.3.2.tar.gz", hash = "sha256:5455d750122cff96745b0dec87368f56d023725a7ebc9d2e54dd23dc86816303"}, ] +types-pkg-resources = [ + {file = "types_pkg_resources-0.1.2-py2.py3-none-any.whl", hash = "sha256:42d640500de564f1ccc21f918117afadf78039e4fa7f513c647ccf742d609aeb"}, +] typing-extensions = [ {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, ] typing-inspect = [ - {file = "typing_inspect-0.6.0-py2-none-any.whl", hash = "sha256:de08f50a22955ddec353876df7b2545994d6df08a2f45d54ac8c05e530372ca0"}, - {file = "typing_inspect-0.6.0-py3-none-any.whl", hash = "sha256:3b98390df4d999a28cf5b35d8b333425af5da2ece8a4ea9e98f71e7591347b4f"}, - {file = "typing_inspect-0.6.0.tar.gz", hash = "sha256:8f1b1dd25908dbfd81d3bebc218011531e7ab614ba6e5bf7826d887c834afab7"}, + {file = "typing_inspect-0.7.1-py2-none-any.whl", hash = "sha256:b1f56c0783ef0f25fb064a01be6e5407e54cf4a4bf4f3ba3fe51e0bd6dcea9e5"}, + {file = "typing_inspect-0.7.1-py3-none-any.whl", hash = "sha256:3cd7d4563e997719a710a3bfe7ffb544c6b72069b6812a02e9b414a8fa3aaa6b"}, + {file = "typing_inspect-0.7.1.tar.gz", hash = "sha256:047d4097d9b17f46531bf6f014356111a1b6fb821a24fe7ac909853ca2a782aa"}, ] urllib3 = [ - {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, - {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, + {file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"}, + {file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"}, ] vistir = [ {file = "vistir-0.5.2-py2.py3-none-any.whl", hash = "sha256:a37079cdbd85d31a41cdd18457fe521e15ec08b255811e81aa061fd5f48a20fb"}, @@ -2458,6 +2375,25 @@ vulture = [ {file = "vulture-1.6-py2.py3-none-any.whl", hash = "sha256:79ecb77e5d61a2375e2b94dea2fd859cd77a067e55e99b8238b64196adfcdc0f"}, {file = "vulture-1.6.tar.gz", hash = "sha256:7b94784ededbf8e2913b5142dc875d8f26de7c903b37cd2d8f478f79275f7ce9"}, ] +watchdog = [ + {file = "watchdog-2.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:581e3548159fe7d2a9f377a1fbcb41bdcee46849cca8ab803c7ac2e5e04ec77c"}, + {file = "watchdog-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:edcd9ef3fd460bb8a98eb1fcf99941e9fd9f275f45f1a82cb1359ec92975d647"}, + {file = "watchdog-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d34ce2261f118ecd57eedeef95fc2a495fc4a40b3ed7b3bf0bd7a8ccc1ab4f8f"}, + {file = "watchdog-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:668391e6c32742d76e5be5db6bf95c455fa4b3d11e76a77c13b39bccb3a47a72"}, + {file = "watchdog-2.1.2-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6ef9fe57162c4c361692620e1d9167574ba1975ee468b24051ca11c9bba6438e"}, + {file = "watchdog-2.1.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:58ebb1095ee493008a7789d47dd62e4999505d82be89fc884d473086fccc6ebd"}, + {file = "watchdog-2.1.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:91387ee2421f30b75f7ff632c9d48f76648e56bf346a7c805c0a34187a93aab4"}, + {file = "watchdog-2.1.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:a6471517315a8541a943c00b45f1d252e36898a3ae963d2d52509b89a50cb2b9"}, + {file = "watchdog-2.1.2-py3-none-manylinux2014_i686.whl", hash = "sha256:a42e6d652f820b2b94cd03156c62559a2ea68d476476dfcd77d931e7f1012d4a"}, + {file = "watchdog-2.1.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:3d6405681471ebe0beb3aa083998c4870e48b57f8afdb45ea1b5957cc5cf1014"}, + {file = "watchdog-2.1.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:598d772beeaf9c98d0df946fbabf0c8365dd95ea46a250c224c725fe0c4730bc"}, + {file = "watchdog-2.1.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:4b219d46d89cfa49af1d73175487c14a318a74cb8c5442603fd13c6a5b418c86"}, + {file = "watchdog-2.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:188145185c08c73c56f1478ccf1f0f0f85101191439679b35b6b100886ce0b39"}, + {file = "watchdog-2.1.2-py3-none-win32.whl", hash = "sha256:255a32d44bbbe62e52874ff755e2eefe271b150e0ec240ad7718a62a7a7a73c4"}, + {file = "watchdog-2.1.2-py3-none-win_amd64.whl", hash = "sha256:1a62a4671796dc93d1a7262286217d9e75823c63d4c42782912d39a506d30046"}, + {file = "watchdog-2.1.2-py3-none-win_ia64.whl", hash = "sha256:104266a778906ae0e971368d368a65c4cd032a490a9fca5ba0b78c6c7ae11720"}, + {file = "watchdog-2.1.2.tar.gz", hash = "sha256:0237db4d9024859bea27d0efb59fe75eef290833fd988b8ead7a879b0308c2db"}, +] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, diff --git a/pyproject.toml b/pyproject.toml index d0a55026a..aa17ef55c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ bandit = "^1.6" safety = "^1.8" flake8-bugbear = "^19.8" black = {version = "^20.08b1", allow-prereleases = true} -mypy = "^0.761.0" +mypy = "^0.901" ipython = "^7.7" pytest = "^6.0" pytest-cov = "^2.7" @@ -89,6 +89,7 @@ mypy-extensions = "^0.4.3" py = "^1.10.0" toml = "^0.10.2" pytest-benchmark = "^3.4.1" +types-pkg-resources = "^0.1.2" [tool.poetry.scripts] isort = "isort.main:main" diff --git a/scripts/lint.sh b/scripts/lint.sh index ece33be7a..6783976fc 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -2,7 +2,7 @@ set -euxo pipefail poetry run cruft check -poetry run mypy --ignore-missing-imports isort/ +poetry run mypy -p isort poetry run black --target-version py36 --check . poetry run isort --profile hug --check --diff isort/ tests/ poetry run isort --profile hug --check --diff example_isort_formatting_plugin/ diff --git a/setup.cfg b/setup.cfg index 8a59aaffd..aa714e7a9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,6 +5,10 @@ disallow_any_generics = True strict_optional = True check_untyped_defs = True allow_redefinition = True +ignore_missing_imports = True + +[mypy-isort.isort._vendored.*] +ignore_errors = True [mypy-test_isort] strict_optional = False From dffefe446ab91848d9329859a17aff32812ad209 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 12 Jun 2021 23:07:02 -0700 Subject: [PATCH 469/539] Update portray --- poetry.lock | 110 ++++++++++++++++++++++++++++++++++++++++++------- pyproject.toml | 2 +- 2 files changed, 96 insertions(+), 16 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6703e1ff4..a135822d6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -213,7 +213,7 @@ examples = ["examples (>=1.0.2,<2.0.0)"] [[package]] name = "dataclasses" -version = "0.8" +version = "0.7" description = "A backport of the dataclasses module for Python 3.6" category = "dev" optional = false @@ -243,6 +243,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "docstring-parser" +version = "0.7.3" +description = "" +category = "dev" +optional = false +python-versions = "~=3.5" + [[package]] name = "dparse" version = "0.5.1" @@ -678,6 +686,18 @@ typing-inspect = ">=0.4.0" [package.extras] dev = ["black (==20.8b1)", "codecov (>=2.1.4)", "coverage (>=4.5.4)", "fixit (==0.1.1)", "flake8 (>=3.7.8)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "isort (==5.5.3)", "jupyter (>=1.0.0)", "nbsphinx (>=0.4.2)", "pyre-check (==0.0.41)", "sphinx-rtd-theme (>=0.4.3)", "prompt-toolkit (>=2.0.9)", "tox (>=3.18.1)"] +[[package]] +name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} + [[package]] name = "mako" version = "1.1.4" @@ -756,7 +776,7 @@ i18n = ["babel (>=2.9.0)"] [[package]] name = "mkdocs-material" -version = "5.5.14" +version = "7.1.8" description = "A Material Design theme for MkDocs" category = "dev" optional = false @@ -866,13 +886,15 @@ python-versions = ">=2.6" [[package]] name = "pdocs" -version = "1.0.2" +version = "1.1.1" description = "A simple program and library to auto generate API documentation for Python modules." category = "dev" optional = false python-versions = ">=3.6,<4.0" [package.dependencies] +dataclasses = {version = ">=0.7,<0.8", markers = "python_version >= \"3.6\" and python_version < \"3.7\""} +docstring_parser = ">=0.7.2,<0.8.0" hug = ">=2.6,<3.0" Mako = ">=1.1,<2.0" Markdown = ">=3.0.0,<4.0.0" @@ -1000,7 +1022,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "portray" -version = "1.4.0" +version = "1.6.0" description = "Your Project with Great Documentation" category = "dev" optional = false @@ -1009,9 +1031,10 @@ python-versions = ">=3.6,<4.0" [package.dependencies] GitPython = ">=3.0,<4.0" hug = ">=2.6,<3.0" +livereload = ">=2.6.3,<3.0.0" mkdocs = ">=1.0,<2.0" -mkdocs-material = ">=5.0,<6.0" -pdocs = ">=1.0.2,<2.0.0" +mkdocs-material = ">=7.0,<8.0" +pdocs = ">=1.1.1,<2.0.0" pymdown-extensions = ">=7.0,<8.0" toml = ">=0.10.0,<0.11.0" yaspin = ">=0.15.0,<0.16.0" @@ -1429,6 +1452,14 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "tornado" +version = "6.1" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "dev" +optional = false +python-versions = ">= 3.5" + [[package]] name = "traitlets" version = "4.3.3" @@ -1597,7 +1628,7 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "d1f7b33a4c7b1c6ae5a9787923d50801c745b0f3ed01248d6025214f413f79b5" +content-hash = "ec0d4a4fed2582647898d3b398e2f90a7297d14c70ef13c4f824089c5c38d7bd" [metadata.files] appdirs = [ @@ -1721,8 +1752,8 @@ cruft = [ {file = "cruft-2.8.0.tar.gz", hash = "sha256:29e6c95ed46bda8d26b9666e78bdcd625f3a2352620fc52c44dc4ff64a298ce0"}, ] dataclasses = [ - {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, - {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, + {file = "dataclasses-0.7-py3-none-any.whl", hash = "sha256:3459118f7ede7c8bea0fe795bff7c6c2ce287d01dd226202f7c9ebc0610a7836"}, + {file = "dataclasses-0.7.tar.gz", hash = "sha256:494a6dcae3b8bcf80848eea2ef64c0cc5cd307ffc263e17cdf42f3e5420808e6"}, ] decorator = [ {file = "decorator-5.0.9-py3-none-any.whl", hash = "sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323"}, @@ -1735,6 +1766,9 @@ distlib = [ docopt = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] +docstring-parser = [ + {file = "docstring_parser-0.7.3.tar.gz", hash = "sha256:cde5fbf8b846433dfbde1e0f96b7f909336a634d5df34a38cb75050c7346734a"}, +] dparse = [ {file = "dparse-0.5.1-py3-none-any.whl", hash = "sha256:e953a25e44ebb60a5c6efc2add4420c177f1d8404509da88da9729202f306994"}, {file = "dparse-0.5.1.tar.gz", hash = "sha256:a1b5f169102e1c894f9a7d5ccf6f9402a836a5d24be80a986c7ce9eaed78f367"}, @@ -1894,6 +1928,9 @@ libcst = [ {file = "libcst-0.3.19-py3-none-any.whl", hash = "sha256:9e26313ded6e17605658b93319299bce43cc8d7e24dafc12d6f782f758a3faf4"}, {file = "libcst-0.3.19.tar.gz", hash = "sha256:4876239db55164acaf034ee01f56a7db0a2f90cacea24b183d8aa69efc11b067"}, ] +livereload = [ + {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, +] mako = [ {file = "Mako-1.1.4.tar.gz", hash = "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab"}, ] @@ -1950,8 +1987,8 @@ mkdocs = [ {file = "mkdocs-1.2.1.tar.gz", hash = "sha256:6e0ea175366e3a50d334597b0bc042b8cebd512398cdd3f6f34842d0ef524905"}, ] mkdocs-material = [ - {file = "mkdocs-material-5.5.14.tar.gz", hash = "sha256:9f3237df1a72f91e0330a5e3b3711cb7aaa0d5705f9585e6ce6fbacaa16e777f"}, - {file = "mkdocs_material-5.5.14-py2.py3-none-any.whl", hash = "sha256:a0b3b3e67606e04d13e777d13f3195402ea09e0c3ce279abc3666cac2c5b3a6d"}, + {file = "mkdocs-material-7.1.8.tar.gz", hash = "sha256:e555c66ece5eab7023c4733270dc7627280e707e5082dab278d6a7a4881d2435"}, + {file = "mkdocs_material-7.1.8-py2.py3-none-any.whl", hash = "sha256:08eaf9f77c6d026706397bae2c50d202cfe3a81ef984027b671b4acd365dfc5b"}, ] mkdocs-material-extensions = [ {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, @@ -2043,8 +2080,8 @@ pbr = [ {file = "pbr-5.6.0.tar.gz", hash = "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd"}, ] pdocs = [ - {file = "pdocs-1.0.2-py3-none-any.whl", hash = "sha256:4d5ff87babcd0c46f12b76c887d53225bddb389dee7c6b338dbe281c729fc035"}, - {file = "pdocs-1.0.2.tar.gz", hash = "sha256:2e32432bd2736fd678ac1ce4447cd508deb62b5a12f7ba3bf0e3a374063221e2"}, + {file = "pdocs-1.1.1-py3-none-any.whl", hash = "sha256:4f5116cf5ce0fa9f13171cd74db224636d4d71370115eefce22d8945526fcfc0"}, + {file = "pdocs-1.1.1.tar.gz", hash = "sha256:f148034970220c9e05d2e04d8eb3fcec3575cf480af0966123ef9d6621b46e4f"}, ] pep517 = [ {file = "pep517-0.10.0-py2.py3-none-any.whl", hash = "sha256:eba39d201ef937584ad3343df3581069085bacc95454c80188291d5b3ac7a249"}, @@ -2086,8 +2123,8 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] portray = [ - {file = "portray-1.4.0-py3-none-any.whl", hash = "sha256:a6a06042a6b7fcb876b1e6cdcaee5adaeb6751388cb292fc05b2f31b1a4c3fb2"}, - {file = "portray-1.4.0.tar.gz", hash = "sha256:ea2271c5e3fbe956070a6f8b1aee6dc3d6a66c18c11907e878db8faa6fd2c449"}, + {file = "portray-1.6.0-py3-none-any.whl", hash = "sha256:592d6f0851dc585b49044d7a61bb6b5b26b3e9693ac06d97e27b86d6e00e9113"}, + {file = "portray-1.6.0.tar.gz", hash = "sha256:7d4b7b58c4964833c704eb5fbdf801aede6f72ea26603d6c2b58e16448fe93d9"}, ] poyo = [ {file = "poyo-0.5.0-py2.py3-none-any.whl", hash = "sha256:3e2ca8e33fdc3c411cd101ca395668395dd5dc7ac775b8e809e3def9f9fe041a"}, @@ -2310,6 +2347,49 @@ tomlkit = [ {file = "tomlkit-0.7.2-py2.py3-none-any.whl", hash = "sha256:173ad840fa5d2aac140528ca1933c29791b79a374a0861a80347f42ec9328117"}, {file = "tomlkit-0.7.2.tar.gz", hash = "sha256:d7a454f319a7e9bd2e249f239168729327e4dd2d27b17dc68be264ad1ce36754"}, ] +tornado = [ + {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, + {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, + {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, + {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, + {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, + {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, + {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, + {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, + {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, + {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, + {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, + {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, + {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, + {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, + {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, + {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, + {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, + {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, +] traitlets = [ {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, diff --git a/pyproject.toml b/pyproject.toml index aa17ef55c..598b8423f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ hypothesis-auto = { version = "^1.0.0" } hypothesmith = "^0.1.3" examples = { version = "^1.0.0" } cruft = { version = "^2.2" } -portray = { version = "^1.3.0" } +portray = { version = "^1.6.0" } pipfile = "^0.0.2" requirementslib = "^1.5" pipreqs = "^0.4.9" From d2f7a9fc5c77d6a93b5cfb8cce93ef3cc0273e59 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sat, 12 Jun 2021 23:08:29 -0700 Subject: [PATCH 470/539] Fixed #1744: repeat noqa comments dropped when * import and non * imports exist from the same package. --- CHANGELOG.md | 3 +- isort/output.py | 3 +- tests/unit/test_regressions.py | 55 ++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cedf1b9af..3967b1937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed (https://github.com/PyCQA/isort/pull/1695): added imports being added to doc string in some cases. - Fixed (https://github.com/PyCQA/isort/pull/1714): in rare cases line continuation combined with tabs can output invalid code. - Fixed (https://github.com/PyCQA/isort/pull/1726): isort ignores reverse_sort when force_sort_within_sections is true. - - Fixed (https://github.com/PyCQA/isort/issues/1741): comments in hanging indent modes can lead to invalid code. + - Fixed #1741: comments in hanging indent modes can lead to invalid code. + - Fixed #1744: repeat noqa comments dropped when * import and non * imports exist from the same package. - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. diff --git a/isort/output.py b/isort/output.py index 04db46d06..f53d81b72 100644 --- a/isort/output.py +++ b/isort/output.py @@ -416,14 +416,13 @@ def _with_from_imports( if "*" in from_imports: output.append( with_comments( - _with_star_comments(parsed, module, list(comments or ())), + _with_star_comments(parsed, module, []), f"{import_start}*", removed=config.ignore_comments, comment_prefix=config.comment_prefix, ) ) from_imports.remove("*") - comments = None for from_import in copy.copy(from_imports): comment = ( diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index ea6571518..48395f8bb 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1725,3 +1725,58 @@ def test_isort_should_never_quietly_remove_imports_in_any_hangin_mode_issue_1741 assert "qwerty" in sorted_code assert "efg" in sorted_code assert "xyz" in sorted_code + + +def test_isort_should_keep_multi_noqa_with_star_issue_1744(): + assert isort.check_code( + """ +from typing import * # noqa +from typing import IO, BinaryIO, Union # noqa +""", + show_diff=True, + ) + assert isort.check_code( + """ +from typing import * # noqa 1 +from typing import IO, BinaryIO, Union # noqa 2 +""", + show_diff=True, + ) + assert isort.check_code( + """ +from typing import * # noqa +from typing import IO, BinaryIO, Union +""", + show_diff=True, + ) + assert isort.check_code( + """ +from typing import * +from typing import IO, BinaryIO, Union # noqa +""", + show_diff=True, + ) + assert ( + isort.code( + """ +from typing import * # hi +from typing import IO, BinaryIO, Union # noqa +""", + combine_star=True, + ) + == """ +from typing import * # noqa; hi +""" + ) + assert ( + isort.code( + """ +from typing import * # noqa +from typing import IO, BinaryIO, Union # noqa +""", + combine_star=True, + ) + == """ +from typing import * # noqa +""" + ) From 6b1030301fdea4e3499d60c8bcf2e94326626157 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 13 Jun 2021 01:26:32 -0700 Subject: [PATCH 471/539] Update portray --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index a135822d6..e394f9c67 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1022,7 +1022,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "portray" -version = "1.6.0" +version = "1.7.0" description = "Your Project with Great Documentation" category = "dev" optional = false @@ -1032,7 +1032,7 @@ python-versions = ">=3.6,<4.0" GitPython = ">=3.0,<4.0" hug = ">=2.6,<3.0" livereload = ">=2.6.3,<3.0.0" -mkdocs = ">=1.0,<2.0" +mkdocs = ">=1.2,<1.3" mkdocs-material = ">=7.0,<8.0" pdocs = ">=1.1.1,<2.0.0" pymdown-extensions = ">=7.0,<8.0" @@ -2123,8 +2123,8 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] portray = [ - {file = "portray-1.6.0-py3-none-any.whl", hash = "sha256:592d6f0851dc585b49044d7a61bb6b5b26b3e9693ac06d97e27b86d6e00e9113"}, - {file = "portray-1.6.0.tar.gz", hash = "sha256:7d4b7b58c4964833c704eb5fbdf801aede6f72ea26603d6c2b58e16448fe93d9"}, + {file = "portray-1.7.0-py3-none-any.whl", hash = "sha256:fb4467105d948fabf0ec35b11af50f3c0c4f2aabaa31d5dcd657fadb1c6132e1"}, + {file = "portray-1.7.0.tar.gz", hash = "sha256:8f3c0a6a6841969329e4dd1e94e180220658c3ad0367a5bad81dd964a75ae1fe"}, ] poyo = [ {file = "poyo-0.5.0-py2.py3-none-any.whl", hash = "sha256:3e2ca8e33fdc3c411cd101ca395668395dd5dc7ac775b8e809e3def9f9fe041a"}, From 19eb9acfe58d169bbe1ba086fd60a9dda7195f0e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 13 Jun 2021 02:26:39 -0700 Subject: [PATCH 472/539] Fixed #1721: repeat noqa comments on separate from lines with force-single-line set, sometimes get dropped. --- CHANGELOG.md | 1 + isort/parse.py | 14 ++++++++++++++ tests/unit/test_regressions.py | 16 ++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3967b1937..98d6c4d7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed (https://github.com/PyCQA/isort/pull/1726): isort ignores reverse_sort when force_sort_within_sections is true. - Fixed #1741: comments in hanging indent modes can lead to invalid code. - Fixed #1744: repeat noqa comments dropped when * import and non * imports exist from the same package. + - Fixed #1721: repeat noqa comments on separate from lines with force-single-line set, sometimes get dropped. - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. diff --git a/isort/parse.py b/isort/parse.py index 2a15c90fd..7a72497bd 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -455,6 +455,20 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte ] = associated_comment if associated_comment in comments: comments.pop(comments.index(associated_comment)) + if ( + config.force_single_line + and comments + and attach_comments_to is None + and len(just_imports) == 1 + ): + nested_from_comments = categorized_comments["nested"].setdefault( + import_from, {} + ) + existing_comment = nested_from_comments.get(just_imports[0], "") + nested_from_comments[ + just_imports[0] + ] = f"{existing_comment}{'; ' if existing_comment else ''}{'; '.join(comments)}" + comments = [] if comments and attach_comments_to is None: attach_comments_to = categorized_comments["from"].setdefault(import_from, []) diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 48395f8bb..ccd3da223 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1780,3 +1780,19 @@ def test_isort_should_keep_multi_noqa_with_star_issue_1744(): from typing import * # noqa """ ) + + +def test_isort_should_keep_multiple_noqa_comments_force_single_line_mode_issue_1721(): + assert isort.check_code( + """ +from some_very_long_filename_to_import_from_that_causes_a_too_long_import_row import ( # noqa: E501 + CONSTANT_1, +) +from some_very_long_filename_to_import_from_that_causes_a_too_long_import_row import ( # noqa: E501 + CONSTANT_2, +) +""", + show_diff=True, + profile="black", + force_single_line=True, + ) From ddbac4dcd7fd6e3065845c41846ebbd700e53fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ni=20Gauffier?= Date: Sun, 13 Jun 2021 10:36:13 +0000 Subject: [PATCH 473/539] windows ok --- isort/settings.py | 11 +++++++---- tests/unit/test_main.py | 17 ++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index 8ebaef668..ed8653093 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -2,6 +2,7 @@ Defines how the default settings for isort should be loaded """ +import codecs import configparser import fnmatch import glob @@ -519,15 +520,17 @@ def _check_folder_gitignore(self, folder: str) -> Optional[Path]: files = glob.glob(str(git_folder) + "/**/*", recursive=True) files_result = ( - subprocess.check_output( # nosec # skipcq: PYL-W1510 - ["git", "-C", str(git_folder), "check-ignore", *files] - ) + codecs.escape_decode( + subprocess.check_output( # nosec # skipcq: PYL-W1510 + ["git", "-C", str(git_folder), "check-ignore", *files] + ) + )[0] .decode("utf-8") .split("\n") ) files_result = files_result[:-1] if files_result else files_result - self.git_ignore[git_folder] = {Path(f) for f in files_result} + self.git_ignore[git_folder] = {Path(f.strip('"')) for f in files_result} return git_folder diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 2849b64cc..4679cee55 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -1123,17 +1123,20 @@ def main_check(args): git_project1.mkdir("nested_dir_ignored").join("has_imports.py").write(import_content) should_check = [ - "has_imports.py", - "nested_dir/has_imports.py", - "git_project0/has_imports.py", - "git_project1/has_imports.py", - "git_project1/nested_dir/has_imports.py", + "/has_imports.py", + "/nested_dir/has_imports.py", + "/git_project0/has_imports.py", + "/git_project1/has_imports.py", + "/git_project1/nested_dir/has_imports.py", ] out, error = main_check([str(tmpdir), "--skip-gitignore", "--filter-files", "--check"]) - assert all(f"{str(tmpdir)}/{file}" in error for file in should_check) + if os.name == "nt": + should_check = [sc.replace("/", "\\") for sc in should_check] + + assert all(f"{str(tmpdir)}{file}" in error for file in should_check) out, error = main_check([str(tmpdir), "--skip-gitignore", "--filter-files"]) - assert all(f"{str(tmpdir)}/{file}" in out for file in should_check) + assert all(f"{str(tmpdir)}{file}" in out for file in should_check) From 2cacda9ddced6aa57d791809f073eed4e5b3e79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ni=20Gauffier?= Date: Sun, 13 Jun 2021 10:59:02 +0000 Subject: [PATCH 474/539] lint pass --- isort/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/settings.py b/isort/settings.py index ed8653093..07768ae0d 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -520,7 +520,7 @@ def _check_folder_gitignore(self, folder: str) -> Optional[Path]: files = glob.glob(str(git_folder) + "/**/*", recursive=True) files_result = ( - codecs.escape_decode( + codecs.escape_decode( # type: ignore subprocess.check_output( # nosec # skipcq: PYL-W1510 ["git", "-C", str(git_folder), "check-ignore", *files] ) From 8b58270f1cdec26ef0f3c7901c4b4d63e27a6cce Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Wed, 16 Jun 2021 02:23:27 +0530 Subject: [PATCH 475/539] Change description of only-sections --- isort/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isort/main.py b/isort/main.py index ae1ab60e0..068a25e3f 100644 --- a/isort/main.py +++ b/isort/main.py @@ -685,8 +685,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="only_sections", action="store_true", help="Causes imports to be sorted based on their sections like STDLIB,THIRDPARTY etc. " - "Within sections, the imports are ordered by their import style and the imports with " - "same style maintain their relative positions.", + "Within sections, the imports maintain their relative positions irrespective of their " + "import_type", ) section_group.add_argument( "--ds", From fa55281f13a5d6f9b891686e4f06aa32f7fc8cc0 Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Wed, 16 Jun 2021 02:24:16 +0530 Subject: [PATCH 476/539] Add original_order field in parse.py and add logic to output.py --- isort/output.py | 11 ++++++++++- isort/parse.py | 7 +++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/isort/output.py b/isort/output.py index f53d81b72..43470b2dd 100644 --- a/isort/output.py +++ b/isort/output.py @@ -85,7 +85,16 @@ def sorted_imports( lines_between = [""] * ( config.lines_between_types if from_modules and straight_modules else 0 ) - if config.from_first: + + if config.only_sections: + section_output = [] + for section_import_type in parsed.original_order[section]: + if section_import_type == "from": + section_output.append(from_imports.pop(0)) + else: + section_output.append(straight_imports.pop(0)) + + elif config.from_first: section_output = from_imports + lines_between + straight_imports else: section_output = straight_imports + lines_between + from_imports diff --git a/isort/parse.py b/isort/parse.py index 2a15c90fd..80c98b942 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -139,6 +139,7 @@ class ParsedContent(NamedTuple): line_separator: str sections: Any verbose_output: List[str] + original_order: Dict[str, List[str]] def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedContent: @@ -165,9 +166,12 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte } imports: OrderedDict[str, Dict[str, Any]] = OrderedDict() verbose_output: List[str] = [] + original_order: Dict[str, List[str]] = {} for section in chain(config.sections, config.forced_separate): imports[section] = {"straight": OrderedDict(), "from": OrderedDict()} + original_order[section] = [] + categorized_comments: CommentsDict = { "from": {}, "straight": {}, @@ -447,6 +451,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte " Do you need to define a default section?" ) root = imports[placed_module][type_of_import] # type: ignore + original_order[placed_module].append(type_of_import) for import_name in just_imports: associated_comment = nested_comments.get(import_name) if associated_comment: @@ -549,6 +554,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte module, False ) imports[placed_module][type_of_import][module] = straight_import # type: ignore + original_order[placed_module].append(type_of_import) change_count = len(out_lines) - original_line_count @@ -566,4 +572,5 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte line_separator=line_separator, sections=config.sections, verbose_output=verbose_output, + original_order=original_order, ) From 02c09b8941a0a6c5c36739117ae67a38f61ca669 Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Wed, 16 Jun 2021 02:24:46 +0530 Subject: [PATCH 477/539] Add/modify tests for only-sections option --- tests/unit/test_isort.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 2cbc93a43..20b8b5186 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -5071,8 +5071,8 @@ def test_only_sections() -> None: == ( "import sys\n" "import os\n" - "import math\n" "from os import path as ospath\n" + "import math\n" "from collections import defaultdict\n" "\n" "import numpy as np\n" @@ -5083,6 +5083,39 @@ def test_only_sections() -> None: == isort.code(test_input, only_sections=True, force_single_line=True) ) + test_input = ( + "from requests import post as POST, get as GET\n" + "import sys\n" + "from math import sqrt\n" + "\n" + "import numpy as np\n" + "\n" + "import os\n" + "\n" + "from .url import *\n" + "import pandas as pd\n" + "\n" + "import .views\n" + "from collections import defaultdict\n" + ) + + assert ( + isort.code(test_input, only_sections=True) + == ( + "import sys\n" + "from math import sqrt\n" + "import os\n" + "from collections import defaultdict\n" + "\n" + "from requests import post as POST, get as GET\n" + "import numpy as np\n" + "import pandas as pd\n" + "\n" + "from .url import *\n" + "import .views\n" + ) + ) + # test to ensure that from_imports remain intact with only_sections test_input = "from foo import b, a, c\n" From f7d0fc56dd590c0668042b09335ceef93e2f3f82 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 15 Jun 2021 21:21:59 -0700 Subject: [PATCH 478/539] Comment common settings that are modified in black profile --- docs/configuration/black_compatibility.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/configuration/black_compatibility.md b/docs/configuration/black_compatibility.md index c07b43cf0..c46552548 100644 --- a/docs/configuration/black_compatibility.md +++ b/docs/configuration/black_compatibility.md @@ -6,6 +6,10 @@ Compatibility with black Compatibility with black is very important to the isort project and comes baked in starting with version 5. All that's required to use isort alongside black is to set the isort profile to "black". +!!! tip + Beyond the profile, it is common to set [skip_gitignore](https://pycqa.github.io/isort/docs/configuration/options/#skip-gitignore) (which is not enabled by default for isort as it requires git to be installed) and [line_length](https://pycqa.github.io/isort/docs/configuration/options/#skip-gitignore) as it is common to deviate from black's default of 88. + + ## Using a config file (such as .isort.cfg) For projects that officially use both isort and black, we recommend setting the black profile in a config file at the root of your project's repository. From 2250203586a49f99f46bbd04a667672bba5db404 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 15 Jun 2021 21:23:48 -0700 Subject: [PATCH 479/539] Document that skip-gitignore requires git to be installed --- isort/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index ae1ab60e0..6057ef388 100644 --- a/isort/main.py +++ b/isort/main.py @@ -346,7 +346,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: "--skip-gitignore", action="store_true", dest="skip_gitignore", - help="Treat project as a git repository and ignore files listed in .gitignore", + help="Treat project as a git repository and ignore files listed in .gitignore." + "\nNOTE: This requires git to be installed and accesible from the same shell as isort.", ) target_group.add_argument( "--ext", From 27bd19da0a554b2d108e5eaf3c708c8780e00606 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 15 Jun 2021 21:24:36 -0700 Subject: [PATCH 480/539] Add recent code contirbutors --- docs/contributing/4.-acknowledgements.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index ecb2a5eaf..3f9210600 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -222,6 +222,10 @@ Code Contributors - ruro (@RuRo) - Léni (@legau) - keno (Ken Okada) (@kenoss) +- Shota Terashita (@shotat) +- Luca Di sera (@diseraluca) +- Tonye Jack (@jackton1) +- Yusuke Hayashi (@yhay81) Documenters =================== From bc5ef518521ea1ae8344f3ac0caaf226bef2d995 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 15 Jun 2021 21:25:03 -0700 Subject: [PATCH 481/539] Update config option docs --- docs/configuration/options.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index c9d665aeb..440a6c887 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -36,7 +36,7 @@ Force specific imports to the top of their appropriate section. Files that isort should skip over. If you want to skip multiple files you should specify twice: --skip file1 --skip file2. Values can be file names, directory names or file paths. To skip all files in a nested path use --skip-glob. **Type:** Frozenset -**Default:** `('.bzr', '.direnv', '.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.svn', '.tox', '.venv', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv')` +**Default:** `('.bzr', '.direnv', '.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.svn', '.tox', '.venv', '__pypackages__', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv')` **Python & Config File Name:** skip **CLI Flags:** @@ -76,7 +76,8 @@ Additional files that isort should skip over (extending --skip-glob). ## Skip Gitignore -Treat project as a git repository and ignore files listed in .gitignore +Treat project as a git repository and ignore files listed in .gitignore. +NOTE: This requires git to be installed and accesible from the same shell as isort. **Type:** Bool **Default:** `False` @@ -468,7 +469,7 @@ Order imports by type, which is determined by case, in addition to alphabeticall ## Atomic -Ensures the output doesn't save if the resulting file contains syntax errors. This option is not compatible with Cython code. +Ensures the output doesn't save if the resulting file contains syntax errors. **Type:** Bool **Default:** `False` @@ -771,7 +772,7 @@ Inserts a blank line before a comment following an import. ## Profile -Base profile type to use for configuration. Profiles include: black, django, pycharm, google, open_stack, plone, attrs, hug, wemake. As well as any shared profiles. +Base profile type to use for configuration. Profiles include: black, django, pycharm, google, open_stack, plone, attrs, hug, wemake, appnexus. As well as any shared profiles. **Type:** String **Default:** `` @@ -968,7 +969,8 @@ Tells isort to only show an identical custom import heading comment once, even i ## Only Sections -Causes imports to be sorted based on their sections like STDLIB,THIRDPARTY etc. Within the sections, the imports are ordered by their import style and the imports with the same style maintain their relative positions. +Causes imports to be sorted based on their sections like STDLIB,THIRDPARTY etc. Within sections, the imports are ordered by their import style and the imports with same style maintain their relative positions. + **Type:** Bool **Default:** `False` **Python & Config File Name:** only_sections @@ -1094,6 +1096,15 @@ Forces star imports above others to avoid overriding directly imported variables - --star-first +## Git Ignore + +**No Description** + +**Type:** Dict +**Default:** `{}` +**Python & Config File Name:** git_ignore +**CLI Flags:** **Not Supported** + ## Show Version Displays the currently installed version of isort. From 3acc03a9574ed121d42c60a24d97ee2d8fa10e59 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 15 Jun 2021 21:25:14 -0700 Subject: [PATCH 482/539] Update profile docs --- docs/configuration/profiles.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/configuration/profiles.md b/docs/configuration/profiles.md index 073f7d60d..a8e09dd25 100644 --- a/docs/configuration/profiles.md +++ b/docs/configuration/profiles.md @@ -84,3 +84,20 @@ To use any of the listed profiles, use `isort --profile PROFILE_NAME` from the c - **include_trailing_comma**: `True` - **use_parentheses**: `True` - **line_length**: `80` + +#appnexus + + + - **multi_line_output**: `3` + - **include_trailing_comma**: `True` + - **force_grid_wrap**: `0` + - **use_parentheses**: `True` + - **ensure_newline_before_comments**: `True` + - **line_length**: `88` + - **force_sort_within_sections**: `True` + - **order_by_type**: `False` + - **case_sensitive**: `False` + - **reverse_relative**: `True` + - **sort_relative_in_force_sorted_sections**: `True` + - **sections**: `['FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'APPLICATION', 'LOCALFOLDER']` + - **no_lines_before**: `'LOCALFOLDER'` From f600f2d261ef028e0bec6878169596dcd3f1fe24 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Wed, 16 Jun 2021 05:10:14 +0000 Subject: [PATCH 483/539] Refactor unnecessary `else` / `elif` when `if` block has a `return` statement --- isort/wrap_modes.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/isort/wrap_modes.py b/isort/wrap_modes.py index 20ce0b6f7..7118afc24 100644 --- a/isort/wrap_modes.py +++ b/isort/wrap_modes.py @@ -155,17 +155,16 @@ def hanging_indent(**interface): line_length_limit + 2 ): return statement_with_comments - else: - return ( - _hanging_indent_end_line(interface["statement"]) - + interface["line_separator"] - + isort.comments.add_to_line( - interface["comments"], - interface["indent"], - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"].lstrip(), - ) + return ( + _hanging_indent_end_line(interface["statement"]) + + interface["line_separator"] + + isort.comments.add_to_line( + interface["comments"], + interface["indent"], + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"].lstrip(), ) + ) return interface["statement"] From f1230c92149a977d24d4003c9ff168d9093a6f43 Mon Sep 17 00:00:00 2001 From: Arthur Rio Date: Wed, 16 Jun 2021 10:08:13 -0600 Subject: [PATCH 484/539] Fix links to documentation --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d1005b6ca..f69119d56 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,9 @@ editors](https://github.com/pycqa/isort/wiki/isort-Plugins) to quickly sort all your imports. It requires Python 3.6+ to run but supports formatting Python 2 code too. -- [Try isort now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try/) -- [Using black? See the isort and black compatibility guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility/) -- [isort has official support for pre-commit!](https://pycqa.github.io/isort/docs/configuration/pre-commit/) +- [Try isort now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try.html) +- [Using black? See the isort and black compatibility guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility.html) +- [isort has official support for pre-commit!](https://pycqa.github.io/isort/docs/configuration/pre-commit.html) ![Example Usage](https://raw.github.com/pycqa/isort/main/example.gif) @@ -168,7 +168,7 @@ notified. You will notice above the \"multi\_line\_output\" setting. This setting defines how from imports wrap when they extend past the line\_length -limit and has [12 possible settings](https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes/). +limit and has [12 possible settings](https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes.html). ## Indentation @@ -221,7 +221,7 @@ the `-e` option into the command line utility. isort provides configuration options to change almost every aspect of how imports are organized, ordered, or grouped together in sections. -[Click here](https://pycqa.github.io/isort/docs/configuration/custom_sections_and_ordering/) for an overview of all these options. +[Click here](https://pycqa.github.io/isort/docs/configuration/custom_sections_and_ordering.html) for an overview of all these options. ## Skip processing of imports (outside of configuration) @@ -258,7 +258,7 @@ import a isort can be ran or configured to add / remove imports automatically. -[See a complete guide here.](https://pycqa.github.io/isort/docs/configuration/add_or_remove_imports/) +[See a complete guide here.](https://pycqa.github.io/isort/docs/configuration/add_or_remove_imports.html) ## Using isort to verify code @@ -289,14 +289,14 @@ project. isort provides a hook function that can be integrated into your Git pre-commit script to check Python code before committing. -[More info here.](https://pycqa.github.io/isort/docs/configuration/git_hook/) +[More info here.](https://pycqa.github.io/isort/docs/configuration/git_hook.html) ## Setuptools integration Upon installation, isort enables a `setuptools` command that checks Python files declared by your project. -[More info here.](https://pycqa.github.io/isort/docs/configuration/setuptools_integration/) +[More info here.](https://pycqa.github.io/isort/docs/configuration/setuptools_integration.html) ## Spread the word From 21fb6e13d7b4238455428275e45fccc59e0433eb Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 16 Jun 2021 23:26:41 -0700 Subject: [PATCH 485/539] Fix where import action comments are located --- isort/core.py | 16 ++++++- poetry.lock | 120 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 3 +- 3 files changed, 134 insertions(+), 5 deletions(-) diff --git a/isort/core.py b/isort/core.py index cc9d7c810..3bfd9618e 100644 --- a/isort/core.py +++ b/isort/core.py @@ -156,8 +156,20 @@ def process( isort_off = True skip_file = True - if not in_quote and stripped_line == "# isort: off": - isort_off = True + if not in_quote: + if stripped_line == "# isort: off": + isort_off = True + elif stripped_line.startswith("# isort: dont-add-imports"): + add_imports = [] + elif stripped_line.startswith("# isort: dont-add-import:"): + import_not_to_add = stripped_line.split("# isort: dont-add-import:", 1)[ + 1 + ].strip() + add_imports = [ + import_to_add + for import_to_add in add_imports + if not import_to_add == import_not_to_add + ] if ( (index == 0 or (index in (1, 2) and not contains_imports)) diff --git a/poetry.lock b/poetry.lock index e394f9c67..492deaf67 100644 --- a/poetry.lock +++ b/poetry.lock @@ -129,6 +129,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "cfgv" +version = "3.3.0" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" + [[package]] name = "chardet" version = "3.0.4" @@ -306,6 +314,14 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "filelock" +version = "3.0.12" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "flake8" version = "3.9.2" @@ -533,6 +549,17 @@ hypothesis = ">=5.41.0" lark-parser = ">=0.7.2" libcst = ">=0.3.8" +[[package]] +name = "identify" +version = "2.2.10" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.6.1" + +[package.extras] +license = ["editdistance-s"] + [[package]] name = "idna" version = "2.10" @@ -568,6 +595,21 @@ zipp = ">=0.5" docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +[[package]] +name = "importlib-resources" +version = "5.1.4" +description = "Read resources from Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] + [[package]] name = "iniconfig" version = "1.1.1" @@ -826,6 +868,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "nodeenv" +version = "1.6.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "numpy" version = "1.19.5" @@ -1047,6 +1097,24 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pre-commit" +version = "2.13.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-resources = {version = "*", markers = "python_version < \"3.7\""} +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=20.0.8" + [[package]] name = "prompt-toolkit" version = "3.0.3" @@ -1542,6 +1610,26 @@ brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +[[package]] +name = "virtualenv" +version = "20.4.7" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.dependencies] +appdirs = ">=1.4.3,<2" +distlib = ">=0.3.1,<1" +filelock = ">=3.0.0,<4" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] + [[package]] name = "vistir" version = "0.5.2" @@ -1627,8 +1715,8 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" -python-versions = "^3.6" -content-hash = "ec0d4a4fed2582647898d3b398e2f90a7297d14c70ef13c4f824089c5c38d7bd" +python-versions = "^3.6.1" +content-hash = "069575195bc09e4d3ac30c5600a5ac8a991c64a4a586eaf8339f6d3137cd18e9" [metadata.files] appdirs = [ @@ -1677,6 +1765,10 @@ certifi = [ {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, ] +cfgv = [ + {file = "cfgv-3.3.0-py2.py3-none-any.whl", hash = "sha256:b449c9c6118fe8cca7fa5e00b9ec60ba08145d281d52164230a69211c5d597a1"}, + {file = "cfgv-3.3.0.tar.gz", hash = "sha256:9e600479b3b99e8af981ecdfc80a0296104ee610cab48a5ae4ffd0b668650eb1"}, +] chardet = [ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, @@ -1801,6 +1893,10 @@ falcon = [ {file = "falcon-2.0.0-py2.py3-none-any.whl", hash = "sha256:54f2cb4b687035b2a03206dbfc538055cc48b59a953187b0458aa1b574d47b53"}, {file = "falcon-2.0.0.tar.gz", hash = "sha256:eea593cf466b9c126ce667f6d30503624ef24459f118c75594a69353b6c3d5fc"}, ] +filelock = [ + {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, + {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, +] flake8 = [ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, @@ -1872,6 +1968,10 @@ hypothesmith = [ {file = "hypothesmith-0.1.8-py3-none-any.whl", hash = "sha256:6248c3d0e0dc934e5352e3f7d79290560ab5861847ca6701e410f9a287461216"}, {file = "hypothesmith-0.1.8.tar.gz", hash = "sha256:f9ff047b15c4ed312ce3da57ea27570f86d6b53ce12af9f25e59e6576a00410a"}, ] +identify = [ + {file = "identify-2.2.10-py2.py3-none-any.whl", hash = "sha256:18d0c531ee3dbc112fa6181f34faa179de3f57ea57ae2899754f16a7e0ff6421"}, + {file = "identify-2.2.10.tar.gz", hash = "sha256:5b41f71471bc738e7b586308c3fca172f78940195cb3bf6734c1e66fdac49306"}, +] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, @@ -1897,6 +1997,10 @@ importlib-metadata = [ {file = "importlib_metadata-4.5.0-py3-none-any.whl", hash = "sha256:833b26fb89d5de469b24a390e9df088d4e52e4ba33b01dc5e0e4f41b81a16c00"}, {file = "importlib_metadata-4.5.0.tar.gz", hash = "sha256:b142cc1dd1342f31ff04bb7d022492b09920cb64fed867cd3ea6f80fe3ebd139"}, ] +importlib-resources = [ + {file = "importlib_resources-5.1.4-py3-none-any.whl", hash = "sha256:e962bff7440364183203d179d7ae9ad90cb1f2b74dcb84300e88ecc42dca3351"}, + {file = "importlib_resources-5.1.4.tar.gz", hash = "sha256:54161657e8ffc76596c4ede7080ca68cb02962a2e074a2586b695a93a925d36e"}, +] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, @@ -2023,6 +2127,10 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] +nodeenv = [ + {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"}, + {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"}, +] numpy = [ {file = "numpy-1.19.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc6bd4fd593cb261332568485e20a0712883cf631f6f5e8e86a52caa8b2b50ff"}, {file = "numpy-1.19.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aeb9ed923be74e659984e321f609b9ba54a48354bfd168d21a2b072ed1e833ea"}, @@ -2130,6 +2238,10 @@ poyo = [ {file = "poyo-0.5.0-py2.py3-none-any.whl", hash = "sha256:3e2ca8e33fdc3c411cd101ca395668395dd5dc7ac775b8e809e3def9f9fe041a"}, {file = "poyo-0.5.0.tar.gz", hash = "sha256:e26956aa780c45f011ca9886f044590e2d8fd8b61db7b1c1cf4e0869f48ed4dd"}, ] +pre-commit = [ + {file = "pre_commit-2.13.0-py2.py3-none-any.whl", hash = "sha256:b679d0fddd5b9d6d98783ae5f10fd0c4c59954f375b70a58cbe1ce9bcf9809a4"}, + {file = "pre_commit-2.13.0.tar.gz", hash = "sha256:764972c60693dc668ba8e86eb29654ec3144501310f7198742a767bec385a378"}, +] prompt-toolkit = [ {file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"}, {file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"}, @@ -2447,6 +2559,10 @@ urllib3 = [ {file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"}, {file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"}, ] +virtualenv = [ + {file = "virtualenv-20.4.7-py2.py3-none-any.whl", hash = "sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76"}, + {file = "virtualenv-20.4.7.tar.gz", hash = "sha256:14fdf849f80dbb29a4eb6caa9875d476ee2a5cf76a5f5415fa2f1606010ab467"}, +] vistir = [ {file = "vistir-0.5.2-py2.py3-none-any.whl", hash = "sha256:a37079cdbd85d31a41cdd18457fe521e15ec08b255811e81aa061fd5f48a20fb"}, {file = "vistir-0.5.2.tar.gz", hash = "sha256:eff1d19ef50c703a329ed294e5ec0b0fbb35b96c1b3ee6dcdb266dddbe1e935a"}, diff --git a/pyproject.toml b/pyproject.toml index 598b8423f..f339ef46a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ include = [ ] [tool.poetry.dependencies] -python = "^3.6" +python = ">=3.6.1,<4.0" pipreqs = {version = "*", optional = true} requirementslib = {version = "*", optional = true} pip-api = {version = "*", optional = true} @@ -90,6 +90,7 @@ py = "^1.10.0" toml = "^0.10.2" pytest-benchmark = "^3.4.1" types-pkg-resources = "^0.1.2" +pre-commit = "^2.13.0" [tool.poetry.scripts] isort = "isort.main:main" From 1cc6bd5ac4bb6077bd13521a23c302e9a3a22f8c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 16 Jun 2021 23:32:51 -0700 Subject: [PATCH 486/539] Add test case for add import skip feature --- tests/unit/test_ticketed_features.py | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 17aaf166b..0a4a5e9e5 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -1115,3 +1115,53 @@ def test_isort_can_combine_reverse_sort_with_force_sort_within_sections_issue_17 import bla """ ) + + +def test_isort_can_turn_off_import_adds_with_action_comment_issue_1737(): + assert ( + isort.code( + """ +import os +""", + add_imports=[ + "from __future__ import absolute_imports", + "from __future__ import annotations", + ], + ) + == """ +from __future__ import absolute_imports, annotations + +import os +""" + ) + + assert isort.check_code( + """ +# isort: dont-add-imports +import os +""", + show_diff=True, + add_imports=[ + "from __future__ import absolute_imports", + "from __future__ import annotations", + ], + ) + + assert ( + isort.code( + """ +# isort: dont-add-import: from __future__ import annotations +import os +""", + add_imports=[ + "from __future__ import absolute_imports", + "from __future__ import annotations", + ], + ) + == """ +# isort: dont-add-import: from __future__ import annotations +from __future__ import absolute_imports + +import os +""" + ) From cdd0e0d7b43fa57388b7fa6757acc05bb3925d5e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 16 Jun 2021 23:33:07 -0700 Subject: [PATCH 487/539] Implemented #1737: Support for using action comments to avoid adding imports to individual files. --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98d6c4d7b..4cda3e3d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,14 +5,16 @@ NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). ### 5.9.0 TBD + - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. + - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. + - Implemented #1737: Support for using action comments to avoid adding imports to individual files. - Fixed (https://github.com/PyCQA/isort/pull/1695): added imports being added to doc string in some cases. - Fixed (https://github.com/PyCQA/isort/pull/1714): in rare cases line continuation combined with tabs can output invalid code. - Fixed (https://github.com/PyCQA/isort/pull/1726): isort ignores reverse_sort when force_sort_within_sections is true. - Fixed #1741: comments in hanging indent modes can lead to invalid code. - Fixed #1744: repeat noqa comments dropped when * import and non * imports exist from the same package. - Fixed #1721: repeat noqa comments on separate from lines with force-single-line set, sometimes get dropped. - - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. - - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. + ### 5.8.0 March 20th 2021 - Fixed #1631: as import comments can in some cases be duplicated. From f8daed6cb1cc59b694a2300854d815cd414fe892 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 16 Jun 2021 23:34:49 -0700 Subject: [PATCH 488/539] Document new action comments --- docs/configuration/action_comments.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/configuration/action_comments.md b/docs/configuration/action_comments.md index 11b1a1a34..f85df7691 100644 --- a/docs/configuration/action_comments.md +++ b/docs/configuration/action_comments.md @@ -106,3 +106,12 @@ import a !!! tip isort split is exactly the same as placing an `# isort: on` immediately below an `# isort: off` + + +## isort: dont-add-imports + +Tells isort to not automatically add imports to this file, even if --add-imports is set. + +## isort: dont-add-import: [IMPORT_LINE] + +Tells isort to not automatically add a particular import, even if --add-imports says to add it. From 90fde3ef126fdccbb947bb59129b63b4cfa7c348 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Jun 2021 01:21:52 -0700 Subject: [PATCH 489/539] Fix missing coverage --- tests/unit/test_isort.py | 9 +++++++++ tests/unit/test_main.py | 25 +++++++++++++++---------- tests/unit/utils.py | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 2cbc93a43..ec2fd5d68 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -19,6 +19,7 @@ from isort.settings import WrapModes, Config from isort.utils import exists_case_sensitive from isort.exceptions import FileSkipped, ExistingSyntaxErrors +from .utils import as_stream, UnreadableStream TEST_DEFAULT_CONFIG = """ [*.{py,pyi}] @@ -1368,6 +1369,7 @@ def test_single_multiline() -> None: def test_atomic_mode() -> None: + """With atomic mode isort should be able to automatically detect and stop syntax errors""" # without syntax error, everything works OK test_input = "from b import d, c\nfrom a import f, e\n" assert isort.code(test_input, atomic=True) == ("from a import e, f\nfrom b import c, d\n") @@ -1377,6 +1379,13 @@ def test_atomic_mode() -> None: with pytest.raises(ExistingSyntaxErrors): isort.code(test_input, atomic=True) + # ensure atomic works with streams + test_input = as_stream("from b import d, c\nfrom a import f, e\n") + test_output = UnreadableStream() + isort.stream(test_input, test_output, atomic=True) + test_output.seek(0) + assert test_output.read() == "from a import e, f\nfrom b import c, d\n" + def test_order_by_type() -> None: test_input = "from module import Class, CONSTANT, function" diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index ccf51c0ee..207f19dc4 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -2,7 +2,6 @@ import os import subprocess from datetime import datetime -from io import BytesIO, TextIOWrapper import py import pytest @@ -14,15 +13,8 @@ from isort.exceptions import InvalidSettingsPath from isort.settings import DEFAULT_CONFIG, Config from isort.wrap_modes import WrapModes - - -class UnseekableTextIOWrapper(TextIOWrapper): - def seek(self, *args, **kwargs): - raise ValueError("underlying stream is not seekable") - - -def as_stream(text: str) -> UnseekableTextIOWrapper: - return UnseekableTextIOWrapper(BytesIO(text.encode("utf8"))) +from .utils import as_stream +from io import BytesIO, TextIOWrapper @given( @@ -910,6 +902,19 @@ def test_stream_skip_file(tmpdir, capsys): """ ) + atomic_input_without_skip = input_with_skip.replace("isort: skip_file", "generic comment") + stream_without_skip = as_stream(atomic_input_without_skip) + main.main(["-", "--atomic"], stdin=stream_without_skip) + out, error = capsys.readouterr() + assert ( + out + == """ +# generic comment +import a +import b +""" + ) + def test_only_modified_flag(tmpdir, capsys): # ensures there is no verbose output for correct files with only-modified flag diff --git a/tests/unit/utils.py b/tests/unit/utils.py index 9c963d637..b87e7a45e 100644 --- a/tests/unit/utils.py +++ b/tests/unit/utils.py @@ -1,6 +1,22 @@ +from io import BytesIO, StringIO, TextIOWrapper + import isort +class UnseekableTextIOWrapper(TextIOWrapper): + def seek(self, *args, **kwargs): + raise ValueError("underlying stream is not seekable") + + +class UnreadableStream(StringIO): + def readable(self, *args, **kwargs) -> bool: + return False + + +def as_stream(text: str) -> UnseekableTextIOWrapper: + return UnseekableTextIOWrapper(BytesIO(text.encode("utf8"))) + + def isort_test(code: str, expected_output: str = "", **config): """Runs isort against the given code snippet and ensures that it gives consistent output accross multiple runs, and if an expected_output From ec527fc004b1951757eba31eab5c62463796689c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Jun 2021 01:23:30 -0700 Subject: [PATCH 490/539] Update to latest version of codecov-action --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1238f7ff7..132172447 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -61,4 +61,4 @@ jobs: poetry run coverage xml - name: Report Coverage if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.8' - uses: codecov/codecov-action@v1.0.6 + uses: codecov/codecov-action@v1 From 9bc8c9a91725f6b8f27d8e1c4d97a3377e6ce64f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Jun 2021 01:54:26 -0700 Subject: [PATCH 491/539] Full coverage for isort/api.py --- isort/api.py | 4 ++-- tests/unit/test_isort.py | 15 +++++++++++++++ tests/unit/test_ticketed_features.py | 16 +++++++++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/isort/api.py b/isort/api.py index 047434632..642784f85 100644 --- a/isort/api.py +++ b/isort/api.py @@ -416,7 +416,7 @@ def sort_file( print(f"Fixing {source_file.path}") finally: try: # Python 3.8+: use `missing_ok=True` instead of try except. - if not config.overwrite_in_place: + if not config.overwrite_in_place: # pragma: no branch tmp_file = _tmp_file(source_file) tmp_file.unlink() except FileNotFoundError: @@ -511,7 +511,7 @@ def find_imports_in_stream( key = f"{identified_import.module}.{identified_import.attribute}" elif unique == ImportKey.MODULE: key = identified_import.module - elif unique == ImportKey.PACKAGE: + elif unique == ImportKey.PACKAGE: # pragma: no branch # type checking ensures this key = identified_import.module.split(".")[0] if key and key not in seen: diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index ec2fd5d68..754d47f6a 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -2198,6 +2198,21 @@ def test_other_file_encodings(tmpdir) -> None: assert tmp_fname.read_text(encoding) == file_contents +def test_other_file_encodings_in_place(tmpdir) -> None: + """Test to ensure file encoding is respected when overwritten in place.""" + for encoding in ("latin1", "utf8"): + tmp_fname = tmpdir.join(f"test_{encoding}.py") + file_contents = f"# coding: {encoding}\n\ns = u'ã'\n" + tmp_fname.write_binary(file_contents.encode(encoding)) + api.sort_file( + Path(tmp_fname), + file_path=Path(tmp_fname), + settings_path=os.getcwd(), + overwrite_in_place=True, + ) + assert tmp_fname.read_text(encoding) == file_contents + + def test_encoding_not_in_comment(tmpdir) -> None: """Test that 'encoding' not in a comment is ignored""" tmp_fname = tmpdir.join("test_encoding.py") diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index 0a4a5e9e5..a29700539 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -893,7 +893,6 @@ def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): to a different stream. See: https://github.com/PyCQA/isort/issues/1583 """ - tmp_file = tmpdir.join("file.py") tmp_file.write("import b\nimport a\n") @@ -914,6 +913,21 @@ def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): isort_output.seek(0) assert isort_output.read().splitlines() == ["import a", "import b"] + # should still work with no diff produced + tmp_file2 = tmpdir.join("file2.py") + tmp_file2.write("import a\nimport b\n") + + isort_diff2 = StringIO() + isort_output2 = StringIO() + + isort.file(tmp_file2, show_diff=isort_diff2, output=isort_output2) + + _, error = capsys.readouterr() + assert not error + + isort_diff2.seek(0) + assert not isort_diff2.read() + def test_autofix_mixed_indent_imports_1575(): """isort should automatically fix import statements that are sent in From 80e3c917445709c72467d70d3d144dd76e5eab68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ni=20Gauffier?= Date: Thu, 17 Jun 2021 11:05:03 +0200 Subject: [PATCH 492/539] add format-errors and format-success options --- docs/configuration/options.md | 28 ++++++++++++++++++++++++++++ isort/api.py | 4 +++- isort/format.py | 20 +++++++++++++------- isort/main.py | 26 ++++++++++++++++++++++---- isort/settings.py | 2 ++ tests/unit/test_format.py | 18 +++++++++++++++--- 6 files changed, 83 insertions(+), 15 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index 440a6c887..e07deb222 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -1312,6 +1312,34 @@ Tells isort to format the given files according to an extensions formatting rule - --ext-format +## Format Errors + +Define the format used to print errors. + +**NOTE** Variables are `error` and `message`. +The `error` variable prints `ERROR` with color depending on the --color option. + +**Type:** String +**Default:** `{error}: {message}` +**Python & Config File Name:** format_errors +**CLI Flags:** + +- --format-errors + +## Format Success + +Define the format used to print successes. + +**NOTE** Variables are `success` and `message`. +The `success` variable prints `SUCCESS` with color depending on the --color option. + +**Type:** String +**Default:** `{success}: {message}` +**Python & Config File Name:** format_success +**CLI Flags:** + +- --format-success + ## Deprecated Flags ==SUPPRESS== diff --git a/isort/api.py b/isort/api.py index 047434632..d7140e7f7 100644 --- a/isort/api.py +++ b/isort/api.py @@ -242,7 +242,9 @@ def check_stream( file_path=file_path, disregard_skip=disregard_skip, ) - printer = create_terminal_printer(color=config.color_output) + printer = create_terminal_printer( + color=config.color_output, error=config.format_error, success=config.format_success + ) if not changed: if config.verbose and not config.only_modified: printer.success(f"{file_path or ''} Everything Looks Good!") diff --git a/isort/format.py b/isort/format.py index ba00f8651..1db549137 100644 --- a/isort/format.py +++ b/isort/format.py @@ -95,22 +95,24 @@ class BasicPrinter: ERROR = "ERROR" SUCCESS = "SUCCESS" - def __init__(self, output: Optional[TextIO] = None): + def __init__(self, error: str, success: str, output: Optional[TextIO] = None): self.output = output or sys.stdout + self.success_message = success + self.error_message = error def success(self, message: str) -> None: - print(f"{self.SUCCESS}: {message}", file=self.output) + print(self.success_message.format(success=self.SUCCESS, message=message), file=self.output) def error(self, message: str) -> None: - print(f"{self.ERROR}: {message}", file=sys.stderr) + print(self.error_message.format(error=self.ERROR, message=message), file=sys.stderr) def diff_line(self, line: str) -> None: self.output.write(line) class ColoramaPrinter(BasicPrinter): - def __init__(self, output: Optional[TextIO] = None): - super().__init__(output=output) + def __init__(self, error: str, success: str, output: Optional[TextIO]): + super().__init__(error, success, output=output) # Note: this constants are instance variables instead ofs class variables # because they refer to colorama which might not be installed. @@ -134,7 +136,9 @@ def diff_line(self, line: str) -> None: self.output.write(self.style_text(line, style)) -def create_terminal_printer(color: bool, output: Optional[TextIO] = None): +def create_terminal_printer( + color: bool, output: Optional[TextIO] = None, error: str = "", success: str = "" +): if color and colorama_unavailable: no_colorama_message = ( "\n" @@ -147,4 +151,6 @@ def create_terminal_printer(color: bool, output: Optional[TextIO] = None): print(no_colorama_message, file=sys.stderr) sys.exit(1) - return ColoramaPrinter(output) if color else BasicPrinter(output) + return ( + ColoramaPrinter(error, success, output) if color else BasicPrinter(error, success, output) + ) diff --git a/isort/main.py b/isort/main.py index 6057ef388..f354d82d2 100644 --- a/isort/main.py +++ b/isort/main.py @@ -127,7 +127,9 @@ def _print_hard_fail( "This should NEVER happen.\n" "If encountered, please open an issue: https://github.com/PyCQA/isort/issues/new" ) - printer = create_terminal_printer(color=config.color_output) + printer = create_terminal_printer( + color=config.color_output, error=config.format_error, success=config.format_success + ) printer.error(message) @@ -297,6 +299,16 @@ def _build_arg_parser() -> argparse.ArgumentParser: action="store_true", help="Tells isort to apply changes interactively.", ) + general_group.add_argument( + "--format-error", + dest="format_error", + help="Override the format used to print errors.", + ) + general_group.add_argument( + "--format-success", + dest="format_success", + help="Override the format used to print success.", + ) target_group.add_argument( "files", nargs="*", help="One or more Python source files that need their imports sorted." @@ -1094,13 +1106,17 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = raise_on_skip=False, ) elif "/" in file_names and not allow_root: - printer = create_terminal_printer(color=config.color_output) + printer = create_terminal_printer( + color=config.color_output, error=config.format_error, success=config.format_success + ) printer.error("it is dangerous to operate recursively on '/'") printer.error("use --allow-root to override this failsafe") sys.exit(1) else: if stream_filename: - printer = create_terminal_printer(color=config.color_output) + printer = create_terminal_printer( + color=config.color_output, error=config.format_error, success=config.format_success + ) printer.error("Filename override is intended only for stream (-) sorting.") sys.exit(1) skipped: List[str] = [] @@ -1222,7 +1238,9 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = sys.exit(1) if no_valid_encodings: - printer = create_terminal_printer(color=config.color_output) + printer = create_terminal_printer( + color=config.color_output, error=config.format_error, success=config.format_success + ) printer.error("No valid encodings.") sys.exit(1) diff --git a/isort/settings.py b/isort/settings.py index c52de99cc..37dd67a07 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -217,6 +217,8 @@ class _Config: star_first: bool = False import_dependencies = Dict[str, str] git_ignore: Dict[Path, Set[Path]] = field(default_factory=dict) + format_error: str = "{error}: {message}" + format_success: str = "{success}: {message}" def __post_init__(self): py_version = self.py_version diff --git a/tests/unit/test_format.py b/tests/unit/test_format.py index f331578a8..527feaa0b 100644 --- a/tests/unit/test_format.py +++ b/tests/unit/test_format.py @@ -21,7 +21,9 @@ def test_ask_whether_to_apply_changes_to_file(): def test_basic_printer(capsys): - printer = isort.format.create_terminal_printer(color=False) + printer = isort.format.create_terminal_printer( + color=False, success="{success}: {message}", error="{error}: {message}" + ) printer.success("All good!") out, _ = capsys.readouterr() assert out == "SUCCESS: All good!\n" @@ -29,6 +31,16 @@ def test_basic_printer(capsys): _, err = capsys.readouterr() assert err == "ERROR: Some error\n" + printer = isort.format.create_terminal_printer( + color=False, success="success: {message}: {success}", error="error: {message}: {error}" + ) + printer.success("All good!") + out, _ = capsys.readouterr() + assert out == "success: All good!: SUCCESS\n" + printer.error("Some error") + _, err = capsys.readouterr() + assert err == "error: Some error: ERROR\n" + def test_basic_printer_diff(capsys): printer = isort.format.create_terminal_printer(color=False) @@ -40,7 +52,7 @@ def test_basic_printer_diff(capsys): def test_colored_printer_success(capsys): - printer = isort.format.create_terminal_printer(color=True) + printer = isort.format.create_terminal_printer(color=True, success="{success}: {message}") printer.success("All good!") out, _ = capsys.readouterr() assert "SUCCESS" in out @@ -49,7 +61,7 @@ def test_colored_printer_success(capsys): def test_colored_printer_error(capsys): - printer = isort.format.create_terminal_printer(color=True) + printer = isort.format.create_terminal_printer(color=True, error="{error}: {message}") printer.error("Some error") _, err = capsys.readouterr() assert "ERROR" in err From 5c0f120eab6da9a5cc22e771a8415714b7765aac Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Jun 2021 02:32:38 -0700 Subject: [PATCH 493/539] Implemented #1394: 100% branch coverage (in addition to line coverage) enforced. --- .coveragerc | 1 + CHANGELOG.md | 2 ++ isort/output.py | 6 ++-- isort/parse.py | 8 ++--- tests/unit/test_isort.py | 75 ++++++++++++++++++++++++++++++++-------- 5 files changed, 71 insertions(+), 21 deletions(-) diff --git a/.coveragerc b/.coveragerc index b9a746519..ba0e10c38 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,4 +1,5 @@ [run] +branch = True omit = isort/_future/* isort/_vendored/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cda3e3d7..ff0c7725b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1744: repeat noqa comments dropped when * import and non * imports exist from the same package. - Fixed #1721: repeat noqa comments on separate from lines with force-single-line set, sometimes get dropped. +#### Goal Zero (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): + - Implemented #1394: 100% branch coverage (in addition to line coverage) enforced. ### 5.8.0 March 20th 2021 - Fixed #1631: as import comments can in some cases be duplicated. diff --git a/isort/output.py b/isort/output.py index f53d81b72..9c2fe9acf 100644 --- a/isort/output.py +++ b/isort/output.py @@ -132,7 +132,7 @@ def sorted_imports( if config.dedup_headings: seen_headings.add(section_title) section_comment = f"# {section_title}" - if section_comment not in parsed.lines_without_imports[0:1]: + if section_comment not in parsed.lines_without_imports[0:1]: # pragma: no branch section_output.insert(0, section_comment) if pending_lines_before or not no_lines_before: @@ -173,7 +173,7 @@ def sorted_imports( next_construct = "" tail = formatted_output[imports_tail:] - for index, line in enumerate(tail): + for index, line in enumerate(tail): # pragma: no branch should_skip, in_quote, *_ = parse.skip_line( line, in_quote="", @@ -190,7 +190,7 @@ def sorted_imports( continue next_construct = line break - if in_quote: + if in_quote: # pragma: no branch next_construct = line break diff --git a/isort/parse.py b/isort/parse.py index 7a72497bd..aec508745 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -187,7 +187,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte ) if line in config.section_comments and not skipping_line: - if import_index == -1: + if import_index == -1: # pragma: no branch import_index = index - 1 continue @@ -394,7 +394,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte direct_imports.remove("as") if nested_module == as_name and config.remove_redundant_aliases: pass - elif as_name not in as_map["from"][module]: + elif as_name not in as_map["from"][module]: # pragma: no branch as_map["from"][module].append(as_name) full_name = f"{nested_module} as {as_name}" @@ -403,7 +403,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte categorized_comments["nested"].setdefault(top_level_module, {})[ full_name ] = associated_comment - if associated_comment in comments: + if associated_comment in comments: # pragma: no branch comments.pop(comments.index(associated_comment)) else: module = just_imports[as_index - 1] @@ -453,7 +453,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte categorized_comments["nested"].setdefault(import_from, {})[ import_name ] = associated_comment - if associated_comment in comments: + if associated_comment in comments: # pragma: no branch comments.pop(comments.index(associated_comment)) if ( config.force_single_line diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 754d47f6a..0599925e7 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -1266,8 +1266,6 @@ def test_force_single_line_imports_and_sort_within_sections() -> None: "from third_party import lib_d\n" ) - # Ensure force_sort_within_sections can work with length sort - # See: https://github.com/pycqa/isort/issues/1038 test_input = """import sympy import numpy as np import pandas as pd @@ -1280,21 +1278,59 @@ def test_force_single_line_imports_and_sort_within_sections() -> None: def test_titled_imports() -> None: """Tests setting custom titled/commented import sections.""" - test_input = ( + # test_input = ( + # "import sys\n" + # "import unicodedata\n" + # "import statistics\n" + # "import os\n" + # "import myproject.test\n" + # "import django.settings" + # ) + # test_output = isort.code( + # code=test_input, + # known_first_party=["myproject"], + # import_heading_stdlib="Standard Library", + # import_heading_firstparty="My Stuff", + # ) + # assert test_output == ( + # "# Standard Library\n" + # "import os\n" + # "import statistics\n" + # "import sys\n" + # "import unicodedata\n" + # "\n" + # "import django.settings\n" + # "\n" + # "# My Stuff\n" + # "import myproject.test\n" + # ) + # test_second_run = isort.code( + # code=test_output, + # known_first_party=["myproject"], + # import_heading_stdlib="Standard Library", + # import_heading_firstparty="My Stuff", + # ) + # assert test_second_run == test_output + + test_input_lines_down = ( + "# comment 1\n" + "import django.settings\n" + "\n" + "# Standard Library\n" "import sys\n" "import unicodedata\n" "import statistics\n" "import os\n" "import myproject.test\n" - "import django.settings" ) - test_output = isort.code( - code=test_input, + test_output_lines_down = isort.code( + code=test_input_lines_down, known_first_party=["myproject"], import_heading_stdlib="Standard Library", import_heading_firstparty="My Stuff", ) - assert test_output == ( + assert test_output_lines_down == ( + "# comment 1\n" "# Standard Library\n" "import os\n" "import statistics\n" @@ -1306,13 +1342,6 @@ def test_titled_imports() -> None: "# My Stuff\n" "import myproject.test\n" ) - test_second_run = isort.code( - code=test_output, - known_first_party=["myproject"], - import_heading_stdlib="Standard Library", - import_heading_firstparty="My Stuff", - ) - assert test_second_run == test_output def test_balanced_wrapping() -> None: @@ -1501,6 +1530,7 @@ def test_combined_from_and_as_imports() -> None: "from translate.storage.placeables import general, parse as rich_parse\n" ) assert isort.code(test_input, combine_as_imports=True) == test_input + assert isort.code(test_input, combine_as_imports=True, only_sections=True) == test_input test_input = "import os \nimport os as _os" test_output = "import os\nimport os as _os\n" assert isort.code(test_input) == test_output @@ -4804,6 +4834,23 @@ def test_noqa_issue_1065() -> None: """ assert isort.code(test_input, line_length=100) == expected_output + test_input_2 = """ +# +# USER SIGNALS +# + +from flask_login import user_logged_in, user_logged_out # noqa + +from flask_security.signals import ( + password_changed as user_reset_password, # noqa + user_confirmed, # noqa + user_registered, # noqa +) + +from flask_principal import identity_changed as user_identity_changed # noqa +""" + assert isort.code(test_input_2, line_length=100) == expected_output + def test_single_line_exclusions(): test_input = """ From 517cc88bc48754ca40619d7e33b65cf80e7394bf Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 17 Jun 2021 22:32:48 -0700 Subject: [PATCH 494/539] Add entry for fixing #1750 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0c7725b..a6b9dcd5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. - Implemented #1737: Support for using action comments to avoid adding imports to individual files. + - Implemented #1750: Ability to customize output format lines. - Fixed (https://github.com/PyCQA/isort/pull/1695): added imports being added to doc string in some cases. - Fixed (https://github.com/PyCQA/isort/pull/1714): in rare cases line continuation combined with tabs can output invalid code. - Fixed (https://github.com/PyCQA/isort/pull/1726): isort ignores reverse_sort when force_sort_within_sections is true. From be0fbd07817abcd5d2e62db9233b6633715d3405 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Fri, 18 Jun 2021 01:18:20 -0700 Subject: [PATCH 495/539] Implemented #1751: Strict typing enforcement (turned on mypy strict mode). --- CHANGELOG.md | 1 + isort/__init__.py | 19 +++++++++++++++ isort/_future/__init__.py | 2 +- isort/api.py | 42 ++++++++++++++++++++++---------- isort/core.py | 2 +- isort/deprecated/finders.py | 6 ++--- isort/format.py | 8 +++---- isort/identify.py | 2 +- isort/io.py | 10 ++++---- isort/literal.py | 10 +++++--- isort/main.py | 11 +++++---- isort/output.py | 16 +++++++------ isort/pylama_isort.py | 8 +++---- isort/settings.py | 33 +++++++++++++++++++------- isort/setuptools_commands.py | 4 ++-- isort/wrap_modes.py | 46 ++++++++++++++++++------------------ setup.cfg | 6 +---- 17 files changed, 140 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b9dcd5a..b6ed2d7d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ #### Goal Zero (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan): - Implemented #1394: 100% branch coverage (in addition to line coverage) enforced. + - Implemented #1751: Strict typing enforcement (turned on mypy strict mode). ### 5.8.0 March 20th 2021 - Fixed #1631: as import comments can in some cases be duplicated. diff --git a/isort/__init__.py b/isort/__init__.py index fdb1d6e93..e0754da42 100644 --- a/isort/__init__.py +++ b/isort/__init__.py @@ -1,4 +1,23 @@ """Defines the public isort interface""" +__all__ = ( + "Config", + "ImportKey", + "__version__", + "check_code", + "check_file", + "check_stream", + "code", + "file", + "find_imports_in_code", + "find_imports_in_file", + "find_imports_in_paths", + "find_imports_in_stream", + "place_module", + "place_module_with_reason", + "settings", + "stream", +) + from . import settings from ._version import __version__ from .api import ImportKey diff --git a/isort/_future/__init__.py b/isort/_future/__init__.py index 4d9ef4b76..48028537b 100644 --- a/isort/_future/__init__.py +++ b/isort/_future/__init__.py @@ -1,7 +1,7 @@ import sys if sys.version_info.major <= 3 and sys.version_info.minor <= 6: - from . import _dataclasses as dataclasses # type: ignore + from . import _dataclasses as dataclasses else: import dataclasses # type: ignore diff --git a/isort/api.py b/isort/api.py index 4d6f5c461..fcc0bd231 100644 --- a/isort/api.py +++ b/isort/api.py @@ -1,3 +1,19 @@ +__all__ = ( + "ImportKey", + "check_code_string", + "check_file", + "check_stream", + "find_imports_in_code", + "find_imports_in_file", + "find_imports_in_paths", + "find_imports_in_stream", + "place_module", + "place_module_with_reason", + "sort_code_string", + "sort_file", + "sort_stream", +) + import contextlib import shutil import sys @@ -5,7 +21,7 @@ from io import StringIO from itertools import chain from pathlib import Path -from typing import Iterator, Optional, Set, TextIO, Union, cast +from typing import Any, Iterator, Optional, Set, TextIO, Union, cast from warnings import warn from isort import core @@ -57,8 +73,8 @@ def sort_code_string( file_path: Optional[Path] = None, disregard_skip: bool = False, show_diff: Union[bool, TextIO] = False, - **config_kwargs, -): + **config_kwargs: Any, +) -> str: """Sorts any imports within the provided code string, returning a new string with them sorted. - **code**: The string of code with imports that need to be sorted. @@ -93,7 +109,7 @@ def check_code_string( config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, disregard_skip: bool = False, - **config_kwargs, + **config_kwargs: Any, ) -> bool: """Checks the order, format, and categorization of imports within the provided code string. Returns `True` if everything is correct, otherwise `False`. @@ -127,7 +143,7 @@ def sort_stream( disregard_skip: bool = False, show_diff: Union[bool, TextIO] = False, raise_on_skip: bool = True, - **config_kwargs, + **config_kwargs: Any, ) -> bool: """Sorts any imports within the provided code stream, outputs to the provided output stream. Returns `True` if anything is modified from the original input stream, otherwise `False`. @@ -215,7 +231,7 @@ def check_stream( config: Config = DEFAULT_CONFIG, file_path: Optional[Path] = None, disregard_skip: bool = False, - **config_kwargs, + **config_kwargs: Any, ) -> bool: """Checks any imports within the provided code stream, returning `False` if any unsorted or incorrectly imports are found or `True` if no problems are identified. @@ -282,7 +298,7 @@ def check_file( file_path: Optional[Path] = None, disregard_skip: bool = True, extension: Optional[str] = None, - **config_kwargs, + **config_kwargs: Any, ) -> bool: """Checks any imports within the provided file, returning `False` if any unsorted or incorrectly imports are found or `True` if no problems are identified. @@ -335,7 +351,7 @@ def sort_file( show_diff: Union[bool, TextIO] = False, write_to_stdout: bool = False, output: Optional[TextIO] = None, - **config_kwargs, + **config_kwargs: Any, ) -> bool: """Sorts and formats any groups of imports imports within the provided file or Path. Returns `True` if the file has been changed, otherwise `False`. @@ -458,7 +474,7 @@ def find_imports_in_code( file_path: Optional[Path] = None, unique: Union[bool, ImportKey] = False, top_only: bool = False, - **config_kwargs, + **config_kwargs: Any, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided code string. @@ -486,7 +502,7 @@ def find_imports_in_stream( unique: Union[bool, ImportKey] = False, top_only: bool = False, _seen: Optional[Set[str]] = None, - **config_kwargs, + **config_kwargs: Any, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided code stream. @@ -527,7 +543,7 @@ def find_imports_in_file( file_path: Optional[Path] = None, unique: Union[bool, ImportKey] = False, top_only: bool = False, - **config_kwargs, + **config_kwargs: Any, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided source file. @@ -556,7 +572,7 @@ def find_imports_in_paths( file_path: Optional[Path] = None, unique: Union[bool, ImportKey] = False, top_only: bool = False, - **config_kwargs, + **config_kwargs: Any, ) -> Iterator[identify.Import]: """Finds and returns all imports within the provided source paths. @@ -581,7 +597,7 @@ def find_imports_in_paths( def _config( - path: Optional[Path] = None, config: Config = DEFAULT_CONFIG, **config_kwargs + path: Optional[Path] = None, config: Config = DEFAULT_CONFIG, **config_kwargs: Any ) -> Config: if path and ( config is DEFAULT_CONFIG diff --git a/isort/core.py b/isort/core.py index 3bfd9618e..7b2910da5 100644 --- a/isort/core.py +++ b/isort/core.py @@ -445,7 +445,7 @@ def process( return made_changes -def _indented_config(config: Config, indent: str): +def _indented_config(config: Config, indent: str) -> Config: if not indent: return config diff --git a/isort/deprecated/finders.py b/isort/deprecated/finders.py index 5be8a419f..52bd7cf84 100644 --- a/isort/deprecated/finders.py +++ b/isort/deprecated/finders.py @@ -19,19 +19,19 @@ from isort.utils import exists_case_sensitive try: - from pipreqs import pipreqs + from pipreqs import pipreqs # type: ignore except ImportError: pipreqs = None try: - from pip_api import parse_requirements + from pip_api import parse_requirements # type: ignore except ImportError: parse_requirements = None try: - from requirementslib import Pipfile + from requirementslib import Pipfile # type: ignore except ImportError: Pipfile = None diff --git a/isort/format.py b/isort/format.py index 1db549137..4de09a423 100644 --- a/isort/format.py +++ b/isort/format.py @@ -6,7 +6,7 @@ from typing import Optional, TextIO try: - import colorama + import colorama # type: ignore except ImportError: colorama_unavailable = True else: @@ -48,7 +48,7 @@ def show_unified_diff( file_path: Optional[Path], output: Optional[TextIO] = None, color_output: bool = False, -): +) -> None: """Shows a unified_diff for the provided input and output against the provided file path. - **file_input**: A string that represents the contents of a file before changes. @@ -125,7 +125,7 @@ def __init__(self, error: str, success: str, output: Optional[TextIO]): def style_text(text: str, style: Optional[str] = None) -> str: if style is None: return text - return style + text + colorama.Style.RESET_ALL + return style + text + str(colorama.Style.RESET_ALL) def diff_line(self, line: str) -> None: style = None @@ -138,7 +138,7 @@ def diff_line(self, line: str) -> None: def create_terminal_printer( color: bool, output: Optional[TextIO] = None, error: str = "", success: str = "" -): +) -> BasicPrinter: if color and colorama_unavailable: no_colorama_message = ( "\n" diff --git a/isort/identify.py b/isort/identify.py index 6a4f6d7d8..e45fcb391 100644 --- a/isort/identify.py +++ b/isort/identify.py @@ -32,7 +32,7 @@ def statement(self) -> str: import_string += f" as {self.alias}" return import_string - def __str__(self): + def __str__(self) -> str: return ( f"{self.file_path or ''}:{self.line_number} " f"{'indented ' if self.indented else ''}{self.statement()}" diff --git a/isort/io.py b/isort/io.py index 7b107ab02..e7a74bfd7 100644 --- a/isort/io.py +++ b/isort/io.py @@ -4,7 +4,7 @@ from contextlib import contextmanager from io import BytesIO, StringIO, TextIOWrapper from pathlib import Path -from typing import Callable, Iterator, TextIO, Union +from typing import Any, Callable, Iterator, TextIO, Union from isort._future import dataclass from isort.exceptions import UnsupportedEncoding @@ -19,7 +19,7 @@ class File: encoding: str @staticmethod - def detect_encoding(filename: str, readline: Callable[[], bytes]): + def detect_encoding(filename: Union[str, Path], readline: Callable[[], bytes]) -> str: try: return tokenize.detect_encoding(readline)[0] except Exception: @@ -33,11 +33,11 @@ def from_contents(contents: str, filename: str) -> "File": ) @property - def extension(self): + def extension(self) -> str: return self.path.suffix.lstrip(".") @staticmethod - def _open(filename): + def _open(filename: Union[str, Path]) -> TextIOWrapper: """Open a file in read only mode using the encoding detected by detect_encoding(). """ @@ -66,7 +66,7 @@ def read(filename: Union[str, Path]) -> Iterator["File"]: class _EmptyIO(StringIO): - def write(self, *args, **kwargs): # skipcq: PTC-W0049 + def write(self, *args: Any, **kwargs: Any) -> None: # type: ignore # skipcq: PTC-W0049 pass diff --git a/isort/literal.py b/isort/literal.py index 0b1838fe3..62feea13f 100644 --- a/isort/literal.py +++ b/isort/literal.py @@ -69,10 +69,14 @@ def assignment(code: str, sort_type: str, extension: str, config: Config = DEFAU return sorted_value_code -def register_type(name: str, kind: type): +def register_type( + name: str, kind: type +) -> Callable[[Callable[[Any, ISortPrettyPrinter], str]], Callable[[Any, ISortPrettyPrinter], str]]: """Registers a new literal sort type.""" - def wrap(function): + def wrap( + function: Callable[[Any, ISortPrettyPrinter], str] + ) -> Callable[[Any, ISortPrettyPrinter], str]: type_mapping[name] = (kind, function) return function @@ -81,7 +85,7 @@ def wrap(function): @register_type("dict", dict) def _dict(value: Dict[Any, Any], printer: ISortPrettyPrinter) -> str: - return printer.pformat(dict(sorted(value.items(), key=lambda item: item[1]))) + return printer.pformat(dict(sorted(value.items(), key=lambda item: item[1]))) # type: ignore @register_type("list", list) diff --git a/isort/main.py b/isort/main.py index f354d82d2..cc344370f 100644 --- a/isort/main.py +++ b/isort/main.py @@ -7,7 +7,7 @@ from gettext import gettext as _ from io import TextIOWrapper from pathlib import Path -from typing import Any, Dict, List, Optional, Sequence +from typing import Any, Dict, List, Optional, Sequence, Union from warnings import warn from . import __version__, api, files, sections @@ -15,7 +15,8 @@ from .format import create_terminal_printer from .logo import ASCII_ART from .profiles import profiles -from .settings import VALID_PY_TARGETS, Config, WrapModes +from .settings import VALID_PY_TARGETS, Config +from .wrap_modes import WrapModes try: from .setuptools_commands import ISortCommand # noqa: F401 @@ -920,16 +921,16 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> Dict[str, Any]: return arguments -def _preconvert(item): +def _preconvert(item: Any) -> Union[str, List[Any]]: """Preconverts objects from native types into JSONifyiable types""" if isinstance(item, (set, frozenset)): return list(item) if isinstance(item, WrapModes): - return item.name + return str(item.name) if isinstance(item, Path): return str(item) if callable(item) and hasattr(item, "__name__"): - return item.__name__ + return str(item.__name__) raise TypeError("Unserializable object {} of type {}".format(item, type(item))) diff --git a/isort/output.py b/isort/output.py index 9c2fe9acf..884bfb636 100644 --- a/isort/output.py +++ b/isort/output.py @@ -1,7 +1,7 @@ import copy import itertools from functools import partial -from typing import Iterable, List, Set, Tuple +from typing import Any, Iterable, List, Optional, Set, Tuple, Type from isort.format import format_simplified @@ -609,19 +609,21 @@ def _normalize_empty_lines(lines: List[str]) -> List[str]: class _LineWithComments(str): comments: List[str] - def __new__(cls, value: str, comments: List[str]): - instance = super().__new__(cls, value) # type: ignore + def __new__( + cls: Type["_LineWithComments"], value: Any, comments: List[str] + ) -> "_LineWithComments": + instance = super().__new__(cls, value) instance.comments = comments return instance -def _ensure_newline_before_comment(output): +def _ensure_newline_before_comment(output: List[str]) -> List[str]: new_output: List[str] = [] - def is_comment(line): - return line and line.startswith("#") + def is_comment(line: Optional[str]) -> bool: + return line.startswith("#") if line else False - for line, prev_line in zip(output, [None] + output): + for line, prev_line in zip(output, [None] + output): # type: ignore if is_comment(line) and prev_line != "" and not is_comment(prev_line): new_output.append("") new_output.append(line) diff --git a/isort/pylama_isort.py b/isort/pylama_isort.py index 4d4ffb922..d8fad9a8d 100644 --- a/isort/pylama_isort.py +++ b/isort/pylama_isort.py @@ -1,9 +1,9 @@ import os import sys from contextlib import contextmanager -from typing import Any, Dict, List +from typing import Any, Dict, Iterator, List -from pylama.lint import Linter as BaseLinter +from pylama.lint import Linter as BaseLinter # type: ignore from isort.exceptions import FileSkipped @@ -11,7 +11,7 @@ @contextmanager -def supress_stdout(): +def supress_stdout() -> Iterator[None]: stdout = sys.stdout with open(os.devnull, "w") as devnull: sys.stdout = devnull @@ -19,7 +19,7 @@ def supress_stdout(): sys.stdout = stdout -class Linter(BaseLinter): +class Linter(BaseLinter): # type: ignore def allow(self, path: str) -> bool: """Determine if this path should be linted.""" return path.endswith(".py") diff --git a/isort/settings.py b/isort/settings.py index 37dd67a07..5b70eb07f 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -14,7 +14,20 @@ import sys from functools import lru_cache from pathlib import Path -from typing import Any, Callable, Dict, FrozenSet, Iterable, List, Optional, Pattern, Set, Tuple +from typing import ( + Any, + Callable, + Dict, + FrozenSet, + Iterable, + List, + Optional, + Pattern, + Set, + Tuple, + Type, + Union, +) from warnings import warn from . import stdlibs @@ -220,7 +233,7 @@ class _Config: format_error: str = "{error}: {message}" format_success: str = "{success}: {message}" - def __post_init__(self): + def __post_init__(self) -> None: py_version = self.py_version if py_version == "auto": # pragma: no cover if sys.version_info.major == 2 and sys.version_info.minor <= 6: @@ -261,7 +274,7 @@ def __post_init__(self): f"{self.wrap_length} > {self.line_length}." ) - def __hash__(self): + def __hash__(self) -> int: return id(self) @@ -274,7 +287,7 @@ def __init__( settings_file: str = "", settings_path: str = "", config: Optional[_Config] = None, - **config_overrides, + **config_overrides: Any, ): self._known_patterns: Optional[List[Tuple[Pattern[str], str]]] = None self._section_comments: Optional[Tuple[str, ...]] = None @@ -487,7 +500,7 @@ def __init__( super().__init__(sources=tuple(sources), **combined_config) # type: ignore - def is_supported_filetype(self, file_name: str): + def is_supported_filetype(self, file_name: str) -> bool: _root, ext = os.path.splitext(file_name) ext = ext.lstrip(".") if ext in self.supported_extensions: @@ -590,7 +603,7 @@ def is_skipped(self, file_path: Path) -> bool: return False @property - def known_patterns(self): + def known_patterns(self) -> List[Tuple[Pattern[str], str]]: if self._known_patterns is not None: return self._known_patterns @@ -651,8 +664,10 @@ def _parse_known_pattern(self, pattern: str) -> List[str]: return patterns -def _get_str_to_type_converter(setting_name: str) -> Callable[[str], Any]: - type_converter: Callable[[str], Any] = type(_DEFAULT_SETTINGS.get(setting_name, "")) +def _get_str_to_type_converter(setting_name: str) -> Union[Callable[[str], Any], Type[Any]]: + type_converter: Union[Callable[[str], Any], Type[Any]] = type( + _DEFAULT_SETTINGS.get(setting_name, "") + ) if type_converter == WrapModes: type_converter = wrap_mode_from_string return type_converter @@ -742,7 +757,7 @@ def _get_config_data(file_path: str, sections: Tuple[str]) -> Dict[str, Any]: and config_key.endswith("}") and extension in map( - lambda text: text.strip(), config_key[len("*.{") : -1].split(",") + lambda text: text.strip(), config_key[len("*.{") : -1].split(",") # type: ignore # noqa ) ): settings.update(config.items(config_key)) diff --git a/isort/setuptools_commands.py b/isort/setuptools_commands.py index 6906604ed..d60deda61 100644 --- a/isort/setuptools_commands.py +++ b/isort/setuptools_commands.py @@ -4,13 +4,13 @@ from typing import Any, Dict, Iterator, List from warnings import warn -import setuptools +import setuptools # type: ignore from . import api from .settings import DEFAULT_CONFIG -class ISortCommand(setuptools.Command): +class ISortCommand(setuptools.Command): # type: ignore """The :class:`ISortCommand` class is used by setuptools to perform imports checks on registered modules. """ diff --git a/isort/wrap_modes.py b/isort/wrap_modes.py index 7118afc24..39143e739 100644 --- a/isort/wrap_modes.py +++ b/isort/wrap_modes.py @@ -5,14 +5,14 @@ import isort.comments -_wrap_modes: Dict[str, Callable[[Any], str]] = {} +_wrap_modes: Dict[str, Callable[..., str]] = {} def from_string(value: str) -> "WrapModes": return getattr(WrapModes, str(value), None) or WrapModes(int(value)) -def formatter_from_string(name: str): +def formatter_from_string(name: str) -> Callable[..., str]: return _wrap_modes.get(name.upper(), grid) @@ -32,18 +32,18 @@ def _wrap_mode_interface( return "" -def _wrap_mode(function): +def _wrap_mode(function: Callable[..., str]) -> Callable[..., str]: """Registers an individual wrap mode. Function name and order are significant and used for creating enum. """ _wrap_modes[function.__name__.upper()] = function - function.__signature__ = signature(_wrap_mode_interface) + function.__signature__ = signature(_wrap_mode_interface) # type: ignore function.__annotations__ = _wrap_mode_interface.__annotations__ return function @_wrap_mode -def grid(**interface): +def grid(**interface: Any) -> str: if not interface["imports"]: return "" @@ -80,11 +80,11 @@ def grid(**interface): interface["comments"] = [] else: interface["statement"] += ", " + next_import - return interface["statement"] + ("," if interface["include_trailing_comma"] else "") + ")" + return f"{interface['statement']}{',' if interface['include_trailing_comma'] else ''})" @_wrap_mode -def vertical(**interface): +def vertical(**interface: Any) -> str: if not interface["imports"]: return "" @@ -113,7 +113,7 @@ def _hanging_indent_end_line(line: str) -> str: @_wrap_mode -def hanging_indent(**interface): +def hanging_indent(**interface: Any) -> str: if not interface["imports"]: return "" @@ -157,7 +157,7 @@ def hanging_indent(**interface): return statement_with_comments return ( _hanging_indent_end_line(interface["statement"]) - + interface["line_separator"] + + str(interface["line_separator"]) + isort.comments.add_to_line( interface["comments"], interface["indent"], @@ -165,11 +165,11 @@ def hanging_indent(**interface): comment_prefix=interface["comment_prefix"].lstrip(), ) ) - return interface["statement"] + return str(interface["statement"]) @_wrap_mode -def vertical_hanging_indent(**interface): +def vertical_hanging_indent(**interface: Any) -> str: _line_with_comments = isort.comments.add_to_line( interface["comments"], "", @@ -184,7 +184,7 @@ def vertical_hanging_indent(**interface): ) -def _vertical_grid_common(need_trailing_char: bool, **interface): +def _vertical_grid_common(need_trailing_char: bool, **interface: Any) -> str: if not interface["imports"]: return "" @@ -217,32 +217,32 @@ def _vertical_grid_common(need_trailing_char: bool, **interface): interface["statement"] = next_statement if interface["include_trailing_comma"]: interface["statement"] += "," - return interface["statement"] + return str(interface["statement"]) @_wrap_mode -def vertical_grid(**interface) -> str: +def vertical_grid(**interface: Any) -> str: return _vertical_grid_common(need_trailing_char=True, **interface) + ")" @_wrap_mode -def vertical_grid_grouped(**interface): +def vertical_grid_grouped(**interface: Any) -> str: return ( _vertical_grid_common(need_trailing_char=False, **interface) - + interface["line_separator"] + + str(interface["line_separator"]) + ")" ) @_wrap_mode -def vertical_grid_grouped_no_comma(**interface): +def vertical_grid_grouped_no_comma(**interface: Any) -> str: # This is a deprecated alias for vertical_grid_grouped above. This function # needs to exist for backwards compatibility but should never get called. raise NotImplementedError @_wrap_mode -def noqa(**interface): +def noqa(**interface: Any) -> str: _imports = ", ".join(interface["imports"]) retval = f"{interface['statement']}{_imports}" comment_str = " ".join(interface["comments"]) @@ -262,7 +262,7 @@ def noqa(**interface): @_wrap_mode -def vertical_hanging_indent_bracket(**interface): +def vertical_hanging_indent_bracket(**interface: Any) -> str: if not interface["imports"]: return "" statement = vertical_hanging_indent(**interface) @@ -270,7 +270,7 @@ def vertical_hanging_indent_bracket(**interface): @_wrap_mode -def vertical_prefix_from_module_import(**interface): +def vertical_prefix_from_module_import(**interface: Any) -> str: if not interface["imports"]: return "" @@ -306,11 +306,11 @@ def vertical_prefix_from_module_import(**interface): if comments and statement_with_comments: output_statement = statement_with_comments - return output_statement + return str(output_statement) @_wrap_mode -def hanging_indent_with_parentheses(**interface): +def hanging_indent_with_parentheses(**interface: Any) -> str: if not interface["imports"]: return "" @@ -366,7 +366,7 @@ def hanging_indent_with_parentheses(**interface): @_wrap_mode -def backslash_grid(**interface): +def backslash_grid(**interface: Any) -> str: interface["indent"] = interface["white_space"][:-1] return hanging_indent(**interface) diff --git a/setup.cfg b/setup.cfg index aa714e7a9..cff281a90 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,11 +1,7 @@ [mypy] python_version = 3.6 +strict = True follow_imports = silent -disallow_any_generics = True -strict_optional = True -check_untyped_defs = True -allow_redefinition = True -ignore_missing_imports = True [mypy-isort.isort._vendored.*] ignore_errors = True From 4a3d67788d0f02c8e436ac2f4b9aaf9292327d18 Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Sat, 19 Jun 2021 12:08:38 +0530 Subject: [PATCH 496/539] Correct links --- docs/major_releases/introducing_isort_5.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/major_releases/introducing_isort_5.md b/docs/major_releases/introducing_isort_5.md index 4f6463ea1..577d147d0 100644 --- a/docs/major_releases/introducing_isort_5.md +++ b/docs/major_releases/introducing_isort_5.md @@ -6,11 +6,11 @@ isort 5.0.0 is the first major release of isort in over five years and the first It's also the first version to require Python 3 (Python 3.6+ at that!) to run - though it can still be run on source files from any version of Python. This does mean that there may be some pain with the upgrade process, but we believe the improvements will be well worth it. -[Click here for an attempt at full changelog with a list of breaking changes.](https://pycqa.github.io/isort/CHANGELOG/) +[Click here for an attempt at full changelog with a list of breaking changes.](https://pycqa.github.io/isort/CHANGELOG.html) -[Using isort 4.x.x? Click here for the isort 5.0.0 upgrade guide.](https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0/) +[Using isort 4.x.x? Click here for the isort 5.0.0 upgrade guide.](https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html) -[Try isort 5 right now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try/) +[Try isort 5 right now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try.html) So why the massive change? @@ -105,7 +105,7 @@ import c import d ``` -isort 5 adds support for [Action Comments](https://pycqa.github.io/isort/docs/configuration/action_comments/) which provide a quick and convient way to control the flow of parsing within single source files. +isort 5 adds support for [Action Comments](https://pycqa.github.io/isort/docs/configuration/action_comments.html) which provide a quick and convient way to control the flow of parsing within single source files. # First class Python API @@ -122,7 +122,7 @@ import b """ ``` -isort now exposes its programmatic API as a first-class citizen. This API makes it easy to extend or use isort in your own Python project. You can see the full documentation for this new API [here](https://pycqa.github.io/isort/reference/isort/api/). +isort now exposes its programmatic API as a first-class citizen. This API makes it easy to extend or use isort in your own Python project. You can see the full documentation for this new API [here](https://pycqa.github.io/isort/reference/isort/api.html). # Solid base for the future @@ -133,10 +133,10 @@ It went from fully dynamic to fully static typing using mypy. Finally, it utiliz # Give 5.0.0 a try! -[Try isort 5 right now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try/) +[Try isort 5 right now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try.html) OR Install isort locally using `pip3 install isort`. -[Click here for full installation instructions.](https://pycqa.github.io/isort/docs/quick_start/1.-install/) +[Click here for full installation instructions.](https://pycqa.github.io/isort/docs/quick_start/1.-install.html) From 9bfcfa839d3b09904994e7b15c55737354a53e54 Mon Sep 17 00:00:00 2001 From: Bob Walker Date: Sat, 19 Jun 2021 15:46:24 +0100 Subject: [PATCH 497/539] 1732 adds sort_order allowing pythonic sort as an option --- docs/configuration/options.md | 11 +++++++++++ isort/main.py | 6 ++++++ isort/output.py | 18 +++++++++++------- isort/settings.py | 1 + isort/sorting.py | 12 ++++++++++++ tests/unit/test_isort.py | 19 +++++++++++++++++++ 6 files changed, 60 insertions(+), 7 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index e07deb222..c605e39f8 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -1074,6 +1074,17 @@ Tells isort to overwrite in place using the same file handle.Comes at a performa - --overwrite-in-place +## Sorting Order + +Use natural language or pythonic sorting. Valid values "default"=natural "pythonic"=python default sorting. + +**Type:** String +**Default:** `default` +**Python & Config File Name:** sort_order +**CLI Flags:** + +- --sort-order + ## Reverse Sort Reverses the ordering of imports. diff --git a/isort/main.py b/isort/main.py index cc344370f..e8af743fe 100644 --- a/isort/main.py +++ b/isort/main.py @@ -582,6 +582,12 @@ def _build_arg_parser() -> argparse.ArgumentParser: action="store_true", help="Reverses the ordering of imports.", ) + output_group.add_argument( + "--sort-order", + dest="sort_order", + choices=["default", "pythonic"], + help="Use natural language or pythonic sorting.", + ) inline_args_group.add_argument( "--sl", "--force-single-line-imports", diff --git a/isort/output.py b/isort/output.py index 884bfb636..76f2637d4 100644 --- a/isort/output.py +++ b/isort/output.py @@ -49,7 +49,8 @@ def sorted_imports( for section in sections: straight_modules = parsed.imports[section]["straight"] if not config.only_sections: - straight_modules = sorting.naturally( + straight_modules = sorting.sort( + config, straight_modules, key=lambda key: sorting.module_key( key, config, section_name=section, straight_import=True @@ -59,7 +60,8 @@ def sorted_imports( from_modules = parsed.imports[section]["from"] if not config.only_sections: - from_modules = sorting.naturally( + from_modules = sorting.sort( + config, from_modules, key=lambda key: sorting.module_key(key, config, section_name=section), reverse=config.reverse_sort, @@ -105,7 +107,8 @@ def sorted_imports( else: new_section_output.append(line) # only_sections options is not imposed if force_sort_within_sections is True - new_section_output = sorting.naturally( + new_section_output = sorting.sort( + config, new_section_output, key=partial(sorting.section_key, config=config), reverse=config.reverse_sort, @@ -238,7 +241,8 @@ def _with_from_imports( not config.no_inline_sort or (config.force_single_line and module not in config.single_line_exclusions) ) and not config.only_sections: - from_imports = sorting.naturally( + from_imports = sorting.sort( + config, from_imports, key=lambda key: sorting.module_key( key, @@ -266,7 +270,7 @@ def _with_from_imports( if not config.no_inline_sort: for as_import in as_imports: if not config.only_sections: - as_imports[as_import] = sorting.naturally(as_imports[as_import]) + as_imports[as_import] = sorting.sort(config, as_imports[as_import]) for from_import in copy.copy(from_imports): if from_import in as_imports: idx = from_imports.index(from_import) @@ -337,7 +341,7 @@ def _with_from_imports( removed=config.ignore_comments, comment_prefix=config.comment_prefix, ) - for as_import in sorting.naturally(as_imports[from_import]) + for as_import in sorting.sort(config, as_imports[from_import]) ) else: @@ -360,7 +364,7 @@ def _with_from_imports( from_import = from_imports.pop(0) if not config.only_sections: - as_imports[from_import] = sorting.naturally(as_imports[from_import]) + as_imports[from_import] = sorting.sort(config, as_imports[from_import]) from_comments = ( parsed.categorized_comments["straight"].get(f"{module}.{from_import}") or [] ) diff --git a/isort/settings.py b/isort/settings.py index 5b70eb07f..e4c32cab3 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -232,6 +232,7 @@ class _Config: git_ignore: Dict[Path, Set[Path]] = field(default_factory=dict) format_error: str = "{error}: {message}" format_success: str = "{success}: {message}" + sort_order: str = "default" def __post_init__(self) -> None: py_version = self.py_version diff --git a/isort/sorting.py b/isort/sorting.py index e8c355bab..60727662f 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -96,6 +96,18 @@ def section_key(line: str, config: Config) -> str: return f"{section}{len(line) if config.length_sort else ''}{line}" +def sort( + config: Config, + to_sort: Iterable[str], + key: Optional[Callable[[str], Any]] = None, + reverse: bool = False, +) -> List[str]: + sorting_func: Callable[..., List[str]] = naturally + if config.sort_order == "pythonic": + sorting_func = sorted + return sorting_func(to_sort, key=key, reverse=reverse) + + def naturally( to_sort: Iterable[str], key: Optional[Callable[[str], Any]] = None, reverse: bool = False ) -> List[str]: diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 0599925e7..9593c42c8 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -5236,3 +5236,22 @@ def seekable(self): test_input = NonSeekableTestStream("import m2\n" "import m1\n" "not_import = 7") identified_imports = list(map(str, api.find_imports_in_stream(test_input))) assert identified_imports == [":1 import m2", ":2 import m1"] + + +def test_sort_pythonic() -> None: + """ Test a plugged-in default python sort with packages/modules containing numbers. """ + test_input = ( + "from bob2.apples2 import aardvark as aardvark2\n" + "from bob.apples import aardvark \n" + "import module9\n" + "import module10\n" + "import module200\n" + ) + test_output = isort.code(test_input, sort_order="pythonic") + assert test_output == ( + "import module10\n" + "import module200\n" + "import module9\n" + "from bob.apples import aardvark\n" + "from bob2.apples2 import aardvark as aardvark2\n" + ) From c9a4b1dbdb617c9363b7b379277c1aa672af5cab Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Sun, 20 Jun 2021 22:34:57 +0530 Subject: [PATCH 498/539] Correct links in docs --- docs/configuration/black_compatibility.md | 6 +++--- docs/configuration/config_files.md | 2 +- docs/configuration/custom_sections_and_ordering.md | 4 ++-- docs/configuration/multi_line_output_modes.md | 2 +- docs/configuration/options.md | 2 +- docs/upgrade_guides/5.0.0.md | 8 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/configuration/black_compatibility.md b/docs/configuration/black_compatibility.md index c46552548..ed96f7ef5 100644 --- a/docs/configuration/black_compatibility.md +++ b/docs/configuration/black_compatibility.md @@ -7,7 +7,7 @@ Compatibility with black is very important to the isort project and comes baked All that's required to use isort alongside black is to set the isort profile to "black". !!! tip - Beyond the profile, it is common to set [skip_gitignore](https://pycqa.github.io/isort/docs/configuration/options/#skip-gitignore) (which is not enabled by default for isort as it requires git to be installed) and [line_length](https://pycqa.github.io/isort/docs/configuration/options/#skip-gitignore) as it is common to deviate from black's default of 88. + Beyond the profile, it is common to set [skip_gitignore](https://pycqa.github.io/isort/docs/configuration/options.html#skip-gitignore) (which is not enabled by default for isort as it requires git to be installed) and [line_length](https://pycqa.github.io/isort/docs/configuration/options.html#line-length) as it is common to deviate from black's default of 88. ## Using a config file (such as .isort.cfg) @@ -23,7 +23,7 @@ profile = "black" multi_line_output = 3 ``` -Read More about supported [config files](https://pycqa.github.io/isort/docs/configuration/config_files/). +Read More about supported [config files](https://pycqa.github.io/isort/docs/configuration/config_files.html). ## CLI @@ -51,7 +51,7 @@ after_success: ``` -See [built-in profiles](https://pycqa.github.io/isort/docs/configuration/profiles/) for more profiles. +See [built-in profiles](https://pycqa.github.io/isort/docs/configuration/profiles.html) for more profiles. ## Integration with pre-commit diff --git a/docs/configuration/config_files.md b/docs/configuration/config_files.md index 814b1ec63..8216a7aa0 100644 --- a/docs/configuration/config_files.md +++ b/docs/configuration/config_files.md @@ -81,7 +81,7 @@ src_paths=isort,test ## Custom config files -Optionally, you can also create a config file with a custom name, or directly point isort to a config file that falls lower in the priority order, by using [--settings-file](https://pycqa.github.io/isort/docs/configuration/options/#settings-path). +Optionally, you can also create a config file with a custom name, or directly point isort to a config file that falls lower in the priority order, by using [--settings-file](https://pycqa.github.io/isort/docs/configuration/options.html#settings-path). This can be useful, for instance, if you want to have one configuration for `.py` files and another for `.pyx` - while keeping the config files at the root of your repository. !!! tip diff --git a/docs/configuration/custom_sections_and_ordering.md b/docs/configuration/custom_sections_and_ordering.md index df145e889..8f12c5d81 100644 --- a/docs/configuration/custom_sections_and_ordering.md +++ b/docs/configuration/custom_sections_and_ordering.md @@ -115,7 +115,7 @@ import b from a import a # This will always appear below because it is a from import. ``` -However, if you prefer to keep strict alphabetical sorting you can set [force sort within sections](https://pycqa.github.io/isort/docs/configuration/options/#force-sort-within-sections) to true. Resulting in: +However, if you prefer to keep strict alphabetical sorting you can set [force sort within sections](https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections) to true. Resulting in: ```python @@ -123,7 +123,7 @@ from a import a # This will now appear at top because a appears in the alphabet import b ``` -You can even tell isort to always place from imports on top, instead of the default of placing them on bottom, using [from first](https://pycqa.github.io/isort/docs/configuration/options/#from-first). +You can even tell isort to always place from imports on top, instead of the default of placing them on bottom, using [from first](https://pycqa.github.io/isort/docs/configuration/options.html#from-first). ```python from b import b # If from first is set to True, all from imports will be placed before non-from imports. diff --git a/docs/configuration/multi_line_output_modes.md b/docs/configuration/multi_line_output_modes.md index aed2897e9..ebc102e5c 100644 --- a/docs/configuration/multi_line_output_modes.md +++ b/docs/configuration/multi_line_output_modes.md @@ -1,6 +1,6 @@ # Multi Line Output Modes -This [config option](https://pycqa.github.io/isort/docs/configuration/options/#multi-line-output) defines how from imports wrap when they extend past the line\_length +This [config option](https://pycqa.github.io/isort/docs/configuration/options.html#multi-line-output) defines how from imports wrap when they extend past the line\_length limit and has 12 possible settings: ## 0 - Grid diff --git a/docs/configuration/options.md b/docs/configuration/options.md index e07deb222..ca9f64a60 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -5,7 +5,7 @@ isort will disagree but commit to your way of formatting. To enable this, isort how you want your imports sorted, organized, and formatted. Too busy to build your perfect isort configuration? For curated common configurations, see isort's [built-in -profiles](https://pycqa.github.io/isort/docs/configuration/profiles/). +profiles](https://pycqa.github.io/isort/docs/configuration/profiles.html). ## Python Version diff --git a/docs/upgrade_guides/5.0.0.md b/docs/upgrade_guides/5.0.0.md index ba8e8288e..743df0297 100644 --- a/docs/upgrade_guides/5.0.0.md +++ b/docs/upgrade_guides/5.0.0.md @@ -6,7 +6,7 @@ This guide is meant to help migrate projects from using isort 4.x.x unto the 5.0 Related documentation: * [isort 5.0.0 changelog](https://pycqa.github.io/isort/CHANGELOG/#500-penny-july-4-2020) -* [isort 5 release document](https://pycqa.github.io/isort/docs/major_releases/introducing_isort_5/) +* [isort 5 release document](https://pycqa.github.io/isort/docs/major_releases/introducing_isort_5.html) !!! important - "If you use pre-commit remove seed-isort-config." If you currently use pre-commit, make sure to see the pre-commit section of this document. In particular, make sure to remove any `seed-isort-config` pre-step. @@ -19,7 +19,7 @@ for those of us who have gotten used to placing imports right above their usage If you want to move all imports to the top, you can use the new`--float-to-top` flag in the CLI or `float_to_top=true` option in your config file. -See: [https://pycqa.github.io/isort/docs/configuration/options/#float-to-top](https://pycqa.github.io/isort/docs/configuration/options/#float-to-top) +See: [https://pycqa.github.io/isort/docs/configuration/options.html#float-to-top](https://pycqa.github.io/isort/docs/configuration/options.html#float-to-top) ## Migrating CLI options @@ -46,7 +46,7 @@ The `-v` (previously for version now for verbose) and `-V` (previously for verbo The first thing to keep in mind is how isort loads config options has changed in isort 5. It will no longer merge multiple config files, instead you must have 1 isort config per a project. If you have multiple configs, they will need to be merged into 1 single one. You can see the priority order of configuration files and the manner in which they are loaded on the -[config files documentation page](https://pycqa.github.io/isort/docs/configuration/config_files/). +[config files documentation page](https://pycqa.github.io/isort/docs/configuration/config_files.html). !!! tip - "Config options are loaded relative to the file, not the isort instance." isort looks for a config file based on the path of the file you request to sort. If you have your config placed outside of the project, you can use `--settings-path` to manually specify the config location instead. Full information about how config files are loaded is in the linked config files documentation page. @@ -62,7 +62,7 @@ The option was originally added to allow working around this, and was then turne ### `known_standard_library` isort settings no longer merge together, instead they override. The old behavior of merging together caused many hard to track down errors, but the one place it was very convenient was for adding a few additional standard library modules. -In isort 5, you can still get this behavior by moving your extra modules from the `known_standard_library` setting to [`extra_standard_library`](https://pycqa.github.io/isort/docs/configuration/options/#extra-standard-library). +In isort 5, you can still get this behavior by moving your extra modules from the `known_standard_library` setting to [`extra_standard_library`](https://pycqa.github.io/isort/docs/configuration/options.html#extra-standard-library). ### module placement changes: `known_third_party`, `known_first_party`, `default_section`, etc... isort has completely rewritten its logic for placing modules in 5.0.0 to ensure the same behavior across environments. You can see the details of this change [here](https://github.com/pycqa/isort/issues/1147). From c249cacab2dc9006041da54422a8980e6c7b7e8e Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Sun, 20 Jun 2021 22:52:33 +0530 Subject: [PATCH 499/539] Revert back to the upstream version --- isort/parse.py | 7 ------- tests/unit/test_isort.py | 35 +---------------------------------- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/isort/parse.py b/isort/parse.py index 403816cc3..aec508745 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -139,7 +139,6 @@ class ParsedContent(NamedTuple): line_separator: str sections: Any verbose_output: List[str] - original_order: Dict[str, List[str]] def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedContent: @@ -166,12 +165,9 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte } imports: OrderedDict[str, Dict[str, Any]] = OrderedDict() verbose_output: List[str] = [] - original_order: Dict[str, List[str]] = {} for section in chain(config.sections, config.forced_separate): imports[section] = {"straight": OrderedDict(), "from": OrderedDict()} - original_order[section] = [] - categorized_comments: CommentsDict = { "from": {}, "straight": {}, @@ -451,7 +447,6 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte " Do you need to define a default section?" ) root = imports[placed_module][type_of_import] # type: ignore - original_order[placed_module].append(type_of_import) for import_name in just_imports: associated_comment = nested_comments.get(import_name) if associated_comment: @@ -568,7 +563,6 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte module, False ) imports[placed_module][type_of_import][module] = straight_import # type: ignore - original_order[placed_module].append(type_of_import) change_count = len(out_lines) - original_line_count @@ -586,5 +580,4 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte line_separator=line_separator, sections=config.sections, verbose_output=verbose_output, - original_order=original_order, ) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 4fe739587..9593c42c8 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -5142,8 +5142,8 @@ def test_only_sections() -> None: == ( "import sys\n" "import os\n" - "from os import path as ospath\n" "import math\n" + "from os import path as ospath\n" "from collections import defaultdict\n" "\n" "import numpy as np\n" @@ -5154,39 +5154,6 @@ def test_only_sections() -> None: == isort.code(test_input, only_sections=True, force_single_line=True) ) - test_input = ( - "from requests import post as POST, get as GET\n" - "import sys\n" - "from math import sqrt\n" - "\n" - "import numpy as np\n" - "\n" - "import os\n" - "\n" - "from .url import *\n" - "import pandas as pd\n" - "\n" - "import .views\n" - "from collections import defaultdict\n" - ) - - assert ( - isort.code(test_input, only_sections=True) - == ( - "import sys\n" - "from math import sqrt\n" - "import os\n" - "from collections import defaultdict\n" - "\n" - "from requests import post as POST, get as GET\n" - "import numpy as np\n" - "import pandas as pd\n" - "\n" - "from .url import *\n" - "import .views\n" - ) - ) - # test to ensure that from_imports remain intact with only_sections test_input = "from foo import b, a, c\n" From 321b1db13a381ac6617e071aa667c84c191d2f53 Mon Sep 17 00:00:00 2001 From: Aniruddha Date: Sun, 20 Jun 2021 23:30:18 +0530 Subject: [PATCH 500/539] Revert back to the upstream version --- isort/main.py | 4 ++-- isort/output.py | 11 +---------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/isort/main.py b/isort/main.py index df5793080..e8af743fe 100644 --- a/isort/main.py +++ b/isort/main.py @@ -705,8 +705,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: dest="only_sections", action="store_true", help="Causes imports to be sorted based on their sections like STDLIB,THIRDPARTY etc. " - "Within sections, the imports maintain their relative positions irrespective of their " - "import_type", + "Within sections, the imports are ordered by their import style and the imports with " + "same style maintain their relative positions.", ) section_group.add_argument( "--ds", diff --git a/isort/output.py b/isort/output.py index 3286256f1..76f2637d4 100644 --- a/isort/output.py +++ b/isort/output.py @@ -87,16 +87,7 @@ def sorted_imports( lines_between = [""] * ( config.lines_between_types if from_modules and straight_modules else 0 ) - - if config.only_sections: - section_output = [] - for section_import_type in parsed.original_order[section]: - if section_import_type == "from": - section_output.append(from_imports.pop(0)) - else: - section_output.append(straight_imports.pop(0)) - - elif config.from_first: + if config.from_first: section_output = from_imports + lines_between + straight_imports else: section_output = straight_imports + lines_between + from_imports From a1a3aff44a16dca71b288f1a7cd19a039931ee2a Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Jun 2021 19:17:16 -0700 Subject: [PATCH 501/539] Implemented #1732: Support for custom sort functions. --- CHANGELOG.md | 1 + .../example_isort_sorting_plugin.py | 6 + example_isort_sorting_plugin/poetry.lock | 22 ++++ example_isort_sorting_plugin/pyproject.toml | 19 +++ example_shared_isort_profile/poetry.lock | 5 +- isort/exceptions.py | 14 ++- isort/main.py | 4 +- isort/settings.py | 30 ++++- isort/sorting.py | 12 +- poetry.lock | 118 ++++++++++++------ pyproject.toml | 1 + scripts/clean.sh | 4 +- scripts/lint.sh | 2 +- tests/unit/test_exceptions.py | 9 ++ tests/unit/test_isort.py | 19 --- tests/unit/test_ticketed_features.py | 42 +++++++ 16 files changed, 236 insertions(+), 72 deletions(-) create mode 100644 example_isort_sorting_plugin/example_isort_sorting_plugin.py create mode 100644 example_isort_sorting_plugin/poetry.lock create mode 100644 example_isort_sorting_plugin/pyproject.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index b6ed2d7d0..9c7ef562c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. - Implemented #1737: Support for using action comments to avoid adding imports to individual files. - Implemented #1750: Ability to customize output format lines. + - Implemented #1732: Support for custom sort functions. - Fixed (https://github.com/PyCQA/isort/pull/1695): added imports being added to doc string in some cases. - Fixed (https://github.com/PyCQA/isort/pull/1714): in rare cases line continuation combined with tabs can output invalid code. - Fixed (https://github.com/PyCQA/isort/pull/1726): isort ignores reverse_sort when force_sort_within_sections is true. diff --git a/example_isort_sorting_plugin/example_isort_sorting_plugin.py b/example_isort_sorting_plugin/example_isort_sorting_plugin.py new file mode 100644 index 000000000..e0c9eacc5 --- /dev/null +++ b/example_isort_sorting_plugin/example_isort_sorting_plugin.py @@ -0,0 +1,6 @@ +from natsort import natsorted + + +def natural_plus(*args, **kwargs) -> str: + """An even more natural sorting order for isort using natsort.""" + return natsorted(*args, **kwargs) diff --git a/example_isort_sorting_plugin/poetry.lock b/example_isort_sorting_plugin/poetry.lock new file mode 100644 index 000000000..22307cf8d --- /dev/null +++ b/example_isort_sorting_plugin/poetry.lock @@ -0,0 +1,22 @@ +[[package]] +name = "natsort" +version = "7.1.1" +description = "Simple yet flexible natural sorting in Python." +category = "main" +optional = false +python-versions = ">=3.4" + +[package.extras] +fast = ["fastnumbers (>=2.0.0)"] +icu = ["PyICU (>=1.0.0)"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.6" +content-hash = "7825ed9542cf5a39140d69f2d73e505bfab06f72cf1ef105202bc221681efd1a" + +[metadata.files] +natsort = [ + {file = "natsort-7.1.1-py3-none-any.whl", hash = "sha256:d0f4fc06ca163fa4a5ef638d9bf111c67f65eedcc7920f98dec08e489045b67e"}, + {file = "natsort-7.1.1.tar.gz", hash = "sha256:00c603a42365830c4722a2eb7663a25919551217ec09a243d3399fa8dd4ac403"}, +] diff --git a/example_isort_sorting_plugin/pyproject.toml b/example_isort_sorting_plugin/pyproject.toml new file mode 100644 index 000000000..ee6e910a4 --- /dev/null +++ b/example_isort_sorting_plugin/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "example_isort_sorting_plugin" +version = "0.0.2" +description = "An example plugin that modifies isorts sorting order to provide an even more natural sort by utilizing natsort." +authors = ["Timothy Crosley "] +license = "MIT" + +[tool.poetry.plugins."isort.sort_function"] +natural_plus = "example_isort_sorting_plugin:natural_plus" + +[tool.poetry.dependencies] +python = "^3.6" +natsort = "^7.1.1" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" diff --git a/example_shared_isort_profile/poetry.lock b/example_shared_isort_profile/poetry.lock index 12fbad913..f6de59971 100644 --- a/example_shared_isort_profile/poetry.lock +++ b/example_shared_isort_profile/poetry.lock @@ -1,7 +1,8 @@ package = [] [metadata] -content-hash = "8165d934e932435bf4742b9198674202413b43524911713d5c7c55cb8d314618" -python-versions = "^3.5" +lock-version = "1.1" +python-versions = "^3.6" +content-hash = "ae1f216c71b9b712a5c479d19bf075b718c35d9248fd89cb1eb7624528ec5ad1" [metadata.files] diff --git a/isort/exceptions.py b/isort/exceptions.py index 3a54d6429..275dc424d 100644 --- a/isort/exceptions.py +++ b/isort/exceptions.py @@ -1,6 +1,6 @@ """All isort specific exception classes should be defined here""" from pathlib import Path -from typing import Any, Dict, Union +from typing import Any, Dict, List, Union from .profiles import profiles @@ -82,6 +82,18 @@ def __init__(self, profile: str): self.profile = profile +class SortingFunctionDoesNotExist(ISortError): + """Raised when the specified sorting function isn't available""" + + def __init__(self, sort_order: str, available_sort_orders: List[str]): + super().__init__( + f"Specified sort_order of {sort_order} does not exist. " + f"Available sort_orders: {','.join(available_sort_orders)}." + ) + self.sort_order = sort_order + self.available_sort_orders = available_sort_orders + + class FormattingPluginDoesNotExist(ISortError): """Raised when a formatting plugin is set by the user that doesn't exist""" diff --git a/isort/main.py b/isort/main.py index e8af743fe..a57b3fd28 100644 --- a/isort/main.py +++ b/isort/main.py @@ -585,8 +585,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: output_group.add_argument( "--sort-order", dest="sort_order", - choices=["default", "pythonic"], - help="Use natural language or pythonic sorting.", + help="Specify sorting function. Can be built in (natural[default] = force numbers " + "to be sequential, native = Python's built-in sorted function) or an installable plugin.", ) inline_args_group.add_argument( "--sl", diff --git a/isort/settings.py b/isort/settings.py index e4c32cab3..9eafb8644 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -30,13 +30,14 @@ ) from warnings import warn -from . import stdlibs +from . import sorting, stdlibs from ._future import dataclass, field from ._vendored import toml # type: ignore from .exceptions import ( FormattingPluginDoesNotExist, InvalidSettingsPath, ProfileDoesNotExist, + SortingFunctionDoesNotExist, UnsupportedSettings, ) from .profiles import profiles @@ -232,7 +233,7 @@ class _Config: git_ignore: Dict[Path, Set[Path]] = field(default_factory=dict) format_error: str = "{error}: {message}" format_success: str = "{success}: {message}" - sort_order: str = "default" + sort_order: str = "natural" def __post_init__(self) -> None: py_version = self.py_version @@ -294,6 +295,7 @@ def __init__( self._section_comments: Optional[Tuple[str, ...]] = None self._skips: Optional[FrozenSet[str]] = None self._skip_globs: Optional[FrozenSet[str]] = None + self._sorting_function: Optional[Callable[..., List[str]]] = None if config: config_vars = vars(config).copy() @@ -303,6 +305,7 @@ def __init__( config_vars.pop("_section_comments") config_vars.pop("_skips") config_vars.pop("_skip_globs") + config_vars.pop("_sorting_function") super().__init__(**config_vars) # type: ignore return @@ -651,6 +654,29 @@ def skip_globs(self) -> FrozenSet[str]: self._skip_globs = self.skip_glob.union(self.extend_skip_glob) return self._skip_globs + @property + def sorting_function(self) -> Callable[..., List[str]]: + if self._sorting_function is not None: + return self._sorting_function + + if self.sort_order == "natural": + self._sorting_function = sorting.naturally + elif self.sort_order == "native": + self._sorting_function = sorted + else: + available_sort_orders = ["natural", "native"] + import pkg_resources + + for sort_plugin in pkg_resources.iter_entry_points("isort.sort_function"): + available_sort_orders.append(sort_plugin.name) + if sort_plugin.name == self.sort_order: + self._sorting_function = sort_plugin.load() + break + else: + raise SortingFunctionDoesNotExist(self.sort_order, available_sort_orders) + + return self._sorting_function + def _parse_known_pattern(self, pattern: str) -> List[str]: """Expand pattern if identified as a directory and return found sub packages""" if pattern.endswith(os.path.sep): diff --git a/isort/sorting.py b/isort/sorting.py index 60727662f..22fdc3804 100644 --- a/isort/sorting.py +++ b/isort/sorting.py @@ -1,7 +1,10 @@ import re -from typing import Any, Callable, Iterable, List, Optional +from typing import TYPE_CHECKING, Any, Callable, Iterable, List, Optional -from .settings import Config +if TYPE_CHECKING: + from .settings import Config +else: + Config = Any _import_line_intro_re = re.compile("^(?:from|import) ") _import_line_midline_import_re = re.compile(" import ") @@ -102,10 +105,7 @@ def sort( key: Optional[Callable[[str], Any]] = None, reverse: bool = False, ) -> List[str]: - sorting_func: Callable[..., List[str]] = naturally - if config.sort_order == "pythonic": - sorting_func = sorted - return sorting_func(to_sort, key=key, reverse=reverse) + return config.sorting_function(to_sort, key=key, reverse=reverse) def naturally( diff --git a/poetry.lock b/poetry.lock index 492deaf67..27358a06b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -287,6 +287,17 @@ python-versions = ">=3.6,<4.0" black = ">=20.08b1,<21.0" isort = ">=5.1.4,<6.0.0" +[[package]] +name = "example-isort-sorting-plugin" +version = "0.0.2" +description = "An example plugin that modifies isorts sorting order to provide an even more natural sort by utilizing natsort." +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +natsort = ">=7.1.1,<8.0.0" + [[package]] name = "example-shared-isort-profile" version = "0.0.1" @@ -397,11 +408,11 @@ gitdb = ">=4.0.1" [[package]] name = "gitpython" -version = "3.1.17" +version = "3.1.18" description = "Python Git Library" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] gitdb = ">=4.0.1,<5" @@ -868,6 +879,18 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "natsort" +version = "7.1.1" +description = "Simple yet flexible natural sorting in Python." +category = "dev" +optional = false +python-versions = ">=3.4" + +[package.extras] +fast = ["fastnumbers (>=2.0.0)"] +icu = ["PyICU (>=1.0.0)"] + [[package]] name = "nodeenv" version = "1.6.0" @@ -1117,11 +1140,11 @@ virtualenv = ">=20.0.8" [[package]] name = "prompt-toolkit" -version = "3.0.3" +version = "3.0.19" description = "Library for building powerful interactive command lines in Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.1" [package.dependencies] wcwidth = "*" @@ -1160,19 +1183,19 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pydantic" -version = "1.7.4" +version = "1.8.2" description = "Data validation and settings management using python 3.6 type hinting" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.1" [package.dependencies] dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} +typing-extensions = ">=3.7.4.3" [package.extras] dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] -typing_extensions = ["typing-extensions (>=3.7.2)"] [[package]] name = "pydocstyle" @@ -1571,7 +1594,7 @@ doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown- [[package]] name = "types-pkg-resources" -version = "0.1.2" +version = "0.1.3" description = "Typing stubs for pkg_resources" category = "dev" optional = false @@ -1715,8 +1738,8 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" -python-versions = "^3.6.1" -content-hash = "069575195bc09e4d3ac30c5600a5ac8a991c64a4a586eaf8339f6d3137cd18e9" +python-versions = ">=3.6.1,<4.0" +content-hash = "f9e26ab53135b8de55863d9138e5ebd78764d8d5172199ad95c516b8383b4bee" [metadata.files] appdirs = [ @@ -1800,6 +1823,9 @@ coverage = [ {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, + {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, + {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, + {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, @@ -1869,6 +1895,10 @@ example-isort-formatting-plugin = [ {file = "example_isort_formatting_plugin-0.0.2-py3-none-any.whl", hash = "sha256:ce428ab5deb4719e4bec56eae63978ff2d9c20dc2c2aa7cc39ece61044153db7"}, {file = "example_isort_formatting_plugin-0.0.2.tar.gz", hash = "sha256:8cb6401c9efe2f97ba3e776439cb647ee964dc7880bd9790b0324be2c7a55907"}, ] +example-isort-sorting-plugin = [ + {file = "example_isort_sorting_plugin-0.0.2-py3-none-any.whl", hash = "sha256:108d3d66bb5fd8ef51fde9bbab3207d2b6176b5df803dd88e31f2f48b8975b59"}, + {file = "example_isort_sorting_plugin-0.0.2.tar.gz", hash = "sha256:ea5ac1f9233023d6d949940ea00d08e2f8ade6e7acdebafe1e82acdf1ef1d969"}, +] example-shared-isort-profile = [ {file = "example_shared_isort_profile-0.0.1-py3-none-any.whl", hash = "sha256:3fa3e2d093e68285fc7893704b727791ed3e0969d07bdd2733e366303d1a2582"}, {file = "example_shared_isort_profile-0.0.1.tar.gz", hash = "sha256:fc2a0fa892611d6c1c2060dc0ca8e511e3f65fad8e4e449e8a375ef49549e710"}, @@ -1921,8 +1951,8 @@ gitdb2 = [ {file = "gitdb2-4.0.2.tar.gz", hash = "sha256:0986cb4003de743f2b3aba4c828edd1ab58ce98e1c4a8acf72ef02760d4beb4e"}, ] gitpython = [ - {file = "GitPython-3.1.17-py3-none-any.whl", hash = "sha256:29fe82050709760081f588dd50ce83504feddbebdc4da6956d02351552b1c135"}, - {file = "GitPython-3.1.17.tar.gz", hash = "sha256:ee24bdc93dce357630764db659edaf6b8d664d4ff5447ccfeedd2dc5c253f41e"}, + {file = "GitPython-3.1.18-py3-none-any.whl", hash = "sha256:fce760879cd2aebd2991b3542876dc5c4a909b30c9d69dfc488e504a8db37ee8"}, + {file = "GitPython-3.1.18.tar.gz", hash = "sha256:b838a895977b45ab6f0cc926a9045c8d1c44e2b653c1fcc39fe91f42c6e8f05b"}, ] h11 = [ {file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"}, @@ -2036,6 +2066,7 @@ livereload = [ {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, ] mako = [ + {file = "Mako-1.1.4-py2.py3-none-any.whl", hash = "sha256:aea166356da44b9b830c8023cd9b557fa856bd8b4035d6de771ca027dfc5cc6e"}, {file = "Mako-1.1.4.tar.gz", hash = "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab"}, ] markdown = [ @@ -2127,6 +2158,10 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] +natsort = [ + {file = "natsort-7.1.1-py3-none-any.whl", hash = "sha256:d0f4fc06ca163fa4a5ef638d9bf111c67f65eedcc7920f98dec08e489045b67e"}, + {file = "natsort-7.1.1.tar.gz", hash = "sha256:00c603a42365830c4722a2eb7663a25919551217ec09a243d3399fa8dd4ac403"}, +] nodeenv = [ {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"}, {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"}, @@ -2243,8 +2278,8 @@ pre-commit = [ {file = "pre_commit-2.13.0.tar.gz", hash = "sha256:764972c60693dc668ba8e86eb29654ec3144501310f7198742a767bec385a378"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"}, - {file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"}, + {file = "prompt_toolkit-3.0.19-py3-none-any.whl", hash = "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88"}, + {file = "prompt_toolkit-3.0.19.tar.gz", hash = "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f"}, ] ptyprocess = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, @@ -2262,28 +2297,28 @@ pycodestyle = [ {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] pydantic = [ - {file = "pydantic-1.7.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3c60039e84552442defbcb5d56711ef0e057028ca7bfc559374917408a88d84e"}, - {file = "pydantic-1.7.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:6e7e314acb170e143c6f3912f93f2ec80a96aa2009ee681356b7ce20d57e5c62"}, - {file = "pydantic-1.7.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:8ef77cd17b73b5ba46788d040c0e820e49a2d80cfcd66fda3ba8be31094fd146"}, - {file = "pydantic-1.7.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:115d8aa6f257a1d469c66b6bfc7aaf04cd87c25095f24542065c68ebcb42fe63"}, - {file = "pydantic-1.7.4-cp36-cp36m-win_amd64.whl", hash = "sha256:66757d4e1eab69a3cfd3114480cc1d72b6dd847c4d30e676ae838c6740fdd146"}, - {file = "pydantic-1.7.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4c92863263e4bd89e4f9cf1ab70d918170c51bd96305fe7b00853d80660acb26"}, - {file = "pydantic-1.7.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3b8154babf30a5e0fa3aa91f188356763749d9b30f7f211fafb247d4256d7877"}, - {file = "pydantic-1.7.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:80cc46378505f7ff202879dcffe4bfbf776c15675028f6e08d1d10bdfbb168ac"}, - {file = "pydantic-1.7.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:dda60d7878a5af2d8560c55c7c47a8908344aa78d32ec1c02d742ede09c534df"}, - {file = "pydantic-1.7.4-cp37-cp37m-win_amd64.whl", hash = "sha256:4c1979d5cc3e14b35f0825caddea5a243dd6085e2a7539c006bc46997ef7a61a"}, - {file = "pydantic-1.7.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8857576600c32aa488f18d30833aa833b54a48e3bab3adb6de97e463af71f8f8"}, - {file = "pydantic-1.7.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1f86d4da363badb39426a0ff494bf1d8510cd2f7274f460eee37bdbf2fd495ec"}, - {file = "pydantic-1.7.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:3ea1256a9e782149381e8200119f3e2edea7cd6b123f1c79ab4bbefe4d9ba2c9"}, - {file = "pydantic-1.7.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:e28455b42a0465a7bf2cde5eab530389226ce7dc779de28d17b8377245982b1e"}, - {file = "pydantic-1.7.4-cp38-cp38-win_amd64.whl", hash = "sha256:47c5b1d44934375a3311891cabd450c150a31cf5c22e84aa172967bf186718be"}, - {file = "pydantic-1.7.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:00250e5123dd0b123ff72be0e1b69140e0b0b9e404d15be3846b77c6f1b1e387"}, - {file = "pydantic-1.7.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d24aa3f7f791a023888976b600f2f389d3713e4f23b7a4c88217d3fce61cdffc"}, - {file = "pydantic-1.7.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:2c44a9afd4c4c850885436a4209376857989aaf0853c7b118bb2e628d4b78c4e"}, - {file = "pydantic-1.7.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:e87edd753da0ca1d44e308a1b1034859ffeab1f4a4492276bff9e1c3230db4fe"}, - {file = "pydantic-1.7.4-cp39-cp39-win_amd64.whl", hash = "sha256:a3026ee105b5360855e500b4abf1a1d0b034d88e75a2d0d66a4c35e60858e15b"}, - {file = "pydantic-1.7.4-py3-none-any.whl", hash = "sha256:a82385c6d5a77e3387e94612e3e34b77e13c39ff1295c26e3ba664e7b98073e2"}, - {file = "pydantic-1.7.4.tar.gz", hash = "sha256:0a1abcbd525fbb52da58c813d54c2ec706c31a91afdb75411a73dd1dec036595"}, + {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"}, + {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"}, + {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"}, + {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"}, + {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"}, + {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"}, + {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"}, + {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"}, + {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"}, + {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, ] pydocstyle = [ {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, @@ -2340,18 +2375,26 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, @@ -2543,7 +2586,8 @@ typer = [ {file = "typer-0.3.2.tar.gz", hash = "sha256:5455d750122cff96745b0dec87368f56d023725a7ebc9d2e54dd23dc86816303"}, ] types-pkg-resources = [ - {file = "types_pkg_resources-0.1.2-py2.py3-none-any.whl", hash = "sha256:42d640500de564f1ccc21f918117afadf78039e4fa7f513c647ccf742d609aeb"}, + {file = "types-pkg_resources-0.1.3.tar.gz", hash = "sha256:834a9b8d3dbea343562fd99d5d3359a726f6bf9d3733bccd2b4f3096fbab9dae"}, + {file = "types_pkg_resources-0.1.3-py2.py3-none-any.whl", hash = "sha256:0cb9972cee992249f93fff1a491bf2dc3ce674e5a1926e27d4f0866f7d9b6d9c"}, ] typing-extensions = [ {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, diff --git a/pyproject.toml b/pyproject.toml index f339ef46a..301a54a78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,6 +82,7 @@ gitdb2 = "^4.0.2" httpx = "^0.13.3" example_shared_isort_profile = "^0.0.1" example_isort_formatting_plugin = "^0.0.2" +example_isort_sorting_plugin = "^0.0.2" flake8 = "^3.8.4" hypothesis = "^6.10.1" libcst = "^0.3.18" diff --git a/scripts/clean.sh b/scripts/clean.sh index 7d446611e..632a023c2 100755 --- a/scripts/clean.sh +++ b/scripts/clean.sh @@ -2,6 +2,6 @@ set -euxo pipefail poetry run isort --profile hug isort/ tests/ scripts/ -poetry run isort --profile hug example_isort_formatting_plugin/ +poetry run isort --profile hug example_*/ poetry run black isort/ tests/ scripts/ -poetry run black example_isort_formatting_plugin/ +poetry run black example_*/ diff --git a/scripts/lint.sh b/scripts/lint.sh index 6783976fc..9af835d0a 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -5,7 +5,7 @@ poetry run cruft check poetry run mypy -p isort poetry run black --target-version py36 --check . poetry run isort --profile hug --check --diff isort/ tests/ -poetry run isort --profile hug --check --diff example_isort_formatting_plugin/ +poetry run isort --profile hug --check --diff example_*/ poetry run flake8 isort/ tests/ poetry run safety check -i 39462 -i 40291 poetry run bandit -r isort/ -x isort/_vendored diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py index d9eae8bf8..2cd17aa11 100644 --- a/tests/unit/test_exceptions.py +++ b/tests/unit/test_exceptions.py @@ -58,6 +58,15 @@ def test_variables(self): assert self.instance.profile == "profile" +class TestSortingFunctionDoesNotExist(TestISortError): + def setup_class(self): + self.instance = exceptions.SortingFunctionDoesNotExist("round", ["square", "peg"]) + + def test_variables(self): + assert self.instance.sort_order == "round" + assert self.instance.available_sort_orders == ["square", "peg"] + + class TestLiteralParsingFailure(TestISortError): def setup_class(self): self.instance = exceptions.LiteralParsingFailure("x = [", SyntaxError) diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 9593c42c8..0599925e7 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -5236,22 +5236,3 @@ def seekable(self): test_input = NonSeekableTestStream("import m2\n" "import m1\n" "not_import = 7") identified_imports = list(map(str, api.find_imports_in_stream(test_input))) assert identified_imports == [":1 import m2", ":2 import m1"] - - -def test_sort_pythonic() -> None: - """ Test a plugged-in default python sort with packages/modules containing numbers. """ - test_input = ( - "from bob2.apples2 import aardvark as aardvark2\n" - "from bob.apples import aardvark \n" - "import module9\n" - "import module10\n" - "import module200\n" - ) - test_output = isort.code(test_input, sort_order="pythonic") - assert test_output == ( - "import module10\n" - "import module200\n" - "import module9\n" - "from bob.apples import aardvark\n" - "from bob2.apples2 import aardvark as aardvark2\n" - ) diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index a29700539..dde08be6c 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -1179,3 +1179,45 @@ def test_isort_can_turn_off_import_adds_with_action_comment_issue_1737(): import os """ ) + + +def test_sort_configurable_sort_issue_1732() -> None: + """Test support for pluggable isort sort functions.""" + test_input = ( + "from bob2.apples2 import aardvark as aardvark2\n" + "from bob.apples import aardvark \n" + "import module9\n" + "import module10\n" + "import module200\n" + ) + assert isort.code(test_input, sort_order="native") == ( + "import module10\n" + "import module200\n" + "import module9\n" + "from bob.apples import aardvark\n" + "from bob2.apples2 import aardvark as aardvark2\n" + ) + assert ( + isort.code(test_input, sort_order="natural") + == isort.code(test_input) + == ( + "import module9\n" + "import module10\n" + "import module200\n" + "from bob2.apples2 import aardvark as aardvark2\n" + "from bob.apples import aardvark\n" + ) + ) + assert ( + isort.code(test_input, sort_order="natural_plus") + == isort.code(test_input) + == ( + "import module9\n" + "import module10\n" + "import module200\n" + "from bob2.apples2 import aardvark as aardvark2\n" + "from bob.apples import aardvark\n" + ) + ) + with pytest.raises(exceptions.SortingFunctionDoesNotExist): + isort.code(test_input, sort_order="round") From eada92819c9027b08a06ab51a57eb10730aa6933 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Jun 2021 19:41:06 -0700 Subject: [PATCH 502/539] Update integration testst to understand new sort_order setting --- tests/integration/test_setting_combinations.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/test_setting_combinations.py b/tests/integration/test_setting_combinations.py index 7b57199f0..d2159fd3e 100644 --- a/tests/integration/test_setting_combinations.py +++ b/tests/integration/test_setting_combinations.py @@ -56,6 +56,7 @@ def configs() -> st.SearchStrategy[isort.Config]: "default_section": st.sampled_from(sorted(isort.settings.KNOWN_SECTION_MAPPING)), "force_grid_wrap": st.integers(0, 20), "profile": st.sampled_from(sorted(isort.settings.profiles)), + "sort_order": st.sampled_from(sorted(("native", "natural", "natural_plus"))), "py_version": st.sampled_from(("auto",) + isort.settings.VALID_PY_TARGETS), } kwargs = {**inferred_kwargs, **specific} From 4cb72fa93fdcd78df00687de7cd91428ab0fdc93 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Jun 2021 21:15:11 -0700 Subject: [PATCH 503/539] Implemented #1722: Improved behavior for running isort in atomic mode over Cython source files. --- CHANGELOG.md | 1 + isort/api.py | 22 ++++++++++++++++------ isort/settings.py | 3 ++- tests/unit/test_isort.py | 11 +++++++++++ 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c7ef562c..33e394eab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Implemented #1737: Support for using action comments to avoid adding imports to individual files. - Implemented #1750: Ability to customize output format lines. - Implemented #1732: Support for custom sort functions. + - Implemented #1722: Improved behavior for running isort in atomic mode over Cython source files. - Fixed (https://github.com/PyCQA/isort/pull/1695): added imports being added to doc string in some cases. - Fixed (https://github.com/PyCQA/isort/pull/1714): in rare cases line continuation combined with tabs can output invalid code. - Fixed (https://github.com/PyCQA/isort/pull/1726): isort ignores reverse_sort when force_sort_within_sections is true. diff --git a/isort/api.py b/isort/api.py index fcc0bd231..31e433cfa 100644 --- a/isort/api.py +++ b/isort/api.py @@ -37,7 +37,7 @@ from .io import Empty, File from .place import module as place_module # noqa: F401 from .place import module_with_reason as place_module_with_reason # noqa: F401 -from .settings import DEFAULT_CONFIG, Config +from .settings import CYTHON_EXTENSIONS, DEFAULT_CONFIG, Config class ImportKey(Enum): @@ -193,9 +193,14 @@ def sort_stream( try: file_content = input_stream.read() compile(file_content, content_source, "exec", 0, 1) - input_stream = StringIO(file_content) except SyntaxError: - raise ExistingSyntaxErrors(content_source) + if extension not in CYTHON_EXTENSIONS: + raise ExistingSyntaxErrors(content_source) + elif config.verbose: + warn( + f"{content_source} Python AST errors found but ignored due to Cython extension" + ) + input_stream = StringIO(file_content) if not output_stream.readable(): _internal_output = StringIO() @@ -216,10 +221,15 @@ def sort_stream( try: compile(_internal_output.read(), content_source, "exec", 0, 1) _internal_output.seek(0) - if _internal_output != output_stream: - output_stream.write(_internal_output.read()) except SyntaxError: # pragma: no cover - raise IntroducedSyntaxErrors(content_source) + if extension not in CYTHON_EXTENSIONS: + raise IntroducedSyntaxErrors(content_source) + elif config.verbose: + warn( + f"{content_source} Python AST errors found but ignored due to Cython extension" + ) + if _internal_output != output_stream: + output_stream.write(_internal_output.read()) return changed diff --git a/isort/settings.py b/isort/settings.py index 9eafb8644..b17488bf2 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -47,7 +47,8 @@ from .wrap_modes import from_string as wrap_mode_from_string _SHEBANG_RE = re.compile(br"^#!.*\bpython[23w]?\b") -SUPPORTED_EXTENSIONS = frozenset({"py", "pyi", "pyx", "pxd"}) +CYTHON_EXTENSIONS = frozenset({"pyx", "pxd"}) +SUPPORTED_EXTENSIONS = frozenset({"py", "pyi", *CYTHON_EXTENSIONS}) BLOCKED_EXTENSIONS = frozenset({"pex"}) FILE_SKIP_COMMENTS: Tuple[str, ...] = ( "isort:" + "skip_file", diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 0599925e7..c90f40d29 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -1408,6 +1408,17 @@ def test_atomic_mode() -> None: with pytest.raises(ExistingSyntaxErrors): isort.code(test_input, atomic=True) + # unless file is for Cython which doesn't yet provide a public AST parsing API + assert ( + isort.code(test_input, extension="pyx", atomic=True, verbose=True) + == isort.code(test_input, extension="pyx", atomic=True) + == """from a import e, f +from b import c, d + +while True print 'Hello world' +""" + ) + # ensure atomic works with streams test_input = as_stream("from b import d, c\nfrom a import f, e\n") test_output = UnreadableStream() From cd2e7636affca6bd7a3856cb6ed23b4f33eaac9f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Jun 2021 22:54:14 -0700 Subject: [PATCH 504/539] Migrate mypy from setuf.cfg -> pyproject.toml. Starting running against tests in CI --- isort/exceptions.py | 4 +- isort/format.py | 2 +- isort/settings.py | 7 ++- poetry.lock | 74 ++++++++++++++-------- pyproject.toml | 14 ++++- scripts/lint.sh | 2 +- setup.cfg | 14 ----- tests/unit/profiles/test_black.py | 2 +- tests/unit/test_api.py | 2 +- tests/unit/test_deprecated_finders.py | 16 ++--- tests/unit/test_exceptions.py | 24 ++++---- tests/unit/test_isort.py | 89 +++++++++++++++------------ tests/unit/test_main.py | 22 ++++--- tests/unit/test_regressions.py | 2 +- tests/unit/test_settings.py | 4 +- 15 files changed, 158 insertions(+), 120 deletions(-) diff --git a/isort/exceptions.py b/isort/exceptions.py index 275dc424d..e5eeceaa8 100644 --- a/isort/exceptions.py +++ b/isort/exceptions.py @@ -1,6 +1,6 @@ """All isort specific exception classes should be defined here""" from pathlib import Path -from typing import Any, Dict, List, Union +from typing import Any, Dict, List, Type, Union from .profiles import profiles @@ -107,7 +107,7 @@ class LiteralParsingFailure(ISortError): the given data structure. """ - def __init__(self, code: str, original_error: Exception): + def __init__(self, code: str, original_error: Union[Exception, Type[Exception]]): super().__init__( f"isort failed to parse the given literal {code}. It's important to note " "that isort literal sorting only supports simple literals parsable by " diff --git a/isort/format.py b/isort/format.py index 4de09a423..3a7c1e0b2 100644 --- a/isort/format.py +++ b/isort/format.py @@ -6,7 +6,7 @@ from typing import Optional, TextIO try: - import colorama # type: ignore + import colorama except ImportError: colorama_unavailable = True else: diff --git a/isort/settings.py b/isort/settings.py index b17488bf2..d04dd8386 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -15,6 +15,7 @@ from functools import lru_cache from pathlib import Path from typing import ( + TYPE_CHECKING, Any, Callable, Dict, @@ -32,7 +33,6 @@ from . import sorting, stdlibs from ._future import dataclass, field -from ._vendored import toml # type: ignore from .exceptions import ( FormattingPluginDoesNotExist, InvalidSettingsPath, @@ -46,6 +46,11 @@ from .wrap_modes import WrapModes from .wrap_modes import from_string as wrap_mode_from_string +if TYPE_CHECKING: + toml: Any +else: + from ._vendored import toml + _SHEBANG_RE = re.compile(br"^#!.*\bpython[23w]?\b") CYTHON_EXTENSIONS = frozenset({"pyx", "pxd"}) SUPPORTED_EXTENSIONS = frozenset({"py", "pyi", *CYTHON_EXTENSIONS}) diff --git a/poetry.lock b/poetry.lock index 27358a06b..92ff49456 100644 --- a/poetry.lock +++ b/poetry.lock @@ -855,7 +855,7 @@ mkdocs-material = ">=5.0.0" [[package]] name = "mypy" -version = "0.901" +version = "0.902" description = "Optional static typing for Python" category = "dev" optional = false @@ -1592,6 +1592,14 @@ all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)"] +[[package]] +name = "types-colorama" +version = "0.4.2" +description = "Typing stubs for colorama" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "types-pkg-resources" version = "0.1.3" @@ -1600,6 +1608,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "types-toml" +version = "0.1.3" +description = "Typing stubs for toml" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "typing-extensions" version = "3.10.0.0" @@ -1739,7 +1755,7 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"] [metadata] lock-version = "1.1" python-versions = ">=3.6.1,<4.0" -content-hash = "f9e26ab53135b8de55863d9138e5ebd78764d8d5172199ad95c516b8383b4bee" +content-hash = "c73791ac0cce25c94e949750728fbe4f48d4dfadacc874f781dcc4d8266531af" [metadata.files] appdirs = [ @@ -2130,29 +2146,29 @@ mkdocs-material-extensions = [ {file = "mkdocs_material_extensions-1.0.1-py3-none-any.whl", hash = "sha256:d90c807a88348aa6d1805657ec5c0b2d8d609c110e62b9dce4daf7fa981fa338"}, ] mypy = [ - {file = "mypy-0.901-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:91211acf1485a1db0b1261bc5f9ed450cba3c0dfd8da0a6680e94827591e34d7"}, - {file = "mypy-0.901-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c8bc628961cca4335ac7d1f2ed59b7125d9252fe4c78c3d66d30b50162359c99"}, - {file = "mypy-0.901-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:4a622faa3be76114cdce009f8ec173401494cf9e8f22713e7ae75fee9d906ab3"}, - {file = "mypy-0.901-cp35-cp35m-win_amd64.whl", hash = "sha256:8183561bfd950e93eeab8379ae5ec65873c856f5b58498d23aa8691f74c86030"}, - {file = "mypy-0.901-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:da914faaa80c25f463913da6db12adba703822a768f452f29f75b40bb4357139"}, - {file = "mypy-0.901-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:307a6c047596d768c3d689734307e47a91596eb9dbb67cfdf7d1fd9117b27f13"}, - {file = "mypy-0.901-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a85c6759dcc6a9884131fa06a037bd34352aa3947e7f5d9d5a35652cc3a44bcd"}, - {file = "mypy-0.901-cp36-cp36m-win_amd64.whl", hash = "sha256:9941b685807b60c58020bb67b3217c9df47820dcd00425f55cdf71f31d3c42d9"}, - {file = "mypy-0.901-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:08cf1f31029612e1008a9432337ca4b1fbac989ff7c8200e2c9ec42705cd4c7b"}, - {file = "mypy-0.901-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bc61153eb4df769538bb4a6e1045f59c2e6119339690ec719feeacbfc3809e89"}, - {file = "mypy-0.901-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:1cd241966a35036f936d4739bd71a1c64e15f02bf7d12bb2815cccfb2993a9de"}, - {file = "mypy-0.901-cp37-cp37m-win_amd64.whl", hash = "sha256:97be0e8ed116f7f79472a49cf06dd45dd806771142401f684d4f13ee652a63c0"}, - {file = "mypy-0.901-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79beb6741df15395908ecc706b3a593a98804c1d5b5b6bd0c5b03b67c7ac03a0"}, - {file = "mypy-0.901-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf347c327c48d963bdef5bf365215d3e98b5fddbe5069fc796cec330e8235a20"}, - {file = "mypy-0.901-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:053b92ebae901fc7954677949049f70133f2f63e3e83dc100225c26d6a46fe95"}, - {file = "mypy-0.901-cp38-cp38-win_amd64.whl", hash = "sha256:f208cc967e566698c4e30a1f65843fc88d8da05a8693bac8b975417e0aee9ced"}, - {file = "mypy-0.901-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c86e3f015bfe7958646825d41c0691c6e5a5cd4015e3409b5c29c18a3c712534"}, - {file = "mypy-0.901-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8577d30daf1b7b6582020f539f76e78ee1ed64a0323b28c8e0333c45db9369f"}, - {file = "mypy-0.901-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:5ddd8f4096d5fc2e7d7bb3924ac22758862163ad2c1cdc902c4b85568160e90a"}, - {file = "mypy-0.901-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:4b54518e399c3f4dc53380d4252c83276b2e60623cfc5274076eb8aae57572ac"}, - {file = "mypy-0.901-cp39-cp39-win_amd64.whl", hash = "sha256:7845ad3a31407bfbd64c76d032c16ab546d282930f747023bf07c17b054bebc5"}, - {file = "mypy-0.901-py3-none-any.whl", hash = "sha256:61b10ba18a01d05fc46adbf4f18b0e92178f6b5fd0f45926ffc2a408b5419728"}, - {file = "mypy-0.901.tar.gz", hash = "sha256:18753a8bb9bcf031ff10009852bd48d781798ecbccf45be5449892e6af4e3f9f"}, + {file = "mypy-0.902-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3f12705eabdd274b98f676e3e5a89f247ea86dc1af48a2d5a2b080abac4e1243"}, + {file = "mypy-0.902-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2f9fedc1f186697fda191e634ac1d02f03d4c260212ccb018fabbb6d4b03eee8"}, + {file = "mypy-0.902-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:0756529da2dd4d53d26096b7969ce0a47997123261a5432b48cc6848a2cb0bd4"}, + {file = "mypy-0.902-cp35-cp35m-win_amd64.whl", hash = "sha256:68a098c104ae2b75e946b107ef69dd8398d54cb52ad57580dfb9fc78f7f997f0"}, + {file = "mypy-0.902-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cd01c599cf9f897b6b6c6b5d8b182557fb7d99326bcdf5d449a0fbbb4ccee4b9"}, + {file = "mypy-0.902-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e89880168c67cf4fde4506b80ee42f1537ad66ad366c101d388b3fd7d7ce2afd"}, + {file = "mypy-0.902-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ebe2bc9cb638475f5d39068d2dbe8ae1d605bb8d8d3ff281c695df1670ab3987"}, + {file = "mypy-0.902-cp36-cp36m-win_amd64.whl", hash = "sha256:f89bfda7f0f66b789792ab64ce0978e4a991a0e4dd6197349d0767b0f1095b21"}, + {file = "mypy-0.902-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:746e0b0101b8efec34902810047f26a8c80e1efbb4fc554956d848c05ef85d76"}, + {file = "mypy-0.902-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0190fb77e93ce971954c9e54ea61de2802065174e5e990c9d4c1d0f54fbeeca2"}, + {file = "mypy-0.902-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:b5dfcd22c6bab08dfeded8d5b44bdcb68c6f1ab261861e35c470b89074f78a70"}, + {file = "mypy-0.902-cp37-cp37m-win_amd64.whl", hash = "sha256:b5ba1f0d5f9087e03bf5958c28d421a03a4c1ad260bf81556195dffeccd979c4"}, + {file = "mypy-0.902-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9ef5355eaaf7a23ab157c21a44c614365238a7bdb3552ec3b80c393697d974e1"}, + {file = "mypy-0.902-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:517e7528d1be7e187a5db7f0a3e479747307c1b897d9706b1c662014faba3116"}, + {file = "mypy-0.902-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:fd634bc17b1e2d6ce716f0e43446d0d61cdadb1efcad5c56ca211c22b246ebc8"}, + {file = "mypy-0.902-cp38-cp38-win_amd64.whl", hash = "sha256:fc4d63da57ef0e8cd4ab45131f3fe5c286ce7dd7f032650d0fbc239c6190e167"}, + {file = "mypy-0.902-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:353aac2ce41ddeaf7599f1c73fed2b75750bef3b44b6ad12985a991bc002a0da"}, + {file = "mypy-0.902-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae94c31bb556ddb2310e4f913b706696ccbd43c62d3331cd3511caef466871d2"}, + {file = "mypy-0.902-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:8be7bbd091886bde9fcafed8dd089a766fa76eb223135fe5c9e9798f78023a20"}, + {file = "mypy-0.902-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:4efc67b9b3e2fddbe395700f91d5b8deb5980bfaaccb77b306310bd0b9e002eb"}, + {file = "mypy-0.902-cp39-cp39-win_amd64.whl", hash = "sha256:9f1d74eeb3f58c7bd3f3f92b8f63cb1678466a55e2c4612bf36909105d0724ab"}, + {file = "mypy-0.902-py3-none-any.whl", hash = "sha256:a26d0e53e90815c765f91966442775cf03b8a7514a4e960de7b5320208b07269"}, + {file = "mypy-0.902.tar.gz", hash = "sha256:9236c21194fde5df1b4d8ebc2ef2c1f2a5dc7f18bcbea54274937cae2e20a01c"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, @@ -2585,10 +2601,18 @@ typer = [ {file = "typer-0.3.2-py3-none-any.whl", hash = "sha256:ba58b920ce851b12a2d790143009fa00ac1d05b3ff3257061ff69dbdfc3d161b"}, {file = "typer-0.3.2.tar.gz", hash = "sha256:5455d750122cff96745b0dec87368f56d023725a7ebc9d2e54dd23dc86816303"}, ] +types-colorama = [ + {file = "types-colorama-0.4.2.tar.gz", hash = "sha256:ae4f7fcb533e529c182b934423b0c589452ec8b3470430b2ee7baf06cb7f4a8d"}, + {file = "types_colorama-0.4.2-py2.py3-none-any.whl", hash = "sha256:42bd280fad509c698e1485f9658493076d315d189a5014be843dffee2d264902"}, +] types-pkg-resources = [ {file = "types-pkg_resources-0.1.3.tar.gz", hash = "sha256:834a9b8d3dbea343562fd99d5d3359a726f6bf9d3733bccd2b4f3096fbab9dae"}, {file = "types_pkg_resources-0.1.3-py2.py3-none-any.whl", hash = "sha256:0cb9972cee992249f93fff1a491bf2dc3ce674e5a1926e27d4f0866f7d9b6d9c"}, ] +types-toml = [ + {file = "types-toml-0.1.3.tar.gz", hash = "sha256:33ebe67bebaec55a123ecbaa2bd98fe588335d8d8dda2c7ac53502ef5a81a79a"}, + {file = "types_toml-0.1.3-py2.py3-none-any.whl", hash = "sha256:d4add39a90993173d49ff0b069edd122c66ad4cf5c01082b590e380ca670ee1a"}, +] typing-extensions = [ {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, diff --git a/pyproject.toml b/pyproject.toml index 301a54a78..dbd6bd2e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ bandit = "^1.6" safety = "^1.8" flake8-bugbear = "^19.8" black = {version = "^20.08b1", allow-prereleases = true} -mypy = "^0.901" +mypy = "^0.902" ipython = "^7.7" pytest = "^6.0" pytest-cov = "^2.7" @@ -116,3 +116,15 @@ palette = {scheme = "isort"} [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.mypy] +python_version = 3.6 +strict = true +follow_imports = "silent" +exclude = "isort/_vendored|tests/unit/example_projects|tests/unit/example_crlf_file.py" + +[[tool.mypy.overrides]] +module = "tests.*" +allow_untyped_defs = true +allow_incomplete_defs = true +allow_untyped_calls = true diff --git a/scripts/lint.sh b/scripts/lint.sh index 9af835d0a..6db1f8f78 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -2,7 +2,7 @@ set -euxo pipefail poetry run cruft check -poetry run mypy -p isort +poetry run mypy -p isort -p tests poetry run black --target-version py36 --check . poetry run isort --profile hug --check --diff isort/ tests/ poetry run isort --profile hug --check --diff example_*/ diff --git a/setup.cfg b/setup.cfg index cff281a90..d3a6c814f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,17 +1,3 @@ -[mypy] -python_version = 3.6 -strict = True -follow_imports = silent - -[mypy-isort.isort._vendored.*] -ignore_errors = True - -[mypy-test_isort] -strict_optional = False - -[mypy-isort.isort] -strict_optional = False - [tool:pytest] testpaths = tests diff --git a/tests/unit/profiles/test_black.py b/tests/unit/profiles/test_black.py index 0e54e706b..7288d4780 100644 --- a/tests/unit/profiles/test_black.py +++ b/tests/unit/profiles/test_black.py @@ -9,7 +9,7 @@ def black_format(code: str, is_pyi: bool = False, line_length: int = 88) -> str: return black.format_file_contents( code, fast=True, - mode=black.FileMode( + mode=black.FileMode( # type: ignore is_pyi=is_pyi, line_length=line_length, ), diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index bffa7fca2..7fe73ab93 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -14,7 +14,7 @@ @pytest.fixture -def imperfect(tmpdir) -> None: +def imperfect(tmpdir): imperfect_file = tmpdir.join("test_needs_changes.py") imperfect_file.write_text(imperfect_content, "utf8") return imperfect_file diff --git a/tests/unit/test_deprecated_finders.py b/tests/unit/test_deprecated_finders.py index bbd43596b..3e3be56e3 100644 --- a/tests/unit/test_deprecated_finders.py +++ b/tests/unit/test_deprecated_finders.py @@ -31,13 +31,13 @@ def test_init(self): assert FindersManager(settings.DEFAULT_CONFIG) class ExceptionOnInit(finders.BaseFinder): - def __init__(*args, **kwargs): + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) raise ValueError("test") with patch( "isort.deprecated.finders.FindersManager._default_finders_classes", - FindersManager._default_finders_classes + (ExceptionOnInit,), + FindersManager._default_finders_classes + (ExceptionOnInit,), # type: ignore ): assert FindersManager(settings.Config(verbose=True)) @@ -59,14 +59,14 @@ class AbstractTestFinder: @classmethod def setup_class(cls): - cls.instance = cls.kind(settings.DEFAULT_CONFIG) + cls.instance = cls.kind(settings.DEFAULT_CONFIG) # type: ignore def test_create(self): - assert self.kind(settings.DEFAULT_CONFIG) + assert self.kind(settings.DEFAULT_CONFIG) # type: ignore def test_find(self): - self.instance.find("isort") - self.instance.find("") + self.instance.find("isort") # type: ignore + self.instance.find("") # type: ignore class TestForcedSeparateFinder(AbstractTestFinder): @@ -154,7 +154,7 @@ def test_requirements_finder(tmpdir) -> None: assert finder.find("flask") is None # package not in reqs assert finder.find("deal") == sections.THIRDPARTY # vcs - assert len(finder.mapping) > 100 + assert len(finder.mapping) > 100 # type: ignore assert finder._normalize_name("deal") == "deal" assert finder._normalize_name("Django") == "django" # lowercase assert finder._normalize_name("django_haystack") == "haystack" # mapping @@ -174,7 +174,7 @@ def test_pipfile_finder(tmpdir) -> None: assert finder.find("flask") is None # package not in reqs assert finder.find("deal") == sections.THIRDPARTY # vcs - assert len(finder.mapping) > 100 + assert len(finder.mapping) > 100 # type: ignore assert finder._normalize_name("deal") == "deal" assert finder._normalize_name("Django") == "django" # lowercase assert finder._normalize_name("django_haystack") == "haystack" # mapping diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py index 2cd17aa11..81610557b 100644 --- a/tests/unit/test_exceptions.py +++ b/tests/unit/test_exceptions.py @@ -11,7 +11,7 @@ def test_init(self): class TestExistingSyntaxErrors(TestISortError): def setup_class(self): - self.instance = exceptions.ExistingSyntaxErrors("file_path") + self.instance: exceptions.ExistingSyntaxErrors = exceptions.ExistingSyntaxErrors("file_path") def test_variables(self): assert self.instance.file_path == "file_path" @@ -19,7 +19,7 @@ def test_variables(self): class TestIntroducedSyntaxErrors(TestISortError): def setup_class(self): - self.instance = exceptions.IntroducedSyntaxErrors("file_path") + self.instance: exceptions.IntroducedSyntaxErrors = exceptions.IntroducedSyntaxErrors("file_path") def test_variables(self): assert self.instance.file_path == "file_path" @@ -27,7 +27,7 @@ def test_variables(self): class TestFileSkipped(TestISortError): def setup_class(self): - self.instance = exceptions.FileSkipped("message", "file_path") + self.instance: exceptions.FileSkipped = exceptions.FileSkipped("message", "file_path") def test_variables(self): assert self.instance.file_path == "file_path" @@ -36,7 +36,7 @@ def test_variables(self): class TestFileSkipComment(TestISortError): def setup_class(self): - self.instance = exceptions.FileSkipComment("file_path") + self.instance: exceptions.FileSkipComment = exceptions.FileSkipComment("file_path") def test_variables(self): assert self.instance.file_path == "file_path" @@ -44,7 +44,7 @@ def test_variables(self): class TestFileSkipSetting(TestISortError): def setup_class(self): - self.instance = exceptions.FileSkipSetting("file_path") + self.instance: exceptions.FileSkipSetting = exceptions.FileSkipSetting("file_path") def test_variables(self): assert self.instance.file_path == "file_path" @@ -52,7 +52,7 @@ def test_variables(self): class TestProfileDoesNotExist(TestISortError): def setup_class(self): - self.instance = exceptions.ProfileDoesNotExist("profile") + self.instance: exceptions.ProfileDoesNotExist = exceptions.ProfileDoesNotExist("profile") def test_variables(self): assert self.instance.profile == "profile" @@ -60,7 +60,7 @@ def test_variables(self): class TestSortingFunctionDoesNotExist(TestISortError): def setup_class(self): - self.instance = exceptions.SortingFunctionDoesNotExist("round", ["square", "peg"]) + self.instance: exceptions.SortingFunctionDoesNotExist = exceptions.SortingFunctionDoesNotExist("round", ["square", "peg"]) def test_variables(self): assert self.instance.sort_order == "round" @@ -69,7 +69,7 @@ def test_variables(self): class TestLiteralParsingFailure(TestISortError): def setup_class(self): - self.instance = exceptions.LiteralParsingFailure("x = [", SyntaxError) + self.instance: exceptions.LiteralParsingFailure = exceptions.LiteralParsingFailure("x = [", SyntaxError) def test_variables(self): assert self.instance.code == "x = [" @@ -78,7 +78,7 @@ def test_variables(self): class TestLiteralSortTypeMismatch(TestISortError): def setup_class(self): - self.instance = exceptions.LiteralSortTypeMismatch(tuple, list) + self.instance: exceptions.LiteralSortTypeMismatch = exceptions.LiteralSortTypeMismatch(tuple, list) def test_variables(self): assert self.instance.kind == tuple @@ -87,7 +87,7 @@ def test_variables(self): class TestAssignmentsFormatMismatch(TestISortError): def setup_class(self): - self.instance = exceptions.AssignmentsFormatMismatch("print x") + self.instance: exceptions.AssignmentsFormatMismatch = exceptions.AssignmentsFormatMismatch("print x") def test_variables(self): assert self.instance.code == "print x" @@ -95,7 +95,7 @@ def test_variables(self): class TestUnsupportedSettings(TestISortError): def setup_class(self): - self.instance = exceptions.UnsupportedSettings({"apply": {"value": "true", "source": "/"}}) + self.instance: exceptions.UnsupportedSettings = exceptions.UnsupportedSettings({"apply": {"value": "true", "source": "/"}}) def test_variables(self): assert self.instance.unsupported_settings == {"apply": {"value": "true", "source": "/"}} @@ -103,7 +103,7 @@ def test_variables(self): class TestUnsupportedEncoding(TestISortError): def setup_class(self): - self.instance = exceptions.UnsupportedEncoding("file.py") + self.instance: exceptions.UnsupportedEncoding = exceptions.UnsupportedEncoding("file.py") def test_variables(self): assert self.instance.filename == "file.py" diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index c90f40d29..63a59f9a1 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -9,18 +9,24 @@ import sys from io import StringIO from tempfile import NamedTemporaryFile -from typing import Any, Dict, Iterator, List, Set, Tuple +from typing import Any, Dict, Iterator, List, Set, Tuple, TYPE_CHECKING import py import pytest import toml import isort from isort import api, sections, files -from isort.settings import WrapModes, Config +from isort.settings import Config + from isort.utils import exists_case_sensitive from isort.exceptions import FileSkipped, ExistingSyntaxErrors from .utils import as_stream, UnreadableStream +if TYPE_CHECKING: + WrapModes: Any +else: + from isort.wrap_modes import WrapModes + TEST_DEFAULT_CONFIG = """ [*.{py,pyi}] max_line_length = 120 @@ -225,7 +231,10 @@ def test_line_length() -> None: ) with pytest.raises(ValueError): test_output = isort.code(code=REALLY_LONG_IMPORT, line_length=80, wrap_length=99) - test_output = isort.code(REALLY_LONG_IMPORT, line_length=100, wrap_length=99) == test_input + assert isort.code(REALLY_LONG_IMPORT, line_length=100, wrap_length=99) == ( +"""from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, + lib13, lib14, lib15, lib16, lib17, lib18, lib20, lib21, lib22) +""") # Test Case described in issue #1015 test_output = isort.code( @@ -1271,46 +1280,44 @@ def test_force_single_line_imports_and_sort_within_sections() -> None: import pandas as pd from matplotlib import pyplot as plt """ - test_output = ( - isort.code(code=test_input, force_sort_within_sections=True, length_sort=True) == test_input - ) + assert isort.code(code=test_input, force_sort_within_sections=True, length_sort=True) == test_input def test_titled_imports() -> None: """Tests setting custom titled/commented import sections.""" - # test_input = ( - # "import sys\n" - # "import unicodedata\n" - # "import statistics\n" - # "import os\n" - # "import myproject.test\n" - # "import django.settings" - # ) - # test_output = isort.code( - # code=test_input, - # known_first_party=["myproject"], - # import_heading_stdlib="Standard Library", - # import_heading_firstparty="My Stuff", - # ) - # assert test_output == ( - # "# Standard Library\n" - # "import os\n" - # "import statistics\n" - # "import sys\n" - # "import unicodedata\n" - # "\n" - # "import django.settings\n" - # "\n" - # "# My Stuff\n" - # "import myproject.test\n" - # ) - # test_second_run = isort.code( - # code=test_output, - # known_first_party=["myproject"], - # import_heading_stdlib="Standard Library", - # import_heading_firstparty="My Stuff", - # ) - # assert test_second_run == test_output + test_input = ( + "import sys\n" + "import unicodedata\n" + "import statistics\n" + "import os\n" + "import myproject.test\n" + "import django.settings" + ) + test_output = isort.code( + code=test_input, + known_first_party=["myproject"], + import_heading_stdlib="Standard Library", + import_heading_firstparty="My Stuff", + ) + assert test_output == ( + "# Standard Library\n" + "import os\n" + "import statistics\n" + "import sys\n" + "import unicodedata\n" + "\n" + "import django.settings\n" + "\n" + "# My Stuff\n" + "import myproject.test\n" + ) + test_second_run = isort.code( + code=test_output, + known_first_party=["myproject"], + import_heading_stdlib="Standard Library", + import_heading_firstparty="My Stuff", + ) + assert test_second_run == test_output test_input_lines_down = ( "# comment 1\n" @@ -1420,9 +1427,9 @@ def test_atomic_mode() -> None: ) # ensure atomic works with streams - test_input = as_stream("from b import d, c\nfrom a import f, e\n") + test_stream_input = as_stream("from b import d, c\nfrom a import f, e\n") test_output = UnreadableStream() - isort.stream(test_input, test_output, atomic=True) + isort.stream(test_stream_input, test_output, atomic=True) test_output.seek(0) assert test_output.read() == "from a import e, f\nfrom b import c, d\n" diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 207f19dc4..e76976437 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -12,10 +12,14 @@ from isort._version import __version__ from isort.exceptions import InvalidSettingsPath from isort.settings import DEFAULT_CONFIG, Config -from isort.wrap_modes import WrapModes from .utils import as_stream from io import BytesIO, TextIOWrapper +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + WrapModes: Any +else: + from isort.wrap_modes import WrapModes @given( file_name=st.text(), @@ -37,15 +41,15 @@ def test_fuzz_sort_imports(file_name, config, check, ask_to_apply, write_to_stdo def test_sort_imports(tmpdir): tmp_file = tmpdir.join("file.py") tmp_file.write("import os, sys\n") - assert main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted + assert main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore main.sort_imports(str(tmp_file), DEFAULT_CONFIG) - assert not main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted + assert not main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore skip_config = Config(skip=["file.py"]) - assert main.sort_imports( + assert main.sort_imports( # type: ignore str(tmp_file), config=skip_config, check=True, disregard_skip=False - ).skipped - assert main.sort_imports(str(tmp_file), config=skip_config, disregard_skip=False).skipped + ).skippedg + assert main.sort_imports(str(tmp_file), config=skip_config, disregard_skip=False).skipped # type: ignore def test_sort_imports_error_handling(tmpdir, mocker, capsys): @@ -53,7 +57,7 @@ def test_sort_imports_error_handling(tmpdir, mocker, capsys): tmp_file.write("import os, sys\n") mocker.patch("isort.core.process").side_effect = IndexError("Example unhandled exception") with pytest.raises(IndexError): - main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted + main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore out, error = capsys.readouterr() assert "Unrecoverable exception thrown when parsing" in error @@ -345,7 +349,7 @@ def test_main(capsys, tmpdir): def test_isort_command(): """Ensure ISortCommand got registered, otherwise setuptools error must have occurred""" - assert main.ISortCommand + assert main.ISortCommand # type: ignore def test_isort_filename_overrides(tmpdir, capsys): @@ -1060,7 +1064,7 @@ def test_identify_imports_main(tmpdir, capsys): len(out.split("\n")) == 2 -def test_gitignore(capsys: pytest.CaptureFixture, tmpdir: py.path.local): +def test_gitignore(capsys, tmpdir: py.path.local): import_content = """ import b diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index ccd3da223..a7933005d 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -233,7 +233,7 @@ def test_ensure_sre_parse_is_identified_as_stdlib_issue_1304(): """Ensure sre_parse is idenified as STDLIB. See: https://github.com/pycqa/isort/issues/1304. """ - assert isort.place_module("sre_parse") == isort.place_module("sre") == isort.settings.STDLIB + assert isort.place_module("sre_parse") == isort.place_module("sre") == isort.settings.STDLIB # type: ignore def test_add_imports_shouldnt_move_lower_comments_issue_1300(): diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index 6ef8f70ec..ad0f3ae42 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -93,11 +93,11 @@ def test_src_paths_are_combined_and_deduplicated(self): assert Config(src_paths=src_paths * 2).src_paths == tuple(src_full_paths) def test_deprecated_multi_line_output(self): - assert Config(multi_line_output=6).multi_line_output == WrapModes.VERTICAL_GRID_GROUPED + assert Config(multi_line_output=6).multi_line_output == WrapModes.VERTICAL_GRID_GROUPED # type: ignore def test_as_list(): - assert settings._as_list([" one "]) == ["one"] + assert settings._as_list([" one "]) == ["one"] # type: ignore assert settings._as_list("one,two") == ["one", "two"] From 9abbe8845ea5daf83cace9c6e3d7c127eb7e9206 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Jun 2021 23:20:27 -0700 Subject: [PATCH 505/539] Drastically improve isort start up speed by avoiding import of deprecated finders --- isort/parse.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/isort/parse.py b/isort/parse.py index aec508745..fb109013a 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -7,7 +7,6 @@ from . import place from .comments import parse as parse_comments -from .deprecated.finders import FindersManager from .exceptions import MissingSection from .settings import DEFAULT_CONFIG, Config @@ -151,6 +150,8 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte out_lines = [] original_line_count = len(in_lines) if config.old_finders: + from .deprecated.finders import FindersManager + finder = FindersManager(config=config).find else: finder = partial(place.module, config=config) From f26ba583bfb11dbd54dbb51fa5153085102e1bad Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Jun 2021 23:27:58 -0700 Subject: [PATCH 506/539] Add note about improving CLI startup time --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33e394eab..98061fffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). ### 5.9.0 TBD + - Improved CLI startup time. - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. - Implemented #1737: Support for using action comments to avoid adding imports to individual files. From 170583575dc8a9f1c50cbd2bb8eb79df1a98fb82 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Jun 2021 23:37:17 -0700 Subject: [PATCH 507/539] Speed up init time further by avoiding import of ISortCommand --- isort/main.py | 5 ----- pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/isort/main.py b/isort/main.py index a57b3fd28..9555798eb 100644 --- a/isort/main.py +++ b/isort/main.py @@ -18,11 +18,6 @@ from .settings import VALID_PY_TARGETS, Config from .wrap_modes import WrapModes -try: - from .setuptools_commands import ISortCommand # noqa: F401 -except ImportError: - pass - DEPRECATED_SINGLE_DASH_ARGS = { "-ac", "-af", diff --git a/pyproject.toml b/pyproject.toml index dbd6bd2e0..5b63e6b84 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,7 +98,7 @@ isort = "isort.main:main" isort-identify-imports = "isort.main:identify_imports_main" [tool.poetry.plugins."distutils.commands"] -isort = "isort.main:ISortCommand" +isort = "isort.setuptools_commands:ISortCommand" [tool.poetry.plugins."pylama.linter"] isort = "isort.pylama_isort:Linter" From 9bf7b356eeebc7b74322a33091dea217271d3041 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Sun, 20 Jun 2021 23:46:18 -0700 Subject: [PATCH 508/539] Address linting errors --- pyproject.toml | 2 ++ tests/unit/test_exceptions.py | 28 +++++++++++---- tests/unit/test_isort.py | 63 ++++++++++++++++++---------------- tests/unit/test_main.py | 16 ++++----- tests/unit/test_regressions.py | 4 ++- tests/unit/test_settings.py | 2 +- 6 files changed, 67 insertions(+), 48 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5b63e6b84..8adde3b59 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,6 +92,8 @@ toml = "^0.10.2" pytest-benchmark = "^3.4.1" types-pkg-resources = "^0.1.2" pre-commit = "^2.13.0" +types-colorama = "^0.4.2" +types-toml = "^0.1.3" [tool.poetry.scripts] isort = "isort.main:main" diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py index 81610557b..d3a063a7d 100644 --- a/tests/unit/test_exceptions.py +++ b/tests/unit/test_exceptions.py @@ -11,7 +11,9 @@ def test_init(self): class TestExistingSyntaxErrors(TestISortError): def setup_class(self): - self.instance: exceptions.ExistingSyntaxErrors = exceptions.ExistingSyntaxErrors("file_path") + self.instance: exceptions.ExistingSyntaxErrors = exceptions.ExistingSyntaxErrors( + "file_path" + ) def test_variables(self): assert self.instance.file_path == "file_path" @@ -19,7 +21,9 @@ def test_variables(self): class TestIntroducedSyntaxErrors(TestISortError): def setup_class(self): - self.instance: exceptions.IntroducedSyntaxErrors = exceptions.IntroducedSyntaxErrors("file_path") + self.instance: exceptions.IntroducedSyntaxErrors = exceptions.IntroducedSyntaxErrors( + "file_path" + ) def test_variables(self): assert self.instance.file_path == "file_path" @@ -60,7 +64,9 @@ def test_variables(self): class TestSortingFunctionDoesNotExist(TestISortError): def setup_class(self): - self.instance: exceptions.SortingFunctionDoesNotExist = exceptions.SortingFunctionDoesNotExist("round", ["square", "peg"]) + self.instance: exceptions.SortingFunctionDoesNotExist = ( + exceptions.SortingFunctionDoesNotExist("round", ["square", "peg"]) + ) def test_variables(self): assert self.instance.sort_order == "round" @@ -69,7 +75,9 @@ def test_variables(self): class TestLiteralParsingFailure(TestISortError): def setup_class(self): - self.instance: exceptions.LiteralParsingFailure = exceptions.LiteralParsingFailure("x = [", SyntaxError) + self.instance: exceptions.LiteralParsingFailure = exceptions.LiteralParsingFailure( + "x = [", SyntaxError + ) def test_variables(self): assert self.instance.code == "x = [" @@ -78,7 +86,9 @@ def test_variables(self): class TestLiteralSortTypeMismatch(TestISortError): def setup_class(self): - self.instance: exceptions.LiteralSortTypeMismatch = exceptions.LiteralSortTypeMismatch(tuple, list) + self.instance: exceptions.LiteralSortTypeMismatch = exceptions.LiteralSortTypeMismatch( + tuple, list + ) def test_variables(self): assert self.instance.kind == tuple @@ -87,7 +97,9 @@ def test_variables(self): class TestAssignmentsFormatMismatch(TestISortError): def setup_class(self): - self.instance: exceptions.AssignmentsFormatMismatch = exceptions.AssignmentsFormatMismatch("print x") + self.instance: exceptions.AssignmentsFormatMismatch = exceptions.AssignmentsFormatMismatch( + "print x" + ) def test_variables(self): assert self.instance.code == "print x" @@ -95,7 +107,9 @@ def test_variables(self): class TestUnsupportedSettings(TestISortError): def setup_class(self): - self.instance: exceptions.UnsupportedSettings = exceptions.UnsupportedSettings({"apply": {"value": "true", "source": "/"}}) + self.instance: exceptions.UnsupportedSettings = exceptions.UnsupportedSettings( + {"apply": {"value": "true", "source": "/"}} + ) def test_variables(self): assert self.instance.unsupported_settings == {"apply": {"value": "true", "source": "/"}} diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 63a59f9a1..526a7b8a0 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -9,7 +9,7 @@ import sys from io import StringIO from tempfile import NamedTemporaryFile -from typing import Any, Dict, Iterator, List, Set, Tuple, TYPE_CHECKING +from typing import Any, Dict, Iterator, List, Set, Tuple, TYPE_CHECKING import py import pytest @@ -231,10 +231,13 @@ def test_line_length() -> None: ) with pytest.raises(ValueError): test_output = isort.code(code=REALLY_LONG_IMPORT, line_length=80, wrap_length=99) - assert isort.code(REALLY_LONG_IMPORT, line_length=100, wrap_length=99) == ( -"""from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, + assert ( + isort.code(REALLY_LONG_IMPORT, line_length=100, wrap_length=99) + == """ +from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14, lib15, lib16, lib17, lib18, lib20, lib21, lib22) -""") +""".lstrip() + ) # Test Case described in issue #1015 test_output = isort.code( @@ -1280,42 +1283,44 @@ def test_force_single_line_imports_and_sort_within_sections() -> None: import pandas as pd from matplotlib import pyplot as plt """ - assert isort.code(code=test_input, force_sort_within_sections=True, length_sort=True) == test_input + assert ( + isort.code(code=test_input, force_sort_within_sections=True, length_sort=True) == test_input + ) def test_titled_imports() -> None: """Tests setting custom titled/commented import sections.""" test_input = ( - "import sys\n" - "import unicodedata\n" - "import statistics\n" - "import os\n" - "import myproject.test\n" - "import django.settings" + "import sys\n" + "import unicodedata\n" + "import statistics\n" + "import os\n" + "import myproject.test\n" + "import django.settings" ) test_output = isort.code( - code=test_input, - known_first_party=["myproject"], - import_heading_stdlib="Standard Library", - import_heading_firstparty="My Stuff", + code=test_input, + known_first_party=["myproject"], + import_heading_stdlib="Standard Library", + import_heading_firstparty="My Stuff", ) assert test_output == ( - "# Standard Library\n" - "import os\n" - "import statistics\n" - "import sys\n" - "import unicodedata\n" - "\n" - "import django.settings\n" - "\n" - "# My Stuff\n" - "import myproject.test\n" + "# Standard Library\n" + "import os\n" + "import statistics\n" + "import sys\n" + "import unicodedata\n" + "\n" + "import django.settings\n" + "\n" + "# My Stuff\n" + "import myproject.test\n" ) test_second_run = isort.code( - code=test_output, - known_first_party=["myproject"], - import_heading_stdlib="Standard Library", - import_heading_firstparty="My Stuff", + code=test_output, + known_first_party=["myproject"], + import_heading_stdlib="Standard Library", + import_heading_firstparty="My Stuff", ) assert test_second_run == test_output diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index e76976437..9039787cc 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -21,6 +21,7 @@ else: from isort.wrap_modes import WrapModes + @given( file_name=st.text(), config=st.builds(Config), @@ -41,15 +42,15 @@ def test_fuzz_sort_imports(file_name, config, check, ask_to_apply, write_to_stdo def test_sort_imports(tmpdir): tmp_file = tmpdir.join("file.py") tmp_file.write("import os, sys\n") - assert main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore + assert main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore # noqa main.sort_imports(str(tmp_file), DEFAULT_CONFIG) - assert not main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore + assert not main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore # noqa skip_config = Config(skip=["file.py"]) assert main.sort_imports( # type: ignore str(tmp_file), config=skip_config, check=True, disregard_skip=False - ).skippedg - assert main.sort_imports(str(tmp_file), config=skip_config, disregard_skip=False).skipped # type: ignore + ).skipped + assert main.sort_imports(str(tmp_file), config=skip_config, disregard_skip=False).skipped # type: ignore # noqa def test_sort_imports_error_handling(tmpdir, mocker, capsys): @@ -57,7 +58,7 @@ def test_sort_imports_error_handling(tmpdir, mocker, capsys): tmp_file.write("import os, sys\n") mocker.patch("isort.core.process").side_effect = IndexError("Example unhandled exception") with pytest.raises(IndexError): - main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore + main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore # noqa out, error = capsys.readouterr() assert "Unrecoverable exception thrown when parsing" in error @@ -347,11 +348,6 @@ def test_main(capsys, tmpdir): main.main(["-sp", str(config_file), "-"], stdin=input_content) -def test_isort_command(): - """Ensure ISortCommand got registered, otherwise setuptools error must have occurred""" - assert main.ISortCommand # type: ignore - - def test_isort_filename_overrides(tmpdir, capsys): """Tests isorts available approaches for overriding filename and extension based behavior""" input_text = """ diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index a7933005d..388aa8d38 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -233,7 +233,9 @@ def test_ensure_sre_parse_is_identified_as_stdlib_issue_1304(): """Ensure sre_parse is idenified as STDLIB. See: https://github.com/pycqa/isort/issues/1304. """ - assert isort.place_module("sre_parse") == isort.place_module("sre") == isort.settings.STDLIB # type: ignore + assert ( + isort.place_module("sre_parse") == isort.place_module("sre") == isort.settings.STDLIB # type: ignore # noqa + ) def test_add_imports_shouldnt_move_lower_comments_issue_1300(): diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index ad0f3ae42..72b473d7b 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -93,7 +93,7 @@ def test_src_paths_are_combined_and_deduplicated(self): assert Config(src_paths=src_paths * 2).src_paths == tuple(src_full_paths) def test_deprecated_multi_line_output(self): - assert Config(multi_line_output=6).multi_line_output == WrapModes.VERTICAL_GRID_GROUPED # type: ignore + assert Config(multi_line_output=6).multi_line_output == WrapModes.VERTICAL_GRID_GROUPED # type: ignore # noqa def test_as_list(): From bacec68bcdad6eb872bb884ea77e02bd959129fe Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Jun 2021 00:03:03 -0700 Subject: [PATCH 509/539] Bump version to 5.9.0 --- CHANGELOG.md | 2 +- isort/_version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98061fffc..0dc1f28d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). -### 5.9.0 TBD +### 5.9.0 June 21st 2021 - Improved CLI startup time. - Implemented #1697: Provisional support for PEP 582: skip `__pypackages__` directories by default. - Implemented #1705: More intuitive handling of isort:skip_file comments on streams. diff --git a/isort/_version.py b/isort/_version.py index e0b752771..b6a1a593e 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.8.0" +__version__ = "5.9.0" diff --git a/pyproject.toml b/pyproject.toml index 8adde3b59..602854b33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.8.0" +version = "5.9.0" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" From 9f39d7e121f910450688d685bbede66dd929dc08 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Jun 2021 00:11:47 -0700 Subject: [PATCH 510/539] Add recent contributors --- docs/contributing/4.-acknowledgements.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 3f9210600..2720c6eea 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -226,6 +226,8 @@ Code Contributors - Luca Di sera (@diseraluca) - Tonye Jack (@jackton1) - Yusuke Hayashi (@yhay81) +- Arthur Rio (@arthurio) +- Bob (@bobwalker99) Documenters =================== From 3d7d1319402853301a6d8bbe4632f947b66a93c7 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Jun 2021 00:12:34 -0700 Subject: [PATCH 511/539] Regenerate options --- docs/configuration/options.md | 74 ++++++++++++++++------------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index 9795e9640..659b6ee5b 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -5,7 +5,7 @@ isort will disagree but commit to your way of formatting. To enable this, isort how you want your imports sorted, organized, and formatted. Too busy to build your perfect isort configuration? For curated common configurations, see isort's [built-in -profiles](https://pycqa.github.io/isort/docs/configuration/profiles.html). +profiles](https://pycqa.github.io/isort/docs/configuration/profiles/). ## Python Version @@ -1074,17 +1074,6 @@ Tells isort to overwrite in place using the same file handle.Comes at a performa - --overwrite-in-place -## Sorting Order - -Use natural language or pythonic sorting. Valid values "default"=natural "pythonic"=python default sorting. - -**Type:** String -**Default:** `default` -**Python & Config File Name:** sort_order -**CLI Flags:** - -- --sort-order - ## Reverse Sort Reverses the ordering of imports. @@ -1116,6 +1105,39 @@ Forces star imports above others to avoid overriding directly imported variables **Python & Config File Name:** git_ignore **CLI Flags:** **Not Supported** +## Format Error + +Override the format used to print errors. + +**Type:** String +**Default:** `{error}: {message}` +**Python & Config File Name:** format_error +**CLI Flags:** + +- --format-error + +## Format Success + +Override the format used to print success. + +**Type:** String +**Default:** `{success}: {message}` +**Python & Config File Name:** format_success +**CLI Flags:** + +- --format-success + +## Sort Order + +Specify sorting function. Can be built in (natural[default] = force numbers to be sequential, native = Python's built-in sorted function) or an installable plugin. + +**Type:** String +**Default:** `natural` +**Python & Config File Name:** sort_order +**CLI Flags:** + +- --sort-order + ## Show Version Displays the currently installed version of isort. @@ -1323,34 +1345,6 @@ Tells isort to format the given files according to an extensions formatting rule - --ext-format -## Format Errors - -Define the format used to print errors. - -**NOTE** Variables are `error` and `message`. -The `error` variable prints `ERROR` with color depending on the --color option. - -**Type:** String -**Default:** `{error}: {message}` -**Python & Config File Name:** format_errors -**CLI Flags:** - -- --format-errors - -## Format Success - -Define the format used to print successes. - -**NOTE** Variables are `success` and `message`. -The `success` variable prints `SUCCESS` with color depending on the --color option. - -**Type:** String -**Default:** `{success}: {message}` -**Python & Config File Name:** format_success -**CLI Flags:** - -- --format-success - ## Deprecated Flags ==SUPPRESS== From ee53d09e037ddc33f5618d3d07f4da6a1fce26a3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Jun 2021 00:28:03 -0700 Subject: [PATCH 512/539] Re-run integration tests From 6f42a66401457dcd913c9d4be60aeff665c9b983 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Jun 2021 01:45:54 -0700 Subject: [PATCH 513/539] Fix links --- docs/configuration/options.md | 2 +- scripts/build_config_option_docs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index 659b6ee5b..c1c4afc6c 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -5,7 +5,7 @@ isort will disagree but commit to your way of formatting. To enable this, isort how you want your imports sorted, organized, and formatted. Too busy to build your perfect isort configuration? For curated common configurations, see isort's [built-in -profiles](https://pycqa.github.io/isort/docs/configuration/profiles/). +profiles](https://pycqa.github.io/isort/docs/configuration/profiles.html). ## Python Version diff --git a/scripts/build_config_option_docs.py b/scripts/build_config_option_docs.py index d57e96c51..15a3db53e 100755 --- a/scripts/build_config_option_docs.py +++ b/scripts/build_config_option_docs.py @@ -22,7 +22,7 @@ how you want your imports sorted, organized, and formatted. Too busy to build your perfect isort configuration? For curated common configurations, see isort's [built-in -profiles](https://pycqa.github.io/isort/docs/configuration/profiles/). +profiles](https://pycqa.github.io/isort/docs/configuration/profiles.html). """ parser = _build_arg_parser() From 0ccefec5a394a77a8a03e6c2f517b484765abcda Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Jun 2021 01:53:35 -0700 Subject: [PATCH 514/539] Fix remaining broken links --- docs/quick_start/0.-try.md | 4 ++-- docs/quick_start/2.-cli.md | 2 +- docs/quick_start/3.-api.md | 2 +- docs/upgrade_guides/5.0.0.md | 2 +- isort/exceptions.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/quick_start/0.-try.md b/docs/quick_start/0.-try.md index 61fae1535..b0e160c3a 100644 --- a/docs/quick_start/0.-try.md +++ b/docs/quick_start/0.-try.md @@ -32,7 +32,7 @@ import b, a
Loading...
- Configuration (Note: the below must follow JSON format). Full configuration guide is here: + Configuration (Note: the below must follow JSON format). Full configuration guide is here:
{"line_length": 80, "profile": "black", @@ -47,4 +47,4 @@ import b, a
Like what you saw? Installing isort to use locally is as simple as `pip3 install isort`. -[Click here for full installation instructions.](https://pycqa.github.io/isort/docs/quick_start/1.-install/) +[Click here for full installation instructions.](https://pycqa.github.io/isort/docs/quick_start/1.-install) diff --git a/docs/quick_start/2.-cli.md b/docs/quick_start/2.-cli.md index f7b313464..5e3e45b6e 100644 --- a/docs/quick_start/2.-cli.md +++ b/docs/quick_start/2.-cli.md @@ -3,7 +3,7 @@ Once installed, `isort` exposes a command line utility for sorting, organizing, and formatting imports within Python and Cython source files. To verify the tool is installed correctly, run `isort` from the command line and you should be given the available commands and the version of isort installed. -For a list of all CLI options type `isort --help` or view [the online configuration reference](https://pycqa.github.io/isort/docs/configuration/options/): +For a list of all CLI options type `isort --help` or view [the online configuration reference](https://pycqa.github.io/isort/docs/configuration/options): diff --git a/docs/quick_start/3.-api.md b/docs/quick_start/3.-api.md index a327fc73a..da197719d 100644 --- a/docs/quick_start/3.-api.md +++ b/docs/quick_start/3.-api.md @@ -19,4 +19,4 @@ Highlights include: - `isort.place_module` - Takes the name of a module as a string and returns the categorization determined for it. - `isort.place_module_with_reason` - Takes the name of a module as a string and returns the categorization determined for it and why that categorization was given. -For a full definition of the API see the [API reference documentation](https://pycqa.github.io/isort/reference/isort/api/) or try `help(isort)` from an interactive interpreter. +For a full definition of the API see the [API reference documentation](https://pycqa.github.io/isort/reference/isort/api) or try `help(isort)` from an interactive interpreter. diff --git a/docs/upgrade_guides/5.0.0.md b/docs/upgrade_guides/5.0.0.md index 743df0297..057ae7be6 100644 --- a/docs/upgrade_guides/5.0.0.md +++ b/docs/upgrade_guides/5.0.0.md @@ -5,7 +5,7 @@ This guide is meant to help migrate projects from using isort 4.x.x unto the 5.0 Related documentation: -* [isort 5.0.0 changelog](https://pycqa.github.io/isort/CHANGELOG/#500-penny-july-4-2020) +* [isort 5.0.0 changelog](https://pycqa.github.io/isort/CHANGELOG#500-penny-july-4-2020) * [isort 5 release document](https://pycqa.github.io/isort/docs/major_releases/introducing_isort_5.html) !!! important - "If you use pre-commit remove seed-isort-config." diff --git a/isort/exceptions.py b/isort/exceptions.py index e5eeceaa8..1cd9251d2 100644 --- a/isort/exceptions.py +++ b/isort/exceptions.py @@ -167,7 +167,7 @@ def __init__(self, unsupported_settings: Dict[str, Dict[str, str]]): "isort was provided settings that it doesn't support:\n\n" f"{errors}\n\n" "For a complete and up-to-date listing of supported settings see: " - "https://pycqa.github.io/isort/docs/configuration/options/.\n" + "https://pycqa.github.io/isort/docs/configuration/options.\n" ) self.unsupported_settings = unsupported_settings From 761aff4493aacfb0fab1c2ad747d009279e7824d Mon Sep 17 00:00:00 2001 From: Martijn Pieters Date: Mon, 21 Jun 2021 13:41:49 +0100 Subject: [PATCH 515/539] Use a pipe to pass filenames to git check-ignore The number of files in a project could be larger than the limit on command-line arguments. Using a pipe avoids running into this issue. --- isort/settings.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index d04dd8386..1dec20467 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -2,7 +2,6 @@ Defines how the default settings for isort should be loaded """ -import codecs import configparser import fnmatch import glob @@ -544,16 +543,12 @@ def _check_folder_gitignore(self, folder: str) -> Optional[Path]: git_folder = Path(topfolder_result.decode("utf-8").split("\n")[0]) files = glob.glob(str(git_folder) + "/**/*", recursive=True) - files_result = ( - codecs.escape_decode( # type: ignore - subprocess.check_output( # nosec # skipcq: PYL-W1510 - ["git", "-C", str(git_folder), "check-ignore", *files] - ) - )[0] - .decode("utf-8") - .split("\n") - ) - files_result = files_result[:-1] if files_result else files_result + files_result = subprocess.check_output( # nosec # skipcq: PYL-W1510 + ["git", "-C", str(git_folder), "check-ignore", "--stdin"], + encoding="utf-8", + env={"LANG": "C.UTF-8"}, + input="\n".join(files), + ).splitlines() self.git_ignore[git_folder] = {Path(f.strip('"')) for f in files_result} From 84664a6820dc62488c46a0500376da1f12bccaa0 Mon Sep 17 00:00:00 2001 From: Timothy Edmund Crosley Date: Mon, 21 Jun 2021 06:09:34 -0700 Subject: [PATCH 516/539] Use escape_decode to address windows based usage --- isort/settings.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index 1dec20467..93ec08ba1 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -2,6 +2,7 @@ Defines how the default settings for isort should be loaded """ +import codecs import configparser import fnmatch import glob @@ -543,12 +544,16 @@ def _check_folder_gitignore(self, folder: str) -> Optional[Path]: git_folder = Path(topfolder_result.decode("utf-8").split("\n")[0]) files = glob.glob(str(git_folder) + "/**/*", recursive=True) - files_result = subprocess.check_output( # nosec # skipcq: PYL-W1510 - ["git", "-C", str(git_folder), "check-ignore", "--stdin"], - encoding="utf-8", - env={"LANG": "C.UTF-8"}, - input="\n".join(files), - ).splitlines() + files_result = ( + codecs.escape_decode( # type: ignore + subprocess.check_output( # nosec # skipcq: PYL-W1510 + ["git", "-C", str(git_folder), "check-ignore", "--stdin"], + input="\n".join(files).encode(), + ) + )[0] + .decode("utf-8") + .splitlines() + ) self.git_ignore[git_folder] = {Path(f.strip('"')) for f in files_result} From 1d7e37c3ff2536abf4359f73c46c418cfec12ada Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Jun 2021 06:19:05 -0700 Subject: [PATCH 517/539] Bump version to 5.9.1 (hotfix) --- CHANGELOG.md | 5 ++++- isort/_version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc1f28d6..b17ed08e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,10 @@ Changelog ========= NOTE: isort follows the [semver](https://semver.org/) versioning standard. -Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy/). +Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy). + +### 5.9.1 June 21st 2021 [hotfix] + - Fixed #1758: projects with many files and skip_ignore set can lead to a command-line overload. ### 5.9.0 June 21st 2021 - Improved CLI startup time. diff --git a/isort/_version.py b/isort/_version.py index b6a1a593e..b98cc2069 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.9.0" +__version__ = "5.9.1" diff --git a/pyproject.toml b/pyproject.toml index 602854b33..3bebb4d1f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.9.0" +version = "5.9.1" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" From fe2735a8a5c3b26679fe34ba4832c7670cc9621c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Mon, 21 Jun 2021 06:32:33 -0700 Subject: [PATCH 518/539] Add - Martijn Pieters (@mjpieters) to acknowledgements --- docs/contributing/4.-acknowledgements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 2720c6eea..32ed30efc 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -228,6 +228,7 @@ Code Contributors - Yusuke Hayashi (@yhay81) - Arthur Rio (@arthurio) - Bob (@bobwalker99) +- Martijn Pieters (@mjpieters) Documenters =================== From 05a8ded5cb1258b978dc95b285fa4410eb328d57 Mon Sep 17 00:00:00 2001 From: Martijn Pieters Date: Mon, 21 Jun 2021 15:35:05 +0100 Subject: [PATCH 519/539] Avoid git octal escaping - Disable the git `core.quotePath` option - Use `-z` to switch from linefeed to null-byte delimiting In addition, clean up exception handling (limit `try...except` to the code that needs it), and top-level path parsing (there is just a single path, with a newline appended). --- isort/settings.py | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index 93ec08ba1..70fe71b32 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -2,7 +2,6 @@ Defines how the default settings for isort should be loaded """ -import codecs import configparser import fnmatch import glob @@ -537,31 +536,31 @@ def is_supported_filetype(self, file_name: str) -> bool: return bool(_SHEBANG_RE.match(line)) def _check_folder_gitignore(self, folder: str) -> Optional[Path]: + env = {"LANG": "C.UTF-8"} try: topfolder_result = subprocess.check_output( # nosec # skipcq: PYL-W1510 - ["git", "-C", folder, "rev-parse", "--show-toplevel"] + ["git", "-C", folder, "rev-parse", "--show-toplevel"], encoding="utf-8", env=env ) - git_folder = Path(topfolder_result.decode("utf-8").split("\n")[0]) - - files = glob.glob(str(git_folder) + "/**/*", recursive=True) - files_result = ( - codecs.escape_decode( # type: ignore - subprocess.check_output( # nosec # skipcq: PYL-W1510 - ["git", "-C", str(git_folder), "check-ignore", "--stdin"], - input="\n".join(files).encode(), - ) - )[0] - .decode("utf-8") - .splitlines() - ) - - self.git_ignore[git_folder] = {Path(f.strip('"')) for f in files_result} + except subprocess.CalledProcessError: + return None - return git_folder + git_folder = Path(topfolder_result.rstrip()) + files = glob.glob(f"{git_folder}/**/*", recursive=True) + git_options = ["-C", str(git_folder), "-c", "core.quotePath="] + try: + ignored = subprocess.check_output( # nosec # skipcq: PYL-W1510 + ["git", *git_options, "check-ignore", "-z", "--stdin"], + encoding="utf-8", + env=env, + input="\0".join(files), + ) except subprocess.CalledProcessError: return None + self.git_ignore[git_folder] = {Path(f) for f in ignored.rstrip("\0").split("\0")} + return git_folder + def is_skipped(self, file_path: Path) -> bool: """Returns True if the file and/or folder should be skipped based on current settings.""" if self.directory and Path(self.directory) in file_path.resolve().parents: From e43e602a65a23a606adaaaf1caa0e571f43b4dd6 Mon Sep 17 00:00:00 2001 From: John Brock Date: Wed, 23 Jun 2021 18:10:40 -0700 Subject: [PATCH 520/539] Fix URLs for 5.0.0 upgrade guide --- docs/warning_and_error_codes/W0500.md | 2 +- isort/main.py | 6 +++--- isort/settings.py | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/warning_and_error_codes/W0500.md b/docs/warning_and_error_codes/W0500.md index a924ffc95..d8efe3f07 100644 --- a/docs/warning_and_error_codes/W0500.md +++ b/docs/warning_and_error_codes/W0500.md @@ -3,7 +3,7 @@ The W0500 error codes are reserved for warnings related to a major release of the isort project. Generally, the existence of any of these will trigger one additional warning listing the upgrade guide. -For the most recent upgrade guide, see: [The 5.0.0 Upgrade Guide.](https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0/). +For the most recent upgrade guide, see: [The 5.0.0 Upgrade Guide.](https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html). ## W0501: Deprecated CLI flags were included that will be ignored. diff --git a/isort/main.py b/isort/main.py index 9555798eb..ca0ce5e20 100644 --- a/isort/main.py +++ b/isort/main.py @@ -137,8 +137,8 @@ def _build_arg_parser() -> argparse.ArgumentParser: "Use `-` as the first argument to represent stdin. Use --interactive to use the pre 5.0.0 " "interactive behavior." " " - "If you've used isort 4 but are new to isort 5, see the upgrading guide:" - "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0/.", + "If you've used isort 4 but are new to isort 5, see the upgrading guide: " + "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html", add_help=False, # prevent help option from appearing in "optional arguments" group ) @@ -1230,7 +1230,7 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = ) warn( "W0500: Please see the 5.0.0 Upgrade guide: " - "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0/" + "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html" ) if wrong_sorted_files: diff --git a/isort/settings.py b/isort/settings.py index 70fe71b32..033315338 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -484,7 +484,8 @@ def __init__( warn( "W0503: Deprecated config options were used: " f"{', '.join(deprecated_options_used)}." - "Please see the 5.0.0 upgrade guide: bit.ly/isortv5." + "Please see the 5.0.0 upgrade guide: " + "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html" ) if known_other: From b8c1286605d539a8fda9f0c837c1f1e9a9362f7f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 23 Jun 2021 22:29:25 -0700 Subject: [PATCH 521/539] Improve behavoir of isort --check --atomic against Cython files --- CHANGELOG.md | 3 +++ isort/api.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b17ed08e4..4863d54ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy). +### 5.9.2 + - Improved behavior of `isort --check --atomic` against Cython files. + ### 5.9.1 June 21st 2021 [hotfix] - Fixed #1758: projects with many files and skip_ignore set can lead to a command-line overload. diff --git a/isort/api.py b/isort/api.py index 31e433cfa..392c94ca6 100644 --- a/isort/api.py +++ b/isort/api.py @@ -158,6 +158,7 @@ def sort_stream( TextIO stream is provided results will be written to it, otherwise no diff will be computed. - ****config_kwargs**: Any config modifications. """ + extension = extension or (file_path and file_path.suffix.lstrip(".")) or "py" if show_diff: _output_stream = StringIO() _input_stream = StringIO(input_stream.read()) @@ -209,7 +210,7 @@ def sort_stream( changed = core.process( input_stream, _internal_output, - extension=extension or (file_path and file_path.suffix.lstrip(".")) or "py", + extension=extension, config=config, raise_on_skip=raise_on_skip, ) From 23d708e863f0d017a33bfda4aa73c1905633f161 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 24 Jun 2021 23:52:45 -0700 Subject: [PATCH 522/539] Move git ignore check to bottom (after all it's the slowest) --- isort/settings.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index 033315338..32e65595f 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -571,22 +571,6 @@ def is_skipped(self, file_path: Path) -> bool: os_path = str(file_path) - if self.skip_gitignore: - if file_path.name == ".git": # pragma: no cover - return True - - git_folder = None - - for folder in self.git_ignore: - if folder in file_path.parents: - git_folder = folder - break - else: - git_folder = self._check_folder_gitignore(str(file_path.parent)) - - if git_folder and file_path in self.git_ignore[git_folder]: - return True - normalized_path = os_path.replace("\\", "/") if normalized_path[1:2] == ":": normalized_path = normalized_path[2:] @@ -610,6 +594,22 @@ def is_skipped(self, file_path: Path) -> bool: if not (os.path.isfile(os_path) or os.path.isdir(os_path) or os.path.islink(os_path)): return True + if self.skip_gitignore: + if file_path.name == ".git": # pragma: no cover + return True + + git_folder = None + + for folder in self.git_ignore: + if folder in file_path.parents: + git_folder = folder + break + else: + git_folder = self._check_folder_gitignore(str(file_path.parent)) + + if git_folder and file_path in self.git_ignore[git_folder]: + return True + return False @property From 72efb69add920118512f32f6bff1673b52ff2d32 Mon Sep 17 00:00:00 2001 From: Giuseppe Lumia Date: Sun, 27 Jun 2021 17:32:00 +0200 Subject: [PATCH 523/539] Add support for glob expansion in src_paths Relates to #1704 --- docs/configuration/options.md | 3 ++- isort/settings.py | 10 ++++++---- tests/unit/test_settings.py | 13 ++++++++++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index c1c4afc6c..bbbdc911b 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -794,7 +794,8 @@ Tells isort to honor noqa comments to enforce skipping those comments. ## Src Paths -Add an explicitly defined source path (modules within src paths have their imports automatically categorized as first_party). +Add an explicitly defined source path (modules within src paths have their imports automatically categorized as first_party). Glob expansion (`*` and `**`) is supported for this option. + **Type:** Tuple **Default:** `()` diff --git a/isort/settings.py b/isort/settings.py index 32e65595f..ab458a1d9 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -450,11 +450,13 @@ def __init__( if "src_paths" not in combined_config: combined_config["src_paths"] = (path_root / "src", path_root) else: - src_paths: List[Path] = [] + src_paths: set[Path] = set() for src_path in combined_config.get("src_paths", ()): - full_path = path_root / src_path - if full_path not in src_paths: - src_paths.append(full_path) + full_paths = ( + path_root.glob(src_path) if "*" in str(src_path) else [path_root / src_path] + ) + for path in full_paths: + src_paths.add(path) combined_config["src_paths"] = tuple(src_paths) diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index 72b473d7b..92f60d692 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -90,7 +90,18 @@ def test_is_supported_filetype_fifo(self, tmpdir): def test_src_paths_are_combined_and_deduplicated(self): src_paths = ["src", "tests"] src_full_paths = (Path(os.getcwd()) / f for f in src_paths) - assert Config(src_paths=src_paths * 2).src_paths == tuple(src_full_paths) + assert sorted(Config(src_paths=src_paths * 2).src_paths) == sorted(src_full_paths) + + def test_src_paths_supports_glob_expansion(self, tmp_path): + libs = tmp_path / "libs" + libs.mkdir() + requests = libs / "requests" + requests.mkdir() + beautifulpasta = libs / "beautifulpasta" + beautifulpasta.mkdir() + assert sorted(Config(directory=tmp_path, src_paths=["libs/*"]).src_paths) == sorted( + (beautifulpasta, requests) + ) def test_deprecated_multi_line_output(self): assert Config(multi_line_output=6).multi_line_output == WrapModes.VERTICAL_GRID_GROUPED # type: ignore # noqa From dc43f99551d88fc99a45383ef6746541a8c92b6d Mon Sep 17 00:00:00 2001 From: Timothy Edmund Crosley Date: Tue, 29 Jun 2021 22:38:31 -0700 Subject: [PATCH 524/539] In order to support older versions of Python typing.Set must be used --- isort/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/settings.py b/isort/settings.py index ab458a1d9..a7293d27e 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -450,7 +450,7 @@ def __init__( if "src_paths" not in combined_config: combined_config["src_paths"] = (path_root / "src", path_root) else: - src_paths: set[Path] = set() + src_paths: Set[Path] = set() for src_path in combined_config.get("src_paths", ()): full_paths = ( path_root.glob(src_path) if "*" in str(src_path) else [path_root / src_path] From b4c54750a43d8fee96ea8339921e7043637e1c25 Mon Sep 17 00:00:00 2001 From: ruro Date: Wed, 30 Jun 2021 17:17:10 +0300 Subject: [PATCH 525/539] fix git_ignore with symlinks --- isort/settings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index a7293d27e..238ddf9ae 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -4,7 +4,6 @@ """ import configparser import fnmatch -import glob import os import posixpath import re @@ -549,7 +548,7 @@ def _check_folder_gitignore(self, folder: str) -> Optional[Path]: git_folder = Path(topfolder_result.rstrip()) - files = glob.glob(f"{git_folder}/**/*", recursive=True) + files = [str(p) for p in Path(git_folder).rglob("*")] git_options = ["-C", str(git_folder), "-c", "core.quotePath="] try: ignored = subprocess.check_output( # nosec # skipcq: PYL-W1510 From 3760e420af5c5a3d6ea1837d91ea45712db0dee9 Mon Sep 17 00:00:00 2001 From: ruro Date: Wed, 30 Jun 2021 17:18:59 +0300 Subject: [PATCH 526/539] check both path and resolved path for gitignore --- isort/settings.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/isort/settings.py b/isort/settings.py index 238ddf9ae..0d227a61a 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -546,7 +546,7 @@ def _check_folder_gitignore(self, folder: str) -> Optional[Path]: except subprocess.CalledProcessError: return None - git_folder = Path(topfolder_result.rstrip()) + git_folder = Path(topfolder_result.rstrip()).resolve() files = [str(p) for p in Path(git_folder).rglob("*")] git_options = ["-C", str(git_folder), "-c", "core.quotePath="] @@ -601,14 +601,15 @@ def is_skipped(self, file_path: Path) -> bool: git_folder = None + file_paths = [file_path, file_path.resolve()] for folder in self.git_ignore: - if folder in file_path.parents: + if any(folder in path.parents for path in file_paths): git_folder = folder break else: git_folder = self._check_folder_gitignore(str(file_path.parent)) - if git_folder and file_path in self.git_ignore[git_folder]: + if git_folder and any(path in self.git_ignore[git_folder] for path in file_paths): return True return False From 2bbf393122bcf69ac7ad923340a2e2b628aa7ace Mon Sep 17 00:00:00 2001 From: Jeremy Paige Date: Fri, 2 Jul 2021 14:44:10 -0700 Subject: [PATCH 527/539] Symlinked paths are excluded from gitignore checks --- isort/settings.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/isort/settings.py b/isort/settings.py index 0d227a61a..7cec61048 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -548,7 +548,15 @@ def _check_folder_gitignore(self, folder: str) -> Optional[Path]: git_folder = Path(topfolder_result.rstrip()).resolve() - files = [str(p) for p in Path(git_folder).rglob("*")] + files: List[str] = [] + # don't check symlinks; either part of the repo and would be checked + # twice, or is external to the repo and git won't konw anything about it + for root, _dirs, git_files in os.walk(git_folder, followlinks=False): + for git_file in git_files: + git_path = os.path.join(root, git_file) + # followlinks only disables walking into linked dirs + if not os.path.islink(git_path): + files.append(git_path) git_options = ["-C", str(git_folder), "-c", "core.quotePath="] try: ignored = subprocess.check_output( # nosec # skipcq: PYL-W1510 From 25174dd55391135f51e74a83afbc07e29257d598 Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Mon, 5 Jul 2021 17:18:40 +0500 Subject: [PATCH 528/539] missing space in description of force_grid_wrap --- docs/configuration/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/options.md b/docs/configuration/options.md index bbbdc911b..47f034433 100644 --- a/docs/configuration/options.md +++ b/docs/configuration/options.md @@ -622,7 +622,7 @@ Force all imports to be sorted as a single section ## Force Grid Wrap -Force number of from imports (defaults to 2 when passed as CLI flag without value)to be grid wrapped regardless of line length. If 0 is passed in (the global default) only line length is considered. +Force number of from imports (defaults to 2 when passed as CLI flag without value) to be grid wrapped regardless of line length. If 0 is passed in (the global default) only line length is considered. **Type:** Int **Default:** `0` From 2c16b7cb49e36c861a4f5fae0ec2ae12019e9142 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 6 Jul 2021 17:15:01 -0700 Subject: [PATCH 529/539] Fix typo in source command doc --- isort/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/main.py b/isort/main.py index ca0ce5e20..0c3950bc9 100644 --- a/isort/main.py +++ b/isort/main.py @@ -472,7 +472,7 @@ def _build_arg_parser() -> argparse.ArgumentParser: const=2, type=int, dest="force_grid_wrap", - help="Force number of from imports (defaults to 2 when passed as CLI flag without value)" + help="Force number of from imports (defaults to 2 when passed as CLI flag without value) " "to be grid wrapped regardless of line " "length. If 0 is passed in (the global default) only line length is considered.", ) From f44b5a55b0b8f09c0983bca8b425ecdd6aaf74a3 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Tue, 6 Jul 2021 17:20:33 -0700 Subject: [PATCH 530/539] Update changelog in preperation for release soon --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4863d54ce..4ba31082c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,10 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy). -### 5.9.2 +### 5.9.2 TBD - Improved behavior of `isort --check --atomic` against Cython files. + - Fixed #1772: skip-gitignore will check files not in the git repository. + - Fixed #1762: in some cases when skip-gitignore is set, isort fails to skip any files. ### 5.9.1 June 21st 2021 [hotfix] - Fixed #1758: projects with many files and skip_ignore set can lead to a command-line overload. From 999961af756848213a2953e299572ff84f60750d Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Jul 2021 23:53:38 -0700 Subject: [PATCH 531/539] Fix issue #1769: Add imports can be added to incorrect location --- isort/core.py | 10 ++++++---- tests/unit/test_regressions.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/isort/core.py b/isort/core.py index 7b2910da5..4b24e45cd 100644 --- a/isort/core.py +++ b/isort/core.py @@ -55,6 +55,7 @@ def process( next_import_section: str = "" next_cimports: bool = False in_quote: str = "" + was_in_quote: bool = False first_comment_index_start: int = -1 first_comment_index_end: int = -1 contains_imports: bool = False @@ -332,14 +333,15 @@ def process( and (stripped_line or end_of_file) and not config.append_only and not in_top_comment - and not in_quote + and not was_in_quote and not import_section and not line.lstrip().startswith(COMMENT_INDICATORS) - and not line.rstrip().endswith(DOCSTRING_INDICATORS) + and not (line.rstrip().endswith(DOCSTRING_INDICATORS) and "=" not in line) ): - import_section = line_separator.join(add_imports) + line_separator + add_line_separator = line_separator or "\n" + import_section = add_line_separator.join(add_imports) + add_line_separator if end_of_file and index != 0: - output_stream.write(line_separator) + output_stream.write(add_line_separator) contains_imports = True add_imports = [] diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 388aa8d38..449f83d53 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -1798,3 +1798,33 @@ def test_isort_should_keep_multiple_noqa_comments_force_single_line_mode_issue_1 profile="black", force_single_line=True, ) + + +def test_isort_should_only_add_imports_to_valid_location_issue_1769(): + assert ( + isort.code( + '''v = """ +""".split( + "\n" +) +''', + add_imports=["from __future__ import annotations"], + ) + == '''from __future__ import annotations + +v = """ +""".split( + "\n" +) +''' + ) + assert ( + isort.code( + '''v=""""""''', + add_imports=["from __future__ import annotations"], + ) + == '''from __future__ import annotations + +v="""""" +''' + ) From b44400369a7566a45df03224cbf0466ec5f9cd4e Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 7 Jul 2021 23:58:25 -0700 Subject: [PATCH 532/539] Add change to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4863d54ce..bf91b4d73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ ### 5.9.2 - Improved behavior of `isort --check --atomic` against Cython files. + - Fixed #1769: Future imports added below assignments when no other imports present. ### 5.9.1 June 21st 2021 [hotfix] - Fixed #1758: projects with many files and skip_ignore set can lead to a command-line overload. From 5fb77966c7c6670b0e5c29452b0a7aee87946ef4 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 8 Jul 2021 00:56:46 -0700 Subject: [PATCH 533/539] Fix issue #1767: codec error in __init__ files without proper encoding set --- isort/place.py | 10 +++++----- .../namespaces/weird_encoding/.isort.cfg | 2 ++ .../namespaces/weird_encoding/root/__init__.py | 1 + .../namespaces/weird_encoding/root/nested/__init__.py | 0 tests/unit/test_place.py | 3 ++- 5 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 tests/unit/example_projects/namespaces/weird_encoding/.isort.cfg create mode 100644 tests/unit/example_projects/namespaces/weird_encoding/root/__init__.py create mode 100644 tests/unit/example_projects/namespaces/weird_encoding/root/nested/__init__.py diff --git a/isort/place.py b/isort/place.py index aaf954aa8..47a68c78a 100644 --- a/isort/place.py +++ b/isort/place.py @@ -125,14 +125,14 @@ def _is_namespace_package(path: Path, src_extensions: FrozenSet[str]) -> bool: if filenames: return False else: - with init_file.open() as open_init_file: + with init_file.open("rb") as open_init_file: file_start = open_init_file.read(4096) if ( - "__import__('pkg_resources').declare_namespace(__name__)" not in file_start - and '__import__("pkg_resources").declare_namespace(__name__)' not in file_start - and "__path__ = __import__('pkgutil').extend_path(__path__, __name__)" + b"__import__('pkg_resources').declare_namespace(__name__)" not in file_start + and b'__import__("pkg_resources").declare_namespace(__name__)' not in file_start + and b"__path__ = __import__('pkgutil').extend_path(__path__, __name__)" not in file_start - and '__path__ = __import__("pkgutil").extend_path(__path__, __name__)' + and b'__path__ = __import__("pkgutil").extend_path(__path__, __name__)' not in file_start ): return False diff --git a/tests/unit/example_projects/namespaces/weird_encoding/.isort.cfg b/tests/unit/example_projects/namespaces/weird_encoding/.isort.cfg new file mode 100644 index 000000000..d3ae4c35a --- /dev/null +++ b/tests/unit/example_projects/namespaces/weird_encoding/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +src_paths=root diff --git a/tests/unit/example_projects/namespaces/weird_encoding/root/__init__.py b/tests/unit/example_projects/namespaces/weird_encoding/root/__init__.py new file mode 100644 index 000000000..1b8a961c7 --- /dev/null +++ b/tests/unit/example_projects/namespaces/weird_encoding/root/__init__.py @@ -0,0 +1 @@ +description = "基于FastAPI + Mysql的 TodoList" # Exception: UnicodeDecodeError diff --git a/tests/unit/example_projects/namespaces/weird_encoding/root/nested/__init__.py b/tests/unit/example_projects/namespaces/weird_encoding/root/nested/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/test_place.py b/tests/unit/test_place.py index 18ac7fccd..11abdd66e 100644 --- a/tests/unit/test_place.py +++ b/tests/unit/test_place.py @@ -47,7 +47,8 @@ def test_namespace_package_placement(examples_path): no_namespace = namespace_examples / "none" almost_implicit = namespace_examples / "almost-implicit" - for lacks_namespace in (no_namespace, almost_implicit): + weird_encoding = namespace_examples / "weird_encoding" + for lacks_namespace in (no_namespace, almost_implicit, weird_encoding): config = Config(settings_path=lacks_namespace) manual_namespace = Config(settings_path=lacks_namespace, namespace_packages=["root"]) assert place.module("root.name", config=config) == "FIRSTPARTY" From 83a9f687f8f7e229845ab0312458eaa56dc40159 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 8 Jul 2021 01:06:13 -0700 Subject: [PATCH 534/539] Closes #1767: Encoding issues surfacing when invalid characters set in files during placement. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc51d2278..a22e15b1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1769: Future imports added below assignments when no other imports present. - Fixed #1772: skip-gitignore will check files not in the git repository. - Fixed #1762: in some cases when skip-gitignore is set, isort fails to skip any files. + - Fixed #1767: Encoding issues surfacing when invalid characters set in `__init__.py` files during placement. ### 5.9.1 June 21st 2021 [hotfix] - Fixed #1758: projects with many files and skip_ignore set can lead to a command-line overload. From 0e808e0e6a3cc5f6343ba545c28a5f2d64168d3c Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 8 Jul 2021 01:09:51 -0700 Subject: [PATCH 535/539] Add pragma: no cover comment to git link branch --- isort/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/settings.py b/isort/settings.py index 7cec61048..632ec10e9 100644 --- a/isort/settings.py +++ b/isort/settings.py @@ -555,7 +555,7 @@ def _check_folder_gitignore(self, folder: str) -> Optional[Path]: for git_file in git_files: git_path = os.path.join(root, git_file) # followlinks only disables walking into linked dirs - if not os.path.islink(git_path): + if not os.path.islink(git_path): # pragma: no cover files.append(git_path) git_options = ["-C", str(git_folder), "-c", "core.quotePath="] try: From 034a5deb258dd409260c0c1ba0da7c91290ae438 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 8 Jul 2021 01:27:27 -0700 Subject: [PATCH 536/539] Skip line for now --- isort/wrap_modes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/isort/wrap_modes.py b/isort/wrap_modes.py index 39143e739..6ea280187 100644 --- a/isort/wrap_modes.py +++ b/isort/wrap_modes.py @@ -337,7 +337,7 @@ def hanging_indent_with_parentheses(**interface: Any) -> str: if ( not interface["line_separator"] in interface["statement"] and "#" in interface["statement"] - ): + ): # pragma: no cover # TODO: fix, this is because of test run inconsistency. line, comments = interface["statement"].split("#", 1) next_statement = ( f"{line.rstrip()}, {next_import}{interface['comment_prefix']}{comments}" From 332cfd5959d7f641314bbded415b1dc514f41235 Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 8 Jul 2021 01:33:52 -0700 Subject: [PATCH 537/539] Fixed #1771: Improved handling of skips against named streamed in content. --- CHANGELOG.md | 1 + isort/main.py | 24 ++++++++++++++---------- tests/unit/test_main.py | 17 +++++++++++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a22e15b1c..b0f0c336a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/ - Fixed #1772: skip-gitignore will check files not in the git repository. - Fixed #1762: in some cases when skip-gitignore is set, isort fails to skip any files. - Fixed #1767: Encoding issues surfacing when invalid characters set in `__init__.py` files during placement. + - Fixed #1771: Improved handling of skips against named streamed in content. ### 5.9.1 June 21st 2021 [hotfix] - Fixed #1758: projects with many files and skip_ignore set can lead to a command-line overload. diff --git a/isort/main.py b/isort/main.py index 0c3950bc9..34ce3fdd0 100644 --- a/isort/main.py +++ b/isort/main.py @@ -1087,9 +1087,10 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = if show_files: sys.exit("Error: can't show files for streaming input.") + input_stream = sys.stdin if stdin is None else stdin if check: incorrectly_sorted = not api.check_stream( - input_stream=sys.stdin if stdin is None else stdin, + input_stream=input_stream, config=config, show_diff=show_diff, file_path=file_path, @@ -1098,15 +1099,18 @@ def main(argv: Optional[Sequence[str]] = None, stdin: Optional[TextIOWrapper] = wrong_sorted_files = incorrectly_sorted else: - api.sort_stream( - input_stream=sys.stdin if stdin is None else stdin, - output_stream=sys.stdout, - config=config, - show_diff=show_diff, - file_path=file_path, - extension=ext_format, - raise_on_skip=False, - ) + try: + api.sort_stream( + input_stream=input_stream, + output_stream=sys.stdout, + config=config, + show_diff=show_diff, + file_path=file_path, + extension=ext_format, + raise_on_skip=False, + ) + except FileSkipped: + sys.stdout.write(input_stream.read()) elif "/" in file_names and not allow_root: printer = create_terminal_printer( color=config.color_output, error=config.format_error, success=config.format_success diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 9039787cc..78d1e9374 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -370,6 +370,23 @@ def build_input_content(): import b +def function(): + pass +""" + ) + + # if file is skipped it should output unchanged. + main.main( + ["-", "--filename", "x.py", "--skip", "x.py", "--filter-files"], + stdin=build_input_content(), + ) + out, error = capsys.readouterr() + assert not error + assert out == ( + """ +import b +import a + def function(): pass """ From 67494af69deee9d96dcce1c29cd7d2b4bcbf70cf Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 8 Jul 2021 01:35:14 -0700 Subject: [PATCH 538/539] Bump version to 5.9.2 --- CHANGELOG.md | 2 +- isort/_version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0f0c336a..e5d2e8792 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Changelog NOTE: isort follows the [semver](https://semver.org/) versioning standard. Find out more about isort's release policy [here](https://pycqa.github.io/isort/docs/major_releases/release_policy). -### 5.9.2 TBD +### 5.9.2 July 8th 2021 - Improved behavior of `isort --check --atomic` against Cython files. - Fixed #1769: Future imports added below assignments when no other imports present. - Fixed #1772: skip-gitignore will check files not in the git repository. diff --git a/isort/_version.py b/isort/_version.py index b98cc2069..68035e358 100644 --- a/isort/_version.py +++ b/isort/_version.py @@ -1 +1 @@ -__version__ = "5.9.1" +__version__ = "5.9.2" diff --git a/pyproject.toml b/pyproject.toml index 3bebb4d1f..9301f1a2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ line-length = 100 [tool.poetry] name = "isort" -version = "5.9.1" +version = "5.9.2" description = "A Python utility / library to sort Python imports." authors = ["Timothy Crosley "] license = "MIT" From 6e4281f018ff848226d8993596765b2285e1624f Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Thu, 8 Jul 2021 01:36:49 -0700 Subject: [PATCH 539/539] Add missing acknowledgements --- docs/contributing/4.-acknowledgements.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/contributing/4.-acknowledgements.md b/docs/contributing/4.-acknowledgements.md index 32ed30efc..09940aee7 100644 --- a/docs/contributing/4.-acknowledgements.md +++ b/docs/contributing/4.-acknowledgements.md @@ -256,6 +256,9 @@ Documenters - David Poznik (@dpoznik) - Mike Frysinger (@vapier) - @DanielFEvans +- Giuseppe Lumia (@glumia) +- John Brock (@JohnHBrock) +- Sergey Fedoseev (@sir-sigurd) --------------------------------------------