Skip to content

Commit 5850ac0

Browse files
youben11ccordoba12
authored andcommitted
Use flake8 utility instead of its internal API (#679)
1 parent f09245f commit 5850ac0

File tree

1 file changed

+65
-26
lines changed

1 file changed

+65
-26
lines changed

pyls/plugins/flake8_lint.py

Lines changed: 65 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Copyright 2019 Palantir Technologies, Inc.
22
"""Linter pluging for flake8"""
33
import logging
4-
from flake8.api import legacy as flake8
4+
import re
5+
from subprocess import Popen, PIPE
56
from pyls import hookimpl, lsp
67

78
log = logging.getLogger(__name__)
@@ -21,23 +22,64 @@ def pyls_lint(config, document):
2122
opts = {
2223
'exclude': settings.get('exclude'),
2324
'filename': settings.get('filename'),
24-
'hang_closing': settings.get('hangClosing'),
25+
'hang-closing': settings.get('hangClosing'),
2526
'ignore': settings.get('ignore'),
26-
'max_line_length': settings.get('maxLineLength'),
27+
'max-line-length': settings.get('maxLineLength'),
2728
'select': settings.get('select'),
2829
}
2930

30-
# Build the flake8 checker and use it to generate a report from the document
31-
kwargs = {k: v for k, v in opts.items() if v}
32-
style_guide = flake8.get_style_guide(quiet=4, verbose=0, **kwargs)
33-
report = style_guide.check_files([document.path])
31+
# Call the flake8 utility then parse diagnostics from stdout
32+
args = build_args(opts, document.path)
33+
output = run_flake8(args)
34+
return parse_stdout(document, output)
3435

35-
return parse_report(document, report)
3636

37+
def run_flake8(args):
38+
"""Run flake8 with the provided arguments, logs errors
39+
from stderr if any.
40+
"""
41+
log.debug("Calling flake8 with args: '%s'", args)
42+
try:
43+
cmd = ['flake8']
44+
cmd.extend(args)
45+
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
46+
except IOError:
47+
log.debug("Can't execute flake8. Trying with 'python -m flake8'")
48+
cmd = ['python', '-m', 'flake8']
49+
cmd.extend(args)
50+
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
51+
stderr = p.stderr.read().decode()
52+
if stderr:
53+
log.error("Error while running flake8 '%s'", stderr)
54+
stdout = p.stdout
55+
return stdout.read().decode()
56+
57+
58+
def build_args(options, doc_path):
59+
"""Build arguments for calling flake8.
60+
61+
Args:
62+
options: dictionary of argument names and their values.
63+
doc_path: path of the document to lint.
64+
"""
65+
args = [doc_path]
66+
for arg_name, arg_val in options.items():
67+
arg = None
68+
if isinstance(arg_val, list):
69+
arg = '--{}={}'.format(arg_name, ','.join(arg_val))
70+
elif isinstance(arg_val, bool):
71+
if arg_val:
72+
arg = '--{}'.format(arg_name)
73+
elif isinstance(arg_val, int):
74+
arg = '--{}={}'.format(arg_name, arg_val)
75+
if arg:
76+
args.append(arg)
77+
return args
3778

38-
def parse_report(document, report):
79+
80+
def parse_stdout(document, stdout):
3981
"""
40-
Build a diagnostics from a report, it should extract every result and format
82+
Build a diagnostics from flake8's output, it should extract every result and format
4183
it into a dict that looks like this:
4284
{
4385
'source': 'flake8',
@@ -58,40 +100,37 @@ def parse_report(document, report):
58100
59101
Args:
60102
document: The document to be linted.
61-
report: A Report object returned by checking the document.
103+
stdout: output from flake8
62104
Returns:
63105
A list of dictionaries.
64106
"""
65107

66-
file_checkers = report._application.file_checker_manager.checkers
67-
# No file have been checked
68-
if not file_checkers:
69-
return []
70-
# There should be only a filechecker since we are parsing using a path and not a pattern
71-
if len(file_checkers) > 1:
72-
log.error("Flake8 parsed more than a file for '%s'", document.path)
73-
74108
diagnostics = []
75-
file_checker = file_checkers[0]
76-
for error in file_checker.results:
77-
code, line, character, msg, physical_line = error
109+
lines = stdout.splitlines()
110+
for raw_line in lines:
111+
parsed_line = re.match(r'(.*):(\d*):(\d*): (\w*) (.*)', raw_line).groups()
112+
if not parsed_line or len(parsed_line) != 5:
113+
log.debug("Flake8 output parser can't parse line '%s'", raw_line)
114+
continue
115+
_, line, character, code, msg = parsed_line
116+
line = int(line) - 1
117+
character = int(character) - 1
78118
diagnostics.append(
79119
{
80120
'source': 'flake8',
81121
'code': code,
82122
'range': {
83123
'start': {
84-
'line': line - 1,
124+
'line': line,
85125
'character': character
86126
},
87127
'end': {
88-
'line': line - 1,
128+
'line': line,
89129
# no way to determine the column
90-
'character': len(physical_line)
130+
'character': len(document.lines[line])
91131
}
92132
},
93133
'message': msg,
94-
# no way to determine the severity using the legacy api
95134
'severity': lsp.DiagnosticSeverity.Warning,
96135
}
97136
)

0 commit comments

Comments
 (0)