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 |