Skip to main content

Python bindings for Quillmark - format-first Markdown rendering

Project description

Quillmark — Python bindings

Python bindings for Quillmark's format-first Markdown rendering engine.

Maintained by TTQ.

Installation

pip install quillmark

Quick Start

from quillmark import Quillmark, Quill, Document, OutputFormat

engine = Quillmark()                       # backend registry + render dispatcher
quill = Quill.from_path("path/to/quill")   # engine-free, validated config data

markdown = """~~~
$quill: my_quill
$kind: main
title: Hello World
~~~

# Hello
"""

parsed = Document.from_markdown(markdown)
result = engine.render(quill, parsed, OutputFormat.PDF)
result.artifacts[0].save("output.pdf")

API surface

The Python surface mirrors the @quillmark/wasm package. Names follow snake_case conventions; the underlying concepts (and shapes of return values) are the same.

Capability principle: a Quill is engine-free, validated config data — quill.metadata is a pure, infallible snapshot of the quill: section. Backend capability (supported_formats / supports_canvas) and rendering (render / open) are resolved by the engine, against a quill; they raise QuillmarkError (UnsupportedBackend) only if the declared backend isn't registered.

Quillmark

engine = Quillmark()
engine.registered_backends()              # ['typst']
engine.render(quill, parsed, OutputFormat.PDF)   # ppi=, pages=, producer= optional
engine.open(quill, parsed)                # RenderSession
engine.supported_formats(quill)           # [OutputFormat.PDF, ...] (raises if backend unregistered)
engine.supports_canvas(quill)             # True iff backend supports canvas preview (non-raising)

Quill

quill = Quill.from_path("path/to/quill")  # pure config load — no backend resolved here

quill.backend_id            # "typst" (declared backend)
quill.blueprint             # auto-generated annotated Markdown blueprint
quill.schema                # structured dict of the quill's document schema
quill.metadata              # pure config snapshot of the quill: section (never raises)
quill.quill_ref             # "name@version"

diags   = quill.validate(parsed)          # list of validation::* diagnostic dicts ([] = valid)
seed    = quill.seed_document()           # starter Document seeded from `example:` values
main    = quill.seed_main()               # just the $kind: main card (dict, like doc.main)
card    = quill.seed_card("note")         # one starter composable card (dict), None if kind undeclared

RenderSession

session = engine.open(quill, parsed)
session.page_count
session.backend_id
session.supports_canvas
session.warnings
session.render(OutputFormat.SVG, pages=[0])

RenderResult / Artifact

result.artifacts            # [Artifact, ...]
result.warnings             # [Diagnostic, ...]
result.format               # OutputFormat
result.render_time_ms       # float

artifact.format             # OutputFormat
artifact.bytes              # bytes
artifact.mime_type          # 'application/pdf', 'image/svg+xml', ...
artifact.save("out.pdf")

Document

doc = Document.from_markdown(markdown)
emitted = doc.to_markdown()

stored   = doc.to_json()
restored = Document.from_json(stored)
maybe    = Document.try_from_json(blob)          # None when not a DTO

Document.schema_version_of(blob)                 # raw tag (incl. unknown futures)
Document.current_schema_version()                # what this build writes

doc.clone()
doc.equals(other)
doc.card_count
doc.main; doc.cards; doc.body; doc.warnings

doc.set_field("title", "New")
doc.push_card(Document.make_card("note", {"x": 1}, "..."))  # or pass a Card from cards/remove_card/seed_card
# insert_card, remove_card, move_card, set_card_kind,
# update_card_field, remove_card_field, update_card_body, ...

Schema model

A field's cell is inferred from whether the schema declares a default::

  • Unendorsed (no default:) — the blueprint renders <must-fill>. An absent Unendorsed field is a non-fatal signal (validation::field_absent) — the render path zero-fills it silently. A surviving <must-fill> sentinel is fatal (validation::must_fill_sentinel). Partial documents are first-class; engine.render(quill, doc) only raises for malformed input.
  • Endorsed (with default:) — the blueprint renders the default value with a ; delete-ok annotation, and the default is used when the document omits the field.

