Do not prevent download if locking is unsupported
Closes #3022 Failure to lock download-archive is still fatal. This is consistent with youtube-dl's behavior
This commit is contained in:
parent
ce0593ef61
commit
0edb3e336c
2 changed files with 52 additions and 45 deletions
|
@ -11,6 +11,7 @@ from ..utils import (
|
||||||
encodeFilename,
|
encodeFilename,
|
||||||
error_to_compat_str,
|
error_to_compat_str,
|
||||||
format_bytes,
|
format_bytes,
|
||||||
|
LockingUnsupportedError,
|
||||||
sanitize_open,
|
sanitize_open,
|
||||||
shell_quote,
|
shell_quote,
|
||||||
timeconvert,
|
timeconvert,
|
||||||
|
@ -234,7 +235,10 @@ class FileDownloader(object):
|
||||||
|
|
||||||
@wrap_file_access('open', fatal=True)
|
@wrap_file_access('open', fatal=True)
|
||||||
def sanitize_open(self, filename, open_mode):
|
def sanitize_open(self, filename, open_mode):
|
||||||
return sanitize_open(filename, open_mode)
|
f, filename = sanitize_open(filename, open_mode)
|
||||||
|
if not getattr(f, 'locked', None):
|
||||||
|
self.write_debug(f'{LockingUnsupportedError.msg}. Proceeding without locking', only_once=True)
|
||||||
|
return f, filename
|
||||||
|
|
||||||
@wrap_file_access('remove')
|
@wrap_file_access('remove')
|
||||||
def try_remove(self, filename):
|
def try_remove(self, filename):
|
||||||
|
|
|
@ -674,26 +674,25 @@ def sanitize_open(filename, open_mode):
|
||||||
|
|
||||||
It returns the tuple (stream, definitive_file_name).
|
It returns the tuple (stream, definitive_file_name).
|
||||||
"""
|
"""
|
||||||
try:
|
if filename == '-':
|
||||||
if filename == '-':
|
if sys.platform == 'win32':
|
||||||
if sys.platform == 'win32':
|
import msvcrt
|
||||||
import msvcrt
|
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
||||||
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
return (sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout, filename)
|
||||||
return (sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout, filename)
|
|
||||||
stream = locked_file(filename, open_mode, block=False).open()
|
|
||||||
return (stream, filename)
|
|
||||||
except (IOError, OSError) as err:
|
|
||||||
if err.errno in (errno.EACCES,):
|
|
||||||
raise
|
|
||||||
|
|
||||||
# In case of error, try to remove win32 forbidden chars
|
for attempt in range(2):
|
||||||
alt_filename = sanitize_path(filename)
|
try:
|
||||||
if alt_filename == filename:
|
try:
|
||||||
raise
|
stream = locked_file(filename, open_mode, block=False).__enter__()
|
||||||
else:
|
except LockingUnsupportedError:
|
||||||
# An exception here should be caught in the caller
|
stream = open(filename, open_mode)
|
||||||
stream = locked_file(filename, open_mode, block=False).open()
|
return (stream, filename)
|
||||||
return (stream, alt_filename)
|
except (IOError, OSError) as err:
|
||||||
|
if attempt or err.errno in (errno.EACCES,):
|
||||||
|
raise
|
||||||
|
old_filename, filename = filename, sanitize_path(filename)
|
||||||
|
if old_filename == filename:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def timeconvert(timestr):
|
def timeconvert(timestr):
|
||||||
|
@ -2120,6 +2119,13 @@ def intlist_to_bytes(xs):
|
||||||
return compat_struct_pack('%dB' % len(xs), *xs)
|
return compat_struct_pack('%dB' % len(xs), *xs)
|
||||||
|
|
||||||
|
|
||||||
|
class LockingUnsupportedError(IOError):
|
||||||
|
msg = 'File locking is not supported on this platform'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(self.msg)
|
||||||
|
|
||||||
|
|
||||||
# Cross-platform file locking
|
# Cross-platform file locking
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
import ctypes.wintypes
|
import ctypes.wintypes
|
||||||
|
@ -2200,21 +2206,20 @@ else:
|
||||||
fcntl.lockf(f, fcntl.LOCK_UN)
|
fcntl.lockf(f, fcntl.LOCK_UN)
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
UNSUPPORTED_MSG = 'file locking is not supported on this platform'
|
|
||||||
|
|
||||||
def _lock_file(f, exclusive, block):
|
def _lock_file(f, exclusive, block):
|
||||||
raise IOError(UNSUPPORTED_MSG)
|
raise LockingUnsupportedError()
|
||||||
|
|
||||||
def _unlock_file(f):
|
def _unlock_file(f):
|
||||||
raise IOError(UNSUPPORTED_MSG)
|
raise LockingUnsupportedError()
|
||||||
|
|
||||||
|
|
||||||
class locked_file(object):
|
class locked_file(object):
|
||||||
_closed = False
|
locked = False
|
||||||
|
|
||||||
def __init__(self, filename, mode, block=True, encoding=None):
|
def __init__(self, filename, mode, block=True, encoding=None):
|
||||||
assert mode in ['r', 'rb', 'a', 'ab', 'w', 'wb']
|
assert mode in {'r', 'rb', 'a', 'ab', 'w', 'wb'}
|
||||||
self.f = io.open(filename, mode, encoding=encoding)
|
self.f = open(filename, mode, encoding=encoding)
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.block = block
|
self.block = block
|
||||||
|
|
||||||
|
@ -2222,37 +2227,35 @@ class locked_file(object):
|
||||||
exclusive = 'r' not in self.mode
|
exclusive = 'r' not in self.mode
|
||||||
try:
|
try:
|
||||||
_lock_file(self.f, exclusive, self.block)
|
_lock_file(self.f, exclusive, self.block)
|
||||||
|
self.locked = True
|
||||||
except IOError:
|
except IOError:
|
||||||
self.f.close()
|
self.f.close()
|
||||||
raise
|
raise
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, etype, value, traceback):
|
def unlock(self):
|
||||||
|
if not self.locked:
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
if not self._closed:
|
_unlock_file(self.f)
|
||||||
_unlock_file(self.f)
|
finally:
|
||||||
|
self.locked = False
|
||||||
|
|
||||||
|
def __exit__(self, *_):
|
||||||
|
try:
|
||||||
|
self.unlock()
|
||||||
finally:
|
finally:
|
||||||
self.f.close()
|
self.f.close()
|
||||||
self._closed = True
|
|
||||||
|
open = __enter__
|
||||||
|
close = __exit__
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
return getattr(self.f, attr)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.f)
|
return iter(self.f)
|
||||||
|
|
||||||
def write(self, *args):
|
|
||||||
return self.f.write(*args)
|
|
||||||
|
|
||||||
def read(self, *args):
|
|
||||||
return self.f.read(*args)
|
|
||||||
|
|
||||||
def flush(self):
|
|
||||||
self.f.flush()
|
|
||||||
|
|
||||||
def open(self):
|
|
||||||
return self.__enter__()
|
|
||||||
|
|
||||||
def close(self, *args):
|
|
||||||
self.__exit__(self, *args, value=False, traceback=False)
|
|
||||||
|
|
||||||
|
|
||||||
def get_filesystem_encoding():
|
def get_filesystem_encoding():
|
||||||
encoding = sys.getfilesystemencoding()
|
encoding = sys.getfilesystemencoding()
|
||||||
|
|
Loading…
Reference in a new issue