PluginPyright.py

changeset 1
191e9ec72893
parent 0
1b1bf094c013
child 3
109b8e5513d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PluginPyright.py	Tue Nov 07 15:17:48 2023 +0100
@@ -0,0 +1,351 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the pyright plug-in.
+"""
+
+import contextlib
+import os
+
+from PyQt6.QtCore import QObject, QTranslator, pyqtSlot
+
+from eric7 import Preferences
+from eric7.EricGui.EricAction import EricAction
+from eric7.EricWidgets.EricApplication import ericApp
+from eric7.Project.ProjectBrowserModel import ProjectBrowserFileItem
+
+# Start-Of-Header
+__header__ = {
+    "name": "Python Typing Checker Plug-in",
+    "author": "Detlev Offenbach <detlev@die-offenbachs.de>",
+    "autoactivate": True,
+    "deactivateable": True,
+    "version": "10.0.0",
+    "className": "PyrightPlugin",
+    "packageName": "PyrightChecker",
+    "shortDescription": "Plug-in to check Python sources for typing issues.",
+    "longDescription": ("""Plug-in to check Python sources for typing issues."""),
+    "needsRestart": False,
+    "pyqtApi": 2,
+}
+# End-Of-Header
+
+error = ""  # noqa: U200
+
+
+def prepareUninstall():
+    """
+    Module function to prepare for an uninstallation.
+    """
+    Preferences.Prefs.settings.remove(PyrightPlugin.PreferencesKey)
+
+
+class PyrightPlugin(QObject):
+    """
+    Class documentation goes here.
+    """
+
+    PreferencesKey = "Pyright"
+
+    def __init__(self, ui):
+        """
+        Constructor
+
+        @param ui reference to the user interface object
+        @type UI.UserInterface
+        """
+        super().__init__(ui)
+        self.__ui = ui
+        self.__initialize()
+
+    def __initialize(self):
+        """
+        Private slot to (re)initialize the plug-in.
+        """
+        self.__projectAct = None
+        self.__projectPyrightCheckerDialog = None
+
+        self.__projectBrowserAct = None
+        self.__projectBrowserMenu = None
+        self.__projectBrowserPyrightCheckerDialog = None
+
+        self.__editors = []
+        self.__editorAct = None
+        self.__editorPyrightCheckerDialog = None
+
+    def activate(self):
+        """
+        Public method to activate this plug-in.
+
+        @return tuple of None and activation status
+        @rtype bool
+        """
+        global error
+        error = ""  # clear previous error
+
+        menu = ericApp().getObject("Project").getMenu("Checks")
+        if menu:
+            self.__projectAct = EricAction(
+                self.tr("Static Type Check"),
+                self.tr("Static &Typing..."),
+                0,
+                0,
+                self,
+                "project_check_pyright",
+            )
+            self.__projectAct.setStatusTip(self.tr("Check for typing issues"))
+            self.__projectAct.setWhatsThis(
+                self.tr(
+                    """<b>Static Type Check...</b>"""
+                    """<p>This checks a Python project for static typing issues.</p>"""
+                )
+            )
+            self.__projectAct.triggered.connect(self.__projectPyrightCheck)
+            ericApp().getObject("Project").addEricActions([self.__projectAct])
+            menu.addAction(self.__projectAct)
+
+        self.__editorAct = EricAction(
+            self.tr("Static Type Check"), self.tr("Static &Typing..."), 0, 0, self, ""
+        )
+        self.__editorAct.setWhatsThis(
+            self.tr(
+                """<b>Static Type Check...</b>"""
+                """<p>This checks a Python file for static typing issues.</p>"""
+            )
+        )
+        self.__editorAct.triggered.connect(self.__editorPyrightCheck)
+
+        ericApp().getObject("Project").showMenu.connect(self.__projectShowMenu)
+        ericApp().getObject("ProjectBrowser").getProjectBrowser(
+            "sources"
+        ).showMenu.connect(self.__projectBrowserShowMenu)
+        ericApp().getObject("ViewManager").editorOpenedEd.connect(self.__editorOpened)
+        ericApp().getObject("ViewManager").editorClosedEd.connect(self.__editorClosed)
+
+        for editor in ericApp().getObject("ViewManager").getOpenEditors():
+            self.__editorOpened(editor)
+
+        return None, True
+
+    def deactivate(self):
+        """
+        Public method to deactivate this plug-in.
+        """
+        ericApp().getObject("Project").showMenu.disconnect(self.__projectShowMenu)
+        ericApp().getObject("ProjectBrowser").getProjectBrowser(
+            "sources"
+        ).showMenu.disconnect(self.__projectBrowserShowMenu)
+
+        menu = ericApp().getObject("Project").getMenu("Checks")
+        if menu is not None and self.__projectAct is not None:
+            menu.removeAction(self.__projectAct)
+            ericApp().getObject("Project").removeEricActions([self.__projectAct])
+
+        if self.__projectBrowserMenu and self.__projectBrowserAct:
+            self.__projectBrowserMenu.removeAction(self.__projectBrowserAct)
+
+        for editor in self.__editors:
+            editor.showMenu.disconnect(self.__editorShowMenu)
+            menu = editor.getMenu("Checks")
+            if menu is not None:
+                menu.removeAction(self.__editorAct)
+
+        self.__initialize()
+
+    def __loadTranslator(self):
+        """
+        Private method to load the translation file.
+        """
+        if self.__ui is not None:
+            loc = self.__ui.getLocale()
+            if loc and loc != "C":
+                locale_dir = os.path.join(
+                    os.path.dirname(__file__), "PyrightChecker", "i18n"
+                )
+                translation = "pyright_{0}".format(loc)
+                translator = QTranslator(None)
+                loaded = translator.load(translation, locale_dir)
+                if loaded:
+                    self.__translator = translator
+                    ericApp().installTranslator(self.__translator)
+                else:
+                    print(
+                        "Warning: translation file '{0}' could not be"
+                        " loaded.".format(translation)
+                    )
+                    print("Using default.")
+
+    def __projectShowMenu(
+        self,
+        menuName,
+        menu,  # noqa: U100
+    ):
+        """
+        Private slot called, when the the project menu or a submenu is
+        about to be shown.
+
+        @param menuName name of the menu to be shown
+        @type str
+        @param menu reference to the menu
+        @type QMenu
+        """
+        if menuName == "Check" and self.__projectAct is not None:
+            self.__projectAct.setEnabled(
+                ericApp().getObject("Project").getProjectLanguage() == "Python3"
+            )
+
+    def __projectBrowserShowMenu(self, menuName, menu):
+        """
+        Private slot called, when the the project browser menu or a submenu is
+        about to be shown.
+
+        @param menuName name of the menu to be shown
+        @type str
+        @param menu reference to the menu
+        @type QMenu
+        """
+        if (
+            menuName == "Checks"
+            and ericApp().getObject("Project").getProjectLanguage() == "Python3"
+        ):
+            self.__projectBrowserMenu = menu
+            if self.__projectBrowserAct is None:
+                self.__projectBrowserAct = EricAction(
+                    self.tr("Static Type Check"),
+                    self.tr("Static &Typing..."),
+                    0,
+                    0,
+                    self,
+                    "",
+                )
+                self.__projectBrowserAct.setWhatsThis(
+                    self.tr(
+                        """<b>Static Type Check...</b>"""
+                        """<p>This checks Python files for static typing issues.</p>"""
+                    )
+                )
+                self.__projectBrowserAct.triggered.connect(
+                    self.__projectBrowserPyrightCheck
+                )
+            if self.__projectBrowserAct not in menu.actions():
+                menu.addAction(self.__projectBrowserAct)
+
+    @pyqtSlot()
+    def __projectPyrightCheck(self):
+        """
+        Private slot used to check the project for static typing issues.
+        """
+        from PyrightChecker.PyrightCheckerDialog import PyrightCheckerDialog
+
+        project = ericApp().getObject("Project")
+        project.saveAllScripts()
+
+        if self.__projectPyrightCheckerDialog is None:
+            self.__projectPyrightCheckerDialog = PyrightCheckerDialog(self)
+        self.__projectPyrightCheckerDialog.show()
+        self.__projectPyrightCheckerDialog.prepare(project)
+
+    @pyqtSlot()
+    def __projectBrowserPyrightCheck(self):
+        """
+        Private method to handle the static typing check context menu action of
+        the project sources browser.
+        """
+        from PyrightChecker.PyrightCheckerDialog import PyrightCheckerDialog
+
+        browser = ericApp().getObject("ProjectBrowser").getProjectBrowser("sources")
+        if browser.getSelectedItemsCount([ProjectBrowserFileItem]) > 1:
+            fn = []
+            for itm in browser.getSelectedItems([ProjectBrowserFileItem]):
+                fn.append(itm.fileName())
+        else:
+            itm = browser.model().item(browser.currentIndex())
+            try:
+                fn = [itm.fileName()]
+            except AttributeError:
+                fn = [itm.dirName()]
+
+        self.__projectBrowserPyrightCheckerDialog = PyrightCheckerDialog(self)
+        self.__projectBrowserPyrightCheckerDialog.show()
+        self.__projectBrowserPyrightCheckerDialog.start(fn, save=True)
+
+    def __editorOpened(self, editor):
+        """
+        Private slot called, when a new editor was opened.
+
+        @param editor reference to the new editor
+        @type QScintilla.Editor
+        """
+        menu = editor.getMenu("Checks")
+        if menu is not None:
+            menu.addAction(self.__editorAct)
+            editor.showMenu.connect(self.__editorShowMenu)
+            self.__editors.append(editor)
+
+    def __editorClosed(self, editor):
+        """
+        Private slot called, when an editor was closed.
+
+        @param editor reference to the editor
+        @type QScintilla.Editor
+        """
+        with contextlib.suppress(ValueError):
+            self.__editors.remove(editor)
+
+    def __editorShowMenu(self, menuName, menu, editor):
+        """
+        Private slot called, when the the editor context menu or a submenu is
+        about to be shown.
+
+        @param menuName name of the menu to be shown
+        @type str
+        @param menu reference to the menu
+        @type QMenu
+        @param editor reference to the editor
+        @type QScintilla.Editor
+        """
+        if menuName == "Checks":
+            if self.__editorAct not in menu.actions():
+                menu.addAction(self.__editorAct)
+            self.__editorAct.setEnabled(editor.isPyFile())
+
+    def __editorPyrightCheck(self):
+        """
+        Private slot to handle the code style check context menu action
+        of the editors.
+        """
+        from PyrightChecker.PyrightCheckerDialog import PyrightCheckerDialog
+
+        editor = ericApp().getObject("ViewManager").activeWindow()
+        if (
+            editor is not None
+            and editor.checkDirty()
+            and editor.getFileName() is not None
+        ):
+            self.__editorPyrightCheckerDialog = PyrightCheckerDialog(self)
+            self.__editorPyrightCheckerDialog.show()
+            self.__editorPyrightCheckerDialog.start([editor.getFileName()], save=True)
+
+
+def installDependencies(pipInstall):
+    """
+    Function to install dependencies of this plug-in.
+
+    @param pipInstall function to be called with a list of package names.
+    @type function
+    """
+    try:
+        import pyright  # __IGNORE_WARNING__
+    except ImportError:
+        pipInstall(["pyright"])
+    try:
+        import tomlkit  # __IGNORE_WARNING__
+    except ImportError:
+        pipInstall(["tomlkit"])
+
+
+#
+# eflag: noqa = M801

eric ide

mercurial