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:
pukkandan 2022-04-05 23:08:18 +05:30
parent ce0593ef61
commit 0edb3e336c
No known key found for this signature in database
GPG key ID: 7EEE9E1E817D0A39
2 changed files with 52 additions and 45 deletions

View file

@ -11,6 +11,7 @@ from ..utils import (
encodeFilename,
error_to_compat_str,
format_bytes,
LockingUnsupportedError,
sanitize_open,
shell_quote,
timeconvert,
@ -234,7 +235,10 @@ class FileDownloader(object):
@wrap_file_access('open', fatal=True)
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')
def try_remove(self, filename):

View file

@ -674,26 +674,25 @@ def sanitize_open(filename, open_mode):
It returns the tuple (stream, definitive_file_name).
"""
try:
if filename == '-':
if sys.platform == 'win32':
import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
return (sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout, filename)
stream = locked_file(filename, open_mode, block=False).open()
for attempt in range(2):
try:
try:
stream = locked_file(filename, open_mode, block=False).__enter__()
except LockingUnsupportedError:
stream = open(filename, open_mode)
return (stream, filename)
except (IOError, OSError) as err:
if err.errno in (errno.EACCES,):
if attempt or err.errno in (errno.EACCES,):
raise
# In case of error, try to remove win32 forbidden chars
alt_filename = sanitize_path(filename)
if alt_filename == filename:
old_filename, filename = filename, sanitize_path(filename)
if old_filename == filename:
raise
else:
# An exception here should be caught in the caller
stream = locked_file(filename, open_mode, block=False).open()
return (stream, alt_filename)
def timeconvert(timestr):
@ -2120,6 +2119,13 @@ def intlist_to_bytes(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
if sys.platform == 'win32':
import ctypes.wintypes
@ -2200,21 +2206,20 @@ else:
fcntl.lockf(f, fcntl.LOCK_UN)
except ImportError:
UNSUPPORTED_MSG = 'file locking is not supported on this platform'
def _lock_file(f, exclusive, block):
raise IOError(UNSUPPORTED_MSG)
raise LockingUnsupportedError()
def _unlock_file(f):
raise IOError(UNSUPPORTED_MSG)
raise LockingUnsupportedError()
class locked_file(object):
_closed = False
locked = False
def __init__(self, filename, mode, block=True, encoding=None):
assert mode in ['r', 'rb', 'a', 'ab', 'w', 'wb']
self.f = io.open(filename, mode, encoding=encoding)
assert mode in {'r', 'rb', 'a', 'ab', 'w', 'wb'}
self.f = open(filename, mode, encoding=encoding)
self.mode = mode
self.block = block
@ -2222,37 +2227,35 @@ class locked_file(object):
exclusive = 'r' not in self.mode
try:
_lock_file(self.f, exclusive, self.block)
self.locked = True
except IOError:
self.f.close()
raise
return self
def __exit__(self, etype, value, traceback):
def unlock(self):
if not self.locked:
return
try:
if not self._closed:
_unlock_file(self.f)
finally:
self.locked = False
def __exit__(self, *_):
try:
self.unlock()
finally:
self.f.close()
self._closed = True
open = __enter__
close = __exit__
def __getattr__(self, attr):
return getattr(self.f, attr)
def __iter__(self):
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():
encoding = sys.getfilesystemencoding()