Merge PR #3690: prepare funasr-onnx 0.4.3

Deliver existing wrapper error fixes through the standalone ONNX distribution.

Signed-off-by: zhifu gao <[email protected]>
This commit is contained in:
zhifu gao
2026-09-10 07:20:45 +08:00
committed by GitHub
5 changed files with 168 additions and 5 deletions
+13 -2
View File
@@ -6,6 +6,7 @@ on:
- ".github/workflows/test-funasr-onnx-package.yml"
- "runtime/python/onnxruntime/**"
- "tests/test_funasr_onnx_release.py"
- "tests/test_funasr_onnx_installed.py"
push:
branches:
- main
@@ -13,6 +14,7 @@ on:
- ".github/workflows/test-funasr-onnx-package.yml"
- "runtime/python/onnxruntime/**"
- "tests/test_funasr_onnx_release.py"
- "tests/test_funasr_onnx_installed.py"
workflow_dispatch:
permissions:
@@ -60,7 +62,7 @@ jobs:
import importlib.util
from importlib.metadata import requires, version
assert version("funasr-onnx") == "0.4.2"
assert version("funasr-onnx") == "0.4.3"
assert importlib.util.find_spec("torch") is None
requirements = requires("funasr-onnx") or []
@@ -76,10 +78,19 @@ jobs:
)
PY
- name: Verify installed wrapper error paths
run: python tests/test_funasr_onnx_installed.py
- name: Verify source distribution error paths
run: |
python -m pip install --force-reinstall --no-deps dist/funasr-onnx/*.tar.gz
python -m pip check
python tests/test_funasr_onnx_installed.py
- name: Upload release candidate
if: matrix.python-version == '3.12'
uses: actions/upload-artifact@v7
with:
name: funasr-onnx-0.4.2
name: funasr-onnx-0.4.3
path: dist/funasr-onnx/*
if-no-files-found: error
+5
View File
@@ -2,6 +2,11 @@
## Install `funasr-onnx`
`funasr-onnx` is a separate distribution from `funasr`. Upgrading `funasr`
does not update the installed ONNX wrappers. Check their versions separately
with `python -m pip show funasr-onnx funasr`; use the source installation below
when a required wrapper fix is newer than the published PyPI package.
install from pip
```shell
+1 -1
View File
@@ -13,7 +13,7 @@ def get_readme():
MODULE_NAME = "funasr_onnx"
VERSION_NUM = "0.4.2"
VERSION_NUM = "0.4.3"
setuptools.setup(
name=MODULE_NAME,
+147
View File
@@ -0,0 +1,147 @@
"""Run against an installed wheel/sdist, outside the source package directory."""
import ast
import builtins
import contextlib
import importlib.metadata
import importlib.util
import io
import json
from pathlib import Path
import subprocess
import sys
import tempfile
from types import SimpleNamespace
import unittest
from unittest.mock import patch
if importlib.util.find_spec('funasr_onnx') is None:
raise unittest.SkipTest('Install the standalone funasr-onnx distribution first.')
import funasr_onnx
from funasr_onnx.paraformer_online_bin import Paraformer as ParaformerOnline
ENTRYPOINTS = (
funasr_onnx.Paraformer,
funasr_onnx.ContextualParaformer,
funasr_onnx.SeacoParaformer,
ParaformerOnline,
funasr_onnx.Fsmn_vad,
funasr_onnx.Fsmn_vad_online,
funasr_onnx.CT_Transformer,
funasr_onnx.CT_Transformer_VadRealtime,
funasr_onnx.SenseVoiceSmall,
)
class InstalledOnnxPackageTest(unittest.TestCase):
def test_installed_distribution_version_and_origin(self):
distribution = importlib.metadata.distribution('funasr-onnx')
self.assertEqual(distribution.version, '0.4.3')
self.assertEqual(
Path(funasr_onnx.__file__).resolve().parent,
Path(distribution.locate_file('funasr_onnx')).resolve(),
)
def test_installed_modules_have_no_string_exceptions(self):
offenders = []
for source in Path(funasr_onnx.__file__).parent.rglob('*.py'):
for node in ast.walk(ast.parse(source.read_text(encoding='utf-8'))):
if not isinstance(node, ast.Raise):
continue
literal = node.exc
if (
isinstance(literal, ast.Call)
and isinstance(literal.func, ast.Attribute)
and literal.func.attr == 'format'
):
literal = literal.func.value
if isinstance(literal, ast.Constant) and isinstance(literal.value, str):
offenders.append(f'{source.name}:{node.lineno}')
self.assertEqual(offenders, [])
def check_export_import_error(self, error, expected_type, preserves_cause):
original_import = builtins.__import__
def broken_export_import(name, *args, **kwargs):
if name == 'funasr':
raise error
return original_import(name, *args, **kwargs)
# Only the optional exporter import is replaced; constructors are installed code.
with tempfile.TemporaryDirectory() as model_dir:
for entrypoint in ENTRYPOINTS:
with self.subTest(entrypoint=entrypoint.__name__):
error.__traceback__ = None
with patch('builtins.__import__', side_effect=broken_export_import):
with contextlib.redirect_stdout(io.StringIO()):
with self.assertRaises(expected_type) as raised:
entrypoint(model_dir)
if preserves_cause:
self.assertIs(raised.exception.__cause__, error)
else:
self.assertIs(raised.exception, error)
def test_missing_transitive_export_dependency_remains_visible(self):
error = ModuleNotFoundError("No module named 'torchaudio'", name='torchaudio')
self.check_export_import_error(error, ImportError, preserves_cause=True)
def test_unrelated_export_import_failure_is_not_misreported(self):
error = RuntimeError('exporter initialization failed')
self.check_export_import_error(error, RuntimeError, preserves_cause=False)
def test_failed_model_download_preserves_cause(self):
original_import = builtins.__import__
error = OSError('model download failed')
def failed_download(*args, **kwargs):
raise error
def controlled_download_import(name, *args, **kwargs):
if name == 'modelscope.hub.snapshot_download':
return SimpleNamespace(snapshot_download=failed_download)
return original_import(name, *args, **kwargs)
with tempfile.TemporaryDirectory() as directory:
missing_model = str(Path(directory) / 'not-downloaded')
for entrypoint in ENTRYPOINTS:
with self.subTest(entrypoint=entrypoint.__name__):
error.__traceback__ = None
with patch('builtins.__import__', side_effect=controlled_download_import):
with self.assertRaises(RuntimeError) as raised:
entrypoint(missing_model)
self.assertIs(raised.exception.__cause__, error)
def test_missing_onnxruntime_import_preserves_cause(self):
script = '''
import builtins
import json
original_import = builtins.__import__
def broken_import(name, *args, **kwargs):
if name == 'onnxruntime':
raise ModuleNotFoundError("No module named 'onnxruntime'", name='onnxruntime')
return original_import(name, *args, **kwargs)
builtins.__import__ = broken_import
try:
import funasr_onnx
except Exception as error:
print(json.dumps({
'type': type(error).__name__,
'cause': type(error.__cause__).__name__,
'missing': getattr(error.__cause__, 'name', None),
}))
else:
print(json.dumps({'type': None}))
'''
result = subprocess.run(
[sys.executable, '-c', script], capture_output=True, text=True, timeout=60,
check=True,
)
self.assertEqual(json.loads(result.stdout.splitlines()[-1]), {
'type': 'ImportError', 'cause': 'ModuleNotFoundError', 'missing': 'onnxruntime',
})
if __name__ == '__main__':
unittest.main()
+2 -2
View File
@@ -7,7 +7,7 @@ import unittest
ROOT = Path(__file__).resolve().parents[1]
PACKAGE_ROOT = ROOT / "runtime" / "python" / "onnxruntime"
SETUP_PATH = PACKAGE_ROOT / "setup.py"
EXPECTED_VERSION = "0.4.2"
EXPECTED_VERSION = "0.4.3"
REQUIREMENT_NAME_PATTERN = re.compile(r"^\s*([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)")
@@ -44,7 +44,7 @@ def normalized_requirement_name(requirement):
class FunASROnnxReleaseContractTest(unittest.TestCase):
def test_release_version_is_0_4_2(self):
def test_release_version_is_0_4_3(self):
self.assertEqual(assigned_literal(read_setup_tree(), "VERSION_NUM"), EXPECTED_VERSION)
def test_runtime_dependencies_keep_onnx_install_torch_free(self):