Interface to add user-defined services, e.g. in plugins. Auto syntax check working. Little cleanup. BgService

Sat, 04 Jan 2014 22:12:42 +0100

author
T.Rzepka <Tobias.Rzepka@gmail.com>
date
Sat, 04 Jan 2014 22:12:42 +0100
branch
BgService
changeset 3173
1fb284abe46e
parent 3172
c0f78e9d0971
child 3174
86047f5f4155

Interface to add user-defined services, e.g. in plugins. Auto syntax check working. Little cleanup.

Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheck.py file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.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
Utilities/__init__.py file | annotate | diff | comparison | revisions
Utilities/py3flakes/__init__.py file | annotate | diff | comparison | revisions
Utilities/py3flakes/checker.py file | annotate | diff | comparison | revisions
Utilities/py3flakes/messages.py file | annotate | diff | comparison | revisions
eric5.e4p file | annotate | diff | comparison | revisions
--- a/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheck.py	Wed Jan 01 22:59:10 2014 +0100
+++ b/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheck.py	Sat Jan 04 22:12:42 2014 +0100
@@ -2,6 +2,7 @@
 
 # Copyright (c) 2011 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
 #
+# pylint: disable=C0103
 
 """
 Module implementing the syntax check for Python 2/3.
@@ -12,8 +13,20 @@
 import sys
 import traceback
 
-from .pyflakes.checker import Checker
-from .pyflakes.messages import ImportStarUsed
+try:
+    from pyflakes.checker import Checker
+    from pyflakes.messages import ImportStarUsed
+except ImportError:
+    pass
+
+
+def initService():
+    """
+    Initialize the service and return the entry point.
+    
+    @return the entry point for the background client (function)
+    """
+    return syntaxAndPyflakesCheck
 
 
 def normalizeCode(codestring):
--- a/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py	Wed Jan 01 22:59:10 2014 +0100
+++ b/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py	Sat Jan 04 22:12:42 2014 +0100
@@ -66,8 +66,8 @@
         self.checkProgressLabel.setVisible(False)
         self.checkProgressLabel.setMaximumWidth(600)
         
-        self.backgroundService = e5App().getObject('BackgroundService')
-        self.backgroundService.syntaxChecked.connect(self.processResult)
+        self.internalServices = e5App().getObject('InternalServices')
+        self.internalServices.syntaxChecked.connect(self.__processResult)
         
     def __resort(self):
         """
@@ -191,7 +191,7 @@
         QApplication.processEvents()
         self.__resort()
         
-        if self.cancelled:  # ???
+        if self.cancelled:
             return
         
         self.__lastFileItem = None
@@ -213,14 +213,14 @@
                 self.check()
                 return
         
