I’ve written a small utility tool capable of

  • backing up a directory,
  • restoring a backed up directory,
  • syncing two directories (copying missing or changed files from src to dst).

I’d love your feedback - any code smells, improvements, potential bugs, non-idiomatic approaches, etc., please let me know.

Here's how I backup and revert:

SHA1_DIGEST_REGEX = "[a-fA-F0-9]{40}"

def backup(directory, repo=REPO, backup_file=BACKUP):
    if not os.path.isdir(repo):
    key = str(time.time())
    sha = hashlib.sha1(key).hexdigest()
    archive_path = os.path.join(repo, sha)
    assert not os.path.isfile(archive_path)
    shutil.make_archive(archive_path, "zip", directory)
    backup_path = os.path.join(directory, backup_file)
    with open(backup_path, 'w') as b:

def revert(directory, repo=REPO, backup_file=BACKUP):
        with open(os.path.join(directory, backup_file)) as b:
            sha = b.readline()
        if re.compile(SHA1_DIGEST_REGEX).match(sha):
            backup_path = os.path.join(repo, sha + ".zip")
            if not os.path.isfile(backup_path):
                raise IOError
            if os.listdir(directory):
            with zipfile.ZipFile(backup_path) as zf:
            print "Backup file corrupted."
    except IOError:
        print "Either no history is available or the backup files are missing."

Comparing two directories and applying the changes:

def find_diff(src, dst):
    if os.path.isdir(dst):
        diff = filecmp.dircmp(src, dst)
        return diff.left_only + diff.diff_files
        return [f for f in os.listdir(src)]

def apply_diff(src, dst, diff_list=None, auto_backup=True):
    if diff_list is None:
        diff_list = find_diff(src, dst)
    if diff_list:
        if auto_backup:
        for item in diff_list:
            src_item = os.path.join(src, item)
            dst_item = os.path.join(dst, item)
            if os.path.isdir(src_item):
                shutil.copytree(src_item, dst_item)
                shutil.copy(src_item, dst_item)

Sync (read multiple src dirs from a file):

def sync(directory, sync_file=SYNC):
        with open(os.path.join(directory, sync_file)) as s:
            src_list = s.read().splitlines()
        if src_list:
            for src in src_list:
                if not os.path.isdir(src):
                    raise IOError
            for src in src_list:
                apply_diff(src, directory, auto_backup=False)
            print "Sync file is empty."
    except IOError:
        print "Sync file is either missing or contains invalid sources."

I hope the code speaks for itself, but I'm happy to clarify things if they don't make sense.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.