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.90.0.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.90.0-cp310-abi3-win_amd64.whl (14.0 MB view details)

Uploaded CPython 3.10+Windows x86-64

quillmark-0.90.0-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.90.0-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.90.0-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.90.0.tar.gz.

File metadata

  • Download URL: quillmark-0.90.0.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.90.0.tar.gz
Algorithm Hash digest
SHA256 1530f684655302d0d49454fa84bd59fddb2d1ae4a6b7263fef9409254c3496c6
MD5 2cf2be56d5a339e57fee4c5028962870
BLAKE2b-256 0b2621c62560817c87f6c695a6ce73b8ef7a73a6c32f50f70039fc2516024763

See more details on using hashes here.

Provenance

The following attestation bundles were made for quillmark-0.90.0.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.90.0-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: quillmark-0.90.0-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.90.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 7c259d27b9c8a0588bb155c9a9daf593260530491677e95199b1cdaa7039cf7a
MD5 bcb4da0a15607ac3b4c9daddf91a2a22
BLAKE2b-256 fd1b38bda5050a05389e02adfb1caf134dcfb8ea94b59bf7c61cd2d9f102fbfb

See more details on using hashes here.

Provenance

The following attestation bundles were made for quillmark-0.90.0-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.90.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for quillmark-0.90.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 a2fe6b06f6e52611b7b22909899e69e15b6407d3b29259079080fe2e3a3b35ec
MD5 02d2aaba02950a3a72dc915edcede7f5
BLAKE2b-256 07f107170b4449abe1d7a9753005a68aed56ffcee948ba49e581c8d49bd03e10

See more details on using hashes here.

Provenance

The following attestation bundles were made for quillmark-0.90.0-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.90.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for quillmark-0.90.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 4a573ebc2bb2feb5d81d3a9c4cd86a8af7cab7d041e4f0d25fe25d72569f9549
MD5 5e03b34c6088999612811fb81bfb7b47
BLAKE2b-256 2f9578a39510f8b135ba0323e9e86f82cb12a0081ec3354c5fa1404e765a6b3c

See more details on using hashes here.

Provenance

The following attestation bundles were made for quillmark-0.90.0-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.90.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for quillmark-0.90.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 f90f9387bd25db64c34de78e3ba6ad26859d24b1c0be7a4b707329b99ae43387
MD5 dcb9c3a30dbbe179b3c1c67ce98f8d96
BLAKE2b-256 a6ea66ab61dfff61d62922f6b693e454b0dae0f0fd3c8d885fa971bf637aeb10

See more details on using hashes here.

Provenance

The following attestation bundles were made for quillmark-0.90.0-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