Python API 参考

bootstrap()、open_model()、编译强档下自动生效的闸门,以及你的代码可能需要处理的异常。

你要面对的运行时 API 其实只有两个顶层函数——其余的一切(激活、授权、闸门本身)都发生在它们背后。没有装饰器需要导入或使用:magiclock build 会自动把授权闸门检查插入每一个编译后的模块(见编译强档)。

magiclock.bootstrap()

python
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()

python
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()
python
import magiclock

magiclock.bootstrap()
weights = magiclock.open_model("model.onnx.enc")

编译强档的闸门

没有函数或装饰器需要调用——magiclock build 会遍历你的源码树,在编译前给每一个模块插入一次闸门检查(核对 python_protect 这项能力):

python
# 你的源码,你自己完全不用改:
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() 会抛出各种异常,从"这台机器还没激活"到"这个产物已经过期"都有。大多数集成方式是宽泛地捕获异常,再按类名分支处理:

python
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在解密边界检测到附加的调试器。
RuntimeNotInitializedErrorfrom magiclock_host.errors import RuntimeNotInitializedError在这个进程里,bootstrap() 还没跑过,某个被门控的模块的检查就先跑了。

兼容性说明

.pya/.enc 信封里嵌入了生成它们的 CPython 大版本号和小版本号。3.12 下加密的产物没法在 3.13 下加载——升级解释器后请重新执行一次 protect/protect-model,而不是让一个产物跨 Python 版本使用。