Metadata-Version: 2.4
Name: PKIPC
Version: 4.0.1
Summary: Cross-platform SDK for industrial software and C++/Python solvers
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: grpcio<2,>=1.81.1
Requires-Dist: protobuf<7,>=6.33.5
Requires-Dist: anyio<5,>=4.15.1
Provides-Extra: test
Requires-Dist: pytest>=8.2; extra == "test"
Requires-Dist: ruff>=0.12; extra == "test"
Requires-Dist: requests<3,>=2.32; extra == "test"
Provides-Extra: build
Requires-Dist: grpcio-tools==1.81.1; extra == "build"
Requires-Dist: build>=1.2; extra == "build"
Provides-Extra: release
Requires-Dist: twine<8,>=6.2; extra == "release"
Requires-Dist: requests<3,>=2.32; extra == "release"

# PKIPC SDK 4 — Python

算法作者保持 SDK 3 的接入方法：注册事件，直接取得配置与请求结果，继续自己的计算循环。SDK 负责后台通信、认证、一次 Hello 和生命周期。初始配置可选，None 表示没有配置，{} 表示明确提供空对象；不恢复额外 configure 请求／ACK。

```python
from pkipc import Solver

solver = Solver()
@solver.on("math.add")
def add(data):
    return {"result": data["a"] + data["b"]}
try:
    config = solver.start()  # 无初始配置时返回 None；不需要配置可忽略
    solver.send("ready", config)
    # 保留原计算循环，或使用 Event/Queue 与回调交换命令。
    solver.wait_closed(timeout=10)
finally:
    solver.close()
```

软件端仍使用原方法：

```python
import sys
from pkipc import Client
client = Client(sys.executable, args=["solver.py"], config={"threads": 4})
client.on("ready", lambda data: print(data))
try:
    client.start()  # 仍返回 Client，支持原链式用法
    result = client.request("math.add", {"a": 20, "b": 22})
    client.send("stop")
    print(client.wait(timeout=5))  # 超时返回 None
finally:
    client.close()
```

需要非阻塞操作的软件端使用 `start_async`、`request_async`、`wait_async`、`close_async`，返回 concurrent.futures.Future，直接共享同一 Endpoint/Session/Process owner，不增加另一套协议、配置或状态。同步接口保留真实求解器公开使用契约，只负责控制线程阻塞边界。Future 被放弃不代表取消已发请求，不自动重放 mutation。

`wait_closed(timeout)` 返回 bool，观察连接断开，不等待长 handler 完成。`wait_closed_async()` 与 `close_async()` 是完全清理回执：监听器、会话线程、托管进程及输出收束后完成。`close()` 等待清理并 join 生命周期线程。关闭马上阻止新请求和迟到资源安装；库不能强行中断用户计算，handler 需要自行结束。重复 close 共用原回执。

SDK 回调及 Future 完成回调可能在 SDK 线程同步执行，必须快速返回，不能调用阻塞的 start/request/close/wait_closed/wait。此类自等待由 SDK 拒绝。UI 更新投递到主线程。with Client/Solver 保持控制线程上的同步启动与清理方式。

## 普通 CLI

```python
from concurrent.futures import TimeoutError
from pkipc import Process

with Process("solver.exe", args=["--input", "input.csv", "--output", "output.csv"]) as process:
    exited = process.wait()
    while True:
        try:
            code = exited.result(timeout=5)
            break
        except TimeoutError:
            print(process.status())  # 本地轮询，不调用模型
```

Process 是 Client 也使用的唯一原生进程 owner，不需要 RPC。on_output(handler) 在 start 前注册，接收 OutputStream 与原始 bytes；未注册时继承 stdout/stderr。args 不经过 shell，stdin 关闭。底层使用 AnyIO >=4.15.1 公共进程/stream API；不手写 Windows 管道或使用私有 asyncio transport。Process 的 start/wait/stop 返回 Future；with 明确等待启动与收束。

本 Python Process 管理直接子进程，不声称整树隔离。stop(grace) 发起停止、宽限后强制结束，共享退出结果；正常退出不被迟到 stop 改写。Future 完成要求 child 与输出资源已收束，Process contextmanager 或 Client cleanup 会 join 执行线程。捕获时 child exit 后按 output_drain_timeout（默认 2 秒）等待 EOF；超时关闭管道并以 Error(TIMEOUT) 结束回执，status 保留实际 exit_code。C++ PKIPC Process 另有受控 tree 与 SDK runner 能力，Python当前使用者没有该需求，不伪称拥有树管理。

RPC 载荷不占命令行长度，普通 CLI 仍受 OS argv 限制，SDK 不改写第三方参数协议。工程授权、序贯轮次、产物冻结及 CSV 安装属于应用。

## 远程与错误

Solver(listen=..., token=..., tls=...) 与 Client(endpoint=..., token=..., tls=...) 使用相同事件接口。Client/Solver 角色独立于 listen/connect 方向。TLS/mTLS/token 保留，非回环默认要求 TLS。远程进程归部署系统，不由本地 SDK 停止。Error.code 为 typed 分类，remote 表示对端错误。请求超时不说明 mutation 未执行。send 只确认入队，需要业务回执使用 request。

## 构建、验收与发布

```sh
python -m pip install -e ".[test,build]"
python tools/sync_protocol.py --source /path/to/PKIPC --check
python tools/verify_sdk.py --cpp-source /path/to/PKIPC --cpp-build /short/build/path
python -m build
```

C++ proto 与 PROTOCOL.md 为唯一来源；sync_protocol 生成副本和 stubs，协议不变时不重生成。verify_sdk 构建 C++ 全模块并运行内部 CTest、Ruff、pytest；本模块已有构建可 --skip-build。测试直接传内部值，覆盖 JSON、Hello、准入和发布元数据，不经过进程/网络/文件/TLS边界。真实跨语言通信、长计算关闭、普通 CLI、输出排空及各平台验收单独报告。

协议 4 不兼容 3；求解器同步使用方法保持，但软件端与求解器端必须同时升级依赖。tools/release.py prepare 从完整验收的干净提交构建 wheel/sdist、检查实际安装 import/version 和 SHA-256 清单；publish 只上传该清单，不覆盖不同内容。PyPI token 经环境传入，不写入 URL、输出或源码。完整目标、ownership 与必要测试依据见 C++ SDK4_PLAN.md。
