Interface for adding different languages to the syntax check, background service BgService

Fri, 31 Jan 2014 22:11:45 +0100

author
T.Rzepka <Tobias.Rzepka@gmail.com>
date
Fri, 31 Jan 2014 22:11:45 +0100
branch
BgService
changeset 3241
957673fc463a
parent 3228
f489068e51e8
child 3412
9364dab2d472

Interface for adding different languages to the syntax check, background service
calls based upon language not Python version any more, refactored services.
Note: CodeStyleCheck is broken.

Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckService.py file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py file | annotate | diff | comparison | revisions
Plugins/PluginSyntaxChecker.py file | annotate | diff | comparison | revisions
QScintilla/Editor.py file | annotate | diff | comparison | revisions
UI/UserInterface.py file | annotate | diff | comparison | revisions
Utilities/BackgroundClient.py file | annotate | diff | comparison | revisions
Utilities/BackgroundService.py file | annotate | diff | comparison | revisions
Utilities/InternalServices.py file | annotate | diff | comparison | revisions
eric5.e4p file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckService.py	Fri Jan 31 22:11:45 2014 +0100
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2013 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+# pylint: disable=C0103
+
+"""
+Module implementing an interface to add different languages to do a syntax
+check.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt4.QtCore import QObject, pyqtSignal
+
+from E5Gui.E5Application import e5App
+from Utilities import determinePythonVersion
+
+
+class SyntaxCheckService(QObject):
+    """
+    Implement the syntax check service.
+    
+    Plugins can add other languages to the syntax check by calling addLanguage
+    and support of an extra checker module on the client side which has to
+    connect directly to the background service.
+    """
+    syntaxChecked = pyqtSignal(str, bool, int, int, str, str, list)
+
+    def __init__(self):
+        """
+        Contructor of SyntaxCheckService.
+        
+        @param backgroundService to connect to (BackgroundService class)
+        """
+        super(SyntaxCheckService, self).__init__()
+        self.backgroundService = e5App().getObject("BackgroundService")
+        self.__supportedLanguages = {}
+
+    def __determineLanguage(self, filename, source):
+        """
+        Private methode to determine the language of the file.
+        
+        @return language of the file or None if not found (str or None)
+        """
+        pyVer = determinePythonVersion(filename, source)
+        if pyVer:
+            return 'Python{0}'.format(pyVer)
+        
+        for lang, (getArgs, getExt) in self.__supportedLanguages.items():
+            if filename.endswith(getExt()):
+                return lang
+        
+        return None
+
+    def addLanguage(
+            self, lang, path, module, getArgs, getExt, callback, onError):
+        """
+        Register the new language to the supported languages.
+        
+        @param lang new language to check syntax (str)
+        @param path full path to the module (str)
+        @param module name to import (str)
+        @param getArgs function to collect the required arguments to call the
+            syntax checker on client side (function)
+        @param getExt function that returns the supported file extensions of
+            the syntax checker (function)
+        @param callback function on service response (function)
+        @param onError callback function if client or service isn't available
+            (function)
+        """
+        self.__supportedLanguages[lang] = getArgs, getExt
+        # Connect to the background service
+        self.backgroundService.serviceConnect(
+            'syntax', lang, path, module, callback, onError)
+
+    def getLanguages(self):
+        """
+        Return the supported language names.
+        
+        @return list of languanges supported (list of str)
+        """
+        return list(self.__supportedLanguages.keys())
+
+    def removeLanguage(self, lang):
+        """
+        Remove the language from syntax check.
+        
+        @param lang language to remove (str)
+        """
+        self.__supportedLanguages.pop(lang, None)
+        self.backgroundService.serviceDisconnect('syntax', lang)
+
+    def getExtensions(self):
+        """
+        Return all supported file extensions for the syntax checker dialog.
+        
+        @return set of all supported file extensions (set of str)
+        """
+        extensions = set()
+        for getArgs, getExt in self.__supportedLanguages.values():
+            for ext in getExt():
+                extensions.add(ext)
+        return extensions
+
+    def syntaxCheck(self, lang, filename, source=""):
+        """
+        Method to prepare to compile one Python source file to Python bytecode
+        and to perform a pyflakes check in another task.
+        
+        @param lang language of the file or None to determine by internal
+            algorithm (str or None)
+        @param filename source filename (string)
+        @keyparam source string containing the code to check (string)
+        """
+        if not lang:
+            lang = self.__determineLanguage(filename, source)
+        if lang not in self.getLanguages():
+            return
+        data = [source]
+        # Call the getArgs function to get the required arguments
+        args = self.__supportedLanguages[lang][0]()
+        data.extend(args)
+        self.backgroundService.enqueueRequest('syntax', lang, filename, data)
--- a/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py	Fri Jan 17 23:38:29 2014 +0100
+++ b/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py	Fri Jan 31 22:11:45 2014 +0100
@@ -21,7 +21,6 @@
 from .Ui_SyntaxCheckerDialog import Ui_SyntaxCheckerDialog
 
 import Utilities
-import Preferences
 import UI.PixmapCache
 
 
@@ -66,8 +65,8 @@
         self.checkProgressLabel.setVisible(False)
         self.checkProgressLabel.setMaximumWidth(600)
         
-        self.internalServices = e5App().getObject('InternalServices')
-        self.internalServices.syntaxChecked.connect(self.__processResult)
+        self.syntaxCheckService = e5App().getObject('SyntaxCheckService')
+        self.syntaxCheckService.syntaxChecked.connect(self.__processResult)
         
     def __resort(self):
         """
