feat: 添加 WithStreamErrors 查询流式调用执行过程中的异常

流式输出/双向流的 handler(Python 生成器)如果执行过程中抛异常,
Invoke[chan T] 本身的 err 只描述"调用有没有发起成功",跟这个异常
无关(永远是 nil),channel 只会静默提前关闭,调用方原本完全无法
感知。新增 WithStreamErrors(ctx) 返回一个包过的 ctx 和一个查询函数
streamErr,opt-in 之后可以查到具体错误。

错误记录挂在 WithStreamErrors 返回的 ctx 的对象图里(context.WithValue),
不是全局表——调用方不再引用 ctx/channel 时会被 GC 自然回收,不需要
任何显式清理逻辑,也不依赖 ctx.Done(),即使用 context.Background()
也能正常释放;ctx 之后被别的 context.With*(包括 StickyCtx)再包一层
也不影响查询。

同时补充完整的自动化测试覆盖 example/main.go 里演示过的所有功能:
四种调用模式 × int/struct/slice/[]byte 的组合(client_test.go)、
WithHandlers/call_go 全双工(handlers_test.go)、NewSession 隔离性
与 StickyCtx 路由(session_test.go),之前这些只能靠人肉跑 go run
看输出,现在都有真实断言。
This commit is contained in:
2026-07-23 16:24:05 +08:00
parent 6ccec66a1e
commit ee5e5b96af
9 changed files with 717 additions and 5 deletions
+22
View File
@@ -336,6 +336,28 @@ func demoTimeout(script string) {
fmt.Print(" ", v)
}
fmt.Println() // 应该完整输出 1~9,不会被 500ms 默认超时打断
// ── 示例5WithStreamErrors——流式 handler 执行过程中抛异常,channel 会静默
// 提前关闭,Invoke 本身的 err 只描述"调用有没有发起成功",跟这个异常无关
// (永远是 nil)。想知道流是不是因为 Python 侧异常提前结束,需要先用
// WithStreamErrors 包一层 ctxopt-in),拿到的 streamErr 函数不用再传 ctx。
//
// 错误记录挂在 WithStreamErrors 返回的这个 ctx 的对象图里,调用方不再引用
// ctx5/ch3 时会被 GC 自然回收,不需要任何显式清理,即使用 context.Background()
// 也一样能正常释放(不依赖 ctx.Done())。
ctx5, streamErr := gobridge.WithStreamErrors(context.Background())
ch3, err := gobridge.Invoke[chan int](ctx5, pool, "stream_then_raise", 3)
if err != nil {
log.Fatal(err) // 这里的 err 只可能是"发起调用失败",不会是 stream_then_raise 里的异常
}
fmt.Print("stream_then_raise(3)(执行过程中会抛异常)=")
for v := range ch3 {
fmt.Print(" ", v)
}
fmt.Println()
if err := streamErr(ch3); err != nil {
fmt.Println("streamErr 查到执行过程中的异常:", err)
}
}
func demoBlocking(script string) {