Exception handling for client side implemented. BgService

Sat, 22 Mar 2014 14:44:39 +0100

author
T.Rzepka <Tobias.Rzepka@gmail.com>
date
Sat, 22 Mar 2014 14:44:39 +0100
branch
BgService
changeset 3417
5a93c6cdc989
parent 3415
180ae0154322
child 3418
27ab90e0f25e

Exception handling for client side implemented.

Utilities/BackgroundClient.py file | annotate | diff | comparison | revisions
Utilities/BackgroundService.py file | annotate | diff | comparison | revisions
eric5.py file | annotate | diff | comparison | revisions
--- a/Utilities/BackgroundClient.py	Sat Mar 22 13:06:32 2014 +0100
+++ b/Utilities/BackgroundClient.py	Sat Mar 22 14:44:39 2014 +0100
@@ -12,13 +12,15 @@
 from __future__ import unicode_literals
 try:
     bytes = unicode
+    import StringIO as io   # __IGNORE_EXCEPTION__
 except NameError:
-    pass
+    import io       # __IGNORE_WARNING__
 
 import json
 import socket
 import struct
 import sys
+import traceback
 from zlib import adler32
 
 
@@ -38,7 +40,6 @@
         self.connection = socket.create_connection((host, port))
         ver = b'Python2' if sys.version_info[0] == 2 else b'Python3'
         self.connection.sendall(ver)
-        self.connection.settimeout(0.25)
 
     def __initClientService(self, fn, path, module):
         """
@@ -77,54 +78,53 @@
         """
         Implement the main loop of the client.
         """
-        while True:
-            try:
-                header = self.connection.recv(8)  # __IGNORE_EXCEPTION__
-            except socket.timeout:
-                continue
-            except socket.error:
+        try:
+            while True:
+                try:
+                    header = b''
+                    while len(header) < 8:
+                        header += self.connection.recv(8 - len(header))
+                except socket.error:
+                    # Leave main loop if connection was closed.
+                    break
                 # Leave main loop if connection was closed.
-                break
-            # Leave main loop if connection was closed.
-            if not header:
-                break
-            
-            length, datahash = struct.unpack(b'!II', header)
-            packedData = b''
-            while len(packedData) < length:
-                packedData += self.connection.recv(length - len(packedData))
-            
-            assert adler32(packedData) & 0xffffffff == datahash, \
-                'Hashes not equal'
-            if sys.version_info[0] == 3:
-                packedData = packedData.decode('utf-8')
-            
-            fx, fn, data = json.loads(packedData)
-            if fx == 'INIT':
-                ret = self.__initClientService(fn, *data)
-            else:
-                callback = self.services.get(fx)
-                if callback:
-                    ret = callback(fn, *data)
+                if not header:
+                    break
+                
+                length, datahash = struct.unpack(b'!II', header)
+                packedData = b''
+                while len(packedData) < length:
+                    packedData += self.connection.recv(
+                        length - len(packedData))
+                
+                assert adler32(packedData) & 0xffffffff == datahash, \
+                    'Hashes not equal'
+                if sys.version_info[0] == 3:
+                    packedData = packedData.decode('utf-8')
+                
+                fx, fn, data = json.loads(packedData)
+                if fx == 'INIT':
+                    ret = self.__initClientService(fn, *data)
                 else:
-                    ret = 'Unknown service.'
-            
-            self.__send(fx, fn, ret)
-            
-        self.connection.close()
-        sys.exit()
+                    callback = self.services.get(fx)
+                    if callback:
+                        ret = callback(fn, *data)
+                    else:
+                        ret = 'Unknown service.'
+                
+                self.__send(fx, fn, ret)
+        except:
+            exctype, excval, exctb = sys.exc_info()
+            tbinfofile = io.StringIO()
+            traceback.print_tb(exctb, None, tbinfofile)
+            tbinfofile.seek(0)
+            tbinfo = tbinfofile.read()
+            del exctb
+            self.__send(
+                'EXCEPTION', '?', [str(exctype), str(excval), tbinfo])
 
-    def __unhandled_exception(self, exctype, excval, exctb):
-        """
-        Private method called to report an uncaught exception.
-        
-        @param exctype the type of the exception
-        @param excval data about the exception
-        @param exctb traceback for the exception
-        """
-        # TODO: Wrap arguments so they can be serialized by JSON
-        self.__send(
-            'EXCEPTION', '?', [str(exctype), str(excval), str(exctb)])
+        self.connection.shutdown(socket.SHUT_RDWR)
+        self.connection.close()
 
 if __name__ == '__main__':
     if len(sys.argv) != 3:
@@ -133,8 +133,5 @@
     
     host, port = sys.argv[1:]
     backgroundClient = BackgroundClient(host, int(port))
-    # set the system exception handling function to ensure, that
-    # we report on all unhandled exceptions
-    sys.excepthook = backgroundClient._BackgroundClient__unhandled_exception
     # Start the main loop
     backgroundClient.run()
--- a/Utilities/BackgroundService.py	Sat Mar 22 13:06:32 2014 +0100
+++ b/Utilities/BackgroundService.py	Sat Mar 22 14:44:39 2014 +0100
@@ -19,6 +19,7 @@
 from zlib import adler32
 
 from PyQt4.QtCore import QProcess, pyqtSignal
+from PyQt4.QtGui import QApplication
 from PyQt4.QtNetwork import QTcpServer, QHostAddress
 
 import Preferences
@@ -54,6 +55,7 @@
         self.listen(QHostAddress(self.hostAddress))
 
         self.newConnection.connect(self.on_newConnection)
+        
         port = self.serverPort()
         ## NOTE: Need the port if started external in debugger:
         print('BackgroundService listening on: %i' % port)
@@ -170,8 +172,8 @@
         elif fx == 'EXCEPTION':
             # Call sys.excepthook(type, value, traceback) to emulate the
             # exception which was caught on the client
-            #sys.excepthook(*data)
-            print(data)
+            sys.excepthook(*data)
+            QApplication.processEvents()
         elif data == 'Unknown service.':
             callback = self.services.get((fx, lang))
             if callback:
@@ -257,12 +259,34 @@
         self.connections[lang] = connection
         connection.readyRead.connect(
             lambda x=lang: self.__receive(x))
-        
+        connection.disconnected.connect(
+            lambda x=lang: self.on_disconnectSocket(x))
+            
         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 on_disconnectSocket(self, lang):
+        """
+        Slot when connection to a client is lost.
+        
+        @param lang client language which connection is lost (str)
+        """
+        self.connections.pop(lang)
+        # Maybe the task is killed while ideling
+        if self.isWorking == lang:
+            self.isWorking = None
+        # Remove pending jobs and send warning to the waiting caller
+        # Make a copy of the list because it's modified in the loop
+        for args in self.__queue[:]:
+            fx, lng, fn, data = args
+            if lng == lang:
+                # Call onErrorCallback with error message
+                self.__queue.remove(args)
+                self.services[(fx, lng)][3](fx, fn, lng, self.tr(
+                    'Error in Erics background service stopped service.'))
+        
     def shutdown(self):
         """
         Cleanup the connections and processes when Eric is shuting down.
