Markdown¶
Markdown to HTML renderer -- zero dependencies, stdlib only, Python 3.10+.
Replaces:
mistune,markdown,markdown-it-py
Overview¶
The Markdown module provides a drop-in replacement for mistune.html() to render common Markdown to HTML. It supports a CommonMark subset plus GFM extensions (tables, strikethrough, task lists, extended autolinks) -- all without any third-party dependencies.
| File | Description | Dependencies |
|---|---|---|
markdown.py |
Pure Python implementation | None (stdlib only) |
The renderer uses a two-phase architecture: a block-level parser (line-based state machine) feeds into an inline parser (regex with a placeholder system to prevent double-escaping), producing HTML string output directly without an intermediate AST.
How to Use in Your Project¶
Just copy the single .py file into your project:
Then import directly:
API Reference¶
render(text)¶
Convert Markdown text to HTML.
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
text |
str |
-- | Markdown source string. |
Returns: str -- HTML string.
Example:
from markdown import render
html = render("# Hello\n\nThis is **bold**.")
# '<h1>Hello</h1>\n<p>This is <strong>bold</strong>.</p>\n'
Usage Examples¶
Basic Rendering¶
from markdown import render
html = render("""
# My Document
This is a paragraph with **bold**, *italic*, and `code`.
## Code Block
```python
def hello():
print("world")
``\`
> A blockquote with **emphasis**.
- Item 1
- Item 2
- Nested item
""")
Headings¶
# ATX headings
render("# Heading 1") # <h1>Heading 1</h1>
render("## Heading 2") # <h2>Heading 2</h2>
render("### Heading 3") # <h3>Heading 3</h3>
# Setext headings
render("Heading 1\n=========") # <h1>Heading 1</h1>
render("Heading 2\n---------") # <h2>Heading 2</h2>
Emphasis¶
render("*italic*") # <em>italic</em>
render("**bold**") # <strong>bold</strong>
render("***bold italic***") # <em><strong>bold italic</strong></em>
Links and Images¶
# Inline link
render("[click](https://example.com)")
# <a href="https://example.com">click</a>
# Link with title
render('[click](https://example.com "Title")')
# <a href="https://example.com" title="Title">click</a>
# Reference link
render("[click][ref]\n\n[ref]: https://example.com")
# Image
render("")
# <img src="https://example.com/img.png" alt="alt text" />
Lists¶
# Unordered
render("- item 1\n- item 2\n- item 3")
# Ordered
render("1. first\n2. second\n3. third")
# Nested
render("- parent\n - child\n - child\n- sibling")
Tables (GFM)¶
Column alignment is supported via :--- (left), :---: (center), and ---: (right) syntax.
Strikethrough (GFM)¶
render("~~deleted text~~")
# <p><del>deleted text</del></p>
render("~~bold **inside** strike~~")
# <p><del>bold <strong>inside</strong> strike</del></p>
Task Lists (GFM)¶
render("- [ ] Write the code\n- [x] Write the tests\n- [ ] Review the PR")
# <ul>
# <li class="task-list-item"><input class="task-list-item-checkbox" type="checkbox" disabled/>Write the code</li>
# <li class="task-list-item"><input class="task-list-item-checkbox" type="checkbox" disabled checked/>Write the tests</li>
# <li class="task-list-item"><input class="task-list-item-checkbox" type="checkbox" disabled/>Review the PR</li>
# </ul>
Works with both unordered (- [ ]) and ordered (1. [ ]) lists.
Extended Autolinks (GFM)¶
render("Visit https://example.com for more info")
# <p>Visit <a href="https://example.com">https://example.com</a> for more info</p>
# Trailing punctuation is excluded from the URL
render("See https://example.com.")
# <p>See <a href="https://example.com">https://example.com</a>.</p>
Only http:// and https:// schemes are auto-linked. Bare www. URLs are not matched.
Fenced Code Blocks¶
render("```python\ndef foo():\n pass\n```")
# Produces <pre><code class="language-python">...</code></pre>
Supported Features¶
| Feature | Syntax | Example |
|---|---|---|
| ATX headings | # to ###### |
# Title |
| Setext headings | === / --- underlines |
Title\n==== |
| Paragraphs | Blank line separated | Two blank lines |
| Emphasis (italic) | *text* or _text_ |
*italic* |
| Emphasis (bold) | **text** or __text__ |
**bold** |
| Bold italic | ***text*** |
***both*** |
| Inline code | `code` |
`var` |
| Fenced code blocks | ``` or ~~~ |
With optional language tag |
| Indented code blocks | 4-space indent | code |
| Block quotes | > prefix |
> quoted, supports nesting |
| Unordered lists | -, *, + |
- item, supports nesting |
| Ordered lists | 1. |
1. first, supports start attribute |
| Inline links | [text](url) |
With optional title |
| Reference links | [text][ref] |
[ref]: url definitions |
| Images |  |
With optional title |
| Thematic breaks | ---, ***, ___ |
Horizontal rule |
| Hard line breaks | Two trailing spaces or \ |
Line break within paragraph |
| Backslash escapes | \*, \_, etc. |
Escape special characters |
| Autolinks | <https://...> |
<user@example.com> |
| GFM tables | Pipe syntax | With column alignment |
| GFM strikethrough | ~~text~~ |
~~deleted~~ |
| GFM task lists | - [ ] / - [x] |
Checkbox list items |
| GFM extended autolinks | Bare https:// URLs |
Auto-linked with punctuation stripping |
| HTML escaping | Automatic | <, >, & escaped |
Not Supported¶
- Raw HTML passthrough (escaped for safety)
- Footnotes, definition lists
- Math/LaTeX
- Anchors/aliases
Security¶
- All text content is HTML-escaped via
html.escape() - Code blocks only escape HTML, no Markdown processing inside
- URLs are checked against harmful protocols (
javascript:,vbscript:,file:,data:) -- blocked URLs are replaced with#harmful-link
Notes and Caveats¶
Single-Function API
Unlike mistune which provides a full parser/renderer class hierarchy, this module exposes a single render() function. This is intentional -- for the common use case of converting Markdown to HTML, no configuration is needed.
CommonMark Subset
This renderer targets the most commonly used Markdown features. It does not aim for 100% CommonMark spec compliance, but covers all features typically found in LLM output and documentation.
- Python version: Requires Python 3.10+ (uses
X | Yunion type syntax). - Output compatibility: Produces HTML output that matches
mistune.html()(with GFM plugins) for all supported features. - Performance: ~2x faster than mistune across small, medium, and large documents, including GFM content.
Benchmark¶
Benchmarked against mistune across five test sizes: CommonMark (small, medium, large) and GFM (medium, large).
See Markdown Benchmark for detailed results.