Skip to content

Python API

The oxydemark package is the frozen public Python surface: everything listed in oxydemark.__all__, and nothing else. The native oxydemark._core module is an implementation detail and must not be imported directly.

Engine and plugins

OxydeEngine

OxydeEngine(plugins: list[Plugin] | None = None)

Pipeline-oriented Markdown engine with plugin support.

The full pipeline is:

Markdown Input
    -> preprocess plugins (text-level)
    -> Rust parser / rushdown (AST generation)
    -> transform plugins (AST-level)
    -> Rust renderer (HTML generation)
    -> postprocess plugins (HTML-level)
    -> Final Output

Attributes:

Name Type Description
plugins list[Plugin]

The ordered plugin list the engine runs.

Example

engine = OxydeEngine() html = engine.render("# Hello")

Build an engine.

Parameters:

Name Type Description Default
plugins list[Plugin] | None

An ordered sequence of plugin instances. Each plugin may implement preprocess, transform, and/or postprocess hooks.

None

render

render(markdown: str) -> str

Run the full pipeline: preprocess, parse, transform, render, postprocess.

Parameters:

Name Type Description Default
markdown str

The Markdown source to render.

required

Returns:

Type Description
str

The final HTML, after every plugin hook has run.

Plugin module-attribute

Any object satisfying at least one of the three plugin hooks.

Plugins may implement any combination of the hooks:

  • preprocess (Preprocessor): transform raw Markdown text before parsing.
  • transform (Transformer): modify the AST between parsing and rendering.
  • postprocess (Postprocessor): transform rendered HTML after rendering.

All hooks are optional; implement only the ones you need. Plugin is therefore a union of the three single-hook protocols rather than a protocol requiring all three -- most real plugins, including every one in oxydemark.contrib, implement exactly one.

The protocols are structural and hooks are dispatched by hasattr, never by isinstance. A plugin does not need to inherit from or register with anything -- any object exposing at least one correctly named hook is a valid plugin.

See oxydemark.contrib for worked examples and the plugin authoring guide for the full picture.

Hook ordering

OxydeEngine runs one phase at a time across the whole plugin list, not one plugin at a time. For plugins [A, B] the call order is A.preprocess, B.preprocess, parse, A.transform, B.transform, render, A.postprocess, B.postprocess.

Choosing the right hook
Hook Operates on Use it when
preprocess raw Markdown str the construct is not representable in the AST yet (custom markers, macros, includes, front-matter tweaks).
transform AstNode tree you need structure: adding, removing, re-typing or annotating nodes.
postprocess rendered HTML str the change is purely presentational and applies to the final markup.
Escaping rules worth knowing
  • AstNode.text is HTML-escaped by the renderer.
  • Raw HTML present in the source Markdown is stripped: the parser replaces it with a <!-- raw HTML omitted --> placeholder, which is what the resulting "raw_html" / "html_block" nodes carry as their text.
  • A node of kind "raw_html" created by a plugin emits its text verbatim; it is the only supported way to inject markup from a transform hook.

Preprocessor

Bases: Protocol

Plugin protocol for the text-level preprocess hook.

preprocess

preprocess(markdown: str) -> str

Transform raw Markdown before it reaches the Rust parser.

Parameters:

Name Type Description Default
markdown str

The Markdown source as produced by the previous plugin.

required

Returns:

Type Description
str

The rewritten Markdown source.

Transformer

Bases: Protocol

Plugin protocol for the AST-level transform hook.

transform

transform(ast: AstNode) -> AstNode

Transform the AST between parsing and rendering.

AstNode has value semantics

node.children is a PyO3 getter that returns a fresh copy of the child list, and each element in it is a copy too. Consequently:

node.children[0].text = "x"   # silently discarded
node.children.append(other)   # silently discarded

Mutations must be applied to a local copy which is then reassigned:

def transform(self, ast):
    self._modify(ast)
    return ast

def _modify(self, node):
    if node.kind == "text" and node.text:
        node.text = node.text.upper()
    children = node.children      # copy out
    for child in children:
        self._modify(child)
    node.children = children      # write back -- mandatory

The same applies to AstNode.walk, which yields copies: it is useful for inspection only, never for mutation.

See oxydemark.contrib for complete, tested examples.

Parameters:

Name Type Description Default
ast AstNode

The root of the tree as produced by the previous plugin.

required

Returns:

Type Description
AstNode

The (possibly modified) tree.

Postprocessor

Bases: Protocol

Plugin protocol for the HTML-level postprocess hook.

postprocess

postprocess(html: str) -> str

Transform rendered HTML after it leaves the Rust renderer.

Parameters:

Name Type Description Default
html str

