Use delete on close semantics on windows for cleanup post copy

This commit is contained in:
Kovid Goyal 2023-06-06 13:24:21 +05:30
parent c2e9a78ddf
commit c3da25d112
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -21,8 +21,9 @@ WindowsFileId = Tuple[int, int, int]
class UnixFileCopier:
def __init__(self, delete_all=False):
def __init__(self, delete_all=False, allow_move=False):
self.delete_all = delete_all
self.allow_move = allow_move
self.copy_map: Dict[str, str] = {}
def register(self, path: str, dest: str) -> None:
@ -62,8 +63,9 @@ class WindowsFileCopier:
Locks all files before starting the copy, ensuring other processes cannot interfere
'''
def __init__(self, delete_all=False):
def __init__(self, delete_all=False, allow_move=False):
self.delete_all = delete_all
self.allow_move = allow_move
self.path_to_fileid_map : Dict[str, WindowsFileId] = {}
self.fileid_to_paths_map: Dict[WindowsFileId, Set[str]] = defaultdict(set)
self.path_to_handle_map: Dict[str, 'winutil.Handle'] = {}
@ -87,9 +89,11 @@ class WindowsFileCopier:
def _open_file(self, path: str, retry_on_sharing_violation: bool = True, is_folder: bool = False) -> 'winutil.Handle':
flags = winutil.FILE_FLAG_BACKUP_SEMANTICS if is_folder else winutil.FILE_FLAG_SEQUENTIAL_SCAN
if self.delete_all:
flags |= winutil.FILE_FLAG_DELETE_ON_CLOSE
try:
return winutil.create_file(make_long_path_useable(path), winutil.GENERIC_READ,
winutil.FILE_SHARE_DELETE, winutil.OPEN_EXISTING, flags)
winutil.FILE_SHARE_DELETE if self.allow_move else 0, winutil.OPEN_EXISTING, flags)
except OSError as e:
if e.winerror == winutil.ERROR_SHARING_VIOLATION:
# The file could be a hardlink to an already opened file,
@ -116,13 +120,9 @@ class WindowsFileCopier:
self.folder_to_handle_map[path] = self._open_file(path, is_folder=True)
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
try:
if self.delete_all and exc_val is None:
self.delete_all_source_files()
finally:
for h in self.path_to_handle_map.values():
h.close()
for h in self.folder_to_handle_map.values():
for h in reversed(self.folder_to_handle_map.values()):
h.close()
def copy_all(self) -> None:
@ -146,20 +146,14 @@ class WindowsFileCopier:
for src_path, dest_path in self.copy_map.items():
winutil.move_file(make_long_path_useable(src_path), make_long_path_useable(dest_path))
def delete_all_source_files(self) -> None:
for src_path in self.copy_map:
winutil.delete_file(make_long_path_useable(src_path))
for path in reversed(self.folders):
os.rmdir(make_long_path_useable(path))
def get_copier(delete_all=False) -> Union[UnixFileCopier, WindowsFileCopier]:
return WindowsFileCopier(delete_all) if iswindows else UnixFileCopier(delete_all)
def get_copier(delete_all=False, allow_move=False) -> Union[UnixFileCopier, WindowsFileCopier]:
return (WindowsFileCopier if iswindows else UnixFileCopier)(delete_all, allow_move)
def rename_files(src_to_dest_map: Dict[str, str]) -> None:
' Rename a bunch of files. On Windows all files are locked before renaming so no other process can interfere. '
copier = get_copier()
copier = get_copier(allow_move=True)
for s, d in src_to_dest_map.items():
copier.register(s, d)
with copier: