mag-parse
One parse() for every document. PDFs go to mag-pdf;
everything else goes to mag-file-handler.
from mag_parse import parse
r = parse("invoice.pdf") # quality="fast" (default)
r = parse("report.pdf", quality="accurate") # table structure
r = parse("deck.pptx") # quality does not apply
r = parse(pdf_bytes, filename="report.pdf") # bytes work too
r.markdown # always populated
r.text # always populated
r.format # "pdf" | "docx" | "xlsx" | "eml" | ...
r.engine # which engine actually ran
r.ok, r.error, r.warnings
The facade itself has zero required dependencies. Both engines are extras, and that is not tidiness — see Install.
Routing
Decided by magic bytes, never the extension. A .docx renamed to .pdf
does not reach the PDF engine, and a PDF with no extension does.
parse(file, quality=...)
│
sniff %PDF
│
┌────┴─────┐
PDF not PDF
│ │
mag-pdf mag-file-handler
│ │
└────┬───────┘
▼
one Result
quality is a PDF-only knob
| What runs | Install | |
|---|---|---|
"fast" (default) |
LiteParse behind an OCR gate; OCRs only pages that need it | mag-parse[pdf] |
"accurate" |
DocLayout-YOLO + TableFormer for real table structure | mag-parse[tables] |
For every other format quality is accepted and ignored, so a caller never
has to branch on file type. Result.quality is "" there — the knob did not
default, it does not exist.
"accurate"selects mag-pdf'sprofile="tables", leavingtable_modeat its default. Those are two different fast/accurate axes: the profile picks the pipeline,table_modepicks TableFormer's weights. The heavier weights measured 27.6 s against 16.1 s for one additional table row, so they are not what "accurate" buys — the table pipeline is.
Markdown, and how honest it is
Both .markdown and .text are always populated, on every path including
failure. They are not equally structured, and structured_markdown says which
you got:
| Source | .markdown |
structured |
|---|---|---|
| real GFM — pipe tables, headings | True |
|
| docx / xlsx / pptx / html / eml | real GFM — headings and pipe tables | True |
| md / txt / csv | the text, which is already faithful | False |
Measured on generated fixtures with known content: every format above recovers 100% of its title, prose, table cells and numeric values, and seven of the eight produce pipe tables a chunker can find. Plain text is the exception, and correctly so — there is no table in it to preserve.
Read result.structured, not the raw structured_markdown field. The
property additionally requires that an extraction succeeded and that the
markdown really does contain a heading or a table; the raw field is set by the
engine adapter and reported True even for an error result carrying no
markdown at all.
Errors
A bad document is a Result, never an exception — check .ok / .error.
Only two things raise, and both are the caller's to fix:
| Exception | Means |
|---|---|
UnknownQuality |
quality= was not "fast" or "accurate". Raised before any I/O, so a typo cannot run the wrong pipeline on a large file first. |
EngineNotAvailable |
The extra for this file type is not installed. Names the exact pip install. |
Result.status is "ok", "empty" or "error". empty is a failure — an
extraction that returns nothing while reporting success is the silent failure
both engines guard against, and the facade does not launder it.
HTTP server
pip install 'mag-parse[http,pdf,office]'
mag-parse-http --host 0.0.0.0 --port 8080
curl -sf -F file=@invoice.pdf -F quality=accurate localhost:8080/v1/parse
curl -sf -F file=@notes.docx localhost:8080/v1/parse -H 'Accept: text/markdown'
curl -s localhost:8080/readyz
| Endpoint | What it is for |
|---|---|
POST /v1/parse |
multipart: file, quality=fast|accurate, password? |
GET /livez |
process alive - restart only if this fails |
GET /readyz |
engines warm - stop routing if this fails |
GET /metrics |
Prometheus |
GET /version |
version, lanes, active limits |
A bad document is 200 with ok: false; a bad server is 4xx/5xx as
application/problem+json. X-Request-Id is echoed if you send one, minted if
you do not.
Set MAGPARSE_FAIL_ON_EMPTY=true to turn an extraction that produced no
content into a 422 rather than a 200 nobody notices.
Install
pip install 'mag-parse[pdf]' # PDFs; one dependency, Tesseract bundled
pip install 'mag-parse[office]' # docx, xlsx, pptx, eml, html, md, txt, csv
pip install 'mag-parse[all]' # both of the above
pip install 'mag-parse[tables]' # adds quality="accurate" (~2 GB: torch)
pip install 'mag-parse[all,http]' # + the HTTP server (`mag-parse-http`)
serve carries no engine on purpose - it is composed with whichever of the
above you need, so a PDF-only sidecar does not inherit the office stack's
platform limits.
The engines are extras rather than dependencies because extractous (under
mag-file-handler) publishes no Linux ARM64 wheel and declares
requires-python <3.14, while liteparse (under mag-pdf) has neither
limit. Making the office side required would impose both constraints on
PDF-only users:
| Platform | [pdf] |
[office] |
|---|---|---|
| Linux x86-64 | ✅ | ✅ |
| Linux ARM64 | ✅ | ❌ no wheel |
| macOS ARM64 / x86-64 | ✅ | ✅ |
| Windows x86-64 | ✅ | ✅ |
| Python 3.14 | ✅ | ❌ <3.14 |
Python 3.10+. Apache-2.0.
Metadata
Release files for mag-parse 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mag_parse-0.3.1.tar.gz | 44.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mag_parse-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 80.5 kB
Release files / mag_parse-0.3.1.tar.gz
| Download URL | mag_parse-0.3.1.tar.gz |
|---|---|
| Size | 44.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9e5850a31f05aa3f31043fd8ceece37b0272b0bbb82256393c7fad085dbd8629
|
|
BLAKE2b-256 checksum How to use checksums |
2dddcd0119c04cd2bfadb1e9696ba3a782cfd51caa7e970f0853c9be6ab2f35f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logRelease files / mag_parse-0.3.1-py3-none-any.whl
| Download URL | mag_parse-0.3.1-py3-none-any.whl |
|---|---|
| Size | 35.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
50982569021d7914f91fcfb41cddce4210652a4319082cb14f6ca2e17906fdef
|
|
BLAKE2b-256 checksum How to use checksums |
42cc2c30be86a70067ec50decd63192725e7dd72cc9a001a9622d33620654b45
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency log