闲来无事,在github上发现一个很有趣的project crudini,实现命令行对ini文件的增删改查和merge操作。起初会觉得至于如此小题大做么,但查阅之后,发现该项目对文件的操作比较精细,从文件锁FileLock、临时文件tempfile、SHA256 hashlib、退出执行atexit、shutil文件操作都使人眼前一亮。工程地址:https://github.com/joehakimrahme/crudini
contextlib上下文管理器
使用装饰器 contextlib.contextmanager() 将一个生成器函数转换为上下文管理器,在yield前,为前置操作,在__enter__中执行,yield为后置操作,在__exit__执行
举个例子:
import contextlib import os @contextlib.contextmanager def remove_file_on_error(path): try: print("befor yield...") yield print("after yield....") except Exception as e: if os.path.exists(path): os.unlink(path) with remove_file_on_error('caesar'): # some file operation print("d")
在contextlib库的GeneratorContextManager中定义被装饰的方法,在__enter__时,调用生成器的next方法(self.gen.next),所以会执行yield前面的操作。在yield返回None之后,__enter__执行完毕。
开始调用上下文的Body,即例子中的print("d")。调用完成后,继续调用GeneratorContextManager的__exit___方法(self.gen.next)执行yield的后面部分,完成后抛出StopIteration异常结束。
在contextlib库中 contextmanager 中对GeneratorContextManager进行调用,形成装饰器,可以很方便实现上下文管理,更重要的是在方法前、方法后、异常中进行操作。
在curlini中使用如下,对文件操作的上下文管理(创建临时文件、权限fchown修改,data写入、rename临时文件),如果出现异常,则删除临时文件
python中os操作
os.unlink(filepath) 删除filepath文件
f=os.fileno() 获取文件对象,返回一个int数字,叫做文件描述符
os.write(f, data) 向f 对象中写入data,os.flush()刷入文件, os.fsysn(f) 强制将文件描述符为f的文件刷入硬盘
sys.stdin.read() 从输入流中读取数据,特别适合shell中使用"<" 流入的情况
创建临时文件tempfile模块,在当前文件夹下创建一个"name.字符串.tmp" 的文件,文件为空
(f, tmp) = tempfile.mkstemp(".tmp", prefix=name + ".", dir=".")
文件锁
在linux系统/proc/locks 下:
第一列表示锁的类型,FLOCK和POSIX,第二列是建议性锁(不具备强制性。一个进程使用flock将文件锁住,另一个进程可以直接操作正在被锁的文件,修改文件中的数据,原因在于flock只是用于检测文件是否被加锁,针对文件已经被加锁,另一个进程写入数据的情况,内核不会阻止这个进程的写入操作,是建议性锁的内核处理策略。在这种情况,使用vim依然可以编辑文件,只对存在锁检查的进程进行阻塞。第四列表示使用这个锁的进程。
如下:进程2223获取锁在执行操作,进程2279在等待,直到2223释放锁。
在curlini中分别对windows和linux下的锁进行lock和unlock方法进行定义,如下:
import os class FileLock(object): """Advisory file based locking. This should be reasonably cross platform and also work over distributed file systems.""" def __init__(self, fno, exclusive=False): # fno is FileObject self.fp = fno self.locked = False if os.name == 'nt': import msvcrt def lock(self): msvcrt.locking(self.fp, msvcrt.LK_LOCK, 1) self.locked = True def unlock(self): if self.locked: msvcrt.locking(self.fp, msvcrt.LK_UNLCK, 1) self.locked = False else: import fcntl def lock(self): operation = fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH fcntl.lockf(self.fp, operation) self.locked = True def unlock(self): if self.locked: fcntl.lockf(self.fp, fcntl.LOCK_UN) self.locked = False FileLock.lock = lock FileLock.unlock = unlock
文件的SHA256
在文件加锁以后,获取文件的data,计算sha256值。在通过命令行操作ini,将命令行中的数据,添加进data数据,获取sha256值。如果值相同,则不进行操作。
def _chksum(self, data): h = hashlib.sha256() if sys.version_info[0] >= 3: h.update(bytearray(data, 'utf-8')) else: h.update(data) return h.digest()