The HTML as produced by the previous plugin.

required

Returns:

Type Description
str

The rewritten HTML.

Parsing and rendering

parse

parse(markdown: str) -> AstNode

Parse Markdown input into an AST node tree.

Parameters:

Name Type Description Default
markdown str

The Markdown source to parse.

required

Returns:

Type Description
AstNode

The root node of the parsed tree.

parse_document

parse_document(markdown: str) -> ParseResult

Parse Markdown and compute structured, typed metadata (OMEP-0010).

Parameters:

Name Type Description Default
markdown str

The Markdown source to parse.

required

Returns:

Type Description
ParseResult

The AST tree bundled with the flat heading list, the nested

ParseResult

table-of-contents tree, the summary HTML and typed YAML frontmatter.

render_ast

render_ast(node: AstNode) -> str

Render an AST node tree to an HTML string.

Parameters:

Name Type Description Default
node AstNode

The root node to render.

required

Returns:

Type Description
str

The rendered HTML.

markdown_to_html

markdown_to_html(markdown: str) -> str

Convert Markdown directly to HTML, the fast path with no AST exposure.

Parameters:

Name Type Description Default
markdown str

The Markdown source to convert.

required

Returns:

Type Description
str

The rendered HTML.

Metadata helpers

slugify

slugify(text: str, existing: list[str] | None = None) -> str

Generate a URL-friendly anchor slug from a text (OMEP-0010).

Parameters:

Name Type Description Default
text str

The label to derive the slug from.

required
existing list[str] | None

Already-assigned slugs, used to de-duplicate by appending a numeric suffix.

None

Returns:

Type Description
str

A unique, URL-friendly slug.

extract_summary

extract_summary(markdown: str) -> str | None

Extract the rendered-HTML summary before a top-level <!-- more --> delimiter.

Parameters:

Name Type Description Default
markdown str

The Markdown source to inspect.

required

Returns:

Type Description
str | None

The rendered summary HTML, or None when the document has no top-level

str | None

delimiter.

AST and metadata types

AstNode

AstNode(kind: str, children: list[AstNode] | None = None, text: str | None = None, attributes: dict[str, str] | None = None, metadata: dict[str, str] | None = None)

A tree-based AST node representing a Markdown element.

Attributes:

Name Type Description
kind str

The node type, such as "paragraph", "text" or "heading".

children list[AstNode]

Direct child nodes. Reading this attribute returns a copy; mutations must be written back to take effect.

text str | None

Literal text content, for leaf nodes that carry any.

attributes dict[str, str]

Rendered HTML attributes, as strings.

metadata dict[str, str] | None

Deprecated (OMEP-0010). Stringly-typed frontmatter, retained for backward compatibility. Use parse_document and its frontmatter property for typed access instead.

Build a node.

Parameters:

Name Type Description Default
kind str

The node type.

required
children list[AstNode] | None

Direct child nodes.

None
text str | None

Literal text content.

None
attributes dict[str, str] | None

Rendered HTML attributes.

None
metadata dict[str, str] | None

Deprecated stringly-typed frontmatter (OMEP-0010).

None

props property

props: dict[str, object] | None

Typed block-component props from a leading YAML block (OMEP-0007).

Present only on block_component nodes that declare a YAML block, and None otherwise. Values preserve their native YAML types (str, int, float, bool, list, dict, None). Inline {…} attributes take precedence on key collisions. Read-only.

walk

walk() -> list[AstNode]

Walk the tree depth-first, returning a flat list of all nodes.

The returned nodes are copies, so this is useful for inspection only, never for mutation.

Returns:

Type Description
list[AstNode]

Every node of the subtree, in depth-first document order.

ParseResult

The AST tree plus structured, typed document metadata (OMEP-0010).

frontmatter property

frontmatter: dict[str, object] | None

Typed YAML frontmatter, or None when the document has none.

Values preserve their native YAML types (str, int, float, bool, list, dict, None).

headings property

headings: list[Heading]

Every heading of the document, in document order (flat).

root property

root: AstNode

The parsed AST tree, identical to what parse returns.

summary property

summary: str | None

Rendered HTML of the content before a top-level <!-- more --> delimiter.

None when the document has no delimiter.

toc property

toc: list[Heading]

The nested table-of-contents tree.

Heading

A document heading, flat entry or TOC tree node (OMEP-0010).

children property

children: list[Heading]

Nested sub-headings.

Always empty for entries of the flat ParseResult.headings list; populated only in ParseResult.toc.

id property

id: str

The anchor id assigned to the heading.

level property

level: int

Heading level, 1 to 6.

text property

text: str

Plain-text heading label, the text the slug is derived from.

Example plugins

