feat: validate python-bidi before initializing Arabic OCR (issue #719)

This commit is contained in:
SWHL authored and GitHub committed 2026-08-31 10:21:10 +08:00
1 parent e7edb01237
commit 0e629c8be0
4 files changed
+93 -11

No files matched your search

+3
View File
@@ -42,6 +42,9 @@ Changelog = "https://github.com/RapidAI/RapidOCR/releases"
[project.scripts]
rapidocr = "rapidocr.main:main"
[project.optional-dependencies]
rtl = ["python-bidi>=0.4.2,<1.0"]
[tool.setuptools]
include-package-data = true
platforms = ["Any"]
+7 -4
View File
@@ -24,7 +24,8 @@ from rapidocr.inference_engine.base import FileInfo, get_engine
from ..utils.download_file import DownloadFile, DownloadFileInput
from ..utils.log import logger
from ..utils.model_resolver import normalize_lang
from ..utils.utils import reorder_bidi_for_display
from ..utils.typings import LangRec
from ..utils.utils import reorder_bidi_for_display, validate_rtl_dependency
from ..utils.vis_res import VisRes
from .typings import TextRecInput, TextRecOutput
from .utils import CTCLabelDecode
@@ -32,10 +33,14 @@ from .utils import CTCLabelDecode
DEFAULT_DICT_PATH = Path(__file__).parent.parent / "models" / "ppocr_keys_v1.txt"
DEFAULT_DICT_URL = "https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/v2.0.7/paddle/PP-OCRv4/rec/ch_PP-OCRv4_rec_infer/ppocr_keys_v1.txt"
DEFAULT_MODEL_PATH = Path(__file__).parent.parent / "models"
RTL_LANGS = {LangRec.ARABIC.value}
class TextRecognizer:
def __init__(self, cfg: Dict[str, Any]):
if normalize_lang(cfg.lang_type) in RTL_LANGS:
validate_rtl_dependency()
self.session = get_engine(cfg.engine_type)(cfg)
# onnx has inner character, other engine get or download character_dict_path
@@ -50,8 +55,6 @@ class TextRecognizer:
self.cfg = cfg
self.RTL_LANGS = {"arabic", "fa", "ur"}
def get_character_dict(self, cfg):
character = None
dict_path = cfg.get("rec_keys_path", None)
@@ -139,7 +142,7 @@ class TextRecognizer:
all_line_results, all_word_results = list(zip(*rec_res))
txts, scores = list(zip(*all_line_results))
if normalize_lang(self.cfg.lang_type) in self.RTL_LANGS:
if normalize_lang(self.cfg.lang_type) in RTL_LANGS:
txts = reorder_bidi_for_display(txts)
elapse = time.perf_counter() - start_time
+13 -7
View File
@@ -12,16 +12,22 @@ import cv2
import numpy as np
def validate_rtl_dependency():
try:
from bidi.algorithm import get_display # noqa: F401
except ImportError as exc:
raise ModuleNotFoundError(
"RTL recognition requires the optional dependency 'python-bidi'. "
"Install it with: pip install 'rapidocr[rtl]'"
) from exc
def reorder_bidi_for_display(
texts: Tuple[Union[str, bytes], ...],
) -> Tuple[Union[str, bytes], ...]:
try:
from bidi.algorithm import get_display
except ImportError as e:
raise ModuleNotFoundError(
"Required dependency 'python-bidi' is not installed. "
"Install it with: pip install python-bidi"
) from e
validate_rtl_dependency()
from bidi.algorithm import get_display # noqa: F401
return tuple([get_display(text) for text in texts])
+70
View File
@@ -0,0 +1,70 @@
# -*- encoding: utf-8 -*-
# @Author: SWHL
# @Contact: liekkaskono@163.com
import builtins
import sys
import types
from pathlib import Path
from types import SimpleNamespace
import pytest
root_dir = Path(__file__).resolve().parent.parent
sys.path.append(str(root_dir))
from rapidocr.ch_ppocr_rec import main as rec_main
from rapidocr.utils import utils
from rapidocr.utils.model_resolver import normalize_lang
from rapidocr.utils.typings import EngineType, LangRec
def test_validate_rtl_dependency_reports_extra(monkeypatch):
original_import = builtins.__import__
def missing_bidi(name, *args, **kwargs):
if name.startswith("bidi"):
raise ModuleNotFoundError("No module named 'bidi'")
return original_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", missing_bidi)
with pytest.raises(ModuleNotFoundError, match=r"rapidocr\[rtl\]"):
utils.validate_rtl_dependency()
def test_reorder_bidi_for_display_uses_bidi_algorithm(monkeypatch):
bidi_module = types.ModuleType("bidi")
algorithm_module = types.ModuleType("bidi.algorithm")
algorithm_module.get_display = lambda text: text[::-1]
bidi_module.algorithm = algorithm_module
monkeypatch.setitem(sys.modules, "bidi", bidi_module)
monkeypatch.setitem(sys.modules, "bidi.algorithm", algorithm_module)
assert utils.reorder_bidi_for_display(("abc", "123")) == ("cba", "321")
def test_arabic_dependency_is_checked_before_engine_initialization(monkeypatch):
engine_called = False
def fail_get_engine(_engine_type):
nonlocal engine_called
engine_called = True
raise AssertionError(
"engine initialization must happen after dependency validation"
)
def missing_dependency():
raise ModuleNotFoundError("RTL dependency is missing")
monkeypatch.setattr(rec_main, "get_engine", fail_get_engine)
monkeypatch.setattr(rec_main, "validate_rtl_dependency", missing_dependency)
cfg = SimpleNamespace(lang_type=LangRec.ARABIC, engine_type=EngineType.ONNXRUNTIME)
with pytest.raises(ModuleNotFoundError, match="RTL dependency is missing"):
rec_main.TextRecognizer(cfg)
assert engine_called is False
def test_arabic_enum_is_normalized_for_rtl_detection():
assert normalize_lang(LangRec.ARABIC) in rec_main.RTL_LANGS