--- a/eric5.py	Sat Mar 22 13:06:32 2014 +0100
+++ b/eric5.py	Sat Mar 22 14:44:39 2014 +0100
@@ -14,7 +14,7 @@
 from __future__ import unicode_literals
 
 try:  # Only for Py2
-    import StringIO as io
+    import StringIO as io   # __IGNORE_EXCEPTION__
     import sip
     sip.setapi('QString', 2)
     sip.setapi('QVariant', 2)
@@ -22,6 +22,7 @@
     import Utilities.compatibility_fixes     # __IGNORE_WARNING__
 except ImportError:
     import io       # __IGNORE_WARNING__
+    basestring = str
 
 import sys
 import os
@@ -100,6 +101,7 @@
     @param excValue exception value
     @param tracebackobj traceback object
     """
+    import xml.sax.saxutils
     from UI.Info import BugAddress
     import Utilities
     import Globals
@@ -122,10 +124,13 @@
     if distroInfo:
         versionInfo += "{0}\n{1}".format(separator, distroInfo)
     
-    tbinfofile = io.StringIO()
-    traceback.print_tb(tracebackobj, None, tbinfofile)
-    tbinfofile.seek(0)
-    tbinfo = tbinfofile.read()
+    if isinstance(excType, basestring):
+        tbinfo = tracebackobj
+    else:
+        tbinfofile = io.StringIO()
+        traceback.print_tb(tracebackobj, None, tbinfofile)
+        tbinfofile.seek(0)
+        tbinfo = tbinfofile.read()
     errmsg = '{0}: \n{1}'.format(str(excType), str(excValue))
     sections = [separator, timeString, separator, errmsg, separator, tbinfo]
     msg = '\n'.join(sections)
@@ -136,7 +141,11 @@
         f.close()
     except IOError:
         pass
-    qWarning(str(notice) + str(msg) + str(versionInfo))
+    
+    warning = str(notice) + str(msg) + str(versionInfo)
+    # Escape &<> otherwise it's not visible in the error dialog
+    warning = xml.sax.saxutils.escape(warning)
+    qWarning(warning)
 
 
 def uiStartUp():

eric ide

mercurial