|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the pyright type checker dialog. |
|
8 """ |
|
9 |
|
10 import contextlib |
|
11 import copy |
|
12 import json |
|
13 import os |
|
14 |
|
15 import tomlkit |
|
16 |
|
17 from PyQt6.QtCore import QProcess, Qt, pyqtSlot |
|
18 from PyQt6.QtGui import QGuiApplication |
|
19 from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QTreeWidgetItem |
|
20 |
|
21 from eric7 import Preferences |
|
22 from eric7.EricWidgets import EricMessageBox |
|
23 from eric7.EricWidgets.EricApplication import ericApp |
|
24 from eric7.QScintilla.Editor import Editor |
|
25 from eric7.SystemUtilities import PythonUtilities |
|
26 |
|
27 from .Ui_PyrightCheckerDialog import Ui_PyrightCheckerDialog |
|
28 |
|
29 |
|
30 class PyrightCheckerDialog(QDialog, Ui_PyrightCheckerDialog): |
|
31 """ |
|
32 Class documentation goes here. |
|
33 """ |
|
34 |
|
35 filenameRole = Qt.ItemDataRole.UserRole + 1 |
|
36 severityRole = Qt.ItemDataRole.UserRole + 2 |
|
37 startRole = Qt.ItemDataRole.UserRole + 3 |
|
38 endRole = Qt.ItemDataRole.UserRole + 4 |
|
39 |
|
40 def __init__(self, plugin, parent=None): |
|
41 """ |
|
42 Constructor |
|
43 |
|
44 @param plugin reference to the plugin object |
|
45 @type PyrightPlugin |
|
46 @param parent reference to the parent widget (defaults to None) |
|
47 @type QWidget (optional) |
|
48 """ |
|
49 super().__init__(parent) |
|
50 self.setupUi(self) |
|
51 self.setWindowFlags(Qt.WindowType.Window) |
|
52 |
|
53 self.__plugin = plugin |
|
54 |
|
55 self.__severityMapping = { |
|
56 "error": self.tr("Error"), |
|
57 "warning": self.tr("Warning"), |
|
58 "information": self.tr("Information"), |
|
59 } |
|
60 |
|
61 self.__severityForEditor = {"warning": Editor.WarningCode} |
|
62 try: |
|
63 self.__severityForEditor["error"] = Editor.WarningError |
|
64 except AttributeError: |
|
65 self.__severityForEditor["error"] = Editor.WarningCode |
|
66 try: |
|
67 self.__severityForEditor["information"] = Editor.WarningInfo |
|
68 except AttributeError: |
|
69 self.__severityForEditor["information"] = Editor.WarningCode |
|
70 |
|
71 self.__exitCodeMapping = { |
|
72 0: self.tr("No issues detected"), |
|
73 1: self.tr("Issues detected"), |
|
74 2: self.tr("Fatal error occurred with no errors or warnings reported"), |
|
75 3: self.tr("Config file could not be read or parsed"), |
|
76 4: self.tr("Illegal command-line parameters specified"), |
|
77 } |
|
78 |
|
79 self.platformComboBox.addItem("", "") |
|
80 self.platformComboBox.addItem("Linux", "Linux") |
|
81 self.platformComboBox.addItem("macOS", "Darwin") |
|
82 self.platformComboBox.addItem("Windows", "Windows") |
|
83 |
|
84 self.versionComboBox.addItems(["", "3.8", "3.9", "3.10", "3.11", "3.12"]) |
|
85 |
|
86 self.__dirOrFileList = [] |
|
87 self.__project = None |
|
88 self.__forProject = False |
|
89 self.__process = None |
|
90 self.__hasResults = False |
|
91 |
|
92 self.showButton.setEnabled(False) |
|
93 self.tomlButton.setEnabled(False) |
|
94 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) |
|
95 |
|
96 self.resultList.headerItem().setText(self.resultList.columnCount(), "") |
|
97 self.resultList.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder) |
|
98 |
|
99 self.__colors = {} |
|
100 |
|
101 self.on_loadDefaultButton_clicked() |
|
102 |
|
103 self.mainWidget.setCurrentWidget(self.configureTab) |
|
104 |
|
105 def __resort(self): |
|
106 """ |
|
107 Private method to resort the tree. |
|
108 """ |
|
109 self.resultList.sortItems( |
|
110 self.resultList.sortColumn(), self.resultList.header().sortIndicatorOrder() |
|
111 ) |
|
112 |
|
113 def __createResultItem(self, result): |
|
114 """ |
|
115 Private method to create an entry in the result list. |
|
116 |
|
117 @param result dictionary containing check result data |
|
118 @type dict |
|
119 """ |
|
120 # step 1: search the file entry or create it |
|
121 filePath = ( |
|
122 self.__project.getRelativePath(result["file"]) |
|
123 if self.__forProject |
|
124 else result["file"] |
|
125 ) |
|
126 fileItems = self.resultList.findItems(filePath, Qt.MatchFlag.MatchExactly, 0) |
|
127 if fileItems: |
|
128 fileItem = fileItems[0] |
|
129 else: |
|
130 fileItem = QTreeWidgetItem(self.resultList, [filePath]) |
|
131 fileItem.setFirstColumnSpanned(True) |
|
132 fileItem.setExpanded(True) |
|
133 fileItem.setData(0, self.filenameRole, result["file"]) |
|
134 |
|
135 itm = QTreeWidgetItem( |
|
136 fileItem, |
|
137 [ |
|
138 "{0:6}".format(result["range"]["start"]["line"] + 1), |
|
139 self.__severityMapping[result["severity"]], |
|
140 result["message"], |
|
141 ], |
|
142 ) |
|
143 |
|
144 itm.setTextAlignment( |
|
145 0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter |
|
146 ) |
|
147 itm.setTextAlignment( |
|
148 1, Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter |
|
149 ) |
|
150 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignVCenter) |
|
151 |
|
152 itm.setData(0, self.filenameRole, result["file"]) |
|
153 itm.setData(0, self.severityRole, result["severity"]) |
|
154 itm.setData(0, self.startRole, result["range"]["start"]) |
|
155 itm.setData(0, self.endRole, result["range"]["end"]) |
|
156 |
|
157 with contextlib.suppress(KeyError): |
|
158 itm.setData( |
|
159 1, Qt.ItemDataRole.ForegroundRole, self.__colors[result["severity"]][0] |
|
160 ) |
|
161 itm.setData( |
|
162 1, Qt.ItemDataRole.BackgroundRole, self.__colors[result["severity"]][1] |
|
163 ) |
|
164 |
|
165 def __updateSummary(self, summary): |
|
166 """ |
|
167 Private method to update the summary data of the dialog. |
|
168 |
|
169 @param summary dictionary containing the summary data |
|
170 @type dict |
|
171 """ |
|
172 self.filesLabel.setText(str(summary["filesAnalyzed"])) |
|
173 self.errorLabel.setText(str(summary["errorCount"])) |
|
174 self.warningLabel.setText(str(summary["warningCount"])) |
|
175 self.infoLabel.setText(str(summary["informationCount"])) |
|
176 |
|
177 def __processResult(self, result): |
|
178 """ |
|
179 Private method to process the pyright result. |
|
180 |
|
181 @param result dictionary containing the type checking result. |
|
182 @type dict |
|
183 """ |
|
184 # 1. update the severity color mapping |
|
185 self.__colors = { |
|
186 # tuple of foreground and background colors |
|
187 "error": ( |
|
188 Preferences.getEditorColour("AnnotationsErrorForeground"), |
|
189 Preferences.getEditorColour("AnnotationsErrorBackground"), |
|
190 ), |
|
191 "warning": ( |
|
192 Preferences.getEditorColour("AnnotationsWarningForeground"), |
|
193 Preferences.getEditorColour("AnnotationsWarningBackground"), |
|
194 ), |
|
195 } |
|
196 with contextlib.suppress(KeyError): |
|
197 # eric-ide before 23.12 doesn't have this color |
|
198 self.__colors["information"] = ( |
|
199 Preferences.getEditorColour("AnnotationsInfoForeground"), |
|
200 Preferences.getEditorColour("AnnotationsInfoBackground"), |
|
201 ) |
|
202 |
|
203 # 2. set pyright version |
|
204 try: |
|
205 self.pyrightLabel.setText(result["version"]) |
|
206 except KeyError: |
|
207 self.pyrightLabel.setText(self.tr("unknown")) |
|
208 |
|
209 # 3. create result items |
|
210 if result["exitCode"] == 1: |
|
211 self.__hasResults = True |
|
212 for diagnostic in result["generalDiagnostics"]: |
|
213 self.__createResultItem(diagnostic) |
|
214 else: |
|
215 itm = QTreeWidgetItem( |
|
216 self.resultList, self.__exitCodeMapping[result["exitCode"]] |
|
217 ) |
|
218 itm.setFirstColumnSpanned(True) |
|
219 |
|
220 for col in range(self.resultList.columnCount()): |
|
221 self.resultList.resizeColumnToContents(col) |
|
222 self.resultList.header().setStretchLastSection(True) |
|
223 self.__resort() |
|
224 self.resultList.setSortingEnabled(True) |
|
225 |
|
226 # 4. set summary information |
|
227 self.__updateSummary(result["summary"]) |
|
228 |
|
229 self.showButton.setEnabled(self.__hasResults) |
|
230 self.mainWidget.setCurrentWidget(self.resultsTab) |
|
231 |
|
232 def getDefaults(self): |
|
233 """ |
|
234 Public method to get a dictionary containing the default values. |
|
235 |
|
236 @return dictionary containing the default values |
|
237 @rtype dict |
|
238 """ |
|
239 defaults = { |
|
240 "PythonPlatform": "", |
|
241 "PythonVersion": "", |
|
242 "SkipUnannotated": False, |
|
243 } |
|
244 |
|
245 return defaults |
|
246 |
|
247 def prepare(self, project): |
|
248 """ |
|
249 Public method to prepare the dialog with a list of filenames. |
|
250 |
|
251 @param project reference to the project object |
|
252 @type Project |
|
253 """ |
|
254 self.__project = project |
|
255 self.__forProject = True |
|
256 self.__dirOrFileList = [self.__project.getProjectPath()] |
|
257 |
|
258 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) |
|
259 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) |
|
260 |
|
261 defaultParameters = self.getDefaults() |
|
262 self.__data = self.__project.getData("CHECKERSPARMS", "PyrightChecker") |
|
263 if self.__data is None: |
|
264 # initialize the data structure |
|
265 self.__data = copy.deepcopy(defaultParameters) |
|
266 else: |
|
267 for key in defaultParameters: |
|
268 if key not in self.__data: |
|
269 self.__data[key] = defaultParameters[key] |
|
270 |
|
271 self.platformComboBox.setCurrentIndex( |
|
272 self.platformComboBox.findData(self.__data["PythonPlatform"]) |
|
273 ) |
|
274 self.versionComboBox.setCurrentText(self.__data["PythonVersion"]) |
|
275 self.skipUnannotatedCheckBox.setChecked(self.__data["SkipUnannotated"]) |
|
276 |
|
277 self.tomlButton.setEnabled(True) |
|
278 |
|
279 self.mainWidget.setCurrentWidget(self.configureTab) |
|
280 |
|
281 def start(self, files=None, save=False): |
|
282 """ |
|
283 Public method to start a pyright type checking run. |
|
284 |
|
285 @param files list of files to be checked (defaults to None) |
|
286 @type list of str (optional) |
|
287 @param save flag indicating to save the given file/file list/directory |
|
288 @type bool |
|
289 """ |
|
290 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) |
|
291 self.startButton.setEnabled(False) |
|
292 self.restartButton.setEnabled(False) |
|
293 self.showButton.setEnabled(False) |
|
294 |
|
295 if not files and not self.__forProject: |
|
296 EricMessageBox.critical( |
|
297 self, |
|
298 self.tr("pyright Type Checking"), |
|
299 self.tr( |
|
300 """pyright type checking has to be performed for individual""" |
|
301 """ files or a project but neither was given. Aborting...""" |
|
302 ), |
|
303 ) |
|
304 return |
|
305 |
|
306 if files and save: |
|
307 self.__dirOrFileList = files |
|
308 |
|
309 interpreter = PythonUtilities.getPythonExecutable() |
|
310 args = ["-m", "pyright", "--outputjson"] |
|
311 |
|
312 pythonPlatform = self.platformComboBox.currentData() |
|
313 if pythonPlatform: |
|
314 args.extend(["--pythonplatform", pythonPlatform]) |
|
315 pythonVersion = self.versionComboBox.currentText() |
|
316 if pythonVersion: |
|
317 args.extend(["--pythonversion", pythonVersion]) |
|
318 if self.skipUnannotatedCheckBox.isChecked(): |
|
319 args.append("--skipunannotated") |
|
320 if self.__forProject: |
|
321 args.extend(["--project", self.__project.getProjectPath()]) |
|
322 args.extend(files) |
|
323 |
|
324 self.__process = QProcess(self) |
|
325 self.__process.readyReadStandardError.connect(self.__readError) |
|
326 self.__process.finished.connect(self.__pyrightProcessFinished) |
|
327 self.__process.start(interpreter, args) |
|
328 |
|
329 @pyqtSlot() |
|
330 def __readError(self): |
|
331 """ |
|
332 Private slot to get the output of the error channel and show it to the user. |
|
333 """ |
|
334 errorMsg = str(self.__process.readAllStandardError(), encoding="utf-8") |
|
335 EricMessageBox.critical( |
|
336 self, |
|
337 self.tr("pyright Type Checking"), |
|
338 self.tr( |
|
339 "<p>The pyright type checking run failed.</p><p>Reason: {0}</p>" |
|
340 ).format(errorMsg), |
|
341 ) |
|
342 |
|
343 @pyqtSlot(int, QProcess.ExitStatus) |
|
344 def __pyrightProcessFinished(self, exitCode, exitStatus): |
|
345 """ |
|
346 Private slot to process the pyright result. |
|
347 |
|
348 @param exitCode exit code of the pyright process |
|
349 @type int |
|
350 @param exitStatus exit status |
|
351 @type QProcess.ExitStatus |
|
352 """ |
|
353 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) |
|
354 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) |
|
355 self.showButton.setEnabled(True) |
|
356 self.startButton.setEnabled(True) |
|
357 self.restartButton.setEnabled(True) |
|
358 |
|
359 if exitStatus != QProcess.ExitStatus.NormalExit: |
|
360 EricMessageBox.critical( |
|
361 self, |
|
362 self.tr("pyright Type Checking"), |
|
363 self.tr( |
|
364 "<p>The pyright type checking process did not end normally.</p>" |
|
365 ), |
|
366 ) |
|
367 return |
|
368 |
|
369 output = str(self.__process.readAllStandardOutput(), encoding="utf-8") |
|
370 try: |
|
371 resultDict = json.loads(output) |
|
372 except json.JSONDecodeError as err: |
|
373 EricMessageBox.critical( |
|
374 self, |
|
375 self.tr("pyright Type Checking"), |
|
376 self.tr( |
|
377 "<p>The pyright type checking process did not return valid" |
|
378 " JSON data.</p><p>Issue: {0}</p>" |
|
379 ).format(str(err)), |
|
380 ) |
|
381 return |
|
382 |
|
383 resultDict["exitCode"] = exitCode |
|
384 self.__processResult(resultDict) |
|
385 |
|
386 @pyqtSlot(QTreeWidgetItem, int) |
|
387 def on_resultList_itemActivated(self, item, column): |
|
388 """ |
|
389 Private slot to handle the activation of an item. |
|
390 |
|
391 @param item reference to the activated item |
|
392 @type QTreeWidgetItem |
|
393 @param column column the item was activated in |
|
394 @type int |
|
395 """ |
|
396 if self.__hasResults and item.parent(): |
|
397 fn = os.path.abspath(item.data(0, self.filenameRole)) |
|
398 start = item.data(0, self.startRole) |
|
399 severity = item.data(0, self.severityRole) |
|
400 |
|
401 vm = ericApp().getObject("ViewManager") |
|
402 vm.openSourceFile(fn, lineno=start["line"] + 1, pos=start["character"] + 1) |
|
403 editor = vm.getOpenEditor(fn) |
|
404 |
|
405 editor.toggleWarning( |
|
406 start["line"] + 1, |
|
407 start["character"] + 1, |
|
408 True, |
|
409 item.text(2), |
|
410 warningType=self.__severityForEditor[severity], |
|
411 ) |
|
412 |
|
413 editor.updateVerticalScrollBar() |
|
414 |
|
415 @pyqtSlot() |
|
416 def on_restartButton_clicked(self): |
|
417 """ |
|
418 Private slot to restart the configured check. |
|
419 """ |
|
420 self.on_startButton_clicked() |
|
421 |
|
422 @pyqtSlot() |
|
423 def on_showButton_clicked(self): |
|
424 """ |
|
425 Private slot to handle the "Show" button press. |
|
426 """ |
|
427 vm = ericApp().getObject("ViewManager") |
|
428 |
|
429 selectedIndexes = [] |
|
430 for index in range(self.resultList.topLevelItemCount()): |
|
431 if self.resultList.topLevelItem(index).isSelected(): |
|
432 selectedIndexes.append(index) |
|
433 if len(selectedIndexes) == 0: |
|
434 selectedIndexes = list(range(self.resultList.topLevelItemCount())) |
|
435 |
|
436 for index in selectedIndexes: |
|
437 itm = self.resultList.topLevelItem(index) |
|
438 fn = os.path.abspath(itm.data(0, self.filenameRole)) |
|
439 vm.openSourceFile(fn, 1) |
|
440 editor = vm.getOpenEditor(fn) |
|
441 self.__clearEditorErrors(editor) |
|
442 for cindex in range(itm.childCount()): |
|
443 citm = itm.child(cindex) |
|
444 start = citm.data(0, self.startRole) |
|
445 severity = citm.data(0, self.severityRole) |
|
446 editor.toggleWarning( |
|
447 start["line"] + 1, |
|
448 start["character"] + 1, |
|
449 True, |
|
450 citm.text(2), |
|
451 warningType=self.__severityForEditor[severity], |
|
452 ) |
|
453 |
|
454 @pyqtSlot() |
|
455 def on_startButton_clicked(self): |
|
456 """ |
|
457 Private slot to start the pyright type checking run. |
|
458 """ |
|
459 if self.__forProject: |
|
460 data = { |
|
461 "PythonPlatform": self.platformComboBox.currentData(), |
|
462 "PythonVersion": self.versionComboBox.currentText(), |
|
463 "SkipUnannotated": self.skipUnannotatedCheckBox.isChecked(), |
|
464 } |
|
465 if json.dumps(data, sort_keys=True) != json.dumps( |
|
466 self.__data, sort_keys=True |
|
467 ): |
|
468 self.__data = data |
|
469 self.__project.setData("CHECKERSPARMS", "PyrightChecker", self.__data) |
|
470 |
|
471 self.__clearErrors() |
|
472 self.__clear() |
|
473 self.start(self.__dirOrFileList) |
|
474 |
|
475 def __clear(self): |
|
476 """ |
|
477 Private method to clear the dialog. |
|
478 """ |
|
479 self.resultList.clear() |
|
480 self.__hasResults = False |
|
481 |
|
482 def __clearErrors(self, files=None): |
|
483 """ |
|
484 Private method to clear all warning markers of open editors to be |
|
485 checked. |
|
486 |
|
487 @param files list of files to be checked (defaults to None |
|
488 @type list of str (optional |
|
489 """ |
|
490 vm = ericApp().getObject("ViewManager") |
|
491 openFiles = vm.getOpenFilenames() |
|
492 if files is not None: |
|
493 # filter out the files checked |
|
494 openFiles = [f for f in openFiles if f in files] |
|
495 for file in openFiles: |
|
496 editor = vm.getOpenEditor(file) |
|
497 try: |
|
498 editor.clearInfoWarnings() |
|
499 editor.clearErrorWarnings() |
|
500 editor.clearCodeWarnings() |
|
501 except AttributeError: |
|
502 # eric before 23.12 |
|
503 editor.clearFlakesWarnings() |
|
504 |
|
505 def __clearEditorErrors(self, editor): |
|
506 """ |
|
507 Private method to clear all warning markers of an editor. |
|
508 |
|
509 @param editor reference to the editor to be cleared |
|
510 @type Editor |
|
511 """ |
|
512 try: |
|
513 editor.clearInfoWarnings() |
|
514 editor.clearErrorWarnings() |
|
515 editor.clearCodeWarnings() |
|
516 except AttributeError: |
|
517 # eric before 23.12 |
|
518 editor.clearFlakesWarnings() |
|
519 |
|
520 ############################################################################ |
|
521 ## Methods for storing, loading and resetting the default values. ## |
|
522 ############################################################################ |
|
523 |
|
524 @pyqtSlot() |
|
525 def on_loadDefaultButton_clicked(self): |
|
526 """ |
|
527 Private slot to load the default configuration values. |
|
528 """ |
|
529 defaultParameters = self.getDefaults() |
|
530 settings = Preferences.getSettings() |
|
531 |
|
532 self.platformComboBox.setCurrentIndex( |
|
533 self.platformComboBox.findData( |
|
534 settings.value( |
|
535 self.__plugin.PreferencesKey + "/PythonPlatform", |
|
536 defaultParameters["PythonPlatform"], |
|
537 ) |
|
538 ) |
|
539 ) |
|
540 self.versionComboBox.setCurrentText( |
|
541 settings.value( |
|
542 self.__plugin.PreferencesKey + "/PythonVersion", |
|
543 defaultParameters["PythonVersion"], |
|
544 ) |
|
545 ) |
|
546 self.skipUnannotatedCheckBox.setChecked( |
|
547 Preferences.toBool( |
|
548 settings.value( |
|
549 self.__plugin.PreferencesKey + "/SkipUnannotated", |
|
550 defaultParameters["SkipUnannotated"], |
|
551 ) |
|
552 ) |
|
553 ) |
|
554 |
|
555 @pyqtSlot() |
|
556 def on_storeDefaultButton_clicked(self): |
|
557 """ |
|
558 Private slot to store the current configuration values as |
|
559 default values. |
|
560 """ |
|
561 settings = Preferences.getSettings() |
|
562 |
|
563 settings.setValue( |
|
564 self.__plugin.PreferencesKey + "/PythonPlatform", |
|
565 self.platformComboBox.currentData(), |
|
566 ) |
|
567 settings.setValue( |
|
568 self.__plugin.PreferencesKey + "/PythonVersion", |
|
569 self.versionComboBox.currentText(), |
|
570 ) |
|
571 settings.setValue( |
|
572 self.__plugin.PreferencesKey + "/SkipUnannotated", |
|
573 self.skipUnannotatedCheckBox.isChecked(), |
|
574 ) |
|
575 |
|
576 @pyqtSlot() |
|
577 def on_resetDefaultButton_clicked(self): |
|
578 """ |
|
579 Private slot to reset the configuration values to their default values. |
|
580 """ |
|
581 defaultParameters = self.getDefaults() |
|
582 settings = Preferences.getSettings() |
|
583 |
|
584 settings.setValue( |
|
585 self.__plugin.PreferencesKey + "/PythonPlatform", |
|
586 defaultParameters["PythonPlatform"], |
|
587 ) |
|
588 settings.setValue( |
|
589 self.__plugin.PreferencesKey + "/PythonVersion", |
|
590 defaultParameters["PythonVersion"], |
|
591 ) |
|
592 settings.setValue( |
|
593 self.__plugin.PreferencesKey + "/SkipUnannotated", |
|
594 defaultParameters["SkipUnannotated"], |
|
595 ) |
|
596 |
|
597 # Update UI with default values |
|
598 self.on_loadDefaultButton_clicked() |
|
599 |
|
600 @pyqtSlot() |
|
601 def on_tomlButton_clicked(self): |
|
602 """ |
|
603 Private slot to generate a TOML snippet of the current configuration. |
|
604 |
|
605 Note: Only non-default values are included in this snippet. |
|
606 |
|
607 The code snippet is copied to the clipboard and may be placed inside the |
|
608 'pyproject.toml' file. |
|
609 """ |
|
610 if not self.__forProject or self.__project is None: |
|
611 EricMessageBox.critical( |
|
612 self, |
|
613 self.tr("Create TOML snippet"), |
|
614 self.tr( |
|
615 "The creation of a 'pyproject.toml' snippet is only available" |
|
616 " when in project mode. Aborting..." |
|
617 ), |
|
618 ) |
|
619 return |
|
620 |
|
621 configDict = self.__getConfigurationDict() |
|
622 |
|
623 pyrightTable = tomlkit.table() |
|
624 for key, value in configDict.items(): |
|
625 pyrightTable[key] = value |
|
626 |
|
627 doc = tomlkit.document() |
|
628 doc["tool"] = tomlkit.table(is_super_table=True) |
|
629 doc["tool"]["pyright"] = pyrightTable |
|
630 |
|
631 QGuiApplication.clipboard().setText(tomlkit.dumps(doc)) |
|
632 |
|
633 EricMessageBox.information( |
|
634 self, |
|
635 self.tr("Create TOML snippet"), |
|
636 self.tr("""The 'pyproject.toml' snippet was copied to the clipboard."""), |
|
637 ) |
|
638 |
|
639 def __getConfigurationDict(self): |
|
640 """ |
|
641 Private method to assemble and return a dictionary containing the entered |
|
642 non-default configuration parameters. |
|
643 |
|
644 The configuration dictionary is amended with some common parameters not |
|
645 accessible via the configuration tab. |
|
646 |
|
647 @return dictionary containing the non-default configuration parameters |
|
648 @rtype dict |
|
649 """ |
|
650 configDict = {} |
|
651 |
|
652 srcDir = self.__project.getProjectData("SOURCESDIR") |
|
653 configDict["include"] = [srcDir] if srcDir else [] |
|
654 |
|
655 configDict["exclude"] = [ |
|
656 "*/node_modules", |
|
657 "**/__pycache__", |
|
658 "**/Ui_*.py", |
|
659 "**/.*", |
|
660 ] |
|
661 |
|
662 pythonVersion = self.versionComboBox.currentText() |
|
663 if pythonVersion: |
|
664 configDict["pythonVersion"] = pythonVersion |
|
665 |
|
666 pythonPlatform = self.platformComboBox.currentData() |
|
667 if pythonPlatform: |
|
668 configDict["pythonPlatform"] = pythonPlatform |
|
669 |
|
670 return configDict |