From 191bcc7e89d3096ec52f3417ac815e5ccbd45ec1 Mon Sep 17 00:00:00 2001 From: SWHL Date: Tue, 6 Oct 2026 22:24:37 +0800 Subject: [PATCH] tests: add error catch --- python/rapidocr/default_models.yaml | 18 +++++++- python/tests/test_model_resolver.py | 61 +++++++++++++++++++++++---- python/tools/validate_model_routes.py | 39 ++++++++++++++--- 3 files changed, 102 insertions(+), 16 deletions(-) diff --git a/python/rapidocr/default_models.yaml b/python/rapidocr/default_models.yaml index eeecfdc..8fa0d24 100644 --- a/python/rapidocr/default_models.yaml +++ b/python/rapidocr/default_models.yaml @@ -1228,6 +1228,22 @@ lang_mapping: cn: "天城文" en: "Devanagari" aliases: [] + cyrillic: + cn: "西里尔语" + en: "Cyrillic" + aliases: [] + ch_doc: + cn: "中文文档" + en: "Chinese Document" + aliases: [] + gl: + cn: "加利西亚语" + en: "Galician" + aliases: [] + lb: + cn: "卢森堡语" + en: "Luxembourgish" + aliases: [] abq: cn: "阿布哈兹语" en: "Abkhaz" @@ -1243,7 +1259,7 @@ lang_mapping: ar: cn: "阿拉伯语" en: "Arabic" - aliases: [] + aliases: ["arabic"] ava: cn: "阿瓦尔语" en: "Avaric" diff --git a/python/tests/test_model_resolver.py b/python/tests/test_model_resolver.py index 10b92ae..d8eb109 100644 --- a/python/tests/test_model_resolver.py +++ b/python/tests/test_model_resolver.py @@ -3,13 +3,13 @@ # @Contact: liekkaskono@163.com import pytest from rapidocr import ModelType, OCRVersion -from rapidocr.inference_engine.base import InferSession +from rapidocr.inference_engine.base import FileInfo, InferSession from rapidocr.utils.model_resolver import ( MODEL_ROUTES, list_supported_langs, route_to_model_key, ) -from rapidocr.utils.typings import TaskType +from rapidocr.utils.typings import EngineType, TaskType from tools.validate_model_routes import validate_model_routes @@ -17,7 +17,7 @@ def test_model_routes_schema_is_valid(): validate_model_routes(MODEL_ROUTES) -def test_every_route_resolves_and_exists_in_a_model_registry(): +def test_every_route_resolves_in_each_available_model_registry(): registries = InferSession.model_info for version, version_cfg in MODEL_ROUTES.items(): if version == "_defs": @@ -34,11 +34,27 @@ def test_every_route_resolves_and_exists_in_a_model_registry(): model_enum = ModelType(model_type) for route in routes: model_key = route["model_key"] - assert any( - model_key in (registry.get(version, {}).get(task, {}) or {}) - for engine, registry in registries.items() - if engine != "model_routes" - ), model_key + available_engines = 0 + for engine, registry in registries.items(): + if engine not in {item.value for item in EngineType}: + continue + model_dict = registry.get(version, {}).get(task, {}) or {} + if model_key not in model_dict: + continue + + available_engines += 1 + model_info = InferSession.get_model_url( + FileInfo( + engine_type=EngineType(engine), + ocr_version=version_enum, + task_type=task_enum, + lang_type=route["supported_langs"][0], + model_type=model_enum, + ) + ) + assert model_info == model_dict[model_key] + + assert available_engines > 0, model_key for lang in route["supported_langs"]: assert ( route_to_model_key( @@ -48,6 +64,35 @@ def test_every_route_resolves_and_exists_in_a_model_registry(): ) +def test_model_routes_reject_unknown_enum_values(): + with pytest.raises(ValueError, match="Invalid OCR version"): + validate_model_routes({"PP-OCRv7": {}}) + + with pytest.raises(ValueError, match="Invalid task"): + validate_model_routes({"PP-OCRv6": {"recognize": {}}}) + + with pytest.raises(ValueError, match="Invalid model type"): + validate_model_routes({"PP-OCRv6": {"rec": {"large": []}}}) + + +def test_model_routes_accept_native_python_containers(): + validate_model_routes( + { + "PP-OCRv6": { + "rec": { + "small": [ + { + "lang": "multi", + "model_key": "multi_PP-OCRv6_rec_small", + "supported_langs": ["ch", "en"], + } + ] + } + } + } + ) + + @pytest.mark.parametrize( "task_type,expected_key", [ diff --git a/python/tools/validate_model_routes.py b/python/tools/validate_model_routes.py index 0c248b3..33631d3 100644 --- a/python/tools/validate_model_routes.py +++ b/python/tools/validate_model_routes.py @@ -1,12 +1,14 @@ # -*- encoding: utf-8 -*- +from collections.abc import Mapping, Sequence from typing import Any from omegaconf import OmegaConf +from rapidocr.utils.typings import ModelType, OCRVersion, TaskType def validate_model_routes(model_routes: Any) -> None: """Validate the engine-neutral route schema explicitly.""" - if not OmegaConf.is_dict(model_routes): + if not _is_mapping(model_routes): raise ValueError("Invalid model_routes: expected a mapping") for path, routes in _iter_model_route_lists(model_routes): @@ -17,7 +19,8 @@ def _iter_model_route_lists(model_routes: Any): for version, version_cfg in model_routes.items(): if version == "_defs": continue - if not OmegaConf.is_dict(version_cfg): + _validate_enum("OCR version", version, OCRVersion) + if not _is_mapping(version_cfg): raise ValueError(f"Invalid model route version: {version}") if "aliases" in version_cfg: _validate_aliases(f"{version}.aliases", version_cfg["aliases"]) @@ -25,18 +28,40 @@ def _iter_model_route_lists(model_routes: Any): for task, task_cfg in version_cfg.items(): if task == "aliases": continue - if not OmegaConf.is_dict(task_cfg): + _validate_enum("task", task, TaskType) + if not _is_mapping(task_cfg): raise ValueError(f"Invalid model route task: {version}.{task}") if "aliases" in task_cfg: _validate_aliases(f"{version}.{task}.aliases", task_cfg["aliases"]) for model_type, routes in task_cfg.items(): if model_type != "aliases": + _validate_enum("model type", model_type, ModelType) yield f"{version}.{task}.{model_type}", routes +def _validate_enum(label: str, value: str, enum_type: Any) -> None: + try: + enum_type(value) + except ValueError as exc: + valid_values = ", ".join(item.value for item in enum_type) + raise ValueError( + f"Invalid {label} {value!r}; expected one of: {valid_values}" + ) from exc + + +def _is_mapping(value: Any) -> bool: + return OmegaConf.is_dict(value) or isinstance(value, Mapping) + + +def _is_sequence(value: Any) -> bool: + return OmegaConf.is_list(value) or ( + isinstance(value, Sequence) and not isinstance(value, (str, bytes)) + ) + + def _validate_aliases(path: str, aliases: Any) -> None: - if not OmegaConf.is_dict(aliases): + if not _is_mapping(aliases): raise ValueError(f"Invalid aliases: {path}") for alias, target in aliases.items(): @@ -47,13 +72,13 @@ def _validate_aliases(path: str, aliases: Any) -> None: def _validate_model_route_list(path: str, routes: Any) -> None: - if not OmegaConf.is_list(routes): + if not _is_sequence(routes): raise ValueError(f"Invalid model route list: {path}") seen = set() seen_languages = set() for route in routes: - if not OmegaConf.is_dict(route): + if not _is_mapping(route): raise ValueError(f"Invalid model route entry: {path}") required = {"lang", "model_key", "supported_langs"} @@ -72,7 +97,7 @@ def _validate_model_route_list(path: str, routes: Any) -> None: seen.add(model_key) supported_langs = route["supported_langs"] - if not OmegaConf.is_list(supported_langs) or not supported_langs: + if not _is_sequence(supported_langs) or not supported_langs: raise ValueError(f"Empty supported languages: {model_key}") for lang in supported_langs: