Tue, 06 Oct 2015 19:29:52 +0200
Finished implementing the whitelist edit dialog.
# -*- coding: utf-8 -*- # Copyright (c) 2015 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the vulture 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 from Utilities import determinePythonVersion # 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 analysisDone(str, dict) emitted when the code analysis has been completed for a file @signal error(str, str) emitted in case of an error @signal batchFinished() emitted when a style check batch is done """ analysisDone = pyqtSignal(str, dict) batchFinished = pyqtSignal() error = pyqtSignal(str, str) 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) self.hasBatch = True 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.hasBatch = False 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 vultureCheckDone(self, filename, result): """ Public slot to dispatch the result. @param filename name of the file the results belong to @type str @param result result dictionary @type dict """ self.analysisDone.emit(filename, result) def __initialize(self): """ Private slot to (re)initialize the plug-in. """ self.__projectAct = None self.__projectVultureCheckerDialog = None def vultureCheck(self, lang, filename, source): """ 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 filename name of the file to analyze @type str @param source string containing the code @type str """ if lang is None: lang = 'Python{0}'.format(determinePythonVersion(filename, source)) if lang not in ['Python2', 'Python3']: return self.backgroundService.enqueueRequest( 'vulture', lang, filename, [source]) def vultureCheckBatch(self, argumentsList): """ Public method to prepare a vulture check for a Python project using the batch mode. @param argumentsList list of arguments tuples with each tuple containing filename and source @type (str, str) """ data = { "Python2": [], "Python3": [], } for filename, source in argumentsList: lang = 'Python{0}'.format(determinePythonVersion(filename, source)) if lang not in ['Python2', 'Python3']: continue else: data[lang].append((filename, source)) self.queuedBatches = [] for lang in ['Python2', 'Python3']: if data[lang]: self.queuedBatches.append(lang) self.backgroundService.enqueueRequest('batch_vulture', lang, "", data[lang]) self.batchesFinished = False def cancelVultureCheckBatch(self): """ Public method to cancel all batch jobs. """ for lang in ['Python2', 'Python3']: self.backgroundService.requestCancel('batch_vulture', lang) 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.__projectVultureCheckerDialog = VultureCheckerDialog(self) self.__projectVultureCheckerDialog.show() self.__projectVultureCheckerDialog.prepare(files, project) def __projectClosed(self): """ Private slot to handle closing a project. """ self.__projectVultureCheckerDialog and \ self.__projectVultureCheckerDialog.clear()