-        self.backgroundService.syntaxCheck(
+        self.internalServices.syntaxCheck(
             self.filename, self.source, self.checkFlakes,
             self.ignoreStarImportWarnings)
 
-    def processResult(
+    def __processResult(
             self, fn, nok, fname, line, index, code, error, warnings):
         """
-        Slot which reports the resulting messages.
+        Slot to display the reported messages.
         
         If checkFlakes is True, warnings contains a list of strings containing
         the warnings (marker, file name, line number, message)
@@ -247,16 +247,6 @@
         else:
             source = self.source.splitlines()
             for warning in warnings:
-                # TODO: Move to BackgroundService
-                # Translate messages
-                msg_args = warning.pop()
-                translated = QApplication.translate(
-                    'py3Flakes', warning[-1]).format(*msg_args)
-                # Avoid leading "u" at Python2 unicode strings
-                if translated.startswith("u'"):
-                    translated = translated[1:]
-                warning[3] = translated.replace(" u'", " '")
-                
                 self.noResults = False
                 scr_line = source[warning[2] - 1].strip()
                 self.__createResultItem(
--- a/QScintilla/Editor.py	Wed Jan 01 22:59:10 2014 +0100
+++ b/QScintilla/Editor.py	Sat Jan 04 22:12:42 2014 +0100
@@ -312,6 +312,8 @@
         self.__setTextDisplay()
         
         # initialize the online syntax check timer
+        self.internalServices = e5App().getObject('InternalServices')
+        self.internalServices.syntaxChecked.connect(self.__processResult)
         self.__initOnlineSyntaxCheck()
         
         self.isResourcesFile = False
@@ -1763,13 +1765,13 @@
                         if isProjectPy2:
                             self.filetype = "Python2"
                         return isProjectPy2
-                    else:
-                        # 3) determine by compiling the sources
-                        syntaxError = Utilities.compile(
-                            self.fileName, self.text(), True)[0]
-                        if not syntaxError:
-                            self.filetype = "Python2"
-                            return True
+#                    else:
+#                        # 3) determine by compiling the sources
+#                        syntaxError = Utilities.compile(
+#                            self.fileName, self.text(), True)[0]
+#                        if not syntaxError:
+#                            self.filetype = "Python2"
+#                            return True
                 
                 if ext in self.dbs.getExtensions('Python2'):
                     self.filetype = "Python2"
@@ -1806,13 +1808,13 @@
                         if isProjectPy3:
                             self.filetype = "Python3"
                         return isProjectPy3
-                    else:
-                        # 3) determine by compiling the sources
-                        syntaxError = Utilities.compile(
-                            self.fileName, self.text(), False)[0]
-                        if not syntaxError:
-                            self.filetype = "Python3"
-                            return True
+#                    else:
+#                        # 3) determine by compiling the sources
+#                        syntaxError = Utilities.compile(
+#                            self.fileName, self.text(), False)[0]
+#                        if not syntaxError:
+#                            self.filetype = "Python3"
+#                            return True
                 
                 if ext in self.dbs.getExtensions('Python3'):
                     self.filetype = "Python3"
@@ -4960,25 +4962,53 @@
         """
         Private method to perform an automatic syntax check of the file.
         """
-        isPy2 = self.isPy2File()
-        if (isPy2 or self.isPy3File()) is False:
+        if (self.isPy2File() or self.isPy3File()) is False:
             return
         
         if Preferences.getEditor("AutoCheckSyntax"):
             if Preferences.getEditor("OnlineSyntaxCheck"):
                 self.__onlineSyntaxCheckTimer.stop()
-            self.clearSyntaxError()
-            self.clearFlakesWarnings()
+            
+            checkFlakes = Preferences.getFlakes("IncludeInSyntaxCheck")
+            ignoreStarImportWarnings = Preferences.getFlakes(
+                "IgnoreStarImportWarnings")
+                
+            self.internalServices.syntaxCheck(
+                self.fileName or "(Unnamed)", self.text(), checkFlakes,
+                ignoreStarImportWarnings, editor=self)
 
-            syntaxError, _fn, errorline, errorindex, _code, _error, warnings =\
-                Utilities.compile(
-                    self.fileName or "(Unnamed)", self.text(), isPy2)
-            if syntaxError:
-                self.toggleSyntaxError(errorline, errorindex, True, _error)
-            else:
-                for warning in warnings:
-                        self.toggleWarning(
-                            warning[2], True, warning[3])
+    def __processResult(
+            self, fn, nok, fname, line, index, code, error, warnings):
+        """
+        Slot to report 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, message)
+        """
+        # Check if it's the requested file, otherwise ignore signal
+        if fn != self.fileName and (
+                self.fileName is not None or fn != "(Unnamed)"):
+            return
+        
+        self.clearSyntaxError()
+        self.clearFlakesWarnings()
+        
+        if nok:
+            self.toggleSyntaxError(line, index, True, error)
+        else:
+            for warning in warnings:
+                self.toggleWarning(warning[2], True, warning[3])
 
     def __initOnlineSyntaxCheck(self):
         """
@@ -5855,6 +5885,8 @@
         self.breakpointModel.rowsInserted.disconnect(
             self.__addBreakPoints)
         
+        self.internalServices.syntaxChecked.disconnect(self.__processResult)
+        
         if self.spell:
             self.spell.stopIncrementalCheck()
         
--- a/UI/UserInterface.py	Wed Jan 01 22:59:10 2014 +0100
+++ b/UI/UserInterface.py	Sat Jan 04 22:12:42 2014 +0100
@@ -205,6 +205,9 @@
         # 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
@@ -455,6 +458,7 @@
         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	Wed Jan 01 22:59:10 2014 +0100
+++ b/Utilities/BackgroundClient.py	Sat Jan 04 22:12:42 2014 +0100
@@ -2,6 +2,7 @@
 
 # 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
@@ -15,21 +16,11 @@
     pass
 
 import json
-import os
 import socket
 import struct
 import sys
 from zlib import adler32
 
-if __name__ == '__main__':
-    # Add Eric basepath to sys.path to be able to import modules which are
-    # laying not only below Utilities
-    path = os.path.dirname(sys.argv[0])
-    path = os.path.dirname(path)
-    sys.path.append(path)
-
-from Plugins.CheckerPlugins.SyntaxChecker import SyntaxCheck
-
 
 class BackgroundClient(object):
     """
@@ -42,11 +33,30 @@
         @param host ip address the background service is listening
         @param port port of the background service
         """
+        self.services = {}
+        
         self.connection = socket.create_connection((host, port))
         ver = b'2' if sys.version_info[0] == 2 else b'3'
         self.connection.sendall(ver)
         self.connection.settimeout(0.25)
 
+    def __initClientService(self, fn, path, module):
+        """
+        Import the given module and register it as service.
+        
+        @param fn service name to register (str)
+        @param path contains the path to the module (str)
+        @param module name to import (str)
+        @return text result of the import action (str)
+        """
+        sys.path.append(path)
+        try:
+            importedModule = __import__(module, globals(), locals(), [], 0)
+            self.services[fn] = importedModule.initService()
+            return 'ok'
+        except ImportError:
+            return 'Import Error'
+
     def __send(self, fx, fn, data):
         """
         Private method to send a job response back to the BackgroundService.
@@ -80,7 +90,6 @@
                 break
             
             length, datahash = struct.unpack(b'!II', header)
-            
             packedData = b''
             while len(packedData) < length:
                 packedData += self.connection.recv(length - len(packedData))
@@ -89,15 +98,16 @@
                 'Hashes not equal'
             if sys.version_info[0] == 3:
                 packedData = packedData.decode('utf-8')
+            
             fx, fn, data = json.loads(packedData)
-            if fx == 'syntax':
-                ret = SyntaxCheck.syntaxAndPyflakesCheck(fn, *data)
-            elif fx == 'style':
-                print(data)
-            elif fx == 'indent':
-                pass
+            if fx == 'INIT':
+                ret = self.__initClientService(fn, *data)
             else:
-                continue
+                callback = self.services.get(fx)
+                if callback:
+                    ret = callback(fn, *data)
+                else:
+                    ret = 'Unknown service.'
             
             self.__send(fx, fn, ret)
             
--- a/Utilities/BackgroundService.py	Wed Jan 01 22:59:10 2014 +0100
+++ b/Utilities/BackgroundService.py	Sat Jan 04 22:12:42 2014 +0100
@@ -2,6 +2,7 @@
 
 # Copyright (c) 2013 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
 #
+# pylint: disable=C0103
 
 """
 Module implementing a background service for the various checkers and other
@@ -18,11 +19,8 @@
 from zlib import adler32
 
 from PyQt4.QtCore import QProcess, pyqtSignal
-from PyQt4.QtGui import QApplication
 from PyQt4.QtNetwork import QTcpServer, QHostAddress
 
-from E5Gui.E5Application import e5App
-
 import Preferences
 import Utilities
 from Utilities.BackgroundClient import BackgroundClient
@@ -34,9 +32,7 @@
     """
     Class implementing the main part of the background service.
     """
-    syntaxChecked = pyqtSignal(str, bool, str, int, int, str, str, list)
-    #styleChecked = pyqtSignal(TBD)
-    #indentChecked = pyqtSignal(TBD)
+    serviceNotAvailable = pyqtSignal(str, str, int, str)
     
     def __init__(self):
         """
@@ -44,6 +40,9 @@
         """
         self.processes = [None, None]
         self.connections = [None, None]
+        self.isWorking = False
+        self.__queue = []
+        self.services = {}
 
         super(BackgroundService, self).__init__()
 
@@ -73,34 +72,6 @@
                 process = self.__startExternalClient(interpreter, port)
             self.processes[pyIdx] = process
 
-    def on_newConnection(self):
-        """
-        Slot for new incomming connections from the clients.
-        """
-        connection = self.nextPendingConnection()
-        if not connection.waitForReadyRead(1000):
-            return
-        ch = 0 if connection.read(1) == b'2' else 1
-        self.connections[ch] = connection
-        connection.readyRead.connect(
-            lambda x=ch: self.__receive(x))
-
-    def shutdown(self):
-        """
-        Cleanup the connections and processes when Eric is shuting down.
-        """
-        for connection in self.connections:
-            if connection:
-                connection.close()
-        
-        for process in self.processes:
-            if isinstance(process, QProcess):
-                process.close()
-                process = None
-            elif isinstance(process, threading.Thread):
-                process.join(0.1)
-                process = None
-
     def __startExternalClient(self, interpreter, port):
         """
         Private method to start the background client as external process.
@@ -129,35 +100,43 @@
         @param port socket port to which the interpreter should connect (int)
         @return the thread object (Thread) or None
         """
-        self.backgroundClient = BackgroundClient(
+        backgroundClient = BackgroundClient(
             self.hostAddress, port)
-        thread = threading.Thread(target=self.backgroundClient.run)
+        thread = threading.Thread(target=backgroundClient.run)
         thread.start()
         return thread
-
-    # TODO: Implement a queued processing of incomming events. Dublicate file
-    # checks should update an older request to avoid overrun or starving of
-    # the check.
-    def __send(self, fx, fn, data, isPy3):
+    
+    def __processQueue(self):
+        """
+        Private method to take the next service request and send it to the
+        client.
+        """
+        if self.__queue and self.isWorking is False:
+            self.isWorking = True
+            fx, fn, pyVer, data = self.__queue.pop(0)
+            self.__send(fx, fn, pyVer, data)
+    
+    def __send(self, fx, fn, pyVer, data):
         """
         Private method to send a job request to one of the clients.
         
         @param fx remote function name to execute (str)
         @param fn filename for identification (str)
+        @param pyVer version for the required interpreter (int)
         @param data function argument(s) (any basic datatype)
-        @param isPy3 flag for the required interpreter (boolean)
         """
         packedData = json.dumps([fx, fn, data])
         if sys.version_info[0] == 3:
             packedData = bytes(packedData, 'utf-8')
-        connection = self.connections[int(isPy3)]
+        connection = self.connections[pyVer - 2]
         if connection is None:
-            self.__postResult(
-                fx, fn, [
-                    True, fn, 0, 0, '',
-                    'No connection to Python{0} interpreter. '
-                    'Check your debugger settings.'.format(int(isPy3) + 2),
-                    []])
+            if fx != 'INIT':
+                self.serviceNotAvailable.emit(
+                    fx, fn, pyVer, self.trUtf8(
+                        'Python{0} interpreter not configured.').format(pyVer))
+            # Reset flag and continue processing queue
+            self.isWorking = False
+            self.__processQueue()
         else:
             header = struct.pack(
                 b'!II', len(packedData), adler32(packedData) & 0xffffffff)
@@ -183,12 +162,9 @@
         if sys.version_info[0] == 3:
             packedData = packedData.decode('utf-8')
         # "check" if is's a tuple of 3 values
-        try:
-            fx, fn, data = json.loads(packedData)
-            self.__postResult(fx, fn, data)
-        except:
-            pass
-
+        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
@@ -198,57 +174,96 @@
         @param fn filename for identification (str)
         @param data function argument(s) (any basic datatype)
         """
-        if fx == 'syntax':
-            self.syntaxChecked.emit(fn, *data)
-        elif fx == 'style':
-            pass
-        elif fx == 'indent':
+        if fx == 'INIT':
             pass
         elif fx == 'exception':
             # Call sys.excepthook(type, value, traceback) to emulate the
             # exception which was caught on the client
-            sys.excepthook(*data)
-        
-        #QApplication.translate(packedData)
-        
-    # ggf. nach Utilities verschieben
-    def determinePythonVersion(self, filename, source):
-        """
-        Determine the python version of a given file.
+            #sys.excepthook(*data)
+            print(data)
+        else:
+            callback = self.services.get(fx)
+            if callback:
+                callback[2](fn, *data)
         
-        @param filename name of the file with extension (str)
-        @param source of the file (str)
-        @return flag if file is Python2 or Python3 (boolean)
+        self.isWorking = False
+        self.__processQueue()
+
+    def enqueueRequest(self, fx, fn, pyVer, data):
         """
-        flags = Utilities.extractFlags(source)
-        ext = os.path.splitext(filename)[1]
-        project = e5App().getObject('Project')
-        if "FileType" in flags:
-            isPy3 = flags["FileType"] not in ["Python", "Python2"]
-        elif (Preferences.getProject("DeterminePyFromProject") and
-              project.isOpen() and
-              project.isProjectFile(filename)):
-                    isPy3 = project.getProjectLanguage() == "Python3"
+        Implement a queued processing of incomming events.
+        
+        Dublicate file checks update an older request to avoid overrun or
+        starving of the check.
+        @param fx function name of the service (str)
+        @param fn filename for identification (str)
+        @param pyVer version for the required interpreter (int)
+        @param data function argument(s) (any basic datatype)
+        """
+        args = [fx, fn, pyVer, data]
+        if fx == 'INIT':
+            self.__queue.insert(0, args)
         else:
-            isPy3 = ext in Preferences.getPython("PythonExtensions")
-        return isPy3
-
-    def syntaxCheck(self, filename, source="", checkFlakes=True,
-                    ignoreStarImportWarnings=False, isPy3=None):
+            for pendingArg in self.__queue:
+                if pendingArg[:3] == args[:3]:
+                    pendingArg[3] = args[3]
+                    break
+            else:
+                self.__queue.append(args)
+        self.__processQueue()
+    
+    def serviceConnect(
+            self, fx, modulepath, module, callback, onErrorCallback=None):
         """
-        Function to compile one Python source file to Python bytecode
-        and to perform a pyflakes check.
+        Announce a new service to the background service/ client.
+        
+        @param fx function name of the 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])
+        if onErrorCallback:
+            self.serviceNotAvailable.connect(onErrorCallback)
+    
+    def serviceDisconnect(self, fx):
+        """
+        Remove the service from the service list.
         
-        @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 isPy3 flag sets the interpreter to use or None for autodetect
-            corresponding interpreter (boolean or None)
+        @param fx function name of the service
+        """
+        self.services.pop(fx, None)
+
+    def on_newConnection(self):
+        """
+        Slot for new incomming connections from the clients.
         """
-        if isPy3 is None:
-            isPy3 = self.determinePythonVersion(filename, source)
+        connection = self.nextPendingConnection()
+        if not connection.waitForReadyRead(1000):
+            return
+        ch = 0 if connection.read(1) == b'2' else 1
+        self.connections[ch] = connection
+        connection.readyRead.connect(
+            lambda x=ch: self.__receive(x))
         
-        data = [source, checkFlakes, ignoreStarImportWarnings]
-        self.__send('syntax', filename, data, isPy3)
+        for fx, args in self.services.items():
+            self.enqueueRequest('INIT', fx, ch, args[:2])
+
+    def shutdown(self):
+        """
+        Cleanup the connections and processes when Eric is shuting down.
+        """
+        for connection in self.connections:
+            if connection:
+                connection.close()
+        
+        for process in self.processes:
+            if isinstance(process, QProcess):
+                process.close()
+                process = None
+            elif isinstance(process, threading.Thread):
+                process.join(0.1)
+                process = None
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Utilities/InternalServices.py	Sat Jan 04 22:12:42 2014 +0100
@@ -0,0 +1,99 @@
+# -*- 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(TBD)
+    #indentChecked = pyqtSignal(TBD)
+
+    def __init__(self, backgroundService):
+        """
+        Contructor of InternalServices.
+        
+        @param backgroundService to connect to
+        """
+        super(InternalServices, self).__init__()
+        self.backgroundService = backgroundService
+        
+        path = os.path.join(
+            getConfig('ericDir'), '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, []))
+
+    def syntaxCheck(self, filename, source="", checkFlakes=True,
+                    ignoreStarImportWarnings=False, pyVer=None, editor=None):
+        """
+        Function to compile one Python source file to Python bytecode
+        and to perform a pyflakes check.
+        
+        @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, message)
+        """
+        for warning in warnings:
+            # Translate messages
+            msg_args = warning.pop()
+            translated = QApplication.translate(
+                'py3Flakes', warning[3]).format(*msg_args)
+            # Avoid leading "u" at Python2 unicode strings
+            if translated.startswith("u'"):
+                translated = translated[1:]
+            warning[3] = translated.replace(" u'", " '")
+        
+        self.syntaxChecked.emit(
+            fn, nok, fname, line, index, code, error, warnings)
--- a/Utilities/__init__.py	Wed Jan 01 22:59:10 2014 +0100
+++ b/Utilities/__init__.py	Sat Jan 04 22:12:42 2014 +0100
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 
-# Copyright (c) 2003 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
+# Copyright (c) 2003 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
 #
 
 """
@@ -70,13 +70,16 @@
 from UI.Info import Program, Version
 
 import Preferences
-from .SyntaxCheck import (readEncodedFile, decode,  # __IGNORE_WARNING__
-    extractLineFlags, normalizeCode, compile_and_check)
+from Plugins.CheckerPlugins.SyntaxChecker.SyntaxCheck import normalizeCode
 
 from eric5config import getConfig
 
 configDir = None
 
+codingBytes_regexps = [
+    (2, re.compile(br'''coding[:=]\s*([-\w_.]+)''')),
+    (1, re.compile(br'''<\?xml.*\bencoding\s*=\s*['"]([-\w_.]+)['"]\?>''')),
+]
 coding_regexps = [
     (2, re.compile(r'''coding[:=]\s*([-\w_.]+)''')),
     (1, re.compile(r'''<\?xml.*\bencoding\s*=\s*['"]([-\w_.]+)['"]\?>''')),
@@ -134,6 +137,24 @@
         return str(self.errorMessage)
     
 
+def get_codingBytes(text):
+    """
+    Function to get the coding of a bytes text.
+    
+    @param text bytes text to inspect (bytes)
+    @return coding string
+    """
+    lines = text.splitlines()
+    for coding in codingBytes_regexps:
+        coding_re = coding[1]
+        head = lines[:coding[0]]
+        for l in head:
+            m = coding_re.search(l)
+            if m:
+                return str(m.group(1), "ascii").lower()
+    return None
+
+
 def get_coding(text):
     """
     Function to get the coding of a text.
@@ -152,6 +173,19 @@
     return None
 
 
+def readEncodedFile(filename):
+    """
+    Function to read a file and decode its contents into proper text.
+    
+    @param filename name of the file to read (string)
+    @return tuple of decoded text and encoding (string, string)
+    """
+    f = open(filename, "rb")
+    text = f.read()
+    f.close()
+    return decode(text)
+
+
 def readEncodedFileWithHash(filename):
     """
     Function to read a file, calculate a hash value and decode its contents
@@ -170,6 +204,70 @@
     return decode(text) + (hash, )
 
 
+def decode(text):
+    """
+    Function to decode some byte text into a string.
+    
+    @param text byte text to decode (bytes)
+    @return tuple of decoded text and encoding (string, string)
+    """
+    try:
+        if text.startswith(BOM_UTF8):
+            # UTF-8 with BOM
+            return str(text[len(BOM_UTF8):], 'utf-8'), 'utf-8-bom'
+        elif text.startswith(BOM_UTF16):
+            # UTF-16 with BOM
+            return str(text[len(BOM_UTF16):], 'utf-16'), 'utf-16'
+        elif text.startswith(BOM_UTF32):
+            # UTF-32 with BOM
+            return str(text[len(BOM_UTF32):], 'utf-32'), 'utf-32'
+        coding = get_codingBytes(text)
+        if coding:
+            return str(text, coding), coding
+    except (UnicodeError, LookupError):
+        pass
+    
+    # Assume UTF-8
+    try:
+        return str(text, 'utf-8'), 'utf-8-guessed'
+    except (UnicodeError, LookupError):
+        pass
+    
+    guess = None
+    if Preferences.getEditor("AdvancedEncodingDetection"):
+        # Try the universal character encoding detector
+        try:
+            import ThirdParty.CharDet.chardet
+            guess = ThirdParty.CharDet.chardet.detect(text)
+            if guess and guess['confidence'] > 0.95 and \
+                    guess['encoding'] is not None:
+                codec = guess['encoding'].lower()
+                return str(text, codec), '{0}-guessed'.format(codec)
+        except (UnicodeError, LookupError):
+            pass
+        except ImportError:
+            pass
+    
+    # Try default encoding
+    try:
+        codec = Preferences.getEditor("DefaultEncoding")
+        return str(text, codec), '{0}-default'.format(codec)
+    except (UnicodeError, LookupError):
+        pass
+    
+    if Preferences.getEditor("AdvancedEncodingDetection"):
+        # Use the guessed one even if confifence level is low
+        if guess and guess['encoding'] is not None:
+            try:
+                codec = guess['encoding'].lower()
+                return str(text, codec), '{0}-guessed'.format(codec)
+            except (UnicodeError, LookupError):
+                pass
+    
+    # Assume UTF-8 loosing information
+    return str(text, "utf-8", "ignore"), 'utf-8-ignore'
+
+
 def writeEncodedFile(filename, text, orig_coding):
     """
     Function to write a file with properly encoded text.
@@ -523,6 +621,28 @@
     return extractFlags(source)
 
 
+def extractLineFlags(line, startComment="#", endComment=""):
+    """
+    Function to extract flags starting and ending with '__' from a line
+    comment.
+    
+    @param line line to extract flags from (string)
+    @keyparam startComment string identifying the start of the comment (string)
+    @keyparam endComment string identifying the end of a comment (string)
+    @return list containing the extracted flags (list of strings)
+    """
+    flags = []
+    
+    pos = line.rfind(startComment)
+    if pos >= 0:
+        comment = line[pos + len(startComment):].strip()
+        if endComment:
+            comment = comment.replace("endComment", "")
+        flags = [f.strip() for f in comment.split()
+                 if (f.startswith("__") and f.endswith("__"))]
+    return flags
+
+
 def toNativeSeparators(path):
     """
     Function returning a path, that is using native separator characters.
@@ -1173,127 +1293,49 @@
     @return An integer representing major and minor version number (integer)
     """
     return sys.hexversion >> 16
-    
-
-def compile(file, codestring="", isPy2=False):
-    """
-    Function to compile one Python source file to Python bytecode.
-    
-    @param file source filename (string)
-    @param codestring string containing the code to compile (string)
-    @param isPy2 shows which interperter to use (boolean)
-    @return A tuple indicating status (True = an error was found), the
-        file name, the line number, the index number, the code string
-        and the error message (boolean, string, string, string, string,
-        string). The values are only valid, if the status is True.
-    """
-    from PyQt4.QtCore import QCoreApplication
-    
-    interpreter_name = 'Python' if isPy2 else 'Python3'
-    interpreter = Preferences.getDebugger(
-        interpreter_name + "Interpreter")
-    checkFlakes = Preferences.getFlakes("IncludeInSyntaxCheck")
-    ignoreStarImportWarnings = Preferences.getFlakes(
-        "IgnoreStarImportWarnings")
-    if samefilepath(interpreter, sys.executable):
-        ret = compile_and_check(
-            file, codestring, checkFlakes, ignoreStarImportWarnings)
-    else:
-        #TODO: create temporary file if only a codestring is given
-        ret = compile_extern(
-            file, isPy2, checkFlakes, ignoreStarImportWarnings)
-    
-    # Translate messages
-    for warning in ret[6]:
-        msg_args = warning.pop()
-        translated = QCoreApplication.translate(
-            'py3Flakes', warning[-1]).format(*msg_args)
-        # Avoid leading "u" at Python2 unicode strings
-        if translated.startswith("u'"):
-            translated = translated[1:]
-        warning[3] = translated.replace(" u'", " '")
-    
-    return ret
 
 
-def compile_extern(
-        file, isPy2, checkFlakes=True, ignoreStarImportWarnings=False):
+def determinePythonVersion(filename, source, editor=None):
     """
-    Function to compile one Python source file to Python bytecode.
+    Determine the python version of a given file.
     
-    @param file source filename (string)
-    @param isPy2 flag indicating if it's a Python 2 or 3 file (boolean)
-    @keyparam checkFlakes flag indicating to do a pyflakes check (boolean)
-    @keyparam ignoreStarImportWarnings flag if star import warnings should be
-        suppressed (boolean)
-    @return A tuple indicating status (True = an error was found), the
-        file name, the line number, the index number, the code string,
-        the error message and a list of tuples of pyflakes warnings indicating
-        file name, line number and message (boolean, string, string, string,
-        string, string, list of (string, string, string)). The syntax error
-        values are only valid, if the status is True. The pyflakes list will
-        be empty, if a syntax error was detected by the syntax checker.
+    @param filename name of the file with extension (str)
+    @param source of the file (str)
+    @keyparam editor if the file is opened already (Editor object)
+    @return flag if file is Python2 or Python3 (int)
     """
-    interpreter_name = 'Python' if isPy2 else 'Python3'
-    interpreter = Preferences.getDebugger(interpreter_name + "Interpreter")
-    if interpreter == "" or not isinpath(interpreter):
-        return (True, file, 1, 0, "",
-                QCoreApplication.translate(
-                    "Utilities",
-                    "{0} interpreter not configured.")
-                .format(interpreter_name), [])
-    syntaxChecker = os.path.join(getConfig('ericDir'),
-                                 "Utilities", "SyntaxCheck.py")
-    args = [syntaxChecker]
-    if checkFlakes:
-        if ignoreStarImportWarnings:
-            args.append("-fi")
-        else:
-            args.append("-fs")
-    args.append(file)
-    proc = QProcess()
-    proc.setProcessChannelMode(QProcess.MergedChannels)
-    proc.start(interpreter, args)
-    finished = proc.waitForFinished(30000)
-    if finished:
-        output = codecs.decode(
-            proc.readAllStandardOutput(),
-            sys.getfilesystemencoding(), 'strict').splitlines()
+    pyAssignment = {"Python": 2, "Python2": 2, "Python3": 3}
+    
+    flags = extractFlags(source)
+    ext = os.path.splitext(filename)[1]
+    py2Ext = Preferences.getPython("PythonExtensions")
+    py3Ext = Preferences.getPython("Python3Extensions")
+    project = e5App().getObject('Project')
+
+    pyVer = 0
+    if editor and editor.getLanguage() in pyAssignment:
+        pyVer = pyAssignment.get(editor.getLanguage())
+    elif "FileType" in flags:
+        pyVer = pyAssignment.get(flags["FileType"], 0)
+    elif (Preferences.getProject("DeterminePyFromProject") and
+          project.isOpen() and
+          project.isProjectFile(filename)):
+                pyVer = pyAssignment.get(project.getProjectLanguage(), 0)
+    elif ext in py2Ext and ext not in py3Ext:
+        pyVer = 2
+    elif ext in py3Ext and ext not in py2Ext:
+        pyVer = 3
+    elif source.startswith("#!"):
+        line0 = source.splitlines()[0]
+        if "python3" in line0:
+            pyVer = 3
+        elif "python" in line0:
+            pyVer = 2
+    
+    if pyVer == 0 and ext in py2Ext + py3Ext:
+        pyVer = sys.version_info[0]
         
-        if output:
-            syntaxerror = output[0] == "ERROR"
-            if syntaxerror:
-                fn = output[1]
-                line = int(output[2])
-                index = int(output[3])
-                code = output[4]
-                error = output[5]
-                return (True, fn, line, index, code, error, [])
-            else:
-                index = 6
-                warnings = []
-                while len(output) - index > 3:
-                    if output[index] == "FLAKES_ERROR":
-                        return (True, output[index + 1],
-                                int(output[index + 2]), -1,
-                                '', output[index + 3], [])
-                    else:
-                        msg_args = output[index + 4].split('#')
-                        warnings.append([
-                            output[index], output[index + 1],
-                            int(output[index + 2]), output[index + 3],
-                            msg_args])
-                        index += 5
-                
-                return (False, None, None, None, None, None, warnings)
-        else:
-            return (False, "", -1, -1, "", "", [])
-    
-    return (True, file, 1, 0, "",
-            QCoreApplication.translate(
-                "Utilities",
-                "{0} interpreter did not finish within 30s.").format(
-                interpreter_name), [])
+    return pyVer
 
 
 ###############################################################################
--- a/Utilities/py3flakes/__init__.py	Wed Jan 01 22:59:10 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2010 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-
-"""
-Package containg the pyflakes Python3 port adapted for Qt.
-"""
-
-__version__ = '0.5.0'
--- a/Utilities/py3flakes/checker.py	Wed Jan 01 22:59:10 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,680 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2010 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-# Original (c) 2005-2010 Divmod, Inc.
-#
-# This module is based on pyflakes for Python2 but was heavily hacked to
-# work with Python3 and eric5
-
-import builtins
-import os.path
-import ast
-
-from . import messages
-
-
-class Binding(object):
-    """
-    Represents the binding of a value to a name.
-
-    The checker uses this to keep track of which names have been bound and
-    which names have not. See Assignment for a special type of binding that
-    is checked with stricter rules.
-    """
-    def __init__(self, name, source):
-        self.name = name
-        self.source = source
-        self.used = False
-
-    def __str__(self):
-        return self.name
-
-    def __repr__(self):
-        return '<{0} object {1!r} from line {2!r} at 0x{3:x}>'.format(
-            self.__class__.__name__,
-            self.name,
-            self.source.lineno,
-            id(self))
-
-
-class UnBinding(Binding):
-    '''
-    Created by the 'del' operator.
-    '''
-    pass
-
-
-class Importation(Binding):
-    """
-    A binding created by an import statement.
-    """
-    def __init__(self, name, source):
-        self.fullName = name
-        name = name.split('.')[0]
-        super(Importation, self).__init__(name, source)
-
-
-class Argument(Binding):
-    """
-    Represents binding a name as an argument.
-    """
-    pass
-
-
-class Assignment(Binding):
-    """
-    Represents binding a name with an explicit assignment.
-
-    The checker will raise warnings for any Assignment that isn't used. Also,
-    the checker does not consider assignments in tuple/list unpacking to be
-    Assignments, rather it treats them as simple Bindings.
-    """
-    pass
-
-
-class FunctionDefinition(Binding):
-    """
-    Represents a function definition.
-    """
-    is_property = False
-
-
-class ExportBinding(Binding):
-    """
-    A binding created by an __all__ assignment.  If the names in the list
-    can be determined statically, they will be treated as names for export and
-    additional checking applied to them.
-
-    The only __all__ assignment that can be recognized is one which takes
-    the value of a literal list containing literal strings.  For example::
-
-        __all__ = ["foo", "bar"]
-
-    Names which are imported and not otherwise used but appear in the value of
-    __all__ will not have an unused import warning reported for them.
-    """
-    def names(self):
-        """
-        Return a list of the names referenced by this binding.
-        """
-        names = []
-        if isinstance(self.source, ast.List):
-            for node in self.source.elts:
-                if isinstance(node, (ast.Str, ast.Bytes)):
-                    names.append(node.s)
-                elif isinstance(node, ast.Num):
-                    names.append(node.n)
-        return names
-
-
-class Scope(dict):
-    """
-    Class defining the scope base class.
-    """
-    importStarred = False       # set to True when import * is found
-
-    def __repr__(self):
-        return '<{0} at 0x{1:x} {2}>'.format(
-            self.__class__.__name__, id(self), dict.__repr__(self))
-
-    def __init__(self):
-        super(Scope, self).__init__()
-
-
-class ClassScope(Scope):
-    """
-    Class representing a name scope for a class.
-    """
-    pass
-
-
-class FunctionScope(Scope):
-    """
-    Class representing a name scope for a function.
-    """
-    def __init__(self):
-        super(FunctionScope, self).__init__()
-        self.globals = {}
-
-
-class ModuleScope(Scope):
-    """
-    Class representing a name scope for a module.
-    """
-    pass
-
-# Globally defined names which are not attributes of the builtins module.
-_MAGIC_GLOBALS = ['__file__', '__builtins__']
-
-
-class Checker(object):
-    """
-    Class to check the cleanliness and sanity of Python code.
-    """
-    nodeDepth = 0
-    traceTree = False
-
-    def __init__(self, module, filename='(none)'):
-        """
-        Constructor
-        
-        @param module parsed module tree or module source code
-        @param filename name of the module file (string)
-        """
-        self._deferredFunctions = []
-        self._deferredAssignments = []
-        self.dead_scopes = []
-        self.messages = []
-        self.filename = filename
-        self.scopeStack = [ModuleScope()]
-        self.futuresAllowed = True
-        
-        if isinstance(module, str):
-            module = ast.parse(module, filename, "exec")
-        self.handleBody(module)
-        self._runDeferred(self._deferredFunctions)
-        # Set _deferredFunctions to None so that deferFunction will fail
-        # noisily if called after we've run through the deferred functions.
-        self._deferredFunctions = None
-        self._runDeferred(self._deferredAssignments)
-        # Set _deferredAssignments to None so that deferAssignment will fail
-        # noisly if called after we've run through the deferred assignments.
-        self._deferredAssignments = None
-        del self.scopeStack[1:]
-        self.popScope()
-        self.check_dead_scopes()
-
-    def deferFunction(self, callable):
-        '''
-        Schedule a function handler to be called just before completion.
-
-        This is used for handling function bodies, which must be deferred
-        because code later in the file might modify the global scope. When
-        `callable` is called, the scope at the time this is called will be
-        restored, however it will contain any new bindings added to it.
-        '''
-        self._deferredFunctions.append((callable, self.scopeStack[:]))
-
-    def deferAssignment(self, callable):
-        """
-        Schedule an assignment handler to be called just after deferred
-        function handlers.
-        """
-        self._deferredAssignments.append((callable, self.scopeStack[:]))
-
-    def _runDeferred(self, deferred):
-        """
-        Run the callables in deferred using their associated scope stack.
-        """
-        for handler, scope in deferred:
-            self.scopeStack = scope
-            handler()
-
-    def scope(self):
-        return self.scopeStack[-1]
-    scope = property(scope)
-
-    def popScope(self):
-        self.dead_scopes.append(self.scopeStack.pop())
-
-    def check_dead_scopes(self):
-        """
-        Look at scopes which have been fully examined and report names in them
-        which were imported but unused.
-        """
-        for scope in self.dead_scopes:
-            export = isinstance(scope.get('__all__'), ExportBinding)
-            if export:
-                all = scope['__all__'].names()
-                if os.path.split(self.filename)[1] != '__init__.py':
-                    # Look for possible mistakes in the export list
-                    undefined = set(all) - set(scope)
-                    for name in undefined:
-                        self.report(
-                            messages.UndefinedExport,
-                            scope['__all__'].source.lineno,
-                            name)
-            else:
-                all = []
-
-            # Look for imported names that aren't used.
-            for importation in scope.values():
-                if isinstance(importation, Importation):
-                    if not importation.used and importation.name not in all:
-                        self.report(
-                            messages.UnusedImport,
-                            importation.source.lineno,
-                            importation.name)
-
-    def pushFunctionScope(self):
-        self.scopeStack.append(FunctionScope())
-
-    def pushClassScope(self):
-        self.scopeStack.append(ClassScope())
-
-    def report(self, messageClass, *args, **kwargs):
-        self.messages.append(messageClass(self.filename, *args, **kwargs))
-
-    def handleBody(self, tree):
-        for node in tree.body:
-            self.handleNode(node, tree)
-
-    def handleChildren(self, tree):
-        for node in ast.iter_child_nodes(tree):
-            self.handleNode(node, tree)
-    
-    def isDocstring(self, node):
-        """
-        Determine if the given node is a docstring, as long as it is at the
-        correct place in the node tree.
-        """
-        return isinstance(node, ast.Str) or \
-               (isinstance(node, ast.Expr) and
-                isinstance(node.value, ast.Str))
-    
-    def handleNode(self, node, parent):
-        if node:
-            node.parent = parent
-            if self.traceTree:
-                print('  ' * self.nodeDepth + node.__class__.__name__)
-            self.nodeDepth += 1
-            if self.futuresAllowed and \
-                    not (isinstance(node, ast.ImportFrom) or 
-                         self.isDocstring(node)):
-                self.futuresAllowed = False
-            nodeType = node.__class__.__name__.upper()
-            try:
-                handler = getattr(self, nodeType)
-                handler(node)
-            except AttributeError:
-                print(nodeType, "not supported yet. Please report this.")
-            finally:
-                self.nodeDepth -= 1
-            if self.traceTree:
-                print('  ' * self.nodeDepth + 'end ' + node.__class__.__name__)
-
-    def ignore(self, node):
-        pass
-    
-    # ast nodes to be ignored
-    PASS = CONTINUE = BREAK = ELLIPSIS = NUM = STR = BYTES = \
-    LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = \
-    ATTRIBUTES = AND = OR = ADD = SUB = MULT = DIV = \
-    MOD = POW = LSHIFT = RSHIFT = BITOR = BITXOR = BITAND = FLOORDIV = \
-    INVERT = NOT = UADD = USUB = EQ = NOTEQ = LT = LTE = GT = GTE = IS = \
-    ISNOT = IN = NOTIN = ignore
-
-    # "stmt" type nodes
-    RETURN = DELETE = PRINT = WHILE = IF = WITH = WITHITEM = RAISE = \
-    TRY = TRYEXCEPT = TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren
-    
-    # "expr" type nodes
-    BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = COMPARE = \
-    CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = handleChildren
-    
-    # "slice" type nodes
-    SLICE = EXTSLICE = INDEX = handleChildren
-    
-    # additional node types
-    COMPREHENSION = KEYWORD = handleChildren
-    
-    def addBinding(self, lineno, value, reportRedef=True):
-        '''
-        Called when a binding is altered.
-
-        @param lineno line of the statement responsible for the change
-            (integer)
-        @param value the optional new value, a Binding instance, associated
-            with the binding; if None, the binding is deleted if it exists
-        @param reportRedef flag indicating if rebinding while unused will be
-            reported (boolean)
-        '''
-        if (isinstance(self.scope.get(value.name), FunctionDefinition)
-                    and isinstance(value, FunctionDefinition)
-                    and not self.scope.get(value.name).is_property
-                    and not value.is_property):
-            self.report(messages.RedefinedFunction,
-                        lineno, value.name,
-                        self.scope[value.name].source.lineno)
-
-        if not isinstance(self.scope, ClassScope):
-            for scope in self.scopeStack[::-1]:
-                existing = scope.get(value.name)
-                if isinstance(existing, Importation) and \
-                   not existing.used and \
-                   not isinstance(value, UnBinding) and \
-                   (not isinstance(value, Importation) or \
-                    value.fullName == existing.fullName) and \
-                   reportRedef:
-                    self.report(messages.RedefinedWhileUnused,
-                                lineno, value.name,
-                                scope[value.name].source.lineno)
-
-        if isinstance(value, UnBinding):
-            try:
-                del self.scope[value.name]
-            except KeyError:
-                self.report(messages.UndefinedName, lineno, value.name)
-        else:
-            self.scope[value.name] = value
-    
-    ############################################################
-    ## individual handler methods below
-    ############################################################
-    
-    def GLOBAL(self, node):
-        """
-        Keep track of globals declarations.
-        """
-        if isinstance(self.scope, FunctionScope):
-            self.scope.globals.update(dict.fromkeys(node.names))
-    
-    NONLOCAL = GLOBAL
-
-    def LISTCOMP(self, node):
-        for generator in node.generators:
-            self.handleNode(generator, node)
-        self.handleNode(node.elt, node)
-    
-    SETCOMP = GENERATOREXP = LISTCOMP
-    
-    def DICTCOMP(self, node):
-        for generator in node.generators:
-            self.handleNode(generator, node)
-        self.handleNode(node.key, node)
-        self.handleNode(node.value, node)
-    
-    def FOR(self, node):
-        """
-        Process bindings for loop variables.
-        """
-        vars = []
-
-        def collectLoopVars(n):
-            if isinstance(n, ast.Name):
-                vars.append(n.id)
-            elif isinstance(n, ast.expr_context):
-                return
-            else:
-                for c in ast.iter_child_nodes(n):
-                    collectLoopVars(c)
-
-        collectLoopVars(node.target)
-        for varn in vars:
-            if (isinstance(self.scope.get(varn), Importation)
-                    # unused ones will get an unused import warning
-                    and self.scope[varn].used):
-                self.report(messages.ImportShadowedByLoopVar,
-                            node.lineno, varn, self.scope[varn].source.lineno)
-        
-        self.handleChildren(node)
-
-    def NAME(self, node):
-        """
-        Handle occurrence of Name (which can be a load/store/delete access.)
-        """
-        # Locate the name in locals / function / globals scopes.
-        if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
-            # try local scope
-            importStarred = self.scope.importStarred
-            try:
-                self.scope[node.id].used = (self.scope, node.lineno)
-            except KeyError:
-                pass
-            else:
-                return
-
-            # try enclosing function scopes
-            for scope in self.scopeStack[-2:0:-1]:
-                importStarred = importStarred or scope.importStarred
-                if not isinstance(scope, FunctionScope):
-                    continue
-                try:
-                    scope[node.id].used = (self.scope, node.lineno)
-                except KeyError:
-                    pass
-                else:
-                    return
-
-            # try global scope
-            importStarred = importStarred or self.scopeStack[0].importStarred
-            try:
-                self.scopeStack[0][node.id].used = (self.scope, node.lineno)
-            except KeyError:
-                if ((not hasattr(builtins, node.id))
-                        and node.id not in _MAGIC_GLOBALS
-                        and not importStarred):
-                    if (os.path.basename(self.filename) == '__init__.py' and
-                        node.id == '__path__'):
-                        # the special name __path__ is valid only in packages
-                        pass
-                    else:
-                        self.report(messages.UndefinedName,
-                                    node.lineno, node.id)
-        elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
-            # if the name hasn't already been defined in the current scope
-            if isinstance(self.scope, FunctionScope) and \
-                    node.id not in self.scope:
-                # for each function or module scope above us
-                for scope in self.scopeStack[:-1]:
-                    if not isinstance(scope, (FunctionScope, ModuleScope)):
-                        continue
-                    # if the name was defined in that scope, and the name has
-                    # been accessed already in the current scope, and hasn't
-                    # been declared global
-                    if (node.id in scope
-                            and scope[node.id].used
-                            and scope[node.id].used[0] is self.scope
-                            and node.id not in self.scope.globals):
-                        # then it's probably a mistake
-                        self.report(messages.UndefinedLocal,
-                                    scope[node.id].used[1],
-                                    node.id,
-                                    scope[node.id].source.lineno)
-                        break
-
-            if isinstance(node.parent,
-                          (ast.For, ast.comprehension, ast.Tuple, ast.List)):
-                binding = Binding(node.id, node)
-            elif (node.id == '__all__' and
-                  isinstance(self.scope, ModuleScope)):
-                binding = ExportBinding(node.id, node.parent.value)
-            else:
-                binding = Assignment(node.id, node)
-            if node.id in self.scope:
-                binding.used = self.scope[node.id].used
-            self.addBinding(node.lineno, binding)
-        elif isinstance(node.ctx, ast.Del):
-            if isinstance(self.scope, FunctionScope) and \
-                   node.id in self.scope.globals:
-                del self.scope.globals[node.id]
-            else:
-                self.addBinding(node.lineno, UnBinding(node.id, node))
-        else:
-            # must be a Param context -- this only happens for names in
-            # function arguments, but these aren't dispatched through here
-            raise RuntimeError(
-                "Got impossible expression context: {0:r}".format(node.ctx,))
-
-    def FUNCTIONDEF(self, node):
-        is_property = False
-        if hasattr(node, "decorator_list"):
-            for decorator in node.decorator_list:
-                self.handleNode(decorator, node)
-                if getattr(decorator, 'id', None) == 'property':
-                    is_property = True
-                if getattr(decorator, 'attr', None) in ('setter', 'deleter'):
-                    is_property = True
-        funcdef = FunctionDefinition(node.name, node)
-        funcdef.is_property = is_property
-        self.addBinding(node.lineno, funcdef)
-        self.LAMBDA(node)
-
-    def LAMBDA(self, node):
-        for default in node.args.defaults + node.args.kw_defaults:
-            self.handleNode(default, node)
-
-        def runFunction():
-            args = []
-
-            def addArgs(arglist):
-                for arg in arglist:
-                    if isinstance(arg.arg, tuple):
-                        addArgs(arg.arg)
-                    else:
-                        if arg.arg in args:
-                            self.report(messages.DuplicateArgument,
-                                        node.lineno, arg.arg)
-                        args.append(arg.arg)
-            
-            def checkUnusedAssignments():
-                """
-                Check to see if any assignments have not been used.
-                """
-                for name, binding in self.scope.items():
-                    if (not binding.used and not name in self.scope.globals
-                        and isinstance(binding, Assignment)):
-                        self.report(messages.UnusedVariable,
-                                    binding.source.lineno, name)
-
-            self.pushFunctionScope()
-            addArgs(node.args.args)
-            addArgs(node.args.kwonlyargs)
-            # vararg/kwarg identifiers are not Name nodes
-            if node.args.vararg:
-                args.append(node.args.vararg)
-            if node.args.kwarg:
-                args.append(node.args.kwarg)
-            for name in args:
-                self.addBinding(node.lineno, Argument(name, node),
-                                reportRedef=False)
-            if isinstance(node.body, list):
-                self.handleBody(node)
-            else:
-                self.handleNode(node.body, node)
-            self.deferAssignment(checkUnusedAssignments)
-            self.popScope()
-
-        self.deferFunction(runFunction)
-
-    def CLASSDEF(self, node):
-        """
-        Check names used in a class definition, including its decorators, base
-        classes, and the body of its definition.  Additionally, add its name to
-        the current scope.
-        """
-        for decorator in getattr(node, "decorator_list", []):
-            self.handleNode(decorator, node)
-        for baseNode in node.bases:
-            self.handleNode(baseNode, node)
-        self.addBinding(node.lineno, Binding(node.name, node))
-        self.pushClassScope()
-        self.handleBody(node)
-        self.popScope()
-
-    def handleAssignName(self, node):
-        # special handling for ast.Subscript and ast.Starred
-        if isinstance(node, (ast.Subscript, ast.Starred)):
-            node.value.parent = node
-            self.handleAssignName(node.value)
-            if isinstance(node, ast.Subscript):
-                if isinstance(node.slice, ast.Slice):
-                    self.handleNode(node.slice.lower, node)
-                    self.handleNode(node.slice.upper, node)
-                else:
-                    self.handleNode(node.slice.value, node)
-            return
-        
-        # if the name hasn't already been defined in the current scope
-        if isinstance(node, (ast.Tuple, ast.List)):
-            for elt in node.elts:
-                elt.parent = node
-                self.handleAssignName(elt)
-            return
-        
-        if isinstance(node, ast.Attribute):
-            self.handleNode(node.value, node)
-            return
-        
-        if isinstance(self.scope, FunctionScope) and node.id not in self.scope:
-            # for each function or module scope above us
-            for scope in self.scopeStack[:-1]:
-                if not isinstance(scope, (FunctionScope, ModuleScope)):
-                    continue
-                # if the name was defined in that scope, and the name has
-                # been accessed already in the current scope, and hasn't
-                # been declared global
-                if (node.id in scope
-                        and scope[node.id].used
-                        and scope[node.id].used[0] is self.scope
-                        and node.id not in self.scope.globals):
-                    # then it's probably a mistake
-                    self.report(messages.UndefinedLocal,
-                                scope[node.id].used[1],
-                                node.id,
-                                scope[node.id].source.lineno)
-                    break
-
-        if isinstance(node.parent,
-                      (ast.For, ast.ListComp, ast.GeneratorExp,
-                       ast.Tuple, ast.List)):
-            binding = Binding(node.id, node)
-        elif (node.id == '__all__' and
-              isinstance(self.scope, ModuleScope) and
-              isinstance(node.parent, ast.Assign)):
-            binding = ExportBinding(node.id, node.parent.value)
-        else:
-            binding = Assignment(node.id, node)
-        if node.id in self.scope:
-            binding.used = self.scope[node.id].used
-        self.addBinding(node.lineno, binding)
-
-    def ASSIGN(self, node):
-        self.handleNode(node.value, node)
-        for target in node.targets:
-            self.handleNode(target, node)
-    
-    def AUGASSIGN(self, node):
-        # AugAssign is awkward: must set the context explicitly and
-        # visit twice, once with AugLoad context, once with AugStore context
-        node.target.ctx = ast.AugLoad()
-        self.handleNode(node.target, node)
-        self.handleNode(node.value, node)
-        node.target.ctx = ast.AugStore()
-        self.handleNode(node.target, node)
-    
-    def IMPORT(self, node):
-        for alias in node.names:
-            name = alias.asname or alias.name
-            importation = Importation(name, node)
-            self.addBinding(node.lineno, importation)
-
-    def IMPORTFROM(self, node):
-        if node.module == '__future__':
-            if not self.futuresAllowed:
-                self.report(messages.LateFutureImport, node.lineno,
-                            [n.name for n in node.names])
-        else:
-            self.futuresAllowed = False
-
-        for alias in node.names:
-            if alias.name == '*':
-                self.scope.importStarred = True
-                self.report(messages.ImportStarUsed, node.lineno, node.module)
-                continue
-            name = alias.asname or alias.name
-            importation = Importation(name, node)
-            if node.module == '__future__':
-                importation.used = (self.scope, node.lineno)
-            self.addBinding(node.lineno, importation)
-    
-    def EXCEPTHANDLER(self, node):
-        node.type and self.handleNode(node.type, node)
-        if node.name:
-            node.id = node.name
-            self.handleAssignName(node)
-        self.handleBody(node)
-    
-    def STARRED(self, node):
-        self.handleNode(node.value, node)
--- a/Utilities/py3flakes/messages.py	Wed Jan 01 22:59:10 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,287 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2010 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-# Original (c) 2005 Divmod, Inc.  See LICENSE file for details
-#
-# This module is based on pyflakes for Python2 but was heavily hacked to
-# work with Python3 and Qt (translatable messages)
-
-"""
-Module implementing the messages for py3flakes.
-"""
-
-
-def QT_TRANSLATE_NOOP(mod, txt):
-    """
-    Function to tell 'lupdate' which strings to keep for translation.
-    
-    @param mod module name
-    @param txt translatable string
-    @return the untranslated! string
-    """
-    return txt
-
-
-class Message(object):
-    """
-    Class defining the base for all specific message classes.
-    """
-    message = ''
-    message_args = ()
-    
-    def __init__(self, filename, lineno):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        """
-        self.filename = filename
-        self.lineno = lineno
-    
-    def __str__(self):
-        """
-        Special method return a string representation of the instance object.
-        
-        @return string representation of the object (string)
-        """
-        return '{0}:{1} {2}'.format(
-            self.filename, self.lineno,
-            self.message.format(*self.message_args))
-    
-    def getMessageData(self):
-        """
-        Public method to get the individual message data elements.
-        
-        @return tuple containing file name, line number and message
-            (string, integer, string)
-        """
-        return (self.filename, self.lineno,
-                self.message, self.message_args)
-
-
-class UnusedImport(Message):
-    """
-    Class defining the "Unused Import" message.
-    """
-    message = QT_TRANSLATE_NOOP(
-        'py3Flakes',
-        '{0!r} imported but unused.')
-    
-    def __init__(self, filename, lineno, name):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param name name of the unused import (string)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name,)
-
-
-class RedefinedWhileUnused(Message):
-    """
-    Class defining the "Redefined While Unused" message.
-    """
-    message = QT_TRANSLATE_NOOP(
-        'py3Flakes',
-        'Redefinition of unused {0!r} from line {1!r}.')
-
-    def __init__(self, filename, lineno, name, orig_lineno):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param name name of the redefined object (string)
-        @param orig_lineno line number of the original definition (integer)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name, orig_lineno)
-
-
-class ImportShadowedByLoopVar(Message):
-    """
-    Class defining the "Import Shadowed By Loop Var" message.
-    """
-    message = QT_TRANSLATE_NOOP(
-        'py3Flakes',
-        'Import {0!r} from line {1!r} shadowed by loop variable.')
-    
-    def __init__(self, filename, lineno, name, orig_lineno):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param name name of the shadowed import (string)
-        @param orig_lineno line number of the import (integer)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name, orig_lineno)
-
-
-class ImportStarUsed(Message):
-    """
-    Class defining the "Import Star Used" message.
-    """
-    message = QT_TRANSLATE_NOOP(
-        'py3Flakes',
-        "'from {0} import *' used; unable to detect undefined names.")
-    
-    def __init__(self, filename, lineno, modname):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param modname name of the module imported using star import (string)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (modname,)
-
-
-class UndefinedName(Message):
-    """
-    Class defining the "Undefined Name" message.
-    """
-    message = QT_TRANSLATE_NOOP('py3Flakes', 'Undefined name {0!r}.')
-    
-    def __init__(self, filename, lineno, name):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param name undefined name (string)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name,)
-
-
-class UndefinedExport(Message):
-    """
-    Class defining the "Undefined Export" message.
-    """
-    message = QT_TRANSLATE_NOOP(
-        'py3Flakes',
-        'Undefined name {0!r} in __all__.')
-    
-    def __init__(self, filename, lineno, name):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param name undefined exported name (string)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name,)
-
-
-class UndefinedLocal(Message):
-    """
-    Class defining the "Undefined Local Variable" message.
-    """
-    message = QT_TRANSLATE_NOOP(
-        'py3Flakes',
-        "Local variable {0!r} (defined in enclosing scope on line {1!r})"
-        " referenced before assignment.")
-    
-    def __init__(self, filename, lineno, name, orig_lineno):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param name name of the prematurely referenced variable (string)
-        @param orig_lineno line number of the variable definition (integer)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name, orig_lineno)
-
-
-class DuplicateArgument(Message):
-    """
-    Class defining the "Duplicate Argument" message.
-    """
-    message = QT_TRANSLATE_NOOP(
-        'py3Flakes',
-        'Duplicate argument {0!r} in function definition.')
-    
-    def __init__(self, filename, lineno, name):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param name name of the duplicate argument (string)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name,)
-
-
-class RedefinedFunction(Message):
-    """
-    Class defining the "Redefined Function" message.
-    """
-    message = QT_TRANSLATE_NOOP(
-        'py3Flakes',
-        'Redefinition of function {0!r} from line {1!r}.')
-    
-    def __init__(self, filename, lineno, name, orig_lineno):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param name name of the redefined function (string)
-        @param orig_lineno line number of the original definition (integer)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name, orig_lineno)
-
-
-class LateFutureImport(Message):
-    """
-    Class defining the "Late Future Import" message.
-    """
-    message = QT_TRANSLATE_NOOP(
-        'py3Flakes',
-        'Future import(s) {0!r} after other statements.')
-    
-    def __init__(self, filename, lineno, names):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param names names of the imported futures (string)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (names,)
-
-
-class UnusedVariable(Message):
-    """
-    Class defining the "Unused Variable" message.
-    
-    Indicates that a variable has been explicitly assigned to but not actually
-    used.
-    """
-    message = QT_TRANSLATE_NOOP(
-        'py3Flakes',
-        'Local variable {0!r} is assigned to but never used.')
-    
-    def __init__(self, filename, lineno, name):
-        """
-        Constructor
-        
-        @param filename name of the file (string)
-        @param lineno line number (integer)
-        @param name name of the unused variable (string)
-        """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name,)
--- a/eric5.e4p	Wed Jan 01 22:59:10 2014 +0100
+++ b/eric5.e4p	Sat Jan 04 22:12:42 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-01, 22:56:51 -->
+<!-- Saved: 2014-01-04, 21:59:29 -->
 <!-- Copyright (C) 2014 Detlev Offenbach, detlev@die-offenbachs.de -->
 <Project version="5.1">
   <Language>en_US</Language>
