Sat, 23 Dec 2023 15:48:12 +0100
Updated copyright for 2024.
9453 | 1 | # -*- coding: utf-8 -*- |
2 | ||
10439
21c28b0f9e41
Updated copyright for 2024.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
10409
diff
changeset
|
3 | # Copyright (c) 2022 - 2024 Detlev Offenbach <detlev@die-offenbachs.de> |
9453 | 4 | # |
5 | ||
6 | """ | |
7 | Module implementing a dialog showing the isort code formatting progress and the results. | |
8 | """ | |
9 | ||
9475
5c09d70e9290
Replaced a custom function by use of standard functions from contextlib.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9473
diff
changeset
|
10 | import contextlib |
9453 | 11 | import copy |
12 | import io | |
13 | import multiprocessing | |
9475
5c09d70e9290
Replaced a custom function by use of standard functions from contextlib.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9473
diff
changeset
|
14 | import os |
9453 | 15 | import pathlib |
16 | ||
17 | from dataclasses import dataclass | |
18 | ||
9468
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
19 | from isort import settings |
9453 | 20 | from isort.api import check_file, sort_file |
10136
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
21 | from isort.exceptions import ISortError |
9473
3f23dbf37dbe
Resorted the import statements using isort.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9472
diff
changeset
|
22 | from PyQt6.QtCore import QCoreApplication, Qt, pyqtSlot |
9453 | 23 | from PyQt6.QtWidgets import ( |
24 | QAbstractButton, | |
25 | QDialog, | |
26 | QDialogButtonBox, | |
27 | QHeaderView, | |
28 | QTreeWidgetItem, | |
29 | ) | |
30 | ||
31 | from eric7 import Preferences | |
32 | from eric7.EricWidgets import EricMessageBox | |
33 | ||
34 | from .FormattingDiffWidget import FormattingDiffWidget | |
35 | from .IsortFormattingAction import IsortFormattingAction | |
36 | from .Ui_IsortFormattingDialog import Ui_IsortFormattingDialog | |
37 | ||
38 | ||
39 | class IsortFormattingDialog(QDialog, Ui_IsortFormattingDialog): | |
40 | """ | |
41 | Class implementing a dialog showing the isort code formatting progress and the | |
42 | results. | |
43 | """ | |
44 | ||
9460
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
45 | DataRole = Qt.ItemDataRole.UserRole |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
46 | DataTypeRole = Qt.ItemDataRole.UserRole + 1 |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
47 | FileNameRole = Qt.ItemDataRole.UserRole + 2 |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
48 | StatusRole = Qt.ItemDataRole.UserRole + 3 |
9453 | 49 | |
9460
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
50 | FileNameColumn = 1 |
9453 | 51 | StatusColumn = 0 |
52 | ||
53 | def __init__( | |
54 | self, | |
55 | configuration, | |
56 | filesList, | |
57 | project=None, | |
58 | action=IsortFormattingAction.Sort, | |
59 | parent=None, | |
60 | ): | |
61 | """ | |
62 | Constructor | |
63 | ||
64 | @param configuration dictionary containing the configuration parameters | |
65 | @type dict | |
66 | @param filesList list of absolute file paths to be processed | |
67 | @type list of str | |
68 | @param project reference to the project object (defaults to None) | |
69 | @type Project (optional) | |
70 | @param action action to be performed (defaults to IsortFormattingAction.Sort) | |
71 | @type IsortFormattingAction (optional) | |
72 | @param parent reference to the parent widget (defaults to None) | |
73 | @type QWidget (optional) | |
74 | """ | |
75 | super().__init__(parent) | |
76 | self.setupUi(self) | |
77 | ||
78 | self.resultsList.header().setSortIndicator(1, Qt.SortOrder.AscendingOrder) | |
79 | ||
9465
8a020c34dce2
Fine tuned the isort support so the configuration will default to project specific config files overwritten by data entered via the dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9460
diff
changeset
|
80 | self.__project = project |
8a020c34dce2
Fine tuned the isort support so the configuration will default to project specific config files overwritten by data entered via the dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9460
diff
changeset
|
81 | |
9453 | 82 | self.__config = copy.deepcopy(configuration) |
83 | self.__config["quiet"] = True # we don't want extra output | |
84 | self.__config["overwrite_in_place"] = True # we want to overwrite the files | |
85 | if "config_source" in self.__config: | |
86 | del self.__config["config_source"] | |
9465
8a020c34dce2
Fine tuned the isort support so the configuration will default to project specific config files overwritten by data entered via the dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9460
diff
changeset
|
87 | |
8a020c34dce2
Fine tuned the isort support so the configuration will default to project specific config files overwritten by data entered via the dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9460
diff
changeset
|
88 | # Create an isort Config object and pre-load it with parameters contained in |
8a020c34dce2
Fine tuned the isort support so the configuration will default to project specific config files overwritten by data entered via the dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9460
diff
changeset
|
89 | # project specific configuration files (like pyproject.toml). The configuration |
8a020c34dce2
Fine tuned the isort support so the configuration will default to project specific config files overwritten by data entered via the dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9460
diff
changeset
|
90 | # given as a dictionary (i.e. data entered in the configuration dialog) |
8a020c34dce2
Fine tuned the isort support so the configuration will default to project specific config files overwritten by data entered via the dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9460
diff
changeset
|
91 | # overwrites these. If the project is not passed, the isort config is based on |
8a020c34dce2
Fine tuned the isort support so the configuration will default to project specific config files overwritten by data entered via the dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9460
diff
changeset
|
92 | # the isort defaults. |
9468
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
93 | if project: |
10409
8d888f38bf89
Fixed an issue in the "isort" interface caused by a change in isort >= 5.13.0.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
10136
diff
changeset
|
94 | with contextlib.suppress(AttributeError): |
8d888f38bf89
Fixed an issue in the "isort" interface caused by a change in isort >= 5.13.0.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
10136
diff
changeset
|
95 | # for isort < 5.13.0 |
8d888f38bf89
Fixed an issue in the "isort" interface caused by a change in isort >= 5.13.0.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
10136
diff
changeset
|
96 | # clear the caches in order to force a re-read of config files |
8d888f38bf89
Fixed an issue in the "isort" interface caused by a change in isort >= 5.13.0.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
10136
diff
changeset
|
97 | settings._get_config_data.cache_clear() |
8d888f38bf89
Fixed an issue in the "isort" interface caused by a change in isort >= 5.13.0.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
10136
diff
changeset
|
98 | settings._find_config.cache_clear() |
9468
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
99 | try: |
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
100 | self.__isortConfig = ( |
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
101 | settings.Config(settings_path=project.getProjectPath(), **self.__config) |
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
102 | if project |
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
103 | else settings.Config(**self.__config) |
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
104 | ) |
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
105 | except KeyError: |
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
106 | # invalid configuration entry found in some config file; use just the dialog |
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
107 | # parameters |
a4d8091cd8f7
Changed the isort formatting dialog to ensure the cached isort config data is cleared.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9465
diff
changeset
|
108 | self.__isortConfig = settings.Config(**self.__config) |
9465
8a020c34dce2
Fine tuned the isort support so the configuration will default to project specific config files overwritten by data entered via the dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9460
diff
changeset
|
109 | |
9453 | 110 | self.__config["__action__"] = action # needed by the workers |
111 | ||
112 | self.__filesList = filesList[:] | |
113 | ||
114 | self.__diffDialog = None | |
115 | ||
116 | self.__allFilter = self.tr("<all>") | |
117 | ||
118 | self.__sortImportsButton = self.buttonBox.addButton( | |
119 | self.tr("Sort Imports"), QDialogButtonBox.ButtonRole.ActionRole | |
120 | ) | |
121 | self.__sortImportsButton.setVisible(False) | |
122 | ||
123 | self.show() | |
124 | QCoreApplication.processEvents() | |
125 | ||
126 | self.__performAction() | |
127 | ||
128 | def __performAction(self): | |
129 | """ | |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
130 | Private method to execute the requested sorting action. |
9453 | 131 | """ |
9472
5798ee4a8807
Corrected a little glitch in the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9468
diff
changeset
|
132 | self.progressBar.setMaximum(len(self.__filesList)) |
9453 | 133 | self.progressBar.setValue(0) |
134 | self.progressBar.setVisible(True) | |
135 | ||
136 | self.statisticsGroup.setVisible(False) | |
137 | self.__statistics = IsortStatistics() | |
138 | ||
139 | self.__cancelled = False | |
140 | ||
141 | self.statusFilterComboBox.clear() | |
142 | self.resultsList.clear() | |
143 | ||
144 | self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True) | |
145 | self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) | |
146 | self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) | |
147 | ||
148 | files = self.__filterFiles(self.__filesList) | |
149 | if len(files) > 1: | |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
150 | self.__sortManyFiles(files) |
9453 | 151 | elif len(files) == 1: |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
152 | self.__sortOneFile(files[0]) |
9453 | 153 | |
154 | def __filterFiles(self, filesList): | |
155 | """ | |
156 | Private method to filter the given list of files according the | |
157 | configuration parameters. | |
158 | ||
159 | @param filesList list of files | |
160 | @type list of str | |
161 | @return list of filtered files | |
162 | @rtype list of str | |
163 | """ | |
164 | files = [] | |
165 | for file in filesList: | |
166 | if not self.__isortConfig.is_supported_filetype( | |
167 | file | |
168 | ) or self.__isortConfig.is_skipped(pathlib.Path(file)): | |
169 | self.__handleIsortResult(file, "skipped") | |
170 | else: | |
171 | files.append(file) | |
172 | ||
173 | return files | |
174 | ||
175 | def __resort(self): | |
176 | """ | |
177 | Private method to resort the result list. | |
178 | """ | |
179 | self.resultsList.sortItems( | |
180 | self.resultsList.sortColumn(), | |
181 | self.resultsList.header().sortIndicatorOrder(), | |
182 | ) | |
183 | ||
184 | def __resizeColumns(self): | |
185 | """ | |
186 | Private method to resize the columns of the result list. | |
187 | """ | |
188 | self.resultsList.header().resizeSections( | |
189 | QHeaderView.ResizeMode.ResizeToContents | |
190 | ) | |
191 | self.resultsList.header().setStretchLastSection(True) | |
192 | ||
193 | def __populateStatusFilterCombo(self): | |
194 | """ | |
195 | Private method to populate the status filter combo box with allowed selections. | |
196 | """ | |
197 | allowedSelections = set() | |
198 | for row in range(self.resultsList.topLevelItemCount()): | |
199 | allowedSelections.add( | |
200 | self.resultsList.topLevelItem(row).text( | |
201 | IsortFormattingDialog.StatusColumn | |
202 | ) | |
203 | ) | |
204 | ||
205 | self.statusFilterComboBox.addItem(self.__allFilter) | |
206 | self.statusFilterComboBox.addItems(sorted(allowedSelections)) | |
207 | ||
208 | def __finish(self): | |
209 | """ | |
210 | Private method to perform some actions after the run was performed or canceled. | |
211 | """ | |
212 | self.__resort() | |
213 | self.__resizeColumns() | |
214 | ||
215 | self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) | |
216 | self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) | |
217 | self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) | |
218 | ||
219 | self.progressBar.setVisible(False) | |
220 | ||
221 | self.__sortImportsButton.setVisible( | |
222 | self.__config["__action__"] is not IsortFormattingAction.Sort | |
223 | and self.__statistics.changeCount > 0 | |
224 | ) | |
225 | ||
226 | self.__updateStatistics() | |
227 | self.__populateStatusFilterCombo() | |
228 | ||
229 | def __updateStatistics(self): | |
230 | """ | |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
231 | Private method to update the statistics about the recent sorting run and |
9453 | 232 | make them visible. |
233 | """ | |
234 | self.reformattedLabel.setText( | |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
235 | self.tr("Resorted:") |
9453 | 236 | if self.__config["__action__"] is IsortFormattingAction.Sort |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
237 | else self.tr("Would Resort:") |
9453 | 238 | ) |
239 | ||
240 | total = self.progressBar.maximum() | |
241 | ||
242 | self.totalCountLabel.setText("{0:n}".format(total)) | |
243 | self.skippedCountLabel.setText("{0:n}".format(self.__statistics.skippedCount)) | |
244 | self.failuresCountLabel.setText("{0:n}".format(self.__statistics.failureCount)) | |
245 | self.processedCountLabel.setText( | |
246 | "{0:n}".format(self.__statistics.processedCount) | |
247 | ) | |
248 | self.reformattedCountLabel.setText( | |
249 | "{0:n}".format(self.__statistics.changeCount) | |
250 | ) | |
251 | self.unchangedCountLabel.setText("{0:n}".format(self.__statistics.sameCount)) | |
252 | ||
253 | self.statisticsGroup.setVisible(True) | |
254 | ||
255 | @pyqtSlot(QAbstractButton) | |
256 | def on_buttonBox_clicked(self, button): | |
257 | """ | |
258 | Private slot to handle button presses of the dialog buttons. | |
259 | ||
260 | @param button reference to the pressed button | |
261 | @type QAbstractButton | |
262 | """ | |
263 | if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): | |
264 | self.__cancelled = True | |
265 | elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): | |
266 | self.accept() | |
267 | elif button is self.__sortImportsButton: | |
268 | self.__sortImportsButtonClicked() | |
269 | ||
270 | @pyqtSlot() | |
271 | def __sortImportsButtonClicked(self): | |
272 | """ | |
273 | Private slot handling the selection of the 'Sort Imports' button. | |
274 | """ | |
9460
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
275 | files = [] |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
276 | for row in range(self.resultsList.topLevelItemCount()): |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
277 | itm = self.resultsList.topLevelItem(row) |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
278 | if itm.data(0, IsortFormattingDialog.StatusRole) == "changed": |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
279 | files.append(itm.data(0, IsortFormattingDialog.FileNameRole)) |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
280 | if files: |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
281 | self.__filesList = files |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
282 | |
9453 | 283 | self.__config["__action__"] = IsortFormattingAction.Sort |
284 | self.__performAction() | |
285 | ||
286 | @pyqtSlot(QTreeWidgetItem, int) | |
287 | def on_resultsList_itemDoubleClicked(self, item, column): | |
288 | """ | |
289 | Private slot handling a double click of a result item. | |
290 | ||
291 | @param item reference to the double clicked item | |
292 | @type QTreeWidgetItem | |
293 | @param column column number that was double clicked | |
294 | @type int | |
295 | """ | |
296 | dataType = item.data(0, IsortFormattingDialog.DataTypeRole) | |
297 | if dataType == "error": | |
298 | EricMessageBox.critical( | |
299 | self, | |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
300 | self.tr("Imports Sorting Failure"), |
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
301 | self.tr( |
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
302 | "<p>Imports sorting failed due to this error.</p><p>{0}</p>" |
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
303 | ).format(item.data(0, IsortFormattingDialog.DataRole)), |
9453 | 304 | ) |
305 | elif dataType == "diff": | |
306 | if self.__diffDialog is None: | |
307 | self.__diffDialog = FormattingDiffWidget() | |
308 | self.__diffDialog.showDiff(item.data(0, IsortFormattingDialog.DataRole)) | |
309 | ||
310 | @pyqtSlot(str) | |
311 | def on_statusFilterComboBox_currentTextChanged(self, status): | |
312 | """ | |
313 | Private slot handling the selection of a status for items to be shown. | |
314 | ||
315 | @param status selected status | |
316 | @type str | |
317 | """ | |
318 | for row in range(self.resultsList.topLevelItemCount()): | |
319 | itm = self.resultsList.topLevelItem(row) | |
320 | itm.setHidden( | |
321 | status != self.__allFilter | |
322 | and itm.text(IsortFormattingDialog.StatusColumn) != status | |
323 | ) | |
324 | ||
325 | def closeEvent(self, evt): | |
326 | """ | |
327 | Protected slot implementing a close event handler. | |
328 | ||
329 | @param evt reference to the close event | |
330 | @type QCloseEvent | |
331 | """ | |
332 | if self.__diffDialog is not None: | |
333 | self.__diffDialog.close() | |
334 | evt.accept() | |
335 | ||
336 | def __handleIsortResult(self, filename, status, data=""): | |
337 | """ | |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
338 | Private method to handle an isort sorting result. |
9453 | 339 | |
340 | @param filename name of the processed file | |
341 | @type str | |
342 | @param status status of the performed action (one of 'changed', 'failed', | |
343 | 'skipped' or 'unchanged') | |
344 | @type str | |
345 | @param data action data (error message or unified diff) (defaults to "") | |
346 | @type str (optional) | |
347 | """ | |
348 | isError = False | |
349 | ||
350 | if status == "changed": | |
351 | statusMsg = ( | |
352 | self.tr("would resort") | |
353 | if self.__config["__action__"] | |
354 | in (IsortFormattingAction.Check, IsortFormattingAction.Diff) | |
355 | else self.tr("resorted") | |
356 | ) | |
357 | self.__statistics.changeCount += 1 | |
358 | ||
359 | elif status == "unchanged": | |
360 | statusMsg = self.tr("unchanged") | |
361 | self.__statistics.sameCount += 1 | |
362 | ||
363 | elif status == "skipped": | |
364 | statusMsg = self.tr("skipped") | |
365 | self.__statistics.skippedCount += 1 | |
366 | ||
367 | elif status == "failed": | |
368 | statusMsg = self.tr("failed") | |
369 | self.__statistics.failureCount += 1 | |
370 | isError = True | |
371 | ||
372 | elif status == "unsupported": | |
373 | statusMsg = self.tr("error") | |
374 | data = self.tr("Unsupported 'isort' action ({0}) given.").format( | |
375 | self.__config["__action__"] | |
376 | ) | |
377 | self.__statistics.failureCount += 1 | |
378 | isError = True | |
379 | ||
380 | else: | |
381 | statusMsg = self.tr("invalid status ({0})").format(status) | |
382 | self.__statistics.failureCount += 1 | |
383 | isError = True | |
384 | ||
385 | if status != "skipped": | |
386 | self.__statistics.processedCount += 1 | |
387 | ||
9460
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
388 | itm = QTreeWidgetItem( |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
389 | self.resultsList, |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
390 | [ |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
391 | statusMsg, |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
392 | self.__project.getRelativePath(filename) |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
393 | if self.__project |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
394 | else filename, |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
395 | ], |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
396 | ) |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
397 | itm.setData(0, IsortFormattingDialog.StatusRole, status) |
0d1b5d0fd815
Modified the Black and Isort dialogs such, that only files that need a change are processed after a diff or check run by pressing the relevant button.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9455
diff
changeset
|
398 | itm.setData(0, IsortFormattingDialog.FileNameRole, filename) |
9453 | 399 | if data: |
400 | itm.setData( | |
401 | 0, IsortFormattingDialog.DataTypeRole, "error" if isError else "diff" | |
402 | ) | |
403 | itm.setData(0, IsortFormattingDialog.DataRole, data) | |
404 | ||
405 | self.progressBar.setValue(self.progressBar.value() + 1) | |
406 | ||
407 | QCoreApplication.processEvents() | |
408 | ||
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
409 | def __sortManyFiles(self, files): |
9453 | 410 | """ |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
411 | Private method to sort imports of the list of files according the configuration |
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
412 | using multiple processes in parallel. |
9453 | 413 | |
414 | @param files list of files to be processed | |
415 | @type list of str | |
416 | """ | |
417 | maxProcesses = Preferences.getUI("BackgroundServiceProcesses") | |
418 | if maxProcesses == 0: | |
419 | # determine based on CPU count | |
420 | try: | |
421 | NumberOfProcesses = multiprocessing.cpu_count() | |
422 | if NumberOfProcesses >= 1: | |
423 | NumberOfProcesses -= 1 | |
424 | except NotImplementedError: | |
425 | NumberOfProcesses = 1 | |
426 | else: | |
427 | NumberOfProcesses = maxProcesses | |
428 | ||
429 | # Create queues | |
430 | taskQueue = multiprocessing.Queue() | |
431 | doneQueue = multiprocessing.Queue() | |
432 | ||
433 | # Submit tasks (initially two times the number of processes) | |
434 | tasks = len(files) | |
435 | initialTasks = min(2 * NumberOfProcesses, tasks) | |
436 | for _ in range(initialTasks): | |
437 | file = files.pop(0) | |
438 | taskQueue.put((file, self.__config["__action__"])) | |
439 | ||
440 | # Start worker processes | |
441 | workers = [ | |
442 | multiprocessing.Process( | |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
443 | target=self.sortingWorkerTask, |
9453 | 444 | args=(taskQueue, doneQueue, self.__isortConfig), |
445 | ) | |
446 | for _ in range(NumberOfProcesses) | |
447 | ] | |
448 | for worker in workers: | |
449 | worker.start() | |
450 | ||
451 | # Get the results from the worker tasks | |
452 | for _ in range(tasks): | |
453 | result = doneQueue.get() | |
454 | self.__handleIsortResult(result.filename, result.status, data=result.data) | |
455 | ||
456 | if self.__cancelled: | |
457 | break | |
458 | ||
459 | if files: | |
460 | file = files.pop(0) | |
461 | taskQueue.put((file, self.__config["__action__"])) | |
462 | ||
463 | # Tell child processes to stop | |
464 | for _ in range(NumberOfProcesses): | |
465 | taskQueue.put("STOP") | |
466 | ||
467 | for worker in workers: | |
468 | worker.join() | |
469 | worker.close() | |
470 | ||
471 | taskQueue.close() | |
472 | doneQueue.close() | |
473 | ||
474 | self.__finish() | |
475 | ||
476 | @staticmethod | |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
477 | def sortingWorkerTask(inputQueue, outputQueue, isortConfig): |
9453 | 478 | """ |
479 | Static method acting as the parallel worker for the formatting task. | |
480 | ||
481 | @param inputQueue input queue | |
482 | @type multiprocessing.Queue | |
483 | @param outputQueue output queue | |
484 | @type multiprocessing.Queue | |
485 | @param isortConfig config object for isort | |
486 | @type isort.Config | |
487 | """ | |
488 | for file, action in iter(inputQueue.get, "STOP"): | |
489 | if action == IsortFormattingAction.Diff: | |
490 | result = IsortFormattingDialog.__isortCheckFile( | |
491 | file, | |
492 | isortConfig, | |
493 | withDiff=True, | |
494 | ) | |
495 | ||
496 | elif action == IsortFormattingAction.Sort: | |
497 | result = IsortFormattingDialog.__isortSortFile( | |
498 | file, | |
499 | isortConfig, | |
500 | ) | |
501 | ||
502 | else: | |
503 | result = IsortResult( | |
504 | status="unsupported", | |
505 | filename=file, | |
506 | ) | |
507 | ||
508 | outputQueue.put(result) | |
509 | ||
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
510 | def __sortOneFile(self, file): |
9453 | 511 | """ |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
512 | Private method to sort the imports of the list of files according the |
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
513 | configuration. |
9453 | 514 | |
515 | @param file name of the file to be processed | |
516 | @type str | |
517 | """ | |
518 | if self.__config["__action__"] == IsortFormattingAction.Diff: | |
519 | result = IsortFormattingDialog.__isortCheckFile( | |
520 | file, | |
521 | self.__isortConfig, | |
522 | withDiff=True, | |
523 | ) | |
524 | ||
525 | elif self.__config["__action__"] == IsortFormattingAction.Sort: | |
526 | result = IsortFormattingDialog.__isortSortFile( | |
527 | file, | |
528 | self.__isortConfig, | |
529 | ) | |
530 | ||
531 | else: | |
532 | result = IsortResult( | |
533 | status="unsupported", | |
534 | filename=file, | |
535 | ) | |
536 | ||
537 | self.__handleIsortResult(result.filename, result.status, data=result.data) | |
538 | ||
539 | self.__finish() | |
540 | ||
541 | @staticmethod | |
542 | def __isortCheckFile(filename, isortConfig, withDiff=True): | |
543 | """ | |
544 | Static method to check, if a file's import statements need to be changed. | |
545 | ||
546 | @param filename name of the file to be processed | |
547 | @type str | |
548 | @param isortConfig config object for isort | |
549 | @type isort.Config | |
550 | @param withDiff flag indicating to return a unified diff, if the file needs to | |
551 | be changed (defaults to True) | |
552 | @type bool (optional) | |
553 | @return result object | |
554 | @rtype IsortResult | |
555 | """ | |
10136
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
556 | try: |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
557 | diffIO = io.StringIO() if withDiff else False |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
558 | with open(os.devnull, "w") as devnull, contextlib.redirect_stderr(devnull): |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
559 | ok = check_file( |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
560 | filename, |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
561 | show_diff=diffIO, |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
562 | config=isortConfig, |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
563 | ) |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
564 | if withDiff: |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
565 | data = "" if ok else diffIO.getvalue() |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
566 | diffIO.close() |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
567 | else: |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
568 | data = "" |
9453 | 569 | |
10136
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
570 | status = "unchanged" if ok else "changed" |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
571 | except ISortError as err: |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
572 | status = "failed" |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
573 | data = str(err) |
9453 | 574 | |
575 | return IsortResult(status=status, filename=filename, data=data) | |
576 | ||
577 | @staticmethod | |
578 | def __isortSortFile(filename, isortConfig): | |
579 | """ | |
580 | Static method to sort the import statements of a file. | |
581 | ||
582 | @param filename name of the file to be processed | |
583 | @type str | |
584 | @param isortConfig config object for isort | |
585 | @type isort.Config | |
586 | @return result object | |
587 | @rtype IsortResult | |
588 | """ | |
10136
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
589 | try: |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
590 | with open(os.devnull, "w") as devnull, contextlib.redirect_stderr(devnull): |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
591 | ok = sort_file( |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
592 | filename, |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
593 | config=isortConfig, |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
594 | ask_to_apply=False, |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
595 | write_to_stdout=False, |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
596 | show_diff=False, |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
597 | ) |
9453 | 598 | |
10136
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
599 | status = "changed" if ok else "unchanged" |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
600 | data = "" |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
601 | except ISortError as err: |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
602 | status = "failed" |
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
603 | data = str(err) |
9453 | 604 | |
10136
ae09a4a9b0d1
Improved the error handling of the isort formatting dialog.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9653
diff
changeset
|
605 | return IsortResult(status=status, filename=filename, data=data) |
9453 | 606 | |
607 | ||
608 | @dataclass | |
609 | class IsortStatistics: | |
610 | """ | |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
611 | Class containing the isort statistic data. |
9453 | 612 | """ |
613 | ||
614 | skippedCount: int = 0 | |
615 | changeCount: int = 0 | |
616 | sameCount: int = 0 | |
617 | failureCount: int = 0 | |
618 | processedCount: int = 0 | |
619 | ||
620 | ||
621 | @dataclass | |
622 | class IsortResult: | |
623 | """ | |
9455
5f138ee215a5
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9453
diff
changeset
|
624 | Class containing the isort result data. |
9453 | 625 | """ |
626 | ||
627 | status: str = "" | |
628 | filename: str = "" | |
629 | data: str = "" |