src/eric7/EricNetwork/EricJsonClient.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9473
3f23dbf37dbe
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
18 18
19 class EricJsonClient: 19 class EricJsonClient:
20 """ 20 """
21 Class implementing a JSON based client base class. 21 Class implementing a JSON based client base class.
22 """ 22 """
23
23 def __init__(self, host, port, idString=""): 24 def __init__(self, host, port, idString=""):
24 """ 25 """
25 Constructor 26 Constructor
26 27
27 @param host IP address the background service is listening 28 @param host IP address the background service is listening
28 @type str 29 @type str
29 @param port port of the background service 30 @param port port of the background service
30 @type int 31 @type int
31 @param idString assigned client id to be sent back to the server in 32 @param idString assigned client id to be sent back to the server in
32 order to identify the connection 33 order to identify the connection
33 @type str 34 @type str
34 """ 35 """
35 self.__connection = socket.create_connection((host, port)) 36 self.__connection = socket.create_connection((host, port))
36 if idString: 37 if idString:
37 reply = idString + '\n' 38 reply = idString + "\n"
38 self.__connection.sendall(reply.encode('utf8', 'backslashreplace')) 39 self.__connection.sendall(reply.encode("utf8", "backslashreplace"))
39 40
40 def sendJson(self, command, params): 41 def sendJson(self, command, params):
41 """ 42 """
42 Public method to send a single refactoring command to the server. 43 Public method to send a single refactoring command to the server.
43 44
44 @param command command name to be sent 45 @param command command name to be sent
45 @type str 46 @type str
46 @param params dictionary of named parameters for the command 47 @param params dictionary of named parameters for the command
47 @type dict 48 @type dict
48 """ 49 """
49 commandDict = { 50 commandDict = {
50 "jsonrpc": "2.0", 51 "jsonrpc": "2.0",
51 "method": command, 52 "method": command,
52 "params": params, 53 "params": params,
53 } 54 }
54 cmd = json.dumps(commandDict) + '\n' 55 cmd = json.dumps(commandDict) + "\n"
55 self.__connection.sendall(cmd.encode('utf8', 'backslashreplace')) 56 self.__connection.sendall(cmd.encode("utf8", "backslashreplace"))
56 57
57 def __receiveJson(self): 58 def __receiveJson(self):
58 """ 59 """
59 Private method to receive a JSON encode command and data from the 60 Private method to receive a JSON encode command and data from the
60 server. 61 server.
61 62
62 @return tuple containing the received command and a dictionary 63 @return tuple containing the received command and a dictionary
63 containing the associated data 64 containing the associated data
64 @rtype tuple of (str, dict) 65 @rtype tuple of (str, dict)
65 """ 66 """
66 # step 1: receive the data 67 # step 1: receive the data
67 # The JSON RPC string is prefixed by a 9 character long length field. 68 # The JSON RPC string is prefixed by a 9 character long length field.
68 length = self.__connection.recv(9) 69 length = self.__connection.recv(9)
69 if len(length) < 9: 70 if len(length) < 9:
70 # invalid length string received 71 # invalid length string received
71 return None, None 72 return None, None
72 73
73 length = int(length) 74 length = int(length)
74 data = b'' 75 data = b""
75 while len(data) < length: 76 while len(data) < length:
76 newData = self.__connection.recv(length - len(data)) 77 newData = self.__connection.recv(length - len(data))
77 if not newData: 78 if not newData:
78 return None, None 79 return None, None
79 80
80 data += newData 81 data += newData
81 82
82 # step 2: decode and convert the data 83 # step 2: decode and convert the data
83 line = data.decode( 84 line = data.decode("utf8", "backslashreplace")
84 'utf8', 'backslashreplace')
85 try: 85 try:
86 commandDict = json.loads(line.strip()) 86 commandDict = json.loads(line.strip())
87 except (TypeError, ValueError) as err: 87 except (TypeError, ValueError) as err:
88 self.sendJson("ClientException", { 88 self.sendJson(
89 "ExceptionType": "ProtocolError", 89 "ClientException",
90 "ExceptionValue": str(err), 90 {
91 "ProtocolData": line.strip(), 91 "ExceptionType": "ProtocolError",
92 }) 92 "ExceptionValue": str(err),
93 "ProtocolData": line.strip(),
94 },
95 )
93 return None, None 96 return None, None
94 97
95 method = commandDict["method"] 98 method = commandDict["method"]
96 params = commandDict["params"] 99 params = commandDict["params"]
97 100
98 return method, params 101 return method, params
99 102
100 def handleCall(self, method, params): 103 def handleCall(self, method, params):
101 """ 104 """
102 Public method to handle a method call from the server. 105 Public method to handle a method call from the server.
103 106
104 Note: This is an empty implementation that must be overridden in 107 Note: This is an empty implementation that must be overridden in
105 derived classes. 108 derived classes.
106 109
107 @param method requested method name 110 @param method requested method name
108 @type str 111 @type str
109 @param params dictionary with method specific parameters 112 @param params dictionary with method specific parameters
110 @type dict 113 @type dict
111 """ 114 """
112 pass 115 pass
113 116
114 def run(self): 117 def run(self):
115 """ 118 """
116 Public method implementing the main loop of the client. 119 Public method implementing the main loop of the client.
117 """ 120 """
118 try: 121 try:
119 selectErrors = 0 122 selectErrors = 0
120 while selectErrors <= 10: # selected arbitrarily 123 while selectErrors <= 10: # selected arbitrarily
121 try: 124 try:
122 rrdy, wrdy, xrdy = select.select( 125 rrdy, wrdy, xrdy = select.select([self.__connection], [], [])
123 [self.__connection], [], []) 126
124
125 # Just waiting for self.__connection. Therefor no check 127 # Just waiting for self.__connection. Therefor no check
126 # needed. 128 # needed.
127 method, params = self.__receiveJson() 129 method, params = self.__receiveJson()
128 if method is None: 130 if method is None:
129 selectErrors += 1 131 selectErrors += 1
130 elif method == "Exit": 132 elif method == "Exit":
131 break 133 break
132 else: 134 else:
133 self.handleCall(method, params) 135 self.handleCall(method, params)
134 136
135 # reset select errors 137 # reset select errors
136 selectErrors = 0 138 selectErrors = 0
137 139
138 except (select.error, KeyboardInterrupt, socket.error): 140 except (select.error, KeyboardInterrupt, socket.error):
139 selectErrors += 1 141 selectErrors += 1
140 142
141 except Exception: 143 except Exception:
142 exctype, excval, exctb = sys.exc_info() 144 exctype, excval, exctb = sys.exc_info()
143 tbinfofile = io.StringIO() 145 tbinfofile = io.StringIO()
144 traceback.print_tb(exctb, None, tbinfofile) 146 traceback.print_tb(exctb, None, tbinfofile)
145 tbinfofile.seek(0) 147 tbinfofile.seek(0)
146 tbinfo = tbinfofile.read() 148 tbinfo = tbinfofile.read()
147 del exctb 149 del exctb
148 self.sendJson("ClientException", { 150 self.sendJson(
149 "ExceptionType": str(exctype), 151 "ClientException",
150 "ExceptionValue": str(excval), 152 {
151 "Traceback": tbinfo, 153 "ExceptionType": str(exctype),
152 }) 154 "ExceptionValue": str(excval),
155 "Traceback": tbinfo,
156 },
157 )
153 158
154 # Give time to process latest response on server side 159 # Give time to process latest response on server side
155 with contextlib.suppress(socket.error, OSError): 160 with contextlib.suppress(socket.error, OSError):
156 self.__connection.shutdown(socket.SHUT_RDWR) 161 self.__connection.shutdown(socket.SHUT_RDWR)
157 self.__connection.close() 162 self.__connection.close()
158 163
159 def poll(self, waitMethod=""): 164 def poll(self, waitMethod=""):
160 """ 165 """
161 Public method to check and receive one message (if available). 166 Public method to check and receive one message (if available).
162 167
163 @param waitMethod name of a method to wait for 168 @param waitMethod name of a method to wait for
164 @type str 169 @type str
165 @return dictionary containing the data of the waited for method 170 @return dictionary containing the data of the waited for method
166 @rtype dict 171 @rtype dict
167 """ 172 """
168 try: 173 try:
169 if waitMethod: 174 if waitMethod:
170 rrdy, wrdy, xrdy = select.select( 175 rrdy, wrdy, xrdy = select.select([self.__connection], [], [])
171 [self.__connection], [], [])
172 else: 176 else:
173 rrdy, wrdy, xrdy = select.select( 177 rrdy, wrdy, xrdy = select.select([self.__connection], [], [], 0)
174 [self.__connection], [], [], 0) 178
175
176 if self.__connection in rrdy: 179 if self.__connection in rrdy:
177 method, params = self.__receiveJson() 180 method, params = self.__receiveJson()
178 if method is not None: 181 if method is not None:
179 if method == "Exit": 182 if method == "Exit":
180 self.__exitClient = True 183 self.__exitClient = True
181 elif method == waitMethod: 184 elif method == waitMethod:
182 return params 185 return params
183 else: 186 else:
184 self.handleCall(method, params) 187 self.handleCall(method, params)
185 188
186 except (select.error, KeyboardInterrupt, socket.error): 189 except (select.error, KeyboardInterrupt, socket.error):
187 # just ignore these 190 # just ignore these
188 pass 191 pass
189 192
190 except Exception: 193 except Exception:
191 exctype, excval, exctb = sys.exc_info() 194 exctype, excval, exctb = sys.exc_info()
192 tbinfofile = io.StringIO() 195 tbinfofile = io.StringIO()
193 traceback.print_tb(exctb, None, tbinfofile) 196 traceback.print_tb(exctb, None, tbinfofile)
194 tbinfofile.seek(0) 197 tbinfofile.seek(0)
195 tbinfo = tbinfofile.read() 198 tbinfo = tbinfofile.read()
196 del exctb 199 del exctb
197 self.sendJson("ClientException", { 200 self.sendJson(
198 "ExceptionType": str(exctype), 201 "ClientException",
199 "ExceptionValue": str(excval), 202 {
200 "Traceback": tbinfo, 203 "ExceptionType": str(exctype),
201 }) 204 "ExceptionValue": str(excval),
202 205 "Traceback": tbinfo,
206 },
207 )
208
203 return None 209 return None

eric ide

mercurial