@@ -154,9 +153,7 @@
             self.files = fn
         elif os.path.isdir(fn):
             self.files = []
-            extensions = set(Preferences.getPython("PythonExtensions") +
-                             Preferences.getPython("Python3Extensions"))
-            for ext in extensions:
+            for ext in self.syntaxCheckService.getExtensions():
                 self.files.extend(
                     Utilities.direntries(fn, True, '*{0}'.format(ext), 0))
         else:
@@ -177,10 +174,6 @@
             self.checkProgressLabel.setVisible(len(self.files) > 1)
             QApplication.processEvents()
 
-            self.checkFlakes = Preferences.getFlakes("IncludeInSyntaxCheck")
-            self.ignoreStarImportWarnings = Preferences.getFlakes(
-                "IgnoreStarImportWarnings")
-            
             # now go through all the files
             self.progress = 0
             self.check(codestring)
@@ -220,12 +213,10 @@
                 self.check()
                 return
         
-        self.internalServices.syntaxCheck(
-            self.filename, self.source, self.checkFlakes,
-            self.ignoreStarImportWarnings)
+        self.syntaxCheckService.syntaxCheck(None, self.filename, self.source)
 
     def __processResult(
-            self, fn, nok, fname, line, index, code, error, warnings):
+            self, fn, nok, line, index, code, error, warnings):
         """
         Slot to display the reported messages.
         
@@ -235,7 +226,6 @@
         
         @param fn filename of the checked file (str)
         @param nok flag if an error in the source was found (boolean)
-        @param fname filename of the checked file (str)  # TODO: remove dubl.
         @param line number where the error occured (int)
         @param index the column where the error occured (int)
         @param code the part of the code where the error occured (str)
@@ -250,7 +240,7 @@
         if nok:
             self.noResults = False
             self.__createResultItem(
-                fname, line, index, error, code.strip(), False)
+                fn, line, index, error, code.strip(), False)
         else:
             source = self.source.splitlines()
             for marker, _fn, lineno, col, msg in warnings:
--- a/Plugins/PluginSyntaxChecker.py	Fri Jan 17 23:38:29 2014 +0100
+++ b/Plugins/PluginSyntaxChecker.py	Fri Jan 31 22:11:45 2014 +0100
@@ -12,10 +12,11 @@
 import os
 
 from PyQt4.QtCore import QObject
-
-from E5Gui.E5Application import e5App
+from PyQt4.QtGui import QApplication
 
 from E5Gui.E5Action import E5Action
+from E5Gui.E5Application import e5App
+from eric5config import getConfig
 
 import Preferences
 
@@ -51,6 +52,31 @@
         self.__ui = ui
         self.__initialize()
         
+        from Plugins.CheckerPlugins.SyntaxChecker.SyntaxCheckService import \
+            SyntaxCheckService
+        self.syntaxCheckService = SyntaxCheckService()
+        e5App().registerObject("SyntaxCheckService", self.syntaxCheckService)
+
+        ericPath = getConfig('ericDir')
+        path = os.path.join(ericPath, 'Plugins', 'CheckerPlugins',
+                            'SyntaxChecker')
+        
+        self.syntaxCheckService.addLanguage('Python2', path, 'SyntaxCheck',
+                self.__getPythonOptions,
+                lambda: Preferences.getPython("PythonExtensions"),
+                self.__translateSyntaxCheck,
+                lambda fx, lng, fn, msg: \
+                    self.syntaxCheckService.syntaxChecked.emit(
+                        fn, True, fn, 0, 0, '', msg, []))
+        
+        self.syntaxCheckService.addLanguage('Python3', path, 'SyntaxCheck',
+                self.__getPythonOptions,
+                lambda: Preferences.getPython("Python3Extensions"),
+                self.__translateSyntaxCheck,
+                lambda fx, lng, fn, msg: \
+                    self.syntaxCheckService.syntaxChecked.emit(
+                        fn, True, fn, 0, 0, '', msg, []))
+
     def __initialize(self):
         """
         Private slot to (re)initialize the plugin.
