writers: Format Writers (typed Doc -> target text)#
base defines the Writer protocol/registry, the Capability contract the degradation engine
consults, and the target-native marker recovery seam required by ENCLAVE-capable extensions.
Markdown dialects drive mdformat through the token bridge; rST, Typst, Plane, and wikitext load
direct typed-tree renderers on demand. Importing this package keeps those direct writers lazy.
Format Writers (typed Doc -> target text).
base defines the Writer protocol/registry, the Capability contract the degradation engine
consults, and the target-native marker recovery seam required by ENCLAVE-capable extensions.
Markdown dialects drive mdformat through the token bridge; rST, Typst, Plane, and wikitext load
direct typed-tree renderers on demand. Importing this package keeps those direct writers lazy.
- class myform.writers.Capability(*values)#
How a writer can honor one node kind (the degradation engine’s contract).
NATIVErenders idiomatically with no loss;APPROXrenders via a lowering rule and is reported as approximated;CARRYcannot render the node itself but carries its attendant info into a replacement, reported as replaced;DROPcannot render at all and is dropped with a report entry – never silently.ENCLAVEis the fourth lossy fate and the only one that is not in-band: the node is preserved verbatim out of band, in its source form, and referenced in-band by a stable, addressable marker. Nothing is approximated and nothing is lost – the content is deferred to a carrier the target can point at, which is what makes a construct the target cannot express survivable rather than merely reported.Added in v1.1, after the surface published at v1.0.0. Third-party writers and rules that predate it never emit it, and consumers that cannot interpret it must fall back to
fallback(CARRY) rather than raising – an enclave is a carry that happens to keep its original.- property fallback: Capability#
The nearest v1.0.0 capability, for consumers that predate this member.
Every v1.0.0 member is its own fallback;
ENCLAVEdegrades toCARRY, which is the documented contract for extensions compiled against the older surface (spec D2).
- class myform.writers.GfmWriter#
Writer for GitHub-Flavored Markdown (the mdformat
gfmextension stack).- name: ClassVar[str] = 'gfm'#
The format name this writer targets (set by concrete subclasses).
- extensions: ClassVar[tuple[str, ...]] = ('myform_gfm', 'footnote')#
The mdformat parser-extension names whose renderers/postprocessors apply.
- capabilities: ClassVar[Mapping[str, Capability]] = {'blockBreak': Capability.DROP, 'definitionDescription': Capability.CARRY, 'definitionList': Capability.CARRY, 'definitionTerm': Capability.CARRY, 'embed': Capability.CARRY, 'frontMatter': Capability.CARRY, 'highlight': Capability.APPROX, 'ignored': Capability.NATIVE, 'inlineMath': Capability.APPROX, 'math': Capability.APPROX, 'mystComment': Capability.APPROX, 'mystDirective': Capability.APPROX, 'mystRole': Capability.CARRY, 'mystTarget': Capability.CARRY}#
Node
type->Capability; kinds not listed fall back todefault_capability.
- default_capability: ClassVar[Capability] = 1#
The capability assumed for node kinds absent from
capabilities.
- class myform.writers.MarkdownWriter#
Shared base for markdown-target writers (MyST, GFM): bridge, engine,
MDRenderer.- extensions: ClassVar[tuple[str, ...]] = ()#
The mdformat parser-extension names whose renderers/postprocessors apply.
- write(doc: Doc, options: Mapping[str, Any] | None = None) str#
Render
docthrough the token bridge and mdformat’sMDRenderer.The document’s
envsnapshot is deep-copied into the render so reference links and other document-level state survive, and repeated writes stay independent. This is the exact render pathmdformat.textdrives, which is what makes formatting (convert(text, f, f)) a byte-for-byte fixed point.
- class myform.writers.MystWriter#
Writer for MyST markdown (the mdformat
mystextension stack); all-NATIVE.- name: ClassVar[str] = 'myst'#
The format name this writer targets (set by concrete subclasses).
- extensions: ClassVar[tuple[str, ...]] = ('myst',)#
The mdformat parser-extension names whose renderers/postprocessors apply.
- capabilities: ClassVar[Mapping[str, Capability]] = {'delete': Capability.APPROX, 'embed': Capability.CARRY, 'highlight': Capability.APPROX}#
the mdformat
myststack has nosrenderer, so a foreign-sourceddelete(e.g. from GFM) must be lowered (the'*'deleterule unwraps it to its retained children) rather than crashMDRendererwithKeyError: 's'. MyST source never emitsdelete, so identity holds.- Type:
MyST is the model’s home format, so every kind is NATIVE except strikethrough
- default_capability: ClassVar[Capability] = 1#
The capability assumed for node kinds absent from
capabilities.
- class myform.writers.ObsidianWriter#
Writer for Obsidian-flavored Markdown (GFM + footnotes + properties + the dialect).
- name: ClassVar[str] = 'obsidian'#
The format name this writer targets (set by concrete subclasses).
- extensions: ClassVar[tuple[str, ...]] = ('gfm', 'footnote', 'front_matters', 'obsidian')#
The mdformat parser-extension names whose renderers/postprocessors apply.
- capabilities: ClassVar[Mapping[str, Capability]] = {'blockBreak': Capability.DROP, 'definitionDescription': Capability.CARRY, 'definitionList': Capability.CARRY, 'definitionTerm': Capability.CARRY, 'inlineMath': Capability.APPROX, 'math': Capability.APPROX, 'mystRole': Capability.CARRY, 'mystTarget': Capability.CARRY}#
Node
type->Capability; kinds not listed fall back todefault_capability.
- default_capability: ClassVar[Capability] = 1#
The capability assumed for node kinds absent from
capabilities.
- class myform.writers.PlaneWriter#
Writer for Plane’s Tiptap
description_html(direct typed-model walk).- name: ClassVar[str] = 'plane'#
The format name this writer targets (set by concrete subclasses).
- capabilities: ClassVar[Mapping[str, Capability]] = {'blockBreak': Capability.APPROX, 'definitionDescription': Capability.CARRY, 'definitionList': Capability.CARRY, 'definitionTerm': Capability.CARRY, 'embed': Capability.CARRY, 'footnoteDefinition': Capability.CARRY, 'footnoteReference': Capability.CARRY, 'frontMatter': Capability.CARRY, 'highlight': Capability.APPROX, 'ignored': Capability.CARRY, 'inlineMath': Capability.CARRY, 'inlineTypstRaw': Capability.APPROX, 'inlineUnknown': Capability.CARRY, 'math': Capability.CARRY, 'mystComment': Capability.CARRY, 'mystDirective': Capability.APPROX, 'mystRole': Capability.APPROX, 'mystTarget': Capability.CARRY, 'typstRaw': Capability.APPROX, 'unknown': Capability.CARRY}#
Node
type->Capability; kinds not listed fall back todefault_capability.
- default_capability: ClassVar[Capability] = 1#
The capability assumed for node kinds absent from
capabilities.
- write(doc: Doc, options: Mapping[str, Any] | None = None) str#
Render
docas Plane’sdescription_htmlwire format.- Parameters:
doc – The typed source document (already lowered by the degradation engine).
options – Unused; the dialect has no writer-tunable knobs today.
- Returns:
HTML wrapped in a single
<div>(Plane’s own payloads vary on this – a bare single-paragraph description omits it – but the reader accepts both shapes, so the writer always emits the unambiguous form).- Raises:
UnsupportedPlaneNodeError – If an opaque or unsupported unlowered node reaches the writer.
- class myform.writers.RstWriter#
Writer for Sphinx reStructuredText (direct model walk).
- name: ClassVar[str] = 'rst'#
The format name this writer targets (set by concrete subclasses).
- capabilities: ClassVar[Mapping[str, Capability]] = {'blockBreak': Capability.APPROX, 'delete': Capability.APPROX, 'embed': Capability.CARRY, 'frontMatter': Capability.CARRY, 'highlight': Capability.APPROX, 'ignored': Capability.CARRY, 'inlineUnknown': Capability.CARRY, 'unknown': Capability.CARRY}#
Node
type->Capability; kinds not listed fall back todefault_capability.
- default_capability: ClassVar[Capability] = 1#
The capability assumed for node kinds absent from
capabilities.
- write(doc: Doc, options: Mapping[str, Any] | None = None) str#
Render
docas deterministic Sphinx reStructuredText.- Parameters:
doc – The typed document to render.
options – Writer options.
heading_underline_laddermay be given directly or in anrstsub-mapping; the default is#*=-^".
- Returns:
Canonical rST ending in exactly one newline (or
''for an empty document).- Raises:
UnsupportedRstNodeError – If degradation has not lowered a non-native construct.
ValueError – If the heading ladder option is invalid.
- class myform.writers.TypstWriter#
Writer for compiler-valid Typst markup (direct typed-model walk).
- name: ClassVar[str] = 'typst'#
The format name this writer targets (set by concrete subclasses).
- capabilities: ClassVar[Mapping[str, Capability]] = {'blockBreak': Capability.APPROX, 'embed': Capability.CARRY, 'frontMatter': Capability.CARRY, 'highlight': Capability.APPROX, 'ignored': Capability.CARRY, 'inlineTypstRaw': Capability.APPROX, 'inlineUnknown': Capability.CARRY, 'mystDirective': Capability.APPROX, 'mystRole': Capability.CARRY, 'typstRaw': Capability.APPROX, 'unknown': Capability.CARRY}#
Node
type->Capability; kinds not listed fall back todefault_capability.
- default_capability: ClassVar[Capability] = 1#
The capability assumed for node kinds absent from
capabilities.
- write(doc: Doc, options: Mapping[str, Any] | None = None) str#
Render
docas deterministic Typst source.- Parameters:
doc – The typed source document.
options – Writer options, either direct (
postformat/prelude) or nested undertypstas emitted byMyformConfig.
- Returns:
Self-contained Typst source, including an inlined prelude when explicitly requested.
- Raises:
UnsupportedTypstNodeError – If an opaque or unsupported unlowered node reaches the writer.
TypstPostformatError – If required normalization is unavailable or formatting fails.
- class myform.writers.WikitextWriter#
Writer for deterministic, bounded MediaWiki wikitext.
- name: ClassVar[str] = 'wikitext'#
The format name this writer targets (set by concrete subclasses).
- capabilities: ClassVar[Mapping[str, Capability]] = {'blockBreak': Capability.APPROX, 'blockquote': Capability.NATIVE, 'break': Capability.NATIVE, 'code': Capability.NATIVE, 'definitionDescription': Capability.NATIVE, 'definitionList': Capability.NATIVE, 'definitionTerm': Capability.NATIVE, 'delete': Capability.NATIVE, 'embed': Capability.CARRY, 'emphasis': Capability.NATIVE, 'footnoteDefinition': Capability.NATIVE, 'footnoteReference': Capability.NATIVE, 'frontMatter': Capability.CARRY, 'heading': Capability.NATIVE, 'highlight': Capability.NATIVE, 'ignored': Capability.CARRY, 'image': Capability.NATIVE, 'inlineCode': Capability.NATIVE, 'inlineMath': Capability.NATIVE, 'inlineTypstRaw': Capability.CARRY, 'inlineUnknown': Capability.CARRY, 'link': Capability.NATIVE, 'list': Capability.NATIVE, 'listItem': Capability.NATIVE, 'math': Capability.NATIVE, 'mystComment': Capability.NATIVE, 'mystDirective': Capability.CARRY, 'mystRole': Capability.CARRY, 'mystTarget': Capability.NATIVE, 'paragraph': Capability.NATIVE, 'root': Capability.NATIVE, 'strong': Capability.NATIVE, 'table': Capability.NATIVE, 'tableCell': Capability.NATIVE, 'tableRow': Capability.NATIVE, 'text': Capability.NATIVE, 'thematicBreak': Capability.NATIVE, 'typstRaw': Capability.CARRY, 'unknown': Capability.CARRY}#
Node
type->Capability; kinds not listed fall back todefault_capability.
- default_capability: ClassVar[Capability] = 3#
The capability assumed for node kinds absent from
capabilities.
- write(doc: Doc, options: Mapping[str, Any] | None = None) str#
Render
docas deterministic MediaWiki wikitext.- Parameters:
doc – The typed document to render.
options – Shared per-format options. The writer currently has no rendering knobs; reader-only values such as
titleand shared formatting values are ignored.
- Returns:
Canonical wikitext ending in one newline, or
''for an empty document.- Raises:
UnsupportedWikitextNodeError – If degradation has not lowered an unsupported node or a content-sensitive shape cannot be represented faithfully.
- class myform.writers.Writer#
The writer seam: render a typed
Docas one target format.- REGISTRY: ClassVar[dict[str, Writer]] = {'gfm': <myform.writers.gfm.GfmWriter object>, 'myst': <myform.writers.myst.MystWriter object>, 'obsidian': <myform.writers.obsidian.ObsidianWriter object>, 'plane': <myform.writers.plane.PlaneWriter object>, 'rst': <myform.writers.rst.RstWriter object>, 'typst': <myform.writers.typst.TypstWriter object>, 'wikitext': <myform.writers.wikitext.WikitextWriter object>}#
Format name -> ready-to-use writer instance; populated by
register.
- name: ClassVar[str] = ''#
The format name this writer targets (set by concrete subclasses).
- capabilities: ClassVar[Mapping[str, Capability]] = {}#
Node
type->Capability; kinds not listed fall back todefault_capability.
- default_capability: ClassVar[Capability] = 1#
The capability assumed for node kinds absent from
capabilities.
- classmethod register(writer_cls: type[W]) type[W]#
Class decorator registering a concrete writer under its
name.
- classmethod get(name: str) Writer#
Resolve a writer, importing direct backends only when first selected.
- classmethod capability(kind: str) Capability#
Look up this writer’s capability for one node
type.
- write(doc: Doc, options: Mapping[str, Any] | None = None) str#
Render
docas this writer’s format (concrete writers implement this).
- find_enclave_markers(text: str, options: Mapping[str, Any] | None = None) tuple[Link, ...]#
Recover target-native enclave marker links from final rendered text.
Writers that declare
Capability.ENCLAVEmust override this method with a parser for their navigable target syntax. Returning raw path-substring matches does not satisfy the contract; the result is typed evidence that the final target still contains a link.
- enclave_marker_options(options: Mapping[str, Any] | None = None) dict[str, Any]#
Select the non-sensitive options needed to recover enclave markers.
Conversion artifacts are durable and routinely serialized, so the complete writer option mapping is never retained implicitly. An ENCLAVE-capable extension whose marker parser depends on options must override this method and copy only its JSON-safe, marker-relevant settings.