ifdef ‘windows’ 的な話。IoT+WEBのためのホームサーバ計画の実装編22

せっかく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)
	#---------------------------------------

おしまい。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です