src/eric7/MicroPython/MicroPythonFileManager.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9249
83d59e29bd91
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
12 import shutil 12 import shutil
13 13
14 from PyQt6.QtCore import pyqtSlot, pyqtSignal, QObject 14 from PyQt6.QtCore import pyqtSlot, pyqtSignal, QObject
15 15
16 from .MicroPythonFileSystemUtilities import ( 16 from .MicroPythonFileSystemUtilities import (
17 mtime2string, mode2string, decoratedName, listdirStat 17 mtime2string,
18 mode2string,
19 decoratedName,
20 listdirStat,
18 ) 21 )
19 22
20 23
21 class MicroPythonFileManager(QObject): 24 class MicroPythonFileManager(QObject):
22 """ 25 """
23 Class implementing an interface to the device file system commands with 26 Class implementing an interface to the device file system commands with
24 some additional sugar. 27 some additional sugar.
25 28
26 @signal longListFiles(result) emitted with a tuple of tuples containing the 29 @signal longListFiles(result) emitted with a tuple of tuples containing the
27 name, mode, size and time for each directory entry 30 name, mode, size and time for each directory entry
28 @signal currentDir(dirname) emitted to report the current directory of the 31 @signal currentDir(dirname) emitted to report the current directory of the
29 device 32 device
30 @signal currentDirChanged(dirname) emitted to report back a change of the 33 @signal currentDirChanged(dirname) emitted to report back a change of the
41 rsync is doing 44 rsync is doing
42 @signal removeDirectoryDone() emitted after a directory has been deleted 45 @signal removeDirectoryDone() emitted after a directory has been deleted
43 @signal createDirectoryDone() emitted after a directory was created 46 @signal createDirectoryDone() emitted after a directory was created
44 @signal fsinfoDone(fsinfo) emitted after the file system information was 47 @signal fsinfoDone(fsinfo) emitted after the file system information was
45 obtained 48 obtained
46 49
47 @signal error(exc) emitted with a failure message to indicate a failure 50 @signal error(exc) emitted with a failure message to indicate a failure
48 during the most recent operation 51 during the most recent operation
49 """ 52 """
53
50 longListFiles = pyqtSignal(tuple) 54 longListFiles = pyqtSignal(tuple)
51 currentDir = pyqtSignal(str) 55 currentDir = pyqtSignal(str)
52 currentDirChanged = pyqtSignal(str) 56 currentDirChanged = pyqtSignal(str)
53 getFileDone = pyqtSignal(str, str) 57 getFileDone = pyqtSignal(str, str)
54 putFileDone = pyqtSignal(str, str) 58 putFileDone = pyqtSignal(str, str)
56 rsyncDone = pyqtSignal(str, str) 60 rsyncDone = pyqtSignal(str, str)
57 rsyncProgressMessage = pyqtSignal(str) 61 rsyncProgressMessage = pyqtSignal(str)
58 removeDirectoryDone = pyqtSignal() 62 removeDirectoryDone = pyqtSignal()
59 createDirectoryDone = pyqtSignal() 63 createDirectoryDone = pyqtSignal()
60 fsinfoDone = pyqtSignal(tuple) 64 fsinfoDone = pyqtSignal(tuple)
61 65
62 error = pyqtSignal(str, str) 66 error = pyqtSignal(str, str)
63 67
64 def __init__(self, commandsInterface, parent=None): 68 def __init__(self, commandsInterface, parent=None):
65 """ 69 """
66 Constructor 70 Constructor
67 71
68 @param commandsInterface reference to the commands interface object 72 @param commandsInterface reference to the commands interface object
69 @type MicroPythonCommandsInterface 73 @type MicroPythonCommandsInterface
70 @param parent reference to the parent object 74 @param parent reference to the parent object
71 @type QObject 75 @type QObject
72 """ 76 """
73 super().__init__(parent) 77 super().__init__(parent)
74 78
75 self.__commandsInterface = commandsInterface 79 self.__commandsInterface = commandsInterface
76 80
77 @pyqtSlot(str) 81 @pyqtSlot(str)
78 def lls(self, dirname, showHidden=False): 82 def lls(self, dirname, showHidden=False):
79 """ 83 """
80 Public slot to get a long listing of the given directory. 84 Public slot to get a long listing of the given directory.
81 85
82 @param dirname name of the directory to list 86 @param dirname name of the directory to list
83 @type str 87 @type str
84 @param showHidden flag indicating to show hidden files as well 88 @param showHidden flag indicating to show hidden files as well
85 @type bool 89 @type bool
86 """ 90 """
87 try: 91 try:
88 filesList = self.__commandsInterface.lls( 92 filesList = self.__commandsInterface.lls(dirname, showHidden=showHidden)
89 dirname, showHidden=showHidden) 93 result = [
90 result = [(decoratedName(name, mode), 94 (
91 mode2string(mode), 95 decoratedName(name, mode),
92 str(size), 96 mode2string(mode),
93 mtime2string(mtime, adjustEpoch=True)) for 97 str(size),
94 name, (mode, size, mtime) in filesList] 98 mtime2string(mtime, adjustEpoch=True),
99 )
100 for name, (mode, size, mtime) in filesList
101 ]
95 self.longListFiles.emit(tuple(result)) 102 self.longListFiles.emit(tuple(result))
96 except Exception as exc: 103 except Exception as exc:
97 self.error.emit("lls", str(exc)) 104 self.error.emit("lls", str(exc))
98 105
99 @pyqtSlot() 106 @pyqtSlot()
100 def pwd(self): 107 def pwd(self):
101 """ 108 """
102 Public slot to get the current directory of the device. 109 Public slot to get the current directory of the device.
103 """ 110 """
104 try: 111 try:
105 pwd = self.__commandsInterface.pwd() 112 pwd = self.__commandsInterface.pwd()
106 self.currentDir.emit(pwd) 113 self.currentDir.emit(pwd)
107 except Exception as exc: 114 except Exception as exc:
108 self.error.emit("pwd", str(exc)) 115 self.error.emit("pwd", str(exc))
109 116
110 @pyqtSlot(str) 117 @pyqtSlot(str)
111 def cd(self, dirname): 118 def cd(self, dirname):
112 """ 119 """
113 Public slot to change the current directory of the device. 120 Public slot to change the current directory of the device.
114 121
115 @param dirname name of the desired current directory 122 @param dirname name of the desired current directory
116 @type str 123 @type str
117 """ 124 """
118 try: 125 try:
119 self.__commandsInterface.cd(dirname) 126 self.__commandsInterface.cd(dirname)
120 self.currentDirChanged.emit(dirname) 127 self.currentDirChanged.emit(dirname)
121 except Exception as exc: 128 except Exception as exc:
122 self.error.emit("cd", str(exc)) 129 self.error.emit("cd", str(exc))
123 130
124 @pyqtSlot(str) 131 @pyqtSlot(str)
125 @pyqtSlot(str, str) 132 @pyqtSlot(str, str)
126 def get(self, deviceFileName, hostFileName=""): 133 def get(self, deviceFileName, hostFileName=""):
127 """ 134 """
128 Public slot to get a file from the connected device. 135 Public slot to get a file from the connected device.
129 136
130 @param deviceFileName name of the file on the device 137 @param deviceFileName name of the file on the device
131 @type str 138 @type str
132 @param hostFileName name of the local file 139 @param hostFileName name of the local file
133 @type str 140 @type str
134 """ 141 """
135 if hostFileName and os.path.isdir(hostFileName): 142 if hostFileName and os.path.isdir(hostFileName):
136 # only a local directory was given 143 # only a local directory was given
137 hostFileName = os.path.join(hostFileName, 144 hostFileName = os.path.join(hostFileName, os.path.basename(deviceFileName))
138 os.path.basename(deviceFileName))
139 try: 145 try:
140 self.__commandsInterface.get(deviceFileName, hostFileName) 146 self.__commandsInterface.get(deviceFileName, hostFileName)
141 self.getFileDone.emit(deviceFileName, hostFileName) 147 self.getFileDone.emit(deviceFileName, hostFileName)
142 except Exception as exc: 148 except Exception as exc:
143 self.error.emit("get", str(exc)) 149 self.error.emit("get", str(exc))
144 150
145 @pyqtSlot(str) 151 @pyqtSlot(str)
146 @pyqtSlot(str, str) 152 @pyqtSlot(str, str)
147 def put(self, hostFileName, deviceFileName=""): 153 def put(self, hostFileName, deviceFileName=""):
148 """ 154 """
149 Public slot to put a file onto the device. 155 Public slot to put a file onto the device.
150 156
151 @param hostFileName name of the local file 157 @param hostFileName name of the local file
152 @type str 158 @type str
153 @param deviceFileName name of the file on the connected device 159 @param deviceFileName name of the file on the connected device
154 @type str 160 @type str
155 """ 161 """
156 try: 162 try:
157 self.__commandsInterface.put(hostFileName, deviceFileName) 163 self.__commandsInterface.put(hostFileName, deviceFileName)
158 self.putFileDone.emit(hostFileName, deviceFileName) 164 self.putFileDone.emit(hostFileName, deviceFileName)
159 except Exception as exc: 165 except Exception as exc:
160 self.error.emit("put", str(exc)) 166 self.error.emit("put", str(exc))
161 167
162 @pyqtSlot(str) 168 @pyqtSlot(str)
163 def delete(self, deviceFileName): 169 def delete(self, deviceFileName):
164 """ 170 """
165 Public slot to delete a file on the device. 171 Public slot to delete a file on the device.
166 172
167 @param deviceFileName name of the file on the connected device 173 @param deviceFileName name of the file on the connected device
168 @type str 174 @type str
169 """ 175 """
170 try: 176 try:
171 self.__commandsInterface.rm(deviceFileName) 177 self.__commandsInterface.rm(deviceFileName)
172 self.deleteFileDone.emit(deviceFileName) 178 self.deleteFileDone.emit(deviceFileName)
173 except Exception as exc: 179 except Exception as exc:
174 self.error.emit("delete", str(exc)) 180 self.error.emit("delete", str(exc))
175 181
176 def __rsync(self, hostDirectory, deviceDirectory, mirror=True, 182 def __rsync(
177 localDevice=False, indentLevel=0): 183 self,
184 hostDirectory,
185 deviceDirectory,
186 mirror=True,
187 localDevice=False,
188 indentLevel=0,
189 ):
178 """ 190 """
179 Private method to synchronize a local directory to the device. 191 Private method to synchronize a local directory to the device.
180 192
181 @param hostDirectory name of the local directory 193 @param hostDirectory name of the local directory
182 @type str 194 @type str
183 @param deviceDirectory name of the directory on the device 195 @param deviceDirectory name of the directory on the device
184 @type str 196 @type str
185 @param mirror flag indicating to mirror the local directory to 197 @param mirror flag indicating to mirror the local directory to
192 @return list of errors 204 @return list of errors
193 @rtype list of str 205 @rtype list of str
194 """ 206 """
195 indent = 4 * " " 207 indent = 4 * " "
196 errors = [] 208 errors = []
197 209
198 if not os.path.isdir(hostDirectory): 210 if not os.path.isdir(hostDirectory):
199 return [self.tr( 211 return [
200 "The given name '{0}' is not a directory or does not exist.") 212 self.tr(
201 .format(hostDirectory) 213 "The given name '{0}' is not a directory or does not exist."
214 ).format(hostDirectory)
202 ] 215 ]
203 216
204 indentStr = indentLevel * indent 217 indentStr = indentLevel * indent
205 self.rsyncProgressMessage.emit( 218 self.rsyncProgressMessage.emit(
206 self.tr("{1}Synchronizing <b>{0}</b>.") 219 self.tr("{1}Synchronizing <b>{0}</b>.").format(deviceDirectory, indentStr)
207 .format(deviceDirectory, indentStr)
208 ) 220 )
209 221
210 doneMessage = self.tr("{1}Done synchronizing <b>{0}</b>.").format( 222 doneMessage = self.tr("{1}Done synchronizing <b>{0}</b>.").format(
211 deviceDirectory, indentStr) 223 deviceDirectory, indentStr
212 224 )
225
213 sourceDict = {} 226 sourceDict = {}
214 sourceFiles = listdirStat(hostDirectory) 227 sourceFiles = listdirStat(hostDirectory)
215 for name, nstat in sourceFiles: 228 for name, nstat in sourceFiles:
216 sourceDict[name] = nstat 229 sourceDict[name] = nstat
217 230
218 destinationDict = {} 231 destinationDict = {}
219 if localDevice: 232 if localDevice:
220 if not os.path.isdir(deviceDirectory): 233 if not os.path.isdir(deviceDirectory):
221 # simply copy destination to source 234 # simply copy destination to source
222 shutil.copytree(hostDirectory, deviceDirectory) 235 shutil.copytree(hostDirectory, deviceDirectory)
227 for name, nstat in destinationFiles: 240 for name, nstat in destinationFiles:
228 destinationDict[name] = nstat 241 destinationDict[name] = nstat
229 else: 242 else:
230 try: 243 try:
231 destinationFiles = self.__commandsInterface.lls( 244 destinationFiles = self.__commandsInterface.lls(
232 deviceDirectory, fullstat=True) 245 deviceDirectory, fullstat=True
246 )
233 except Exception as exc: 247 except Exception as exc:
234 return [str(exc)] 248 return [str(exc)]
235 if destinationFiles is None: 249 if destinationFiles is None:
236 # the destination directory does not exist 250 # the destination directory does not exist
237 try: 251 try:
239 except Exception as exc: 253 except Exception as exc:
240 return [str(exc)] 254 return [str(exc)]
241 else: 255 else:
242 for name, nstat in destinationFiles: 256 for name, nstat in destinationFiles:
243 destinationDict[name] = nstat 257 destinationDict[name] = nstat
244 258
245 destinationSet = set(destinationDict.keys()) 259 destinationSet = set(destinationDict.keys())
246 sourceSet = set(sourceDict.keys()) 260 sourceSet = set(sourceDict.keys())
247 toAdd = sourceSet - destinationSet # add to dev 261 toAdd = sourceSet - destinationSet # add to dev
248 toDelete = destinationSet - sourceSet # delete from dev 262 toDelete = destinationSet - sourceSet # delete from dev
249 toUpdate = destinationSet.intersection(sourceSet) # update files 263 toUpdate = destinationSet.intersection(sourceSet) # update files
250 indentStr = (indentLevel + 1) * indent 264 indentStr = (indentLevel + 1) * indent
251 265
252 if localDevice: 266 if localDevice:
253 for sourceBasename in toAdd: 267 for sourceBasename in toAdd:
254 # name exists in source but not in device 268 # name exists in source but not in device
255 sourceFilename = os.path.join(hostDirectory, sourceBasename) 269 sourceFilename = os.path.join(hostDirectory, sourceBasename)
256 destFilename = os.path.join(deviceDirectory, sourceBasename) 270 destFilename = os.path.join(deviceDirectory, sourceBasename)
257 self.rsyncProgressMessage.emit( 271 self.rsyncProgressMessage.emit(
258 self.tr("{1}Adding <b>{0}</b>...") 272 self.tr("{1}Adding <b>{0}</b>...").format(destFilename, indentStr)
259 .format(destFilename, indentStr)) 273 )
260 if os.path.isfile(sourceFilename): 274 if os.path.isfile(sourceFilename):
261 shutil.copy2(sourceFilename, destFilename) 275 shutil.copy2(sourceFilename, destFilename)
262 elif os.path.isdir(sourceFilename): 276 elif os.path.isdir(sourceFilename):
263 # recurse 277 # recurse
264 errs = self.__rsync(sourceFilename, destFilename, 278 errs = self.__rsync(
265 mirror=mirror, localDevice=localDevice, 279 sourceFilename,
266 indentLevel=indentLevel + 1) 280 destFilename,
281 mirror=mirror,
282 localDevice=localDevice,
283 indentLevel=indentLevel + 1,
284 )
267 # just note issues but ignore them otherwise 285 # just note issues but ignore them otherwise
268 errors.extend(errs) 286 errors.extend(errs)
269 287
270 if mirror: 288 if mirror:
271 for destBasename in toDelete: 289 for destBasename in toDelete:
272 # name exists in device but not local, delete 290 # name exists in device but not local, delete
273 destFilename = os.path.join(deviceDirectory, destBasename) 291 destFilename = os.path.join(deviceDirectory, destBasename)
274 if os.path.isdir(destFilename): 292 if os.path.isdir(destFilename):
275 shutil.rmtree(destFilename, ignore_errors=True) 293 shutil.rmtree(destFilename, ignore_errors=True)
276 elif os.path.isfile(destFilename): 294 elif os.path.isfile(destFilename):
277 os.remove(destFilename) 295 os.remove(destFilename)
278 296
279 for sourceBasename in toUpdate: 297 for sourceBasename in toUpdate:
280 # names exist in both; do an update 298 # names exist in both; do an update
281 sourceStat = sourceDict[sourceBasename] 299 sourceStat = sourceDict[sourceBasename]
282 destStat = destinationDict[sourceBasename] 300 destStat = destinationDict[sourceBasename]
283 sourceFilename = os.path.join(hostDirectory, sourceBasename) 301 sourceFilename = os.path.join(hostDirectory, sourceBasename)
284 destFilename = os.path.join(deviceDirectory, sourceBasename) 302 destFilename = os.path.join(deviceDirectory, sourceBasename)
285 destMode = destStat[0] 303 destMode = destStat[0]
286 if os.path.isdir(sourceFilename): 304 if os.path.isdir(sourceFilename):
287 if os.path.isdir(destFilename): 305 if os.path.isdir(destFilename):
288 # both are directories => recurs 306 # both are directories => recurs
289 errs = self.__rsync(sourceFilename, destFilename, 307 errs = self.__rsync(
290 mirror=mirror, 308 sourceFilename,
291 localDevice=localDevice, 309 destFilename,
292 indentLevel=indentLevel + 1) 310 mirror=mirror,
311 localDevice=localDevice,
312 indentLevel=indentLevel + 1,
313 )
293 # just note issues but ignore them otherwise 314 # just note issues but ignore them otherwise
294 errors.extend(errs) 315 errors.extend(errs)
295 else: 316 else:
296 self.rsyncProgressMessage.emit( 317 self.rsyncProgressMessage.emit(
297 self.tr("Source <b>{0}</b> is a directory and" 318 self.tr(
298 " destination <b>{1}</b> is a file." 319 "Source <b>{0}</b> is a directory and"
299 " Ignoring it.") 320 " destination <b>{1}</b> is a file."
300 .format(sourceFilename, destFilename) 321 " Ignoring it."
322 ).format(sourceFilename, destFilename)
301 ) 323 )
302 else: 324 else:
303 if os.path.isdir(destFilename): 325 if os.path.isdir(destFilename):
304 self.rsyncProgressMessage.emit( 326 self.rsyncProgressMessage.emit(
305 self.tr("Source <b>{0}</b> is a file and" 327 self.tr(
306 " destination <b>{1}</b> is a directory." 328 "Source <b>{0}</b> is a file and"
307 " Ignoring it.") 329 " destination <b>{1}</b> is a directory."
308 .format(sourceFilename, destFilename) 330 " Ignoring it."
331 ).format(sourceFilename, destFilename)
309 ) 332 )
310 else: 333 else:
311 if sourceStat[8] > destStat[8]: # mtime 334 if sourceStat[8] > destStat[8]: # mtime
312 self.rsyncProgressMessage.emit( 335 self.rsyncProgressMessage.emit(
313 self.tr("Updating <b>{0}</b>...") 336 self.tr("Updating <b>{0}</b>...").format(destFilename)
314 .format(destFilename)
315 ) 337 )
316 shutil.copy2(sourceFilename, destFilename) 338 shutil.copy2(sourceFilename, destFilename)
317 else: 339 else:
318 for sourceBasename in toAdd: 340 for sourceBasename in toAdd:
319 # name exists in source but not in device 341 # name exists in source but not in device
320 sourceFilename = os.path.join(hostDirectory, sourceBasename) 342 sourceFilename = os.path.join(hostDirectory, sourceBasename)
321 destFilename = ( 343 destFilename = (
322 "/" + sourceBasename 344 "/" + sourceBasename
323 if deviceDirectory == "/" else 345 if deviceDirectory == "/"
324 deviceDirectory + "/" + sourceBasename 346 else deviceDirectory + "/" + sourceBasename
325 ) 347 )
326 self.rsyncProgressMessage.emit( 348 self.rsyncProgressMessage.emit(
327 self.tr("{1}Adding <b>{0}</b>...") 349 self.tr("{1}Adding <b>{0}</b>...").format(destFilename, indentStr)
328 .format(destFilename, indentStr)) 350 )
329 if os.path.isfile(sourceFilename): 351 if os.path.isfile(sourceFilename):
330 try: 352 try:
331 self.__commandsInterface.put(sourceFilename, 353 self.__commandsInterface.put(sourceFilename, destFilename)
332 destFilename)
333 except Exception as exc: 354 except Exception as exc:
334 # just note issues but ignore them otherwise 355 # just note issues but ignore them otherwise
335 errors.append(str(exc)) 356 errors.append(str(exc))
336 elif os.path.isdir(sourceFilename): 357 elif os.path.isdir(sourceFilename):
337 # recurse 358 # recurse
338 errs = self.__rsync(sourceFilename, destFilename, 359 errs = self.__rsync(
339 mirror=mirror, 360 sourceFilename,
340 indentLevel=indentLevel + 1) 361 destFilename,
362 mirror=mirror,
363 indentLevel=indentLevel + 1,
364 )
341 # just note issues but ignore them otherwise 365 # just note issues but ignore them otherwise
342 errors.extend(errs) 366 errors.extend(errs)
343 367
344 if mirror: 368 if mirror:
345 for destBasename in toDelete: 369 for destBasename in toDelete:
346 # name exists in device but not local, delete 370 # name exists in device but not local, delete
347 destFilename = ( 371 destFilename = (
348 "/" + sourceBasename 372 "/" + sourceBasename
349 if deviceDirectory == "/" else 373 if deviceDirectory == "/"
350 deviceDirectory + "/" + destBasename 374 else deviceDirectory + "/" + destBasename
351 ) 375 )
352 self.rsyncProgressMessage.emit( 376 self.rsyncProgressMessage.emit(
353 self.tr("{1}Removing <b>{0}</b>...") 377 self.tr("{1}Removing <b>{0}</b>...").format(
354 .format(destFilename, indentStr)) 378 destFilename, indentStr
379 )
380 )
355 try: 381 try:
356 self.__commandsInterface.rmrf(destFilename, 382 self.__commandsInterface.rmrf(
357 recursive=True, 383 destFilename, recursive=True, force=True
358 force=True) 384 )
359 except Exception as exc: 385 except Exception as exc:
360 # just note issues but ignore them otherwise 386 # just note issues but ignore them otherwise
361 errors.append(str(exc)) 387 errors.append(str(exc))
362 388
363 for sourceBasename in toUpdate: 389 for sourceBasename in toUpdate:
364 # names exist in both; do an update 390 # names exist in both; do an update
365 sourceStat = sourceDict[sourceBasename] 391 sourceStat = sourceDict[sourceBasename]
366 destStat = destinationDict[sourceBasename] 392 destStat = destinationDict[sourceBasename]
367 sourceFilename = os.path.join(hostDirectory, sourceBasename) 393 sourceFilename = os.path.join(hostDirectory, sourceBasename)
368 destFilename = ( 394 destFilename = (
369 "/" + sourceBasename 395 "/" + sourceBasename
370 if deviceDirectory == "/" else 396 if deviceDirectory == "/"
371 deviceDirectory + "/" + sourceBasename 397 else deviceDirectory + "/" + sourceBasename
372 ) 398 )
373 destMode = destStat[0] 399 destMode = destStat[0]
374 if os.path.isdir(sourceFilename): 400 if os.path.isdir(sourceFilename):
375 if stat.S_ISDIR(destMode): 401 if stat.S_ISDIR(destMode):
376 # both are directories => recurs 402 # both are directories => recurs
377 errs = self.__rsync(sourceFilename, destFilename, 403 errs = self.__rsync(
378 mirror=mirror, 404 sourceFilename,
379 indentLevel=indentLevel + 1) 405 destFilename,
406 mirror=mirror,
407 indentLevel=indentLevel + 1,
408 )
380 # just note issues but ignore them otherwise 409 # just note issues but ignore them otherwise
381 errors.extend(errs) 410 errors.extend(errs)
382 else: 411 else:
383 self.rsyncProgressMessage.emit( 412 self.rsyncProgressMessage.emit(
384 self.tr("Source <b>{0}</b> is a directory and" 413 self.tr(
385 " destination <b>{1}</b> is a file." 414 "Source <b>{0}</b> is a directory and"
386 " Ignoring it.") 415 " destination <b>{1}</b> is a file."
387 .format(sourceFilename, destFilename) 416 " Ignoring it."
417 ).format(sourceFilename, destFilename)
388 ) 418 )
389 else: 419 else:
390 if stat.S_ISDIR(destMode): 420 if stat.S_ISDIR(destMode):
391 self.rsyncProgressMessage.emit( 421 self.rsyncProgressMessage.emit(
392 self.tr("Source <b>{0}</b> is a file and" 422 self.tr(
393 " destination <b>{1}</b> is a directory." 423 "Source <b>{0}</b> is a file and"
394 " Ignoring it.") 424 " destination <b>{1}</b> is a directory."
395 .format(sourceFilename, destFilename) 425 " Ignoring it."
426 ).format(sourceFilename, destFilename)
396 ) 427 )
397 else: 428 else:
398 if sourceStat[8] > destStat[8]: # mtime 429 if sourceStat[8] > destStat[8]: # mtime
399 self.rsyncProgressMessage.emit( 430 self.rsyncProgressMessage.emit(
400 self.tr("{1}Updating <b>{0}</b>...") 431 self.tr("{1}Updating <b>{0}</b>...").format(
401 .format(destFilename, indentStr) 432 destFilename, indentStr
433 )
402 ) 434 )
403 try: 435 try:
404 self.__commandsInterface.put(sourceFilename, 436 self.__commandsInterface.put(
405 destFilename) 437 sourceFilename, destFilename
438 )
406 except Exception as exc: 439 except Exception as exc:
407 errors.append(str(exc)) 440 errors.append(str(exc))
408 441
409 self.rsyncProgressMessage.emit(doneMessage) 442 self.rsyncProgressMessage.emit(doneMessage)
410 443
411 return errors 444 return errors
412 445
413 @pyqtSlot(str, str) 446 @pyqtSlot(str, str)
414 @pyqtSlot(str, str, bool) 447 @pyqtSlot(str, str, bool)
415 @pyqtSlot(str, str, bool, bool) 448 @pyqtSlot(str, str, bool, bool)
416 def rsync(self, hostDirectory, deviceDirectory, mirror=True, 449 def rsync(self, hostDirectory, deviceDirectory, mirror=True, localDevice=False):
417 localDevice=False):
418 """ 450 """
419 Public slot to synchronize a local directory to the device. 451 Public slot to synchronize a local directory to the device.
420 452
421 @param hostDirectory name of the local directory 453 @param hostDirectory name of the local directory
422 @type str 454 @type str
423 @param deviceDirectory name of the directory on the device 455 @param deviceDirectory name of the directory on the device
424 @type str 456 @type str
425 @param mirror flag indicating to mirror the local directory to 457 @param mirror flag indicating to mirror the local directory to
426 the device directory 458 the device directory
427 @type bool 459 @type bool
428 @param localDevice flag indicating device access via local file system 460 @param localDevice flag indicating device access via local file system
429 @type bool 461 @type bool
430 """ 462 """
431 errors = self.__rsync(hostDirectory, deviceDirectory, mirror=mirror, 463 errors = self.__rsync(
432 localDevice=localDevice) 464 hostDirectory, deviceDirectory, mirror=mirror, localDevice=localDevice
465 )
433 if errors: 466 if errors:
434 self.error.emit("rsync", "\n".join(errors)) 467 self.error.emit("rsync", "\n".join(errors))
435 468
436 self.rsyncDone.emit(hostDirectory, deviceDirectory) 469 self.rsyncDone.emit(hostDirectory, deviceDirectory)
437 470
438 @pyqtSlot(str) 471 @pyqtSlot(str)
439 def mkdir(self, dirname): 472 def mkdir(self, dirname):
440 """ 473 """
441 Public slot to create a new directory. 474 Public slot to create a new directory.
442 475
443 @param dirname name of the directory to create 476 @param dirname name of the directory to create
444 @type str 477 @type str
445 """ 478 """
446 try: 479 try:
447 self.__commandsInterface.mkdir(dirname) 480 self.__commandsInterface.mkdir(dirname)
448 self.createDirectoryDone.emit() 481 self.createDirectoryDone.emit()
449 except Exception as exc: 482 except Exception as exc:
450 self.error.emit("mkdir", str(exc)) 483 self.error.emit("mkdir", str(exc))
451 484
452 @pyqtSlot(str) 485 @pyqtSlot(str)
453 @pyqtSlot(str, bool) 486 @pyqtSlot(str, bool)
454 def rmdir(self, dirname, recursive=False): 487 def rmdir(self, dirname, recursive=False):
455 """ 488 """
456 Public slot to (recursively) remove a directory. 489 Public slot to (recursively) remove a directory.
457 490
458 @param dirname name of the directory to be removed 491 @param dirname name of the directory to be removed
459 @type str 492 @type str
460 @param recursive flag indicating a recursive removal 493 @param recursive flag indicating a recursive removal
461 @type bool 494 @type bool
462 """ 495 """
463 try: 496 try:
464 if recursive: 497 if recursive:
465 self.__commandsInterface.rmrf(dirname, recursive=True, 498 self.__commandsInterface.rmrf(dirname, recursive=True, force=True)
466 force=True)
467 else: 499 else:
468 self.__commandsInterface.rmdir(dirname) 500 self.__commandsInterface.rmdir(dirname)
469 self.removeDirectoryDone.emit() 501 self.removeDirectoryDone.emit()
470 except Exception as exc: 502 except Exception as exc:
471 self.error.emit("rmdir", str(exc)) 503 self.error.emit("rmdir", str(exc))
472 504
473 def fileSystemInfo(self): 505 def fileSystemInfo(self):
474 """ 506 """
475 Public method to obtain information about the currently mounted file 507 Public method to obtain information about the currently mounted file
476 systems. 508 systems.
477 """ 509 """

eric ide

mercurial