关于 lazy-import-refactor
指导 Python 项目将 PyTorch、TensorFlow 等重型原生依赖从模块级导入改为函数内延迟导入的技能,适用于希望降低导入副作用或启动负担的开发者。还包含测试调整、CI 等效验证及跨仓库 PR 顺序。
主要功能
- 01扫描模块级重型依赖导入
- 02将运行时导入移至函数内部
- 03处理测试中的模块属性补丁
- 04使用子进程验证延迟导入
- 05按 CI 完整筛选条件本地验证
适用场景
- 01排查导入时发生的 SIGSEGV
- 02减少不需要重型依赖用户的导入负担
- 03为延迟导入重构补充回归测试
- 04协调库与下游项目的 PR 顺序
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ nextaltair/ altairs-agent-dev-kit --skill lazy-import-refactor安装后在 Claude Code 中输入/lazy-import-refactor确认已加载。
附带文件test-side-effects.md
详细介绍
惰性导入重构
将模块级的 import torch / import tensorflow 等重型原生依赖移至函数内部进行惰性导入的步骤 + 测试副作用预测 + CI 等效验证要求。
适用场景
- 仅执行
import <library>就会触发 SIGSEGV(例如缺少 CUDA 驱动 + triton C-ext 段错误) - 希望延迟重型导入,以缩短启动时间
- 不希望仅使用 WebAPI 的用户依赖重型原生依赖
- 下游项目在考虑如何请求上游库改为惰性导入
相关实例:Linux + triton + 缺少 CUDA 驱动时,import <ML ライブラリ> 会触发 SIGSEGV → 通过移除 torch / transformers 的模块级导入,问题得到永久解决
步骤 1:预先 grep — 完整梳理影响范围
所有模块级导入(仅 col 0):
find src -name "*.py" | while read f; do
awk -v fn="$f" '/^(import torch|from torch|import torchvision|from torchvision|import tensorflow|from tensorflow)/{print fn":"NR":"$0}' "$f"
done
测试侧所有模块属性 patch:
find tests -name "*.py" | while read f; do
awk -v fn="$f" '/@patch.*\.(torch|tensorflow|jax)|patch\.object.*\.(torch|tensorflow|jax)/{print fn":"NR":"$0}' "$f"
done
将模块级导入移至函数内部后,该模块的 module.torch 属性会消失。测试侧所有使用 @patch("path.to.module.torch") 的位置都会因 AttributeError 而失败,因此必须提前 grep。
步骤 2:重构模式
# Before (module-level)
import torch
class Foo:
def bar(self, x: torch.Tensor) -> torch.Tensor:
with torch.no_grad():
...
# After (TYPE_CHECKING + function-internal)
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
import torch
class Foo:
def bar(self, x: torch.Tensor) -> torch.Tensor:
import torch # lazy load
with torch.no_grad():
...
必需事项:
- 在文件开头添加
from __future__ import annotations(将类型提示转为字符串,避免运行时求值) - 在运行时使用的函数开头重新声明
import torch - 删除模块级的裸
import torch
步骤 3:处理测试副作用
# Before
@patch("your_package.core.heavy_deps.torch")
def test_foo(mock_torch):
...
# After
def test_foo(monkeypatch):
import sys
from unittest.mock import MagicMock
mock_torch = MagicMock()
monkeypatch.setitem(sys.modules, "torch", mock_torch)
...
详细模式(上下文管理器 mock、cuda.is_available、基于 getattr 的访问等)请参阅 test-side-effects.md。
步骤 4:验证 — 务必使用 CI 等效筛选条件进行验证
请参阅项目的测试规则(例如 rules/testing.md 中的“使用 CI 等效筛选条件进行本地验证”章节)。
使用 -m unit 等简化筛选条件进行验证并不充分(这正是本 SKILL 存在的原因——曾有实例因简化筛选条件无法复现子模块侧的 marker 差异,导致问题首次在下游 CI 中被发现)。在本地运行时,必须与 CI workflow 的筛选条件完全一致。
对于单体仓库中独立的包(如 submodule):
cd <package-path>
uv run pytest -m "not downloads_and_runs_model and not calls_real_webapi"
步骤 5:跨仓库 PR 顺序
对于由 submodule 使用的库:
- 在库侧修复,并确认 CI 等效测试通过
- 在库侧创建 PR → 等待合并
- 合并后,在下游项目中创建更新 submodule pin 的 PR
如果顺序相反,问题会首次在下游 CI 中被发现,并需要连续提交 hot-fix PR。
执行 gh pr create 时,如果已引入 kit 的 submodule PR 前检查 hook(hook_pre_pr_submodule_check.py),则包含 submodule 变更的 PR 必须确认已运行 CI 等效测试(可通过 CI-EQUIV-TESTED marker comment 绕过)。
反模式
- 模块级导入上游库:
from transformers.models.clip import CLIPProcessor会导致 transformers 提前加载 torch,因此需要改为惰性导入。即使没有直接import torch,所有会引入重型原生依赖的导入都属于处理范围 - 只在 TYPE_CHECKING 内处理:对于需要运行时求值的位置,例如
cast(<heavy_lib>.Type, ...)或isinstance(x, <heavy_lib>.Type),无法完全规避 → 必须同时使用from __future__ import annotations将类型提示转为字符串,并在函数内执行import - 缺少 subprocess 回归测试:应在同一个 PR 中添加用于验证惰性导入是否生效的测试。推荐在全新解释器中使用
import <lib>; assert 'torch' not in sys.modules模式(通过 subprocess 避免 session 污染) - 使用
-m unit进行本地验证:如果 CI 使用独立 marker(如standard),务必使用 CI 配置中的筛选条件进行验证