Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/test-funasr-onnx-package.yml
Original file line number Diff line number Diff line change
@@ -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@v7

- uses: actions/setup-python@v6
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@v7
with:
name: funasr-onnx-0.4.2
path: dist/funasr-onnx/*
if-no-files-found: error
2 changes: 1 addition & 1 deletion runtime/python/onnxruntime/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
85 changes: 85 additions & 0 deletions tests/test_funasr_onnx_release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import ast
from pathlib import Path
import re
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"
REQUIREMENT_NAME_PATTERN = re.compile(r"^\s*([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)")


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")


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 = {normalized_requirement_name(requirement) for requirement in requirements}
self.assertIn("jieba", names)
self.assertNotIn("torch", names)
Comment thread
LauraGPT marked this conversation as resolved.

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"
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()
Loading