@@ -66,6 +92,49 @@
         self.__editorAct = None
         self.__editorSyntaxCheckerDialog = None
 
+    def __getPythonOptions(self):
+        """
+        Private methode to determine the syntax check options.
+        
+        @return state of checkFlakes and ignoreStarImportWarnings (bool, bool)
+        """
+        checkFlakes = Preferences.getFlakes("IncludeInSyntaxCheck")
+        ignoreStarImportWarnings = Preferences.getFlakes(
+            "IgnoreStarImportWarnings")
+        return checkFlakes, ignoreStarImportWarnings
+
+    def __translateSyntaxCheck(
+            self, fn, nok, fname, line, index, code, error, warnings):
+        """
+        Slot to translate the resulting messages.
+        
+        If checkFlakes is True, warnings contains a list of strings containing
+        the warnings (marker, file name, line number, message)
+        The values are only valid, if nok is False.
+        
+        @param fn filename of the checked file (str)
+        @param nok flag if an error in the source was found (boolean)
+        @param fname filename of the checked file (str)  # TODO: remove dubl.
+        @param line number where the error occured (int)
+        @param index the column where the error occured (int)
+        @param code the part of the code where the error occured (str)
+        @param error the name of the error (str)
+        @param warnings a list of strings containing the warnings
+            (marker, file name, line number, col, message, list(msg_args))
+        """
+        for warning in warnings:
+            # Translate messages
+            msg_args = warning.pop()
+            translated = QApplication.translate(
+                'py3Flakes', warning[4]).format(*msg_args)
+            # Avoid leading "u" at Python2 unicode strings
+            if translated.startswith("u'"):
+                translated = translated[1:]
+            warning[4] = translated.replace(" u'", " '")
+        
+        self.syntaxCheckService.syntaxChecked.emit(
+            fn, nok, line, index, code, error, warnings)
+
     def activate(self):
         """
         Public method to activate this plugin.
@@ -152,7 +221,7 @@
         if menuName == "Checks" and self.__projectAct is not None:
             self.__projectAct.setEnabled(
                 e5App().getObject("Project").getProjectLanguage() in
-                ["Python3", "Python2", "Python"])
+                self.syntaxCheckService.getLanguages())
     
     def __projectBrowserShowMenu(self, menuName, menu):
         """
@@ -164,7 +233,7 @@
         """
         if menuName == "Checks" and \
            e5App().getObject("Project").getProjectLanguage() in \
-                ["Python3", "Python2", "Python"]:
+                self.syntaxCheckService.getLanguages():
             self.__projectBrowserMenu = menu
             if self.__projectBrowserAct is None:
                 self.__projectBrowserAct = E5Action(
@@ -187,11 +256,10 @@
         project = e5App().getObject("Project")
         project.saveAllScripts()
         ppath = project.getProjectPath()
+        extensions = tuple(self.syntaxCheckService.getExtensions())
         files = [os.path.join(ppath, file)
                  for file in project.pdata["SOURCES"]
-                 if file.endswith(
-                     tuple(Preferences.getPython("Python3Extensions")) +
-                     tuple(Preferences.getPython("PythonExtensions")))]
+                 if file.endswith(extensions)]
         
         from CheckerPlugins.SyntaxChecker.SyntaxCheckerDialog import \
             SyntaxCheckerDialog
@@ -254,7 +322,7 @@
             if not self.__editorAct in menu.actions():
                 menu.addAction(self.__editorAct)
             self.__editorAct.setEnabled(
-                editor.isPy3File() or editor.isPy2File())
+                editor.getLanguage() in self.syntaxCheckService.getLanguages())
     
     def __editorSyntaxCheck(self):
         """
