|
13 | 13 | from mne.utils import _soft_import, warn |
14 | 14 |
|
15 | 15 |
|
16 | | -@contextlib.contextmanager |
17 | | -def _mne_open_lock(path, *args, **kwargs): |
18 | | - """ |
19 | | - Context manager that opens a file with an optional file lock. |
20 | | -
|
21 | | - If the `filelock` package is available, a lock is acquired on a lock file |
22 | | - based on the given path (by appending '.lock'). |
23 | | -
|
24 | | - Otherwise, a null context is used. The path is then opened in the |
25 | | - specified mode. |
26 | | -
|
27 | | - Parameters |
28 | | - ---------- |
29 | | - path : str |
30 | | - The path to the file to be opened. |
31 | | - *args, **kwargs : optional |
32 | | - Additional arguments and keyword arguments to be passed to the |
33 | | - `open` function. |
34 | | -
|
35 | | - """ |
| 16 | +def _get_lock_context(path): |
| 17 | + """Return a context manager that locks ``path`` if possible.""" |
36 | 18 | filelock = _soft_import( |
37 | 19 | "filelock", purpose="parallel config set and get", strict=False |
38 | 20 | ) |
39 | 21 |
|
40 | | - lock_context = contextlib.nullcontext() # default to no lock |
| 22 | + lock_context = contextlib.nullcontext() |
| 23 | + lock_path = Path(f"{os.fspath(path)}.lock") |
| 24 | + have_lock = False |
41 | 25 |
|
42 | 26 | if filelock: |
43 | | - lock_path = f"{path}.lock" |
44 | 27 | try: |
45 | 28 | lock_context = filelock.FileLock(lock_path, timeout=5) |
46 | 29 | lock_context.acquire() |
| 30 | + have_lock = True |
47 | 31 | except TimeoutError: |
48 | 32 | warn( |
49 | 33 | "Could not acquire lock file after 5 seconds, consider deleting it " |
50 | 34 | f"if you know the corresponding file is usable:\n{lock_path}" |
51 | 35 | ) |
52 | 36 | lock_context = contextlib.nullcontext() |
| 37 | + except OSError: |
| 38 | + warn( |
| 39 | + "Could not create lock file due to insufficient permissions. " |
| 40 | + "Proceeding without a lock." |
| 41 | + ) |
| 42 | + lock_context = contextlib.nullcontext() |
53 | 43 |
|
54 | | - with lock_context, open(path, *args, **kwargs) as fid: |
55 | | - yield fid |
| 44 | + return lock_context, lock_path, have_lock |
56 | 45 |
|
57 | 46 |
|
58 | 47 | @contextmanager |
59 | 48 | def _open_lock(path, *args, **kwargs): |
60 | 49 | """Wrap :func:`mne.utils.config._open_lock` and remove stale ``.lock`` files.""" |
61 | | - lock_path = Path(f"{os.fspath(path)}.lock") |
| 50 | + lock_context, lock_path, have_lock = _get_lock_context(path) |
62 | 51 | try: |
63 | | - with _mne_open_lock(path, *args, **kwargs) as fid: |
| 52 | + with lock_context, open(path, *args, **kwargs) as fid: |
64 | 53 | yield fid |
65 | 54 | finally: |
66 | | - if lock_path.exists(): |
| 55 | + if have_lock and lock_path.exists(): |
| 56 | + try: |
| 57 | + lock_path.unlink() |
| 58 | + except OSError: |
| 59 | + pass |
| 60 | + |
| 61 | + |
| 62 | +@contextmanager |
| 63 | +def _file_lock(path): |
| 64 | + """Acquire a lock on ``path`` without opening the file.""" |
| 65 | + lock_context, lock_path, have_lock = _get_lock_context(path) |
| 66 | + try: |
| 67 | + with lock_context: |
| 68 | + yield |
| 69 | + finally: |
| 70 | + if have_lock and lock_path.exists(): |
67 | 71 | try: |
68 | 72 | lock_path.unlink() |
69 | 73 | except OSError: |
|
0 commit comments