Provisional surface

oxydemark.contrib is public and documented, but it is intentionally not part of oxydemark.__all__ and carries no stability guarantee: these plugins may change or be removed in a MINOR release. If you need long-term stability, copy the plugin into your own codebase.

contrib

Example plugins shipped with OxydeMark.

This namespace exists to demonstrate the Plugin protocol against real, tested implementations. Each plugin is deliberately small and focuses on one pipeline layer:

Plugin Hooks Demonstrates
AdmonitionPlugin preprocess + transform text normalisation, then AST enrichment
ShortcodePlugin transform replacing text nodes with raw_html
MentionPlugin transform splitting text into structural nodes
LazyImagesPlugin postprocess pure HTML-string rewriting

See the plugin authoring guide for the full picture.

Stability

oxydemark.contrib is a provisional surface (see OMEP-0008). It is public and documented, but it is intentionally not part of oxydemark.__all__ and carries no stability guarantee: these plugins may change or be removed in a MINOR release. If you need long-term stability, copy the plugin into your own codebase.

Shortcode

Shortcode = Callable[[str], str]

AdmonitionPlugin

AdmonitionPlugin(kinds: Mapping[str, str] | None = None)

Turn GitHub-style alerts into styled admonition blocks.

Attributes:

Name Type Description
kinds dict[str, str]

The active marker-to-title mapping.

Build the plugin.

Parameters:

Name Type Description Default
kinds Mapping[str, str] | None

Mapping of lowercase marker name to the title rendered in the admonition header. Defaults to DEFAULT_KINDS. Markers absent from this mapping are left untouched as ordinary blockquotes.

None
preprocess
preprocess(markdown: str) -> str

Rewrite > [!KIND] blockquotes into Comark :::kind fences.

Parameters:

Name Type Description Default
markdown str

The Markdown source to rewrite.

required

Returns:

Type Description
str

The Markdown source with alerts turned into block components.

transform
transform(ast: AstNode) -> AstNode

Add admonition classes and a title node to admonition components.

Parameters:

Name Type Description Default
ast AstNode

The root of the parsed tree.

required

Returns:

Type Description
AstNode

The decorated tree.

ShortcodePlugin

ShortcodePlugin(shortcodes: Mapping[str, Shortcode] | None = None)

Expand {{ name argument }} markers into raw HTML nodes.

Attributes:

Name Type Description
shortcodes dict[str, Shortcode]

The active name-to-handler mapping.

Build the plugin.

Parameters:

Name Type Description Default
shortcodes Mapping[str, Shortcode] | None

Mapping of shortcode name to a handler returning an HTML fragment. Defaults to DEFAULT_SHORTCODES. Unknown names, and handlers returning an empty string, leave the marker untouched.

None
transform
transform(ast: AstNode) -> AstNode

Replace shortcode markers found in text nodes.

Parameters:

Name Type Description Default
ast AstNode

The root of the parsed tree.

required

Returns:

Type Description
AstNode

The rewritten tree.

MentionPlugin

MentionPlugin(base_url: str = 'https://github.com/')

Linkify @handle mentions found in text nodes.

Attributes:

Name Type Description
base_url str

The prefix handles are appended to.

Build the plugin.

Parameters:

Name Type Description Default
base_url str

Prefix the handle is appended to.

'https://github.com/'
transform
transform(ast: AstNode) -> AstNode

Replace mentions found in text nodes with link nodes.

Parameters:

Name Type Description Default
ast AstNode

The root of the parsed tree.

required

Returns:

Type Description
AstNode

The rewritten tree.

LazyImagesPlugin

LazyImagesPlugin(decoding: str | None = 'async')

Add lazy-loading hints to every <img> that lacks them.

Attributes:

Name Type Description
decoding str | None

The configured decoding attribute value.

Build the plugin.

Parameters:

Name Type Description Default
decoding str | None

Value for the decoding attribute, or None to skip it.

'async'
postprocess
postprocess(html: str) -> str

Rewrite <img> tags, leaving already-annotated ones untouched.

Parameters:

Name Type Description Default
html str

The rendered HTML.

required

Returns:

Type Description
str

The HTML with lazy-loading hints added.

Defaults

The kinds= and shortcodes= arguments replace these defaults rather than extending them; spread the mapping to add entries.

DEFAULT_KINDS module-attribute

DEFAULT_KINDS: Mapping[str, str] = {'note': 'Note', 'tip': 'Tip', 'important': 'Important', 'warning': 'Warning', 'caution': 'Caution'}

DEFAULT_SHORTCODES module-attribute

DEFAULT_SHORTCODES: Mapping[str, Shortcode] = {'youtube': youtube}