From 21fb6e13d7b4238455428275e45fccc59e0433eb Mon Sep 17 00:00:00 2001 From: Timothy Crosley Date: Wed, 16 Jun 2021 23:26:41 -0700 Subject: [PATCH 1/4] 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 2/4] 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 3/4] 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 4/4] 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.