Lazy Import Refactor
Module-level import torch / import tensorflow 等 heavy native dependency を function-internal lazy import に移行するための手順 + test 副作用予測 + CI-equivalent 検証要件。
When to Use
import <library>単体で SIGSEGV (CUDA driver 不在 + triton C-ext segfault 等)- 起動時間短縮のため重い import を遅延させたい
- WebAPI 専用利用者に heavy native dep を不要にしたい
- 上流 lib の lazy import 化を下流プロジェクトから依頼する設計検討
該当した実例: Linux + triton + CUDA driver 不在で import <ML ライブラリ> が SIGSEGV → torch / transformers の module-level import 排除で恒久解消したケースがある
Step 1: Pre-flight grep — 影響範囲完全洗い出し
Module-level import 全件 (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
テスト側の module attribute 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-level import を関数内に移すと、その module の module.torch 属性が消滅する。test 側で @patch("path.to.module.torch") を使用している箇所は全件 AttributeError で壊れる ため、事前 grep が必須。
Step 2: Refactor パターン
# 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を冒頭に追加 (型ヒントを文字列化、runtime evaluation 回避)- runtime で使う関数の冒頭で
import torchを再宣言 - module-level の bare
import torchは削除
Step 3: Test 副作用対応
# 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)
...
詳細パターン (context manager mock, cuda.is_available, getattr-based access 等) は test-side-effects.md 参照。
Step 4: Verification — CI-equivalent filter で必ず検証
プロジェクトの testing ルール(例: rules/testing.md の "CI-equivalent filter で local 検証する" セクション)を参照。
-m unit 等の短縮 filter では検証不充分 (本 SKILL の存在理由 — サブモジュール側の marker 差分が短縮 filter では再現できず下流 CI で初検出された実例がある)。CI workflow の filter を完全一致で local 実行する。
モノレポ内の独立パッケージ (submodule 等) の場合:
cd <package-path>
uv run pytest -m "not downloads_and_runs_model and not calls_real_webapi"
Step 5: Cross-repo PR 順序
submodule で利用される lib の場合:
- lib 側で fix + CI-equivalent test pass を確認
- lib 側 PR 起票 → merge 待ち
- merge 後、下流プロジェクトで submodule pin 更新 PR 起票
順序を逆にすると下流 CI で初検出され、hot-fix PR 連鎖が必要になる。
gh pr create 実行時、kit の submodule pre-PR check hook (hook_pre_pr_submodule_check.py) が導入されていれば、submodule 変更を含む PR で CI-equivalent test 実行確認を要求する (bypass は CI-EQUIV-TESTED marker comment)。
Anti-patterns
- module-level の上位 lib import:
from transformers.models.clip import CLIPProcessorは transformers が torch を eager load するため lazy 化が必要。直接import torchでなくても heavy native dep を引き連れる import は全て対象 - TYPE_CHECKING 内のみで対応:
cast(<heavy_lib>.Type, ...)やisinstance(x, <heavy_lib>.Type)のような runtime 評価が必要な箇所では完全には escape できない →from __future__ import annotationsで型ヒントの string 化と、関数内importの両方を必須とする - subprocess regression test の欠落: lazy import 化が効いているか検証する test を同 PR で追加する。fresh interpreter での
import <lib>; assert 'torch' not in sys.modulesパターンを推奨 (subprocess 経由で session 汚染を回避) -m unitでの local 検証: CI が独立 marker (standard等) を使う場合、必ず CI 設定の filter で検証する