Skip to main content

Python 字典解析器

项目描述

字典解析

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: If True, 如果接收到任何未添加到解析器的参数,则引发异常
  • 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将被赋值destadd_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%

项目详情


下载文件

下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。

源分布

dictparse-1.4.tar.gz (14.9 kB 查看哈希)

已上传 source

内置分布

dictparse-1.4-py3-none-any.whl (12.4 kB 查看哈希)

已上传 py3