CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-markdown-it-py

Python port of markdown-it providing CommonMark-compliant markdown parsing with configurable syntax and pluggable architecture

Pending
Overview
Eval results
Files

rendering.mddocs/

Rendering and Output

HTML rendering system with customizable render rules and support for custom renderers to generate different output formats from parsed markdown tokens.

Capabilities

HTML Renderer

Default renderer that converts tokens to HTML output.

class RendererHTML:
    """HTML renderer with customizable render rules."""
    
    __output__ = "html"  # Output format identifier
    
    def __init__(self, parser: MarkdownIt = None):
        """
        Initialize HTML renderer.
        
        Parameters:
        - parser: MarkdownIt instance (for reference)
        """
    
    def render(self, tokens: list[Token], options: dict, env: dict) -> str:
        """
        Render token stream to HTML.
        
        Parameters:
        - tokens: list of tokens to render
        - options: parser options
        - env: environment data
        
        Returns:
        - str: rendered HTML output
        """
    
    def renderInline(self, tokens: list[Token], options: dict, env: dict) -> str:
        """
        Render inline tokens to HTML.
        
        Parameters:
        - tokens: list of inline tokens
        - options: parser options  
        - env: environment data
        
        Returns:
        - str: rendered HTML without block wrappers
        """
    
    def renderToken(self, tokens: list[Token], idx: int, options: dict, env: dict) -> str:
        """
        Render single token at specified index.
        
        Parameters:
        - tokens: full token list
        - idx: index of token to render
        - options: parser options
        - env: environment data
        
        Returns:
        - str: rendered HTML for single token
        """

Usage Example:

from markdown_it import MarkdownIt
from markdown_it.renderer import RendererHTML

md = MarkdownIt()
tokens = md.parse("# Hello\n\n**Bold** text.")

# Direct rendering
html = md.renderer.render(tokens, md.options, {})
print(html)

# Custom renderer instance
custom_renderer = RendererHTML()
html = custom_renderer.render(tokens, md.options, {})

Custom Render Rules

Customize rendering for specific token types.

# Render rules dictionary
rules: dict[str, callable]  # Maps token types to render functions

def add_render_rule(self, name: str, function: callable, fmt: str = "html") -> None:
    """
    Add custom render rule for token type.
    
    Parameters:
    - name: token type name
    - function: render function with signature (tokens, idx, options, env) -> str
    - fmt: output format (must match renderer.__output__)
    """

Usage Example:

from markdown_it import MarkdownIt

def custom_heading_rule(tokens, idx, options, env):
    """Custom heading renderer with anchor links."""
    token = tokens[idx]
    if token.nesting == 1:  # opening tag
        # Get heading text from next inline token
        heading_text = tokens[idx + 1].content if idx + 1 < len(tokens) else ""
        anchor_id = heading_text.lower().replace(" ", "-")
        return f'<{token.tag} id="{anchor_id}">'
    else:  # closing tag
        return f'</{token.tag}>'

md = MarkdownIt()
md.add_render_rule("heading_open", custom_heading_rule)
md.add_render_rule("heading_close", custom_heading_rule)

html = md.render("# Hello World")
# Output: <h1 id="hello-world">Hello World</h1>

Custom Renderer Classes

Create custom renderers for different output formats.

class RendererProtocol(Protocol):
    """Protocol defining renderer interface."""
    
    __output__: str  # Output format identifier
    
    def render(self, tokens: list[Token], options: dict, env: dict) -> Any:
        """Render tokens to output format."""

Usage Example:

from markdown_it import MarkdownIt
from markdown_it.renderer import RendererProtocol

class MarkdownRenderer(RendererProtocol):
    """Custom renderer that outputs markdown (round-trip)."""
    
    __output__ = "markdown"
    
    def __init__(self, parser=None):
        self.rules = {}
        
    def render(self, tokens, options, env):
        result = ""
        for i, token in enumerate(tokens):
            if token.type in self.rules:
                result += self.rules[token.type](tokens, i, options, env)
            else:
                result += self.default_render(token)
        return result
    
    def default_render(self, token):
        if token.type == "heading_open":
            return "#" * int(token.tag[1]) + " "
        elif token.type == "paragraph_open":
            return ""
        elif token.type == "inline":
            return token.content
        elif token.type in ["heading_close", "paragraph_close"]:
            return "\n\n"
        return ""

# Use custom renderer
md = MarkdownIt(renderer_cls=MarkdownRenderer)
markdown_output = md.render("# Title\n\nParagraph text.")

Built-in Render Rules

Standard render rules for common token types:

# Block elements
def code_block(self, tokens, idx, options, env): ...
def fence(self, tokens, idx, options, env): ...
def blockquote_open(self, tokens, idx, options, env): ...
def blockquote_close(self, tokens, idx, options, env): ...
def hr(self, tokens, idx, options, env): ...

# Lists
def bullet_list_open(self, tokens, idx, options, env): ...
def bullet_list_close(self, tokens, idx, options, env): ...
def list_item_open(self, tokens, idx, options, env): ...
def list_item_close(self, tokens, idx, options, env): ...

