mcpField
L
lazy-import-refactor

lazy-import-refactor

github.com/nextaltair开发053 次安装4 周前更新
代码仓库

关于 lazy-import-refactor

指导 Python 项目将 PyTorch、TensorFlow 等重型原生依赖从模块级导入改为函数内延迟导入的技能,适用于希望降低导入副作用或启动负担的开发者。还包含测试调整、CI 等效验证及跨仓库 PR 顺序。

主要功能

  1. 01扫描模块级重型依赖导入
  2. 02将运行时导入移至函数内部
  3. 03处理测试中的模块属性补丁
  4. 04使用子进程验证延迟导入
  5. 05按 CI 完整筛选条件本地验证

适用场景

  1. 01排查导入时发生的 SIGSEGV
  2. 02减少不需要重型依赖用户的导入负担
  3. 03为延迟导入重构补充回归测试
  4. 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 使用的库:

  1. 在库侧修复,并确认 CI 等效测试通过
  2. 在库侧创建 PR → 等待合并
  3. 合并后,在下游项目中创建更新 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 配置中的筛选条件进行验证

评论

支持 Markdown