介绍 Wrapture:面向测试与追踪的 Python 包装工具
Introducing wrapture。Graham Dumpleton 的新项目。他因开发 wrapt、mod_wsgi 以及 New Relic 的 Python agent 而知名。他介绍说,Wrapture 借鉴了 wrapt 的 monkeypatch 思路,并将其扩展到测试和 tracing 场景。
Wrapture(完整文档)可以轻松包装任意函数或方法,从而追踪所有访问,或将其重写为返回其他值。
它既可以作为 unittest.mock 的替代方案,也可以用于为现有项目实现 tracing:
如何在不干扰被观察程序的前提下,为自己无法控制的代码附加观测能力,并记录其中流转的数据,这是我一直在思考的问题。
Wrapture 支持 OpenTelemetry,甚至提供了完全基于配置的机制,可以为现有 Python 项目添加 tracing,配置如下:
capture = "summary" [[observe]] target = "domain:Calculator" name = ["outer", "inner"] [[sink]] type = "jsonlines" path = "trace.jsonl"
这个项目还非常年轻——诞生至今只有几周——但开局相当亮眼。
有意思的是,这也是 Graham 首次尝试完全由 agent 驱动的大型项目:
Wrapture 中的每一行代码和文档,都是 AI assistant 在我的指导下完成的。我想坦率说明这一点,也同样想明确它不是什么。这不是所谓的 vibe coding:不是靠一次性提示词生成一堆代码,再因为缺乏判断能力而只能听天由命。Vibe coding 落得坏名声并非没有原因。从一开始,我就对 Wrapture 进行了精心设计。我在 Python 的这个领域深耕已久,非常清楚最终成果应当是什么样;AI 只是实现设计的工具,而不是设计的来源。
在后续文章 Unit testing with wrapture 中,Graham 展示了这个新库支持的测试模式:
def test_stub_with_wrapture():
with wrapture.binding(
Gateway, "charge"
).on_call.returns({
"id": "stub", "amount": 0}
):
assert OrderService().place(
500
)["id"] == "stub"
下面这个例子也很简洁:先调用原方法,再修改它的返回值:
def test_pinned_result_with_wrapture():
charge = wrapture.binding(
Gateway, "charge"
)
charge.on_call.transforms_result(
lambda r: {**r, "id": "ch_TEST"}
)
with charge:
assert OrderService().place(
500
) == {
"id": "ch_TEST", "amount": 500
}
(在这两个例子中,OrderService().place(...) 方法都会调用 Gateway().charge(...)。)