# Inline elements  
def text(self, tokens, idx, options, env): ...
def html_inline(self, tokens, idx, options, env): ...
def html_block(self, tokens, idx, options, env): ...
def softbreak(self, tokens, idx, options, env): ...
def hardbreak(self, tokens, idx, options, env): ...

# Links and media
def link_open(self, tokens, idx, options, env): ...
def link_close(self, tokens, idx, options, env): ...
def image(self, tokens, idx, options, env): ...

# Tables
def table_open(self, tokens, idx, options, env): ...
def tr_open(self, tokens, idx, options, env): ...
def td_open(self, tokens, idx, options, env): ...
def th_open(self, tokens, idx, options, env): ...

Render Rule Customization

Override built-in render rules for customization:

from markdown_it import MarkdownIt

class CustomRenderer(RendererHTML):
    """Custom HTML renderer with modifications."""
    
    def code_block(self, tokens, idx, options, env):
        """Custom code block rendering with line numbers."""
        token = tokens[idx]
        info = token.info.strip() if token.info else ""
        lang = info.split()[0] if info else ""
        
        code = token.content
        lines = code.rstrip().split('\n')
        
        html = f'<pre><code class="language-{lang}">'
        for i, line in enumerate(lines, 1):
            html += f'<span class="line-number">{i:3d}</span>{line}\n'
        html += '</code></pre>'
        
        return html
    
    def strong_open(self, tokens, idx, options, env):
        """Use <b> instead of <strong>."""
        return '<b>'
    
    def strong_close(self, tokens, idx, options, env):
        """Use <b> instead of <strong>."""
        return '</b>'

# Use custom renderer
md = MarkdownIt(renderer_cls=CustomRenderer)
html = md.render("```python\nprint('hello')\n```\n\n**Bold text**")

Attribute Rendering

Utility methods for rendering HTML attributes:

def renderAttrs(self, token: Token) -> str:
    """
    Render token attributes to HTML attribute string.
    
    Parameters:
    - token: token with attributes
    
    Returns:
    - str: HTML attribute string
    """

def renderToken(self, tokens: list[Token], idx: int, options: dict, env: dict) -> str:
    """
    Default token rendering with attribute support.
    
    Parameters:
    - tokens: token list
    - idx: token index
    - options: parser options
    - env: environment data
    
    Returns:
    - str: rendered token HTML
    """

Usage Example:

from markdown_it.token import Token
from markdown_it.renderer import RendererHTML

renderer = RendererHTML()

# Token with attributes
token = Token("div_open", "div", 1)
token.attrSet("class", "container")
token.attrSet("id", "main")

# Render attributes
attrs_html = renderer.renderAttrs(token)
print(attrs_html)  # ' class="container" id="main"'

# Full token rendering
html = renderer.renderToken([token], 0, {}, {})
print(html)  # '<div class="container" id="main">'

Output Customization

Escaping and Security

HTML escaping utilities for safe output:

from markdown_it.common.utils import escapeHtml, unescapeAll

def escapeHtml(raw: str) -> str:
    """
    Escape HTML characters for safe output.
    
    Parameters:
    - raw: raw text to escape
    
    Returns:
    - str: HTML-escaped text
    """

def unescapeAll(str: str) -> str:
    """
    Unescape entities and backslash escapes.
    
    Parameters:
    - str: text to unescape
    
    Returns:
    - str: unescaped text
    """

Link and URL Processing

URL handling in render context:

def custom_link_render(tokens, idx, options, env):
    """Custom link rendering with security checks."""
    token = tokens[idx]
    
    if token.nesting == 1:  # link_open
        href = token.attrGet("href")
        if href:
            # Custom URL validation
            if not is_safe_url(href):
                return '<span class="invalid-link">'
            # Add custom attributes
            token.attrSet("rel", "noopener")
            token.attrSet("target", "_blank")
        
        # Render with attributes
        return renderer.renderToken(tokens, idx, options, env)
    else:  # link_close
        return '</a>' if token.attrGet("href") else '</span>'

md = MarkdownIt()
md.add_render_rule("link_open", custom_link_render)
md.add_render_rule("link_close", custom_link_render)

Multiple Output Formats

Supporting multiple renderers:

from markdown_it import MarkdownIt

class MultiFormatRenderer:
    """Wrapper supporting multiple output formats."""
    
    def __init__(self):
        self.html_renderer = RendererHTML()
        self.markdown_renderer = MarkdownRenderer()
    
    def render(self, tokens, options, env, format="html"):
        if format == "html":
            return self.html_renderer.render(tokens, options, env)
        elif format == "markdown":
            return self.markdown_renderer.render(tokens, options, env)
        else:
            raise ValueError(f"Unknown format: {format}")

# Usage
md = MarkdownIt()
tokens = md.parse("# Title\n\n**Bold** text.")

multi_renderer = MultiFormatRenderer()
html = multi_renderer.render(tokens, md.options, {}, "html")
markdown = multi_renderer.render(tokens, md.options, {}, "markdown")

Install with Tessl CLI

npx tessl i tessl/pypi-markdown-it-py

docs

cli.md

configuration.md

core-parsing.md

index.md

link-processing.md

rendering.md

syntax-tree.md

token-system.md

tile.json