Utilities/BackgroundClient.py

Tue, 31 Dec 2013 18:03:31 +0100

author
T.Rzepka <Tobias.Rzepka@gmail.com>
date
Tue, 31 Dec 2013 18:03:31 +0100
branch
BgService
changeset 3159
02cb2adb4868
child 3173
1fb284abe46e
permissions
-rw-r--r--

First implementation for the BackgroundService.

# -*- coding: utf-8 -*-

# Copyright (c) 2013 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
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
try:
    bytes = unicode  #__IGNORE_WARNING__
except NameError:
    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):
    """
    Class implementing the main part of the background client.
    """
    def __init__(self, host, port):
        """
        Constructor of the BackgroundClient class.
        
        @param host ip address the background service is listening
        @param port port of the background service
        """
        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 __send(self, fx, fn, data):
        """
        Private method to send a job response back to the BackgroundService.
        
        @param fx remote function name to execute (str)
        @param fn filename for identification (str)
        @param data return value(s) (any basic datatype)
        """
        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)
        self.connection.sendall(header)
        self.connection.sendall(packedData)

    def run(self):
        """
        Implement the main loop of the client.
        """
        while True:
            try:
                header = self.connection.recv(8)  # __IGNORE_EXCEPTION__
            except socket.timeout:
                continue
            except socket.error:
                # 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 == 'syntax':
                ret = SyntaxCheck.syntaxAndPyflakesCheck(fn, *data)
            elif fx == 'style':
                print(data)
            elif fx == 'indent':
                pass
            else:
                continue
            
            self.__send(fx, fn, ret)
            
        self.connection.close()
        sys.exit()

    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)])

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print('Host and port parameters are missing. Abort.')
        sys.exit(1)
    
    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()

eric ide

mercurial