PyOxidizer 打包后导入 AnyIO 报 built-in module
AnyIO 4.15.1 读取模块源码时,内存加载器可能抛出未捕获的 TypeError。核对可执行文件内的依赖和加载器,评估兼容回退或可读取源码的打包布局。
同一套代码在普通 Python 环境能导入 AnyIO,打进 PyOxidizer 可执行文件后,启动却在 inspect.getsource() 处失败:
TypeError: <module 'anyio' ...> is a built-in module
模块显示为 built-in,不代表装成了另一个同名包。PyOxidizer 可以从内存加载 Python 模块,这类加载器不一定提供普通文件模块的 __file__ 和源码读取能力。AnyIO 4.15.1 的延迟导入初始化会读取模块源码,而其中的降级分支只捕获了 OSError,没有捕获这里的 TypeError。
先沿异常栈确认是源码检查失败
这一问题发生在 AnyIO 导入阶段,尚未运行网络请求或异步任务。典型路径是 install_lazy_importer() 进入 _build_lazy_map(),再由 inspect.getsource(module) 抛出异常。
保存完整构建配置、打包前后的依赖锁定记录和启动日志。核对可执行文件实际包含的 AnyIO 版本,以及对应模块由内存导入器还是普通文件导入器加载。不要只查看构建机虚拟环境里的版本;旧产物可能仍嵌入另一套资源。
这里明确涉及 AnyIO 4.15.1。它有读取源码建立延迟映射的代码,不能据此认定所有冻结工具都不兼容:不同加载器缺少源码时抛出的异常、提供的模块属性可能不同。
先选一个可以核对的兼容方案
可以从依赖回退或打包布局调整中选择,保留原构建产物,分别验证,避免同时改变太多条件。
项目允许回退时,AnyIO 4.14.2 的包入口使用直接导入,没有这一套延迟映射初始化。可以在项目约束中锁定该版本,重新解析并构建;前提是所有上层依赖允许这一组合:
anyio==4.14.2
这个约束针对上述导入回归,不保证其他依赖或整个 PyOxidizer 应用都兼容。必须核对锁文件差异,并在打包后的产物中检查实际入口;只在开发环境回退、继续分发旧可执行文件,没有作用。
需要保留 4.15.1 时,可以评估将相关 Python 源资源放到文件系统,并使用能向 inspect 提供源码的加载路径。PyOxidizer 的 filesystem-relative 资源布局确实能把模块文件放在可执行文件旁,但仅有一个同名 .py 文件还不够:运行时若仍优先选中不提供源码的内存模块,异常仍会出现。资源包含策略、搜索路径和导入器能力要一起核对,不把复制一份源码当成已完成修复。
后续修复已经加入对 TypeError 的降级处理,使源码不可用时回到直接导入。不过截至 2026 年 10 月 22 日核对的稳定发行版仍为 4.15.1,不能把代码合并等同于已装到正式修复版。采用自有补丁构建时应记录补丁和产物版本,避免长期手改 site-packages 却无法追溯。
验收对象必须是最终可执行文件
在没有构建虚拟环境帮助的目标环境中,确认产物能导入 AnyIO,并运行应用实际使用的异步入口、线程调用或网络路径。若调整了文件布局,还要确认安装包包含所需文件,启动目录改变后仍能找到它们。
不要全局替换 inspect.getsource,也不要给模块伪造一个不存在的 __file__ 来跳过异常。这会改变其他库对源码与模块位置的判断,把一次启动错误转成更隐蔽的运行期问题。
若需恢复,回退完整的可执行文件、资源目录和依赖锁定记录。只替换二进制或旁边的一个目录,容易留下版本不一致的混合产物。


