async def sync(self, now: datetime):
    if (self.simulate_error is not None):
        if self.simulate_error.startswith('test'):
            raise Exception(self.simulate_error)
        else:
            raise SimulatedError(self.simulate_error)
    (await self._syncBackups([self.source, self.dest]))
    self.source.checkBeforeChanges()
    self.dest.checkBeforeChanges()
    if (not self.dest.needsConfiguration()):
        if self.source.enabled():
            (await self._purge(self.source))
        if self.dest.enabled():
            (await self._purge(self.dest))
    if ((self.config.get(Setting.IGNORE_OTHER_BACKUPS) or self.config.get(Setting.IGNORE_UPGRADE_BACKUPS)) and (self.config.get(Setting.DELETE_IGNORED_AFTER_DAYS) > 0)):
        cutoff = (now - timedelta(days=self.config.get(Setting.DELETE_IGNORED_AFTER_DAYS)))
        delete = []
        for backup in self.backups.values():
            if (backup.ignore() and (backup.date() < cutoff)):
                delete.append(backup)
        for backup in delete:
            (await self.deleteBackup(backup, self.source))
    self._handleBackupDetails()
    next_backup = self.nextBackup(now)
    if (next_backup and (now >= next_backup) and self.source.enabled() and (not self.dest.needsConfiguration())):
        if self.config.get(Setting.DELETE_BEFORE_NEW_BACKUP):
            (await self._purge(self.source, pre_purge=True))
        (await self.createBackup(CreateOptions(now, self.config.get(Setting.BACKUP_NAME))))
        (await self._purge(self.source))
        self._handleBackupDetails()
    if (self.dest.enabled() and self.dest.upload()):
        uploads = []
        for backup in self.backups.values():
            if ((backup.getSource(self.source.name()) is not None) and backup.getSource(self.source.name()).uploadable() and (backup.getSource(self.dest.name()) is None) and (not backup.ignore())):
                uploads.append(backup)
        uploads.sort(key=(lambda s: s.date()))
        uploads.reverse()
        for upload in uploads:
            dummy = DummyBackup('', upload.date(), self.dest.name(), 'dummy_slug_name')
            proposed = list(self.backups.values())
            proposed.append(dummy)
            if (self._nextPurge(self.dest, proposed) != dummy):
                if self.config.get(Setting.DELETE_BEFORE_NEW_BACKUP):
                    (await self._purge(self.dest, pre_purge=True))
                upload.addSource((await self.dest.save(upload, (await self.source.read(upload)))))
                (await self._purge(self.dest))
                self._handleBackupDetails()
            else:
                break
        if self.config.get(Setting.DELETE_AFTER_UPLOAD):
            (await self._purge(self.source))
    self._handleBackupDetails()
    self.source.postSync()
    self.dest.postSync()
    self._data_cache.saveIfDirty()
async def sync(self, now: datetime):
    if (self.simulate_error is not None):
        if self.simulate_error.startswith('test'):
            raise Exception(self.simulate_error)
        else:
            raise SimulatedError(self.simulate_error)
    (await self._syncBackups([self.source, self.dest]))
    self.source.checkBeforeChanges()
    self.dest.checkBeforeChanges()
    if (not self.dest.needsConfiguration()):
        if self.source.enabled():
            (await self._purge(self.source))
        if self.dest.enabled():
            (await self._purge(self.dest))
    if ((self.config.get(Setting.IGNORE_OTHER_BACKUPS) or self.config.get(Setting.IGNORE_UPGRADE_BACKUPS)) and (self.config.get(Setting.DELETE_IGNORED_AFTER_DAYS) > 0)):
        cutoff = (now - timedelta(days=self.config.get(Setting.DELETE_IGNORED_AFTER_DAYS)))
        delete = []
        for backup in self.backups.values():
            if (backup.ignore() and (backup.date() < cutoff)):
                delete.append(backup)
        for backup in delete:
            (await self.deleteBackup(backup, self.source))
    self._handleBackupDetails()
    next_backup = self.nextBackup(now)
    if (next_backup and (now >= next_backup) and self.source.enabled() and (not self.dest.needsConfiguration())):
        if self.config.get(Setting.DELETE_BEFORE_NEW_BACKUP):
            (await self._purge(self.source, pre_purge=True))
        (await self.createBackup(CreateOptions(now, self.config.get(Setting.BACKUP_NAME))))
        (await self._purge(self.source))
        self._handleBackupDetails()
    if (self.dest.enabled() and self.dest.upload()):
        uploads = []
        for backup in self.backups.values():
            if ((backup.getSource(self.source.name()) is not None) and backup.getSource(self.source.name()).uploadable() and (backup.getSource(self.dest.name()) is None) and (not backup.ignore())):
                uploads.append(backup)
        uploads.sort(key=(lambda s: s.date()))
        uploads.reverse()
        for upload in uploads:
            dummy = DummyBackup(, upload.date(), self.dest.name(), 'dummy_slug_name')
            proposed = list(self.backups.values())
            proposed.append(dummy)
            if (self._nextPurge(self.dest, proposed) != dummy):
                if self.config.get(Setting.DELETE_BEFORE_NEW_BACKUP):
                    (await self._purge(self.dest, pre_purge=True))
                upload.addSource((await self.dest.save(upload, (await self.source.read(upload)))))
                (await self._purge(self.dest))
                self._handleBackupDetails()
            else:
                break
        if self.config.get(Setting.DELETE_AFTER_UPLOAD):
            (await self._purge(self.source))
    self._handleBackupDetails()
    self.source.postSync()
    self.dest.postSync()
    self._data_cache.saveIfDirty()