Skip to content

Commit

Permalink
Support both isort 4 and isort 5
Browse files Browse the repository at this point in the history
The API of isort 5 (released on 2020-07-04) is completely different.
We must still support isort 4 because isort 5 dropped the
compatibility with Python 3.5, which pylint still supports.

Note about the `known-standard-library` option: it has been included
in pylint for years. Until now, it was mapped with the option of the
same name in isort. However, isort 5 has changed the meaning of this
option (see https://timothycrosley.github.io/isort/docs/upgrade_guides/5.0.0/#known_standard_library).

Most users of pylint want the meaning of the new
`extra-standard-library` option. To avoid a breaking change in pylint,
the `known-standard-library` pylint option is now mapped to
`known-standard-library` in isort 4, and `extra-standard-library` in
isort 5. Users that really want the _new_ meaning of
`known-standard-library` in isort 4 must disable the
`wrong-import-order` check in pylint and run isort manually, outside
of pylint.

Fix #3722.
  • Loading branch information
dbaty authored and Pierre-Sassoulas committed Aug 18, 2020
1 parent 707fc46 commit 9bc9bdf
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 14 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Expand Up @@ -47,6 +47,10 @@ Release date: TBA

* Add `raise-missing-from` check for exceptions that should have a cause.

* Support both isort 4 and isort 5. If you have pinned isort 4 in your projet requirements, nothing changes. If you use isort 5, though, note that the `known-standard-library` option is not interpreted the same in isort 4 and isort 5 (see the migration guide in isort documentation for further details). For compatibility's sake for most pylint users, the `known-standard-library` option in pylint now maps to `extra-standard-library` in isort 5. If you really want what `known-standard-library` now means in isort 5, you must disable the `wrong-import-order` check in pylint and run isort manually with a proper isort configuration file.

Close #3722


What's New in Pylint 2.5.4?
===========================
Expand Down
4 changes: 4 additions & 0 deletions doc/whatsnew/2.6.rst
Expand Up @@ -27,3 +27,7 @@ Other Changes
* `mixed-indentation` has been removed, it is no longer useful since TabError is included directly in python3

* Fix superfluous-parens false-positive for the walrus operator

* Add support for both isort 4 and isort 5. If you have pinned isort 4 in your projet requirements, nothing changes. If you use isort 5, though, note that the `known-standard-library` option is not interpreted the same in isort 4 and isort 5 (see `the migration guide in isort documentation`_ for further details). For compatibility's sake for most pylint users, the `known-standard-library` option in pylint now maps to `extra-standard-library` in isort 5. If you really want what `known-standard-library` now means in isort 5, you must disable the `wrong-import-order` check in pylint and run isort manually with a proper isort configuration file.

.. _the migration guide in isort documentation: https://timothycrosley.github.io/isort/docs/upgrade_guides/5.0.0/#known_standard_library
2 changes: 1 addition & 1 deletion man/pylint.1
Expand Up @@ -209,7 +209,7 @@ Create a graph of external dependencies in the given file (report RP0402 must no
.IP "--int-import-graph=<file.dot>"
Create a graph of internal dependencies in the given file (report RP0402 must not be disabled). [default: none]
.IP "--known-standard-library=<modules>"
Force import order to recognize a module as part of the standard compatibility libraries. [default: none]
Force import order to recognize a module as part of the standard compatibility libraries.
.IP "--known-third-party=<modules>"
Force import order to recognize a module as part of a third party library. [default: enchant]
.IP "--allow-any-import-level=<modules>"
Expand Down
2 changes: 1 addition & 1 deletion pylint/__pkginfo__.py
Expand Up @@ -38,7 +38,7 @@

install_requires = [
"astroid>=2.4.0,<=2.5",
"isort>=4.2.5,<5",
"isort>=4.2.5,<6",
"mccabe>=0.6,<0.7",
"toml>=0.7.1",
]
Expand Down
11 changes: 3 additions & 8 deletions pylint/checkers/imports.py
Expand Up @@ -46,7 +46,6 @@
from distutils import sysconfig

import astroid
import isort
from astroid import modutils
from astroid.decorators import cached

Expand All @@ -60,7 +59,7 @@
from pylint.graph import DotBackend, get_cycles
from pylint.interfaces import IAstroidChecker
from pylint.reporters.ureports.nodes import Paragraph, VerbatimText
from pylint.utils import get_global_option
from pylint.utils import IsortDriver, get_global_option


def _qualified_names(modname):
Expand Down Expand Up @@ -709,11 +708,7 @@ def _check_imports_order(self, _module_node):
third_party_not_ignored = []
first_party_not_ignored = []
local_not_ignored = []
isort_obj = isort.SortImports(
file_contents="",
known_third_party=self.config.known_third_party,
known_standard_library=self.config.known_standard_library,
)
isort_driver = IsortDriver(self.config)
for node, modname in self._imports_stack:
if modname.startswith("."):
package = "." + modname.split(".")[1]
Expand All @@ -723,7 +718,7 @@ def _check_imports_order(self, _module_node):
ignore_for_import_order = not self.linter.is_message_enabled(
"wrong-import-order", node.fromlineno
)
import_category = isort_obj.place_module(package)
import_category = isort_driver.place_module(package)
node_and_package_import = (node, package)
if import_category in ("FUTURE", "STDLIB"):
std_imports.append(node_and_package_import)
Expand Down
2 changes: 2 additions & 0 deletions pylint/utils/__init__.py
Expand Up @@ -46,6 +46,8 @@
from pylint.utils.ast_walker import ASTWalker
from pylint.utils.file_state import FileState
from pylint.utils.utils import (
HAS_ISORT_5,
IsortDriver,
_basename_in_blacklist_re,
_check_csv,
_format_option_value,
Expand Down
35 changes: 35 additions & 0 deletions pylint/utils/utils.py
@@ -1,6 +1,15 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING

try:
import isort.api

HAS_ISORT_5 = True
except ImportError: # isort < 5
import isort

HAS_ISORT_5 = False

import codecs
import os
import re
Expand Down Expand Up @@ -398,3 +407,29 @@ def _ini_format(stream, options):
# remove trailing ',' from last element of the list
value = value[:-1]
print("%s=%s" % (optname, value), file=stream)


class IsortDriver:
"""A wrapper around isort API that changed between versions 4 and 5."""

def __init__(self, config):
if HAS_ISORT_5:
self.isort5_config = isort.api.Config(
# There is not typo here. EXTRA_standard_library is
# what most users want. The option has been named
# KNOWN_standard_library for ages in pylint and we
# don't want to break compatibility.
extra_standard_library=config.known_standard_library,
known_third_party=config.known_third_party,
)
else:
self.isort4_obj = isort.SortImports( # pylint: disable=no-member
file_contents="",
known_standard_library=config.known_standard_library,
known_third_party=config.known_third_party,
)

def place_module(self, package):
if HAS_ISORT_5:
return isort.api.place_module(package, self.isort5_config)
return self.isort4_obj.place_module(package)
6 changes: 3 additions & 3 deletions tests/functional/w/wrong_import_order.txt
@@ -1,6 +1,6 @@
wrong-import-order:12::standard import "import os.path" should be placed before "import six"
wrong-import-order:14::standard import "import sys" should be placed before "import six"
wrong-import-order:15::standard import "import datetime" should be placed before "import six"
wrong-import-order:18::first party import "import totally_missing" should be placed before "from .package import Class"
wrong-import-order:20::third party import "import astroid" should be placed before "import unused_import"
wrong-import-order:24::third party import "from six.moves.urllib.parse import quote" should be placed before "import unused_import"
wrong-import-order:18::third party import "import totally_missing" should be placed before "from .package import Class"
wrong-import-order:20::third party import "import astroid" should be placed before "from .package import Class"
wrong-import-order:24::third party import "from six.moves.urllib.parse import quote" should be placed before "from .package import Class"
5 changes: 5 additions & 0 deletions tests/test_functional.py
Expand Up @@ -28,6 +28,7 @@
import pytest

from pylint import testutils
from pylint.utils import HAS_ISORT_5


class test_dialect(csv.excel):
Expand Down Expand Up @@ -77,6 +78,10 @@ def get_tests():
continue
for filename in filenames:
if filename != "__init__.py" and filename.endswith(".py"):
# isort 5 has slightly different rules as isort 4. Testing
# both would be hard: test with isort 5 only.
if filename == "wrong_import_order.py" and not HAS_ISORT_5:
continue
suite.append(testutils.FunctionalTestFile(dirpath, filename))
return suite

Expand Down
4 changes: 3 additions & 1 deletion tox.ini
Expand Up @@ -37,7 +37,7 @@ commands =
basepython = python3
deps =
black==19.10b0
isort==4.3.21
isort==5.4.2
commands =
black --check . --exclude="tests/functional/|tests/input|tests/extensions/data|tests/regrtest_data/|tests/data/|venv|astroid|.tox"
isort -rc . --check-only
Expand All @@ -58,6 +58,8 @@ deps =
coverage<5.0
isort
mccabe
# isort 5 is not compatible with Python 3.5
py35: isort>=4.2.5,<5
pytest
pytest-xdist
pytest-benchmark
Expand Down

0 comments on commit 9bc9bdf

Please sign in to comment.