For Leptonica 1.79+ use leptSetStderrHandler

Lock free and considerably less dangerous to stderr messages.
This commit is contained in:
James R. Barlow
2020-07-19 03:40:33 -07:00
parent 4ea9cffebd
commit 5cbbff8472
4 changed files with 81 additions and 15 deletions
+59 -1
View File
@@ -24,7 +24,9 @@ import argparse
import logging
import os
import sys
import threading
import warnings
from collections import deque
from collections.abc import Sequence
from contextlib import suppress
from ctypes.util import find_library
@@ -75,7 +77,7 @@ except ffi.error as e:
) from e
class _LeptonicaErrorTrap:
class _LeptonicaErrorTrap_Redirect:
"""
Context manager to trap errors reported by Leptonica.
@@ -155,6 +157,62 @@ class _LeptonicaErrorTrap:
return False
tls = threading.local()
tls.trap = None
@ffi.callback("void(char *)")
def _stderr_handler(cstr):
msg = ffi.string(cstr).decode(errors='replace')
if msg.startswith("Error"):
logger.error(msg)
elif msg.startswith("Warning"):
logger.warning(msg)
else:
logger.debug(msg)
if tls.trap is not None:
tls.trap.append(msg)
return
class _LeptonicaErrorTrap_Queue:
def __init__(self):
self.queue = deque()
def __enter__(self):
self.queue.clear()
tls.trap = self.queue
def __exit__(self, exc_type, exc_value, traceback):
tls.trap = None
output = ''.join(self.queue)
self.queue.clear()
# If there are Python errors, record them
if exc_type:
logger.warning(output)
if 'Error' in output:
if 'image file not found' in output:
raise FileNotFoundError()
if 'pixWrite: stream not opened' in output:
raise LeptonicaIOError()
if 'index not valid' in output:
raise IndexError()
raise LeptonicaError(output)
return False
try:
lept.leptSetStderrHandler(_stderr_handler)
except ffi.error:
# Pre-1.79 Leptonica does not have leptSetStderrHandler
_LeptonicaErrorTrap = _LeptonicaErrorTrap_Redirect
else:
# 1.79 have this new symbol
_LeptonicaErrorTrap = _LeptonicaErrorTrap_Queue
class LeptonicaError(Exception):
pass
File diff suppressed because one or more lines are too long
+2
View File
@@ -509,6 +509,8 @@ void selDestroy ( SEL **psel );
l_int32
setMsgSeverity(l_int32 newsev);
void
leptSetStderrHandler(void (*handler)(const char *));
"""
)
+15 -9
View File
@@ -16,27 +16,25 @@
# along with OCRmyPDF. If not, see <http://www.gnu.org/licenses/>.
import os
from os import fspath
from pickle import dumps, loads
from unittest.mock import patch
import pytest
from PIL import Image, ImageChops
import ocrmypdf.leptonica as lept
from ocrmypdf import leptonica as lp
def test_colormap_backgroundnorm(resources):
# Issue #262 - unclear how to reproduce exactly, so just ensure leptonica
# can handle that case
pix = lept.Pix.open(resources / 'baiona_colormapped.png')
pix = lp.Pix.open(resources / 'baiona_colormapped.png')
pix.background_norm()
@pytest.fixture
def crom_pix(resources):
pix = lept.Pix.open(resources / 'crom.png')
pix = lp.Pix.open(resources / 'crom.png')
im = Image.open(resources / 'crom.png')
yield pix, im
im.close()
@@ -64,17 +62,17 @@ def test_pix_otsu(crom_pix):
@pytest.mark.skipif(
lept.get_leptonica_version() < 'leptonica-1.76',
lp.get_leptonica_version() < 'leptonica-1.76',
reason="needs new leptonica for API change",
)
def test_crop(resources):
pix = lept.Pix.open(resources / 'linn.png')
pix = lp.Pix.open(resources / 'linn.png')
foreground = pix.crop_to_foreground()
assert foreground.width < pix.width
def test_clean_bg(resources):
pix = lept.Pix.open(resources / 'congress.jpg')
pix = lp.Pix.open(resources / 'congress.jpg')
imbg = pix.clean_background_to_white()
@@ -96,4 +94,12 @@ def test_leptonica_compile(tmp_path):
def test_file_not_found():
with pytest.raises(FileNotFoundError):
lept.Pix.open("does_not_exist1")
lp.Pix.open("does_not_exist1")
def test_error_trap():
with pytest.raises(lp.LeptonicaError, match=r"Error in pixReadMem"):
with lp._LeptonicaErrorTrap():
lp.Pix(lp.lept.pixReadMem(lp.ffi.NULL, 0))
with lp._LeptonicaErrorTrap_Redirect():
lp.Pix(lp.lept.pixReadMem(lp.ffi.NULL, 0))