def sftp_sync(local_dir: str, sftp: paramiko.SFTPClient, remote_dir: str): """Synchronizes local_dir towards remote_dir (i.e. files in remote_dir not in local_dir won't be copied to local_dir)""" info_ = ColorLogger.getLogger('blue') warn_ = ColorLogger.getLogger('yellow') info = info_.log warn = warn_.log o1 = LocalFileOps() o2 = SftpFileOps(sftp) # ensure both base paths exist and are actual folders o1.mkpath(local_dir) o2.mkpath(remote_dir) # stack of (local_relpath_including_base_dir, remote_relpath_including_remote_dir) S = [(local_dir, remote_dir)] while S: relpath, remote_relpath = S.pop() efd = o1.existsIsFileIsDir(relpath) if efd == 1: p1 = relpath p2 = remote_relpath o2.mkpath(os.path.dirname(p2)) # upload only if modification time of p1 > modification time of p2 try: tmp = o2.stat(p2) remote_times = (int(tmp.st_atime), int(tmp.st_mtime)) except: # assume remote file not existing remote_times = (0, 0) tmp = o1.stat(p1) local_times = (int(tmp.st_atime), int(tmp.st_mtime)) local_mtime = local_times[1] remote_mtime = remote_times[1] if local_mtime > remote_mtime: warn( f"Updating remote path {p2} with mtime {remote_mtime} from local path {p1} with mtime {local_mtime}" ) sftp.put(p1, p2) sftp.utime(p2, local_times) else: info( f"Won't update remote path {p2} with mtime {remote_mtime}, equal or more recent than local path {p1} with mtime {local_mtime}" ) elif efd == 2: S.extend(((pathConcat(relpath, filename, '/')), (pathConcat(remote_relpath, filename, '/'))) for filename in o1.listdir(relpath))