From 6aeb659f84fb7f062d4ea486652eade95e4724ff Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 11 Mar 2026 11:38:47 +0000 Subject: [PATCH 1/4] src/ tests/: allow typing tools to see Page.find_tables(). Fixes #4932. --- src/__init__.py | 10 ++++++---- tests/test_typing.py | 3 +++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/__init__.py b/src/__init__.py index df5f03c39..608996d7d 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -13,12 +13,13 @@ import atexit import binascii import collections +import glob +import importlib.util import inspect import io import math import os import pathlib -import glob import re import string import sys @@ -30,7 +31,6 @@ import zipfile from . import extra -import importlib.util # Set up g_out_log and g_out_message from environment variables. # @@ -11383,6 +11383,9 @@ def extend_textpage(self, tpage, flags=0, matrix=None): mupdf.fz_run_page( page, dev, ctm, mupdf.FzCookie()) mupdf.fz_close_device( dev) + def find_tables(self, **kwargs): + return table.find_tables(self, **kwargs) + @property def first_annot(self): """First annotation.""" @@ -25571,8 +25574,7 @@ def _mupdf_devel(make_links=True): recover_quad = utils.recover_quad recover_span_quad = utils.recover_span_quad -from .table import find_tables -Page.find_tables = find_tables +from . import table class FitzDeprecation(DeprecationWarning): diff --git a/tests/test_typing.py b/tests/test_typing.py index 88f7dceee..d734ac8a0 100644 --- a/tests/test_typing.py +++ b/tests/test_typing.py @@ -43,3 +43,6 @@ def test_py_typed(): def _test_4903(page: pymupdf.Page) -> float: # In 1.27.1, error: Variable "pymupdf.Page" is not valid as a type return page.rect.width # In 1.27.1, error: pymupdf.Page? has no attribute "rect" + +def _test_4932(page: pymupdf.Page): + page.find_tables() From ef3e63382ed18bb97be5fedd8188d8636023d472 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 11 Mar 2026 14:34:30 +0000 Subject: [PATCH 2/4] tests/: avoid running known very slow tests if PYMUPDF_TEST_QUICK=1. --- tests/test_memory.py | 3 +++ tests/test_pixmap.py | 6 ++++++ tests/util.py | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/tests/test_memory.py b/tests/test_memory.py index 8da6de934..4d70d58fc 100644 --- a/tests/test_memory.py +++ b/tests/test_memory.py @@ -1,4 +1,5 @@ import pymupdf +import util import gc import os @@ -17,6 +18,8 @@ def test_2791(): ''' Check for memory leaks. ''' + if util.skip_slow_tests('test_2791'): + return if os.environ.get('PYODIDE_ROOT'): print('test_2791(): not running on Pyodide - No module named \'psutil\'.') return diff --git a/tests/test_pixmap.py b/tests/test_pixmap.py index bfbf3c565..921e7bcfe 100644 --- a/tests/test_pixmap.py +++ b/tests/test_pixmap.py @@ -18,6 +18,7 @@ import pytest import textwrap import time +import util scriptdir = os.path.abspath(os.path.dirname(__file__)) @@ -371,6 +372,8 @@ def do(gi): def test_3848(): + if util.skip_slow_tests('test_3848'): + return if os.environ.get('PYMUPDF_RUNNING_ON_VALGRIND') == '1': # Takes 40m on Github. print(f'test_3848(): not running on valgrind because very slow.', flush=1) @@ -509,6 +512,9 @@ def test_4336(): def test_4435(): + # This is slow, e.g. 224s. + if util.skip_slow_tests('test_4435'): + return print(f'{pymupdf.version=}') path = os.path.normpath(f'{__file__}/../../tests/resources/test_4435.pdf') with pymupdf.open(path) as document: diff --git a/tests/util.py b/tests/util.py index dbb246581..32785f673 100644 --- a/tests/util.py +++ b/tests/util.py @@ -26,3 +26,9 @@ def download(url, name, size=None): with open(path, 'wb') as f: f.write(r.content) return path + +def skip_slow_tests(test_name): + PYMUPDF_TEST_QUICK = os.environ.get('PYMUPDF_TEST_QUICK') + if PYMUPDF_TEST_QUICK == '1': + print(f'{test_name}(): skipping test because {PYMUPDF_TEST_QUICK=}.') + return True From 7d03601039b2091061dd553fceef50307a2e69ee Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 11 Mar 2026 14:36:16 +0000 Subject: [PATCH 3/4] src/__init__.py: add Annot.__bool__(). Avoids the need to do `if annot and annot.this`. --- src/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/__init__.py b/src/__init__.py index 608996d7d..a4b1cac02 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -586,6 +586,9 @@ class Annot: def __init__(self, annot): assert isinstance( annot, mupdf.PdfAnnot) self.this = annot + + def __bool__(self): + return bool(self.this) def __repr__(self): parent = getattr(self, 'parent', '<>') From 56f43fcd247665e0c7d92e499ec16f6a2834cc20 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 11 Mar 2026 14:38:48 +0000 Subject: [PATCH 4/4] src/__init__.py tests/: Partial fix for #4928 - exception in Document.scrub(). We fix the original problem in _load_annot(), but there is a later exception. --- src/__init__.py | 2 +- tests/resources/test_4928.pdf | Bin 0 -> 1833 bytes tests/test_general.py | 9 +++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/resources/test_4928.pdf diff --git a/src/__init__.py b/src/__init__.py index a4b1cac02..2bda40ff7 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -10187,7 +10187,7 @@ def _load_annot(self, name, xref): annot = JM_get_annot_by_name(page, name) else: annot = JM_get_annot_by_xref(page, xref) - if annot.m_internal: + if annot: return Annot(annot) def _makePixmap(self, doc, ctm, cs, alpha=0, annots=1, clip=None): diff --git a/tests/resources/test_4928.pdf b/tests/resources/test_4928.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d40b8c7a052878aceb8d2a8b525206ddf4408dd3 GIT binary patch literal 1833 zcmah~U1%It6c&_X=czvRspUinNzu;SJG1jAF*LiIZ5B6X-7E+V5hk-Yo6*^O!_3{7 z)FPrj1d%G3Hx>K=eNcRmKIBEHLVap|@TnjO)ruf(ebBc)=(#hqn+Yj&+1;6Y&bjA& z=R4<~JK3%`&QjgcC=d{9FKaV1no$!&E`e&sb5Spa6$@3JRn1sry(p-PZCD{Fv3*-N zJ@6`2_k7fLcQzRq?V!(6ZFW{;yrN zj3v%$A|4KSE>1zg>x_cb0vW`jUkaljj?m9>E+oR9nK7y&>9HioOE5%DaLG6-r@I?b z3iu5`vTX^0+7l>f`Wek=Kt(e;ER?W9U7`~YTo02aKwVS!DXvW2a?s!Ma7|`uchSiR zT^1i1cu;(Fs8TruWg+Ts#Q2jCiopQkjSwutbp09X`-D_Xk7OTIy)X=VZ4s#~Rw$D1 zTHsP2@qkHOog%t?Rp=Zn(}8ZZ@^s4vmk^ygK&ho${%B=pD#vw7s(gi@b506n5y!JD zx^KeRX3WVruZIvr6&nvEhQ+afS49%{CPthz`75m`#lo!QKy8vSU^}|wgKcHS!dj6E z9MV+WEXVSm)p0(Bve;cbav|D`^TE)V2~4+!VUQx8yy1&#s~kqnRmpqxd?JRMfL)Lq z=1XH4jBXO}bTdd84|l+*p`^~XqL6jYSFvsI9H0h|U+$&DQZY9as#MM)UH$}EEs6pA1z+~PW$29iMQ|1T-&>_fA8KO zum7xF*u8oC^u(9V_M^!s?}^Wjbz0V?OV&H#_q}Idx~8wac4DJ>efQ*@-@6fnx89mO z^~mY+T0CpTv$z3Tska0C%>R6Pc=lUsPP7yMNU6t911g1l^ zf%RZ*2Nr`wMjW~uVi2H7I}yEMh$Wuh%-=Gj20}AcTShX*2w7id4p4rk1Zq2B>ly)1 z%O{HZ2gkN?9<0NG%!f=3{6OX-OPTe742RPZneSlQhwUi!j@VJtJK~R8ILyX-WfDX& zOR^hjIl9OYve6Mjg1S@iE@<*~0g7VvUdMUw5QkK}`HDH`xs(!|NOKj_sye1wui1`c Z&OY2Ct6nOR?~EpDo2E87Ilt7<{s;5}2EYIS literal 0 HcmV?d00001 diff --git a/tests/test_general.py b/tests/test_general.py index 96d070f1b..b96e68eda 100644 --- a/tests/test_general.py +++ b/tests/test_general.py @@ -2212,3 +2212,12 @@ def test_4907(): print(f'{i=}') display_list = page.get_displaylist(annots=False) text_page = display_list.get_textpage() + +def test_4928(): + path = os.path.normpath(f'{__file__}/../../tests/resources/test_4928.pdf') + with pymupdf.open(path) as document: + try: + document.scrub() + except Exception as e: + print(f'Ignoring expected exception: {e}') +