From ffaa9d5556e6c2c28d029e18da7e09ad3c7dc1ce Mon Sep 17 00:00:00 2001 From: LauraGPT Date: Sun, 19 Jul 2026 10:26:55 +0000 Subject: [PATCH] fix: lazy load top-level AutoModel exports --- README.md | 1 + README_zh.md | 1 + funasr/__init__.py | 34 ++++++++++++++++++++--- funasr/version.txt | 2 +- tests/test_top_level_import.py | 49 ++++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 tests/test_top_level_import.py diff --git a/README.md b/README.md index 23f5f15cc..95198c8b8 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,7 @@ hf download FunAudioLLM/fsmn-vad-GGUF fsmn-vad.gguf --local-dir .\gguf ## What's new +- 2026/07/19: **v1.3.21 on PyPI** — fixes first-import onboarding in fresh environments where users install `funasr` before choosing a platform-specific PyTorch build. `import funasr` and `funasr.__version__` now work without torch; accessing `AutoModel` still requires PyTorch and raises a clear install hint. Install with `python -m pip install -U "funasr==1.3.21"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.3.21) - 2026/07/19: **v1.3.20 on PyPI** — PyPI metadata and install guidance now point at the current FunASR docs, community integrations, and quoted `python -m pip install -U "funasr>=1.3.19"` commands for Fun-ASR-Nano deployment paths. This is a documentation/packaging sync; runtime code remains unchanged from v1.3.19. Install with `python -m pip install -U "funasr==1.3.20"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.3.20) - 2026/07/19: **v1.3.19 on PyPI** — realtime WebSocket long-session troubleshooting docs are now shipped with the package. Run the server with `--enable-spk --log-session-stats-interval 30` and attach the emitted `Session stats:` lines when reporting disconnects or memory growth. Install with `python -m pip install -U "funasr==1.3.19"`. [Long-session guide ->](docs/vllm_guide.md#long-session-diagnostics) · [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.3.19) - 2026/07/19: **v1.3.18 on PyPI** — CLI SRT/TSV subtitle output now requests sentence timestamps and loads punctuation when needed, so `funasr audio.wav --output-format srt --output-dir ./subs` writes segmented subtitle cues instead of one full-text block. Install with `python -m pip install -U "funasr==1.3.18"`. [Release ->](https://github.com/modelscope/FunASR/releases/tag/v1.3.18) diff --git a/README_zh.md b/README_zh.md index eb879bef6..0860c62db 100644 --- a/README_zh.md +++ b/README_zh.md @@ -132,6 +132,7 @@ Whisper 是单个模型,**FunASR 是一个工具箱**——按场景挑模型 ## 最新动态 +- 2026/07/19:**v1.3.21 已发布到 PyPI** — 修复全新环境里先安装 `funasr`、尚未选择平台对应 PyTorch 版本时的首次导入阻塞。现在 `import funasr` 和 `funasr.__version__` 不再因为缺少 torch 失败;真正访问 `AutoModel` 时仍会要求安装 PyTorch,并给出明确安装提示。安装命令:`python -m pip install -U "funasr==1.3.21"`。[发布页 →](https://github.com/modelscope/FunASR/releases/tag/v1.3.21) - 2026/07/19:**v1.3.20 已发布到 PyPI** — PyPI 项目页和安装引导已同步到当前 FunASR 文档、社区集成列表,以及 Fun-ASR-Nano 部署路径中带引号的 `python -m pip install -U "funasr>=1.3.19"` 命令。本版本是文档/打包元数据同步,运行时代码与 v1.3.19 保持一致。安装命令:`python -m pip install -U "funasr==1.3.20"`。[发布页 →](https://github.com/modelscope/FunASR/releases/tag/v1.3.20) - 2026/07/19:**v1.3.19 已发布到 PyPI** — 实时 WebSocket 长会话排障文档已随包发布。启动服务时加上 `--enable-spk --log-session-stats-interval 30`,如果仍遇到断连或内存增长,请在 issue 中附上输出的 `Session stats:` 日志。安装命令:`python -m pip install -U "funasr==1.3.19"`。[长会话诊断 →](docs/vllm_guide_zh.md#长会话诊断) · [发布页 →](https://github.com/modelscope/FunASR/releases/tag/v1.3.19) - 2026/07/19:**v1.3.18 已发布到 PyPI** — CLI 的 SRT/TSV 字幕输出现在会请求句级时间戳,并在需要时加载标点模型;`funasr audio.wav --output-format srt --output-dir ./subs` 会输出分句字幕,不再退化成一个全文字幕块。安装命令:`python -m pip install -U "funasr==1.3.18"`。[发布页 →](https://github.com/modelscope/FunASR/releases/tag/v1.3.18) diff --git a/funasr/__init__.py b/funasr/__init__.py index 37a5d8e2a..8c8cdc251 100644 --- a/funasr/__init__.py +++ b/funasr/__init__.py @@ -16,6 +16,10 @@ _IMPORT_ERROR_TRACEBACKS = {} _IMPORT_DEBUG = os.environ.get("FUNASR_IMPORT_DEBUG") == "1" _STRICT_IMPORT = os.environ.get("FUNASR_STRICT_IMPORT") == "1" +_LAZY_EXPORTS = { + "AutoModel": ("funasr.auto.auto_model", "AutoModel"), + "AutoFrontend": ("funasr.auto.auto_frontend", "AutoFrontend"), +} def _record_import_error(name, error): @@ -73,7 +77,31 @@ def import_submodules(package, recursive=True): import_submodules(__name__) -from funasr.auto.auto_model import AutoModel -from funasr.auto.auto_frontend import AutoFrontend - os.environ["HYDRA_FULL_ERROR"] = "1" + + +def __getattr__(name): + if name not in _LAZY_EXPORTS: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + module_name, attr_name = _LAZY_EXPORTS[name] + try: + module = importlib.import_module(module_name) + except ModuleNotFoundError as error: + if error.name == "torch": + raise ModuleNotFoundError( + "FunASR requires PyTorch before using AutoModel. Install a " + "platform-appropriate torch build first, for example: " + "`python -m pip install torch torchaudio --index-url " + "https://download.pytorch.org/whl/cu126` for CUDA 12.6, " + "or follow https://pytorch.org/get-started/locally/.", + name="torch", + ) from error + raise + + value = getattr(module, attr_name) + globals()[name] = value + return value + + +__all__ = ["AutoModel", "AutoFrontend", "__version__", "get_import_errors", "get_import_error_tracebacks"] diff --git a/funasr/version.txt b/funasr/version.txt index 3c59b4047..89bdc0948 100644 --- a/funasr/version.txt +++ b/funasr/version.txt @@ -1 +1 @@ -1.3.20 +1.3.21 diff --git a/tests/test_top_level_import.py b/tests/test_top_level_import.py new file mode 100644 index 000000000..2d25d17dc --- /dev/null +++ b/tests/test_top_level_import.py @@ -0,0 +1,49 @@ +import os +import subprocess +import sys +from pathlib import Path + + +def test_top_level_import_without_torch_succeeds(): + repo_root = Path(__file__).resolve().parents[1] + script = r""" +import importlib.abc +import sys + + +class BlockTorch(importlib.abc.MetaPathFinder): + def find_spec(self, fullname, path=None, target=None): + if fullname == "torch" or fullname.startswith("torch."): + raise ModuleNotFoundError("No module named 'torch'", name="torch") + return None + + +sys.meta_path.insert(0, BlockTorch()) + +import funasr + +print(funasr.__version__) +assert isinstance(funasr.get_import_errors(), dict) +try: + from funasr import AutoModel # noqa: F401 +except ModuleNotFoundError as error: + assert error.name == "torch" + assert "requires PyTorch before using AutoModel" in str(error) +else: + raise AssertionError("AutoModel import should fail clearly when torch is missing") +""" + + env = os.environ.copy() + env["PYTHONPATH"] = str(repo_root) + env.pop("FUNASR_STRICT_IMPORT", None) + result = subprocess.run( + [sys.executable, "-c", script], + cwd=repo_root, + env=env, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + ) + + assert result.returncode == 0, result.stdout + result.stderr