使用cattrs进行序列化,并希望省略字段x1字符串字段



使用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)

不能保证发布日期;)

相关内容

最新更新