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