PluginPyright.py

changeset 1
191e9ec72893
parent 0
1b1bf094c013
child 3
109b8e5513d8
equal deleted inserted replaced
0:1b1bf094c013 1:191e9ec72893
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the pyright plug-in.
8 """
9
10 import contextlib
11 import os
12
13 from PyQt6.QtCore import QObject, QTranslator, pyqtSlot
14
15 from eric7 import Preferences
16 from eric7.EricGui.EricAction import EricAction
17 from eric7.EricWidgets.EricApplication import ericApp
18 from eric7.Project.ProjectBrowserModel import ProjectBrowserFileItem
19
20 # Start-Of-Header
21 __header__ = {
22 "name": "Python Typing Checker Plug-in",
23 "author": "Detlev Offenbach <detlev@die-offenbachs.de>",
24 "autoactivate": True,
25 "deactivateable": True,
26 "version": "10.0.0",
27 "className": "PyrightPlugin",
28 "packageName": "PyrightChecker",
29 "shortDescription": "Plug-in to check Python sources for typing issues.",
30 "longDescription": ("""Plug-in to check Python sources for typing issues."""),
31 "needsRestart": False,
32 "pyqtApi": 2,
33 }
34 # End-Of-Header
35
36 error = "" # noqa: U200
37
38
39 def prepareUninstall():
40 """
41 Module function to prepare for an uninstallation.
42 """
43 Preferences.Prefs.settings.remove(PyrightPlugin.PreferencesKey)
44
45
46 class PyrightPlugin(QObject):
47 """
48 Class documentation goes here.
49 """
50
51 PreferencesKey = "Pyright"
52
53 def __init__(self, ui):
54 """
55 Constructor
56
57 @param ui reference to the user interface object
58 @type UI.UserInterface
59 """
60 super().__init__(ui)
61 self.__ui = ui
62 self.__initialize()
63
64 def __initialize(self):
65 """
66 Private slot to (re)initialize the plug-in.
67 """
68 self.__projectAct = None
69 self.__projectPyrightCheckerDialog = None
70
71 self.__projectBrowserAct = None
72 self.__projectBrowserMenu = None
73 self.__projectBrowserPyrightCheckerDialog = None
74
75 self.__editors = []
76 self.__editorAct = None
77 self.__editorPyrightCheckerDialog = None
78
79 def activate(self):
80 """
81 Public method to activate this plug-in.
82
83 @return tuple of None and activation status
84 @rtype bool
85 """
86 global error
87 error = "" # clear previous error
88
89 menu = ericApp().getObject("Project").getMenu("Checks")
90 if menu:
91 self.__projectAct = EricAction(
92 self.tr("Static Type Check"),
93 self.tr("Static &Typing..."),
94 0,
95 0,
96 self,
97 "project_check_pyright",
98 )
99 self.__projectAct.setStatusTip(self.tr("Check for typing issues"))
100 self.__projectAct.setWhatsThis(
101 self.tr(
102 """<b>Static Type Check...</b>"""
103 """<p>This checks a Python project for static typing issues.</p>"""
104 )
105 )
106 self.__projectAct.triggered.connect(self.__projectPyrightCheck)
107 ericApp().getObject("Project").addEricActions([self.__projectAct])
108 menu.addAction(self.__projectAct)
109
110 self.__editorAct = EricAction(
111 self.tr("Static Type Check"), self.tr("Static &Typing..."), 0, 0, self, ""
112 )
113 self.__editorAct.setWhatsThis(
114 self.tr(
115 """<b>Static Type Check...</b>"""
116 """<p>This checks a Python file for static typing issues.</p>"""
117 )
118 )
119 self.__editorAct.triggered.connect(self.__editorPyrightCheck)
120
121 ericApp().getObject("Project").showMenu.connect(self.__projectShowMenu)
122 ericApp().getObject("ProjectBrowser").getProjectBrowser(
123 "sources"
124 ).showMenu.connect(self.__projectBrowserShowMenu)
125 ericApp().getObject("ViewManager").editorOpenedEd.connect(self.__editorOpened)
126 ericApp().getObject("ViewManager").editorClosedEd.connect(self.__editorClosed)
127
128 for editor in ericApp().getObject("ViewManager").getOpenEditors():
129 self.__editorOpened(editor)
130
131 return None, True
132
133 def deactivate(self):
134 """
135 Public method to deactivate this plug-in.
136 """
137 ericApp().getObject("Project").showMenu.disconnect(self.__projectShowMenu)
138 ericApp().getObject("ProjectBrowser").getProjectBrowser(
139 "sources"
140 ).showMenu.disconnect(self.__projectBrowserShowMenu)
141
142 menu = ericApp().getObject("Project").getMenu("Checks")
143 if menu is not None and self.__projectAct is not None:
144 menu.removeAction(self.__projectAct)
145 ericApp().getObject("Project").removeEricActions([self.__projectAct])
146
147 if self.__projectBrowserMenu and self.__projectBrowserAct:
148 self.__projectBrowserMenu.removeAction(self.__projectBrowserAct)
149
150 for editor in self.__editors:
151 editor.showMenu.disconnect(self.__editorShowMenu)
152 menu = editor.getMenu("Checks")
153 if menu is not None:
154 menu.removeAction(self.__editorAct)
155
156 self.__initialize()
157
158 def __loadTranslator(self):
159 """
160 Private method to load the translation file.
161 """
162 if self.__ui is not None:
163 loc = self.__ui.getLocale()
164 if loc and loc != "C":
165 locale_dir = os.path.join(
166 os.path.dirname(__file__), "PyrightChecker", "i18n"
167 )
168 translation = "pyright_{0}".format(loc)
169 translator = QTranslator(None)
170 loaded = translator.load(translation, locale_dir)
171 if loaded:
172 self.__translator = translator
173 ericApp().installTranslator(self.__translator)
174 else:
175 print(
176 "Warning: translation file '{0}' could not be"
177 " loaded.".format(translation)
178 )
179 print("Using default.")
180
181 def __projectShowMenu(
182 self,
183 menuName,
184 menu, # noqa: U100
185 ):
186 """
187 Private slot called, when the the project menu or a submenu is
188 about to be shown.
189
190 @param menuName name of the menu to be shown
191 @type str
192 @param menu reference to the menu
193 @type QMenu
194 """
195 if menuName == "Check" and self.__projectAct is not None:
196 self.__projectAct.setEnabled(
197 ericApp().getObject("Project").getProjectLanguage() == "Python3"
198 )
199
200 def __projectBrowserShowMenu(self, menuName, menu):
201 """
202 Private slot called, when the the project browser menu or a submenu is
203 about to be shown.
204
205 @param menuName name of the menu to be shown
206 @type str
207 @param menu reference to the menu
208 @type QMenu
209 """
210 if (
211 menuName == "Checks"
212 and ericApp().getObject("Project").getProjectLanguage() == "Python3"
213 ):
214 self.__projectBrowserMenu = menu
215 if self.__projectBrowserAct is None:
216 self.__projectBrowserAct = EricAction(
217 self.tr("Static Type Check"),
218 self.tr("Static &Typing..."),
219 0,
220 0,
221 self,
222 "",
223 )
224 self.__projectBrowserAct.setWhatsThis(
225 self.tr(
226 """<b>Static Type Check...</b>"""
227 """<p>This checks Python files for static typing issues.</p>"""
228 )
229 )
230 self.__projectBrowserAct.triggered.connect(
231 self.__projectBrowserPyrightCheck
232 )
233 if self.__projectBrowserAct not in menu.actions():
234 menu.addAction(self.__projectBrowserAct)
235
236 @pyqtSlot()
237 def __projectPyrightCheck(self):
238 """
239 Private slot used to check the project for static typing issues.
240 """
241 from PyrightChecker.PyrightCheckerDialog import PyrightCheckerDialog
242
243 project = ericApp().getObject("Project")
244 project.saveAllScripts()
245
246 if self.__projectPyrightCheckerDialog is None:
247 self.__projectPyrightCheckerDialog = PyrightCheckerDialog(self)
248 self.__projectPyrightCheckerDialog.show()
249 self.__projectPyrightCheckerDialog.prepare(project)
250
251 @pyqtSlot()
252 def __projectBrowserPyrightCheck(self):
253 """
254 Private method to handle the static typing check context menu action of
255 the project sources browser.
256 """
257 from PyrightChecker.PyrightCheckerDialog import PyrightCheckerDialog
258
259 browser = ericApp().getObject("ProjectBrowser").getProjectBrowser("sources")
260 if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1:
261 fn = []
262 for itm in browser.getSelectedItems([ProjectBrowserFileItem]):
263 fn.append(itm.fileName())
264 else:
265 itm = browser.model().item(browser.currentIndex())
266 try:
267 fn = [itm.fileName()]
268 except AttributeError:
269 fn = [itm.dirName()]
270
271 self.__projectBrowserPyrightCheckerDialog = PyrightCheckerDialog(self)
272 self.__projectBrowserPyrightCheckerDialog.show()
273 self.__projectBrowserPyrightCheckerDialog.start(fn, save=True)
274
275 def __editorOpened(self, editor):
276 """
277 Private slot called, when a new editor was opened.
278
279 @param editor reference to the new editor
280 @type QScintilla.Editor
281 """
282 menu = editor.getMenu("Checks")
283 if menu is not None:
284 menu.addAction(self.__editorAct)
285 editor.showMenu.connect(self.__editorShowMenu)
286 self.__editors.append(editor)
287
288 def __editorClosed(self, editor):
289 """
290 Private slot called, when an editor was closed.
291
292 @param editor reference to the editor
293 @type QScintilla.Editor
294 """
295 with contextlib.suppress(ValueError):
296 self.__editors.remove(editor)
297
298 def __editorShowMenu(self, menuName, menu, editor):
299 """
300 Private slot called, when the the editor context menu or a submenu is
301 about to be shown.
302
303 @param menuName name of the menu to be shown
304 @type str
305 @param menu reference to the menu
306 @type QMenu
307 @param editor reference to the editor
308 @type QScintilla.Editor
309 """
310 if menuName == "Checks":
311 if self.__editorAct not in menu.actions():
312 menu.addAction(self.__editorAct)
313 self.__editorAct.setEnabled(editor.isPyFile())
314
315 def __editorPyrightCheck(self):
316 """
317 Private slot to handle the code style check context menu action
318 of the editors.
319 """
320 from PyrightChecker.PyrightCheckerDialog import PyrightCheckerDialog
321
322 editor = ericApp().getObject("ViewManager").activeWindow()
323 if (
324 editor is not None
325 and editor.checkDirty()
326 and editor.getFileName() is not None
327 ):
328 self.__editorPyrightCheckerDialog = PyrightCheckerDialog(self)
329 self.__editorPyrightCheckerDialog.show()
330 self.__editorPyrightCheckerDialog.start([editor.getFileName()], save=True)
331
332
333 def installDependencies(pipInstall):
334 """
335 Function to install dependencies of this plug-in.
336
337 @param pipInstall function to be called with a list of package names.
338 @type function
339 """
340 try:
341 import pyright # __IGNORE_WARNING__
342 except ImportError:
343 pipInstall(["pyright"])
344 try:
345 import tomlkit # __IGNORE_WARNING__
346 except ImportError:
347 pipInstall(["tomlkit"])
348
349
350 #
351 # eflag: noqa = M801

eric ide

mercurial