lazy-import-refactor
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLazy Import Refactor
延迟导入重构
Module-level / 等 heavy native dependency を function-internal lazy import に移行するための手順 + test 副作用予測 + CI-equivalent 検証要件。
import torchimport tensorflow将模块级的/等重型原生依赖迁移至函数内部延迟导入的步骤 + 测试副作用预测 + 等效CI验证要求。
import torchimport tensorflowWhen to Use
适用场景
- 単体で SIGSEGV (CUDA driver 不在 + triton C-ext segfault 等)
import <library> - 起動時間短縮のため重い import を遅延させたい
- WebAPI 専用利用者に heavy native dep を不要にしたい
- 上流 lib の lazy import 化を下流プロジェクトから依頼する設計検討
該当した実例: Linux + triton + CUDA driver 不在で が SIGSEGV → torch / transformers の module-level import 排除で恒久解消したケースがある
import <ML ライブラリ>- 仅执行就触发SIGSEGV(例如缺少CUDA驱动 + triton C扩展段错误等情况)
import <library> - 为缩短启动时间而需要延迟重型依赖的导入
- 希望让仅使用WebAPI的用户无需安装重型原生依赖
- 考虑从下游项目向上游库提出延迟导入改造的设计需求
实际案例:在Linux + triton + 缺少CUDA驱动的环境中,触发SIGSEGV → 通过移除torch/transformers的模块级导入彻底解决问题
import <机器学习库>Step 1: Pre-flight grep — 影響範囲完全洗い出し
步骤1:预检查 grep — 全面排查影响范围
Module-level import 全件 (col 0 のみ):
bash
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テスト側の module attribute patch 全件:
bash
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"
doneModule-level import を関数内に移すと、その module の 属性が消滅する。test 側で を使用している箇所は全件 AttributeError で壊れる ため、事前 grep が必須。
module.torch@patch("path.to.module.torch")所有模块级导入(仅第0列):
bash
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测试侧所有模块属性补丁:
bash
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将模块级导入移至函数内部后,该模块的属性会消失。测试侧使用的所有位置都会因AttributeError而失效,因此必须提前进行grep排查。
module.torch@patch("path.to.module.torch")Step 2: Refactor パターン
步骤2:重构模式
python
undefinedpython
undefinedBefore (module-level)
重构前(模块级)
import torch
class Foo:
def bar(self, x: torch.Tensor) -> torch.Tensor:
with torch.no_grad():
...
import torch
class Foo:
def bar(self, x: torch.Tensor) -> torch.Tensor:
with torch.no_grad():
...
After (TYPE_CHECKING + function-internal)
重构后(TYPE_CHECKING + 函数内部)
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` を冒頭に追加 (型ヒントを文字列化、runtime evaluation 回避)
- runtime で使う関数の冒頭で `import torch` を再宣言
- module-level の bare `import torch` は削除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 # 延迟加载
with torch.no_grad():
...
**必填事项:**
- 在文件开头添加`from __future__ import annotations`(将类型提示字符串化,避免运行时求值)
- 在运行时使用的函数开头重新声明`import torch`
- 删除模块级的裸`import torch`Step 3: Test 副作用対応
步骤3:测试副作用处理
python
undefinedpython
undefinedBefore
重构前
@patch("your_package.core.heavy_deps.torch")
def test_foo(mock_torch):
...
@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)
...
詳細パターン (context manager mock, cuda.is_available, getattr-based access 等) は [test-side-effects.md](test-side-effects.md) 参照。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](test-side-effects.md)。Step 4: Verification — CI-equivalent filter で必ず検証
步骤4:验证 — 务必使用等效CI过滤器进行验证
プロジェクトの testing ルール(例: の "CI-equivalent filter で local 検証する" セクション)を参照。
rules/testing.md-m unitモノレポ内の独立パッケージ (submodule 等) の場合:
bash
cd <package-path>
uv run pytest -m "not downloads_and_runs_model and not calls_real_webapi"请参考项目的测试规则(例如:中的“使用等效CI过滤器进行本地验证”章节)。
rules/testing.md使用等简化过滤器无法充分验证(本技巧的存在原因——子模块的标记差异无法通过简化过滤器复现,曾出现过在下游CI中才首次发现问题的案例)。请完全匹配CI工作流的过滤器进行本地执行。
-m unit对于单体仓库中的独立包(如子模块):
bash
cd <package-path>
uv run pytest -m "not downloads_and_runs_model and not calls_real_webapi"Step 5: Cross-repo PR 順序
步骤5:跨仓库PR顺序
submodule で利用される lib の場合:
- lib 側で fix + CI-equivalent test pass を確認
- lib 側 PR 起票 → merge 待ち
- merge 後、下流プロジェクトで submodule pin 更新 PR 起票
順序を逆にすると下流 CI で初検出され、hot-fix PR 連鎖が必要になる。
gh pr createhook_pre_pr_submodule_check.pyCI-EQUIV-TESTED如果是被子模块使用的库:
- 在库侧修复并确认等效CI测试通过
- 在库侧提交PR → 等待合并
- 合并后,在下游项目提交更新子模块版本的PR
如果顺序颠倒,会在下游CI中首次发现问题,导致需要连续提交热修复PR。
执行时,如果已引入kit的子模块PR前检查钩子(),则包含子模块变更的PR会要求确认已执行等效CI测试(可通过添加标记注释绕过)。
gh pr createhook_pre_pr_submodule_check.pyCI-EQUIV-TESTEDAnti-patterns
反模式
- module-level の上位 lib import: は transformers が torch を eager load するため lazy 化が必要。直接
from transformers.models.clip import CLIPProcessorでなくても heavy native dep を引き連れる import は全て対象import torch - TYPE_CHECKING 内のみで対応: や
cast(<heavy_lib>.Type, ...)のような runtime 評価が必要な箇所では完全には escape できない →isinstance(x, <heavy_lib>.Type)で型ヒントの string 化と、関数内from __future__ import annotationsの両方を必須とするimport - subprocess regression test の欠落: lazy import 化が効いているか検証する test を同 PR で追加する。fresh interpreter での パターンを推奨 (subprocess 経由で session 汚染を回避)
import <lib>; assert 'torch' not in sys.modules - での local 検証: CI が独立 marker (
-m unit等) を使う場合、必ず CI 設定の filter で検証するstandard
- 模块级导入上层库:会导致transformers提前加载torch,因此需要进行延迟导入改造。即使不是直接
from transformers.models.clip import CLIPProcessor,只要会引入重型原生依赖的导入都属于改造对象import torch - 仅在TYPE_CHECKING中处理:对于或
cast(<heavy_lib>.Type, ...)这类需要运行时求值的场景,无法完全规避问题 → 必须同时使用isinstance(x, <heavy_lib>.Type)将类型提示字符串化,以及在函数内添加from __future__ import annotations语句import - 缺少子进程回归测试:需在同一PR中添加验证延迟导入是否生效的测试。推荐使用模式(通过子进程执行,避免会话污染)
import <lib>; assert 'torch' not in sys.modules - 使用进行本地验证:如果CI使用独立标记(如
-m unit等),务必使用CI配置中的过滤器进行验证standard