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, 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):

View file

@ -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()