Datagrunt 5.3.6: Single-Pass Object Walk, Fast Trusted Constructors, and Single-Load Corpus Renders
Datagrunt 5.3.6 is officially available! This performance-focused release dramatically accelerates PDF extraction, page background rendering, and large-scale batch corpus ingestion (#488).
By unifying PDFium object tree traversals, introducing zero-overhead internal segment constructors, and fusing page stage extraction with background rendering in a single load pass, Datagrunt 5.3.6 significantly reduces wall-clock extraction time and memory overhead across large document sets.
Key Highlights in Datagrunt 5.3.6
- Single-Pass PDFium Object Tree Walk (
pdfium_engine.py) — Replaces redundant page traversals (text extraction, vector stroke segment compilation, image inspection, and render refusal checks) with a single-pass object walk (collect_images_and_refusal/_read_stages_with_guard). - Fast Trusted Segment Constructors (
_path_segments.py) — IntroducesRawSegment._trustedandPageSegments._trustedclass constructors for engine-validated path segments, bypassing redundant $O(N)$ type checks and finite-float validation during dense vector extraction. - Single-Load Fused Page Renders (
_page_worker.py) — Eliminates repeated page handle re-opening. PDF pages are loaded once to extract document stages, assess OCR/vision routes, draw background raster images, and capture full-page pre-render PNGs in a single pass (_assessed_fused_single_load). - Bounded In-Memory Prerender Cache (
_options.py) — Adds_PrerenderCache, a 128 MB bounded cache with pop-on-write eviction that preserves single-load full-page PNG bytes between extraction and pipeline stages without re-drawing. - CI Runner Resiliency (
.github/workflows/ci.yml) — Adds automatic fallback to GPG-verifiedmirrors.edge.kernel.orgmirrors to protect CI builds against Azure runner DNS outages on Ubuntu package archives.
1. Single-Pass PDFium Object Walk (#488)
The Overhead of Multi-Pass Traversals
In earlier releases, extracting a PDF page required multiple traversals over the underlying C/PDFium page object tree:
- Traversing top-level and form objects for text spans and character boxes.
- Traversing path objects to assemble vector strokes and fill segments.
- Traversing image objects to gather dimensions and stream metadata.
- Traversing image objects a second time to test for render refusal guards.
For complex PDFs—such as CAD schematics or multi-page financial statements containing thousands of vector paths and inline images—the repeated context switching across Python and the underlying PDFium C library introduced measurable CPU overhead.
Unified Object Tree Walking
Datagrunt 5.3.6 unifies image collection and render refusal checking into collect_images_and_refusal(), integrated directly into _read_stages_with_guard() inside src/datagrunt/pdf/pdfium_engine.py:
# Internal engine architecture: single-pass traversal
# Collects text, path segments, images, and first render refusal in ONE object walk
page_images, first_refusal = collect_images_and_refusal(
placed_images, page_raw, page_box, to_display
)This single-pass architecture halves C API traversal calls per page while preserving identical page stage isolation and error handling bounds.
2. Single-Load Fused Page Renders & Prerender Cache (#488)
Eliminating Duplicate Page Loads
When processing batch document corpora with extract_corpus() or running vision/OCR workflows, pages previously underwent multiple open/close cycles: one for text/segment stage assessment, and another for drawing background raster layers or OCR page rendering.
Datagrunt 5.3.6 introduces _assessed_fused_single_load() in src/datagrunt/pdf/_page_worker.py and _PrerenderCache in src/datagrunt/pdf/_options.py. When _can_fuse_page_load(options) is active:
- The page is opened once via
_eng._load_page(handle, index). - Text, vector segments, and images are extracted.
- If background rendering or OCR pre-rendering is required, 8-bit grey PNG pre-renders and background rasters are drawn immediately on the active
pdfium.PdfPagehandle. - Pre-rendered PNG bytes are stored in
_PrerenderCache(bounded at 128 MB by default) and popped on write, releasing memory instantly as downstream pipeline workers consume them.
from datagrunt.pdf.corpus import extract_corpus, load_corpus
# Batch extract a directory of multi-page PDFs using fused single-load rendering
extract_corpus(
sources="input_documents/",
output_dir="corpus_output/",
ocr="auto",
workers=8,
)
# Load the verified corpus manifest and query extracted bundles
manifest = load_corpus("corpus_output/")
print(f"Extracted {len(manifest.entries)} documents successfully.")3. Fast Trusted Constructors for Dense Vector Paths (#488)
PDF documents with heavy vector graphics (such as CAD architectural drawings, spatial maps, or charts) contain thousands of RawSegment instances per page.
Previously, RawSegment.__init__ performed scalar type checking, bounds validation (start < end), finite number checks (math.isfinite), and tuple verification for every single path segment. In 5.3.6, internal engine functions use RawSegment._trusted() and PageSegments._trusted(), which construct frozen dataclass instances directly via object.__new__ after the engine has already validated finite coordinates. This removes validation bottlenecks during heavy vector extraction.
Summary of Changes in 5.3.6
| File | Changes | Description |
|---|---|---|
src/datagrunt/pdf/pdfium_engine.py |
+669 / -85 | Unified single-pass object tree walk (_read_stages_with_guard, collect_images_and_refusal). |
src/datagrunt/pdf/_page_worker.py |
+172 / -15 | Implemented _assessed_fused_single_load and _capture_page_prerender for fused page rendering. |
src/datagrunt/pdf/_options.py |
+55 / -0 | Added _PrerenderCache bounded in-memory PNG cache with pop-on-write eviction. |
src/datagrunt/pdf/_path_segments.py |
+76 / -12 | Added fast trusted constructors RawSegment._trusted and PageSegments._trusted. |
src/datagrunt/pdf/_pdfium_images.py |
+35 / -15 | Added collect_images_and_refusal for single-pass image gathering & refusal checks. |
src/datagrunt/pdf/corpus.py & bundle.py |
+285 / -65 | Integrated single-load fused page renders and prerender caching into corpus/bundle workflows. |
tests/pdf_tests/test_speed_invariants.py |
+513 / -0 | Added performance speed invariant test suite for single-pass walks and fused loads. |
.github/workflows/ci.yml |
+40 / -4 | Added GPG-verified Ubuntu kernel mirror fallback for Azure runner DNS outages. |
Getting Started
To upgrade to Datagrunt 5.3.6 using uv:
uv pip install --upgrade datagruntFor PDF parsing support:
uv pip install --upgrade "datagrunt[pdf]"