Skip to content

Commit

Permalink
Merge pull request #19 from cas--/Feature/OutputLineNumbers
Browse files Browse the repository at this point in the history
Feature/output line numbers
  • Loading branch information
gforcada committed Nov 24, 2016
2 parents f386ce7 + 05c44fa commit bed7298
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 23 deletions.
78 changes: 72 additions & 6 deletions flake8_isort.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
from isort import SortImports
from difflib import Differ
from flake8_polyfill import stdin
from isort import SortImports
from testfixtures import OutputCapture

import os
import pycodestyle


try:
from configparser import ConfigParser
except ImportError:
Expand All @@ -18,13 +20,18 @@
class Flake8Isort(object):
name = 'flake8_isort'
version = '2.0.3'
isort_error_msg = (
'I001 isort found deviations to configured sorting rules, '
'run it on the file to fix this.'
isort_unsorted = (
'I001 isort found an import in the wrong position'
)
no_config_msg = (
'I002 no configuration found (.isort.cfg or [isort] on setup.cfg)'
)
isort_blank_req = (
'I003 isort expected 1 blank line in imports, found 0'
)
isort_blank_unexp = (
'I004 isort found an unexpected blank line in imports'
)

config_file = None

Expand Down Expand Up @@ -59,8 +66,9 @@ def run(self):
)
else:
sort_result = SortImports(self.filename, check=True)
if sort_result.incorrectly_sorted:
yield 0, 0, self.isort_error_msg, type(self)

for line_num, message in self.sortimports_linenum_msg(sort_result):
yield line_num, 0, message, type(self)

def search_isort_config(self):
"""Search for isort configuration all the way up to the root folder
Expand Down Expand Up @@ -91,3 +99,61 @@ def search_isort_config(self):
return True

return False

def sortimports_linenum_msg(self, sort_result):
"""Parses isort.SortImports for line number changes and message.
Uses a diff.Differ comparison of SortImport `in_lines`:`out_lines` to
yield the line numbers of import lines that have been moved or blank
lines added.
Args:
sort_imports (isort.SortImports): The isorts results object.
Yields:
tuple: A tuple of the specific isort line number and message.
"""

self._fixup_sortimports_eof(sort_result)

differ = Differ()
diff = differ.compare(sort_result.in_lines, sort_result.out_lines)

line_num = 0
for line in diff:
if line.startswith(' ', 0, 2):
line_num += 1 # Ignore unchanged lines but increment line_num.
elif line.startswith('- ', 0, 2):
line_num += 1
if line.strip() == '-':
yield line_num, self.isort_blank_unexp
else:
yield line_num, self.isort_unsorted
elif line.strip() == '+':
# Include newline additions but do not increment line_num.
yield line_num + 1, self.isort_blank_req

@staticmethod
def _fixup_sortimports_eof(sort_imports):
"""Ensure single end-of-file newline in `isort.SortImports.in_lines`.
isort attempts to fix EOF blank lines but Flake8 will also flag them.
So that these EOF changes are ignored in the diff comparison ensure
that SortImports `in_lines` list has just the single EOF newline to
match `out_lines` list.
Args:
sort_imports (isort.SortImports): The isorts results object.
Returns:
isort.SortImports: The modified isort results object.
"""

for line in reversed(sort_imports.in_lines):
if not line.strip():
sort_imports.in_lines.pop()
else:
sort_imports.in_lines.append('')
return sort_imports
109 changes: 92 additions & 17 deletions run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,102 @@ def _given_a_file_in_test_dir(self, contents, isort_config):

return file_path

def test_sorted_correctly(self):
def test_sorted_correctly_alpha(self):
file_path = self._given_a_file_in_test_dir(
'from sys import path\n'
'\n'
'import os'
'\n',
'import os\n',
isort_config='force_single_line=True\nforce_alphabetical_sort=True'
)
with OutputCapture():
checker = Flake8Isort(None, file_path)
ret = list(checker.run())
self.assertEqual(ret, [])

def test_sorted_correctly_default(self):
file_path = self._given_a_file_in_test_dir(
'import os\n'
'from sys import path\n',
isort_config=''
)
with OutputCapture():
checker = Flake8Isort(None, file_path)
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 = self._given_a_file_in_test_dir(
'import os\n'
'from sys import path\n'
'\n'
'\n'
' \n',
isort_config=''
)
with OutputCapture():
checker = Flake8Isort(None, file_path)
ret = list(checker.run())
self.assertEqual(ret, [])

def test_imports_requires_blank_line(self):
file_path = self._given_a_file_in_test_dir(
'from __future__ import division\n'
'import threading\n'
'from sys import pid\n',
isort_config=''
)
with OutputCapture():
checker = Flake8Isort(None, file_path)
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_imports_unexpected_blank_line(self):
file_path = self._given_a_file_in_test_dir(
'from __future__ import division\n'
'\n'
'import threading\n'
'\n'
'from sys import pid\n',
isort_config=''
)
with OutputCapture():
checker = Flake8Isort(None, file_path)
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 = self._given_a_file_in_test_dir(
'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',
isort_config=''
)
with OutputCapture():
checker = Flake8Isort(None, file_path)
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 = self._given_a_file_in_test_dir(
'from sys import pid\n'
Expand All @@ -46,12 +129,9 @@ def test_sorted_incorrectly(self):
checker = Flake8Isort(None, file_path)
ret = list(checker.run())
self.assertEqual(len(ret), 1)
self.assertEqual(ret[0][0], 0)
self.assertEqual(ret[0][0], 2)
self.assertEqual(ret[0][1], 0)
self.assertIn(
'I001 ',
ret[0][2],
)
self.assertTrue(ret[0][2].startswith('I001 '))

def test_isortcfg_found(self):
# _given_a_file_in_test_dir already creates an .isort.cfg file
Expand All @@ -60,17 +140,15 @@ def test_isortcfg_found(self):
'import threading',
isort_config='force_single_line=True'
)

with OutputCapture():
checker = Flake8Isort(None, file_path)
checker.config_file = True
ret = list(checker.run())
self.assertEqual(len(ret), 1)
self.assertEqual(ret[0][0], 0)
self.assertEqual(ret[0][0], 2)
self.assertEqual(ret[0][1], 0)
self.assertIn(
'I001 ',
ret[0][2],
)
self.assertTrue(ret[0][2].startswith('I001 '))

def test_isortcfg_not_found(self):
file_path = self._given_a_file_in_test_dir(
Expand All @@ -90,10 +168,7 @@ def test_isortcfg_not_found(self):
self.assertEqual(len(ret), 1)
self.assertEqual(ret[0][0], 0)
self.assertEqual(ret[0][1], 0)
self.assertIn(
'I002 ',
ret[0][2],
)
self.assertTrue(ret[0][2].startswith('I002 '))

def test_default_option(self):
"""By default a config file (.isort.cfg) is expected"""
Expand Down

0 comments on commit bed7298

Please sign in to comment.