1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2006 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing the VCS status monitor thread base class. 8""" 9 10import contextlib 11 12from PyQt5.QtCore import ( 13 QThread, QMutex, QWaitCondition, pyqtSignal, QCoreApplication 14) 15 16 17class VcsStatusMonitorThread(QThread): 18 """ 19 Class implementing the VCS status monitor thread base class. 20 21 @signal vcsStatusMonitorData(list of str) emitted to update the VCS status 22 @signal vcsStatusMonitorStatus(str, str) emitted to signal the status of 23 the monitoring thread (ok, nok, op) and a status message 24 @signal vcsStatusMonitorInfo(str) emitted to signal some info of the 25 monitoring thread 26 """ 27 vcsStatusMonitorData = pyqtSignal(list) 28 vcsStatusMonitorStatus = pyqtSignal(str, str) 29 vcsStatusMonitorInfo = pyqtSignal(str) 30 31 def __init__(self, interval, project, vcs, parent=None): 32 """ 33 Constructor 34 35 @param interval new interval in seconds (integer) 36 @param project reference to the project object (Project) 37 @param vcs reference to the version control object 38 @param parent reference to the parent object (QObject) 39 """ 40 super().__init__(parent) 41 self.setObjectName("VcsStatusMonitorThread") 42 43 self.setTerminationEnabled(True) 44 45 self.projectDir = project.getProjectPath() 46 self.project = project 47 self.vcs = vcs 48 49 self.interval = interval 50 self.autoUpdate = False 51 52 self.statusList = [] 53 self.reportedStates = {} 54 self.shouldUpdate = False 55 56 self.monitorMutex = QMutex() 57 self.monitorCondition = QWaitCondition() 58 self.__stopIt = False 59 60 def run(self): 61 """ 62 Public method implementing the tasks action. 63 """ 64 while not self.__stopIt: 65 # perform the checking task 66 self.statusList = [] 67 self.vcsStatusMonitorStatus.emit( 68 "wait", QCoreApplication.translate( 69 "VcsStatusMonitorThread", "Waiting for lock")) 70 try: 71 locked = self.vcs.vcsExecutionMutex.tryLock(5000) 72 except TypeError: 73 locked = self.vcs.vcsExecutionMutex.tryLock() 74 if locked: 75 try: 76 self.vcsStatusMonitorStatus.emit( 77 "op", QCoreApplication.translate( 78 "VcsStatusMonitorThread", 79 "Checking repository status")) 80 res, statusMsg = self._performMonitor() 81 infoMsg = self._getInfo() 82 finally: 83 self.vcs.vcsExecutionMutex.unlock() 84 if res: 85 status = "ok" 86 else: 87 status = "nok" 88 self.vcsStatusMonitorStatus.emit( 89 "send", QCoreApplication.translate( 90 "VcsStatusMonitorThread", "Sending data")) 91 self.vcsStatusMonitorData.emit(self.statusList) 92 self.vcsStatusMonitorStatus.emit(status, statusMsg) 93 self.vcsStatusMonitorInfo.emit(infoMsg) 94 else: 95 self.vcsStatusMonitorStatus.emit( 96 "timeout", QCoreApplication.translate( 97 "VcsStatusMonitorThread", 98 "Timed out waiting for lock")) 99 self.vcsStatusMonitorInfo.emit("") 100 101 if self.autoUpdate and self.shouldUpdate: 102 self.vcs.vcsUpdate(self.projectDir, True) 103 continue # check again 104 self.shouldUpdate = False 105 106 # wait until interval has expired checking for a stop condition 107 self.monitorMutex.lock() 108 if not self.__stopIt: 109 self.monitorCondition.wait( 110 self.monitorMutex, self.interval * 1000) 111 self.monitorMutex.unlock() 112 113 self._shutdown() 114 self.exit() 115 116 def setInterval(self, interval): 117 """ 118 Public method to change the monitor interval. 119 120 @param interval new interval in seconds (integer) 121 """ 122 locked = self.monitorMutex.tryLock() 123 self.interval = interval 124 self.monitorCondition.wakeAll() 125 if locked: 126 self.monitorMutex.unlock() 127 128 def getInterval(self): 129 """ 130 Public method to get the monitor interval. 131 132 @return interval in seconds (integer) 133 """ 134 return self.interval 135 136 def setAutoUpdate(self, auto): 137 """ 138 Public method to enable the auto update function. 139 140 @param auto status of the auto update function (boolean) 141 """ 142 self.autoUpdate = auto 143 144 def getAutoUpdate(self): 145 """ 146 Public method to retrieve the status of the auto update function. 147 148 @return status of the auto update function (boolean) 149 """ 150 return self.autoUpdate 151 152 def checkStatus(self): 153 """ 154 Public method to wake up the status monitor thread. 155 """ 156 locked = self.monitorMutex.tryLock() 157 self.monitorCondition.wakeAll() 158 if locked: 159 self.monitorMutex.unlock() 160 161 def stop(self): 162 """ 163 Public method to stop the monitor thread. 164 """ 165 locked = self.monitorMutex.tryLock() 166 self.__stopIt = True 167 self.monitorCondition.wakeAll() 168 if locked: 169 self.monitorMutex.unlock() 170 171 def clearCachedState(self, name): 172 """ 173 Public method to clear the cached VCS state of a file/directory. 174 175 @param name name of the entry to be cleared (string) 176 """ 177 key = self.project.getRelativePath(name) 178 with contextlib.suppress(KeyError): 179 del self.reportedStates[key] 180 181 def _performMonitor(self): 182 """ 183 Protected method implementing the real monitoring action. 184 185 This method must be overridden and populate the statusList member 186 variable with a list of strings giving the status in the first column 187 and the path relative to the project directory starting with the 188 third column. The allowed status flags are: 189 <ul> 190 <li>"A" path was added but not yet comitted</li> 191 <li>"M" path has local changes</li> 192 <li>"O" path was removed</li> 193 <li>"R" path was deleted and then re-added</li> 194 <li>"U" path needs an update</li> 195 <li>"Z" path contains a conflict</li> 196 <li>" " path is back at normal</li> 197 </ul> 198 199 @return tuple of flag indicating successful operation (boolean) and 200 a status message in case of non successful operation (string) 201 @exception RuntimeError to indicate that this method must be 202 implemented by a subclass 203 """ 204 raise RuntimeError('Not implemented') 205 206 return () 207 208 def _getInfo(self): 209 """ 210 Protected method implementing the real info action. 211 212 This method should be overridden and create a short info message to be 213 shown in the main window status bar right next to the status indicator. 214 215 @return short info message 216 @rtype str 217 """ 218 return "" 219 220 def _shutdown(self): 221 """ 222 Protected method performing shutdown actions. 223 224 The default implementation does nothing. 225 """ 226 pass 227