eric6/MicroPython/MicroPythonFileManager.py

branch
micropython
changeset 7131
f75e990caf99
parent 7108
4f6133a01c6a
child 7137
4ed2573947ff
equal deleted inserted replaced
7130:6014d37d9683 7131:f75e990caf99
9 9
10 from __future__ import unicode_literals 10 from __future__ import unicode_literals
11 11
12 import os 12 import os
13 import stat 13 import stat
14 import shutil
14 15
15 from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject 16 from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject
16 17
17 from .MicroPythonFileSystemUtilities import ( 18 from .MicroPythonFileSystemUtilities import (
18 mtime2string, mode2string, decoratedName, listdirStat 19 mtime2string, mode2string, decoratedName, listdirStat
169 self.__commandsInterface.rm(deviceFileName) 170 self.__commandsInterface.rm(deviceFileName)
170 self.deleteFileDone.emit(deviceFileName) 171 self.deleteFileDone.emit(deviceFileName)
171 except Exception as exc: 172 except Exception as exc:
172 self.error.emit("delete", str(exc)) 173 self.error.emit("delete", str(exc))
173 174
174 def __rsync(self, hostDirectory, deviceDirectory, mirror=True): 175 def __rsync(self, hostDirectory, deviceDirectory, mirror=True,
176 localDevice=False):
175 """ 177 """
176 Private method to synchronize a local directory to the device. 178 Private method to synchronize a local directory to the device.
177 179
178 @param hostDirectory name of the local directory 180 @param hostDirectory name of the local directory
179 @type str 181 @type str
180 @param deviceDirectory name of the directory on the device 182 @param deviceDirectory name of the directory on the device
181 @type str 183 @type str
182 @param mirror flag indicating to mirror the local directory to 184 @param mirror flag indicating to mirror the local directory to
183 the device directory 185 the device directory
186 @type bool
187 @param localDevice flag indicating device access via local file system
184 @type bool 188 @type bool
185 @return list of errors 189 @return list of errors
186 @rtype list of str 190 @rtype list of str
187 """ 191 """
188 errors = [] 192 errors = []
195 199
196 self.rsyncProgressMessage.emit( 200 self.rsyncProgressMessage.emit(
197 self.tr("Synchronizing <b>{0}</b>.").format(deviceDirectory) 201 self.tr("Synchronizing <b>{0}</b>.").format(deviceDirectory)
198 ) 202 )
199 203
204 doneMessage = self.tr("Done synchronizing <b>{0}</b>.").format(
205 deviceDirectory)
206
200 sourceDict = {} 207 sourceDict = {}
201 sourceFiles = listdirStat(hostDirectory) 208 sourceFiles = listdirStat(hostDirectory)
202 for name, nstat in sourceFiles: 209 for name, nstat in sourceFiles:
203 sourceDict[name] = nstat 210 sourceDict[name] = nstat
204 211
205 destinationDict = {} 212 destinationDict = {}
206 try: 213 if localDevice:
207 destinationFiles = self.__commandsInterface.lls(deviceDirectory, 214 if not os.path.isdir(deviceDirectory):
208 fullstat=True) 215 # simply copy destination to source
209 except Exception as exc: 216 shutil.copytree(hostDirectory, deviceDirectory)
210 return [str(exc)] 217 self.rsyncProgressMessage.emit(doneMessage)
211 if destinationFiles is None: 218 return errors
212 # the destination directory does not exist 219 else:
220 destinationFiles = listdirStat(deviceDirectory)
221 for name, nstat in destinationFiles:
222 destinationDict[name] = nstat
223 else:
213 try: 224 try:
214 self.__commandsInterface.mkdir(deviceDirectory) 225 destinationFiles = self.__commandsInterface.lls(deviceDirectory,
226 fullstat=True)
215 except Exception as exc: 227 except Exception as exc:
216 return [str(exc)] 228 return [str(exc)]
217 else: 229 if destinationFiles is None:
218 for name, nstat in destinationFiles: 230 # the destination directory does not exist
219 destinationDict[name] = nstat 231 try:
232 self.__commandsInterface.mkdir(deviceDirectory)
233 except Exception as exc:
234 return [str(exc)]
235 else:
236 for name, nstat in destinationFiles:
237 destinationDict[name] = nstat
220 238
221 destinationSet = set(destinationDict.keys()) 239 destinationSet = set(destinationDict.keys())
222 sourceSet = set(sourceDict.keys()) 240 sourceSet = set(sourceDict.keys())
223 toAdd = sourceSet - destinationSet # add to dev 241 toAdd = sourceSet - destinationSet # add to dev
224 toDelete = destinationSet - sourceSet # delete from dev 242 toDelete = destinationSet - sourceSet # delete from dev
225 toUpdate = destinationSet.intersection(sourceSet) # update files 243 toUpdate = destinationSet.intersection(sourceSet) # update files
226 244
227 for sourceBasename in toAdd: 245 if localDevice:
228 # name exists in source but not in device 246 for sourceBasename in toAdd:
229 sourceFilename = os.path.join(hostDirectory, sourceBasename) 247 # name exists in source but not in device
230 destFilename = deviceDirectory + "/" + sourceBasename 248 sourceFilename = os.path.join(hostDirectory, sourceBasename)
231 self.rsyncProgressMessage.emit( 249 destFilename = os.path.join(deviceDirectory, sourceBasename)
232 self.tr("Adding <b>{0}</b>...").format(destFilename)) 250 self.rsyncProgressMessage.emit(
233 if os.path.isfile(sourceFilename): 251 self.tr("Adding <b>{0}</b>...").format(destFilename))
234 try: 252 if os.path.isfile(sourceFilename):
235 self.__commandsInterface.put(sourceFilename, destFilename) 253 shutil.copy2(sourceFilename, destFilename)
236 except Exception as exc: 254 elif os.path.isdir(sourceFilename):
255 # recurse
256 errs = self.__rsync(sourceFilename, destFilename,
257 mirror=mirror, localDevice=localDevice)
237 # just note issues but ignore them otherwise 258 # just note issues but ignore them otherwise
238 errors.append(str(exc)) 259 errors.extend(errs)
239 if os.path.isdir(sourceFilename): 260
240 # recurse 261 if mirror:
241 errs = self.__rsync(sourceFilename, destFilename, 262 for destBasename in toDelete:
242 mirror=mirror) 263 # name exists in device but not local, delete
243 # just note issues but ignore them otherwise 264 destFilename = os.path.join(deviceDirectory, destBasename)
244 errors.extend(errs) 265 if os.path.isdir(destFilename):
245 266 shutil.rmtree(destFilename, ignore_errors=True)
246 if mirror: 267 elif os.path.isfile(destFilename):
247 for destBasename in toDelete: 268 os.remove(destFilename)
248 # name exists in device but not local, delete 269
249 destFilename = deviceDirectory + "/" + destBasename 270 for sourceBasename in toUpdate:
271 # names exist in both; do an update
272 sourceStat = sourceDict[sourceBasename]
273 destStat = destinationDict[sourceBasename]
274 sourceFilename = os.path.join(hostDirectory, sourceBasename)
275 destFilename = os.path.join(deviceDirectory, sourceBasename)
276 destMode = destStat[0]
277 if os.path.isdir(sourceFilename):
278 if os.path.isdir(destFilename):
279 # both are directories => recurs
280 errs = self.__rsync(sourceFilename, destFilename,
281 mirror=mirror,
282 localDevice=localDevice)
283 # just note issues but ignore them otherwise
284 errors.extend(errs)
285 else:
286 self.rsyncProgressMessage.emit(
287 self.tr("Source <b>{0}</b> is a directory and"
288 " destination <b>{1}</b> is a file."
289 " Ignoring it.")
290 .format(sourceFilename, destFilename)
291 )
292 else:
293 if os.path.isdir(destFilename):
294 self.rsyncProgressMessage.emit(
295 self.tr("Source <b>{0}</b> is a file and"
296 " destination <b>{1}</b> is a directory."
297 " Ignoring it.")
298 .format(sourceFilename, destFilename)
299 )
300 else:
301 if sourceStat[8] > destStat[8]: # mtime
302 self.rsyncProgressMessage.emit(
303 self.tr("Updating <b>{0}</b>...")
304 .format(destFilename)
305 )
306 shutil.copy2(sourceFilename, destFilename)
307 else:
308 for sourceBasename in toAdd:
309 # name exists in source but not in device
310 sourceFilename = os.path.join(hostDirectory, sourceBasename)
311 destFilename = deviceDirectory + "/" + sourceBasename
250 self.rsyncProgressMessage.emit( 312 self.rsyncProgressMessage.emit(
251 self.tr("Removing <b>{0}</b>...").format(destFilename)) 313 self.tr("Adding <b>{0}</b>...").format(destFilename))
252 try: 314 if os.path.isfile(sourceFilename):
253 self.__commandsInterface.rmrf(destFilename, recursive=True, 315 try:
254 force=True) 316 self.__commandsInterface.put(sourceFilename,
255 except Exception as exc: 317 destFilename)
256 # just note issues but ignore them otherwise 318 except Exception as exc:
257 errors.append(str(exc)) 319 # just note issues but ignore them otherwise
258 320 errors.append(str(exc))
259 for sourceBasename in toUpdate: 321 elif os.path.isdir(sourceFilename):
260 # names exist in both; do an update 322 # recurse
261 sourceStat = sourceDict[sourceBasename]
262 destStat = destinationDict[sourceBasename]
263 sourceFilename = os.path.join(hostDirectory, sourceBasename)
264 destFilename = deviceDirectory + "/" + sourceBasename
265 destMode = destStat[0]
266 if os.path.isdir(sourceFilename):
267 if stat.S_ISDIR(destMode):
268 # both are directories => recurs
269 errs = self.__rsync(sourceFilename, destFilename, 323 errs = self.__rsync(sourceFilename, destFilename,
270 mirror=mirror) 324 mirror=mirror)
271 # just note issues but ignore them otherwise 325 # just note issues but ignore them otherwise
272 errors.extend(errs) 326 errors.extend(errs)
327
328 if mirror:
329 for destBasename in toDelete:
330 # name exists in device but not local, delete
331 destFilename = deviceDirectory + "/" + destBasename
332 self.rsyncProgressMessage.emit(
333 self.tr("Removing <b>{0}</b>...").format(destFilename))
334 try:
335 self.__commandsInterface.rmrf(destFilename,
336 recursive=True,
337 force=True)
338 except Exception as exc:
339 # just note issues but ignore them otherwise
340 errors.append(str(exc))
341
342 for sourceBasename in toUpdate:
343 # names exist in both; do an update
344 sourceStat = sourceDict[sourceBasename]
345 destStat = destinationDict[sourceBasename]
346 sourceFilename = os.path.join(hostDirectory, sourceBasename)
347 destFilename = deviceDirectory + "/" + sourceBasename
348 destMode = destStat[0]
349 if os.path.isdir(sourceFilename):
350 if stat.S_ISDIR(destMode):
351 # both are directories => recurs
352 errs = self.__rsync(sourceFilename, destFilename,
353 mirror=mirror)
354 # just note issues but ignore them otherwise
355 errors.extend(errs)
356 else:
357 self.rsyncProgressMessage.emit(
358 self.tr("Source <b>{0}</b> is a directory and"
359 " destination <b>{1}</b> is a file."
360 " Ignoring it.")
361 .format(sourceFilename, destFilename)
362 )
273 else: 363 else:
274 self.rsyncProgressMessage.emit( 364 if stat.S_ISDIR(destMode):
275 self.tr("Source <b>{0}</b> is a directory and"
276 " destination <b>{1}</b> is a file. Ignoring"
277 " it.")
278 .format(sourceFilename, destFilename)
279 )
280 else:
281 if stat.S_ISDIR(destMode):
282 self.rsyncProgressMessage.emit(
283 self.tr("Source <b>{0}</b> is a file and destination"
284 " <b>{1}</b> is a directory. Ignoring it.")
285 .format(sourceFilename, destFilename)
286 )
287 else:
288 if sourceStat[8] > destStat[8]: # mtime
289 self.rsyncProgressMessage.emit( 365 self.rsyncProgressMessage.emit(
290 self.tr("Updating <b>{0}</b>...") 366 self.tr("Source <b>{0}</b> is a file and"
291 .format(destFilename) 367 " destination <b>{1}</b> is a directory."
368 " Ignoring it.")
369 .format(sourceFilename, destFilename)
292 ) 370 )
293 try: 371 else:
294 self.__commandsInterface.put(sourceFilename, 372 if sourceStat[8] > destStat[8]: # mtime
295 destFilename) 373 self.rsyncProgressMessage.emit(
296 except Exception as exc: 374 self.tr("Updating <b>{0}</b>...")
297 errors.append(str(exc)) 375 .format(destFilename)
298 376 )
299 self.rsyncProgressMessage.emit( 377 try:
300 self.tr("Done synchronizing <b>{0}</b>.").format(deviceDirectory) 378 self.__commandsInterface.put(sourceFilename,
301 ) 379 destFilename)
380 except Exception as exc:
381 errors.append(str(exc))
382
383 self.rsyncProgressMessage.emit(doneMessage)
302 384
303 return errors 385 return errors
304 386
305 @pyqtSlot(str, str) 387 @pyqtSlot(str, str)
306 @pyqtSlot(str, str, bool) 388 @pyqtSlot(str, str, bool)
307 def rsync(self, hostDirectory, deviceDirectory, mirror=True): 389 @pyqtSlot(str, str, bool, bool)
390 def rsync(self, hostDirectory, deviceDirectory, mirror=True,
391 localDevice=False):
308 """ 392 """
309 Public slot to synchronize a local directory to the device. 393 Public slot to synchronize a local directory to the device.
310 394
311 @param hostDirectory name of the local directory 395 @param hostDirectory name of the local directory
312 @type str 396 @type str
313 @param deviceDirectory name of the directory on the device 397 @param deviceDirectory name of the directory on the device
314 @type str 398 @type str
315 @param mirror flag indicating to mirror the local directory to 399 @param mirror flag indicating to mirror the local directory to
316 the device directory 400 the device directory
317 @type bool 401 @type bool
318 """ 402 @param localDevice flag indicating device access via local file system
319 errors = self.__rsync(hostDirectory, deviceDirectory, mirror=mirror) 403 @type bool
404 """
405 errors = self.__rsync(hostDirectory, deviceDirectory, mirror=mirror,
406 localDevice=localDevice)
320 if errors: 407 if errors:
321 self.error.emit("rsync", "\n".join(errors)) 408 self.error.emit("rsync", "\n".join(errors))
322 409
323 self.rsyncDone.emit(hostDirectory, deviceDirectory) 410 self.rsyncDone.emit(hostDirectory, deviceDirectory)
324 411

eric ide

mercurial