操作yaml文件
1.yaml介绍
yaml:Yet Another Markup Language的缩写。Yaml是专门用来写配置文件的语言,非常简洁和强大,远比json格式方便。
Python搭建yaml环境
pip install PyYaml
pip install –ignore-installed PyYAML
yaml的语法规则
大小写敏感
使用缩进表示层级关系
缩进时不允许使用Tab键,只允许使用空格。
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
使用#表示注释 字符串可以不用引号标注
yaml的样式
1. 对象:键值对的集合(字典形式)
键值对用冒号”:” 间隔,冒号之间需要用空格分隔
如:
phone: 151xxxxxxxx addr: xx路xx号
得到结果:
{“phone”:”151xxxxxxxx”,”addr”:”xx路xx号”}
2. 数组:列表的形式
数组前需要有短横杠”-”符号,符号与值之间需要用空格分隔
如:
– value1 – value2
得到结果:
[“value1”,”value2”]
3. 特殊类型
字符串默认不使用引号表示。但是字符串之中包含空格或特殊字符,需要放在引号之中
str: ‘内容: 字符串’
None值可用null 或 ~ 符号表示
单引号和双引号都可以使用,双引号不会对特殊字符转义。
s1: ‘内容\n字符串’ s2: “内容\n字符串”
其他内容可参考:https://www.ruanyifeng.com/blog/2016/07/yaml.html?f=tt
4.多个yaml在一个文档中,使用—分割
yaml文件示例
---student:- name: lucy,age: 18class: 19grade: { English: 98,Math: 50,Art: 33 }- name: momo,age: 33class: 20grade: { English: 55,Math: 100,Art: 98 }---reqesttdata:- { shouji: 13456755448,appkey: 0c818521d38759e1 }- { shouji: 13456755449,appkey: 0c818521d38759e1 }- { shouji: 13456755450,appkey: 0c818521d38759e1 }
读取方法示例
def get_more_than_one_yaml_for_one_file():f = open(file=data_file_path1,mode="r")data = yaml.safe_load_all(f)#读取一个yaml文件中多个文档需要使用yaml.load_all())return datatest_data = get_more_than_one_yaml_for_one_file()for i in test_data:print(i)"""结果是:{'student': [{'name': 'lucy,', 'age': 18, 'class': 19, 'grade': {'English': 98, 'Math': 50, 'Art': 33}}, {'name': 'momo,', 'age': 33, 'class': 20, 'grade': {'English': 55, 'Math': 100, 'Art': 98}}]}{'reqesttdata': [{'shouji': 13456755448, 'appkey': '0c818521d38759e1'}, {'shouji': 13456755449, 'appkey': '0c818521d38759e1'}, {'shouji': 13456755450, 'appkey': '0c818521d38759e1'}]}"""print(list(get_more_than_one_yaml_for_one_file())) #通过将generator转换为列表来操作列表"""结果是[{'student': [{'name': 'lucy,', 'age': 18, 'class': 19, 'grade': {'English': 98, 'Math': 50, 'Art': 33}}, {'name': 'momo,', 'age': 33, 'class': 20, 'grade': {'English': 55, 'Math': 100, 'Art': 98}}]}, {'reqesttdata': [{'shouji': 13456755448, 'appkey': '0c818521d38759e1'}, {'shouji': 13456755449, 'appkey': '0c818521d38759e1'}, {'shouji': 13456755450, 'appkey': '0c818521d38759e1'}]}]"""
此外有个注意点,如果使用with open去操作一个一个yaml文件中多个文档的时候,会报异常:ValueError: I/O operation on closed file.
比如
def get_more_than_one_yaml_for_one_file_1():with open(file=data_file_path1,mode='r') as f:data = yaml.safe_load_all(f)return dataa = get_more_than_one_yaml_for_one_file_1()print(a)#生成了generatorfor i in a:print(i)# 循环拿generator的值就会报错ValueError: I/O operation on closed file.,# 我猜是因为with open操作完文件流自动关闭了# 但是为什么是这样,没有找到原因,如果知道原因麻烦评论留言
2.读取yaml文件
step1:导入yaml包
import yaml
step2:打开目标文件,读取内容(以字典内容嵌套的格式为例)
打开目标文件的时候,要找yaml文件所在目录
yaml的内容
student: - name: lucy, age: 18 class: 19 grade: { English: 98,Math: 50,Art: 33 } - name: momo, age: 33 class: 20 grade: { English: 55,Math: 100,Art: 98 }
1、为了全局使用考虑,一般在项目的根目录下面,写一个方法,获取跟目录的路径
conftest.py 在项目的根目录下
import osdef get_project_path():return os.path.dirname(os.path.abspath(__file__))PROJECT_PATH = get_project_path()print(PROJECT_PATH)
结果是:
2. 然后在读取的时候,先导入这个PROJECT_PATH,拼接yaml文件所在的路径
from conftest import PROJECT_PATHimport osdata_file_path = PROJECT_PATH + "\data\student.yaml"data_file_path1 = os.path.join(PROJECT_PATH,"data","student.yaml")print("data_file_path是: ",data_file_path)print("data_file_path1是:",data_file_path1)# data_file_path 和data_file_path1 是同样的结果
结果是
3.读取yaml的内容
import osimport yamlfrom conftest import PROJECT_PATHdata_file_path1 = os.path.join(PROJECT_PATH,"data","student.yaml")def get_yaml_data():with open(file=data_file_path1, mode='r') as f:data = yaml.safe_load(f)return datastudents_info = get_yaml_data()print(students_info)
结果是
{'student': [{'name': 'lucy,', 'age': 18, 'class': 19, 'grade': {'English': 98, 'Math': 50, 'Art': 33}},{'name': 'momo,', 'age': 33, 'class': 20, 'grade': {'English': 55, 'Math': 100, 'Art': 98}}]}
如果要获取student列表,可通过key去拿值,然后获取相关的内容
students_info["student"]
3.yaml文件的写入
写入是使用yaml.dump()来完成的,要传入两个参数,一个是写入的内容,一个是文件流
import osimport yamlfrom conftest import PROJECT_PATHdata_file_path2 = os.path.join(PROJECT_PATH, "data", "student1.yaml")data = {'reqesttdata': [{'shouji': 13456755448, 'appkey': '0c818521d38759e1'},{'shouji': 13456755449, 'appkey': '0c818521d38759e1'},{'shouji': 13456755450, 'appkey': '0c818521d38759e1'}]}def write_yaml(data,file_path):with open(file=file_path,mode='w',encoding='utf8') as f:yaml.dump(data,f)def read_yaml(data,file_path):write_yaml(data,file_path)f = open(file=file_path, mode='r', encoding='utf8')return yaml.safe_load(f)print(read_yaml(data,data_file_path2))
运行结果是:成功写入
注意“w”和“a”的区别
with open(yaml_path, "w", encoding="utf-8") 这里w是将文件数据清除后重新写入with open(yaml_path, "a", encoding="utf-8") 这里a是追加写入
现在数据已经可以正常写入了,如果想要修改数据怎么办呢?也很简单,大体思路就是读取–>修改–>重写
yaml文件内容
reqesttdata:- appkey: 0c818521d38759e1shouji: '13456755448'- appkey: 0c818521d38759e1shouji: '13456755449'- appkey: 0c818521d38759e1shouji: '13456755450'
data_file_path2 = os.path.join(PROJECT_PATH, "data", "student2.yaml")data = {'reqesttdata': [{'shouji': "13456755448", 'appkey': '0c818521d38759e1'},{'shouji': "13456755449", 'appkey': '0c818521d38759e1'},{'shouji': "13456755450", 'appkey': '0c818521d38759e1'}]}def write_yaml(data, file_path):"""写入yaml文件,需要写入的内容和写入的路径:param data::param file_path::return:"""with open(file=file_path, mode='w', encoding='utf8') as f:yaml.dump(data, f)def read_yaml(file_path):"""读取yaml文件,需要读取的路径参数file_path:param file_path::return:"""f = open(file=file_path, mode='r', encoding='utf8')return yaml.safe_load(f)# 修改shouji=13456755448 为15091757825def update(k, old_v, new_v):"""修改yaml中列表嵌套字典的值,通过键值对去修改:param k::param old_v::param new_v::return:"""yam_data = read_yaml(data_file_path2)#先读取内容#yaml文件是字典嵌套列表,所以要通过 yam_data["reqesttdata"]拿到里面的字典列表new_data_list = []for item in yam_data["reqesttdata"]:for i in item:# 然后判断对应的键值对是否存在{'shouji': '13456755448'}if i==k and item[i]==old_v:item[i]=new_v# 如果存在修改值,放到修的列表中new_data_list.append(item)#把新的列表再赋值给字典yam_data["reqesttdata"]yam_data["reqesttdata"]=new_data_list#调用写入yaml的方法重新写入文件write_yaml(yam_data,data_file_path2)#先写入才能读取write_yaml(data,data_file_path2)update("shouji", "13456755448", "15091757825")print(read_yaml(data_file_path2))
运行结果
4.yaml文件的引用
(1)内部引用
锚点&和引用*对于重复的数据,可以单独写到yaml文件的开头位置,其它的地方用到的可以用*引用
读取
import osimport pprintimport yamlfrom conftest import PROJECT_PATHdef read_yaml(file_path):"""读取yaml文件,需要读取的路径参数file_path:param file_path::return:"""f = open(file=file_path, mode='r', encoding='utf8')return yaml.safe_load(f)data_file_path3 = os.path.join(PROJECT_PATH, "data", "test_yaml_refer.yaml")yamldata = read_yaml(data_file_path3)#pprint用于美化打印的字典pprint.pprint(yamldata)
读取结果
(2)外部引用
-未理解,需要找时间进一步学习
使用关键字:!include
4.yaml的应用场景
自动化测试框架的配置文件或用例文件
用例文件和pytest结合使用示例:
import configparserimport yamlfrom conftest import PROJECT_PATHini_file = PROJECT_PATH + "\config\setting.ini"yaml_file = PROJECT_PATH + "\data\yaml_test_data.yaml"class GetData:# ini_file = PROJECT_PATH + "\config\setting.ini"# yaml_file = PROJECT_PATH + "\data\yaml_test_data.yaml"def __init__(self):self.ini_file = ini_fileself.yaml_file = yaml_filedef get_ini_data(self):con = configparser.ConfigParser()con.read(self.ini_file, encoding="utf8")return condef get_yaml_data(self):with open(self.yaml_file, encoding="utf8") as f:data = yaml.safe_load(f)return datamy_data = GetData()get_ini_data = my_data.get_ini_data()get_yaml_data = my_data.get_yaml_data()
setting.ini
[host] api_sit_url=https://api.binstd.com
yaml_test_data.yaml
reqesttdata: - {shouji: 13456755448,appkey: 0c818521d38759e1} - {shouji: 13456755449,appkey: 0c818521d38759e1} - {shouji: 13456755450,appkey: 0c818521d38759e1}
testcase代码
import requestsfrom util.read_data import get_ini_data,get_yaml_dataimport pytesturl = get_ini_data["host"]["api_sit_url"]request_data = get_yaml_data["reqesttdata"]@pytest.mark.parametrize("testdata",request_data)def test_mobie(testdata):res = requests.get(url=url+"/shouji/query",params=testdata)assert res.status_code==200assert res.json()["result"]["shouji"]=="13456755448"assert res.json()["result"]["province"]=="浙江"assert res.json()["result"]["city"]=="杭州"assert res.json()["result"]["company"]=="中国移动"assert res.json()["result"]["areacode"]=="0571"
执行结果