FileLock wrapper drops timeout and every other constructor argument
Describe the bug
datasets.utils._filelock.FileLock silently discards every keyword argument except lock_file. Passing timeout= has no effect, and is_singleton=True raises instead of returning the cached lock.
The wrapper declares a variadic signature:
# src/datasets/utils/_filelock.py
def __init__(self, lock_file, *args, **kwargs):
filelock inspects that signature to decide what to forward. From filelock/_api.py, FileLockMeta.__call__:
all_params = {"timeout": timeout, "mode": mode, "thread_local": thread_local,
"blocking": blocking, "is_singleton": is_singleton,
"poll_interval": poll_interval, "lifetime": lifetime, **kwargs}
present_params = inspect.signature(cls.__init__).parameters
init_params = {key: value for key, value in all_params.items() if key in present_params}
instance = super().__call__(lock_file, **init_params)
*args/**kwargs expose none of those names, so init_params is always empty and all seven are dropped before __init__ runs. Affected: timeout, mode, thread_local, blocking, is_singleton, poll_interval, lifetime.
The is_singleton failure is a knock-on effect. Our __init__ injects a umask-derived mode into the call it makes to super().__init__, so the cached instance ends up with mode=0o640, while the singleton check compares against the _UNSET_FILE_MODE sentinel (-1) that the caller never set. The parameters "differ" and the second construction raises.
Steps to reproduce the bug
import tempfile, os, filelock
from datasets.utils._filelock import FileLock
d = tempfile.mkdtemp()
print(FileLock(os.path.join(d, "a.lock"), timeout=5).timeout) # -1
print(filelock.FileLock(os.path.join(d, "b.lock"), timeout=5).timeout) # 5
p = os.path.join(d, "c.lock")
first = FileLock(p, is_singleton=True)
second = FileLock(p, is_singleton=True) # ValueError
datasets 5.0.2.dev0, filelock 3.25.2
datasets FileLock timeout: -1
filelock FileLock timeout: 5
Traceback (most recent call last):
File "<stdin>", line 9, in <module>
File ".../filelock/_api.py", line 150, in __call__
raise ValueError(msg)
ValueError: Singleton lock instances cannot be initialized with differing arguments
Non-matching arguments:
mode (existing lock has 416 but -1 was passed)
Hold a reference to the first lock to see the is_singleton error: _instances is a WeakValueDictionary, so an unreferenced first lock is collected and the second call quietly succeeds.
Expected behavior
Arguments accepted by filelock.FileLock should reach it, and is_singleton=True should return the cached instance.
Impact
Nothing in datasets passes these arguments today, so internal behaviour is unaffected and there is no cache-correctness problem. It matters for anyone constructing this lock directly, where a timeout= that looks set is not, and a lock that should time out blocks indefinitely instead.
A fix means naming the parameters on __init__ so the signature inspection finds them, keeping the umask default expressed as filelock's _UNSET_FILE_MODE sentinel rather than a concrete mode, so the singleton comparison still matches. Happy to open a PR if that approach sounds right.
Environment info
datasets5.0.2.dev0 (main, commit 3e2c1a6c3); reproduced identically on the released code path since the variadic signature arrived in #6445, first released in 2.16.0filelock3.25.2- Python 3.11.5, Linux
Source: huggingface/datasets