Started to rename eric6 to eric7.
8300
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
1
|
# -*- coding: utf-8 -*- |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
2
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
3
|
# Copyright (c) 2017 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
4
|
# |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
5
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
6
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
7
|
Module implementing the JSON based client base class. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
8
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
9
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
10
|
import io |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
11
|
import sys |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
12
|
import socket |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
13
|
import select |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
14
|
import traceback |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
15
|
import json |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
16
|
import contextlib |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
17
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
18
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
19
|
class E5JsonClient: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
20
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
21
|
Class implementing a JSON based client base class. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
22
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
23
|
def __init__(self, host, port, idString=""): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
24
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
25
|
Constructor |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
26
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
27
|
@param host ip address the background service is listening |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
28
|
@type str |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
29
|
@param port port of the background service |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
30
|
@type int |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
31
|
@param idString assigned client id to be sent back to the server in |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
32
|
order to identify the connection |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
33
|
@type str |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
34
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
35
|
self.__connection = socket.create_connection((host, port)) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
36
|
if idString: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
37
|
reply = idString + '\n' |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
38
|
self.__connection.sendall(reply.encode('utf8', 'backslashreplace')) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
39
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
40
|
def sendJson(self, command, params): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
41
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
42
|
Public method to send a single refactoring command to the server. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
43
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
44
|
@param command command name to be sent |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
45
|
@type str |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
46
|
@param params dictionary of named parameters for the command |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
47
|
@type dict |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
48
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
49
|
commandDict = { |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
50
|
"jsonrpc": "2.0", |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
51
|
"method": command, |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
52
|
"params": params, |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
53
|
} |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
54
|
cmd = json.dumps(commandDict) + '\n' |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
55
|
self.__connection.sendall(cmd.encode('utf8', 'backslashreplace')) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
56
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
57
|
def __receiveJson(self): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
58
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
59
|
Private method to receive a JSON encode command and data from the |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
60
|
server. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
61
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
62
|
@return tuple containing the received command and a dictionary |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
63
|
containing the associated data |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
64
|
@rtype tuple of (str, dict) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
65
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
66
|
# step 1: receive the data |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
67
|
# The JSON RPC string is prefixed by a 9 character long length field. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
68
|
length = self.__connection.recv(9) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
69
|
if len(length) < 9: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
70
|
# invalid length string received |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
71
|
return None, None |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
72
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
73
|
length = int(length) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
74
|
data = b'' |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
75
|
while len(data) < length: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
76
|
newData = self.__connection.recv(length - len(data)) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
77
|
if not newData: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
78
|
return None, None |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
79
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
80
|
data += newData |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
81
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
82
|
# step 2: decode and convert the data |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
83
|
line = data.decode( |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
84
|
'utf8', 'backslashreplace') |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
85
|
try: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
86
|
commandDict = json.loads(line.strip()) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
87
|
except (TypeError, ValueError) as err: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
88
|
self.sendJson("ClientException", { |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
89
|
"ExceptionType": "ProtocolError", |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
90
|
"ExceptionValue": str(err), |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
91
|
"ProtocolData": line.strip(), |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
92
|
}) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
93
|
return None, None |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
94
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
95
|
method = commandDict["method"] |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
96
|
params = commandDict["params"] |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
97
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
98
|
return method, params |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
99
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
100
|
def handleCall(self, method, params): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
101
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
102
|
Public method to handle a method call from the server. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
103
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
104
|
Note: This is an empty implementation that must be overridden in |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
105
|
derived classes. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
106
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
107
|
@param method requested method name |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
108
|
@type str |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
109
|
@param params dictionary with method specific parameters |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
110
|
@type dict |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
111
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
112
|
pass |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
113
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
114
|
def run(self): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
115
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
116
|
Public method implementing the main loop of the client. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
117
|
""" |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
118
|
try: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
119
|
selectErrors = 0 |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
120
|
while selectErrors <= 10: # selected arbitrarily |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
121
|
try: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
122
|
rrdy, wrdy, xrdy = select.select( |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
123
|
[self.__connection], [], []) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
124
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
125
|
# Just waiting for self.__connection. Therefor no check |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
126
|
# needed. |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
127
|
method, params = self.__receiveJson() |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
128
|
if method is None: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
129
|
selectErrors += 1 |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
130
|
elif method == "Exit": |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
131
|
break |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
132
|
else: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
133
|
self.handleCall(method, params) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
134
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
135
|
# reset select errors |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
136
|
selectErrors = 0 |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
137
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
138
|
except (select.error, KeyboardInterrupt, socket.error): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
139
|
selectErrors += 1 |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
140
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
141
|
except Exception: |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
142
|
exctype, excval, exctb = sys.exc_info() |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
143
|
tbinfofile = io.StringIO() |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
144
|
traceback.print_tb(exctb, None, tbinfofile) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
145
|
tbinfofile.seek(0) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
146
|
tbinfo = tbinfofile.read() |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
147
|
del exctb |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
148
|
self.sendJson("ClientException", { |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
149
|
"ExceptionType": str(exctype), |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
150
|
"ExceptionValue": str(excval), |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
151
|
"Traceback": tbinfo, |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
152
|
}) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
153
|
|
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
154
|
# Give time to process latest response on server side |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
155
|
with contextlib.suppress(socket.error, OSError): |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
156
|
self.__connection.shutdown(socket.SHUT_RDWR) |
Detlev Offenbach <detlev@die-offenbachs.de>
parents:
diff
changeset
|
157
|
self.__connection.close() |