一个并发安全的简单json文件的操作库。适合于完全独立的多个进程通过JSON文件的内容协调工作的场景。

一个并发安全的简单json文件的操作库。适合于完全独立的多个进程通过JSON文件的内容协调工作的场景。

json文件的内容对应一个简单的字典,字典元素的值是简单的数据类型,比如布尔值、数值、字符串,不能是字典、列表和集合等复合类型。

使用文件锁保证多进程并发操作安全。

要求读写json文件前,要先创建json文件。

支持日志记录,如果要记录日志,要先创建日志文件。

'''
author: Liu Yifan
一个并发安全的简单json文件的操作库。适合于完全独立的多个进程通过JSON文件的内容协调工作的场景。
json文件的内容对应一个简单的字典,字典元素的值是简单的数据类型,比如布尔值、数值、字符串,不能是字典、列表和集合等复合类型。
使用文件锁保证多进程并发操作安全。
要求读写json文件前,要先创建json文件。
支持日志记录,如果要记录日志,要先创建日志文件。
'''
import fcntl
import json
import datetime
from pathlib import Path

def create_file(file_path):
    '''
    在指定位置创建文件。如果父目录或者祖先目录不存在,则自动创建。如果文件已经存在,则更新修改时间。
    可以用来创建json或者log文件。
    '''
    fold_path = Path(file_path).parent
    # parents=True:如果父目录或者祖先目录不存在,则自动创建。exist_ok=True:忽略FileExistsError异常。
    Path(fold_path).mkdir(parents=True, exist_ok=True)
    # exist_ok=True:如果文件已经存在,则更新修改时间,而不是抛出FileExistsError异常。
    Path(file_path).touch(exist_ok=True)

def read_json_file_field(file,key):
    '''
    从json文件中读取一个字段的值,使用key来标识字段
    如果文件不存在,则抛出异常FileNotFoundError。
    如果key不存在,则抛出异常KeyError。
    '''
    with open(file,"r") as f:
        fcntl.flock(f, fcntl.LOCK_SH)
        return json.load(f)[key]

def read_json_file(file):
    '''
    读取整个json文件的内容,返回一个字典。
    如果文件不存在,则抛出异常FileNotFoundError。
    '''
    with open(file,"r") as f:
        fcntl.flock(f, fcntl.LOCK_SH)
        return json.load(f)

def write_log(file, log_file, operator_name, msg):
    '''
    记录log,方便回溯问题。
    operator_name是操作者的名字。
    如果json文件或者log文件不存在,则抛出异常FileNotFoundError。
    '''
    # 为了避免同时获取json_file和json_两把锁导致死锁的可能性,这里同一时间只获取一把锁
    json_file_content = read_json_file(file)
    with open(log_file,"a") as f:
        fcntl.flock(f, fcntl.LOCK_EX)
        f.write(f"{datetime.datetime.now()}: {operator_name} {msg}\n")
        f.write(f"    {json_file_content}\n")

def write_json_file(file, dict_, log_file=None, operator_name=None):
    '''
    将整个字典写入json_file,可以用于初始化操作,也可以用于json_file内容重置操作。
    如果json文件不存在,会尝试创建文件,但是如果所属文件夹不存在,则抛出异常FileNotFoundError。
    '''
    with open(file,"w") as f:
        fcntl.flock(f, fcntl.LOCK_EX)
        json.dump(dict_,f)
    if log_file:
        write_log(file, log_file, operator_name, f"Write dict {dict_} into json file {file}")

def write_json_file_field(file,key,value,log_file=None,operator_name=None):
    '''
    修改指定元素的值。
    如果key不存在则捎带添加了元素。
    如果json文件不存在,则抛出异常FileNotFoundError。
    '''
    with open(file,"r+") as f:
        fcntl.flock(f, fcntl.LOCK_EX)
        dict_ = json.load(f)
        dict_[key]=value
        # 因为执行了json.load()之后,指针移动到了文件末尾。
        # 这里需要重写文件的所有内容,所以先将指针移动文件头部,然后清除文件内容
        f.seek(0) 
        f.truncate() 
        json.dump(dict_,f)
    if log_file:
        write_log(file, log_file, operator_name, f"set {key} to {value}")

def incr_json_file_field(file, key, incr_number=1, log_file=None, operator_name=None):
    '''
    将指定元素的值加上指定的值,默认加一。如果指定的是负数,则实际效果是减小数值。
    如果key不存在,则首先添加元素,默认值为0。
    如果json文件不存在,则抛出异常FileNotFoundError。
    '''
    with open(file,"r+") as f:
        fcntl.flock(f, fcntl.LOCK_EX)
        dict_ = json.load(f)
        dict_.setdefault(key,0)
        dict_[key] += incr_number
        f.seek(0) 
        f.truncate() 
        json.dump(dict_,f)
    if log_file:
        write_log(file, log_file, operator_name, f"add one to {key}")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值