Как лучше открыть файл для эксклюзивного доступа в Python?
Описание проблемы для StackOverflow:
Каково наиболее элегантное решение следующей задачи:
- Открыть файл для чтения, но только если он не открыт для записи.
- Открыть файл для записи, но только если он не открыт ни для чтения, ни для записи.
Встроенные функции работают следующим образом:
>>> path = r"c:\scr.txt"
>>> file1 = open(path, "w")
>>> print(file1)
<open file 'c:\scr.txt', mode 'w' at 0x019F88D8>
>>> file2 = open(path, "w")
>>> print(file2)
<open file 'c:\scr.txt', mode 'w' at 0x02332188>
>>> file1.write("111")
>>> file2.write("222")
>>> file1.close()
Теперь файл scr.txt
содержит '111'.
>>> file2.close()
Файл scr.txt
был перезаписан и теперь содержит '222' (на Windows, Python 2.4).
Решение должно работать как внутри одного процесса (согласно приведенному выше примеру), так и когда другой процесс открыл файл. Также желательно, чтобы при аварийном завершении программы блокировка не оставалась открытой.
2 ответ(ов)
EDIT: Я сам решил проблему! Я использовал существование директории и её возраст как механизм блокировки! Блокировка файла безопасна только на Windows (поскольку в Linux происходит тихая перезапись), но блокировка по директории работает отлично как на Linux, так и на Windows. Я создал удобный класс 'lockbydir.DLock' для этой цели, который можно найти в моем репозитории:
https://github.com/drandreaskrueger/lockbydir
Внизу README вы найдете 3 GIT-плейера, где можно увидеть живые примеры кода в вашем браузере! Довольно круто, не так ли? 😃
Спасибо за внимание!
Это был мой исходный вопрос:
Я хотел бы ответить parity3 (https://meta.stackoverflow.com/users/1454536/parity3), но не могу комментировать напрямую ('Вам нужно 50 репутации, чтобы комментировать'), и не вижу никакого способа связаться с ним/ней напрямую. Что вы мне посоветуете, чтобы донести до него информацию?
Мой вопрос:
Я реализовал что-то похожее на то, что предложил parity3 здесь в ответе: https://stackoverflow.com/a/21444311/3693375 ("Предполагая, что ваш интерпретатор Python ...")
И это отлично работает на Windows. (Я использую это для реализации механизма блокировки, работающего в параллельно запущенных процессах. https://github.com/drandreaskrueger/lockbyfile)
Но, в отличие от того, что говорит parity3, это НЕ работает так же на Linux:
os.rename(src, dst)
Переименовать файл или директорию src в dst. ... В Unix, если dst существует и является файлом, он будет заменен без уведомления, если у пользователя есть на это разрешение. Операция может завершиться неудачей на некоторых Unix системах, если src и dst находятся на разных файловых системах. Если операция прошла успешно, переименование будет атомарной операцией (это требование POSIX). На Windows, если dst уже существует, будет вызвано исключение OSError (https://docs.python.org/2/library/os.html#os.rename)
Тихая перезапись – это проблема. На Linux.
"если dst уже существует, будет вызвано исключение OSError" – это прекрасно для моих целей, но, к сожалению, только для Windows.
Я полагаю, что пример parity3 все еще работает большую часть времени из-за его условия if
if not os.path.exists(lock_filename):
try:
os.rename(tmp_filename, lock_filename)
Но тогда это все больше не атомарно.
Поскольку условие if может быть истинно в двух параллельных процессах, и тогда оба могут выполнить переименование, но только один выиграет в гонке переименования. И никакого исключения не будет вызвано (в Linux).
Есть ли какие-нибудь предложения? Спасибо!
P.S.: Я знаю, что это не самый правильный способ, но у меня нет альтернативы. ПОЖАЛУЙСТА, не наказывайте меня снижением репутации. Я много искал, чтобы решить это сам. Как отправить личные сообщения пользователям здесь? И пфу почему я не могу?
Чтобы обеспечить безопасность при открытии файлов в одном приложении, вы можете попробовать использовать следующий код:
import time
class ExclusiveFile(file):
openFiles = {}
fileLocks = []
class FileNotExclusiveException(Exception):
pass
def __init__(self, *args):
sMode = 'r'
sFileName = args[0]
try:
sMode = args[1]
except:
pass
while sFileName in ExclusiveFile.fileLocks:
time.sleep(1)
ExclusiveFile.fileLocks.append(sFileName)
if not sFileName in ExclusiveFile.openFiles.keys() or (ExclusiveFile.openFiles[sFileName] == 'r' and sMode == 'r'):
ExclusiveFile.openFiles[sFileName] = sMode
try:
file.__init__(self, sFileName, sMode)
finally:
ExclusiveFile.fileLocks.remove(sFileName)
else:
ExclusiveFile.fileLocks.remove(sFileName)
raise self.FileNotExclusiveException(sFileName)
def close(self):
del ExclusiveFile.openFiles[self.name]
file.close(self)
С помощью этого кода вы создаете подкласс для класса file
. Теперь вы можете использовать его следующим образом:
>>> f = ExclusiveFile('/tmp/a.txt', 'r')
>>> f
<open file '/tmp/a.txt', mode 'r' at 0xb7d7cc8c>
>>> f1 = ExclusiveFile('/tmp/a.txt', 'r')
>>> f1
<open file '/tmp/a.txt', mode 'r' at 0xb7d7c814>
>>> f2 = ExclusiveFile('/tmp/a.txt', 'w') # нельзя открыть для записи сейчас
exclfile.FileNotExclusiveException: /tmp/a.txt
Если вы сначала откроете файл в режиме 'w', то дальнейшее открытие, даже в режиме чтения, не будет разрешено, так как и задумывалось.
Как читать файл построчно в список?
Как получить даты/времена создания и изменения файла?
Как удалить не пустую папку?
Как удалить содержимое папки?
Как вызвать функцию из другого .py файла?