--- a/QScintilla/Editor.py	Fri Jan 17 23:38:29 2014 +0100
+++ b/QScintilla/Editor.py	Fri Jan 31 22:11:45 2014 +0100
@@ -312,8 +312,8 @@
         self.__setTextDisplay()
         
         # initialize the online syntax check timer
-        self.internalServices = e5App().getObject('InternalServices')
-        self.internalServices.syntaxChecked.connect(self.__processResult)
+        self.syntaxCheckService = e5App().getObject('SyntaxCheckService')
+        self.syntaxCheckService.syntaxChecked.connect(self.__processResult)
         self.__initOnlineSyntaxCheck()
         
         self.isResourcesFile = False
@@ -4962,23 +4962,18 @@
         """
         Private method to perform an automatic syntax check of the file.
         """
-        if (self.isPy2File() or self.isPy3File()) is False:
+        if self.filetype not in self.syntaxCheckService.getLanguages():
             return
         
         if Preferences.getEditor("AutoCheckSyntax"):
             if Preferences.getEditor("OnlineSyntaxCheck"):
                 self.__onlineSyntaxCheckTimer.stop()
             
-            checkFlakes = Preferences.getFlakes("IncludeInSyntaxCheck")
-            ignoreStarImportWarnings = Preferences.getFlakes(
-                "IgnoreStarImportWarnings")
-                
-            self.internalServices.syntaxCheck(
-                self.fileName or "(Unnamed)", self.text(), checkFlakes,
-                ignoreStarImportWarnings, editor=self)
+            self.syntaxCheckService.syntaxCheck(
+                self.filetype, self.fileName or "(Unnamed)", self.text())
 
     def __processResult(
-            self, fn, nok, fname, line, index, code, error, warnings):
+            self, fn, nok, line, index, code, error, warnings):
         """
         Slot to report the resulting messages.
         
@@ -4988,7 +4983,6 @@
         
         @param fn filename of the checked file (str)
         @param nok flag if an error in the source was found (boolean)
-        @param fname filename of the checked file (str)  # TODO: remove dubl.
         @param line number where the error occured (int)
         @param index the column where the error occured (int)
         @param code the part of the code where the error occured (str)
@@ -5885,7 +5879,7 @@
         self.breakpointModel.rowsInserted.disconnect(
             self.__addBreakPoints)
         
-        self.internalServices.syntaxChecked.disconnect(self.__processResult)
+        self.syntaxCheckService.syntaxChecked.disconnect(self.__processResult)
         
         if self.spell:
             self.spell.stopIncrementalCheck()
--- a/UI/UserInterface.py	Fri Jan 17 23:38:29 2014 +0100
+++ b/UI/UserInterface.py	Fri Jan 31 22:11:45 2014 +0100
@@ -205,9 +205,6 @@
         # Create the background service object
         from Utilities.BackgroundService import BackgroundService
         self.backgroundService = BackgroundService()
-        # And initialize the standard services
-        from Utilities.InternalServices import InternalServices
-        self.internalServices = InternalServices(self.backgroundService)
         
         # Generate an empty project object and multi project object
         from Project.Project import Project
@@ -458,7 +455,6 @@
         e5App().registerObject("DebugUI", self.debuggerUI)
         e5App().registerObject("DebugServer", debugServer)
         e5App().registerObject("BackgroundService", self.backgroundService)
-        e5App().registerObject("InternalServices", self.internalServices)
         e5App().registerObject("ViewManager", self.viewmanager)
         e5App().registerObject("Project", self.project)
         e5App().registerObject("ProjectBrowser", self.projectBrowser)
--- a/Utilities/BackgroundClient.py	Fri Jan 17 23:38:29 2014 +0100
+++ b/Utilities/BackgroundClient.py	Fri Jan 31 22:11:45 2014 +0100
@@ -36,7 +36,7 @@
         self.services = {}
         
         self.connection = socket.create_connection((host, port))
-        ver = b'2' if sys.version_info[0] == 2 else b'3'
+        ver = b'Python2' if sys.version_info[0] == 2 else b'Python3'
         self.connection.sendall(ver)
         self.connection.settimeout(0.25)
 
@@ -124,7 +124,7 @@
         """
         # TODO: Wrap arguments so they can be serialized by JSON
         self.__send(
-            'exception', '?', [str(exctype), str(excval), str(exctb)])
+            'EXCEPTION', '?', [str(exctype), str(excval), str(exctb)])
 
 if __name__ == '__main__':
     if len(sys.argv) != 3:
