forked from vorushin/sublimetext_python_checker
-
Notifications
You must be signed in to change notification settings - Fork 0
/
python_checker.py
157 lines (125 loc) · 4.96 KB
/
python_checker.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import re
import signal
from subprocess import Popen, PIPE
import sublime
import sublime_plugin
try:
from local_settings import CHECKERS
except ImportError as e:
print '''
Please create file local_settings.py in the same directory with
python_checker.py. Add to local_settings.py list of your checkers.
Example:
CHECKERS = [('/Users/vorushin/.virtualenvs/checkers/bin/pep8', []),
('/Users/vorushin/.virtualenvs/checkers/bin/pyflakes', [])]
First parameter is path to command, second - optional list of arguments.
If you want to disable line length checking in pep8, set second parameter
to ['--ignore=E501'].
'''
raise e
global view_messages
view_messages = {}
class PythonCheckerCommand(sublime_plugin.EventListener):
def on_activated(self, view):
signal.signal(signal.SIGALRM, lambda s, f: check_and_mark(view))
signal.alarm(1)
def on_deactivated(self, view):
signal.alarm(0)
def on_post_save(self, view):
check_and_mark(view)
def on_selection_modified(self, view):
global view_messages
lineno = view.rowcol(view.sel()[0].end())[0]
if view.id() in view_messages and lineno in view_messages[view.id()]:
view.set_status('python_checker', view_messages[view.id()][lineno])
else:
view.erase_status('python_checker')
def check_and_mark(view):
if not 'python' in view.settings().get('syntax').lower():
return
if not view.file_name(): # we check files (not buffers)
return
messages = []
for checker, args in CHECKERS:
checker_messages = []
try:
p = Popen([checker, view.file_name()] + args, stdout=PIPE,
stderr=PIPE)
stdout, stderr = p.communicate(None)
checker_messages += parse_messages(stdout)
checker_messages += parse_messages(stderr)
for line in checker_messages:
print "[%s] %s:%s:%s %s" % (
checker.split('/')[-1], view.file_name(),
line['lineno'] + 1, line['col'] + 1, line['text'])
messages += checker_messages
except OSError:
print "Checker could not be found:", checker
outlines = [view.full_line(view.text_point(m['lineno'], 0)) \
for m in messages]
view.erase_regions('python_checker_outlines')
view.add_regions('python_checker_outlines',
outlines,
'keyword',
sublime.DRAW_EMPTY | sublime.DRAW_OUTLINED)
underlines = []
for m in messages:
if m['col']:
a = view.text_point(m['lineno'], m['col'])
underlines.append(sublime.Region(a, a))
view.erase_regions('python_checker_underlines')
view.add_regions('python_checker_underlines',
underlines,
'keyword',
sublime.DRAW_EMPTY_AS_OVERWRITE | sublime.DRAW_OUTLINED)
line_messages = {}
for m in (m for m in messages if m['text']):
if m['lineno'] in line_messages:
line_messages[m['lineno']] += ';' + m['text']
else:
line_messages[m['lineno']] = m['text']
global view_messages
view_messages[view.id()] = line_messages
def parse_messages(checker_output):
'''
Examples of lines in checker_output
pep8 on *nix
/Users/vorushin/Python/answers/urls.py:24:80: E501 line too long \
(96 characters)
pyflakes on *nix
/Users/vorushin/Python/answers/urls.py:4: 'from django.conf.urls.defaults \
import *' used; unable to detect undefined names
pyflakes, invalid syntax message (3 lines)
/Users/vorushin/Python/answers/urls.py:14: invalid syntax
dict_test = {key: value for (key, value) in [('one', 1), ('two': 2)]}
^
pyflakes on Windows
c:\Python26\Scripts\pildriver.py:208: 'ImageFilter' imported but unused
'''
pep8_re = re.compile(r'.*:(\d+):(\d+):\s+(.*)')
pyflakes_re = re.compile(r'.*:(\d+):\s+(.*)')
messages = []
for i, line in enumerate(checker_output.splitlines()):
if pep8_re.match(line):
lineno, col, text = pep8_re.match(line).groups()
elif pyflakes_re.match(line):
lineno, text = pyflakes_re.match(line).groups()
col = 1
if text == 'invalid syntax':
col = invalid_syntax_col(checker_output, i)
else:
continue
messages.append({'lineno': int(lineno) - 1,
'col': int(col) - 1,
'text': text})
return messages
def invalid_syntax_col(checker_output, line_index):
'''
For error messages like this:
/Users/vorushin/Python/answers/urls.py:14: invalid syntax
dict_test = {key: value for (key, value) in [('one', 1), ('two': 2)]}
^
'''
for line in checker_output.splitlines()[line_index + 1:]:
if line.startswith(' ') and line.find('^') != -1:
return line.find('^')