资讯动态

monty-typeshed:为 Monty 沙箱定制的精简 typeshed 类型存根裁剪方案

发布时间:2026/9/16 10:58:58 来源:尧图企业网站定制
monty-typeshed为 Monty 沙箱定制的精简 typeshed 类型存根裁剪方案【免费下载链接】montyA minimal, secure Python interpreter written in Rust for use by AI项目地址: https://gitcode.com/GitHub_Trending/monty3/monty导读MontyGitHub 推荐项目精选 / monty3 / monty 仓库中的核心项目是一个用 Rust 编写的极简、安全的 Python 解释器面向 AI 场景使用。为了让沙箱内的 Python 代码在运行前就能获得类型检查能力Monty 需要一个描述自身运行时 API 面、而非 CPython 完整标准库的类型存根stub集合——这就是monty-typeshedcrate 的职责。阅读本文后你将掌握为什么 Monty 需要一份裁剪过的 typeshed、这些存根如何在构建期被打包进二进制、custom/手写存根如何覆盖上游差异以及如何通过make update-typeshed安全地升级这份存根树而不破坏沙箱的类型检查一致性。背景为什么一个最小解释器需要自己的 typeshedMonty 只实现了 Python 标准库的一个刻意缩小的子集。这意味着它的类型检查器monty-type-checkingcrate底层由 ty 驱动需要的存根必须描述Monty 自己的运行时表面而不是 CPython 的表面代码使用了 Monty不支持的内置函数或标准库模块应当在类型检查阶段直接失败而不是让检查通过、等到运行时才报错。这正是monty-typeshed的核心理念只 vendoring内置托管Monty 实际支持的存根把不支持的函数和类过滤掉从而把类型检查通过但运行失败这一类问题提前消灭在静态分析阶段。从来源上看该 crate 最初派生自 ruff 的ty_vendoredcrate见 crates/monty-typeshed/README.md并在其基础上针对 Monty 的 API 面做了裁剪与定制。目录布局四条各司其职的路径monty-typeshedcrate 的整体结构如下crates/monty-typeshed/ ├── custom/ # 手写存根覆盖/补充上游差异 │ ├── README.md │ ├── asyncio.pyi │ ├── base64.pyi │ ├── binascii.pyi │ ├── functools.pyi │ ├── itertools.pyi │ ├── os.pyi │ ├── sys.pyi │ ├── unicodedata.pyi │ └── collections/__init__.pyi ├── vendor/typeshed/ # 裁剪后的上游存根构建期产物 │ ├── source_commit.txt # 记录上游 commit暴露为 SOURCE_COMMIT │ └── stdlib/ # 过滤后的 stdlib 存根 VERSIONS ├── update.py # 更新脚本克隆、过滤、覆盖 ├── check.py # 同步校验脚本 ├── build.rs # 构建期把存根打包为 zip 嵌入二进制 ├── src/lib.rs # crate 的唯一 API 入口 └── Cargo.toml各部分职责如下vendor/typeshed/—— 来自上游 typeshed 的 vendored 存根其来源 commit 记录在 crates/monty-typeshed/vendor/typeshed/source_commit.txt 中并通过 src/lib.rs 暴露为常量SOURCE_COMMIT。这些文件禁止手工编辑——它们会被更新脚本整体覆盖。custom/—— 手写存根用于覆盖或补充上游文件处理 Monty 的运行时表面与 CPython有意不同的模块如asyncio、os、sys、unicodedata。更新脚本会把它们复制进vendor/typeshed/stdlib。update.py—— 克隆上游 typeshed按白名单过滤出 Monty 支持的 builtins 与模块白名单与crates/monty/src/builtins/镜像对应再套用custom/覆盖。在仓库根目录运行make update-typeshed即可执行。build.rs—— 构建期把 vendored 存根打包进编译产物使 Monty 在磁盘上零存根文件的情况下也能提供类型检查。唯一公开 APIfile_system()该 crate 对外只暴露一个入口点file_system()。它返回一个静态的ruff_dbVendoredFileSystem基于 zip 中的存根构建monty-type-checking将其用作标准库模块解析的搜索路径。用法如 crates/monty-typeshed/README.md 所示let typeshed monty_typeshed::file_system(); assert!(typeshed.exists(stdlib/builtins.pyi));从源码看src/lib.rs 的实现非常精简SOURCE_COMMIT通过include_str!直接内联source_commit.txt的内容zip 字节则通过include_bytes!(concat!(env!(OUT_DIR), /zipped_typeshed.zip))在编译期嵌入file_system()内部用LazyLock保证VendoredFileSystem只初始化一次。在消费端monty-type-checking 的 db.rs 中MemoryDb::default()会调用monty_typeshed::file_system().clone()把它作为ProgramSettings::empty(vendored)的 vendored 文件系统再注册项目根目录并构建搜索路径。这意味着类型检查器解析import os、from typing import ...时看到的完全是 monty-typeshed 裁剪后的世界——这正是不支持的模块在类型检查阶段就报错这一目标得以实现的机制基础。该 crate 还自带了两个构建期/测试期验证见 src/lib.rs 中的测试模块test_commit断言SOURCE_COMMIT长度为 40即合法的 SHA-1 提交号typeshed_zip_created_at_build_time打开内嵌的 zip确认stdlib/builtins.pyi存在且包含class int:typeshed_versions_file_exists确认 zip 内stdlib/VERSIONS存在且包含builtins:条目。更新脚本 update.py从上游克隆到白名单过滤crates/monty-typeshed/update.py 是整套裁剪流水线的核心它依次完成四件事克隆/更新上游 typeshed 仓库到crates/monty-typeshed/typeshed-repo并将其 checkout 到固定 commit0e16ea31d2e188fdc126cb31e7c4fcc6b5a8da96。脚本会先用git status --porcelain检查工作树是否干净——如果本地有未提交改动会直接raise ValueError防止本地改动泄漏进 vendored 存根。过滤builtins.pyi用 Python 的ast模块解析源码按白名单保留支持的函数与类ast.unparse后写回。写入固定产物stdlib/builtins.pyi、stdlib/VERSIONS、vendor/typeshed/source_commit.txt。复制COPY_FILES列表中的文件无需过滤直接拷贝再把custom/下的存根覆盖上去。内置函数与类白名单内置函数白名单对应crates/monty/src/builtins/的实现ALLOWED_FUNCTIONS { abs, all, any, bin, chr, divmod, hash, hex, id, isinstance, iter, len, max, min, next, oct, ord, pow, print, repr, round, sorted, sum, }内置类白名单对应crates/monty/src/types/与exception_private.rs的实现分为几组核心类型object、type基本类型bool、int、float字符串/字节类型str、bytes容器类型list、tuple、dict、set、frozenset、range迭代器类enumerate、reversed、zip切片slice供 pathlib.Path 使用的property异常层级来自exception_private.rsBaseException、Exception、SystemExit、KeyboardInterrupt、ArithmeticError、OverflowError、ZeroDivisionError、LookupError、IndexError、KeyError、RuntimeError、NotImplementedError、RecursionError、AttributeError、AssertionError、MemoryError、NameError、SyntaxError、OSError、TimeoutError、TypeError、ValueError、StopIteration。过滤算法filter_statements/filter_if_block的关键行为顶层FunctionDef/AsyncFunctionDef仅当名字在白名单中时保留顶层ClassDef名字以_开头或命中白名单时保留下划线开头的私有类会被保留因为它们通常是公开 API 的内部依赖If节点递归过滤版本条件块如if sys.version_info (3, 10):若两个分支过滤后都为空则整个丢弃其余节点import、类型别名、赋值等一律保留。无需过滤直接拷贝的文件COPY_FILES列出的是完整保留的上游存根因为它们是类型系统运转的基础设施COPY_FILES [ typing.pyi, typing_extensions.pyi, _collections_abc.pyi, abc.pyi, # abstractmethod 装饰 Protocol 成员缺失会让成员推断为 Unknown types.pyi, # 类型注解中使用 dataclasses.pyi, # 支持 dataclass enum.pyi, # dataclasses 依赖 re.pyi, collections/__init__.pyi, collections/abc.pyi, _typeshed/__init__.pyi, _typeshed/_type_checker_internals.pyi, pathlib/__init__.pyi, pathlib/types.pyi, json/__init__.pyi, json/encoder.pyi, json/decoder.pyi, math.pyi, datetime.pyi, ]脚本注释给出了两个值得注意的设计理由abc.pyi之所以必须保留是因为abstractmethod装饰了存根中大量 Protocol 成员如Iterator.__next__缺少它这些成员会推断为Unknown从而静默吞掉下游错误_typeshed/_type_checker_internals.pyi之所以保留是因为 ty 会从中合成 TypedDict 成员缺少它 TypedDict 上的属性访问会错误地解析为Unknown而非unresolved-attribute对应 issue #799。VERSIONS 文件模块版本门控脚本会写入一份裁剪后的VERSIONS文件见 update.py 中的VERSIONS常量它声明了每个模块对应的 Python 版本范围例如_collections_abc: 3.3- _typeshed: 3.0- # not present at runtime, only for type checking abc: 3.0- # not importable at runtime, only for type checking asyncio: 3.4- base64: 3.0- builtins: 3.0- dataclasses: 3.7- datetime: 3.0- json: 3.0- math: 3.0- os: 3.0- pathlib: 3.4- pathlib.types: 3.14- re: 3.0- sys: 3.0- typing: 3.5- typing_extensions: 3.7- unicodedata: 3.0-这份文件是模块解析的门控开关类型检查器对已列出但无存根的模块报unresolved-import对有存根但未列出的模块直接忽略。因此只有 Monty 自身暴露的模块即custom/中的模块需要被列出而仅作为其他存根内部依赖被 vendored 的上游存根如enum经由dataclasses被引用则刻意不列出。custom/手写存根覆盖 Monty 的差异化 APIcrates/monty-typeshed/custom/README.md 说明了这一目录的定位当 Monty 中的类型与标准库不同时这里存放自定义类型存根。update.py会用rglob(*.pyi)递归遍历而非glob因为需要处理嵌套包如collections/__init__.pyi把每个文件按相对路径复制到vendor/typeshed/stdlib下覆盖或补充上游对应文件。以os.pyi为例custom/os.pyi 只声明了 Monty 沙箱真正暴露的一小部分 APIenviron、POSIX 路径常量sep、altsep、extsep、curdir、pardir、linesep、name、devnull、getenv带两个overload以支持默认值、listdir、stat、mkdir、makedirs、remove、unlink、rmdir、rename、replace、fspath以及stat_result类。注释特别说明沙箱路径模型在所有宿主上都是 POSIX 的——这是 Monty 有意为之的简化。custom/sys.pyi则定义了stdout/stderr类型为TextIO | MaybeNone、version、version_info等其中_version_info用type_check_only标注因为它运行时不可实例化。custom/asyncio.pyi更是直接手写了一个最小化的_Future类注释标明是_typeshed/stdlib/_asyncio.pyi的最小拷贝并为gather提供了逐个参数展开的重载overload精确描述 Monty 的 asyncio 子集。build.rs把存根打进二进制磁盘零文件build.rs 在构建期把整个vendor/typeshed/目录递归打包成一个 zipzipped_typeshed.zip输出到OUT_DIR随后由src/lib.rs用include_bytes!在编译期嵌入二进制。这样 Monty 运行时不依赖任何磁盘文件即可完成 stdlib 模块解析。几个值得注意的工程细节压缩算法按 feature 选择启用zstdfeature 时用CompressionMethod::Zstd启用deflate时用Deflated否则Stored。注释解释WASM 构建下编译zstd-sys需要 clang会大幅复杂化构建因此 WASM 场景改用 deflate见 crates/monty-typeshed/Cargo.toml 中的[features]定义因为 build script 的 target arch 是宿主架构而非构建目标架构这里不能用#[cfg(...)]只能读取TARGET环境变量。路径统一用/分隔zip 内路径用to_slash()归一化且输出文件名用format!而非Path::join拼接保证跨平台在编译期都能正确定位 zip。运行时补丁打包时若遇到stdlib/VERSIONS会追加一行ty_extensions: 3.0-使 ty 的扩展模块在类型检查中可用。同步校验 check.py防止静默漂移crates/monty-typeshed/check.py 用于校验 vendored 存根树与update.py的产出是否一致其背景问题很尖锐build.rs把vendor/typeshed/打进二进制而没有任何机制自动重新生成它——编辑custom/*.pyi、COPY_FILES、VERSIONS或COMMIT在重新运行update.py之前都不会生效。而漂移是静默的过时存根未描述的成员会报unresolved-attribute从VERSIONS缺失的模块会被当成不存在。check.py通过导入update.py脚本兄弟文件复用其常量定义从四个维度做校验check_tree_contents双向比较树中实际文件集合与update.py 会写出的文件集合既抓缺失update 会写但树里没有也抓陈旧树里有但 update 不再写防止改名/删除存根后旧副本继续躺在 zip 里check_custom_stubs每个custom/*.pyi必须与 zip 中的副本字节级一致check_generated_filessource_commit.txt必须等于COMMIT常量 换行stdlib/VERSIONS必须等于VERSIONS常量check_versionsVERSIONS中列出的每个模块都必须能解析到 vendored 存根.pyi或__init__.pyi且每个custom/*.pyi都必须在VERSIONS中列出否则类型检查器会忽略它。其中builtins.pyi的过滤需要上游克隆才能重新推导因此只校验其存在性内容不做离线重算。实际操作升级与校验命令在仓库根目录下Monty 提供两个 Makefile 目标见 Makefile# 更新 vendored typeshed克隆上游 → 过滤 → 应用 custom/ 覆盖 → 格式化 make update-typeshed # 校验 vendored typeshed 与 update.py 的产出是否同步 make check-typeshedmake update-typeshed等价于依次执行uv run crates/monty-typeshed/update.py # 核心更新流水线 uv run ruff format # 对产物做格式化 uv run ruff check --fix --fix-only --silent # 自动修复 lintmake check-typeshed则运行uv run crates/monty-typeshed/check.py校验失败时会逐条列出问题并以状态码 1 退出同时提示runmake update-typeshedto regenerate the vendored tree。从 crates/monty-typeshed/vendor/typeshed/stdlib 的实际产物可以看到裁剪后的 stdlib 存根树包含builtins.pyi过滤后的核心、VERSIONS、typing.pyi、typing_extensions.pyi、_collections_abc.pyi、abc.pyi、types.pyi、dataclasses.pyi、enum.pyi、re.pyi、math.pyi、datetime.pyi以及collections/、_typeshed/、pathlib/、json/等子目录和custom/覆盖来的asyncio.pyi、base64.pyi、binascii.pyi、functools.pyi、itertools.pyi、os.pyi、sys.pyi、unicodedata.pyi——与 README 与脚本声明完全吻合。在 Monty crate 家族中的位置monty-typeshed是 Monty 类型检查链路中的数据层。它与其余 crate 的分工如下crates/monty —— 核心解释器Python 解析器、字节码 VM 与沙箱crates/monty-types —— 共享边界数据类型值、异常、OS 调用、资源限制宿主无需链接解释器即可使用crates/monty-fs —— 宿主侧文件系统挂载把虚拟沙箱路径映射到真实宿主目录crates/monty-runtime ——monty可执行文件REPL、文件运行器与子进程 worker 模式crates/monty-pool —— 崩溃隔离的montyworker 子进程弹性池crates/monty-proto —— 池父进程与 worker 之间的 protobuf 线协议crates/monty-type-checking —— 沙箱代码的类型检查由 ty 驱动依赖 monty-typeshed 提供 stdlib 搜索路径crates/monty-typeshed—— 描述 Monty 所实现 stdlib 子集的裁剪存根本文主题crates/monty-macros ——monty参数解析背后的过程宏。小结monty-typeshed用一套上游克隆 → AST 白名单过滤 → custom 覆盖 → 构建期 zip 嵌入 → 同步校验的流水线为 Monty 的最小运行时提供了与之一一对应的类型表面。它的设计价值在于把运行时才暴露的不支持转化为类型检查阶段就拒绝同时借助SOURCE_COMMIT与check.py严格防止上游与定制内容之间的静默漂移。对需要深度集成 Monty 类型检查能力的开发者而言理解这份裁剪存根的结构与更新机制是正确使用、安全升级其类型检查能力的前提。【免费下载链接】montyA minimal, secure Python interpreter written in Rust for use by AI项目地址: https://gitcode.com/GitHub_Trending/monty3/monty创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价