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