1
\$\begingroup\$

I have two python scripts (or one bash script + one python script) which are working independently from another, one script puts data into a folder, the other one reads from it.

Since I don't want to accidentally read from the folder at the same time it's updated, I created this simple folder lock using a dummy file.

In the bash script a similar thing is done, by checking whether that lock.file exists, and if not create it, put the current PID into it, and after it's done delete it.

import os
import sys
import time
import logging
LOGGER = logging.getLogger()
class SimpleFolderLock():
 def __init__(self, folder_path, time_out=5):
 self._folder_to_lock = folder_path
 self._lock_file_path = os.path.join(self._folder_to_lock, "folder.lock")
 self._lock_time_out = time_out
 
 def __enter__(self):
 start_time = time.time()
 while os.path.exists(self._lock_file_path):
 LOGGER.debug(f"Waiting for {self._lock_file_path} to clear")
 time.sleep(5)
 if (time.time() - start_time) > self._lock_time_out:
 LOGGER.error(
 f"Time-out for waiting for {self._lock_file_path} to clear")
 raise TimeoutError(f"Can't lock {self._folder_to_lock} after {self._lock_time_out} seconds")
 
 LOGGER.debug(f"Locking with {self._lock_file_path}")
 with open(self._lock_file_path, "w") as fn:
 fn.write(str(os.getpid()))
 
 return self._lock_file_path
 
 def __exit__(self, type, value, traceback):
 try:
 LOGGER.debug(f"Clearing {self._lock_file_path}")
 os.remove(self._lock_file_path)
 except FileNotFoundError:
 LOGGER.error(f"File {self._lock_file_path} already got deleted")

And for a simple test:

# Just some dummy logging configuration
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
test_folder = "/tmp/test_dir/"
os.makedirs(test_folder, exist_ok=True)
with SimpleFolderLock(test_folder) as lock:
 lock_path = lock
 LOGGER.info(f"Locked with {lock_path}")
 assert os.path.exists(lock_path)
 fiddle_around_in_folder(test_folder)
 
assert not os.path.exists(lock_path)
Heslacher
50.9k5 gold badges83 silver badges177 bronze badges
asked Mar 3, 2021 at 8:30
\$\endgroup\$
1
  • \$\begingroup\$ Welcome to Code Review. I have rolled back your last edit. Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers . \$\endgroup\$ Commented Mar 4, 2021 at 9:22

1 Answer 1

1
\$\begingroup\$

There is a race condition here:

while os.path.exists(self._lock_file_path):
...
with open(self._lock_file_path, "w") as fn:

The file could have been created by the other process in between. You need to check file existence and create the file in one operation.

Since python 3.3 there is a special open mode x for this use case:

open for exclusive creation, failing if the file already exists

For example:

while (time.time() - start_time) < self._lock_time_out:
 try:
 with open(self._lock_file_path, "x") as fn:
 fn.write(str(os.getpid()))
 except FileExistsError:
 time.sleep(5)
answered Mar 3, 2021 at 9:06
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.