@@ -706,9 +706,6 @@
     <Source>E5Gui/E5ToolBox.py</Source>
     <Source>E5Gui/E5TreeSortFilterProxyModel.py</Source>
     <Source>E5Gui/E5TreeView.py</Source>
-    <Source>Utilities/py3flakes/__init__.py</Source>
-    <Source>Utilities/py3flakes/checker.py</Source>
-    <Source>Utilities/py3flakes/messages.py</Source>
     <Source>Preferences/ConfigurationPages/EditorKeywordsPage.py</Source>
     <Source>Cooperation/__init__.py</Source>
     <Source>Cooperation/Connection.py</Source>
@@ -1053,10 +1050,6 @@
     <Source>QScintilla/SortOptionsDialog.py</Source>
     <Source>Debugger/CallStackViewer.py</Source>
     <Source>Utilities/compatibility_fixes.py</Source>
-    <Source>Utilities/SyntaxCheck.py</Source>
-    <Source>Utilities/py2flakes/checker.py</Source>
-    <Source>Utilities/py2flakes/messages.py</Source>
-    <Source>Utilities/py2flakes/__init__.py</Source>
     <Source>Examples/hallo.py</Source>
     <Source>Examples/modpython.py</Source>
     <Source>Examples/modpython_dbg.py</Source>
@@ -1114,14 +1107,12 @@
     <Source>UtilitiesPython2/__init__.py</Source>
     <Source>UtilitiesPython2/Tools.py</Source>
     <Source>Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheck.py</Source>
-    <Source>Plugins/CheckerPlugins/SyntaxChecker/py3flakes/checker.py</Source>
-    <Source>Plugins/CheckerPlugins/SyntaxChecker/py3flakes/messages.py</Source>
     <Source>Utilities/BackgroundClient.py</Source>
     <Source>Utilities/BackgroundService.py</Source>
     <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>Plugins/CheckerPlugins/SyntaxChecker/py3flakes/__init__.py</Source>
+    <Source>Utilities/InternalServices.py</Source>
   </Sources>
   <Forms>
     <Form>PyUnit/UnittestDialog.ui</Form>

eric ide

mercurial