2
\$\begingroup\$

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):
        os.makedirs(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:
        b.write(sha)


def revert(directory, repo=REPO, backup_file=BACKUP):
    try:
        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):
                shutil.rmtree(directory)
            with zipfile.ZipFile(backup_path) as zf:
                zf.extractall(directory)
        else:
            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
    else:
        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:
            backup(dst)
        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)
            else:
                shutil.copy(src_item, dst_item)

Sync (read multiple src dirs from a file):

def sync(directory, sync_file=SYNC):
    try:
        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
            backup(directory)
            for src in src_list:
                apply_diff(src, directory, auto_backup=False)
        else:
            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.

\$\endgroup\$

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.