mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Use delete on close semantics on windows for cleanup post copy
This commit is contained in:
parent
c2e9a78ddf
commit
c3da25d112
@ -21,8 +21,9 @@ WindowsFileId = Tuple[int, int, int]
|
|||||||
|
|
||||||
class UnixFileCopier:
|
class UnixFileCopier:
|
||||||
|
|
||||||
def __init__(self, delete_all=False):
|
def __init__(self, delete_all=False, allow_move=False):
|
||||||
self.delete_all = delete_all
|
self.delete_all = delete_all
|
||||||
|
self.allow_move = allow_move
|
||||||
self.copy_map: Dict[str, str] = {}
|
self.copy_map: Dict[str, str] = {}
|
||||||
|
|
||||||
def register(self, path: str, dest: str) -> None:
|
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
|
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.delete_all = delete_all
|
||||||
|
self.allow_move = allow_move
|
||||||
self.path_to_fileid_map : Dict[str, WindowsFileId] = {}
|
self.path_to_fileid_map : Dict[str, WindowsFileId] = {}
|
||||||
self.fileid_to_paths_map: Dict[WindowsFileId, Set[str]] = defaultdict(set)
|
self.fileid_to_paths_map: Dict[WindowsFileId, Set[str]] = defaultdict(set)
|
||||||
self.path_to_handle_map: Dict[str, 'winutil.Handle'] = {}
|
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':
|
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
|
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:
|
try:
|
||||||
return winutil.create_file(make_long_path_useable(path), winutil.GENERIC_READ,
|
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:
|
except OSError as e:
|
||||||
if e.winerror == winutil.ERROR_SHARING_VIOLATION:
|
if e.winerror == winutil.ERROR_SHARING_VIOLATION:
|
||||||
# The file could be a hardlink to an already opened file,
|
# The file could be a hardlink to an already opened file,
|
||||||
@ -116,14 +120,10 @@ class WindowsFileCopier:
|
|||||||
self.folder_to_handle_map[path] = self._open_file(path, is_folder=True)
|
self.folder_to_handle_map[path] = self._open_file(path, is_folder=True)
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
||||||
try:
|
for h in self.path_to_handle_map.values():
|
||||||
if self.delete_all and exc_val is None:
|
h.close()
|
||||||
self.delete_all_source_files()
|
for h in reversed(self.folder_to_handle_map.values()):
|
||||||
finally:
|
h.close()
|
||||||
for h in self.path_to_handle_map.values():
|
|
||||||
h.close()
|
|
||||||
for h in self.folder_to_handle_map.values():
|
|
||||||
h.close()
|
|
||||||
|
|
||||||
def copy_all(self) -> None:
|
def copy_all(self) -> None:
|
||||||
for src_path, dest_path in self.copy_map.items():
|
for src_path, dest_path in self.copy_map.items():
|
||||||
@ -146,20 +146,14 @@ class WindowsFileCopier:
|
|||||||
for src_path, dest_path in self.copy_map.items():
|
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))
|
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, allow_move=False) -> Union[UnixFileCopier, WindowsFileCopier]:
|
||||||
def get_copier(delete_all=False) -> Union[UnixFileCopier, WindowsFileCopier]:
|
return (WindowsFileCopier if iswindows else UnixFileCopier)(delete_all, allow_move)
|
||||||
return WindowsFileCopier(delete_all) if iswindows else UnixFileCopier(delete_all)
|
|
||||||
|
|
||||||
|
|
||||||
def rename_files(src_to_dest_map: Dict[str, str]) -> None:
|
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. '
|
' 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():
|
for s, d in src_to_dest_map.items():
|
||||||
copier.register(s, d)
|
copier.register(s, d)
|
||||||
with copier:
|
with copier:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user