Skip to content

fix(python): import converters and specs lazily so inference does not load torch - #2080

Open
Anai-Guo wants to merge 1 commit into
OpenNMT:masterfrom
Anai-Guo:fix-lazy-converter-imports
Open

fix(python): import converters and specs lazily so inference does not load torch#2080
Anai-Guo wants to merge 1 commit into
OpenNMT:masterfrom
Anai-Guo:fix-lazy-converter-imports

Conversation

@Anai-Guo

Copy link
Copy Markdown

Fixes #2078 (#2079 is a duplicate of the same report).

Problem

import ctranslate2 unconditionally imports torch, transformers and huggingface_hub when they are present in the environment, even for inference-only use (Translator / Generator / Whisper).

python/ctranslate2/__init__.py ends with:

from ctranslate2 import converters, models, specs
  • converters/__init__.py imports converters.transformers, which does try: import huggingface_hub, torch, transformers.
  • specs/model_spec.py does try: import torch to set torch_is_available.

Both are only needed for model conversion, never for inference — so every inference-only user with torch installed pays the import cost. As the reporter notes, this also forces users who mix CTranslate2 inference with onnxruntime in one process to shell out to a subprocess just to keep torch out of it.

Change

Import converters and specs on first attribute access using a module-level __getattr__ (PEP 562), plus a matching __dir__. models stays eager — it only imports from the compiled _ext, so it costs nothing.

Compatibility

All existing access patterns still work, verified against an installed 4.7.1 with the patch applied:

form result
import ctranslate2; ctranslate2.converters.TransformersConverter ok (triggers lazy import)
import ctranslate2; ctranslate2.specs.TransformerSpec ok
from ctranslate2 import converters ok
from ctranslate2 import specs ok
import ctranslate2.converters ok
from ctranslate2.converters import TransformersConverter ok
from ctranslate2.specs import TransformerSpec ok
from ctranslate2.specs import model_spectorch_is_available True
'converters' in dir(ctranslate2) / 'specs' in dir(...) True
ctranslate2.does_not_exist raises AttributeError

All 6 ct2-*-converter console_scripts entry points still resolve (ctranslate2.converters.{transformers,opennmt_py,fairseq,marian,opus_mt,openai_gpt2}:main — each imports and exposes a callable main).

Measurements

Windows, Python 3.12.10, torch 2.5.1+cu121, ctranslate2 4.7.1:

import ctranslate2 (subprocess, min of 3) package subtree (-X importtime)
before 3.04 s 2.73 s
after 0.15 s 0.11 s
before: torch in sys.modules = True,  transformers in sys.modules = True
after:  torch in sys.modules = False, transformers in sys.modules = False

Test

Added test_import_does_not_load_conversion_dependencies to python/tests/test_misc.py, parametrized over torch and transformers. It runs import ctranslate2 in a subprocess (the test session itself imports both, so an in-process check would be meaningless).

Against the installed package:

before fix: 2 failed, 2 passed   (both new params fail: got True, expected False)
after fix:  4 passed

black, isort (profile=black, lines_between_types=1) and flake8 (max-line-length=100) are clean on both changed files.


AI disclosure, per CONTRIBUTING.md: this change was written with AI assistance (Claude Code). The reported import chain was verified by reading the actual source on master, and every claim above — the compatibility matrix, entry points, timings, and before/after test results — was produced by running it locally against an installed CTranslate2, not inferred.

🤖 Generated with Claude Code

… load torch

`import ctranslate2` eagerly imported the `converters` and `specs` submodules, both of
which import torch at module level (`converters` also imports transformers and
huggingface_hub). Those dependencies are only needed to convert models, so every
inference-only user with torch in the environment paid for the import.

Import both submodules on first attribute access via a module-level `__getattr__`
(PEP 562). `from ctranslate2 import converters`, `import ctranslate2.converters` and
the `ct2-*-converter` entry points are unaffected; `models` stays eager because it
only imports from the compiled extension.

Measured with torch 2.5.1+cu121 installed: `import ctranslate2` drops from 3.04s to
0.15s (the package's own import subtree, per -X importtime, from 2.73s to 0.11s).

Fixes OpenNMT#2078
Comment thread python/tests/test_misc.py
import sys

import pytest

@jordimas jordimas Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your work

Could we add a small regression test for from ctranslate2 import *?

Before this change, wildcard imports exposed converters and specs. Since wildcard import uses __all__ if defined, otherwise current module globals, it would be good to check that this behavior is still preserved with the lazy imports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

import ctranslate2 eagerly imports torch (via converters + specs), even for inference-only use

2 participants