/
flake8_isort.py
137 lines (119 loc) · 4.71 KB
/
flake8_isort.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import warnings
from contextlib import redirect_stdout
from difflib import unified_diff
from io import StringIO
from pathlib import Path
import isort
from pkg_resources import DistributionNotFound, get_distribution
def _version():
try:
return get_distribution('flake8_isort').version
except DistributionNotFound:
return 'dev' # for local development if package is not installed yet
class Flake8IsortBase:
name = 'flake8_isort'
version = _version()
isort_unsorted = 'I001 isort found an import in the wrong position'
no_config_msg = 'I002 no configuration found (.isort.cfg or [isort] in configs)'
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'
isort_add_unexp = 'I005 isort found an unexpected missing import'
show_traceback = False
stdin_display_name = None
search_current = True
def __init__(self, tree, filename, lines):
self.filename = filename
self.lines = lines
@staticmethod
def add_options(option_manager):
option_manager.add_option(
'--isort-show-traceback',
action='store_true',
parse_from_config=True,
help='Show full traceback with diff from isort',
)
@classmethod
def parse_options(cls, option_manager, options, args):
cls.stdin_display_name = options.stdin_display_name
cls.show_traceback = options.isort_show_traceback
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:
file_path = None
isort_config = isort.settings.Config(settings_path=Path.cwd())
input_string = ''.join(self.lines)
traceback = ''
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=f'{self.filename}:before',
tofile=f'{self.filename}:after',
)
)
traceback = f'{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, udiff):
"""Parse unified diff for changes and generate messages
Args
----
udiff : unified diff delta
Yields
------
tuple: A tuple of the specific isort line number and message.
"""
line_num = 0
additions = []
moves = []
for line in udiff.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