There is no required: axis on FieldSchema.

Error contract

A single exception type — QuillmarkError — is raised for every failure mode. Every raised exception carries a non-empty .diagnostics list of Diagnostic objects. This matches the WASM binding's contract.

try:
    Document.from_markdown(bad_md)
except QuillmarkError as exc:
    for d in exc.diagnostics:
        print(d.severity, d.code, d.message, d.path)

EditError-shaped failures (invalid field names, kind names, out-of-range indices) prefix the message with [EditError::<Variant>] — the same format WASM uses — so callers can pattern-match on the message when they need to.

Changelog

See the changelog and the GitHub Releases page for release notes and version history.

Development

uv venv
uv pip install -e ".[dev]"
uv run pytest

License

Apache-2.0

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quillmark-0.89.1.tar.gz (2.5 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

quillmark-0.89.1-cp310-abi3-win_amd64.whl (14.0 MB view details)

Uploaded CPython 3.10+Windows x86-64

quillmark-0.89.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (15.1 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

quillmark-0.89.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (15.0 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

quillmark-0.89.1-cp310-abi3-macosx_11_0_arm64.whl (13.8 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file quillmark-0.89.1.tar.gz.

File metadata

  • Download URL: quillmark-0.89.1.tar.gz
  • Upload date:
  • Size: 2.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for quillmark-0.89.1.tar.gz
Algorithm Hash digest
SHA256 4cb10d0e9ec6eb11c965f8a3f73cfcbb21de5018700efdb0fa1d8d544624be49
MD5 5ea49856297a945fc28eb656764c34fb
BLAKE2b-256 9d0cf35936d26374d74720b2e103570c92006b55f86b7617087301b8d22edc6a

See more details on using hashes here.

Provenance

The following attestation bundles were made for quillmark-0.89.1.tar.gz:

Publisher: release.yml on quillmark-org/quillmark

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file quillmark-0.89.1-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: quillmark-0.89.1-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 14.0 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for quillmark-0.89.1-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 e5ade54ae5bd6c7caba0bdc7cae1129d0e5f944e1950e0ce3d975cbbceafd350
MD5 c96e81eb99464ff21d39b6e001909b90
BLAKE2b-256 8a70cbd8d102d0e84b4743ba4ef15c41f457f1c7466405b931769e3618f14e77

See more details on using hashes here.

Provenance

The following attestation bundles were made for quillmark-0.89.1-cp310-abi3-win_amd64.whl:

Publisher: release.yml on quillmark-org/quillmark

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file quillmark-0.89.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for quillmark-0.89.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 76229b04edbce7c351efd53338c63291e78eb8715f5076a0c6086f2076220022
MD5 7c3e093c5da8102240e55e3f6f5866ce
BLAKE2b-256 2878cb21eef8b0ef7010218129014240b1a0510d520b4463d22c7a2373f8af24

See more details on using hashes here.

Provenance

The following attestation bundles were made for quillmark-0.89.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on quillmark-org/quillmark

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file quillmark-0.89.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for quillmark-0.89.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 2d084f0e10448cb3bd6ff272680681ce79f90de9e5f8e8f20da601bc75278d1e
MD5 f3efe7391d71e1434a2609e46f52931a
BLAKE2b-256 b1cadcf2f3cb63c580c09e491c0bd3e4cedd90180306ef1494b012d2d9efe10c

See more details on using hashes here.

Provenance

The following attestation bundles were made for quillmark-0.89.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on quillmark-org/quillmark

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file quillmark-0.89.1-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for quillmark-0.89.1-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 55ba22b693bb74d9c10b5ec6fc14afb7d90af9d730fc6613a1f46bd50e2148f7
MD5 1fa02baa58fd36a5ca9ecf77578079db
BLAKE2b-256 b8dbd4c5001592727a9ff1387dc3ccdaba04cc55d9608f9a780db9ca431bf517

See more details on using hashes here.

Provenance

The following attestation bundles were made for quillmark-0.89.1-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on quillmark-org/quillmark

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page