操作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"

执行结果