--- a/Utilities/BackgroundService.py	Fri Jan 17 23:38:29 2014 +0100
+++ b/Utilities/BackgroundService.py	Fri Jan 31 22:11:45 2014 +0100
@@ -32,14 +32,14 @@
     """
     Class implementing the main part of the background service.
     """
-    serviceNotAvailable = pyqtSignal(str, str, int, str)
+    serviceNotAvailable = pyqtSignal(str, str, str, str)
     
     def __init__(self):
         """
         Constructor of the BackgroundService class.
         """
-        self.processes = [None, None]
-        self.connections = [None, None]
+        self.processes = []
+        self.connections = {}
         self.isWorking = None
         self.__queue = []
         self.services = {}
@@ -58,19 +58,20 @@
         ## NOTE: Need the port if started external in debugger:
         print('BackgroundService listening on: %i' % port)
         if sys.platform == 'win32':
-            pyCompare = Utilities.samefilepath
+            interpreterCompare = Utilities.samefilepath
         else:
-            pyCompare = Utilities.samepath
+            interpreterCompare = Utilities.samepath
         
-        for pyIdx, pyName in enumerate(['Python', 'Python3']):
+        for pyName in ['Python', 'Python3']:
             interpreter = Preferences.getDebugger(
                 pyName + "Interpreter")
             
-            if pyCompare(interpreter, sys.executable):
+            if interpreterCompare(interpreter, sys.executable):
                 process = self.__startInternalClient(port)
             else:
                 process = self.__startExternalClient(interpreter, port)
-            self.processes[pyIdx] = process
+            if process:
+                self.processes.append(process)
 
     def __startExternalClient(self, interpreter, port):
         """
@@ -78,7 +79,7 @@
         
         @param interpreter path and name of the executable to start (string)
         @param port socket port to which the interpreter should connect (int)
-        @return the process object (QProcess) or None
+        @return the process object (QProcess or None)
         """
         if interpreter == "" or not Utilities.isinpath(interpreter):
             return None
@@ -112,44 +113,44 @@
         client.
         """
         if self.__queue and self.isWorking is None:
-            fx, fn, pyVer, data = self.__queue.pop(0)
-            self.isWorking = pyVer
-            self.__send(fx, fn, pyVer, data)
+            fx, lang, fn, data = self.__queue.pop(0)
+            self.isWorking = lang
+            self.__send(fx, lang, fn, data)
     
-    def __send(self, fx, fn, pyVer, data):
+    def __send(self, fx, lang, fn, data):
         """
         Private method to send a job request to one of the clients.
         
         @param fx remote function name to execute (str)
+        @param lang language to connect to (str)
         @param fn filename for identification (str)
-        @param pyVer version for the required interpreter (int)
         @param data function argument(s) (any basic datatype)
         """
-        packedData = json.dumps([fx, fn, data])
-        if sys.version_info[0] == 3:
-            packedData = bytes(packedData, 'utf-8')
-        connection = self.connections[pyVer - 2]
+        connection = self.connections.get(lang)
         if connection is None:
             if fx != 'INIT':
                 self.serviceNotAvailable.emit(
-                    fx, fn, pyVer, self.trUtf8(
-                        'Python{0} interpreter not configured.').format(pyVer))
+                    fx, lang, fn, self.trUtf8(
+                        '{0} not configured.').format(lang))
             # Reset flag and continue processing queue
             self.isWorking = None
             self.__processQueue()
         else:
+            packedData = json.dumps([fx, fn, data])
+            if sys.version_info[0] == 3:
+                packedData = bytes(packedData, 'utf-8')
             header = struct.pack(
                 b'!II', len(packedData), adler32(packedData) & 0xffffffff)
             connection.write(header)
             connection.write(packedData)
 
-    def __receive(self, channel):
+    def __receive(self, lang):
         """
         Private method to receive the response from the clients.
         
