Python API 参考
bootstrap()、open_model()、编译强档下自动生效的闸门,以及你的代码可能需要处理的异常。
你要面对的运行时 API 其实只有两个顶层函数——其余的一切(激活、授权、闸门本身)都发生在它们背后。没有装饰器需要导入或使用:magiclock build 会自动把授权闸门检查插入每一个编译后的模块(见编译强档)。
magiclock.bootstrap()
magiclock.bootstrap(vault_dir=None, *, app_version="1.0.0") -> MagicLockRuntime针对这台机器的激活状态,为当前进程把授权闸门立起来。在 magiclock build 的产物里,这个调用会在入口模块自动插入;只有普通(未编译)脚本才需要你自己在第一次 open_model() 调用之前手动调用它。
vault_dir——覆盖本地激活状态的存放位置,通常不需要用到。app_version——会与你授权设置的max_app_version做比对,做版本锁定的发布策略时会用到。- 返回运行时对象(很少需要直接用它——大多数代码只是为了它的副作用而调用
bootstrap())。 - 如果这台机器尚未激活,或者激活状态存在但对这台机器无效(见下文异常),会抛出异常。
magiclock.open_model()
magiclock.open_model(path, *, vault_dir=None, passphrase=None, key=None) -> bytes解密由 protect-model 生成的 .enc 信封,返回明文字节——只存在于内存中,从不写入磁盘。
path——.enc文件路径。passphrase/key——用于二因子(--lock-passphrase)或便携(--no-bind-machine)产物;普通机器锁定产物不需要传。- 机器锁定产物要求先调用过
bootstrap()。
import magiclock
magiclock.bootstrap()
weights = magiclock.open_model("model.onnx.enc")编译强档的闸门
没有函数或装饰器需要调用——magiclock build 会遍历你的源码树,在编译前给每一个模块插入一次闸门检查(核对 python_protect 这项能力):
# 你的源码,你自己完全不用改:
def export_report(data: str) -> bytes:
...
# `magiclock build` 实际编译的内容,概念上等价于:
current_runtime().require_feature("python_protect")
def export_report(data: str) -> bytes:
...这个检查每个模块只跑一次,在进程第一次 import 它的时候执行——不是每次调用都跑一次。因为它被编织进了每一个编译后的模块,而不是集中在一处可删除的共享装饰器背后,攻击者必须逐个模块去找、去 patch。非入口模块的检查失败会照常向上抛出异常(它有可能被当作库导入);入口模块检查失败则会打印一行干净的提示后退出,因为没有它其他任何东西都跑不起来。
你可能需要处理的异常
bootstrap() 和 open_model() 会抛出各种异常,从"这台机器还没激活"到"这个产物已经过期"都有。大多数集成方式是宽泛地捕获异常,再按类名分支处理:
try:
magiclock.bootstrap()
data = magiclock.open_model("model.onnx.enc")
except Exception as exc:
name = type(exc).__name__
if name == "NotActivatedError":
... # 这台机器还没激活——执行 `magiclock activate`
elif name == "TrialExpiredError":
... # 产物的有效期已经过了
else:
raise| 异常 | 触发时机 |
|---|---|
NotActivatedError | 这台机器上找不到激活状态——运行 magiclock activate(或者让 CLI 在首次加密时自动激活)。 |
VaultLockedError | 本地激活状态在这台机器上打不开——要么是机器不对,要么是凭据文件被移动或篡改过。这两种情况被有意设计成无法区分,见安全模型。 |
TrialExpiredError | --trial、--expires-in 或 --expires-at 产物已经过了它的解密窗口。 |
SubscriptionExpiredError | 你的方案已经过期。这只会阻止新的加密——解密路径永远不会抛出它。 |
SubscriptionInvalidError | 订阅凭据在需要的地方缺失、格式错误,或签名验证失败。 |
PortableFormatError | 便携(--no-bind-machine)信封格式不对或版本不受支持。 |
PortableKeyError | 传给 open_model()/run --key 的口令或密钥和便携产物对不上。 |
DebuggerDetectedError | 在解密边界检测到附加的调试器。 |
RuntimeNotInitializedError(from magiclock_host.errors import RuntimeNotInitializedError) | 在这个进程里,bootstrap() 还没跑过,某个被门控的模块的检查就先跑了。 |
兼容性说明
.pya/.enc 信封里嵌入了生成它们的 CPython 大版本号和小版本号。3.12 下加密的产物没法在 3.13 下加载——升级解释器后请重新执行一次 protect/protect-model,而不是让一个产物跨 Python 版本使用。