Plugins

Enable non-standard Markdown syntax with explicit Wenmode plugins.


Plugins are feature modules that install parser rules and renderer handlers together. Use them when syntax creates nodes outside the CommonMark, GFM, or mdast directive surface.

Most applications use plugins in addition to a preset:

from wenmode import Wenmode
from wenmode.presets import github
from wenmode.plugins import math

wenmode = Wenmode(github, plugins=[math])

Use a plugin when you want a complete feature. Use individual rules when you are deliberately building a small dialect and already know which parser behavior you need.

Using Plugins

Import a plugin module from wenmode.plugins and pass it to Wenmode with the plugins argument. During initialization, Wenmode calls each plugin’s setup(wenmode, **options) function with no extra options.

from wenmode import Wenmode
from wenmode.plugins import math

wenmode = Wenmode(plugins=[math])

assert wenmode.render('Inline $x + y$.\n') == (
    '<p>Inline <span class="math math-inline">x + y</span>.</p>\n'
)

Install multiple plugins by listing them:

from wenmode import Wenmode
from wenmode.plugins import mark, superscript

wenmode = Wenmode(plugins=[mark, superscript])

Some plugins accept setup options. For example, math can install only inline or block syntax. Install those plugins with use() when you need to pass options:

from wenmode import Wenmode
from wenmode.plugins import math

inline_math = Wenmode().use(math, block=False)
block_math = Wenmode().use(math, inline=False)

Use Wenmode.use(plugin, **options) when you need to install a plugin after the instance already exists. It returns the same Wenmode instance, so existing chain-style setup remains supported.

Built-In Plugins

Plugin

Enables

wenmode.plugins.abbr

Abbreviation definitions and abbreviation nodes

wenmode.plugins.definition_list

Definition list syntax and nodes

wenmode.plugins.fenced_directive

MyST-style fenced directives, rendered as containerDirective or literalDirective nodes

wenmode.plugins.frontmatter

Top-level --- front matter stored on root.data["frontmatter"]

wenmode.plugins.inline_role

MyST-style inline roles, rendered as textDirective nodes

wenmode.plugins.insert

insert inline nodes

wenmode.plugins.mark

mark inline nodes

wenmode.plugins.math

Display and inline math nodes

wenmode.plugins.ruby

Ruby annotation nodes

wenmode.plugins.spoiler

Block and inline spoiler nodes

wenmode.plugins.subscript

subscript inline nodes

wenmode.plugins.superscript

superscript inline nodes

Each plugin also registers default HTML, Markdown, or RST renderer handlers when the feature has a standard representation in Wenmode’s built-in renderers.

Front Matter

The frontmatter plugin consumes top-level --- front matter before normal Markdown block parsing and stores the parsed value on the root node. It does not emit a child node. HTML output ignores front matter by default, Markdown output serializes it back to a --- block, and RST output renders flat metadata as a docinfo field list.

from wenmode import MarkdownRenderer, RSTRenderer, Wenmode
from wenmode.plugins import frontmatter

source = '---\ntitle: Hello\n---\n\n# Hi\n'

html = Wenmode(plugins=[frontmatter])
root = html.parse(source)
assert root.data == {'frontmatter': {'title': 'Hello'}}
assert html.render_node(root) == '<h1>Hi</h1>\n'

markdown = Wenmode(renderer=MarkdownRenderer(), plugins=[frontmatter])
assert markdown.render(source) == source

rst = Wenmode(renderer=RSTRenderer(), plugins=[frontmatter])
assert rst.render(source) == ':title: Hello\n\nHi\n==\n'

The default loader and dumper handle simple scalar key: value lines. Pass custom callbacks when your application wants YAML or another metadata format. The load callback receives only the text between the opening and closing fences:

from wenmode import Wenmode
from wenmode.plugins import frontmatter


def load_meta(source: str) -> dict[str, str]:
    return {'raw': source}


def dump_meta(value: object) -> str | None:
    if not isinstance(value, dict):
        return None
    return str(value['raw'])


wenmode = Wenmode().use(frontmatter, load=load_meta, dump=dump_meta, data_key='meta')

Fenced Directives And Roles

The fenced_directive and inline_role plugins provide MyST-style directive syntax. Inline roles map onto textDirective, one of the mdast-compatible directive nodes documented in Directives. Fenced directives usually create containerDirective nodes, and literal-body directives such as code-block create literalDirective nodes.

from wenmode import Wenmode
from wenmode.plugins import fenced_directive, inline_role

wenmode = Wenmode(plugins=[fenced_directive, inline_role])

Fenced directives use code-fence-style syntax:

```{note} Important
:class: warning

Read this first.
```

The fenced directive plugin creates a containerDirective node by default. Its first-line argument becomes the directive label, :key: value option lines become attributes, and body content is parsed as Markdown.

Literal-body directives create literalDirective nodes so their body is kept as source text instead of being parsed as Markdown. By default this applies to code-block:

```{code-block} python
:caption: example.py

print("*not emphasis*")
```

Pass literal_names to use() when your application needs a different set. Pass fence when your dialect also accepts other repeated fence characters, such as MyST colon fences:

from wenmode import Wenmode
from wenmode.plugins import fenced_directive

wenmode = Wenmode().use(
    fenced_directive,
    literal_names={'code-block', 'sourcecode'},
    fence=('`', '~', ':'),
)

Inline roles use MyST-style role syntax:

{iconify}`devicon:pypi`

The inline role plugin creates a textDirective node. The role name becomes the directive name, and the backtick content becomes children.

After these plugins create directive nodes, custom HTML output is still handled through directive renderers registered by node type and directive name. Without a matching renderer, text, leaf, and container directives fall back to their child content. Literal directives fall back to escaped literal text, and code-block has default code-block output in the HTML renderer.

Use these plugins when you want MyST-style syntax. Use the core TextDirective, LeafDirective, and ContainerDirective rules when you want mdast directive syntax with colon markers.

Creating Plugins

A custom plugin is a module or object with a setup(wenmode, **options) function. Inside setup(), register parser rules, renderer handlers, directive renderers, or any combination of them.

from wenmode import Wenmode
from wenmode.rules import Emphasis


class MyPlugin:
    def setup(self, wenmode: Wenmode, **options) -> None:
        wenmode.register_rule(Emphasis)


wenmode = Wenmode([], plugins=[MyPlugin()])

For non-trivial syntax, define the node, rule, render handlers, and setup() together. See Custom Plugins for a complete custom plugin walkthrough.