Skip to content

Commit fac6678

Browse files
scripts/test.py: add crude pybind test.
Also see pymupdf#3869 `Crash in ~FzColorspace()`.
1 parent 2a5d082 commit fac6678

File tree

1 file changed

+68
-1
lines changed

1 file changed

+68
-1
lines changed

scripts/test.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@
9090
Whether to rebuild mupdf when we build PyMuPDF. Default is 1.
9191
--gdb 0|1
9292
Run tests under gdb.
93+
--pybind 0|1
94+
Run tests inside C++ pybind.Requires `sudo apt install pybind11-dev` or
95+
similar.
9396
--system-site-packages 0|1
9497
If 1, use `--system-site-packages` when creating venv.
9598
--timeout <seconds>
@@ -165,6 +168,7 @@ def main(argv):
165168
implementations = 'r'
166169
test_names = list()
167170
venv = 2
171+
pybind = False
168172
pytest_options = None
169173
timeout = None
170174
pytest_k = None
@@ -211,6 +215,8 @@ def main(argv):
211215
value = next(args)
212216
assert value in ('0', '1'), f'`-s` must be followed by `0` or `1`, not {value=}.'
213217
os.environ['PYMUPDF_SETUP_PY_LIMITED_API'] = value
218+
elif arg == '--pybind':
219+
pybind = int(next(args))
214220
elif arg == '--system-site-packages':
215221
system_site_packages = int(next(args))
216222
elif arg == '-t':
@@ -282,6 +288,7 @@ def do_test():
282288
gdb=gdb,
283289
test_fitz=test_fitz,
284290
pytest_k=pytest_k,
291+
pybind=pybind,
285292
)
286293

287294
for command in commands:
@@ -621,7 +628,8 @@ def test(
621628
timeout=None,
622629
gdb=False,
623630
test_fitz=True,
624-
pytest_k=None
631+
pytest_k=None,
632+
pybind=False,
625633
):
626634
'''
627635
Args:
@@ -642,6 +650,65 @@ def test(
642650
test_fitz:
643651
See top-level option `-f`.
644652
'''
653+
if pybind:
654+
cpp_path = 'pymupdf_test_pybind.cpp'
655+
cpp_exe = 'pymupdf_test_pybind.exe'
656+
cpp = textwrap.dedent('''
657+
#include <pybind11/embed.h>
658+
659+
int main()
660+
{
661+
pybind11::scoped_interpreter guard{};
662+
pybind11::exec(R"(
663+
print('Hello world', flush=1)
664+
import pymupdf
665+
pymupdf.JM_mupdf_show_warnings = 1
666+
print(f'{pymupdf.version=}', flush=1)
667+
doc = pymupdf.Document()
668+
pymupdf.mupdf.fz_warn('Dummy warning.')
669+
pymupdf.mupdf.fz_warn('Dummy warning.')
670+
pymupdf.mupdf.fz_warn('Dummy warning.')
671+
print(f'{doc=}', flush=1)
672+
)");
673+
}
674+
''')
675+
def fs_read(path):
676+
try:
677+
with open(path) as f:
678+
return f.read()
679+
except Exception:
680+
return
681+
def fs_remove(path):
682+
try:
683+
os.remove(path)
684+
except Exception:
685+
pass
686+
cpp_existing = fs_read(cpp_path)
687+
if cpp == cpp_existing:
688+
log(f'Not creating {cpp_exe} because unchanged: {cpp_path}')
689+
else:
690+
with open(cpp_path, 'w') as f:
691+
f.write(cpp)
692+
def getmtime(path):
693+
try:
694+
return os.path.getmtime(path)
695+
except Exception:
696+
return 0
697+
python_config = f'{os.path.realpath(sys.executable)}-config'
698+
# `--embed` adds `-lpython3.11` to the link command, which appears to
699+
# be necessary when building an executable.
700+
flags = run(f'{python_config} --cflags --ldflags --embed', capture=1)
701+
build_command = f'c++ {cpp_path} -o {cpp_exe} -g -W -Wall {flags}'
702+
build_path = f'{cpp_exe}.cmd'
703+
build_command_prev = fs_read(build_path)
704+
if build_command != build_command_prev or getmtime(cpp_path) >= getmtime(cpp_exe):
705+
fs_remove(build_path)
706+
run(build_command)
707+
with open(build_path, 'w') as f:
708+
f.write(build_command)
709+
run(f'./{cpp_exe}')
710+
return
711+
645712
pymupdf_dir_rel = gh_release.relpath(pymupdf_dir)
646713
if pytest_options is None:
647714
if valgrind:

0 commit comments

Comments
 (0)