PyLint/PyLintConfigDialog.py

branch
eric7
changeset 98
ab4aabca55ec
parent 97
2226347d86e4
child 99
f34bc41cd4b4
equal deleted inserted replaced
97:2226347d86e4 98:ab4aabca55ec
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2005 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to configure the PyLint process.
8 """
9
10 import os
11 import copy
12
13 from PyQt5.QtCore import pyqtSlot, QProcess
14 from PyQt5.QtWidgets import QDialog
15
16 from E5Gui.E5Application import e5App
17 from E5Gui import E5FileDialog, E5MessageBox
18 from E5Gui.E5Completers import E5FileCompleter
19
20 from .Ui_PyLintConfigDialog import Ui_PyLintConfigDialog
21
22 import Preferences
23 import Utilities
24
25
26 class PyLintConfigDialog(QDialog, Ui_PyLintConfigDialog):
27 """
28 Class implementing a dialog to configure the PyLint process.
29 """
30 def __init__(self, ppath, exe, parms, version):
31 """
32 Constructor
33
34 @param ppath project path (string or QString)
35 Used to set the default path for the rcfile selection dialog
36 @param exe name of the pylint executable (string)
37 @param parms parameters to set in the dialog
38 @param version pylint version (string)
39 """
40 super().__init__(None)
41 self.setupUi(self)
42
43 self.version = version
44 self.pylintProc = None
45 self.lint = exe
46
47 self.__initializeDefaults()
48
49 # get a copy of the defaults to store the user settings
50 self.parameters = copy.deepcopy(self.defaults)
51
52 # combine it with the values of parms
53 if parms is not None:
54 self.parameters.update(parms)
55
56 self.configfileCompleter = E5FileCompleter(self.configfileEdit)
57 self.reportfileCompleter = E5FileCompleter(self.reportfileEdit)
58
59 # initialize general tab
60 self.configfileEdit.setText(self.parameters['configFile'])
61 self.txtOutputButton.setChecked(self.parameters['txtReport'])
62 self.htmlOutputButton.setChecked(self.parameters['htmlReport'])
63 self.dialogOutputButton.setChecked(self.parameters['dialogReport'])
64 self.reportfileEdit.setText(self.parameters['reportFile'])
65
66 # initialize checkers tab
67 self.basicCheckBox.setChecked(self.parameters['enableBasic'])
68 self.classesCheckBox.setChecked(self.parameters['enableClasses'])
69 self.designCheckBox.setChecked(self.parameters['enableDesign'])
70 self.exceptionsCheckBox.setChecked(self.parameters['enableExceptions'])
71 self.formatCheckBox.setChecked(self.parameters['enableFormat'])
72 self.importsCheckBox.setChecked(self.parameters['enableImports'])
73 self.metricsCheckBox.setChecked(self.parameters['enableMetrics'])
74 self.miscellaneousCheckBox.setChecked(
75 self.parameters['enableMiscellaneous'])
76 self.newstyleCheckBox.setChecked(self.parameters['enableNewstyle'])
77 self.similaritiesCheckBox.setChecked(
78 self.parameters['enableSimilarities'])
79 self.typecheckCheckBox.setChecked(self.parameters['enableTypecheck'])
80 self.variablesCheckBox.setChecked(self.parameters['enableVariables'])
81 self.loggingCheckBox.setChecked(self.parameters['enableLogging'])
82 self.stringFormatCheckBox.setChecked(
83 self.parameters['enableStringFormat'])
84
85 # initialize messages tab
86 self.enabledMessagesEdit.setText(self.parameters['enabledMessages'])
87 self.disabledMessagesEdit.setText(self.parameters['disabledMessages'])
88
89 self.ppath = ppath
90
91 def __initializeDefaults(self):
92 """
93 Private method to set the default values.
94
95 These are needed later on to generate the commandline
96 parameters.
97 """
98 self.defaults = {
99 # general options
100 'configFile': '',
101 'reportFile': '',
102 'txtReport': False,
103 'htmlReport': True,
104 'dialogReport': False,
105
106 # enabled checkers
107 'enableBasic': True,
108 'enableClasses': True,
109 'enableDesign': True,
110 'enableExceptions': True,
111 'enableFormat': False,
112 'enableImports': False,
113 'enableLogging': True,
114 'enableMetrics': True,
115 'enableMiscellaneous': True,
116 'enableNewstyle': True,
117 'enableSimilarities': True,
118 'enableStringFormat': True,
119 'enableTypecheck': True,
120 'enableVariables': True,
121
122 # messages
123 'enabledMessages': '',
124 'disabledMessages': '',
125 }
126
127 def generateParameters(self):
128 """
129 Public method that generates the commandline parameters.
130
131 It generates a QStringList to be used
132 to set the QProcess arguments for the pylint call and
133 a list containing the non default parameters. The second
134 list can be passed back upon object generation to overwrite
135 the default settings.
136
137 <b>Note</b>: The arguments list contains the name of the pylint
138 executable as the first parameter.
139
140 @return a tuple of the commandline parameters and non default
141 parameters (list of strings, dictionary)
142 """
143 parms = {}
144 args = []
145
146 # 1. the program name
147 args.append(self.lint)
148
149 # 2. the commandline options
150 # 2.1 general options
151 if self.parameters['configFile'] != self.defaults['configFile']:
152 parms['configFile'] = self.parameters['configFile']
153 args.append('--rcfile={0}'.format(self.parameters['configFile']))
154 parms['txtReport'] = self.parameters['txtReport']
155 parms['htmlReport'] = self.parameters['htmlReport']
156 parms['dialogReport'] = self.parameters['dialogReport']
157 if self.parameters['htmlReport']:
158 args.append('--output-format=html')
159 elif self.parameters['dialogReport']:
160 args.append('--output-format=parseable')
161 args.append('--reports=n')
162 else:
163 args.append('--output-format=text')
164 if self.parameters['reportFile'] != self.defaults['reportFile']:
165 parms['reportFile'] = self.parameters['reportFile']
166
167 # 2.2 checkers options
168 parms['enableBasic'] = self.parameters['enableBasic']
169 parms['enableClasses'] = self.parameters['enableClasses']
170 parms['enableDesign'] = self.parameters['enableDesign']
171 parms['enableExceptions'] = self.parameters['enableExceptions']
172 parms['enableFormat'] = self.parameters['enableFormat']
173 parms['enableImports'] = self.parameters['enableImports']
174 parms['enableMetrics'] = self.parameters['enableMetrics']
175 parms['enableMiscellaneous'] = self.parameters['enableMiscellaneous']
176 parms['enableNewstyle'] = self.parameters['enableNewstyle']
177 parms['enableSimilarities'] = self.parameters['enableSimilarities']
178 parms['enableTypecheck'] = self.parameters['enableTypecheck']
179 parms['enableVariables'] = self.parameters['enableVariables']
180 parms['enableLogging'] = self.parameters['enableLogging']
181 parms['enableStringFormat'] = self.parameters['enableStringFormat']
182
183 checkers = []
184 if self.parameters['enableBasic']:
185 checkers.append('basic')
186 if self.parameters['enableClasses']:
187 checkers.append('classes')
188 if self.parameters['enableDesign']:
189 checkers.append('design')
190 if self.parameters['enableExceptions']:
191 checkers.append('exceptions')
192 if self.parameters['enableFormat']:
193 checkers.append('format')
194 if self.parameters['enableImports']:
195 checkers.append('imports')
196 if self.parameters['enableMetrics']:
197 checkers.append('metrics')
198 if self.parameters['enableMiscellaneous']:
199 checkers.append('miscellaneous')
200 if self.parameters['enableNewstyle']:
201 checkers.append('newstyle')
202 if self.parameters['enableSimilarities']:
203 checkers.append('similarities')
204 if self.parameters['enableTypecheck']:
205 checkers.append('typecheck')
206 if self.parameters['enableVariables']:
207 checkers.append('variables')
208 if self.parameters['enableLogging']:
209 checkers.append('logging')
210 if self.parameters['enableStringFormat']:
211 if self.version > '0.27.0':
212 checkers.append('string')
213 else:
214 checkers.append('string_format')
215
216 args.append('--disable=all')
217 if checkers:
218 args.append('--enable={0}'.format(','.join(checkers)))
219
220 # 2.3 messages options
221 parms['enabledMessages'] = self.parameters['enabledMessages']
222 parms['disabledMessages'] = self.parameters['disabledMessages']
223 if parms['enabledMessages']:
224 args.append('--enable={0}'.format(parms['enabledMessages']))
225 if parms['disabledMessages']:
226 args.append('--disable={0}'.format(parms['disabledMessages']))
227
228 return (args, parms)
229
230 @pyqtSlot()
231 def on_configfileButton_clicked(self):
232 """
233 Private slot to select the configuration file.
234
235 It displays a file selection dialog to select the configuration file.
236 """
237 startWith = self.configfileEdit.text()
238 if startWith == "":
239 startWith = self.ppath
240 config = E5FileDialog.getOpenFileName(
241 self,
242 self.tr("Select configuration file"),
243 startWith,
244 self.tr("Configuration Files (*.cfg *.cnf *.rc);;"
245 "All Files (*)"))
246 if config:
247 self.configfileEdit.setText(Utilities.toNativeSeparators(config))
248
249 @pyqtSlot()
250 def on_reportfileButton_clicked(self):
251 """
252 Private slot to select the report file.
253
254 It displays a file selection dialog to select the report file.
255 """
256 report = E5FileDialog.getSaveFileName(
257 self,
258 self.tr("Select report file"),
259 self.reportfileEdit.text(),
260 None,
261 None,
262 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
263
264 if report:
265 self.reportfileEdit.setText(Utilities.toNativeSeparators(report))
266
267 def accept(self):
268 """
269 Public slot called by the Ok button.
270
271 It saves the values in the parameters dictionary.
272 """
273 # get data of general tab
274 self.parameters['configFile'] = self.configfileEdit.text()
275 self.parameters['txtReport'] = self.txtOutputButton.isChecked()
276 self.parameters['htmlReport'] = self.htmlOutputButton.isChecked()
277 self.parameters['dialogReport'] = self.dialogOutputButton.isChecked()
278 self.parameters['reportFile'] = self.reportfileEdit.text()
279
280 # get data of checkers tab
281 self.parameters['enableBasic'] = self.basicCheckBox.isChecked()
282 self.parameters['enableClasses'] = self.classesCheckBox.isChecked()
283 self.parameters['enableDesign'] = self.designCheckBox.isChecked()
284 self.parameters['enableExceptions'] = (
285 self.exceptionsCheckBox.isChecked())
286 self.parameters['enableFormat'] = self.formatCheckBox.isChecked()
287 self.parameters['enableImports'] = self.importsCheckBox.isChecked()
288 self.parameters['enableMetrics'] = self.metricsCheckBox.isChecked()
289 self.parameters['enableMiscellaneous'] = (
290 self.miscellaneousCheckBox.isChecked())
291 self.parameters['enableNewstyle'] = self.newstyleCheckBox.isChecked()
292 self.parameters['enableSimilarities'] = (
293 self.similaritiesCheckBox.isChecked())
294 self.parameters['enableTypecheck'] = self.typecheckCheckBox.isChecked()
295 self.parameters['enableVariables'] = self.variablesCheckBox.isChecked()
296 self.parameters['enableLogging'] = self.loggingCheckBox.isChecked()
297 self.parameters['enableStringFormat'] = (
298 self.stringFormatCheckBox.isChecked())
299
300 # get data of messages tab
301 self.parameters['enabledMessages'] = ','.join(
302 [m.strip() for m in self.enabledMessagesEdit.text().split(',')])
303 self.parameters['disabledMessages'] = ','.join(
304 [m.strip() for m in self.disabledMessagesEdit.text().split(',')])
305
306 # call the accept slot of the base class
307 super().accept()
308
309 ###########################################################################
310 ## Methods below are needed to generate a configuration file template
311 ###########################################################################
312
313 @pyqtSlot()
314 def on_configButton_clicked(self):
315 """
316 Private slot to handle the generation of a sample configuration.
317 """
318 self.buf = ""
319 self.pylintProc = QProcess()
320 args = []
321
322 self.__ioEncoding = Preferences.getSystem("IOEncoding")
323
324 args.append('--generate-rcfile')
325
326 self.pylintProc.readyReadStandardOutput.connect(self.__readStdout)
327 self.pylintProc.readyReadStandardError.connect(self.__readStderr)
328 self.pylintProc.finished.connect(self.__createConfigDone)
329
330 self.pylintProc.start(self.lint, args)
331 procStarted = self.pylintProc.waitForStarted()
332 if procStarted:
333 e5App().getObject("ViewManager").enableEditorsCheckFocusIn(False)
334 else:
335 E5MessageBox.critical(
336 self,
337 self.tr('Process Generation Error'),
338 self.tr(
339 'Could not start {0}.<br>'
340 'Ensure that it is in the search path.'
341 ).format(self.lint))
342
343 def __createConfigDone(self, exitCode, exitStatus):
344 """
345 Private slot to handle the the finished signal of the pylint process.
346
347 @param exitCode exit code of the process (integer)
348 @param exitStatus exit status of the process (QProcess.ExitStatus)
349 """
350 vm = e5App().getObject("ViewManager")
351 vm.enableEditorsCheckFocusIn(True)
352 if exitStatus == QProcess.NormalExit and exitCode == 0:
353 vm.newEditor()
354 aw = vm.activeWindow()
355 aw.insertAt(self.buf, 0, 0)
356 aw.setLanguage('dummy.rc')
357 self.reject()
358
359 def __readStdout(self):
360 """
361 Private slot to handle the readyReadStandardOutput signal of the
362 pylint process.
363 """
364 if self.pylintProc is None:
365 return
366 self.pylintProc.setReadChannel(QProcess.StandardOutput)
367
368 while self.pylintProc and self.pylintProc.canReadLine():
369 line = str(self.pylintProc.readLine(), self.__ioEncoding,
370 "replace").rstrip()
371 self.buf += line + os.linesep
372
373 def __readStderr(self):
374 """
375 Private slot to handle the readyReadStandardError signal of the
376 pylint process.
377 """
378 if self.pylintProc is None:
379 return
380 self.pylintProc.setReadChannel(QProcess.StandardError)
381 while self.pylintProc and self.pylintProc.canReadLine():
382 s = 'pylint: ' + str(
383 self.pylintProc.readLine(), self.__ioEncoding, "replace")
384 e5App().getObject("UserInterface").appendStderr.emit(s)

eric ide

mercurial