RefactoringRope/JsonClient.py

Sun, 17 Sep 2017 17:03:16 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 17 Sep 2017 17:03:16 +0200
branch
server_client_variant
changeset 168
53d76b4fc1ac
parent 166
6fc202183b3b
child 169
fb8a4182f12e
permissions
-rw-r--r--

Implemented the distributed History dialog and moved the Undo/Redo functions to this dialog.

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

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

"""
Module implementing the JSON based client base class.
"""

from __future__ import unicode_literals
try:
    bytes = unicode
    import StringIO as io   # __IGNORE_EXCEPTION__
except NameError:
    import io       # __IGNORE_WARNING__

import sys
import socket
import select
import traceback
import json


class JsonClient(object):
    """
    Class implementing the JSON based client base class.
    """
    def __init__(self, host, port):
        """
        Constructor
        
        @param host ip address the background service is listening
        @type str
        @param port port of the background service
        @type int
        """
        self.__exitClient = False
        self.__connection = socket.create_connection((host, port))
    
    def sendJson(self, command, params):
        """
        Public method to send a single refactoring command to the server.
        
        @param command command name to be sent
        @type str
        @param params dictionary of named parameters for the command
        @type dict
        """
        commandDict = {
            "jsonrpc": "2.0",
            "method": command,
            "params": params,
        }
        cmd = json.dumps(commandDict) + '\n'
        self.__connection.sendall(cmd.encode('utf8', 'backslashreplace'))
    
    def __receiveJson(self):
        """
        Private method to receive a JSON encode command and data from the
        server.
        """
        line = self.__connection.recv(1024 * 1024, socket.MSG_PEEK)
        # 1MB buffer

        eol = line.find(b'\n')

        if eol >= 0:
            size = eol + 1

            # Now we know how big the line is, read it for real.
            line = self.__connection.recv(size).decode(
                'utf8', 'backslashreplace')
            try:
                commandDict = json.loads(line.strip())
            except (TypeError, ValueError) as err:
                self.sendJson("ClientException", {
                    "ExceptionType": "ProtocolError",
                    "ExceptionValue": str(err),
                    "ProtocolData": line.strip(),
                })
                return
            
            method = commandDict["method"]
            params = commandDict["params"]
            
            if method == "Exit":
                self.__exitClient = True
            else:
                self.handleCall(method, params)
    
    def handleCall(self, method, params):
        """
        Public method to handle a method call from the server.
        
        Note: This is an empty implementation that must be overridden in
        derived classes.
        
        @param method requested method name
        @type str
        @param params dictionary with method specific parameters
        @type dict
        """
        pass
    
    def run(self):
        """
        Public method implementing the main loop of the client.
        """
        try:
            while True:
                try:
                    rrdy, wrdy, xrdy = select.select(
                        [self.__connection], [], [])
                except (select.error, KeyboardInterrupt, socket.error):
                    # just carry on
                    continue
                
                if self.__connection in rrdy:
                    self.__receiveJson()
                
                if self.__exitClient:
                    break
        
        except Exception:
            exctype, excval, exctb = sys.exc_info()
            tbinfofile = io.StringIO()
            traceback.print_tb(exctb, None, tbinfofile)
            tbinfofile.seek(0)
            tbinfo = tbinfofile.read()
            del exctb
            self.sendJson("ClientException", {
                "ExceptionType": str(exctype),
                "ExceptionValue": str(excval),
                "Traceback": tbinfo,
            })

        # Give time to process latest response on server side
        self.__connection.shutdown(socket.SHUT_RDWR)
        self.__connection.close()
    
    def poll(self):
        """
        Public method to check and receive one message (if available).
        """
        try:
            rrdy, wrdy, xrdy = select.select([self.__connection], [], [], 0)
            if self.__connection in rrdy:
                self.__receiveJson()
        
        except (select.error, KeyboardInterrupt, socket.error):
            # just ignore these
            return
        
        except Exception:
            exctype, excval, exctb = sys.exc_info()
            tbinfofile = io.StringIO()
            traceback.print_tb(exctb, None, tbinfofile)
            tbinfofile.seek(0)
            tbinfo = tbinfofile.read()
            del exctb
            self.sendJson("ClientException", {
                "ExceptionType": str(exctype),
                "ExceptionValue": str(excval),
                "Traceback": tbinfo,
            })

eric ide

mercurial