使用cattrs来结构数据,我想省略x1字符串字段。
我想对传入的字符串执行一个简单的清理,除了密码字段。
我可以让它在所有字符串上工作
from attrs import define
from cattrs import Converter
MYDATA = {
"hostname": "MYhostNAme ",
"port": 389,
"adminuser": " cn=admin, dc=acme, dc=com",
"adminpass": " ADmin "
}
@define
class _LDAP:
hostname: str
port: int
adminuser: str
adminpass: str
def tidystr(text):
return text.lower().translate(str.maketrans("", "", " ntr"))
class _Vars:
converter = Converter()
converter.register_structure_hook(str, lambda x, cls: tidystr(x))
ldap = converter.structure(MYDATA, _LDAP)
app = _Vars()
assert app.ldap.hostname == "myhostname" # True
assert app.ldap.adminpass == "admin" # True !!Not what I want!!
我可以通过在adminpass字段中传递Any
来欺骗cattrs@define
class _LDAP:
adminpass: Any
但是这个有点笨拙。
文档展示了如何省略单个字段-但是我不知道如何调用tidystr函数。紧跟文档,我将执行
class Vars:
converter = Converter()
hook = make_dict_structure_fn(_LDAP, converter, adminpass=override(omit=True))
converter.register_structure_hook(_LDAP, hook)
ldap = converter.structure(MYDATA, _LDAP)
显然不能工作,因为没有调用tidystr()。
我已经尝试了各种方法,但还是迷路了。文档还显示了类似于我正在尝试做的事情,但示例是更改键而不是值。我是catters的作者。让我们看看如何解决这个问题。
首先,我们需要一种方法来识别哪些字段需要整理,哪些不需要。看起来你想在默认情况下对所有字符串应用整理,并对某些字段选择退出。
选项#1:NewType
使用NewType作为密码字段。
from typing import NewType
from attrs import define
from cattrs import Converter
NonTidyStr = NewType("NonTidyStr", str)
MYDATA = {
"hostname": "MYhostNAme ",
"port": 389,
"adminuser": " cn=admin, dc=acme, dc=com",
"adminpass": " ADmin ",
}
@define
class _LDAP:
hostname: str
port: int
adminuser: str
adminpass: NonTidyStr
def tidystr(text):
return text.lower().translate(str.maketrans("", "", " ntr"))
class _Vars:
converter = Converter()
converter.register_structure_hook(str, lambda x, cls: tidystr(x))
converter.register_structure_hook(NonTidyStr, lambda x, _: str(x))
ldap = converter.structure(MYDATA, _LDAP)
app = _Vars()
assert app.ldap.hostname == "myhostname" # True
assert app.ldap.adminpass == " ADmin "
(任何接受字符串的函数都很乐意接受基于它的NewType,并且在运行时它实际上是一个字符串。)
选项2:Annotated
我们可以用typing.Annotated
来构建我们自己的小系统。
类变成:
@define
class _LDAP:
hostname: str
port: int
adminuser: str
adminpass: Annotated[str, "notidy"]
然后,我们需要创建合适的钩子:
from cattrs._compat import is_annotated
def is_no_tidy(t: Any) -> bool:
return is_annotated(t) and "notidy" in t.__metadata__
converter.register_structure_hook_func(is_no_tidy, lambda x, _: str(x))
实现有点粗糙,因为Python没有非常好的类型检查功能,但我们可以使用内部cattrs函数来代替。
这种方法通常是非常强大的。
选项3:wait for 22.3.0
在下一个版本中,我将添加一个选项来覆盖单个字段的结构和非结构函数。然后,您将能够执行如下操作:
hook = make_dict_structure_fn(_LDAP, converter, adminpass=override(structure_fn=str))
converter.register_structure_hook(_LDAP, hook)
不能保证发布日期;)