diff --git a/.github/workflows/test-text-postprocessing.yml b/.github/workflows/test-text-postprocessing.yml index 55364def5..c851661af 100644 --- a/.github/workflows/test-text-postprocessing.yml +++ b/.github/workflows/test-text-postprocessing.yml @@ -3,6 +3,8 @@ name: Validate text postprocessing on: pull_request: paths: + - "funasr/utils/type_utils.py" + - "tests/test_type_utils_compat.py" - "funasr/utils/postprocess_hotwords.py" - "funasr/utils/postprocess_utils.py" - "runtime/python/onnxruntime/funasr_onnx/utils/postprocess_utils.py" @@ -14,6 +16,8 @@ on: push: branches: [main] paths: + - "funasr/utils/type_utils.py" + - "tests/test_type_utils_compat.py" - "funasr/utils/postprocess_hotwords.py" - "funasr/utils/postprocess_utils.py" - "runtime/python/onnxruntime/funasr_onnx/utils/postprocess_utils.py" @@ -52,6 +56,7 @@ jobs: - name: Check hotword and rich transcription behavior without model weights run: | python -m pytest -q \ + tests/test_type_utils_compat.py \ tests/test_postprocess_hotwords.py \ tests/test_rich_transcription_postprocess.py \ tests/test_sentence_postprocess_timestamps.py \ diff --git a/funasr/utils/type_utils.py b/funasr/utils/type_utils.py index 0731e46f8..2b2323c35 100644 --- a/funasr/utils/type_utils.py +++ b/funasr/utils/type_utils.py @@ -1,4 +1,3 @@ -from distutils.util import strtobool from typing import Optional from typing import Tuple from typing import Union @@ -15,7 +14,12 @@ def str2bool(value: str) -> bool: Args: value: TODO. """ - return bool(strtobool(value)) + normalized = value.lower() + if normalized in ("y", "yes", "t", "true", "on", "1"): + return True + if normalized in ("n", "no", "f", "false", "off", "0"): + return False + raise ValueError(f"invalid truth value {normalized!r}") def remove_parenthesis(value: str): diff --git a/tests/test_type_utils_compat.py b/tests/test_type_utils_compat.py new file mode 100644 index 000000000..0be3cfbda --- /dev/null +++ b/tests/test_type_utils_compat.py @@ -0,0 +1,65 @@ +"""Keep the command-line boolean parser compatible without distutils.""" +import importlib.util +from pathlib import Path +import subprocess +import sys + +import pytest + + +SOURCE = Path(__file__).resolve().parents[1] / "funasr/utils/type_utils.py" + + +@pytest.fixture(scope="module") +def parser(): + spec = importlib.util.spec_from_file_location("boolean_parser_under_test", SOURCE) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module.str2bool + + +@pytest.mark.parametrize( + ("value", "expected"), + [("y", True), ("yes", True), ("t", True), ("true", True), ("on", True), + ("1", True), ("n", False), ("no", False), ("f", False), ("false", False), + ("off", False), ("0", False), ("YeS", True), ("TRUE", True), + ("nO", False), ("OFF", False)], +) +def test_boolean_argument_preserves_values_and_boolean_type(parser, value, expected): + assert parser(value) is expected + + +@pytest.mark.parametrize( + ("value", "message"), + [("MAYBE", "invalid truth value 'maybe'"), + ("FaLsE!", "invalid truth value 'false!'"), + (" TRUE ", "invalid truth value ' true '"), + ("", "invalid truth value ''"), + ("2", "invalid truth value '2'"), + ("false!", "invalid truth value 'false!'")], +) +def test_invalid_boolean_argument_preserves_normalized_error(parser, value, message): + with pytest.raises(ValueError) as caught: + parser(value) + assert str(caught.value) == message + + +def test_boolean_parser_imports_when_distutils_is_unavailable(): + code = """ +import importlib.abc +import runpy +import sys + +class MissingDistutils(importlib.abc.MetaPathFinder): + def find_spec(self, fullname, path=None, target=None): + if fullname == 'distutils' or fullname.startswith('distutils.'): + raise ImportError('distutils is unavailable') + +sys.meta_path.insert(0, MissingDistutils()) +parse = runpy.run_path(sys.argv[1])['str2bool'] +assert parse('yes') is True +assert parse('no') is False +""" + result = subprocess.run([sys.executable, "-c", code, str(SOURCE)], + text=True, capture_output=True, timeout=20) + assert result.returncode == 0, result.stdout + result.stderr