Last active
May 14, 2024 19:41
-
-
Save wecsam/74aeb122cd3deea3d45ab7c5fad38995 to your computer and use it in GitHub Desktop.
Wait for free space when moving files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import datetime | |
import humanfriendly # pip install humanfriendly | |
import os | |
import shutil | |
import subprocess | |
import sys | |
import time | |
def print_with_time(*args, **kwargs): | |
print(datetime.datetime.now(), "-", *args, **kwargs) | |
def size(s): | |
try: return humanfriendly.parse_size(s) | |
except humanfriendly.InvalidSize: raise ValueError | |
def wait_for_free_space(dst, minimum_free): | |
while True: | |
if (free := shutil.disk_usage(dst).free) >= minimum_free: # PEP 572 requires Python 3.8 or later. | |
break | |
print_with_time( | |
"Waiting for free space (available: {}, required: {})...".format( | |
humanfriendly.format_size(free, binary=True), | |
humanfriendly.format_size(minimum_free, binary=True) | |
) | |
) | |
# Wait 5 minutes, but try to land on the top of the minute. | |
time.sleep(300.0 - time.time() % 60.0) | |
def move_one_file(src, dst, minimum_free): | |
wait_for_free_space(dst, minimum_free) | |
print_with_time(src, "->", end=" ") | |
sys.stdout.flush() | |
try: | |
dst = shutil.move(src, dst) | |
except (OSError, shutil.Error) as e: | |
print("error:", e) | |
return | |
else: | |
print(dst) | |
# Free the space up after uploading to OneDrive. | |
subprocess.run([r"C:\Windows\System32\attrib.exe", "-P", "+U", dst]) | |
def move_tree(src, dst, minimum_free): | |
for dirpath, dirnames, filenames in os.walk(src): | |
dst_subdir = os.path.normpath(os.path.join(dst, os.path.relpath(dirpath, src))) | |
os.makedirs(dst_subdir, exist_ok=True) | |
for filename in filenames: | |
move_one_file(os.path.join(dirpath, filename), dst_subdir, minimum_free) | |
def main(): | |
parser = argparse.ArgumentParser( | |
description= | |
"This tool moves a file or recursively moves a directory of files. " | |
"Before moving each file, it waits for the destination disk to have the specified amount of free space. " | |
"After moving each file, it sets its attributes so that OneDrive Files on Demand will free its space up." | |
) | |
parser.add_argument("src", help="the file to move or the directory from which to move files") | |
parser.add_argument("dst", help="the directory to which to move files") | |
parser.add_argument( | |
"-f", "--free", | |
type=size, | |
default="8 GiB", | |
metavar="size", | |
dest="minimum_free", | |
help="the amount of space to leave free on the destination disk (default: %(default)s)" | |
) | |
args = parser.parse_args() | |
if os.path.isdir(args.src): | |
if not os.path.isdir(args.dst): | |
parser.error("If the source is a directory, the destination must be a directory.") | |
move = move_tree | |
else: | |
move = move_one_file | |
try: | |
move(**vars(args)) | |
except KeyboardInterrupt: | |
print_with_time("^C", file=sys.stderr) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment