|
""" |
|
Disk management utilities. |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
import errno |
|
import os |
|
import shutil |
|
import sys |
|
import time |
|
from multiprocessing import util |
|
|
|
try: |
|
WindowsError |
|
except NameError: |
|
WindowsError = OSError |
|
|
|
|
|
def disk_used(path): |
|
"""Return the disk usage in a directory.""" |
|
size = 0 |
|
for file in os.listdir(path) + ["."]: |
|
stat = os.stat(os.path.join(path, file)) |
|
if hasattr(stat, "st_blocks"): |
|
size += stat.st_blocks * 512 |
|
else: |
|
|
|
|
|
size += (stat.st_size // 512 + 1) * 512 |
|
|
|
|
|
return int(size / 1024.0) |
|
|
|
|
|
def memstr_to_bytes(text): |
|
"""Convert a memory text to its value in bytes.""" |
|
kilo = 1024 |
|
units = dict(K=kilo, M=kilo**2, G=kilo**3) |
|
try: |
|
size = int(units[text[-1]] * float(text[:-1])) |
|
except (KeyError, ValueError) as e: |
|
raise ValueError( |
|
"Invalid literal for size give: %s (type %s) should be " |
|
"alike '10G', '500M', '50K'." % (text, type(text)) |
|
) from e |
|
return size |
|
|
|
|
|
def mkdirp(d): |
|
"""Ensure directory d exists (like mkdir -p on Unix) |
|
No guarantee that the directory is writable. |
|
""" |
|
try: |
|
os.makedirs(d) |
|
except OSError as e: |
|
if e.errno != errno.EEXIST: |
|
raise |
|
|
|
|
|
|
|
|
|
|
|
|
|
RM_SUBDIRS_RETRY_TIME = 0.1 |
|
RM_SUBDIRS_N_RETRY = 10 |
|
|
|
|
|
def rm_subdirs(path, onerror=None): |
|
"""Remove all subdirectories in this path. |
|
|
|
The directory indicated by `path` is left in place, and its subdirectories |
|
are erased. |
|
|
|
If onerror is set, it is called to handle the error with arguments (func, |
|
path, exc_info) where func is os.listdir, os.remove, or os.rmdir; |
|
path is the argument to that function that caused it to fail; and |
|
exc_info is a tuple returned by sys.exc_info(). If onerror is None, |
|
an exception is raised. |
|
""" |
|
|
|
|
|
|
|
|
|
names = [] |
|
try: |
|
names = os.listdir(path) |
|
except os.error: |
|
if onerror is not None: |
|
onerror(os.listdir, path, sys.exc_info()) |
|
else: |
|
raise |
|
|
|
for name in names: |
|
fullname = os.path.join(path, name) |
|
delete_folder(fullname, onerror=onerror) |
|
|
|
|
|
def delete_folder(folder_path, onerror=None, allow_non_empty=True): |
|
"""Utility function to cleanup a temporary folder if it still exists.""" |
|
if os.path.isdir(folder_path): |
|
if onerror is not None: |
|
shutil.rmtree(folder_path, False, onerror) |
|
else: |
|
|
|
|
|
err_count = 0 |
|
while True: |
|
files = os.listdir(folder_path) |
|
try: |
|
if len(files) == 0 or allow_non_empty: |
|
shutil.rmtree(folder_path, ignore_errors=False, onerror=None) |
|
util.debug("Successfully deleted {}".format(folder_path)) |
|
break |
|
else: |
|
raise OSError( |
|
"Expected empty folder {} but got {} files.".format( |
|
folder_path, len(files) |
|
) |
|
) |
|
except (OSError, WindowsError): |
|
err_count += 1 |
|
if err_count > RM_SUBDIRS_N_RETRY: |
|
|
|
|
|
|
|
raise |
|
time.sleep(RM_SUBDIRS_RETRY_TIME) |
|
|