-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathocr_util.py
More file actions
104 lines (89 loc) · 3.8 KB
/
Copy pathocr_util.py
File metadata and controls
104 lines (89 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""
ocr_util.py — Shared Tesseract/pytesseract configuration helper.
Every code path that calls pytesseract should call configure(config) first.
This is idempotent — the underlying state is module-level on pytesseract.
"""
from __future__ import annotations
import logging
import os
import shutil
from typing import Any, Dict, Optional
logger = logging.getLogger(__name__)
_LAST_CONFIGURED: Optional[str] = None
# Reused by every code path that surfaces an OCR install / configuration
# failure so the fix instructions stay consistent.
INSTALL_HINT = (
"Install Tesseract: Windows → "
"https://github.com/tesseract-ocr/tesseract/releases · "
"macOS → 'brew install tesseract' · "
"Linux → 'sudo apt install tesseract-ocr'. "
"Then `pip install pytesseract`. "
"If the tesseract binary is not on PATH (the default on Windows), set "
'ocr.tesseract_cmd in config.json to its full path. Backslashes inside '
'JSON strings must be escaped, e.g. '
'"tesseract_cmd": "c:\\\\program files\\\\tesseract-ocr\\\\tesseract.exe", '
'or use forward slashes: '
'"tesseract_cmd": "c:/program files/tesseract-ocr/tesseract.exe".'
)
def configure(config: Optional[Dict[str, Any]]) -> Optional[str]:
"""
Apply ocr.tesseract_cmd from *config* to pytesseract.
Returns the path that was applied (or detected via PATH), or None when
pytesseract isn't installed. Safe to call many times; only re-applies
when the configured path changes.
"""
global _LAST_CONFIGURED
try:
import pytesseract
except ImportError:
return None
cmd: Optional[str] = None
if config:
cmd = (config.get("ocr") or {}).get("tesseract_cmd") or None
if cmd:
# Normalise: trim quotes, expand env vars, accept forward slashes.
cmd = os.path.expanduser(os.path.expandvars(cmd.strip().strip('"').strip("'")))
cmd = cmd.replace("\\\\", "\\")
if cmd != _LAST_CONFIGURED:
pytesseract.pytesseract.tesseract_cmd = cmd
_LAST_CONFIGURED = cmd
logger.info(f"[ocr_util] tesseract_cmd set to {cmd!r}")
return cmd
# No explicit cmd — let pytesseract's default discovery (PATH) handle it.
discovered = shutil.which("tesseract")
return discovered
def diagnose(config: Optional[Dict[str, Any]]) -> Dict[str, Any]:
"""
Diagnostic snapshot of the Tesseract setup. Useful when OCR fails;
surface this via /api/healthz or include it in OCR error messages.
"""
info: Dict[str, Any] = {"pytesseract_installed": False,
"configured_path": None,
"configured_path_exists": False,
"path_discovered": shutil.which("tesseract"),
"version": None,
"error": None,
"hint": INSTALL_HINT}
try:
import pytesseract # noqa: F401
info["pytesseract_installed"] = True
except ImportError as e:
info["error"] = (f"pytesseract not installed: {e}. {INSTALL_HINT}")
return info
cmd = configure(config)
info["configured_path"] = cmd
if cmd and not os.path.exists(cmd) and shutil.which(cmd) is None:
info["configured_path_exists"] = False
info["error"] = (f"tesseract_cmd={cmd!r} does not exist on disk and "
f"is not on PATH. {INSTALL_HINT}")
return info
info["configured_path_exists"] = cmd is not None and (
os.path.exists(cmd) or shutil.which(cmd) is not None
)
try:
import pytesseract as _pt
info["version"] = str(_pt.get_tesseract_version())
except Exception as e:
info["error"] = (f"pytesseract.get_tesseract_version() failed: {e}. "
f"{INSTALL_HINT}")
return info