diff -r 9b743f8d436b -r ea6aed49cd69 PluginVulture.py --- a/PluginVulture.py Sat Oct 03 17:08:28 2015 +0200 +++ b/PluginVulture.py Sat Oct 03 19:07:40 2015 +0200 @@ -0,0 +1,284 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2015 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the radon code metrics plug-in. +""" + +from __future__ import unicode_literals + +import os + +from PyQt5.QtCore import pyqtSignal, QObject, QTranslator + +from E5Gui.E5Application import e5App +from E5Gui.E5Action import E5Action + +import Preferences + +# Start-Of-Header +name = "PluginVulture" +author = "Detlev Offenbach <detlev@die-offenbachs.de>" +autoactivate = True +deactivateable = True +version = "0.1.0" +className = "VulturePlugin" +packageName = "VultureChecker" +shortDescription = "Plug-in to detect unused code using the vulture library" +longDescription = ( + """Plug-in to detect unused code using the vulture library.""" +) +needsRestart = False +pyqtApi = 2 +python2Compatible = True +# End-Of-Header + +error = "" + + +class VulturePlugin(QObject): + """ + Class documentation goes here. + + @signal metricsDone(str, dict) emitted when the code metrics were + determined for a file + @signal error(str, str) emitted in case of an error + @signal batchFinished() emitted when a style check batch is done + """ + batchFinished = pyqtSignal() + + def __init__(self, ui): + """ + Constructor + + @param ui reference to the user interface object (UI.UserInterface) + """ + super(VulturePlugin, self).__init__(ui) + self.__ui = ui + self.__initialize() + + self.backgroundService = e5App().getObject("BackgroundService") + + path = os.path.join(os.path.dirname(__file__), packageName) + try: + self.backgroundService.serviceConnect( + 'vulture', 'Python2', path, 'VultureCheckerService', + self.vultureCheckDone, + onErrorCallback=self.serviceErrorPy2, + onBatchDone=self.batchJobDone) + self.backgroundService.serviceConnect( + 'vulture', 'Python3', path, 'VultureCheckerService', + self.vultureCheckDone, + onErrorCallback=self.serviceErrorPy3, + onBatchDone=self.batchJobDone) + except TypeError: + # backward compatibility for eric 6.0 + self.backgroundService.serviceConnect( + 'vulture', 'Python2', path, 'VultureCheckerService', + self.vultureCheckDone, + onErrorCallback=self.serviceErrorPy2) + self.backgroundService.serviceConnect( + 'vulture', 'Python3', path, 'VultureCheckerService', + self.vultureCheckDone, + onErrorCallback=self.serviceErrorPy3) + + self.queuedBatches = [] + self.batchesFinished = True + + self.__translator = None + self.__loadTranslator() + + def __serviceError(self, fn, msg): + """ + Private slot handling service errors. + + @param fn file name + @type str + @param msg message text + @type str + """ + self.error.emit(fn, msg) + + def serviceErrorPy2(self, fx, lang, fn, msg): + """ + Public slot handling service errors for Python 2. + + @param fx service name + @type str + @param lang language + @type str + @param fn file name + @type str + @param msg message text + @type str + """ + if fx in ['vulture', 'batch_vulture'] and \ + lang == 'Python2': + if fx == 'vulture': + self.__serviceError(fn, msg) + else: + self.__serviceError(self.tr("Python 2 batch job"), msg) + self.batchJobDone(fx, lang) + + def serviceErrorPy3(self, fx, lang, fn, msg): + """ + Public slot handling service errors for Python 3. + + @param fx service name + @type str + @param lang language + @type str + @param fn file name + @type str + @param msg message text + @type str + """ + if fx in ['vulture', 'batch_vulture'] and \ + lang == 'Python3': + if fx == 'vulture': + self.__serviceError(fn, msg) + else: + self.__serviceError(self.tr("Python 3 batch job"), msg) + self.batchJobDone(fx, lang) + + def batchJobDone(self, fx, lang): + """ + Public slot handling the completion of a batch job. + + @param fx service name + @type str + @param lang language + @type str + """ + if fx in ['vulture', 'batch_vulture']: + if lang in self.queuedBatches: + self.queuedBatches.remove(lang) + # prevent sending the signal multiple times + if len(self.queuedBatches) == 0 and not self.batchesFinished: + self.batchFinished.emit() + self.batchesFinished = True + + def __initialize(self): + """ + Private slot to (re)initialize the plug-in. + """ + self.__projectAct = None + self.__projectVultureCheckerDialog = None + + def vultureCheck(self, lang, filenames): + """ + Public method to prepare a vulture check for a Python project. + + @param lang language of the files or None to determine by internal + algorithm + @type str or None + @param filenames list of file names to include in the check + @type list of str + """ + # TODO: implement this + + def activate(self): + """ + Public method to activate this plug-in. + + @return tuple of None and activation status (boolean) + """ + global error + error = "" # clear previous error + + menu = e5App().getObject("Project").getMenu("Checks") + if menu: + self.__projectAct = E5Action( + self.tr('Check Unused Code'), + self.tr('&Unused Code...'), 0, 0, + self, 'project_check_vulture') + self.__projectAct.setStatusTip( + self.tr('Check for unused code')) + self.__projectAct.setWhatsThis(self.tr( + """<b>Check Unused Code...</b>""" + """<p>This checks a Python project for unused code.</p>""" + )) + self.__projectAct.triggered.connect( + self.__projectVultureCheck) + e5App().getObject("Project").addE5Actions([self.__projectAct]) + menu.addAction(self.__projectAct) + + e5App().getObject("Project").showMenu.connect(self.__projectShowMenu) + e5App().getObject("Project").projectClosed.connect( + self.__projectClosed) + + return None, True + + def deactivate(self): + """ + Public method to deactivate this plug-in. + """ + e5App().getObject("Project").showMenu.disconnect( + self.__projectShowMenu) + e5App().getObject("Project").projectClosed.disconnect( + self.__projectClosed) + + menu = e5App().getObject("Project").getMenu("Show") + if menu: + if self.__projectAct is not None: + menu.removeAction(self.__projectAct) + e5App().getObject("Project").removeE5Actions( + [self.self.__projectAct]) + + 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__), "VultureChecker", "i18n") + translation = "vulture_{0}".format(loc) + translator = QTranslator(None) + loaded = translator.load(translation, locale_dir) + if loaded: + self.__translator = translator + e5App().installTranslator(self.__translator) + else: + print("Warning: translation file '{0}' could not be" + " loaded.".format(translation)) + print("Using default.") + + def __projectShowMenu(self, menuName, menu): + """ + 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": + if self.__projectAct is not None: + self.__projectAct.setEnabled( + e5App().getObject("Project").getProjectLanguage() in + ["Python3", "Python2", "Python"]) + + def __projectVultureCheck(self): + """ + Private slot used to check the project for unused code. + """ + project = e5App().getObject("Project") + project.saveAllScripts() + ppath = project.getProjectPath() + files = [os.path.join(ppath, file) + for file in project.pdata["SOURCES"] + if file.endswith( + tuple(Preferences.getPython("Python3Extensions")) + + tuple(Preferences.getPython("PythonExtensions")))] + + if self.__projectVultureCheckerDialog is None: + from VultureChecker.VultureCheckerDialog import \ + VultureCheckerDialog + self.__projectRawMetricsDialog = VultureCheckerDialog(self) + self.__projectVultureCheckerDialog.show() + self.__projectVultureCheckerDialog.prepare(files, project)