Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: third_party/pylint/pylint/gui.py

Issue 1920403002: [content/test/gpu] Run pylint check of gpu tests in unittest instead of PRESUBMIT (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update path to LICENSE.txt of logilab/README.chromium Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/pylint/pylint/epylint.py ('k') | third_party/pylint/pylint/interfaces.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 #
4 # This program is free software; you can redistribute it and/or modify it under
5 # the terms of the GNU General Public License as published by the Free Software
6 # Foundation; either version 2 of the License, or (at your option) any later
7 # version.
8 #
9 # This program is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
12 #
13 # You should have received a copy of the GNU General Public License along with
14 # this program; if not, write to the Free Software Foundation, Inc.,
15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 """Tkinker gui for pylint"""
17 from __future__ import print_function
18
19 import os
20 import sys
21 import re
22 from threading import Thread
23
24 import six
25
26 from six.moves.tkinter import (
27 Tk, Frame, Listbox, Entry, Label, Button, Scrollbar,
28 Checkbutton, Radiobutton, IntVar, StringVar, PanedWindow,
29 TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH, SUNKEN, W,
30 HORIZONTAL, DISABLED, NORMAL, W,
31 )
32 from six.moves.tkinter_tkfiledialog import (
33 askopenfilename, askdirectory,
34 )
35
36 import pylint.lint
37 from pylint.reporters.guireporter import GUIReporter
38
39 HOME = os.path.expanduser('~/')
40 HISTORY = '.pylint-gui-history'
41 COLORS = {'(I)':'lightblue',
42 '(C)':'blue', '(R)':'darkblue',
43 '(W)':'black', '(E)':'darkred',
44 '(F)':'red'}
45
46
47 def convert_to_string(msg):
48 """make a string representation of a message"""
49 module_object = msg.module
50 if msg.obj:
51 module_object += ".%s" % msg.obj
52 return "(%s) %s [%d]: %s" % (msg.C, module_object, msg.line, msg.msg)
53
54 class BasicStream(object):
55 '''
56 used in gui reporter instead of writing to stdout, it is written to
57 this stream and saved in contents
58 '''
59 def __init__(self, gui):
60 """init"""
61 self.curline = ""
62 self.gui = gui
63 self.contents = []
64 self.outdict = {}
65 self.currout = None
66 self.next_title = None
67
68 def write(self, text):
69 """write text to the stream"""
70 if re.match('^--+$', text.strip()) or re.match('^==+$', text.strip()):
71 if self.currout:
72 self.outdict[self.currout].remove(self.next_title)
73 self.outdict[self.currout].pop()
74 self.currout = self.next_title
75 self.outdict[self.currout] = ['']
76
77 if text.strip():
78 self.next_title = text.strip()
79
80 if text.startswith(os.linesep):
81 self.contents.append('')
82 if self.currout:
83 self.outdict[self.currout].append('')
84 self.contents[-1] += text.strip(os.linesep)
85 if self.currout:
86 self.outdict[self.currout][-1] += text.strip(os.linesep)
87 if text.endswith(os.linesep) and text.strip():
88 self.contents.append('')
89 if self.currout:
90 self.outdict[self.currout].append('')
91
92 def fix_contents(self):
93 """finalize what the contents of the dict should look like before output """
94 for item in self.outdict:
95 num_empty = self.outdict[item].count('')
96 for _ in range(num_empty):
97 self.outdict[item].remove('')
98 if self.outdict[item]:
99 self.outdict[item].pop(0)
100
101 def output_contents(self):
102 """output contents of dict to the gui, and set the rating"""
103 self.fix_contents()
104 self.gui.tabs = self.outdict
105 try:
106 self.gui.rating.set(self.outdict['Global evaluation'][0])
107 except KeyError:
108 self.gui.rating.set('Error')
109 self.gui.refresh_results_window()
110
111 #reset stream variables for next run
112 self.contents = []
113 self.outdict = {}
114 self.currout = None
115 self.next_title = None
116
117
118 class LintGui(object):
119 """Build and control a window to interact with pylint"""
120
121 def __init__(self, root=None):
122 """init"""
123 self.root = root or Tk()
124 self.root.title('Pylint')
125 #reporter
126 self.reporter = None
127 #message queue for output from reporter
128 self.msg_queue = six.moves.queue.Queue()
129 self.msgs = []
130 self.visible_msgs = []
131 self.filenames = []
132 self.rating = StringVar()
133 self.tabs = {}
134 self.report_stream = BasicStream(self)
135 #gui objects
136 self.lb_messages = None
137 self.showhistory = None
138 self.results = None
139 self.btnRun = None
140 self.information_box = None
141 self.convention_box = None
142 self.refactor_box = None
143 self.warning_box = None
144 self.error_box = None
145 self.fatal_box = None
146 self.txtModule = None
147 self.status = None
148 self.msg_type_dict = None
149 self.init_gui()
150
151 def init_gui(self):
152 """init helper"""
153
154 window = PanedWindow(self.root, orient="vertical")
155 window.pack(side=TOP, fill=BOTH, expand=True)
156
157 top_pane = Frame(window)
158 window.add(top_pane)
159 mid_pane = Frame(window)
160 window.add(mid_pane)
161 bottom_pane = Frame(window)
162 window.add(bottom_pane)
163
164 #setting up frames
165 top_frame = Frame(top_pane)
166 mid_frame = Frame(top_pane)
167 history_frame = Frame(top_pane)
168 radio_frame = Frame(mid_pane)
169 rating_frame = Frame(mid_pane)
170 res_frame = Frame(mid_pane)
171 check_frame = Frame(bottom_pane)
172 msg_frame = Frame(bottom_pane)
173 btn_frame = Frame(bottom_pane)
174 top_frame.pack(side=TOP, fill=X)
175 mid_frame.pack(side=TOP, fill=X)
176 history_frame.pack(side=TOP, fill=BOTH, expand=True)
177 radio_frame.pack(side=TOP, fill=X)
178 rating_frame.pack(side=TOP, fill=X)
179 res_frame.pack(side=TOP, fill=BOTH, expand=True)
180 check_frame.pack(side=TOP, fill=X)
181 msg_frame.pack(side=TOP, fill=BOTH, expand=True)
182 btn_frame.pack(side=TOP, fill=X)
183
184 # Binding F5 application-wide to run lint
185 self.root.bind('<F5>', self.run_lint)
186
187 #Message ListBox
188 rightscrollbar = Scrollbar(msg_frame)
189 rightscrollbar.pack(side=RIGHT, fill=Y)
190 bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL)
191 bottomscrollbar.pack(side=BOTTOM, fill=X)
192 self.lb_messages = Listbox(
193 msg_frame,
194 yscrollcommand=rightscrollbar.set,
195 xscrollcommand=bottomscrollbar.set,
196 bg="white")
197 self.lb_messages.bind("<Double-Button-1>", self.show_sourcefile)
198 self.lb_messages.pack(expand=True, fill=BOTH)
199 rightscrollbar.config(command=self.lb_messages.yview)
200 bottomscrollbar.config(command=self.lb_messages.xview)
201
202 #History ListBoxes
203 rightscrollbar2 = Scrollbar(history_frame)
204 rightscrollbar2.pack(side=RIGHT, fill=Y)
205 bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL)
206 bottomscrollbar2.pack(side=BOTTOM, fill=X)
207 self.showhistory = Listbox(
208 history_frame,
209 yscrollcommand=rightscrollbar2.set,
210 xscrollcommand=bottomscrollbar2.set,
211 bg="white")
212 self.showhistory.pack(expand=True, fill=BOTH)
213 rightscrollbar2.config(command=self.showhistory.yview)
214 bottomscrollbar2.config(command=self.showhistory.xview)
215 self.showhistory.bind('<Double-Button-1>', self.select_recent_file)
216 self.set_history_window()
217
218 #status bar
219 self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W)
220 self.status.pack(side=BOTTOM, fill=X)
221
222 #labelbl_ratingls
223 lbl_rating_label = Label(rating_frame, text='Rating:')
224 lbl_rating_label.pack(side=LEFT)
225 lbl_rating = Label(rating_frame, textvariable=self.rating)
226 lbl_rating.pack(side=LEFT)
227 Label(mid_frame, text='Recently Used:').pack(side=LEFT)
228 Label(top_frame, text='Module or package').pack(side=LEFT)
229
230 #file textbox
231 self.txt_module = Entry(top_frame, background='white')
232 self.txt_module.bind('<Return>', self.run_lint)
233 self.txt_module.pack(side=LEFT, expand=True, fill=X)
234
235 #results box
236 rightscrollbar = Scrollbar(res_frame)
237 rightscrollbar.pack(side=RIGHT, fill=Y)
238 bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL)
239 bottomscrollbar.pack(side=BOTTOM, fill=X)
240 self.results = Listbox(
241 res_frame,
242 yscrollcommand=rightscrollbar.set,
243 xscrollcommand=bottomscrollbar.set,
244 bg="white", font="Courier")
245 self.results.pack(expand=True, fill=BOTH, side=BOTTOM)
246 rightscrollbar.config(command=self.results.yview)
247 bottomscrollbar.config(command=self.results.xview)
248
249 #buttons
250 Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT)
251 Button(top_frame, text='Open Package',
252 command=(lambda: self.file_open(package=True))).pack(side=LEFT)
253
254 self.btnRun = Button(top_frame, text='Run', command=self.run_lint)
255 self.btnRun.pack(side=LEFT)
256 Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM)
257
258 #radio buttons
259 self.information_box = IntVar()
260 self.convention_box = IntVar()
261 self.refactor_box = IntVar()
262 self.warning_box = IntVar()
263 self.error_box = IntVar()
264 self.fatal_box = IntVar()
265 i = Checkbutton(check_frame, text="Information", fg=COLORS['(I)'],
266 variable=self.information_box, command=self.refresh_msg_ window)
267 c = Checkbutton(check_frame, text="Convention", fg=COLORS['(C)'],
268 variable=self.convention_box, command=self.refresh_msg_w indow)
269 r = Checkbutton(check_frame, text="Refactor", fg=COLORS['(R)'],
270 variable=self.refactor_box, command=self.refresh_msg_win dow)
271 w = Checkbutton(check_frame, text="Warning", fg=COLORS['(W)'],
272 variable=self.warning_box, command=self.refresh_msg_wind ow)
273 e = Checkbutton(check_frame, text="Error", fg=COLORS['(E)'],
274 variable=self.error_box, command=self.refresh_msg_window )
275 f = Checkbutton(check_frame, text="Fatal", fg=COLORS['(F)'],
276 variable=self.fatal_box, command=self.refresh_msg_window )
277 i.select()
278 c.select()
279 r.select()
280 w.select()
281 e.select()
282 f.select()
283 i.pack(side=LEFT)
284 c.pack(side=LEFT)
285 r.pack(side=LEFT)
286 w.pack(side=LEFT)
287 e.pack(side=LEFT)
288 f.pack(side=LEFT)
289
290 #check boxes
291 self.box = StringVar()
292 # XXX should be generated
293 report = Radiobutton(
294 radio_frame, text="Report", variable=self.box,
295 value="Report", command=self.refresh_results_window)
296 raw_met = Radiobutton(
297 radio_frame, text="Raw metrics", variable=self.box,
298 value="Raw metrics", command=self.refresh_results_window)
299 dup = Radiobutton(
300 radio_frame, text="Duplication", variable=self.box,
301 value="Duplication", command=self.refresh_results_window)
302 ext = Radiobutton(
303 radio_frame, text="External dependencies",
304 variable=self.box, value="External dependencies",
305 command=self.refresh_results_window)
306 stat = Radiobutton(
307 radio_frame, text="Statistics by type",
308 variable=self.box, value="Statistics by type",
309 command=self.refresh_results_window)
310 msg_cat = Radiobutton(
311 radio_frame, text="Messages by category",
312 variable=self.box, value="Messages by category",
313 command=self.refresh_results_window)
314 msg = Radiobutton(
315 radio_frame, text="Messages", variable=self.box,
316 value="Messages", command=self.refresh_results_window)
317 source_file = Radiobutton(
318 radio_frame, text="Source File", variable=self.box,
319 value="Source File", command=self.refresh_results_window)
320 report.select()
321 report.grid(column=0, row=0, sticky=W)
322 raw_met.grid(column=1, row=0, sticky=W)
323 dup.grid(column=2, row=0, sticky=W)
324 msg.grid(column=3, row=0, sticky=W)
325 stat.grid(column=0, row=1, sticky=W)
326 msg_cat.grid(column=1, row=1, sticky=W)
327 ext.grid(column=2, row=1, sticky=W)
328 source_file.grid(column=3, row=1, sticky=W)
329
330 #dictionary for check boxes and associated error term
331 self.msg_type_dict = {
332 'I': lambda: self.information_box.get() == 1,
333 'C': lambda: self.convention_box.get() == 1,
334 'R': lambda: self.refactor_box.get() == 1,
335 'E': lambda: self.error_box.get() == 1,
336 'W': lambda: self.warning_box.get() == 1,
337 'F': lambda: self.fatal_box.get() == 1
338 }
339 self.txt_module.focus_set()
340
341
342 def select_recent_file(self, event): # pylint: disable=unused-argument
343 """adds the selected file in the history listbox to the Module box"""
344 if not self.showhistory.size():
345 return
346
347 selected = self.showhistory.curselection()
348 item = self.showhistory.get(selected)
349 #update module
350 self.txt_module.delete(0, END)
351 self.txt_module.insert(0, item)
352
353 def refresh_msg_window(self):
354 """refresh the message window with current output"""
355 #clear the window
356 self.lb_messages.delete(0, END)
357 self.visible_msgs = []
358 for msg in self.msgs:
359 if self.msg_type_dict.get(msg.C)():
360 self.visible_msgs.append(msg)
361 msg_str = convert_to_string(msg)
362 self.lb_messages.insert(END, msg_str)
363 fg_color = COLORS.get(msg_str[:3], 'black')
364 self.lb_messages.itemconfigure(END, fg=fg_color)
365
366 def refresh_results_window(self):
367 """refresh the results window with current output"""
368 #clear the window
369 self.results.delete(0, END)
370 try:
371 for res in self.tabs[self.box.get()]:
372 self.results.insert(END, res)
373 except KeyError:
374 pass
375
376 def process_incoming(self):
377 """process the incoming messages from running pylint"""
378 while self.msg_queue.qsize():
379 try:
380 msg = self.msg_queue.get(0)
381 if msg == "DONE":
382 self.report_stream.output_contents()
383 return False
384
385 #adding message to list of msgs
386 self.msgs.append(msg)
387
388 #displaying msg if message type is selected in check box
389 if self.msg_type_dict.get(msg.C)():
390 self.visible_msgs.append(msg)
391 msg_str = convert_to_string(msg)
392 self.lb_messages.insert(END, msg_str)
393 fg_color = COLORS.get(msg_str[:3], 'black')
394 self.lb_messages.itemconfigure(END, fg=fg_color)
395
396 except six.moves.queue.Empty:
397 pass
398 return True
399
400 def periodic_call(self):
401 """determine when to unlock the run button"""
402 if self.process_incoming():
403 self.root.after(100, self.periodic_call)
404 else:
405 #enabling button so it can be run again
406 self.btnRun.config(state=NORMAL)
407
408 def mainloop(self):
409 """launch the mainloop of the application"""
410 self.root.mainloop()
411
412 def quit(self, _=None):
413 """quit the application"""
414 self.root.quit()
415
416 def halt(self): # pylint: disable=no-self-use
417 """program halt placeholder"""
418 return
419
420 def file_open(self, package=False, _=None):
421 """launch a file browser"""
422 if not package:
423 filename = askopenfilename(parent=self.root,
424 filetypes=[('pythonfiles', '*.py'),
425 ('allfiles', '*')],
426 title='Select Module')
427 else:
428 filename = askdirectory(title="Select A Folder", mustexist=1)
429
430 if filename == ():
431 return
432
433 self.txt_module.delete(0, END)
434 self.txt_module.insert(0, filename)
435
436 def update_filenames(self):
437 """update the list of recent filenames"""
438 filename = self.txt_module.get()
439 if not filename:
440 filename = os.getcwd()
441 if filename+'\n' in self.filenames:
442 index = self.filenames.index(filename+'\n')
443 self.filenames.pop(index)
444
445 #ensure only 10 most recent are stored
446 if len(self.filenames) == 10:
447 self.filenames.pop()
448 self.filenames.insert(0, filename+'\n')
449
450 def set_history_window(self):
451 """update the history window with info from the history file"""
452 #clear the window
453 self.showhistory.delete(0, END)
454 # keep the last 10 most recent files
455 try:
456 view_history = open(HOME+HISTORY, 'r')
457 for hist in view_history.readlines():
458 if not hist in self.filenames:
459 self.filenames.append(hist)
460 self.showhistory.insert(END, hist.split('\n')[0])
461 view_history.close()
462 except IOError:
463 # do nothing since history file will be created later
464 return
465
466 def run_lint(self, _=None):
467 """launches pylint"""
468 self.update_filenames()
469 self.root.configure(cursor='watch')
470 self.reporter = GUIReporter(self, output=self.report_stream)
471 module = self.txt_module.get()
472 if not module:
473 module = os.getcwd()
474
475 #cleaning up msgs and windows
476 self.msgs = []
477 self.visible_msgs = []
478 self.lb_messages.delete(0, END)
479 self.tabs = {}
480 self.results.delete(0, END)
481 self.btnRun.config(state=DISABLED)
482
483 #setting up a worker thread to run pylint
484 worker = Thread(target=lint_thread, args=(module, self.reporter, self,))
485 self.periodic_call()
486 worker.start()
487
488 # Overwrite the .pylint-gui-history file with all the new recently added files
489 # in order from filenames but only save last 10 files
490 write_history = open(HOME+HISTORY, 'w')
491 write_history.writelines(self.filenames)
492 write_history.close()
493 self.set_history_window()
494
495 self.root.configure(cursor='')
496
497 def show_sourcefile(self, event=None): # pylint: disable=unused-argument
498 selected = self.lb_messages.curselection()
499 if not selected:
500 return
501
502 msg = self.visible_msgs[int(selected[0])]
503 scroll = msg.line - 3
504 if scroll < 0:
505 scroll = 0
506
507 self.tabs["Source File"] = open(msg.path, "r").readlines()
508 self.box.set("Source File")
509 self.refresh_results_window()
510 self.results.yview(scroll)
511 self.results.select_set(msg.line - 1)
512
513
514 def lint_thread(module, reporter, gui):
515 """thread for pylint"""
516 gui.status.text = "processing module(s)"
517 pylint.lint.Run(args=[module], reporter=reporter, exit=False)
518 gui.msg_queue.put("DONE")
519
520
521 def Run(args):
522 """launch pylint gui from args"""
523 if args:
524 print('USAGE: pylint-gui\n launch a simple pylint gui using Tk')
525 sys.exit(1)
526 gui = LintGui()
527 gui.mainloop()
528 sys.exit(0)
529
530 if __name__ == '__main__':
531 Run(sys.argv[1:])
OLDNEW
« no previous file with comments | « third_party/pylint/pylint/epylint.py ('k') | third_party/pylint/pylint/interfaces.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698