From a4d7803cf132d0ae420738abdeb22ca879967fd3 Mon Sep 17 00:00:00 2001 From: zhifu gao Date: Thu, 16 Jul 2026 10:26:48 +0000 Subject: [PATCH 1/3] Release funasr-onnx 0.4.2 candidate --- .../workflows/test-funasr-onnx-package.yml | 85 +++++++++++++++++++ runtime/python/onnxruntime/setup.py | 2 +- tests/test_funasr_onnx_release.py | 75 ++++++++++++++++ 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test-funasr-onnx-package.yml create mode 100644 tests/test_funasr_onnx_release.py diff --git a/.github/workflows/test-funasr-onnx-package.yml b/.github/workflows/test-funasr-onnx-package.yml new file mode 100644 index 000000000..2d9b7c65d --- /dev/null +++ b/.github/workflows/test-funasr-onnx-package.yml @@ -0,0 +1,85 @@ +name: Validate funasr-onnx package + +on: + pull_request: + paths: + - ".github/workflows/test-funasr-onnx-package.yml" + - "runtime/python/onnxruntime/**" + - "tests/test_funasr_onnx_release.py" + push: + branches: + - main + paths: + - ".github/workflows/test-funasr-onnx-package.yml" + - "runtime/python/onnxruntime/**" + - "tests/test_funasr_onnx_release.py" + workflow_dispatch: + +permissions: + contents: read + +jobs: + package-smoke: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + python-version: + - "3.11" + - "3.12" + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + + - name: Install build tooling + run: python -m pip install --upgrade pip build twine + + - name: Check release contract + run: python tests/test_funasr_onnx_release.py + + - name: Build wheel and source distribution + run: python -m build runtime/python/onnxruntime --outdir dist/funasr-onnx + + - name: Check distribution metadata + run: python -m twine check dist/funasr-onnx/* + + - name: Install built wheel + run: python -m pip install --force-reinstall dist/funasr-onnx/*.whl + + - name: Verify clean ONNX install + run: | + python -m pip check + python - <<'PY' + import importlib.util + from importlib.metadata import requires, version + + assert version("funasr-onnx") == "0.4.2" + assert importlib.util.find_spec("torch") is None + + requirements = requires("funasr-onnx") or [] + normalized = {item.lower().replace("_", "-") for item in requirements} + assert any(item.startswith("jieba") for item in normalized) + assert not any(item.startswith("torch") for item in normalized) + + from funasr_onnx import CT_Transformer, Fsmn_vad, Paraformer, SenseVoiceSmall + + assert all( + entrypoint is not None + for entrypoint in (Paraformer, Fsmn_vad, CT_Transformer, SenseVoiceSmall) + ) + PY + + - name: Upload release candidate + if: matrix.python-version == '3.12' + uses: actions/upload-artifact@v4 + with: + name: funasr-onnx-0.4.2 + path: dist/funasr-onnx/* + if-no-files-found: error diff --git a/runtime/python/onnxruntime/setup.py b/runtime/python/onnxruntime/setup.py index 8acd2f9f0..564cbc17a 100644 --- a/runtime/python/onnxruntime/setup.py +++ b/runtime/python/onnxruntime/setup.py @@ -13,7 +13,7 @@ def get_readme(): MODULE_NAME = "funasr_onnx" -VERSION_NUM = "0.4.1" +VERSION_NUM = "0.4.2" setuptools.setup( name=MODULE_NAME, diff --git a/tests/test_funasr_onnx_release.py b/tests/test_funasr_onnx_release.py new file mode 100644 index 000000000..6b4d49b81 --- /dev/null +++ b/tests/test_funasr_onnx_release.py @@ -0,0 +1,75 @@ +import ast +from pathlib import Path +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" + + +def read_setup_tree(): + return ast.parse(SETUP_PATH.read_text(encoding="utf-8"), filename=str(SETUP_PATH)) + + +def assigned_literal(tree, name): + for node in tree.body: + if not isinstance(node, ast.Assign): + continue + if any(isinstance(target, ast.Name) and target.id == name for target in node.targets): + return ast.literal_eval(node.value) + raise AssertionError(f"{name} is not assigned in {SETUP_PATH}") + + +def setup_keyword_literal(tree, name): + for node in ast.walk(tree): + if not isinstance(node, ast.Call): + continue + if not isinstance(node.func, ast.Attribute) or node.func.attr != "setup": + continue + for keyword in node.keywords: + if keyword.arg == name: + return ast.literal_eval(keyword.value) + raise AssertionError(f"setuptools.setup() has no {name!r} keyword") + + +class FunASROnnxReleaseContractTest(unittest.TestCase): + def test_release_version_is_0_4_2(self): + self.assertEqual(assigned_literal(read_setup_tree(), "VERSION_NUM"), EXPECTED_VERSION) + + def test_runtime_dependencies_keep_onnx_install_torch_free(self): + requirements = setup_keyword_literal(read_setup_tree(), "install_requires") + names = { + requirement.split(";", 1)[0] + .split("[", 1)[0] + .split("=", 1)[0] + .split("<", 1)[0] + .split(">", 1)[0] + .strip() + .lower() + .replace("_", "-") + for requirement in requirements + } + self.assertIn("jieba", names) + self.assertNotIn("torch", names) + + def test_package_source_has_no_torch_imports(self): + offenders = [] + package_dir = PACKAGE_ROOT / "funasr_onnx" + for source_path in sorted(package_dir.rglob("*.py")): + tree = ast.parse(source_path.read_text(encoding="utf-8"), filename=str(source_path)) + for node in ast.walk(tree): + if isinstance(node, ast.Import): + modules = [alias.name for alias in node.names] + elif isinstance(node, ast.ImportFrom): + modules = [node.module or ""] + else: + continue + if any(module == "torch" or module.startswith("torch.") for module in modules): + offenders.append(f"{source_path.relative_to(ROOT)}:{node.lineno}") + self.assertEqual(offenders, []) + + +if __name__ == "__main__": + unittest.main() From d69f0e7ce802e91a82d5304d4472f6cac0dae989 Mon Sep 17 00:00:00 2001 From: zhifu gao Date: Thu, 16 Jul 2026 10:30:50 +0000 Subject: [PATCH 2/3] Use Node 24 release validation actions --- .github/workflows/test-funasr-onnx-package.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-funasr-onnx-package.yml b/.github/workflows/test-funasr-onnx-package.yml index 2d9b7c65d..2501f2a98 100644 --- a/.github/workflows/test-funasr-onnx-package.yml +++ b/.github/workflows/test-funasr-onnx-package.yml @@ -31,9 +31,9 @@ jobs: - "3.12" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} cache: pip @@ -78,7 +78,7 @@ jobs: - name: Upload release candidate if: matrix.python-version == '3.12' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: funasr-onnx-0.4.2 path: dist/funasr-onnx/* From ca024b8dd4061332b2dcb934db7bc161e62bfd62 Mon Sep 17 00:00:00 2001 From: zhifu gao Date: Thu, 16 Jul 2026 10:37:59 +0000 Subject: [PATCH 3/3] Harden release dependency parsing --- tests/test_funasr_onnx_release.py | 32 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/tests/test_funasr_onnx_release.py b/tests/test_funasr_onnx_release.py index 6b4d49b81..843a93ed4 100644 --- a/tests/test_funasr_onnx_release.py +++ b/tests/test_funasr_onnx_release.py @@ -1,5 +1,6 @@ import ast from pathlib import Path +import re import unittest @@ -7,6 +8,7 @@ PACKAGE_ROOT = ROOT / "runtime" / "python" / "onnxruntime" SETUP_PATH = PACKAGE_ROOT / "setup.py" EXPECTED_VERSION = "0.4.2" +REQUIREMENT_NAME_PATTERN = re.compile(r"^\s*([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)") def read_setup_tree(): @@ -34,26 +36,34 @@ def setup_keyword_literal(tree, name): raise AssertionError(f"setuptools.setup() has no {name!r} keyword") +def normalized_requirement_name(requirement): + match = REQUIREMENT_NAME_PATTERN.match(requirement) + if match is None: + raise AssertionError(f"Unable to parse requirement name from {requirement!r}") + return re.sub(r"[-_.]+", "-", match.group(1)).lower() + + class FunASROnnxReleaseContractTest(unittest.TestCase): def test_release_version_is_0_4_2(self): self.assertEqual(assigned_literal(read_setup_tree(), "VERSION_NUM"), EXPECTED_VERSION) def test_runtime_dependencies_keep_onnx_install_torch_free(self): requirements = setup_keyword_literal(read_setup_tree(), "install_requires") - names = { - requirement.split(";", 1)[0] - .split("[", 1)[0] - .split("=", 1)[0] - .split("<", 1)[0] - .split(">", 1)[0] - .strip() - .lower() - .replace("_", "-") - for requirement in requirements - } + names = {normalized_requirement_name(requirement) for requirement in requirements} self.assertIn("jieba", names) self.assertNotIn("torch", names) + def test_requirement_name_parser_handles_pep508_forms(self): + cases = { + "torch!=2.0": "torch", + "Torch_CUDA~=2.0": "torch-cuda", + 'torch[distributed]>=2.0; python_version >= "3.11"': "torch", + "torch @ https://example.invalid/torch.whl": "torch", + } + for requirement, expected in cases.items(): + with self.subTest(requirement=requirement): + self.assertEqual(normalized_requirement_name(requirement), expected) + def test_package_source_has_no_torch_imports(self): offenders = [] package_dir = PACKAGE_ROOT / "funasr_onnx"