Skip to content

ENH: replace portalocker with filelock #3025

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 11, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions nipype/algorithms/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -799,11 +799,11 @@ def _run_interface(self, runtime):
'(http://pandas.pydata.org/) to run.'), e)

try:
import lockfile as pl
from filelock import SoftFileLock
self._have_lock = True
except ImportError:
from warnings import warn
warn(('Python module lockfile was not found: AddCSVRow will not be'
warn(('Python module filelock was not found: AddCSVRow will not be'
' thread-safe in multi-processor execution'))

input_dict = {}
Expand All @@ -822,7 +822,7 @@ def _run_interface(self, runtime):
df = pd.DataFrame([input_dict])

if self._have_lock:
self._lock = pl.FileLock(self.inputs.in_file)
self._lock = SoftFileLock(self.inputs.in_file + '.lock')

# Acquire lock
self._lock.acquire()
Expand All @@ -837,13 +837,6 @@ def _run_interface(self, runtime):
if self._have_lock:
self._lock.release()

# Using nipype.external.portalocker this might be something like:
# with pl.Lock(self.inputs.in_file, timeout=1) as fh:
# if op.exists(fh):
# formerdf = pd.read_csv(fh, index_col=0)
# df = pd.concat([formerdf, df], ignore_index=True)
# df.to_csv(fh)

return runtime

def _list_outputs(self):
Expand Down
23 changes: 5 additions & 18 deletions nipype/external/cloghandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@
testing, performance was more than adequate, but if you need a high-volume or
low-latency solution, I suggest you look elsewhere.

This module currently only support the 'nt' and 'posix' platforms due to the
usage of the portalocker module. I do not have access to any other platforms
for testing, patches are welcome.

See the README file for an example usage of this module.

"""
Expand All @@ -63,13 +59,7 @@
except ImportError:
codecs = None

# Question/TODO: Should we have a fallback mode if we can't load portalocker /
# we should still be better off than with the standard RotattingFileHandler
# class, right? We do some rename checking... that should prevent some file
# clobbering that the builtin class allows.

# sibling module than handles all the ugly platform-specific details of file locking
from .portalocker import lock, unlock, LOCK_EX, LOCK_NB, LockException
from filelock import SoftFileLock

# A client can set this to true to automatically convert relative paths to
# absolute paths (which will also hide the absolute path warnings)
Expand Down Expand Up @@ -168,11 +158,8 @@ def __init__(self,
self.maxBytes = maxBytes
self.backupCount = backupCount
# Prevent multiple extensions on the lock file (Only handles the normal "*.log" case.)
if filename.endswith(".log"):
lock_file = filename[:-4]
else:
lock_file = filename
self.stream_lock = open(lock_file + ".lock", "w")
self.lock_file = filename + '.lock'
self.stream_lock = SoftFileLock(self.lock_file)

# For debug mode, swap out the "_degrade()" method with a more a verbose one.
if debug:
Expand All @@ -189,7 +176,7 @@ def acquire(self):
in 'degraded' mode. """
# handle thread lock
Handler.acquire(self)
lock(self.stream_lock, LOCK_EX)
self.stream_lock.acquire()
if self.stream.closed:
self._openFile(self.mode)

Expand All @@ -206,7 +193,7 @@ def release(self):
self.stream.close()
finally:
try:
unlock(self.stream_lock)
self.stream_lock.release()
finally:
# release thread lock
Handler.release(self)
Expand Down
145 changes: 0 additions & 145 deletions nipype/external/portalocker.py

This file was deleted.

1 change: 1 addition & 0 deletions nipype/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ def get_nipype_gitversion():
'scipy>=%s,<%s ; python_version <= "3.4"' % (SCIPY_MIN_VERSION, SCIPY_MAX_VERSION_34),
'simplejson>=%s' % SIMPLEJSON_MIN_VERSION,
'traits>=%s,!=5.0' % TRAITS_MIN_VERSION,
'filelock>=3.0.0'
]

# neurdflib has to come after prov
Expand Down
22 changes: 11 additions & 11 deletions nipype/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from future import standard_library

from .misc import str2bool
from ..external import portalocker
from filelock import SoftFileLock

standard_library.install_aliases()

Expand Down Expand Up @@ -209,9 +209,9 @@ def get_data(self, key):
"""Read options file"""
if not os.path.exists(self.data_file):
return None
with open(self.data_file, 'rt') as file:
portalocker.lock(file, portalocker.LOCK_EX)
datadict = load(file)
with SoftFileLock(self.data_file + '.lock'):
with open(self.data_file, 'rt') as file:
datadict = load(file)
if key in datadict:
return datadict[key]
return None
Expand All @@ -220,17 +220,17 @@ def save_data(self, key, value):
"""Store config flie"""
datadict = {}
if os.path.exists(self.data_file):
with open(self.data_file, 'rt') as file:
portalocker.lock(file, portalocker.LOCK_EX)
datadict = load(file)
with SoftFileLock(self.data_file + '.lock'):
with open(self.data_file, 'rt') as file:
datadict = load(file)
else:
dirname = os.path.dirname(self.data_file)
if not os.path.exists(dirname):
mkdir_p(dirname)
with open(self.data_file, 'wt') as file:
portalocker.lock(file, portalocker.LOCK_EX)
datadict[key] = value
dump(datadict, file)
with SoftFileLock(self.data_file + '.lock'):
with open(self.data_file, 'wt') as file:
datadict[key] = value
dump(datadict, file)

def update_config(self, config_dict):
"""Extend internal dictionary with config_dict"""
Expand Down
Loading