feat: 添加 Python→Go 全双工回调支持(call_go)
- 新增 WithHandlers 选项,通过反射将 Go 结构体方法暴露给 Python - 新增 callback/callback_result 消息类型,支持 Python 在处理中回调 Go - client 侧新增 readResult,内联处理 callback,复用同一连接避免死锁 - Python 侧新增 call_go[T]() 泛型调用,支持 dataclass 自动构造 - 注入 GOBRIDGE_WORKER_ID/WORKER_COUNT 环境变量,支持多 worker 初始化分工 - 新增示例演示 Go→Python→Go→Python 四层全双工链路 - Python 包版本升至 0.1.1
This commit is contained in:
@@ -32,6 +32,12 @@ func main() {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
demoPool(ctx, pool)
|
||||
demoServer(ctx, script)
|
||||
}
|
||||
|
||||
func demoPool(ctx context.Context, pool gobridge.Pool) {
|
||||
|
||||
// ── 普通调用 ──────────────────────────────────────────────────────────
|
||||
sum, err := gobridge.Invoke[int](ctx, pool, "add", 3, 4)
|
||||
if err != nil {
|
||||
@@ -139,3 +145,78 @@ func main() {
|
||||
fmt.Printf(" %+v\n", u)
|
||||
}
|
||||
}
|
||||
|
||||
// goService 实现 Handler 接口,公开方法自动暴露给 Python 通过 call_go() 调用
|
||||
type goService struct {
|
||||
pool gobridge.Pool // 用于 EnrichName 内部再调 Python
|
||||
}
|
||||
|
||||
func (s *goService) Multiply(ctx context.Context, a, b int) (int, error) {
|
||||
return a * b, nil
|
||||
}
|
||||
|
||||
func (s *goService) Log(msg string) {
|
||||
fmt.Println("[Go Log]", msg)
|
||||
}
|
||||
|
||||
// EnrichName 内部通过 Invoke 调用 Python 的 to_upper,演示 Go→Python→Go→Python 四层链路
|
||||
func (s *goService) EnrichName(ctx context.Context, name string) (string, error) {
|
||||
upper, err := gobridge.Invoke[string](ctx, s.pool, "to_upper", name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "Hello, " + upper + "!", nil
|
||||
}
|
||||
|
||||
func (s *goService) MakeUser(ctx context.Context, uid int) (User, error) {
|
||||
return User{ID: uid, Name: fmt.Sprintf("user_%d", uid), Score: float64(uid) * 1.5}, nil
|
||||
}
|
||||
|
||||
func demoServer(ctx context.Context, script string) {
|
||||
fmt.Println("\n── Server 全双工示例 ─────────────────────────────────────────────")
|
||||
|
||||
svc := &goService{}
|
||||
serv, err := gobridge.NewPool(script,
|
||||
gobridge.WithWorkers(1),
|
||||
gobridge.WithHandlers(svc),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer serv.Close()
|
||||
svc.pool = serv // 注入 pool 供 EnrichName 内部调用
|
||||
|
||||
// ── 示例1:Python 调用 Go Multiply ───────────────────────────────────────
|
||||
result, err := gobridge.Invoke[int](ctx, serv, "compute_with_go_mul", 6, 7)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("compute_with_go_mul(6, 7) =", result) // 42
|
||||
|
||||
// ── 示例2:流式输出 + Go Log 回调 ────────────────────────────────────────
|
||||
ch, err := gobridge.Invoke[chan int](ctx, serv, "squared_with_log", 4)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Print("squared_with_log(4) =")
|
||||
for v := range ch {
|
||||
fmt.Print(" ", v)
|
||||
}
|
||||
fmt.Println() // 1 4 9 16
|
||||
|
||||
// ── 示例3:Go→Python→Go→Python 四层全双工链路 ───────────────────────────
|
||||
// full_chain("world") → call_go[str]("EnrichName","world") → Invoke to_upper("world") → "WORLD"
|
||||
// ← "Hello, WORLD!" ← "Hello, WORLD!"
|
||||
greeting, err := gobridge.Invoke[string](ctx, serv, "full_chain", "world")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("full_chain(world) =", greeting) // Hello, WORLD!
|
||||
|
||||
// ── 示例4:call_go[User] 将 Go 返回的 dict 自动构造为 dataclass ──────────
|
||||
enriched, err := gobridge.Invoke[User](ctx, serv, "get_user_via_go", 12)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("get_user_via_go(12) = %+v\n", enriched) // {ID:12 Name:user_12 Score:18 Level:gold}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,24 @@ import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "python"))
|
||||
|
||||
from gobridge import expose, run
|
||||
import dataclasses
|
||||
import threading
|
||||
from typing import Iterator
|
||||
|
||||
from gobridge import expose, call_go, run, worker_id, worker_count
|
||||
|
||||
# ── worker_id / worker_count ──────────────────────────────────────────────────
|
||||
# 只有 worker 0 才执行一次性初始化(如监听端口、建立长连接等),
|
||||
# 其余 worker 跳过,避免端口冲突 / 重复连接。
|
||||
print(f"[worker {worker_id}/{worker_count}] started", flush=True)
|
||||
if worker_id == 0:
|
||||
def _init_shared_resource():
|
||||
# 示例:此处可启动 WebSocket 客户端、监听 TCP 端口等
|
||||
print(f"[worker {worker_id}] shared resource initialized", flush=True)
|
||||
threading.Thread(target=_init_shared_resource, daemon=True).start()
|
||||
|
||||
|
||||
# ── 基础类型 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@expose
|
||||
def add(a: int, b: int) -> int:
|
||||
@@ -26,12 +41,20 @@ def sum_stream(numbers: Iterator[int]) -> int:
|
||||
|
||||
@expose
|
||||
def double_stream(numbers: Iterator[int]) -> Iterator[int]:
|
||||
"""双向流:输入每个数,yield 其平方,对应 Go 侧 Invoke[chan int](c, ctx, "double_stream", inputChan)"""
|
||||
"""双向流:输入每个数,yield 其平方"""
|
||||
for n in numbers:
|
||||
yield n * n
|
||||
|
||||
|
||||
# ── struct(dict)类型 ──────────────────────────────────────────────────────
|
||||
# ── struct(dataclass / dict)类型 ───────────────────────────────────────────
|
||||
|
||||
@dataclasses.dataclass
|
||||
class User:
|
||||
id: int
|
||||
name: str
|
||||
score: float
|
||||
level: str = ""
|
||||
|
||||
|
||||
@expose
|
||||
def get_user(uid: int) -> dict:
|
||||
@@ -56,8 +79,6 @@ def enrich_users(users: list) -> list:
|
||||
return result
|
||||
|
||||
|
||||
# ── struct/slice 流式组合 ────────────────────────────────────────────────────
|
||||
|
||||
@expose
|
||||
def gen_users(count: int) -> Iterator[dict]:
|
||||
"""流式输出 struct:yield 多个 User,对应 Go 侧 Invoke[chan User]"""
|
||||
@@ -72,4 +93,48 @@ def process_users(users: Iterator[dict]) -> Iterator[dict]:
|
||||
yield {"id": u["id"], "name": u["name"].upper(), "score": u["score"] * 2}
|
||||
|
||||
|
||||
# ── Server 全双工示例 ────────────────────────────────────────────────────────
|
||||
|
||||
@expose
|
||||
def compute_with_go_mul(a: int, b: int) -> int:
|
||||
"""示例1:call_go[int] 指定返回类型"""
|
||||
return call_go[int]("Multiply", a, b)
|
||||
|
||||
|
||||
@expose
|
||||
def squared_with_log(n: int) -> Iterator[int]:
|
||||
"""示例2:流式输出,每次 yield 前 call_go("Log") 回调 Go"""
|
||||
for i in range(1, n + 1):
|
||||
call_go("Log", f"yielding {i}² = {i * i}")
|
||||
yield i * i
|
||||
|
||||
|
||||
@expose
|
||||
def to_upper(s: str) -> str:
|
||||
"""辅助方法:被 Go 的 EnrichName handler 内部调用"""
|
||||
return s.upper()
|
||||
|
||||
|
||||
@expose
|
||||
def full_chain(name: str) -> str:
|
||||
"""示例3:Go→Python→Go→Python 四层链路
|
||||
|
||||
full_chain("world")
|
||||
→ call_go[str]("EnrichName", "world") # Python 调 Go
|
||||
→ Invoke[string](ctx, serv, "to_upper", "world") # Go 再调 Python
|
||||
← "WORLD"
|
||||
← "Hello, WORLD!"
|
||||
← "Hello, WORLD!"
|
||||
"""
|
||||
return call_go[str]("EnrichName", name)
|
||||
|
||||
|
||||
@expose
|
||||
def get_user_via_go(uid: int) -> dict:
|
||||
"""示例4:call_go[User] 自动将 Go 返回的 dict 构造为 dataclass 实例"""
|
||||
user = call_go[User]("MakeUser", uid) # Go 返回 {"id":..,"name":..,"score":..}
|
||||
user.level = "gold" if user.score >= 10 else "silver"
|
||||
return dataclasses.asdict(user)
|
||||
|
||||
|
||||
run()
|
||||
|
||||
Reference in New Issue
Block a user