From fd0f47fb06b6b8c4edb635d2353bbc8a34b6aba6 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Mon, 13 Jul 2020 02:26:50 +0200 Subject: [PATCH 1/7] move tests to pytest --- run_tests.py | 512 ++++++++++++++++++++++----------------------------- 1 file changed, 216 insertions(+), 296 deletions(-) diff --git a/run_tests.py b/run_tests.py index 2ba6981..06650bd 100644 --- a/run_tests.py +++ b/run_tests.py @@ -1,302 +1,222 @@ -# -*- coding: utf-8 -*- -from flake8_isort import Flake8Isort -from tempfile import mkdtemp -from testfixtures import OutputCapture +"""unit tests for flake8-isort + +the test should pass with both isort 4 and isort 5 +""" import collections import os -import unittest - - -class TestFlake8Isort(unittest.TestCase): - - def setUp(self): - self.test_dir = os.path.realpath(mkdtemp()) - - def write_python_file(self, content): - file_path = os.path.join(self.test_dir, 'test.py') - with open(file_path, 'w') as python_file: - python_file.write(content) - return (file_path, content) - - def write_isort_cfg(self, content): - content = '[settings]\n' + content - self.write_config_file('.isort.cfg', content) - - def write_setup_cfg(self, content): - content = '[isort]\n' + content - self.write_config_file('setup.cfg', content) - - def write_tox_ini(self, content): - content = '[isort]\n' + content - self.write_config_file('tox.ini', content) - - def write_pyproject_toml(self, content): - content = '[tool.isort]\n' + content - self.write_config_file('pyproject.toml', content) - - def write_config_file(self, name, content): - file_path = os.path.join(self.test_dir, name) - with open(file_path, 'w') as config_file: - config_file.write(content) - - def test_sorted_correctly_alpha(self): - (file_path, lines) = self.write_python_file( - 'from sys import path\n' - '\n' - 'import os\n', - ) - self.write_isort_cfg( - 'force_single_line=True\nforce_alphabetical_sort=True' - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(ret, []) - - def test_sorted_correctly_default(self): - (file_path, lines) = self.write_python_file( - 'import os\n' - 'from sys import path\n', - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(ret, []) - - def test_with_eof_blank_lines(self): - """Pass with eof blank line as flake8 will flag them""" - (file_path, lines) = self.write_python_file( - 'import os\n' - 'from sys import path\n' - '\n' - '\n' - ' \n', - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(ret, []) - - def test_imports_requires_blank_line(self): - (file_path, lines) = self.write_python_file( - 'from __future__ import division\n' - 'import threading\n' - 'from sys import pid\n', - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(len(ret), 1) - self.assertEqual(ret[0][0], 2) - self.assertEqual(ret[0][1], 0) - self.assertTrue(ret[0][2].startswith('I003 ')) - - def test_isortcfg_skip_file(self): - (file_path, lines) = self.write_python_file( - 'skipped_file', - ) - self.write_isort_cfg('skip=test.py') - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(ret, []) - - def test_file_skipped_with_comment(self): - # Note: files skipped in this way are not marked as - # "skipped" by isort <= 4.2.15, so we handle them in a - # different code path and test to ensure they also work. - (file_path, lines) = self.write_python_file( - '# isort:skip_file', - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(ret, []) - - def test_imports_unexpected_blank_line(self): - (file_path, lines) = self.write_python_file( - 'from __future__ import division\n' - '\n' - 'import threading\n' - '\n' - 'from sys import pid\n', - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(len(ret), 1) - self.assertEqual(ret[0][0], 4) - self.assertEqual(ret[0][1], 0) - self.assertTrue(ret[0][2].startswith('I004 ')) - - def test_sorted_incorrectly_multiple(self): - (file_path, lines) = self.write_python_file( - 'from __future__ import division\n' - 'import os\n' - 'from sys import pid\n' - 'import threading\n' - '\n' - 'import isort\n' - '\n\n\n' - 'def func()\n', - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(len(ret), 3) - self.assertEqual(ret[0][0], 2) - self.assertEqual(ret[0][1], 0) - self.assertTrue(ret[0][2].startswith('I003 ')) - self.assertEqual(ret[1][0], 4) - self.assertEqual(ret[1][1], 0) - self.assertTrue(ret[1][2].startswith('I001 ')) - self.assertEqual(ret[2][0], 9) - self.assertEqual(ret[2][1], 0) - self.assertTrue(ret[2][2].startswith('I004 ')) - - def test_sorted_incorrectly(self): - (file_path, lines) = self.write_python_file( - 'from sys import pid\n' - 'import threading', - ) - self.write_isort_cfg('force_single_line=True') - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(len(ret), 1) - self.assertEqual(ret[0][0], 2) - self.assertEqual(ret[0][1], 0) - self.assertTrue(ret[0][2].startswith('I001 ')) - - def test_empty_file(self): - (file_path, lines) = self.write_python_file( - '\n\n', - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(ret, []) - - def test_wrapped_imports(self): - (file_path, lines) = self.write_python_file( - 'from deluge.common import (fdate, fpcnt, fpeer, fsize, fspeed,\n' - ' ftime, get_path_size, is_infohash,\n' - ' is_ip, is_magnet, is_url)\n', - ) - self.write_isort_cfg('wrap_length=65') - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(ret, []) - - def test_force_single_line_imports(self): - (file_path, lines) = self.write_python_file( - 'from plone.app.testing import applyProfile\n' - 'from plone.app.testing import FunctionalTesting\n', - ) - self.write_isort_cfg( - 'force_alphabetical_sort=True\nforce_single_line=True' - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(ret, []) - - def test_missing_add_imports(self): - (file_path, lines) = self.write_python_file('import os') - self.write_isort_cfg( - 'add_imports=from __future__ import unicode_literals' - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(len(ret), 2) - self.assertEqual(ret[0][0], 1) - self.assertEqual(ret[0][1], 0) - self.assertTrue(ret[0][2].startswith('I005 ')) - self.assertEqual(ret[1][0], 1) - self.assertEqual(ret[1][1], 0) - self.assertTrue(ret[1][2].startswith('I003 ')) - - def test_isortcfg_found(self): - (file_path, lines) = self.write_python_file( - 'from sys import pid\n' - 'import threading', - ) - self.write_isort_cfg('force_single_line=True') - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - checker.config_file = True - ret = list(checker.run()) - self.assertEqual(len(ret), 1) - self.assertEqual(ret[0][0], 2) - self.assertEqual(ret[0][1], 0) - self.assertTrue(ret[0][2].startswith('I001 ')) - - def test_isortcfg_not_found(self): - (file_path, lines) = self.write_python_file( - 'from sys import pid, path' - ) - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - checker.search_current = False - checker.config_file = True - ret = list(checker.run()) - self.assertEqual(len(ret), 1) - self.assertEqual(ret[0][0], 1) - self.assertEqual(ret[0][1], 0) - self.assertTrue(ret[0][2].startswith('I001 ')) - - def test_isort_formatted_output(self): - options = collections.namedtuple( - 'Options', [ - 'no_isort_config', - 'isort_show_traceback', - 'stdin_display_name' - ] - ) - - (file_path, lines) = self.write_python_file( - 'from __future__ import division\n' - 'import os\n' - 'from sys import pid\n', - ) - - diff = ' from __future__ import division\n+\n import os' - - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - checker.parse_options(options(None, True, 'stdin')) - ret = list(checker.run()) - self.assertEqual(len(ret), 1) - self.assertEqual(ret[0][0], 2) - self.assertEqual(ret[0][1], 0) - self.assertIn(diff, ret[0][2]) +import pytest - def test_if_isort_cfg_is_used(self): - self.check_if_config_file_is_used(self.write_isort_cfg) - - def test_if_setup_cfg_is_used(self): - self.check_if_config_file_is_used(self.write_setup_cfg) - - def test_if_tox_ini_is_used(self): - self.check_if_config_file_is_used(self.write_tox_ini) - - def test_if_pyproject_toml_is_used(self): - self.check_if_config_file_is_used(self.write_pyproject_toml) +from flake8_isort import Flake8Isort - def check_if_config_file_is_used(self, method_to_write_config): - (file_path, lines) = self.write_python_file( - 'import os\n' - 'from sys import path\n', - ) - method_to_write_config('lines_between_types=1') - with OutputCapture(): - checker = Flake8Isort(None, file_path, lines) - ret = list(checker.run()) - self.assertEqual(len(ret), 1) - self.assertEqual(ret[0][0], 2) - self.assertEqual(ret[0][1], 0) - self.assertTrue(ret[0][2].startswith('I003 ')) +def write_python_file(tmpdir, content): + file_path = os.path.join(str(tmpdir), 'test.py') + with open(file_path, 'w') as python_file: + python_file.write(content) + return (file_path, content) + + +def write_isort_cfg(tmpdir, content): + content = '[settings]\n' + content + write_config_file(tmpdir, '.isort.cfg', content) + + +def write_setup_cfg(tmpdir, content): + content = '[isort]\n' + content + write_config_file(tmpdir, 'setup.cfg', content) + + +def write_tox_ini(tmpdir, content): + content = '[isort]\n' + content + write_config_file(tmpdir, 'tox.ini', content) + + +def write_pyproject_toml(tmpdir, content): + content = '[tool.isort]\n' + content + write_config_file(tmpdir, 'pyproject.toml', content) + + +def write_config_file(tmpdir, filename, content): + file_path = os.path.join(str(tmpdir), filename) + with open(file_path, 'w') as config_file: + config_file.write(content) + + +def check_isort_ret(ret, ref): + """Sort the return by (line, errortype) and compare it to the reference""" + assert len(ret) == len(ref) + for ret_i, ref_i in zip(sorted(ret, key=lambda x: (x[0], x[2])), ref): + assert ret_i[:2] == ref_i[:2] + assert ret_i[2].startswith(ref_i[2]) + + +testcases = [ + {'name': 'sorted_correctly_default', + 'code': ('import os\n' + 'from sys import path\n'), + 'ref': []}, + {'name': 'sorted_correctly_alpha', + 'config': 'force_single_line=True\n' + 'force_alphabetical_sort=True\n', + 'code': 'from sys import path\n' + '\n' + 'import os\n', + 'ref': []}, + {'name': 'eof_blank_lines', + 'code': 'import os\n' + 'from sys import path\n' + '\n' + '\n' + ' \n', + 'ref': []}, + {'name': 'imports_requires_blank_line', + 'code': 'from __future__ import division\n' + 'import threading\n' + 'from sys import pid\n', + 'ref': [(2, 0, 'I003 ')]}, + {'name': 'isortcfg_skip_file', + 'config': 'skip=test.py', + 'code': 'skipped_file', + 'ref': []}, + {'name': 'file_skipped_with_comment', + 'code': '# isort:skip_file', + 'ref': []}, + {'name': 'imports_unexpected_blank_line', + 'code': 'from __future__ import division\n' + '\n' + 'import threading\n' + '\n' + 'from sys import pid\n', + 'ref': [(4, 0, 'I004 ')]}, + {'name': 'sorted_incorrectly_multiple', + 'code': 'from __future__ import division\n' + 'import os\n' + 'from sys import pid\n' + 'import threading\n' + '\n' + 'import isort\n' + '\n\n\n' + 'def func()\n', + 'ref': [(2, 0, 'I003 '), + (4, 0, 'I001 '), + (9, 0, 'I004 ')]}, + {'name': 'sorted_incorrectly', + 'config': 'force_single_line=True', + 'code': 'from sys import pid\n' + 'import threading', + 'ref': [(2, 0, 'I001 ')]}, + {'name': 'empty_file', + 'code': '\n\n', + 'ref': []}, + {'name': 'wrapped_imports', + 'config': 'wrap_length=65', + 'code': 'from deluge.common import (fdate, fpcnt, fpeer, fsize, fspeed,\n' + ' ftime, get_path_size, is_infohash,\n' + ' is_ip, is_magnet, is_url)\n', + 'ref': []}, + {'name': 'force_single_line_imports', + 'config': 'force_alphabetical_sort=True\n' + 'force_single_line=True', + 'code': 'from plone.app.testing import applyProfile\n' + 'from plone.app.testing import FunctionalTesting\n', + 'ref': []}, + {'name': 'missing_add_imports', + 'config': 'add_imports=from __future__ import unicode_literals', + 'code': 'import os\n', + 'ref': [(1, 0, 'I003'), + (1, 0, 'I005')]}, +] +""" + {'name': '', + 'config': + 'code': + 'ref': []}, +""" + + +@pytest.mark.parametrize('testcase', testcases, + ids=[t['name'] for t in testcases]) +@pytest.mark.parametrize('mode', ["file", "code_string"]) +def test_flake8_isort(tmpdir, testcase, mode): + """Test the code examples in files and directly from string""" + with tmpdir.as_cwd(): + if 'config' in testcase: + write_isort_cfg(tmpdir, testcase['config']) + if mode == "file": + (file_path, lines) = write_python_file(tmpdir, testcase['code']) + checker = Flake8Isort(None, file_path, lines) + elif mode == "code_string": + checker = Flake8Isort(None, None, testcase['code']) + else: + raise RuntimeError("invalid mode") + ret = list(checker.run()) + check_isort_ret(ret, testcase['ref']) + + +def test_isortcfg_found(tmpdir): + (file_path, lines) = write_python_file( + tmpdir, + 'from sys import pid\n' + 'import threading', + ) + write_isort_cfg(tmpdir, 'force_single_line=True') + checker = Flake8Isort(None, file_path, lines) + checker.config_file = True + ret = list(checker.run()) + check_isort_ret(ret, [(2, 0, 'I001 ')]) + + +def test_isortcfg_not_found(tmpdir): + (file_path, lines) = write_python_file( + tmpdir, + 'from sys import pid, path' + ) + checker = Flake8Isort(None, file_path, lines) + checker.search_current = False + checker.config_file = True + ret = list(checker.run()) + check_isort_ret(ret, [(1, 0, 'I001 ')]) + + +def test_isort_formatted_output(tmpdir): + options = collections.namedtuple( + 'Options', [ + 'no_isort_config', + 'isort_show_traceback', + 'stdin_display_name' + ] + ) + + (file_path, lines) = write_python_file( + tmpdir, + 'from __future__ import division\n' + 'import os\n' + 'from sys import pid\n', + ) + + diff = ' from __future__ import division\n+\n import os' + + checker = Flake8Isort(None, file_path, lines) + checker.parse_options(options(None, True, 'stdin')) + ret = list(checker.run()) + assert len(ret) == 1 + assert ret[0][0] == 2 + assert ret[0][1] == 0 + assert diff in ret[0][2] + + +@pytest.mark.parametrize( + 'method_to_write_config', + [write_isort_cfg, write_setup_cfg, write_tox_ini, write_pyproject_toml]) +def test_if_config_file_is_used(tmpdir, method_to_write_config): + (file_path, lines) = write_python_file( + tmpdir, + 'import os\n' + 'from sys import path\n', + ) + method_to_write_config(tmpdir, 'lines_between_types=1') + + checker = Flake8Isort(None, file_path, lines) + ret = list(checker.run()) + check_isort_ret(ret, [(2, 0, 'I003 ')]) From 7d88315cf7e7c0d656cc4d3685031b42acdaafbc Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Mon, 13 Jul 2020 03:05:56 +0200 Subject: [PATCH 2/7] support isort 5 --- CHANGES.rst | 4 +- flake8_isort.py | 105 +++++++++++++++++++++++++++++++++++++++++++++--- setup.py | 2 +- 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2d801c4..40ecbd5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,10 +3,10 @@ Changelog ========= -3.0.2 (unreleased) +4.0.0 (unreleased) ------------------ -- Nothing changed yet. +- support isort >= 5 [bnavigator, pkolbus] 3.0.1 (2020-07-08) diff --git a/flake8_isort.py b/flake8_isort.py index 44ac799..21624ad 100644 --- a/flake8_isort.py +++ b/flake8_isort.py @@ -1,13 +1,20 @@ # -*- coding: utf-8 -*- -from difflib import Differ -from isort import SortImports from testfixtures import OutputCapture +import isort -__version__ = '3.0.2.dev0' +if hasattr(isort, 'api'): # isort 5 + from pathlib import Path -class Flake8Isort(object): + import warnings +else: + from difflib import Differ + +__version__ = '4.0.0.dev0' + + +class Flake8IsortBase(object): name = 'flake8_isort' version = __version__ isort_unsorted = ( @@ -49,13 +56,17 @@ def parse_options(cls, options): cls.stdin_display_name = options.stdin_display_name cls.show_traceback = options.isort_show_traceback + +class Flake8Isort4(Flake8IsortBase): + """class for isort <5""" + def run(self): if self.filename is not self.stdin_display_name: file_path = self.filename else: file_path = None with OutputCapture() as buffer: - sort_result = SortImports( + sort_result = isort.SortImports( file_path=file_path, file_contents=''.join(self.lines), check=True, @@ -168,3 +179,87 @@ def _fixup_sortimports_wrapped(sort_imports): for new_idx, new_line in enumerate( sort_imports.out_lines.pop(idx).splitlines()): sort_imports.out_lines.insert(idx + new_idx, new_line) + + +class Flake8Isort5(Flake8IsortBase): + """class for isort >=5""" + + def run(self): + if self.filename is not self.stdin_display_name: + file_path = Path(self.filename) + isort_config = isort.settings.Config( + settings_path=file_path.parent) + else: + isort_config = isort.settings.Config( + settings_path=Path.cwd()) + in_string = ''.join(self.lines) + traceback = '' + iscorrect = True + with OutputCapture() as buffer: + try: + if self.filename is not self.stdin_display_name: + iscorrect = isort.api.check_file( + filename=self.filename, + show_diff=True, + config=isort_config) + else: + iscorrect = isort.api.check_code_string( + code=in_string, + show_diff=True, + config=isort_config) + except isort.exceptions.FileSkipped: + pass + except isort.exceptions.ISortError as e: + warnings.warn(e) + isort_output = buffer.captured + if not iscorrect: + traceback += isort_output + for line_num, message in self.isort_linenum_msg(isort_output): + if self.show_traceback: + message += traceback + yield line_num, 0, message, type(self) + + def isort_linenum_msg(self, isort_output): + """Parse isort output for line number changes and message + + Args + ---- + isort_diff : the stdout of the isort check when show_diff=True + + Yields + ------ + tuple: A tuple of the specific isort line number and message. + """ + line_num = 0 + additions = [] + moves = [] + for line in isort_output.splitlines(): + if line.startswith('@@', 0, 2): + line_num = int(line[4:].split(' ')[0].split(',')[0]) + continue + elif not line_num: # skip lines before first hunk + continue + if line.startswith(' ', 0, 1): + line_num += 1 # Ignore unchanged lines but increment line_num. + elif line.startswith('-', 0, 1): + if line.strip() == '-': + yield line_num, self.isort_blank_unexp + line_num += 1 + else: + moves.append(line[1:]) + yield line_num, self.isort_unsorted + line_num += 1 + elif line.startswith('+', 0, 1): + if line.strip() == '+': + # Include newline additions but do not increment line_num. + yield line_num, self.isort_blank_req + else: + additions.append((line_num, line)) + + # return all additions that did not move + for line_num, line in additions: + if not line[1:] in moves: + yield line_num, self.isort_add_unexp + + +Flake8Isort = Flake8Isort5 if hasattr(isort, 'api') else Flake8Isort4 diff --git a/setup.py b/setup.py index ceb80cb..f02e0b4 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ def get_version(file="flake8_isort.py"): zip_safe=False, install_requires=[ 'flake8 >= 3.2.1, <4', - 'isort[pyproject] >= 4.3.5, <5', + 'isort[pyproject] >= 4.3.5, <6', 'testfixtures >= 6.8.0, <7', ], extras_require={ From e502c246241602dd91451e809d499347c87f97d9 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Mon, 13 Jul 2020 12:59:28 +0200 Subject: [PATCH 3/7] extend travis matrix with isort5 --- .travis.yml | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 76a47df..7f5abed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,27 @@ dist: xenial language: python -matrix: - - python: '2.7' - - python: '3.5' - - python: '3.6' - - python: '3.7' - - python: '3.8' - - python: 'pypy' - - python: 'pypy3' +python: + - '2.7' + - '3.5' + - '3.6' + - '3.7' + - '3.8' + - 'pypy' + - 'pypy3' +env: + - ISORT_VER="4" + - ISORT_VER="5" +jobs: + exclude: + - python: "2.7" + env: ISORT_VER="5" + - python: "3.5" + env: ISORT_VER="5" + - python: "pypy" + env: ISORT_VER="5" cache: pip install: + - if [[ $ISORT_VER == "4" ]]; then pip install 'isort[pyproject] < 5'; fi - pip install .[test] script: - flake8 *.py From 7a517cee93ea10c0207896a06e6ac102b4384aea Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Mon, 13 Jul 2020 15:41:40 +0200 Subject: [PATCH 4/7] revert #76: no isort[pyproject] Isort > 5 does not have the pyproject extra either. Just require toml directly for the tests --- .travis.yml | 2 +- setup.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7f5abed..4411545 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ jobs: env: ISORT_VER="5" cache: pip install: - - if [[ $ISORT_VER == "4" ]]; then pip install 'isort[pyproject] < 5'; fi + - if [[ $ISORT_VER == "4" ]]; then pip install 'isort < 5'; fi - pip install .[test] script: - flake8 *.py diff --git a/setup.py b/setup.py index f02e0b4..a2628be 100644 --- a/setup.py +++ b/setup.py @@ -57,12 +57,13 @@ def get_version(file="flake8_isort.py"): zip_safe=False, install_requires=[ 'flake8 >= 3.2.1, <4', - 'isort[pyproject] >= 4.3.5, <6', + 'isort >= 4.3.5, <6', 'testfixtures >= 6.8.0, <7', ], extras_require={ 'test': [ 'pytest >= 4.0.2, <6', + 'toml', ], }, entry_points={ From 81b12582e05993ec0f3e248df3bfe8b0e8df7f55 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Mon, 13 Jul 2020 15:45:55 +0200 Subject: [PATCH 5/7] move run_tests.py to test_flake8_isort.py This let's pytest discover the file on its own. --- .travis.yml | 4 ++-- run_tests.py => test_flake8_isort.py | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename run_tests.py => test_flake8_isort.py (100%) diff --git a/.travis.yml b/.travis.yml index 4411545..3bc57bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,8 @@ install: - pip install .[test] script: - flake8 *.py - - pytest run_tests.py + - pytest -v after_success: - pip install -q -r requirements-cov.txt - - pytest run_tests.py --cov flake8_isort --cov-report term-missing + - pytest -v --cov flake8_isort --cov-report term-missing - coveralls diff --git a/run_tests.py b/test_flake8_isort.py similarity index 100% rename from run_tests.py rename to test_flake8_isort.py From db1ba1f4c0fa604e4e6471814a5afa4024e71396 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Sat, 18 Jul 2020 22:29:49 +0200 Subject: [PATCH 6/7] clean test file, and change parametrize prioritity for better sorting --- test_flake8_isort.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/test_flake8_isort.py b/test_flake8_isort.py index 06650bd..4c66411 100644 --- a/test_flake8_isort.py +++ b/test_flake8_isort.py @@ -127,17 +127,11 @@ def check_isort_ret(ret, ref): 'ref': [(1, 0, 'I003'), (1, 0, 'I005')]}, ] -""" - {'name': '', - 'config': - 'code': - 'ref': []}, -""" +@pytest.mark.parametrize('mode', ["file", "code_string"]) @pytest.mark.parametrize('testcase', testcases, ids=[t['name'] for t in testcases]) -@pytest.mark.parametrize('mode', ["file", "code_string"]) def test_flake8_isort(tmpdir, testcase, mode): """Test the code examples in files and directly from string""" with tmpdir.as_cwd(): From 6b103a0646bb6677cb02810b5ddb4cddac160f4e Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Sun, 19 Jul 2020 01:08:07 +0200 Subject: [PATCH 7/7] support isort >= 5.1.2: generate diff ourselves --- flake8_isort.py | 61 +++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/flake8_isort.py b/flake8_isort.py index 21624ad..52066af 100644 --- a/flake8_isort.py +++ b/flake8_isort.py @@ -1,15 +1,18 @@ # -*- coding: utf-8 -*- -from testfixtures import OutputCapture import isort if hasattr(isort, 'api'): # isort 5 + from contextlib import redirect_stdout + from difflib import unified_diff + from io import StringIO from pathlib import Path import warnings else: from difflib import Differ + from testfixtures import OutputCapture __version__ = '4.0.0.dev0' @@ -190,41 +193,45 @@ def run(self): isort_config = isort.settings.Config( settings_path=file_path.parent) else: + file_path = None isort_config = isort.settings.Config( settings_path=Path.cwd()) - in_string = ''.join(self.lines) + input_string = ''.join(self.lines) traceback = '' - iscorrect = True - with OutputCapture() as buffer: - try: - if self.filename is not self.stdin_display_name: - iscorrect = isort.api.check_file( - filename=self.filename, - show_diff=True, - config=isort_config) - else: - iscorrect = isort.api.check_code_string( - code=in_string, - show_diff=True, - config=isort_config) - except isort.exceptions.FileSkipped: - pass - except isort.exceptions.ISortError as e: - warnings.warn(e) - isort_output = buffer.captured - if not iscorrect: - traceback += isort_output - for line_num, message in self.isort_linenum_msg(isort_output): + isort_changed = False + input_stream = StringIO(input_string) + output_stream = StringIO() + isort_stdout = StringIO() + try: + with redirect_stdout(isort_stdout): + isort_changed = isort.api.sort_stream( + input_stream=input_stream, + output_stream=output_stream, + config=isort_config, + file_path=file_path) + except isort.exceptions.FileSkipped: + pass + except isort.exceptions.ISortError as e: + warnings.warn(e) + if isort_changed: + outlines = output_stream.getvalue() + diff_delta = "".join(unified_diff( + input_string.splitlines(keepends=True), + outlines.splitlines(keepends=True), + fromfile="{}:before".format(self.filename), + tofile="{}:after".format(self.filename))) + traceback = (isort_stdout.getvalue() + "\n" + diff_delta) + for line_num, message in self.isort_linenum_msg(diff_delta): if self.show_traceback: message += traceback yield line_num, 0, message, type(self) - def isort_linenum_msg(self, isort_output): - """Parse isort output for line number changes and message + def isort_linenum_msg(self, udiff): + """Parse unified diff for changes and generate messages Args ---- - isort_diff : the stdout of the isort check when show_diff=True + udiff : unified diff delta Yields ------ @@ -233,7 +240,7 @@ def isort_linenum_msg(self, isort_output): line_num = 0 additions = [] moves = [] - for line in isort_output.splitlines(): + for line in udiff.splitlines(): if line.startswith('@@', 0, 2): line_num = int(line[4:].split(' ')[0].split(',')[0]) continue