diff -r 6014d37d9683 -r f75e990caf99 eric6/MicroPython/MicroPythonFileManager.py --- a/eric6/MicroPython/MicroPythonFileManager.py Sat Aug 10 20:06:37 2019 +0200 +++ b/eric6/MicroPython/MicroPythonFileManager.py Mon Aug 12 14:53:07 2019 +0200 @@ -11,6 +11,7 @@ import os import stat +import shutil from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject @@ -171,7 +172,8 @@ except Exception as exc: self.error.emit("delete", str(exc)) - def __rsync(self, hostDirectory, deviceDirectory, mirror=True): + def __rsync(self, hostDirectory, deviceDirectory, mirror=True, + localDevice=False): """ Private method to synchronize a local directory to the device. @@ -182,6 +184,8 @@ @param mirror flag indicating to mirror the local directory to the device directory @type bool + @param localDevice flag indicating device access via local file system + @type bool @return list of errors @rtype list of str """ @@ -197,26 +201,40 @@ self.tr("Synchronizing <b>{0}</b>.").format(deviceDirectory) ) + doneMessage = self.tr("Done synchronizing <b>{0}</b>.").format( + deviceDirectory) + sourceDict = {} sourceFiles = listdirStat(hostDirectory) for name, nstat in sourceFiles: sourceDict[name] = nstat destinationDict = {} - try: - destinationFiles = self.__commandsInterface.lls(deviceDirectory, - fullstat=True) - except Exception as exc: - return [str(exc)] - if destinationFiles is None: - # the destination directory does not exist + if localDevice: + if not os.path.isdir(deviceDirectory): + # simply copy destination to source + shutil.copytree(hostDirectory, deviceDirectory) + self.rsyncProgressMessage.emit(doneMessage) + return errors + else: + destinationFiles = listdirStat(deviceDirectory) + for name, nstat in destinationFiles: + destinationDict[name] = nstat + else: try: - self.__commandsInterface.mkdir(deviceDirectory) + destinationFiles = self.__commandsInterface.lls(deviceDirectory, + fullstat=True) except Exception as exc: return [str(exc)] - else: - for name, nstat in destinationFiles: - destinationDict[name] = nstat + if destinationFiles is None: + # the destination directory does not exist + try: + self.__commandsInterface.mkdir(deviceDirectory) + except Exception as exc: + return [str(exc)] + else: + for name, nstat in destinationFiles: + destinationDict[name] = nstat destinationSet = set(destinationDict.keys()) sourceSet = set(sourceDict.keys()) @@ -224,87 +242,153 @@ toDelete = destinationSet - sourceSet # delete from dev toUpdate = destinationSet.intersection(sourceSet) # update files - for sourceBasename in toAdd: - # name exists in source but not in device - sourceFilename = os.path.join(hostDirectory, sourceBasename) - destFilename = deviceDirectory + "/" + sourceBasename - self.rsyncProgressMessage.emit( - self.tr("Adding <b>{0}</b>...").format(destFilename)) - if os.path.isfile(sourceFilename): - try: - self.__commandsInterface.put(sourceFilename, destFilename) - except Exception as exc: + if localDevice: + for sourceBasename in toAdd: + # name exists in source but not in device + sourceFilename = os.path.join(hostDirectory, sourceBasename) + destFilename = os.path.join(deviceDirectory, sourceBasename) + self.rsyncProgressMessage.emit( + self.tr("Adding <b>{0}</b>...").format(destFilename)) + if os.path.isfile(sourceFilename): + shutil.copy2(sourceFilename, destFilename) + elif os.path.isdir(sourceFilename): + # recurse + errs = self.__rsync(sourceFilename, destFilename, + mirror=mirror, localDevice=localDevice) # just note issues but ignore them otherwise - errors.append(str(exc)) - if os.path.isdir(sourceFilename): - # recurse - errs = self.__rsync(sourceFilename, destFilename, - mirror=mirror) - # just note issues but ignore them otherwise - errors.extend(errs) - - if mirror: - for destBasename in toDelete: - # name exists in device but not local, delete - destFilename = deviceDirectory + "/" + destBasename + errors.extend(errs) + + if mirror: + for destBasename in toDelete: + # name exists in device but not local, delete + destFilename = os.path.join(deviceDirectory, destBasename) + if os.path.isdir(destFilename): + shutil.rmtree(destFilename, ignore_errors=True) + elif os.path.isfile(destFilename): + os.remove(destFilename) + + for sourceBasename in toUpdate: + # names exist in both; do an update + sourceStat = sourceDict[sourceBasename] + destStat = destinationDict[sourceBasename] + sourceFilename = os.path.join(hostDirectory, sourceBasename) + destFilename = os.path.join(deviceDirectory, sourceBasename) + destMode = destStat[0] + if os.path.isdir(sourceFilename): + if os.path.isdir(destFilename): + # both are directories => recurs + errs = self.__rsync(sourceFilename, destFilename, + mirror=mirror, + localDevice=localDevice) + # just note issues but ignore them otherwise + errors.extend(errs) + else: + self.rsyncProgressMessage.emit( + self.tr("Source <b>{0}</b> is a directory and" + " destination <b>{1}</b> is a file." + " Ignoring it.") + .format(sourceFilename, destFilename) + ) + else: + if os.path.isdir(destFilename): + self.rsyncProgressMessage.emit( + self.tr("Source <b>{0}</b> is a file and" + " destination <b>{1}</b> is a directory." + " Ignoring it.") + .format(sourceFilename, destFilename) + ) + else: + if sourceStat[8] > destStat[8]: # mtime + self.rsyncProgressMessage.emit( + self.tr("Updating <b>{0}</b>...") + .format(destFilename) + ) + shutil.copy2(sourceFilename, destFilename) + else: + for sourceBasename in toAdd: + # name exists in source but not in device + sourceFilename = os.path.join(hostDirectory, sourceBasename) + destFilename = deviceDirectory + "/" + sourceBasename self.rsyncProgressMessage.emit( - self.tr("Removing <b>{0}</b>...").format(destFilename)) - try: - self.__commandsInterface.rmrf(destFilename, recursive=True, - force=True) - except Exception as exc: - # just note issues but ignore them otherwise - errors.append(str(exc)) - - for sourceBasename in toUpdate: - # names exist in both; do an update - sourceStat = sourceDict[sourceBasename] - destStat = destinationDict[sourceBasename] - sourceFilename = os.path.join(hostDirectory, sourceBasename) - destFilename = deviceDirectory + "/" + sourceBasename - destMode = destStat[0] - if os.path.isdir(sourceFilename): - if stat.S_ISDIR(destMode): - # both are directories => recurs + self.tr("Adding <b>{0}</b>...").format(destFilename)) + if os.path.isfile(sourceFilename): + try: + self.__commandsInterface.put(sourceFilename, + destFilename) + except Exception as exc: + # just note issues but ignore them otherwise + errors.append(str(exc)) + elif os.path.isdir(sourceFilename): + # recurse errs = self.__rsync(sourceFilename, destFilename, mirror=mirror) # just note issues but ignore them otherwise errors.extend(errs) - else: + + if mirror: + for destBasename in toDelete: + # name exists in device but not local, delete + destFilename = deviceDirectory + "/" + destBasename self.rsyncProgressMessage.emit( - self.tr("Source <b>{0}</b> is a directory and" - " destination <b>{1}</b> is a file. Ignoring" - " it.") - .format(sourceFilename, destFilename) - ) - else: - if stat.S_ISDIR(destMode): - self.rsyncProgressMessage.emit( - self.tr("Source <b>{0}</b> is a file and destination" - " <b>{1}</b> is a directory. Ignoring it.") - .format(sourceFilename, destFilename) - ) + self.tr("Removing <b>{0}</b>...").format(destFilename)) + try: + self.__commandsInterface.rmrf(destFilename, + recursive=True, + force=True) + except Exception as exc: + # just note issues but ignore them otherwise + errors.append(str(exc)) + + for sourceBasename in toUpdate: + # names exist in both; do an update + sourceStat = sourceDict[sourceBasename] + destStat = destinationDict[sourceBasename] + sourceFilename = os.path.join(hostDirectory, sourceBasename) + destFilename = deviceDirectory + "/" + sourceBasename + destMode = destStat[0] + if os.path.isdir(sourceFilename): + if stat.S_ISDIR(destMode): + # both are directories => recurs + errs = self.__rsync(sourceFilename, destFilename, + mirror=mirror) + # just note issues but ignore them otherwise + errors.extend(errs) + else: + self.rsyncProgressMessage.emit( + self.tr("Source <b>{0}</b> is a directory and" + " destination <b>{1}</b> is a file." + " Ignoring it.") + .format(sourceFilename, destFilename) + ) else: - if sourceStat[8] > destStat[8]: # mtime + if stat.S_ISDIR(destMode): self.rsyncProgressMessage.emit( - self.tr("Updating <b>{0}</b>...") - .format(destFilename) + self.tr("Source <b>{0}</b> is a file and" + " destination <b>{1}</b> is a directory." + " Ignoring it.") + .format(sourceFilename, destFilename) ) - try: - self.__commandsInterface.put(sourceFilename, - destFilename) - except Exception as exc: - errors.append(str(exc)) + else: + if sourceStat[8] > destStat[8]: # mtime + self.rsyncProgressMessage.emit( + self.tr("Updating <b>{0}</b>...") + .format(destFilename) + ) + try: + self.__commandsInterface.put(sourceFilename, + destFilename) + except Exception as exc: + errors.append(str(exc)) - self.rsyncProgressMessage.emit( - self.tr("Done synchronizing <b>{0}</b>.").format(deviceDirectory) - ) + self.rsyncProgressMessage.emit(doneMessage) return errors @pyqtSlot(str, str) @pyqtSlot(str, str, bool) - def rsync(self, hostDirectory, deviceDirectory, mirror=True): + @pyqtSlot(str, str, bool, bool) + def rsync(self, hostDirectory, deviceDirectory, mirror=True, + localDevice=False): """ Public slot to synchronize a local directory to the device. @@ -315,8 +399,11 @@ @param mirror flag indicating to mirror the local directory to the device directory @type bool + @param localDevice flag indicating device access via local file system + @type bool """ - errors = self.__rsync(hostDirectory, deviceDirectory, mirror=mirror) + errors = self.__rsync(hostDirectory, deviceDirectory, mirror=mirror, + localDevice=localDevice) if errors: self.error.emit("rsync", "\n".join(errors))