Python 字典解析器
项目描述
字典解析
用于解析字典或类字典对象的简单、苗条且有用的零依赖实用程序。
它对于解析 REST API 和 Web 应用程序中的传入请求数据特别有用,例如在 Flask 的情况下,解析来自 的表单数据、来自request.form的查询字符串参数request.args或来自 的 JSON 数据
request.json。
该dictparse设计的灵感来自 Python 自己的argparse库,类似于ArgumentParserclass ,将输入作为字典或类字典对象,强制执行规则、类型、应用函数、默认值并返回 a NameSpace,并将值映射到属性。
安装
pip install dictparse
例子
以下代码是一个 Python 程序,它以字典的形式获取一些数据并对其进行解析:
>>> from dictparse import DictionaryParser
>>> parser = DictionaryParser()
>>> parser.add_param("name", str, required=True)
>>> params = parser.parse_dict({"name": "FooBar"})
>>> params.name
'FooBar'
创建解析器
第一步是创建 DictionaryParser 对象
>>> from dictparse import DictionaryParser
>>> parser = DictionaryParser(description="Create a new user")
添加参数
向解析器添加参数是通过调用add_param方法来完成的。这些调用告诉
DictionaryParser如何处理传入的值并将它们转换为所需的输出,执行规则,更改类型并根据传递给add_param方法的参数转换值。
>>> parser = DictionaryParser()
>>> parser.add_param("name", str, required=True)
>>> parser.add_param("language", str, choices=["python", "javascript", "rust"])
>>> parser.add_param("tags", str, action=lambda x: x.split(","))
>>> params = parser.parse_dict({"name": "FooBar", "language": "python", "tags": "foo,bar,baz"})
>>> params.name
'FooBar'
>>> params.language
'python'
>>> params.tags
['foo', 'bar', 'baz']
>>> params.to_dict()
{'name': 'FooBar', 'language': 'python', 'tags': ['foo', 'bar', 'baz']}
如果解析器没有找到与名称匹配的值,则默认值为None
可用于的参数add_param
DictionaryParser.add_param(
name: str,
type_: Optional[Union[Type[str], Type[int], Type[float], Type[bool], Type[list], Type[dict], Type[set], Type[tuple]]] = None,
dest: Optional[str] = None,
required: Optional[bool] = False,
choices: Optional[Union[list, set, tuple]] = None,
action: Optional[Callable] = None,
description: Optional[str] = None,
default: Optional[Any] = None,
regex: Optional[str] = None
) -> None
name: 参数名称(必填 - 请参阅下面的注释)type_: 常用参数类型(解析器会尝试将参数值转换为给定类型)dest:参数的目标名称(请参见下面的注释)required:如果True,强制参数的值必须存在choices: 可能选择的列表、集合或元组action: 应用于值的函数(在任何类型转换后应用)description: 参数说明default:如果没有找到参数的默认值regex:要匹配的正则表达式(None如果匹配为负,则将参数设置为)
注意 -
nameanddest参数必须符合标准 Python 变量命名约定(仅以字母或下划线开头 & 仅包含字母数字字符),不是 Python 关键字,也不能以双下划线 (dunder) 开头和结尾
解析数据
创建解析器并为其添加参数后,可以通过调用该parse_dict方法解析数据,传入要解析的数据。这将返回一个NameSpace对象。
DictionaryParser.parse_dict(
data: Dict[str, Any],
strict: Optional[bool] = False,
action: Optional[Callable] = None
) -> NameSpace:
data: 字典或类字典对象strict: IfTrue, 如果接收到任何未添加到解析器的参数,则引发异常action: 应用于所有参数的函数(在任何类型转换之后和传递给的操作之后add_param)
NameSpace对象_
调用时返回一个NameSpace对象,parse_dict并在应用添加参数时定义的规则后包含解析的数据。
参数可以作为NameSpaceusing dot notation 的属性访问:
>>> parser = DictionaryParser()
>>> parser.add_param("age", int, required=True)
>>> params = parser.parse_dict({"age": 30})
>>> params.age
30
AttributeError如果在未添加到解析器的情况下访问属性,则会提出标准:
>>> params.foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NameSpace' object has no attribute 'foo'
如果dest在添加参数时提供了参数add_param,则只能通过使用该
值来访问该dest值:
>>> parser = DictionaryParser()
>>> parser.add_param("bar", str, dest="foo")
>>> params = parser.parse_dict({"bar": "bar"})
>>> params.foo
'bar'
该NameSpace对象具有以下可用方法:
get
NameSpace.get(
name: str,
default: Optional[Any] = None
) -> Union[None, Any]:
调用 的get方法NameSpace并传入一个键的工作方式与调用get字典的方式相同,返回所请求参数的值或者None如果NameSpace没有该属性。
default如果属性不存在,则可以使用要返回的参数提供可选的默认值。
>>> parser = DictionaryParser()
>>> parser.add_param("age", int, required=True)
>>> parser.add_param("weight", int)
>>> params = parser.parse_dict({"age": 30, "height": 1.9})
>>> params.weight
None
>>> params.get("age")
30
>>> params.get("foo", 42)
42
to_dict
NameSpace.to_dict() -> dict
返回已解析参数的字典。
>>> parser = DictionaryParser()
>>> parser.add_param("one", str)
>>> parser.add_param("two", int)
>>> parser.add_param("three", list)
>>> params = parser.parse_dict({"one": "one", "two": 2, "three": [1, 2, 3]})
>>> params.to_dict()
{'one': 'one', 'two': 2, 'three': [1, 2, 3]}
to_dict()接受一个可选参数exclude,从返回的字典中排除的键列表
>>> from dictparse import DictionaryParser
>>> parser = DictionaryParser()
>>> parser.add_param("csrf_token", str, required=True)
>>> parser.add_param("name", str)
>>> parser.add_param("email", str)
>>> params = parser.parse_dict({"csrf_token": "xxyyzz112233", "name": "foo", "email": "foo@bar.com"})
>>> params.to_dict(exclude=["csrf_token"])
{'name': 'foo', 'email': 'foo@bar.com'}
get_param
返回一个Param对象
>>> from dictparse import DictionaryParser
>>> parser = DictionaryParser()
>>> parser.add_param("names", list, default=[])
>>> params = parser.parse_dict({"names": ["foo", "bar"]})
>>> names = params.get_param("names")
>>> names.name
'names'
>>> names.value
['foo', 'bar']
>>> names.default
[]
Param对象保存与参数关联的所有数据,如下面的Param.__init__方法所示:
class Param(object):
def __init__(
self,
name: str,
type_: Optional[Union[Type[str], Type[int], Type[float], Type[bool], Type[list], Type[dict], Type[set], Type[tuple]]] = None,
dest: Optional[str] = None,
required: Optional[bool] = False,
choices: Optional[Union[list, set, tuple]] = None,
action: Optional[Callable] = None,
description: Optional[str] = None,
default: Optional[Any] = None,
regex: Optional[str] = None,
value: Optional[Any] = None
):
注意 -
NameSpace将被赋值dest为add_param
>>> from dictparse import DictionaryParser
>>> parser = DictionaryParser()
>>> parser.add_param("foo", str, dest="bar")
>>> params = parser.parse_dict({"foo": 42})
>>> param = params.get_param("bar")
>>> param.name
'foo'
>>> param.dest
'bar'
>>> param.value
'42'
烧瓶示例
解析 POST 请求中发送到 Flask 路由的 JSON 数据的示例:
from app.users import create_user
from flask import Flask, request
from respond import JSONResponse
from dictparse import DictionaryParser
def create_app():
app = Flask(__name__)
@app.route("/", methods=["POST"])
def post():
parser = DictionaryParser(description="Create a new user")
parser.add_param("name", str, required=True)
parser.add_param("age", int)
parser.add_param("password", str, required=True, action=lambda x: x.encode("utf-8"))
parser.add_param("interests", list, action=lambda x: [i.strip() for i in x])
parser.add_param("level", float, default=1.5)
parser.add_param("stage", str, choices=["alpha", "beta"])
try:
params = parser.parse_dict(request.get_json())
except Exception as e:
return JSONResponse.bad_request(str(e))
user = create_user(
name=params.name,
age=params.age,
password=params.password,
interests=params.interests,
level=params.level,
stage=params.stage
)
return JSONResponse.created(user.to_dict())
return app
if __name__ == "__main__":
app = create_app()
app.run()
异常处理
在以下情况下将引发异常:
ParserTypeError
当参数无法解析为声明的类型时引发add_param
from dictparse import DictionaryParser
from dictparse.exceptions import ParserTypeError
parser = DictionaryParser()
parser.add_param("age", int)
try:
params = parser.parse_dict({"age": "thirty"})
except ParserTypeError as e:
print(e) # Invalid value 'thirty' for parameter 'age', expected 'int' not 'str'
ParserTypeError包含以下属性:
param: 参数名称 (str)value: 参数值 (Any)expected: 预期类型 (type)
ParserRequiredParameterError
在parse_dict被调用并且需要参数但未找到时引发
from dictparse import DictionaryParser
from dictparse.exceptions import ParserRequiredParameterError
parser = DictionaryParser()
parser.add_param("name", str)
parser.add_param("email", str, required=True)
try:
params = parser.parse_dict({"name": "John Doe"})
except ParserRequiredParameterError as e:
print(e) # Missing required parameter 'email'
ParserRequiredParameterError有一个属性param,参数的名称(str)
ParserInvalidChoiceError
调用时引发parse_dict并解析未在choices参数中定义的值add_param
from dictparse import DictionaryParser
from dictparse.exceptions import ParserInvalidChoiceError
parser = DictionaryParser()
parser.add_param("name", str)
parser.add_param("language", str, choices=["python", "bash"])
try:
params = parser.parse_dict({"name": "John Doe", "language": "javascript"})
except ParserInvalidChoiceError as e:
print(e) # Parameter 'language' must be one of '['python', 'bash']', not 'javascript'
ParserInvalidChoiceError具有以下 3 个属性:
param:参数名称(str)value:参数值(任意)choices: 通过add_param(list|set|tuple)添加的可用选项
ParserInvalidParameterError
parse_dict设置strict为引发调用True
该strict参数强制解析器仅接受已添加到解析器的参数
from dictparse import DictionaryParser
from dictparse.exceptions import ParserInvalidParameterError
parser = DictionaryParser()
parser.add_param("name", str)
parser.add_param("language", str, choices=["python", "bash"])
try:
params = parser.parse_dict({"name": "John Doe", "language": "python", "email": "jdoe@gmail.com"}, strict=True)
except ParserInvalidParameterError as e:
print(e) # Invalid parameter 'email'
ParserInvalidParameterError有一个属性param,参数的名称(str)
其他运行时注意事项parse_dict
data如果传递了一个无效的数据类型parse_dict(例如列表或字符串),它会引发一个
ParserInvalidDataTypeError
from dictparse import DictionaryParser
from dictparse.exceptions import ParserInvalidDataTypeError
parser = DictionaryParser()
parser.add_param("name", str)
try:
params = parser.parse_dict([{"name", "John Doe"}])
except ParserInvalidDataTypeError as e:
print(e) # Invalid type for 'data', must be a dict or dict-like object, not 'list'
try:
params = parser.parse_dict("foo")
except ParserInvalidDataTypeError as e:
print(e) # Invalid type for 'data', must be a dict or dict-like object, not 'str'
测试和覆盖
目录中提供了一个测试套件,tests覆盖率为 100%(2020 年 9 月 15 日)
Name Stmts Miss Cover
---------------------------------------------
dictparse/__init__.py 1 0 100%
dictparse/exceptions.py 37 0 100%
dictparse/parser.py 106 0 100%
tests/__init__.py 0 0 100%
tests/test_parser.py 310 0 100%
---------------------------------------------
TOTAL 454 0 100%
项目详情
下载文件
下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。