diff --git a/src/calibre/ebooks/oeb/polish/container.py b/src/calibre/ebooks/oeb/polish/container.py index 3f5ff12994..36a03914d2 100644 --- a/src/calibre/ebooks/oeb/polish/container.py +++ b/src/calibre/ebooks/oeb/polish/container.py @@ -10,15 +10,13 @@ import os import re import shutil import sys -import time import unicodedata import uuid from collections import defaultdict +from css_parser import getUrls, replaceUrls from io import BytesIO from itertools import count -from css_parser import getUrls, replaceUrls - from calibre import CurrentDir, walk from calibre.constants import iswindows from calibre.customize.ui import plugin_for_input_format, plugin_for_output_format @@ -48,7 +46,7 @@ from calibre.ebooks.oeb.polish.utils import ( CommentFinder, PositionFinder, guess_type, parse_css ) from calibre.ptempfile import PersistentTemporaryDirectory, PersistentTemporaryFile -from calibre.utils.filenames import hardlink_file, nlinks_file +from calibre.utils.filenames import hardlink_file, nlinks_file, retry_on_fail from calibre.utils.ipc.simple_worker import WorkerError, fork_job from calibre.utils.logging import default_log from calibre.utils.xml_parse import safe_xml_fromstring @@ -1057,12 +1055,9 @@ class Container(ContainerBase): # {{{ # Decouple this file from its links temp = path + 'xxx' shutil.copyfile(path, temp) - try: - os.unlink(path) - except EnvironmentError: - if not iswindows: - raise - time.sleep(1) # Wait for whatever has locked the file to release it + if iswindows: + retry_on_fail(os.unlink, path) + else: os.unlink(path) os.rename(temp, path) return path diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index e8a75b646f..acf13fe880 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -466,21 +466,25 @@ if iswindows: move_file(a, b) +def retry_on_fail(func, *args, count=10, sleep_time=0.2): + for i in range(count): + try: + func(*args) + break + except OSError: + if i > count - 2: + raise + # Try the operation repeatedly in case something like a virus + # scanner has opened one of the files (I love windows) + time.sleep(sleep_time) + + def atomic_rename(oldpath, newpath): '''Replace the file newpath with the file oldpath. Can fail if the files are on different volumes. If succeeds, guaranteed to be atomic. newpath may or may not exist. If it exists, it is replaced. ''' if iswindows: - for i in range(10): - try: - rename_file(oldpath, newpath) - break - except Exception: - if i > 8: - raise - # Try the rename repeatedly in case something like a virus - # scanner has opened one of the files (I love windows) - time.sleep(1) + retry_on_fail(rename_file, oldpath, newpath) else: os.rename(oldpath, newpath)