Skip to content

Commit 6842fd7

Browse files
talagayevorbeckstyuxuanzhuang
authored
Transfer of fasteners dependency to filelock (#4800)
- change fasteners to filelock for preventing race condition when generating offsets - use mock in tests to emulate PermissionError when creating filelock --------- Co-authored-by: Oliver Beckstein <[email protected]> Co-authored-by: Yuxuan Zhuang <[email protected]>
1 parent bdfb2c9 commit 6842fd7

File tree

8 files changed

+38
-38
lines changed

8 files changed

+38
-38
lines changed

.github/actions/setup-deps/action.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ inputs:
2121
default: 'codecov'
2222
cython:
2323
default: 'cython'
24-
fasteners:
25-
default: 'fasteners'
24+
filelock:
25+
default: 'filelock'
2626
griddataformats:
2727
default: 'griddataformats'
2828
gsd:
@@ -110,7 +110,7 @@ runs:
110110
CONDA_MIN_DEPS: |
111111
${{ inputs.codecov }}
112112
${{ inputs.cython }}
113-
${{ inputs.fasteners }}
113+
${{ inputs.filelock }}
114114
${{ inputs.griddataformats }}
115115
${{ inputs.hypothesis }}
116116
${{ inputs.matplotlib }}

azure-pipelines.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ jobs:
9090
scikit-learn
9191
tqdm
9292
threadpoolctl
93-
fasteners
93+
filelock
9494
displayName: 'Install dependencies'
9595
# for wheel install testing, we pin to an
9696
# older NumPy, the oldest version we support and that

maintainer/conda/environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ dependencies:
77
- codecov
88
- cython
99
- docutils
10-
- fasteners
10+
- filelock
1111
- griddataformats
1212
- gsd
1313
- h5py>=2.10

package/CHANGELOG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Enhancements
3636

3737

3838
Changes
39+
* Changed `fasteners` dependency to `filelock` (Issue #4797, PR #4800)
3940
* Codebase is now formatted with black (version `24`) (PR #4886)
4041

4142
Deprecations

package/MDAnalysis/coordinates/XDR.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import numpy as np
3939
from os.path import getctime, getsize, isfile, split, join
4040
import warnings
41-
import fasteners
41+
from filelock import FileLock
4242

4343
from . import base
4444
from ..lib.mdamath import triclinic_box
@@ -121,6 +121,8 @@ class XDRBaseReader(base.ReaderBase):
121121
Add a InterProcessLock when generating offsets
122122
.. versionchanged:: 2.4.0
123123
Use a direct read into ts attributes
124+
.. versionchanged:: 2.9.0
125+
Changed fasteners.InterProcessLock() to filelock.FileLock
124126
"""
125127
@store_init_arguments
126128
def __init__(self, filename, convert_units=True, sub=None,
@@ -195,18 +197,18 @@ def _load_offsets(self):
195197

196198
# check if the location of the lock is writable.
197199
try:
198-
with fasteners.InterProcessLock(lock_name) as filelock:
200+
with FileLock(lock_name) as filelock:
199201
pass
200202
except OSError as e:
201203
if isinstance(e, PermissionError) or e.errno == errno.EROFS:
202204
warnings.warn(f"Cannot write lock/offset file in same location as "
203205
f"{self.filename}. Using slow offset calculation.")
204-
self._read_offsets(store=True)
206+
self._read_offsets(store=False)
205207
return
206208
else:
207209
raise
208210

209-
with fasteners.InterProcessLock(lock_name) as filelock:
211+
with FileLock(lock_name) as filelock:
210212
if not isfile(fname):
211213
self._read_offsets(store=True)
212214
return

package/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ dependencies = [
3838
'tqdm>=4.43.0',
3939
'threadpoolctl',
4040
'packaging',
41-
'fasteners',
41+
'filelock',
4242
'mda-xdrlib',
4343
'waterdynamics',
4444
'pathsimanalysis',

package/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
biopython>=1.80
22
codecov
33
cython
4-
fasteners
4+
filelock
55
griddataformats
66
gsd
77
hypothesis

testsuite/MDAnalysisTests/coordinates/test_xdr.py

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525

2626
import re
2727
import os
28+
import sys
2829
import shutil
2930
import subprocess
31+
import time
3032
from pathlib import Path
3133

3234
import numpy as np
@@ -60,6 +62,7 @@
6062
from MDAnalysis.coordinates.base import Timestep
6163
from MDAnalysis.coordinates import XDR
6264
from MDAnalysisTests.util import get_userid
65+
from filelock import FileLock
6366

6467

6568
@pytest.mark.parametrize(
@@ -977,45 +980,39 @@ def test_unsupported_format(self, traj):
977980
reader = self._reader(traj)
978981
reader[idx_frame]
979982

980-
@pytest.mark.skipif(get_userid() == 0, reason="cannot readonly as root")
981-
def test_persistent_offsets_readonly(self, tmpdir):
983+
def test_persistent_offsets_readonly(self, tmpdir, trajectory):
982984
shutil.copy(self.filename, str(tmpdir))
983985

984-
if os.name == "nt":
985-
# Windows platform has a unique way to deny write access
986-
subprocess.call(
987-
"icacls {fname} /deny Users:W".format(fname=tmpdir), shell=True
986+
filename = str(tmpdir.join(os.path.basename(self.filename)))
987+
print('filename', filename)
988+
ref_offset = trajectory._xdr.offsets
989+
# Mock filelock acquire to raise an error
990+
with patch.object(FileLock, "acquire", side_effect=PermissionError): # Simulate failure
991+
with pytest.warns(UserWarning, match="Cannot write lock"):
992+
reader = self._reader(filename)
993+
saved_offsets = reader._xdr.offsets
994+
995+
# Check if offsets are handled properly and match reference offsets
996+
assert_almost_equal(
997+
saved_offsets, # Compare with reference offsets
998+
ref_offset,
999+
err_msg="error loading frame offsets",
9881000
)
989-
else:
990-
os.chmod(str(tmpdir), 0o555)
9911001

992-
filename = str(tmpdir.join(os.path.basename(self.filename)))
993-
# try to write a offsets file
994-
with pytest.warns(
995-
UserWarning, match="Couldn't save offsets"
996-
) and pytest.warns(UserWarning, match="Cannot write"):
997-
self._reader(filename)
9981002
assert_equal(os.path.exists(XDR.offsets_filename(filename)), False)
9991003
# check the lock file is not created as well.
10001004
assert_equal(
10011005
os.path.exists(XDR.offsets_filename(filename, ending=".lock")),
10021006
False,
10031007
)
10041008

1005-
# pre-teardown permission fix - leaving permission blocked dir
1006-
# is problematic on py3.9 + Windows it seems. See issue
1007-
# [4123](https://github.com/MDAnalysis/mdanalysis/issues/4123)
1008-
# for more details.
1009-
if os.name == "nt":
1010-
subprocess.call(f"icacls {tmpdir} /grant Users:W", shell=True)
1011-
else:
1012-
os.chmod(str(tmpdir), 0o777)
1013-
1014-
shutil.rmtree(tmpdir)
1015-
1016-
def test_offset_lock_created(self):
1009+
@pytest.mark.skipif(
1010+
sys.platform.startswith("win"),
1011+
reason="The lock file only exists when it's locked in windows"
1012+
)
1013+
def test_offset_lock_created(self, traj):
10171014
assert os.path.exists(
1018-
XDR.offsets_filename(self.filename, ending="lock")
1015+
XDR.offsets_filename(traj, ending="lock")
10191016
)
10201017

10211018

0 commit comments

Comments
 (0)