Utilities/BackgroundService.py

changeset 4218
f542ad1f76c5
parent 4021
195a471c327b
child 4221
c9fdc07753a7
equal deleted inserted replaced
4217:38e8903f9c2f 4218:f542ad1f76c5
32 class BackgroundService(QTcpServer): 32 class BackgroundService(QTcpServer):
33 """ 33 """
34 Class implementing the main part of the background service. 34 Class implementing the main part of the background service.
35 """ 35 """
36 serviceNotAvailable = pyqtSignal(str, str, str, str) 36 serviceNotAvailable = pyqtSignal(str, str, str, str)
37 batchJobDone = pyqtSignal(str, str)
37 38
38 def __init__(self): 39 def __init__(self):
39 """ 40 """
40 Constructor of the BackgroundService class. 41 Constructor of the BackgroundService class.
41 """ 42 """
138 Private method to receive the response from the clients. 139 Private method to receive the response from the clients.
139 140
140 @param lang language of the incomming connection (str) 141 @param lang language of the incomming connection (str)
141 """ 142 """
142 connection = self.connections[lang] 143 connection = self.connections[lang]
143 header = connection.read(8) 144 while connection.bytesAvailable():
144 length, datahash = struct.unpack(b'!II', header) 145 header = connection.read(8)
145 146 length, datahash = struct.unpack(b'!II', header)
146 packedData = b'' 147
147 while len(packedData) < length: 148 packedData = b''
148 connection.waitForReadyRead(50) 149 while len(packedData) < length:
149 packedData += connection.read(length - len(packedData)) 150 connection.waitForReadyRead(50)
150 151 packedData += connection.read(length - len(packedData))
151 assert adler32(packedData) & 0xffffffff == datahash, 'Hashes not equal' 152
152 if sys.version_info[0] == 3: 153 assert adler32(packedData) & 0xffffffff == datahash, \
153 packedData = packedData.decode('utf-8') 154 'Hashes not equal'
154 # "check" if is's a tuple of 3 values 155 if sys.version_info[0] == 3:
155 fx, fn, data = json.loads(packedData) 156 packedData = packedData.decode('utf-8')
156 157 # "check" if is's a tuple of 3 values
157 if fx == 'INIT': 158 fx, fn, data = json.loads(packedData)
158 pass 159
159 elif fx == 'EXCEPTION': 160 if fx == 'INIT':
160 # Remove connection because it'll close anyway 161 pass
161 self.connections.pop(lang, None) 162 elif fx == 'EXCEPTION':
162 # Call sys.excepthook(type, value, traceback) to emulate the 163 # Remove connection because it'll close anyway
163 # exception which was caught on the client 164 self.connections.pop(lang, None)
164 sys.excepthook(*data) 165 # Call sys.excepthook(type, value, traceback) to emulate the
165 res = E5MessageBox.question( 166 # exception which was caught on the client
166 None, 167 sys.excepthook(*data)
167 self.tr("Restart background client?"), 168 res = E5MessageBox.question(
168 self.tr( 169 None,
169 "<p>The background client for <b>{0}</b> has stopped" 170 self.tr("Restart background client?"),
170 " due to an exception. It's used by various plug-ins like" 171 self.tr(
171 " the different checkers.</p>" 172 "<p>The background client for <b>{0}</b> has stopped"
172 "<p>Select" 173 " due to an exception. It's used by various plug-ins"
173 "<ul>" 174 " like the different checkers.</p>"
174 "<li><b>'Yes'</b> to restart the client, but abort the" 175 "<p>Select"
175 " last job</li>" 176 "<ul>"
176 "<li><b>'Retry'</b> to restart the client and the last" 177 "<li><b>'Yes'</b> to restart the client, but abort the"
177 " job</li>" 178 " last job</li>"
178 "<li><b>'No'</b> to leave the client off.</li>" 179 "<li><b>'Retry'</b> to restart the client and the last"
179 "</ul></p>" 180 " job</li>"
180 "<p>Note: The client can be restarted by opening and" 181 "<li><b>'No'</b> to leave the client off.</li>"
181 " accepting the preferences dialog or reloading/changing" 182 "</ul></p>"
182 " the project.</p>").format(lang), 183 "<p>Note: The client can be restarted by opening and"
183 E5MessageBox.Yes | E5MessageBox.No | E5MessageBox.Retry, 184 " accepting the preferences dialog or reloading/"
184 E5MessageBox.Yes) 185 "changing the project.</p>").format(lang),
185 186 E5MessageBox.Yes | E5MessageBox.No | E5MessageBox.Retry,
186 if res == E5MessageBox.Retry: 187 E5MessageBox.Yes)
187 self.enqueueRequest(*self.runningJob) 188
189 if res == E5MessageBox.Retry:
190 self.enqueueRequest(*self.runningJob)
191 else:
192 fx, lng, fn, data = self.runningJob
193 self.services[(fx, lng)][3](fx, lng, fn, self.tr(
194 'An error in Erics background client stopped the'
195 ' service.')
196 )
197 if res != E5MessageBox.No:
198 self.isWorking = None
199 self.restartService(lang, True)
200 return
201 elif data == 'Unknown service.':
202 callback = self.services.get((fx, lang))
203 if callback:
204 callback[3](fx, lang, fn, data)
205 elif fx.startswith("batch_"):
206 fx = fx.replace("batch_", "")
207 if data != "__DONE__":
208 callback = self.services.get((fx, lang))
209 if callback:
210 callback[2](fn, *data)
211 continue
212 else:
213 self.batchJobDone.emit(fx, lang)
188 else: 214 else:
189 fx, lng, fn, data = self.runningJob 215 callback = self.services.get((fx, lang))
190 self.services[(fx, lng)][3](fx, lng, fn, self.tr( 216 if callback:
191 'An error in Erics background client stopped the service.') 217 callback[2](fn, *data)
192 )
193 if res != E5MessageBox.No:
194 self.isWorking = None
195 self.restartService(lang, True)
196 return
197 elif data == 'Unknown service.':
198 callback = self.services.get((fx, lang))
199 if callback:
200 callback[3](fx, lang, fn, data)
201 else:
202 callback = self.services.get((fx, lang))
203 if callback:
204 callback[2](fn, *data)
205 218
206 self.isWorking = None 219 self.isWorking = None
207 self.__processQueue() 220 self.__processQueue()
208 221
209 def preferencesOrProjectChanged(self): 222 def preferencesOrProjectChanged(self):
277 self.__queue.append(args) 290 self.__queue.append(args)
278 self.__processQueue() 291 self.__processQueue()
279 292
280 def serviceConnect( 293 def serviceConnect(
281 self, fx, lang, modulepath, module, callback, 294 self, fx, lang, modulepath, module, callback,
282 onErrorCallback=None): 295 onErrorCallback=None, onBatchDone=None):
283 """ 296 """
284 Public method to announce a new service to the background 297 Public method to announce a new service to the background
285 service/client. 298 service/client.
286 299
287 @param fx function name of the service (str) 300 @param fx function name of the service (str)
288 @param lang language of the new service (str) 301 @param lang language of the new service (str)
289 @param modulepath full path to the module (str) 302 @param modulepath full path to the module (str)
290 @param module name to import (str) 303 @param module name to import (str)
291 @param callback function on service response (function) 304 @param callback function called on service response (function)
292 @param onErrorCallback function if client isn't available (function) 305 @param onErrorCallback function called, if client isn't available
306 (function)
307 @param onBatchDone function called when a batch job is done (function)
293 """ 308 """
294 self.services[(fx, lang)] = \ 309 self.services[(fx, lang)] = \
295 modulepath, module, callback, onErrorCallback 310 modulepath, module, callback, onErrorCallback
296 self.enqueueRequest('INIT', lang, fx, [modulepath, module]) 311 self.enqueueRequest('INIT', lang, fx, [modulepath, module])
297 if onErrorCallback: 312 if onErrorCallback:
298 self.serviceNotAvailable.connect(onErrorCallback) 313 self.serviceNotAvailable.connect(onErrorCallback)
314 if onBatchDone:
315 self.batchJobDone.connect(onBatchDone)
299 316
300 def serviceDisconnect(self, fx, lang): 317 def serviceDisconnect(self, fx, lang):
301 """ 318 """
302 Public method to remove the service from the service list. 319 Public method to remove the service from the service list.
303 320

eric ide

mercurial