eric6/Utilities/BackgroundService.py

changeset 7965
48fbf9149d16
parent 7923
91e843545d9a
child 8043
0acf98cd089a
child 8143
2c730d5fd177
equal deleted inserted replaced
7963:19bbd56ac5f2 7965:48fbf9149d16
1 # -*- coding: utf-8 -*- 1 # -*- coding: utf-8 -*-
2 2
3 # Copyright (c) 2013 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 3 # Copyright (c) 2013 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 # 4 #
5 # pylint: disable=C0103
6 5
7 """ 6 """
8 Module implementing a background service for the various checkers and other 7 Module implementing a background service for the various checkers and other
9 python interpreter dependent functions. 8 Python interpreter dependent functions.
10 """ 9 """
11 10
12 import json 11 import json
13 import os 12 import os
14 import struct 13 import struct
40 serviceNotAvailable = pyqtSignal(str, str, str, str) 39 serviceNotAvailable = pyqtSignal(str, str, str, str)
41 batchJobDone = pyqtSignal(str, str) 40 batchJobDone = pyqtSignal(str, str)
42 41
43 def __init__(self): 42 def __init__(self):
44 """ 43 """
45 Constructor of the BackgroundService class. 44 Constructor
46 """ 45 """
47 self.processes = {} 46 self.processes = {}
48 self.connections = {} 47 self.connections = {}
49 self.isWorking = None 48 self.isWorking = None
50 self.runningJob = [None, None, None, None] 49 self.runningJob = [None, None, None, None]
62 61
63 self.newConnection.connect(self.on_newConnection) 62 self.newConnection.connect(self.on_newConnection)
64 63
65 port = self.serverPort() 64 port = self.serverPort()
66 ## Note: Need the port if started external in debugger: 65 ## Note: Need the port if started external in debugger:
67 print('BackgroundService listening on: {0:d}'.format(port)) 66 print('Background Service listening on: {0:d}'.format(port))
68 # __IGNORE_WARNING__ 67 # __IGNORE_WARNING__
69 venvName = Preferences.getDebugger("Python3VirtualEnv") 68 venvName = Preferences.getDebugger("Python3VirtualEnv")
70 interpreter = e5App().getObject( 69 interpreter = e5App().getObject(
71 "VirtualEnvManager").getVirtualenvInterpreter(venvName) 70 "VirtualEnvManager").getVirtualenvInterpreter(venvName)
72 if not interpreter: 71 if not interpreter:
78 77
79 def __startExternalClient(self, interpreter, port): 78 def __startExternalClient(self, interpreter, port):
80 """ 79 """
81 Private method to start the background client as external process. 80 Private method to start the background client as external process.
82 81
83 @param interpreter path and name of the executable to start (string) 82 @param interpreter path and name of the executable to start
84 @param port socket port to which the interpreter should connect (int) 83 @type str
85 @return the process object (QProcess or None) 84 @param port socket port to which the interpreter should connect
85 @type int
86 @return the process object
87 @rtype QProcess or None
86 """ 88 """
87 if interpreter == "" or not Utilities.isinpath(interpreter): 89 if interpreter == "" or not Utilities.isinpath(interpreter):
88 return None 90 return None
89 91
90 backgroundClient = os.path.join( 92 backgroundClient = os.path.join(
112 114
113 def __send(self, fx, lang, fn, data): 115 def __send(self, fx, lang, fn, data):
114 """ 116 """
115 Private method to send a job request to one of the clients. 117 Private method to send a job request to one of the clients.
116 118
117 @param fx remote function name to execute (str) 119 @param fx remote function name to execute
118 @param lang language to connect to (str) 120 @type str
119 @param fn filename for identification (str) 121 @param lang language to connect to
120 @param data function argument(s) (any basic datatype) 122 @type str
123 @param fn filename for identification
124 @type str
125 @param data function argument(s)
126 @type any basic datatype
121 """ 127 """
122 self.__cancelled = False 128 self.__cancelled = False
123 connection = self.connections.get(lang) 129 connection = self.connections.get(lang)
124 if connection is None: 130 if connection is None:
125 if fx != 'INIT': 131 if fx != 'INIT':
144 150
145 def __receive(self, lang): 151 def __receive(self, lang):
146 """ 152 """
147 Private method to receive the response from the clients. 153 Private method to receive the response from the clients.
148 154
149 @param lang language of the incomming connection (str) 155 @param lang language of the incoming connection
156 @type str
150 @exception RuntimeError raised if hashes don't match 157 @exception RuntimeError raised if hashes don't match
151 """ 158 """
152 connection = self.connections[lang] 159 connection = self.connections[lang]
153 while connection.bytesAvailable(): 160 while connection.bytesAvailable():
154 if self.__cancelled: 161 if self.__cancelled:
204 self.enqueueRequest(*self.runningJob) 211 self.enqueueRequest(*self.runningJob)
205 else: 212 else:
206 fx, lng, fn, data = self.runningJob 213 fx, lng, fn, data = self.runningJob
207 try: 214 try:
208 self.services[(fx, lng)][3](fx, lng, fn, self.tr( 215 self.services[(fx, lng)][3](fx, lng, fn, self.tr(
209 'An error in Erics background client stopped the' 216 "An error in Eric's background client stopped the"
210 ' service.') 217 " service.")
211 ) 218 )
212 except (KeyError, TypeError): 219 except (KeyError, TypeError):
213 # ignore silently 220 # ignore silently
214 pass 221 pass
215 if res != E5MessageBox.No: 222 if res != E5MessageBox.No:
256 263
257 self.restartService('Python3') 264 self.restartService('Python3')
258 265
259 def restartService(self, language, forceKill=False): 266 def restartService(self, language, forceKill=False):
260 """ 267 """
261 Public method to restart a given lanuage. 268 Public method to restart a given language.
262 269
263 @param language to restart (str) 270 @param language to restart
264 @param forceKill flag to kill a running task (bool) 271 @type str
272 @param forceKill flag to kill a running task
273 @type bool
265 """ 274 """
266 try: 275 try:
267 proc, interpreter = self.processes.pop(language) 276 proc, interpreter = self.processes.pop(language)
268 except KeyError: 277 except KeyError:
269 return 278 return
286 if process: 295 if process:
287 self.processes[language] = process, interpreter 296 self.processes[language] = process, interpreter
288 297
289 def enqueueRequest(self, fx, lang, fn, data): 298 def enqueueRequest(self, fx, lang, fn, data):
290 """ 299 """
291 Public method implementing a queued processing of incomming events. 300 Public method implementing a queued processing of incoming events.
292 301
293 Dublicate service requests updates an older request to avoid overrun or 302 Duplicate service requests update an older request to avoid overrun or
294 starving of the services. 303 starving of the services.
295 @param fx function name of the service (str) 304
296 @param lang language to connect to (str) 305 @param fx function name of the service
297 @param fn filename for identification (str) 306 @type str
298 @param data function argument(s) (any basic datatype(s)) 307 @param lang language to connect to
308 @type str
309 @param fn filename for identification
310 @type str
311 @param data function argument(s)
312 @type any basic datatype
299 """ 313 """
300 args = [fx, lang, fn, data] 314 args = [fx, lang, fn, data]
301 if fx == 'INIT': 315 if fx == 'INIT':
302 self.__queue.insert(0, args) 316 self.__queue.insert(0, args)
303 else: 317 else:
313 327
314 def requestCancel(self, fx, lang): 328 def requestCancel(self, fx, lang):
315 """ 329 """
316 Public method to ask a batch job to terminate. 330 Public method to ask a batch job to terminate.
317 331
318 @param fx function name of the service (str) 332 @param fx function name of the service
319 @param lang language to connect to (str) 333 @type str
334 @param lang language to connect to
335 @type str
320 """ 336 """
321 self.__cancelled = True 337 self.__cancelled = True
322 338
323 entriesToRemove = [] 339 entriesToRemove = []
324 for pendingArg in self.__queue: 340 for pendingArg in self.__queue:
340 onErrorCallback=None, onBatchDone=None): 356 onErrorCallback=None, onBatchDone=None):
341 """ 357 """
342 Public method to announce a new service to the background 358 Public method to announce a new service to the background
343 service/client. 359 service/client.
344 360
345 @param fx function name of the service (str) 361 @param fx function name of the service
346 @param lang language of the new service (str) 362 @type str
347 @param modulepath full path to the module (str) 363 @param lang language of the new service
348 @param module name to import (str) 364 @type str
349 @param callback function called on service response (function) 365 @param modulepath full path to the module
366 @type str
367 @param module name to import
368 @type str
369 @param callback function called on service response
370 @type function
350 @param onErrorCallback function called, if client isn't available 371 @param onErrorCallback function called, if client isn't available
351 (function) 372 (function)
352 @param onBatchDone function called when a batch job is done (function) 373 @param onBatchDone function called when a batch job is done
374 @type function
353 """ 375 """
354 self.services[(fx, lang)] = ( 376 self.services[(fx, lang)] = (
355 modulepath, module, callback, onErrorCallback 377 modulepath, module, callback, onErrorCallback
356 ) 378 )
357 self.enqueueRequest('INIT', lang, fx, [modulepath, module]) 379 self.enqueueRequest('INIT', lang, fx, [modulepath, module])
362 384
363 def serviceDisconnect(self, fx, lang): 385 def serviceDisconnect(self, fx, lang):
364 """ 386 """
365 Public method to remove the service from the service list. 387 Public method to remove the service from the service list.
366 388
367 @param fx function name of the service (function) 389 @param fx function name of the service
368 @param lang language of the service (str) 390 @type function
391 @param lang language of the service
392 @type str
369 """ 393 """
370 serviceArgs = self.services.pop((fx, lang), None) 394 serviceArgs = self.services.pop((fx, lang), None)
371 if serviceArgs and serviceArgs[3]: 395 if serviceArgs and serviceArgs[3]:
372 self.serviceNotAvailable.disconnect(serviceArgs[3]) 396 self.serviceNotAvailable.disconnect(serviceArgs[3])
373 397
374 def on_newConnection(self): 398 def on_newConnection(self):
375 """ 399 """
376 Private slot for new incomming connections from the clients. 400 Private slot for new incoming connections from the clients.
377 """ 401 """
378 connection = self.nextPendingConnection() 402 connection = self.nextPendingConnection()
379 if not connection.waitForReadyRead(1000): 403 if not connection.waitForReadyRead(1000):
380 return 404 return
381 lang = connection.read(64) 405 lang = connection.read(64)
407 431
408 def on_disconnectSocket(self, lang): 432 def on_disconnectSocket(self, lang):
409 """ 433 """
410 Private slot called when connection to a client is lost. 434 Private slot called when connection to a client is lost.
411 435
412 @param lang client language which connection is lost (str) 436 @param lang client language which connection is lost
437 @type str
413 """ 438 """
414 conn = self.connections.pop(lang, None) 439 conn = self.connections.pop(lang, None)
415 if conn: 440 if conn:
416 conn.close() 441 conn.close()
417 fx, lng, fn, data = self.runningJob 442 fx, lng, fn, data = self.runningJob

eric ide

mercurial