Guess headings from font size.
Parsers
A parser is the first axis in the grid. It turns a SourceFile — raw
bytes plus a media type — into a ParsedDocument: text, plus the blocks (headings,
paragraphs, tables) the parser found in it. Every later axis works on that text, so a bad
parser choice caps everything downstream before chunking even starts.
Resolve one by name with contextgrid.get_parser:
import contextgrid as cg
parser = cg.get_parser("markdown")print(repr(parser))MarkdownParser()The spec grammar
A parser spec is a name, optionally followed by a colon and arguments:
"pymupdf" # defaults"pymupdf:detect_headings=False,margin_ratio=0.08" # keyword argumentsOnly two parsers have a shorthand — a bare value right after the colon, with no
key= in front of it: agno (shorthand fills reader) and marker (shorthand fills
languages). Every other parser rejects a bare value:
import contextgrid as cg
try: cg.get_parser("pymupdf:512")except Exception as e: print(type(e).__name__, e)ContextGridError '512' in 'pymupdf:512' must be written as key=valueAsking for a name that was never a real parser raises UnknownPluginError, which lists every
registered name so a typo is easy to spot:
import contextgrid as cg
try: cg.get_parser("unstructured")except Exception as e: print(type(e).__name__, e)UnknownPluginError no parser named 'unstructured'. Available: agno, docling, markdown, marker, pdfplumber, pymupdf, pymupdf4llm, textunstructured was planned once and never built — if you see it mentioned anywhere older, it
does not exist in the current package.
The 8
| name | good for | extra | shorthand |
|---|---|---|---|
text | Plain text, split into paragraphs on blank lines. No dependencies. | — | — |
markdown | Markdown with headings, code, lists, quotes and tables identified. No dependencies. | — | — |
pymupdf | Fast PDF text extraction, no table awareness. The speed baseline. | parse | — |
pdfplumber | Slower PDF extraction that finds tables, in reading order. | parse | — |
docling | IBM’s layout and table-structure models. Also reads DOCX, PPTX, HTML — the widest format coverage. | parse-ml | — |
marker | Surya layout and OCR, 90+ languages. The most faithful, and roughly 100x slower than a text extractor. | parse-marker | languages |
agno | Text via agno’s reader framework — what most RAG stacks reach for by default without choosing a parser at all. | agent | reader |
pymupdf4llm | Markdown output from the same extraction engine as pymupdf. Isolates whether Markdown structure helps, apart from extraction quality. | parse | — |
Only text and markdown need nothing installed. Every PDF-capable parser needs at least the
parse extra: pip install "context-grid[parse]" covers pymupdf, pdfplumber and
pymupdf4llm; docling and marker each need their own heavier extra.
Arguments and real defaults
Every parser below is a real, verified cg.get_parser(...) call and its printed repr.
text — contextgrid.parse.TextParser
No fields at all.
import contextgrid as cg
print(repr(cg.get_parser("text")))TextParser()markdown — contextgrid.parse.MarkdownParser
No fields at all.
import contextgrid as cg
print(repr(cg.get_parser("markdown")))MarkdownParser()pymupdf — contextgrid.parse.pymupdf.PyMuPDFParser
detect_headingsbooldefault Truemargin_ratiofloatdefault 0.0Strips top and bottom page bands as a fraction of page height (repeated headers and
footers) — not a size in points or pixels, despite reading like one. 0.0 means nothing is
stripped.
import contextgrid as cg
print(repr(cg.get_parser("pymupdf")))print(repr(cg.get_parser("pymupdf:detect_headings=False,margin_ratio=0.08")))PyMuPDFParser(detect_headings=True, margin_ratio=0.0)PyMuPDFParser(detect_headings=False, margin_ratio=0.08)pdfplumber — contextgrid.parse.pdfplumber.PDFPlumberParser
extract_tablesbooldefault TrueDetect and render tables.
table_formatstrdefault "pipe"One of "pipe", "tsv", "plain". Anything else raises DocumentError, but not until a
table is actually hit during parsing — a typo here does not fail at construction time.
import contextgrid as cg
print(repr(cg.get_parser("pdfplumber")))PDFPlumberParser(extract_tables=True, table_format='pipe')docling — contextgrid.parse.layout.DoclingParser
table_structurebooldefault TrueRun docling’s table-structure model.
ocrbooldefault FalseRun OCR. Turning ocr=False on a text-layer PDF recovers most of the speed for no quality
loss on that corpus — but on a scanned PDF, ocr=False silently returns nothing
extractable. table_structure and ocr are separate cost toggles; each one you turn off
buys speed on the documents it doesn’t apply to and loses content on the ones it does.
import contextgrid as cg
print(repr(cg.get_parser("docling")))DoclingParser(table_structure=True, ocr=False)marker — contextgrid.parse.layout.MarkerParser
languagesstrdefault "en"The shorthand parameter — marker:fr means languages="fr".
use_llmbooldefault FalseHave a model clean up marker’s output.
import contextgrid as cg
print(repr(cg.get_parser("marker")))MarkerParser(languages='en', use_llm=False)agno — contextgrid.parse.layout.AgnoParser
readerstrdefault "auto"The shorthand parameter. "auto" picks a reader by file extension; its PDF path needs
pypdf separately from the agent extra — missing it raises DocumentError naming
pip install 'context-grid[agent]', a different exception type than every other
missing-dependency path among the parsers.
import contextgrid as cg
print(repr(cg.get_parser("agno")))AgnoParser(reader='auto')pymupdf4llm — contextgrid.parse.layout.PyMuPDF4LLMParser
page_chunksbooldefault TrueKeep page boundaries in the output.
table_strategystrdefault "lines_strict"Passed straight through to pymupdf4llm’s own table detection.
isolatebooldefault TrueRuns each document in its own subprocess. This is not a performance knob to casually turn
off: pymupdf4llm’s C-layer state leaks across documents converted in the same interpreter.
The module’s own docstring records a measured effect on its fixtures — a prose PDF that
parses to 1182 clean characters alone parses to 919 mangled ones once a table PDF has gone
through the same process first. isolate=False re-enables that failure silently, for about
a 0.1s/doc speedup.
import contextgrid as cg
print(repr(cg.get_parser("pymupdf4llm")))PyMuPDF4LLMParser(page_chunks=True, table_strategy='lines_strict', isolate=True)Using one on a real file
Every PDF parser raises DocumentError if the SourceFile it’s given has no bytes loaded —
loading the extra is necessary but not sufficient. Load your files through
Corpus, which reads the bytes for you:
The directory below is called parser-demo rather than corpus on purpose: running this
block writes a file into whatever directory you are sitting in, and a stray policy.md dropped
into a real corpus quietly joins it and shows up in every sweep you run afterwards.
from pathlib import Path
Path("parser-demo").mkdir(exist_ok=True)Path("parser-demo/policy.md").write_text( "# Termination\n\n" "## Notice period\n\n" "Either party may end this agreement with thirty days written notice, or " "immediately by mutual written agreement.\n\n" "## Refunds\n\n" "Refunds are issued within 14 days of a valid cancellation, minus a processing " "fee, credited to the original payment method or a different account on " "request.\n\n" "## Data retention\n\n" "Account data is retained for 90 days after cancellation, then permanently " "deleted from every backup and log we keep.\n")import contextgrid as cg
corpus = cg.Corpus.from_dir("parser-demo") # the directory written just aboveparser = cg.get_parser("markdown")doc = parser.parse(corpus.files[0])
print(type(doc).__name__, doc.id)print(repr(doc.text[:80]))ParsedDocument policy.md'# Termination\n\n## Notice period\n\nEither party may end this agreement with thirty'Parsing an empty SourceFile directly shows the failure mode. This one block needs
pip install "context-grid[parse]", because pymupdf is one of the six parsers that need an
extra — on a bare install you get MissingExtraError naming the extra instead, which is the
earlier of the two failures and not the one being shown here:
from contextgrid.core.documents import SourceFile, MediaTypeimport contextgrid as cg
parser = cg.get_parser("pymupdf") # needs: pip install "context-grid[parse]"empty = SourceFile(id="no-bytes.pdf", media_type=MediaType.PDF)try: parser.parse(empty)except Exception as e: print(type(e).__name__, e)DocumentError source file 'no-bytes.pdf' has no bytes loaded. Read the file before parsing it.How to choose
Start with markdown or pymupdf
markdown for Markdown sources, pymupdf for PDFs — both are free, fast, and good enough to
get a first sweep running before you spend anything on a heavier parser.
Add pdfplumber or docling if your PDFs have tables
pymupdf doesn’t see table structure at all. pdfplumber is slower but finds tables in
reading order; docling goes further with a dedicated table-structure model and also reads
DOCX, PPTX and HTML.
Reach for marker only when fidelity matters more than speed
Scanned documents, non-Latin scripts, or layouts other parsers mangle. Budget for it — it’s roughly 100x slower and downloads model weights on first use.
Put the parser on the grid and let recall tell you
Sweep two or three candidates against your eval set rather than guessing:
parser: [markdown, pymupdf, docling] in a config file. See Running.
Have a parser for a format none of these cover? See Writing a Custom Plugin
for the Parser protocol and how to register one.