-        @param channel of the incomming connection (int: 0 or 1)
+        @param lang language of the incomming connection (str)
         """
-        connection = self.connections[channel]
+        connection = self.connections[lang]
         header = connection.read(8)
         length, datahash = struct.unpack(b'!II', header)
         
@@ -163,49 +164,45 @@
             packedData = packedData.decode('utf-8')
         # "check" if is's a tuple of 3 values
         fx, fn, data = json.loads(packedData)
-        self.__postResult(fx, fn, data)
         
-    def __postResult(self, fx, fn, data):
-        """
-        Private method to emit the correspondig signal for the returned
-        function.
-        
-        @param fx remote function name to execute (str)
-        @param fn filename for identification (str)
-        @param data function argument(s) (any basic datatype)
-        """
         if fx == 'INIT':
             pass
-        elif fx == 'exception':
+        elif fx == 'EXCEPTION':
             # Call sys.excepthook(type, value, traceback) to emulate the
             # exception which was caught on the client
             #sys.excepthook(*data)
             print(data)
+        elif data == 'Unknown service.':
+            callback = self.services.get((fx, lang))
+            if callback:
+                callback[3](fx, lang, fn, data)
         else:
-            callback = self.services.get(fx)
+            callback = self.services.get((fx, lang))
             if callback:
                 callback[2](fn, *data)
         
         self.isWorking = None
         self.__processQueue()
 
-    def enqueueRequest(self, fx, fn, pyVer, data):
+    def enqueueRequest(self, fx, lang, fn, data):
         """
         Implement a queued processing of incomming events.
         
-        Dublicate file checks update an older request to avoid overrun or
-        starving of the check.
+        Dublicate service requests updates an older request to avoid overrun or
+        starving of the services.
         @param fx function name of the service (str)
+        @param lang language to connect to (str)
         @param fn filename for identification (str)
-        @param pyVer version for the required interpreter (int)
-        @param data function argument(s) (any basic datatype)
+        @param data function argument(s) (any basic datatype(s))
         """
-        args = [fx, fn, pyVer, data]
+        args = [fx, lang, fn, data]
         if fx == 'INIT':
             self.__queue.insert(0, args)
         else:
             for pendingArg in self.__queue:
+                # Check if it's the same service request (fx, lang, fn equal)
                 if pendingArg[:3] == args[:3]:
+                    # Update the data
                     pendingArg[3] = args[3]
                     break
             else:
@@ -213,29 +210,34 @@
         self.__processQueue()
     
     def serviceConnect(
-            self, fx, modulepath, module, callback, onErrorCallback=None):
+            self, fx, lang, modulepath, module, callback,
+            onErrorCallback=None):
         """
         Announce a new service to the background service/ client.
         
         @param fx function name of the service (str)
+        @param lang language of the new service (str)
         @param modulepath full path to the module (str)
         @param module name to import (str)
         @param callback function on service response (function)
         @param onErrorCallback function if client isn't available (function)
         """
-        self.services[fx] = modulepath, module, callback, onErrorCallback
-        self.enqueueRequest('INIT', fx, 0, [modulepath, module])
-        self.enqueueRequest('INIT', fx, 1, [modulepath, module])
+        self.services[(fx, lang)] = \
+            modulepath, module, callback, onErrorCallback
+        self.enqueueRequest('INIT', lang, fx, [modulepath, module])
         if onErrorCallback:
             self.serviceNotAvailable.connect(onErrorCallback)
     
-    def serviceDisconnect(self, fx):
+    def serviceDisconnect(self, fx, lang):
         """
         Remove the service from the service list.
         
-        @param fx function name of the service
+        @param fx function name of the service (function)
+        @param lang language of the service (str)
         """
-        self.services.pop(fx, None)
+        serviceArgs = self.services.pop((fx, lang), None)
+        if serviceArgs and serviceArgs[3]:
+            self.serviceNotAvailable.disconnect(serviceArgs[3])
 
     def on_newConnection(self):
         """
@@ -244,24 +246,28 @@
         connection = self.nextPendingConnection()
         if not connection.waitForReadyRead(1000):
             return
-        ch = 0 if connection.read(1) == b'2' else 1
+        lang = connection.read(64)
+        if sys.version_info[0] == 3:
+            lang = lang.decode('utf-8')
         # Avoid hanging of eric on shutdown
-        if self.connections[ch]:
-            self.connections[ch].close()
-        if self.isWorking == ch + 2:
+        if self.connections.get(lang):
+            self.connections[lang].close()
+        if self.isWorking == lang:
             self.isWorking = None
-        self.connections[ch] = connection
+        self.connections[lang] = connection
         connection.readyRead.connect(
-            lambda x=ch: self.__receive(x))
+            lambda x=lang: self.__receive(x))
         
