Usage¶
Learn the main APIs for parsing Markdown, rendering output, and streaming HTML chunks.
Install¶
Install Wenmode from PyPI with your preferred Python package manager.
pip install wenmode
uv add wenmode
Quick start¶
Wenmode is the main convenience API. It owns a Parser and a renderer, and
uses the commonmark preset with HTMLRenderer when no options are provided.
from wenmode import Wenmode
wenmode = Wenmode()
text = '''
# Hello
This is **wenmode**.
'''
expected = '''
<h1>Hello</h1>
<p>This is <strong>wenmode</strong>.</p>
'''
html = wenmode.render(text)
assert html == expected.lstrip()
render() parses the source and renders the resulting syntax tree. The source
can be a string, a synchronous text stream, or another iterable of lines.
from wenmode import Wenmode
wenmode = Wenmode()
with open('README.md', encoding='utf-8') as file:
html = wenmode.render(file)
Parsing¶
Use parse() when you want the AST instead of rendered output.
from wenmode import Wenmode
wenmode = Wenmode()
text = 'A [link](https://example.com).'
tree = wenmode.parse(text)
ast = tree.to_ast()
assert ast == {
'type': 'root',
'children': [
{
'type': 'paragraph',
'children': [
{'type': 'text', 'value': 'A '},
{
'type': 'link',
'children': [{'type': 'text', 'value': 'link'}],
'url': 'https://example.com',
},
{'type': 'text', 'value': '.'},
],
}
],
}
The returned root node is a wenmode.nodes.Root. Nodes are data objects; their
rendering behavior lives in renderers.
Rendering¶
Wenmode() uses HTMLRenderer by default. Pass a different renderer when you
want another output format.
from wenmode import RSTRenderer, Wenmode
wenmode = Wenmode(renderer=RSTRenderer())
text = '# Hello'
expected = '''
Hello
=====
'''
rst = wenmode.render(text)
assert rst == expected.lstrip()
Wenmode currently provides:
HTMLRenderer, for HTML output.MarkdownRenderer, for serializing the AST back to Markdown.RSTRenderer, for serializing the AST to reStructuredText.BaseRenderer, a small dispatch-based base class for custom renderers.
MarkdownRenderer and RSTRenderer serialize the AST to canonical markup. They
are not source-preserving formatters; syntax details that are not represented in
the AST may be normalized or omitted.
If you already have a node, use render_node() to render it directly.
from wenmode import Wenmode
wenmode = Wenmode()
text = '# Hello'
root = wenmode.parse(text)
html = wenmode.render_node(root)
Parser and renderer separately¶
Use Parser directly when you want parsing and rendering to be separate steps.
from wenmode import HTMLRenderer, Parser
from wenmode.presets import commonmark
parser = Parser(commonmark)
text = '# Hello'
tree = parser.parse(text)
html = HTMLRenderer().render(tree)
Parser state is created per parse. Reference definitions, footnote definitions, and abbreviation definitions do not leak between calls, so a parser instance can be reused safely.
Streaming output¶
Use the streaming preset when you want HTML chunks without waiting for the
whole document to be parsed and rendered.
from wenmode import Wenmode
from wenmode.presets import streaming
wenmode = Wenmode(streaming)
text = '''
# Hello
A [link](/url).
'''
sent_chunks: list[str] = []
for chunk in wenmode.stream(text):
sent_chunks.append(chunk)
expected = '''
<h1>Hello</h1>
<p>A <a href="/url">link</a>.</p>
'''
assert ''.join(sent_chunks) == expected.lstrip()
The streaming API yields rendered block output as parsing progresses. It only
works with rules that do not require deferred document-wide inline resolution.
If unsupported rules are enabled, stream() raises StreamingUnsupportedError.
Wenmode.stream() returns a synchronous iterator of HTML chunks. Web frameworks
that accept iterable response bodies can send those chunks directly.
See choosing a rule preset, HTML and URL safety behavior, and common integration tasks.
FastAPI¶
from fastapi.responses import StreamingResponse
from wenmode import Wenmode
from wenmode.presets import streaming
wenmode = Wenmode(streaming)
async def preview(markdown: str):
return StreamingResponse(
wenmode.stream(markdown),
media_type='text/html; charset=utf-8',
)
Flask¶
from flask import Response
from wenmode import Wenmode
from wenmode.presets import streaming
wenmode = Wenmode(streaming)
def preview(markdown: str):
return Response(
wenmode.stream(markdown),
mimetype='text/html',
)
Django¶
from django.http import StreamingHttpResponse
from wenmode import Wenmode
from wenmode.presets import streaming
wenmode = Wenmode(streaming)
def preview(request):
markdown = request.POST['markdown']
return StreamingHttpResponse(
wenmode.stream(markdown),
content_type='text/html; charset=utf-8',
)