ifdef ‘windows’ 的な話。IoT+WEBのためのホームサーバ計画の実装編22
2020-03-25 2020-04-10
せっかくPythonなのに
そうなんですよね。まぁしかたないことですが、
Pythonでファイルで排他処理しようとすると、、fcntlっていっても、、
windowsでは使えない
ってことになるわけですよね。
こればっかりは、どうしようも無いっすよねー。
まぁ問題はWindowsとLinuxとどっちをメインにするかって話でもあるわけで、
今回は、RaspberryPiとかCentOSの比率が高いので、Linuxをメインにします。
そうすると、Windowsの方はお茶を濁す方向性にして、それをクラスで作ってみます。
お茶を濁す程度のWindowsでのファイルの排他処理
そうですね。排他LOCK用のファイルを作るってのも、そのファイルの排他LOCKをどうするかって話にもなるので、『ファイルをリネームする』ってことにします。
もちろん、Windowsが主体の場合には、
Win32APIを叩くようなモジュールを作りますけどね(o^^o)
よって、ファイルのOpenに失敗するのは、
・ファイルのリネームに失敗した場合
・そもそもファイルが無い
のハズですが、ファイルが無い場合にも、リネームしたファイルがあるかどうかによっても違うわけですよね。
元のファイルもリネームしたファイルも無ければ、いくら待っても無理なわけなので。
よって、
glob.glob(fname + ".????????-????-????-????-????????????")
みたいなことやって、リネームファイルの存在確認します。
リネームするファイル名は、【元のファイル名】+【.】+【GUID】みたいな感じにします。
で、できたのがこれ。
import os
import time
import uuid
import sys
import glob
#-----------------------------------------
if os.name != 'nt':
import fcntl
#-----------------------------------------
class cryticalFile:
#---------------------------------------
def __init__(self):
self.f = None
self.fname = ""
self.fname2 = ""
self.mode = ""
self.maxloop = 50
#---------------------------------------
def __enter__(self):
#print("前処理")
return self
#--------------------------
def __exit__(self, exc_type, exc_value, traceback):
#print("後処理")
a = 0
#--------------------------
def __del__(self):
if self.f != None:
self.f.close()
if os.path.isfile(self.fname2):
self.rrrename(self.fname2,self.fname)
self.f = None
self.fname = ""
self.fname2 = ""
#--------------------------
def clear2(self,fname):
a = glob.glob(fname + ".????????-????-????-????-????????????")
n = len(a)
for w in range(n):
os.remove(a[w])
#--------------------------
def isLinux(self):
return os.name != 'nt'
def isWindows(self):
return os.name == 'nt'
#--------------------------
def ooopen(self,fname,mode="rb"):
try:
f = open(fname,mode)
except Exception as e:
f = False
return f
#--------------------------
def rrrename(self,f1,f2):
try:
os.rename(f1,f2)
return True
except Exception as e:
return False
#--------------------------
def open(self,fname,mode="rb"):
if self.isLinux():
return self.openLinux(fname,mode)
else:
return self.openWin(fname,mode)
#--------------------------
def openLinux(self,fname,mode="rb"):
if ("w" in mode) or ("a" in mode):
if not os.path.isfile(fname):
f = self.ooopen(fname,"x")
if f:
f.close()
f = self.ooopen(fname,mode)
ct = 0
#------------------------------
if f:
self.f = f
#--------------------------
while True:
try:
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
self.f = f
break;
except IOError:
ct = ct + 1
if ct > self.maxloop:
f.close()
return False
time.sleep(0.1)
#print("re_try")
continue
return self.f
#------------------------------
else:
return False
#------------------------------
#--------------------------
def openWin(self,fname,mode="rb"):
ct = 0
fname2 = ""
while True:
gid = str(uuid.uuid4()) #u4 = str(uuid.uuid4())
fname2 = fname + "." + gid
if os.path.isfile(fname):
self.rrrename(fname,fname2)
if os.path.isfile(fname2):
break
elif ("w" in mode) or ("a" in mode):
a = glob.glob(fname + ".????????-????-????-????-????????????")
if len(a) == 0:
fd = self.ooopen(fname,"x")
if fd:
fd.close()
continue
#print("fname:",fname)
ct = ct + 1
if ct > self.maxloop:
break
time.sleep(0.1)
#--------------------------------
if not os.path.isfile(fname2):
return False
#--------------------------------
self.f = self.ooopen(fname2, mode)
self.fname = fname
self.fname2 = fname2
return self.f;
#---------------------------------------
def close(self):
if self.f:
if self.isLinux():
fcntl.flock(self.f, fcntl.LOCK_UN)
self.f.close()
self.f = None
if os.path.isfile(fname2):
self.rrrename(self.fname2,self.fname)
#---------------------------------------
おしまい。