A fast and complete Python implementation of Markdown with extensive extras support
—
Extensions for advanced link processing, auto-linking, reference management, and URL handling that enhance markdown's linking capabilities.
Automatically convert text patterns to clickable links using regular expressions.
# link-patterns extra - auto-link regex patterns
link_patterns = [
(compiled_regex, replacement_string_or_callable),
# More patterns...
]
extras = {"link-patterns": link_patterns}Usage Examples:
import markdown2
import re
# Auto-link issue numbers
issue_pattern = (
re.compile(r'issue #(\d+)'),
r'https://github.com/user/repo/issues/\1'
)
# Auto-link user mentions
user_pattern = (
re.compile(r'@(\w+)'),
lambda m: f'<a href="/users/{m.group(1)}">@{m.group(1)}</a>'
)
# Auto-link bug numbers
bug_pattern = (
re.compile(r'bug (\d+)'),
r'<a href="http://bugs.example.com/\1">bug \1</a>'
)
text = "See issue #123 and @john for bug 456 details"
html = markdown2.markdown(
text,
extras={"link-patterns": [issue_pattern, user_pattern, bug_pattern]}
)Simplified reference link syntax and shortcut handling.
# link-shortrefs extra - allow shortcut reference links without []
extras = ["link-shortrefs"]Usage Examples:
import markdown2
markdown_text = '''
Visit [Google] or [Python].
[Google]: https://google.com
[Python]: https://python.org
'''
# Standard markdown requires [Google][] syntax
# With link-shortrefs, [Google] works directly
html = markdown2.markdown(markdown_text, extras=["link-shortrefs"])Convert markdown file links to HTML file links automatically.
# markdown-file-links extra - convert .md links to .html
extras = ["markdown-file-links"]
# With configuration options
extras = {
"markdown-file-links": {
"url_rewrite_func": custom_rewrite_function
}
}Usage Examples:
import markdown2
markdown_text = '''
See the [installation guide](install.md) and [API docs](api/reference.md) for details.
'''
html = markdown2.markdown(markdown_text, extras=["markdown-file-links"])
# Converts install.md -> install.html
# Converts api/reference.md -> api/reference.html
# Custom URL rewriting
def custom_rewrite(url):
if url.endswith('.md'):
return url.replace('.md', '.html')
return url
html = markdown2.markdown(
markdown_text,
extras={"markdown-file-links": {"url_rewrite_func": custom_rewrite}}
)Add rel="nofollow" attribute to external links for SEO and security.
# nofollow extra - add rel="nofollow" to <a> tags with href
extras = ["nofollow"]Usage Examples:
import markdown2
markdown_text = '''
Visit [our site](https://example.com) or [external site](https://external.com).
'''
html = markdown2.markdown(markdown_text, extras=["nofollow"])
# Adds rel="nofollow" to all links with href attributesCreate sophisticated link processing with callable replacements:
import markdown2
import re
def process_ticket_links(match):
"""Convert ticket references to full HTML links with metadata."""
ticket_id = match.group(1)
return f'''
<a href="/tickets/{ticket_id}"
class="ticket-link"
data-ticket-id="{ticket_id}"
title="View ticket #{ticket_id}">
Ticket #{ticket_id}
</a>
'''
def process_user_mentions(match):
"""Convert @username to user profile links."""
username = match.group(1)
return f'''
<a href="/users/{username}"
class="user-mention"
data-username="{username}">
@{username}
</a>
'''
patterns = [
(re.compile(r'ticket #(\d+)', re.IGNORECASE), process_ticket_links),
(re.compile(r'@([a-zA-Z0-9_]+)'), process_user_mentions),
]
text = "User @alice reported ticket #1234 yesterday."
html = markdown2.markdown(text, extras={"link-patterns": patterns})Use multiple link extras together for comprehensive link processing:
import markdown2
import re
# GitHub-style link processing
github_patterns = [
# Issue references: #123
(re.compile(r'#(\d+)'), r'https://github.com/owner/repo/issues/\1'),
# Pull request references: PR #123
(re.compile(r'PR #(\d+)'), r'https://github.com/owner/repo/pull/\1'),
# Commit references: abc1234
(re.compile(r'\b([a-f0-9]{7,40})\b'), r'https://github.com/owner/repo/commit/\1'),
# User mentions: @username
(re.compile(r'@([a-zA-Z0-9-]+)'), r'https://github.com/\1'),
]
markdown_text = '''
# Project Documentation
See the [setup guide](setup.md) for installation.
Bug reported by @john in #123, fixed in commit abc1234.
Also see PR #456 for related changes.
External reference: [Stack Overflow](https://stackoverflow.com)
'''
html = markdown2.markdown(
markdown_text,
extras={
"link-patterns": github_patterns,
"markdown-file-links": None,
"link-shortrefs": None,
"nofollow": None,
"header-ids": None
}
)Link patterns are specified as tuples of (pattern, replacement):
import re
# String replacement with capture groups
pattern1 = (re.compile(r'bug (\d+)'), r'<a href="/bugs/\1">Bug \1</a>')
# Callable replacement for complex processing
def custom_replacer(match):
return f"<custom>{match.group(0)}</custom>"
pattern2 = (re.compile(r'CUSTOM-(\w+)'), custom_replacer)
# Multiple patterns
patterns = [pattern1, pattern2]
extras = {"link-patterns": patterns}Customize how markdown file links are converted:
def custom_md_rewriter(url):
"""Custom function to rewrite .md URLs."""
if url.endswith('.md'):
# Convert to .html and add version parameter
return url.replace('.md', '.html') + '?v=latest'
return url
extras = {
"markdown-file-links": {
"url_rewrite_func": custom_md_rewriter
}
}import markdown2
import re
# Documentation site with cross-references
doc_patterns = [
# API references: {{api:function_name}}
(re.compile(r'\{\{api:([^}]+)\}\}'), r'<a href="/api/\1" class="api-link">\1</a>'),
# Tutorial references: {{tutorial:name}}
(re.compile(r'\{\{tutorial:([^}]+)\}\}'), r'<a href="/tutorials/\1" class="tutorial-link">\1</a>'),
# External docs: {{external:name}}
(re.compile(r'\{\{external:([^}]+)\}\}'), r'<a href="https://docs.example.com/\1" rel="nofollow">\1</a>'),
]
processor = markdown2.Markdown(
extras={
"link-patterns": doc_patterns,
"markdown-file-links": None,
"header-ids": None,
"toc": None,
"tables": None
}
)
documentation_html = processor.convert(doc_content)Control how external links are opened by adding target attributes automatically.
# target-blank-links extra - add target="_blank" to external links
extras = ["target-blank-links"]Usage Examples:
import markdown2
content = '''
# Links Demo
[Internal link](./local-page.md)
[External link](https://example.com)
[Another external](https://github.com/user/repo)
'''
html = markdown2.markdown(content, extras=["target-blank-links"])
# External links get target="_blank" attribute automatically
# Internal/relative links remain unchangedimport markdown2
import re
# Blog-style auto-linking
blog_patterns = [
# Tag links: #tag
(re.compile(r'(?<!\w)#([a-zA-Z0-9_]+)'), r'<a href="/tags/\1" class="tag-link">#\1</a>'),
# Category links: [category:name]
(re.compile(r'\[category:([^]]+)\]'), r'<a href="/category/\1" class="category-link">\1</a>'),
]
blog_processor = markdown2.Markdown(
extras={
"link-patterns": blog_patterns,
"nofollow": None, # Add nofollow to external links
"smarty-pants": None, # Nice typography
"break-on-newline": None # GitHub-style line breaks
}
)
blog_html = blog_processor.convert(blog_post_content)Install with Tessl CLI
npx tessl i tessl/pypi-markdown2