Description
Every call to Page.get_texttrace() decrements the reference count of None more times than it increments it. On Python 3.11 (where None is not yet immortal) the deficit accumulates; once it exceeds None's base refcount (~12k), the interpreter dies with:
Fatal Python error: none_dealloc: deallocating None:
bug likely caused by a refcount error in a C extension
Long-running processes (batch conversion, large test suites) are affected; short single-document scripts usually stay under the threshold.
Minimal reproduction
import sys, pymupdf
doc = pymupdf.open()
page = doc.new_page()
for i in range(10):
page.insert_text((72, 72 + i * 20), f"Hello world span {i}")
before = sys.getrefcount(None)
for _ in range(200):
page.get_texttrace()
after = sys.getrefcount(None)
print("None refcount drift:", after - before) # expected 0
Observed output: None refcount drift: -2657 (≈ −13 per call for this page). Increase the loop count and the interpreter eventually crashes with none_dealloc. All other hot APIs we probed (get_text, get_pixmap, open/close, page_count) show a drift of exactly 0.
Details we measured
- The deficit scales with span count (≈ −4/call on an empty page, dozens on dense pages).
- Part of the deficit fires when the result is disposed, not at call time: at construction a legitimate INCREF(None) and an erroneous extra DECREF(None) cancel numerically, but the slot still holds a pointer to None — disposing the result later performs a real, uncompensated DECREF(None). So a simple before/after window around the bare call underestimates the leak.
Environment
- PyMuPDF 1.27.2.3 (MuPDF 1.27.2) — also reproduced on 1.28.0
- Python 3.11 (Windows 11 x64; also reproduced on ubuntu-latest CI)
Prior art
Looks like the same defect class as #2146 (fixed in 2023), now present in get_texttrace().
Happy to test a fix; we currently work around it with a measured compensation wrapper on our side.
Description
Every call to
Page.get_texttrace()decrements the reference count ofNonemore times than it increments it. On Python 3.11 (whereNoneis not yet immortal) the deficit accumulates; once it exceeds None's base refcount (~12k), the interpreter dies with:Long-running processes (batch conversion, large test suites) are affected; short single-document scripts usually stay under the threshold.
Minimal reproduction
Observed output:
None refcount drift: -2657(≈ −13 per call for this page). Increase the loop count and the interpreter eventually crashes with none_dealloc. All other hot APIs we probed (get_text,get_pixmap,open/close,page_count) show a drift of exactly 0.Details we measured
Environment
Prior art
Looks like the same defect class as #2146 (fixed in 2023), now present in
get_texttrace().Happy to test a fix; we currently work around it with a measured compensation wrapper on our side.