重写一个有 20 年历史的 Python 库
早在 2005 年,很多人都有个人博客,会在上面撰写各类内容,还喜欢设置评论区,但这也成了垃圾信息的磁石。当时 Akismet 上线,提供网络服务对评论进行垃圾信息分类,很受欢迎且沿用至今。其开发团队对 API 进行文档记录并建立密钥系统,不久后 Michael Foord 编写并发布了 Python 库 `akismet`。
后来 Python 3 发布,Michael 没时间维护,2015 年作者接手维护,将代码移植以支持 Python 2 和 3,2017 年发布 `akismet` 1.0 版本。之后作者定期发布小版本,2024 年启动重写项目,几个月前完成。
为什么要重写?
重写 `akismet` 有两个目标。一是支持 Akismet API 的特定功能,原客户端对 `comment - check` 方法返回 `bool` 值,无法展示“明显的”垃圾信息情况,修复会改变返回类型,是重大变更。二是异步 Python 兴起,作者希望库支持异步/非阻塞 HTTP 请求。
是否保留类设计?
作者探讨能否替换库基于类的设计,尝试勾勒纯基于函数的 API,但处理 API 凭证和凭证验证存在问题。基于函数的方法使参数签名复杂,且处理凭证验证会产生有状态模块,所以基于类的设计更合适。
用一个类还是两个类?
作者放弃只使用一个 `Akismet` 客户端类的想法,因为在同时支持同步和异步的类中实现自动凭证验证存在问题。于是决定使用两个客户端类,即 `akismet.SyncClient` 和 `akismet.AsyncClient`,将旧的 `Akismet` 类标记为已弃用。
如何处理异步,第一部分
使用两个类仍未完全解决自动凭证验证问题。Python 中 `__init__()` 不能有效异步,作者采用折中的解决方案:为 `SyncClient` 和 `AsyncClient` 提供 `validated_client()` 替代构造方法,实现自动凭证验证;为 `SyncClient` 实现上下文管理器协议,为 `AsyncClient` 实现异步上下文管理器协议,通过 `with` 或 `async with` 语句构造客户端进行凭证验证。
如何处理异步,第二部分
编写支持同步和异步操作的代码时,作者采用将非 I/O 部分提取到公共模块,构建辅助函数库的方法,减少代码重复。目前代码在错误处理方面仍有重复,作者认为当前代码状态不错,不急于进一步重构。
枚举选项
`akismet` 库历史上用 `bool` 表示垃圾信息检查结果,但 Akismet 网络服务支持三种值,作者选择用 `enum.IntEnum` 表示结果,开发者可将结果作为伪布尔类型使用。
Python 历史小知识
最初 Python 没有内置布尔类型,用整数 `0` 和 `1` 表示假/真。Python 2 逐步引入真正的布尔类型,如今 Python 的 `bool` 仍是 `int` 的子类。