Module-level import torch / import tensorflow Steps for migrating heavy native dependencies to function-internal lazy imports + test side-effect prediction + CI-equivalent verification requirements.
When to Use
import <library>standalone SIGSEGV (CUDA driver absent + triton C-ext segfault, etc.)- Want to defer heavy imports to shorten startup time
- Want to make heavy native deps unnecessary for WebAPI-only users
- Design study for requesting lazy import conversion of upstream libs from a downstream project
Relevant real example: on Linux + triton + CUDA driver absent, import <ML ライブラリ> caused SIGSEGV → there is a case permanently resolved by eliminating module-level imports of torch / transformers
Step 1: Pre-flight grep — exhaustive identification of impact scope
All module-level imports (col 0 only):
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
All module attribute patches on the test side:
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
Moving a module-level import into a function makes that module's module.torch attribute disappear.On the test side, @patch("path.to.module.torch") all locations using it will break with AttributeError , so a pre-flight grep is mandatory.
Step 2: Refactor Pattern
# 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():
...
Required items:
from __future__ import annotationsadd at the top (stringify type hints, avoid runtime evaluation)- At the top of functions used at runtime,
import torchredeclare - module-level の bare
import torchdelete
Step 3: Test side-effect handling
# 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)
...
For detailed patterns (context manager mock, cuda.is_available, getattr-based access, etc.), see test-side-effects.md .
Step 4: Verification — always verify with a CI-equivalent filter
Refer to the project's testing rules (e.g., the "verify locally with a CI-equivalent filter" section of rules/testing.md ).
-m unit Shortened filters such as these are insufficient for verification (the reason this SKILL exists — there is a real example where marker differences on the submodule side could not be reproduced with a shortened filter and were first detected in downstream CI). Run the CI workflow's filter locally with an exact match.
For independent packages within a monorepo (submodules, etc.):
cd <package-path>
uv run pytest -m "not downloads_and_runs_model and not calls_real_webapi"
Step 5: Cross-repo PR order
For a lib used as a submodule:
- Confirm fix + CI-equivalent test pass on the lib side
- Open PR on the lib side → wait for merge
- After merge, open a PR in the downstream project to update the submodule pin
If the order is reversed, it will be first detected in downstream CI, requiring a chain of hot-fix PRs.
gh pr create When running, if the kit's submodule pre-PR check hook (hook_pre_pr_submodule_check.py) is installed, it requires confirmation of CI-equivalent test execution for PRs containing submodule changes (bypass is CI-EQUIV-TESTED marker comment)。
Anti-patterns
- module-level upper lib import:
from transformers.models.clip import CLIPProcessorrequires lazy conversion because transformers eagerly loads torch. Even if not directlyimport torch, all imports that drag in heavy native deps are targets - Handled only within TYPE_CHECKING:
cast(<heavy_lib>.Type, ...)やisinstance(x, <heavy_lib>.Type)cannot be completely escaped in locations requiring runtime evaluation such as this →from __future__ import annotationsboth stringifying type hints and in-functionimportare mandatory - Missing subprocess regression testAdd a test in the same PR to verify whether lazy importification is working. In a fresh interpreter,
import <lib>; assert 'torch' not in sys.modulespattern is recommended (avoid session pollution via subprocess) -m unitlocal verification with: If CI uses an independent marker (standardetc.), always verify with the filter in the CI configuration