-        for fx, args in self.services.items():
-            self.enqueueRequest('INIT', fx, ch, args[:2])
+        for (fx, lng), args in self.services.items():
+            if lng == lang:
+                # Register service with modulepath and module
+                self.enqueueRequest('INIT', lng, fx, args[:2])
 
     def shutdown(self):
         """
         Cleanup the connections and processes when Eric is shuting down.
         """
-        for connection in self.connections:
+        for connection in self.connections.values():
             if connection:
                 connection.close()
         
--- a/Utilities/InternalServices.py	Fri Jan 17 23:38:29 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,177 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2013 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-# pylint: disable=C0103
-
-"""
-Module implementing a Qt free version of a background client for the various
-checkers and other python interpreter dependent functions.
-"""
-
-from __future__ import unicode_literals
-
-import os
-
-from PyQt4.QtCore import QObject, pyqtSignal
-from PyQt4.QtGui import QApplication
-
-from eric5config import getConfig
-from Utilities import determinePythonVersion
-
-
-class InternalServices(QObject):
-    """
-    Implement the standard services (syntax with flakes and the style check).
-    """
-    syntaxChecked = pyqtSignal(str, bool, str, int, int, str, str, list)
-    styleChecked = pyqtSignal(str, dict, int, list)
-    #indentChecked = pyqtSignal(TBD)
-
-    def __init__(self, backgroundService):
-        """
-        Contructor of InternalServices.
-        
-        @param backgroundService to connect to
-        """
-        super(InternalServices, self).__init__()
-        self.backgroundService = backgroundService
-        
-        ericPath = getConfig('ericDir')
-        # Syntax check
-        path = os.path.join(ericPath, 'Plugins', 'CheckerPlugins',
-                            'SyntaxChecker')
-        self.backgroundService.serviceConnect(
-            'syntax', path, 'SyntaxCheck',
-            self.__translateSyntaxCheck,
-            lambda fx, fn, ver, msg: self.syntaxChecked.emit(
-                fn, True, fn, 0, 0, '', msg, []))
-        
-        # Style check
-        path = os.path.join(ericPath, 'Plugins', 'CheckerPlugins',
-                            'CodeStyleChecker')
-        self.backgroundService.serviceConnect(
-            'style', path, 'CodeStyleChecker',
-            self.__translateStyleCheck,
-            lambda fx, fn, ver, msg: self.styleChecked.emit(
-                fn, {}, 0, [[0, 0, '---- ' + msg, False, False]]))
-        
-#        # Indent check
-#        path = os.path.join(ericPath, 'Plugins', 'CheckerPlugins',
-#                            'Tabnanny')
-#        self.backgroundService.serviceConnect(
-#            'indent', path, 'Tabnanny',
-#            self.__translateIndentCheck)
-
-    def syntaxCheck(self, filename, source="", checkFlakes=True,
-                    ignoreStarImportWarnings=False, pyVer=None, editor=None):
-        """
-        Method to prepare to compile one Python source file to Python bytecode
-        and to perform a pyflakes check in another task.
-        
-        @param filename source filename (string)
-        @keyparam source string containing the code to check (string)
-        @keyparam checkFlakes flag indicating to do a pyflakes check (boolean)
-        @keyparam ignoreStarImportWarnings flag indicating to
-            ignore 'star import' warnings (boolean)
-        @keyparam pyVer version of the interpreter to use or None for
-            autodetect corresponding interpreter (int or None)
-        @keyparam editor if the file is opened already (Editor object)
-        """
-        if pyVer is None:
-            pyVer = determinePythonVersion(filename, source, editor)
-        
-        data = [source, checkFlakes, ignoreStarImportWarnings]
-        self.backgroundService.enqueueRequest('syntax', filename, pyVer, data)
-
-    def __translateSyntaxCheck(
-            self, fn, nok, fname, line, index, code, error, warnings):
-        """
-        Slot to translate the resulting messages.
-        
-        If checkFlakes is True, warnings contains a list of strings containing
-        the warnings (marker, file name, line number, message)
-        The values are only valid, if nok is False.
-        
-        @param fn filename of the checked file (str)
-        @param nok flag if an error in the source was found (boolean)
-        @param fname filename of the checked file (str)  # TODO: remove dubl.
-        @param line number where the error occured (int)
-        @param index the column where the error occured (int)
-        @param code the part of the code where the error occured (str)
-        @param error the name of the error (str)
-        @param warnings a list of strings containing the warnings
-            (marker, file name, line number, col, message, list(msg_args))
-        """
-        for warning in warnings:
-            # Translate messages
-            msg_args = warning.pop()
-            translated = QApplication.translate(
-                'py3Flakes', warning[4]).format(*msg_args)
-            # Avoid leading "u" at Python2 unicode strings
-            if translated.startswith("u'"):
-                translated = translated[1:]
-            warning[4] = translated.replace(" u'", " '")
-        
-        self.syntaxChecked.emit(
-            fn, nok, fname, line, index, code, error, warnings)
-
-    def styleCheck(self, filename, source, args, pyVer=None, editor=None):
-        """
-        Method to prepare a style check on one Python source file in another
-        task.
-        
-        @param filename source filename (string)
-        @param source string containing the code to check (string)
-        @param args arguments used by the codeStyleCheck function (list of
-            excludeMessages (str), includeMessages (str), repeatMessages
-            (bool), fixCodes (str), noFixCodes (str), fixIssues (bool),
-            maxLineLength (int), hangClosing (bool), docType (str), errors
-            (list of str), eol (str), encoding (str))
-        @keyparam pyVer version of the interpreter to use or None for
-            autodetect corresponding interpreter (int or None)
-        @keyparam editor if the file is opened already (Editor object)
-        """
-        if pyVer is None:
-            pyVer = determinePythonVersion(filename, source, editor)
-        
-        data = [source, args]
-        self.backgroundService.enqueueRequest('style', filename, pyVer, data)
-    
-    def __translateStyleCheck(self, fn, codeStyleCheckerStats, results):
-        """
-        Privat slot called after perfoming a style check on one file.
-        
-        @param fn filename of the just checked file (str)
-        @param codeStyleCheckerStats stats of style and name check (dict)
-        @param results tuple for each found violation of style (tuple of
-            lineno (int), position (int), text (str), fixed (bool),
-            autofixing (bool), fixedMsg (str))
-        """
-        fixes = 0
-        for result in results:
-            msg = result[2].split('@@')
-            if msg[0].startswith(('W', 'E')):
-                msgType = 'pep8'
-            elif msg[0].startswith('N'):
-                msgType = 'NamingStyleChecker'
-            else:
-                msgType = 'DocStyleChecker'
-            translMsg = msg[0][:5] + QApplication.translate(
-                msgType, msg[0][5:]).format(*msg[1:])
-        
-            fixedMsg = result.pop()
-            if fixedMsg:
-                fixes += 1
-                if '@@' in fixedMsg:
-                    msg, param = fixedMsg.split('@@')
-                    fixedMsg = QApplication.translate(
-                        'CodeStyleFixer', msg).format(param)
-                else:
-                    fixedMsg = QApplication.translate(
-                        'CodeStyleFixer', fixedMsg)
-                
-                translMsg += "\n" + QApplication.translate(
-                    'CodeStyleCheckerDialog', "Fix: {0}").format(fixedMsg)
-            result[2] = translMsg
-        self.styleChecked.emit(fn, codeStyleCheckerStats, fixes, results)
--- a/eric5.e4p	Fri Jan 17 23:38:29 2014 +0100
+++ b/eric5.e4p	Fri Jan 31 22:11:45 2014 +0100
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE Project SYSTEM "Project-5.1.dtd">
 <!-- eric5 project file for project eric5 -->
-<!-- Saved: 2014-01-14, 23:44:35 -->
+<!-- Saved: 2014-01-30, 22:41:13 -->
 <!-- Copyright (C) 2014 Detlev Offenbach, detlev@die-offenbachs.de -->
 <Project version="5.1">
   <Language>en_US</Language>
@@ -1106,7 +1106,7 @@
     <Source>Plugins/CheckerPlugins/SyntaxChecker/pyflakes/checker.py</Source>
     <Source>Plugins/CheckerPlugins/SyntaxChecker/pyflakes/messages.py</Source>
     <Source>Plugins/CheckerPlugins/SyntaxChecker/pyflakes/__init__.py</Source>
-    <Source>Utilities/InternalServices.py</Source>
+    <Source>Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckService.py</Source>
   </Sources>
   <Forms>
     <Form>PyUnit/UnittestDialog.ui</Form>

eric ide

mercurial