Mon, 19 Sep 2022 19:44:38 +0200
Code Formatting
- added a Project menu entry to just configure the formatting parameters
9214 | 1 | # -*- coding: utf-8 -*- |
2 | ||
3 | # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> | |
4 | # | |
5 | ||
6 | """ | |
7 | Module implementing a dialog to enter the parameters for a Black formatting run. | |
8 | """ | |
9 | ||
10 | import contextlib | |
11 | import copy | |
12 | import pathlib | |
13 | ||
14 | import black | |
15 | import tomlkit | |
16 | ||
17 | from PyQt6.QtCore import pyqtSlot, Qt | |
18 | from PyQt6.QtGui import QFontMetricsF, QGuiApplication | |
19 | from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QListWidgetItem | |
20 | ||
21 | from EricWidgets import EricMessageBox | |
22 | from EricWidgets.EricApplication import ericApp | |
23 | ||
24 | from .Ui_BlackConfigurationDialog import Ui_BlackConfigurationDialog | |
25 | ||
26 | from . import BlackUtilities | |
27 | ||
28 | ||
29 | class BlackConfigurationDialog(QDialog, Ui_BlackConfigurationDialog): | |
30 | """ | |
31 | Class implementing a dialog to enter the parameters for a Black formatting run. | |
32 | """ | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
33 | |
9337
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
34 | def __init__(self, withProject=True, onlyProject=False, parent=None): |
9214 | 35 | """ |
36 | Constructor | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
37 | |
9214 | 38 | @param withProject flag indicating to look for project configurations |
39 | (defaults to True) | |
9337
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
40 | @type bool (optional) |
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
41 | @param onlyProject flag indicating to only look for project configurations |
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
42 | (defaults to False) |
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
43 | @type bool (optional) |
9214 | 44 | @param parent reference to the parent widget (defaults to None) |
45 | @type QWidget (optional) | |
46 | """ | |
47 | super().__init__(parent) | |
48 | self.setupUi(self) | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
49 | |
9337
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
50 | self.__project = ( |
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
51 | ericApp().getObject("Project") if (withProject or onlyProject) else None |
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
52 | ) |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
53 | |
9214 | 54 | indentTabWidth = ( |
55 | QFontMetricsF(self.excludeEdit.font()).horizontalAdvance(" ") * 2 | |
56 | ) | |
57 | self.excludeEdit.document().setIndentWidth(indentTabWidth) | |
58 | self.excludeEdit.setTabStopDistance(indentTabWidth) | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
59 | |
9214 | 60 | self.__pyprojectData = {} |
61 | self.__projectData = {} | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
62 | |
9214 | 63 | self.__tomlButton = self.buttonBox.addButton( |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
64 | self.tr("Generate TOML"), QDialogButtonBox.ButtonRole.ActionRole |
9214 | 65 | ) |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
66 | self.__tomlButton.setToolTip( |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
67 | self.tr("Place a code snippet for 'pyproject.toml' into the clipboard.") |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
68 | ) |
9214 | 69 | self.__tomlButton.clicked.connect(self.__createTomlSnippet) |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
70 | |
9214 | 71 | # setup the source combobox |
72 | self.sourceComboBox.addItem("", "") | |
73 | if self.__project: | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
74 | pyprojectPath = ( |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
75 | pathlib.Path(self.__project.getProjectPath()) / "pyproject.toml" |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
76 | ) |
9214 | 77 | if pyprojectPath.exists(): |
78 | with contextlib.suppress(tomlkit.exceptions.ParseError, OSError): | |
79 | with pyprojectPath.open("r", encoding="utf-8") as f: | |
80 | data = tomlkit.load(f) | |
81 | config = data.get("tool", {}).get("black", {}) | |
82 | if config: | |
83 | self.__pyprojectData = { | |
9222
e384c0e986be
Fixed a bug in the Black configuration dialog causing 'pyproject.toml' values being handled incorrectly.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9221
diff
changeset
|
84 | k.replace("--", ""): v for k, v in config.items() |
9214 | 85 | } |
86 | self.sourceComboBox.addItem("pyproject.toml", "pyproject") | |
87 | if self.__project.getData("OTHERTOOLSPARMS", "Black") is not None: | |
88 | self.__projectData = copy.deepcopy( | |
89 | self.__project.getData("OTHERTOOLSPARMS", "Black") | |
90 | ) | |
91 | self.sourceComboBox.addItem(self.tr("Project File"), "project") | |
9337
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
92 | if not onlyProject: |
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
93 | self.sourceComboBox.addItem(self.tr("Defaults"), "default") |
073b872fce59
Code Formatting
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9222
diff
changeset
|
94 | self.sourceComboBox.addItem(self.tr("Configuration Below"), "dialog") |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
95 | |
9214 | 96 | self.__populateTargetVersionsList() |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
97 | |
9214 | 98 | if self.__projectData: |
99 | source = self.__projectData.get("source", "") | |
100 | self.sourceComboBox.setCurrentIndex(self.sourceComboBox.findData(source)) | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
101 | |
9214 | 102 | def __populateTargetVersionsList(self): |
103 | """ | |
104 | Private method to populate the target versions list widget with checkable | |
105 | Python version entries. | |
106 | """ | |
107 | targets = [ | |
108 | (int(t[2]), int(t[3:]), t) | |
109 | for t in dir(black.TargetVersion) | |
110 | if t.startswith("PY") | |
111 | ] | |
112 | for target in sorted(targets): | |
113 | itm = QListWidgetItem( | |
114 | "Python {0}.{1}".format(target[0], target[1]), self.targetVersionsList | |
115 | ) | |
116 | itm.setData(Qt.ItemDataRole.UserRole, target[2]) | |
117 | itm.setFlags(itm.flags() | Qt.ItemFlag.ItemIsUserCheckable) | |
118 | itm.setCheckState(Qt.CheckState.Unchecked) | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
119 | |
9214 | 120 | def __loadConfiguration(self, configurationDict): |
121 | """ | |
122 | Private method to load the configuration section with data of the given | |
123 | dictionary. | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
124 | |
9214 | 125 | @param configurationDict reference to the data to be loaded |
126 | @type dict | |
127 | """ | |
128 | confDict = copy.deepcopy(BlackUtilities.getDefaultConfiguration()) | |
129 | confDict.update(configurationDict) | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
130 | |
9214 | 131 | self.lineLengthSpinBox.setValue(int(confDict["line-length"])) |
132 | self.skipStringNormalCheckBox.setChecked(confDict["skip-string-normalization"]) | |
133 | self.skipMagicCommaCheckBox.setChecked(confDict["skip-magic-trailing-comma"]) | |
134 | self.excludeEdit.setPlainText(confDict["extend-exclude"]) | |
135 | for row in range(self.targetVersionsList.count()): | |
136 | itm = self.targetVersionsList.item(row) | |
137 | itm.setCheckState( | |
138 | Qt.CheckState.Checked | |
139 | if itm.data(Qt.ItemDataRole.UserRole).lower() | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
140 | in confDict["target-version"] |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
141 | else Qt.CheckState.Unchecked |
9214 | 142 | ) |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
143 | |
9214 | 144 | @pyqtSlot(str) |
145 | def on_sourceComboBox_currentTextChanged(self, selection): | |
146 | """ | |
147 | Private slot to handle the selection of a configuration source. | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
148 | |
9214 | 149 | @param selection text of the currently selected item |
150 | @type str | |
151 | """ | |
152 | self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled( | |
153 | bool(selection) | |
154 | ) | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
155 | |
9214 | 156 | source = self.sourceComboBox.currentData() |
157 | if source == "pyproject": | |
158 | self.__loadConfiguration(self.__pyprojectData) | |
159 | elif source == "project": | |
160 | self.__loadConfiguration(self.__projectData) | |
161 | elif source == "default": | |
162 | self.__loadConfiguration(BlackUtilities.getDefaultConfiguration()) | |
163 | elif source == "dialog": | |
164 | # just leave the current entries | |
165 | pass | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
166 | |
9214 | 167 | @pyqtSlot() |
168 | def on_excludeEdit_textChanged(self): | |
169 | """ | |
170 | Private slot to enable the validate button depending on the exclude text. | |
171 | """ | |
172 | self.validateButton.setEnabled(bool(self.excludeEdit.toPlainText())) | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
173 | |
9214 | 174 | @pyqtSlot() |
175 | def on_validateButton_clicked(self): | |
176 | """ | |
177 | Private slot to validate the entered exclusion regular expression. | |
178 | """ | |
179 | regexp = self.excludeEdit.toPlainText() | |
180 | valid, error = BlackUtilities.validateRegExp(regexp) | |
181 | if valid: | |
182 | EricMessageBox.information( | |
183 | self, | |
184 | self.tr("Validation"), | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
185 | self.tr("""The exclusion expression is valid."""), |
9214 | 186 | ) |
187 | else: | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
188 | EricMessageBox.critical(self, self.tr("Validation Error"), error) |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
189 | |
9214 | 190 | def __getTargetList(self): |
191 | """ | |
192 | Private method to get the list of checked target versions. | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
193 | |
9214 | 194 | @return list of target versions |
195 | @rtype list of str | |
196 | """ | |
197 | targets = [] | |
198 | for row in range(self.targetVersionsList.count()): | |
199 | itm = self.targetVersionsList.item(row) | |
200 | if itm.checkState() == Qt.CheckState.Checked: | |
201 | targets.append(itm.data(Qt.ItemDataRole.UserRole).lower()) | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
202 | |
9214 | 203 | return targets |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
204 | |
9214 | 205 | @pyqtSlot() |
206 | def __createTomlSnippet(self): | |
207 | """ | |
208 | Private slot to generate a TOML snippet of the current configuration. | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
209 | |
9214 | 210 | Note: Only non-default values are included in this snippet. |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
211 | |
9214 | 212 | The code snippet is copied to the clipboard and may be placed inside the |
213 | 'pyproject.toml' file. | |
214 | """ | |
215 | doc = tomlkit.document() | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
216 | |
9214 | 217 | black = tomlkit.table() |
218 | targetList = self.__getTargetList() | |
219 | if targetList: | |
220 | black["target-version"] = targetList | |
221 | black["line-length"] = self.lineLengthSpinBox.value() | |
222 | if self.skipStringNormalCheckBox.isChecked(): | |
223 | black["skip-string-normalization"] = True | |
224 | if self.skipMagicCommaCheckBox.isChecked(): | |
225 | black["skip-magic-trailing-comma"] = True | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
226 | |
9214 | 227 | excludeRegexp = self.excludeEdit.toPlainText() |
228 | if excludeRegexp and BlackUtilities.validateRegExp(excludeRegexp)[0]: | |
229 | black["extend-exclude"] = tomlkit.string( | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
230 | "\n{0}\n".format(excludeRegexp.strip()), literal=True, multiline=True |
9214 | 231 | ) |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
232 | |
9214 | 233 | doc["tool"] = tomlkit.table(is_super_table=True) |
234 | doc["tool"]["black"] = black | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
235 | |
9214 | 236 | QGuiApplication.clipboard().setText(tomlkit.dumps(doc)) |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
237 | |
9214 | 238 | EricMessageBox.information( |
239 | self, | |
9216
e89083501ce3
Updated translations.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9214
diff
changeset
|
240 | self.tr("Create TOML snippet"), |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
241 | self.tr( |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
242 | """The 'pyproject.toml' snippet was copied to the clipboard""" |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
243 | """ successfully.""" |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
244 | ), |
9214 | 245 | ) |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
246 | |
9214 | 247 | def getConfiguration(self): |
248 | """ | |
249 | Public method to get the current configuration parameters. | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
250 | |
9214 | 251 | @return dictionary containing the configuration parameters |
252 | @rtype dict | |
253 | """ | |
254 | configuration = BlackUtilities.getDefaultConfiguration() | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
255 | |
9214 | 256 | configuration["source"] = self.sourceComboBox.currentData() |
257 | configuration["target-version"] = self.__getTargetList() | |
258 | configuration["line-length"] = self.lineLengthSpinBox.value() | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
259 | configuration[ |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
260 | "skip-string-normalization" |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
261 | ] = self.skipStringNormalCheckBox.isChecked() |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
262 | configuration[ |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
263 | "skip-magic-trailing-comma" |
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
264 | ] = self.skipMagicCommaCheckBox.isChecked() |
9214 | 265 | configuration["extend-exclude"] = self.excludeEdit.toPlainText().strip() |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
266 | |
9214 | 267 | if self.__project: |
268 | self.__project.setData("OTHERTOOLSPARMS", "Black", configuration) | |
9221
bf71ee032bb4
Reformatted the source code using the 'Black' utility.
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
9216
diff
changeset
|
269 | |
9214 | 270 | return configuration |