yaml:Yet Another Markup Language的缩写。Yaml是专门用来写配置文件的语言,非常简洁和强大,远比json格式方便。
pip install PyYaml
pip install --ignore-installed PyYAML
大小写敏感
使用缩进表示层级关系
缩进时不允许使用Tab键,只允许使用空格。
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
使用#表示注释 字符串可以不用引号标注
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: 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 }
读取方法示例
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 data
test_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 data
a = get_more_than_one_yaml_for_one_file_1()
print(a)#生成了generator
for i in a:
print(i)
# 循环拿generator的值就会报错ValueError: I/O operation on closed file.,
# 我猜是因为with open操作完文件流自动关闭了
# 但是为什么是这样,没有找到原因,如果知道原因麻烦评论留言
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 os
def 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_PATH
import os
data_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 os
import yaml
from conftest import PROJECT_PATH
data_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 data
students_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 os
import yaml
from conftest import PROJECT_PATH
data_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: 0c818521d38759e1
shouji: '13456755448'
- appkey: 0c818521d38759e1
shouji: '13456755449'
- appkey: 0c818521d38759e1
shouji: '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 为15091757825
def 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))
运行结果
(1)内部引用
锚点&和引用*对于重复的数据,可以单独写到yaml文件的开头位置,其它的地方用到的可以用*引用
读取
import os
import pprint
import yaml
from conftest import PROJECT_PATH
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)
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 configparser
import yaml
from conftest import PROJECT_PATH
ini_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_file
self.yaml_file = yaml_file
def get_ini_data(self):
con = configparser.ConfigParser()
con.read(self.ini_file, encoding="utf8")
return con
def get_yaml_data(self):
with open(self.yaml_file, encoding="utf8") as f:
data = yaml.safe_load(f)
return data
my_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 requests
from util.read_data import get_ini_data,get_yaml_data
import pytest
url = 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==200
assert 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"
执行结果