Classes
VariableSubstitutionPlugin
Mistune plugin for safe variable substitution in markdown content.
ARCHITECTURE: Separation of Con…
VariableSubstitutionPlugin
Mistune plugin for safe variable substitution in markdown content.
ARCHITECTURE: Separation of Concerns
This plugin handles ONLY variable substitution ({{ vars }}) in markdown. It operates at the AST level after Mistune parses the markdown structure.
WHAT THIS HANDLES:
✅ {{ page.metadata.xxx }} - Access page frontmatter ✅ {{ site.config.xxx }} - Access site configuration ✅ {{ page.title }}, {{ page.date }}, etc. - Page properties
WHAT THIS DOESN'T HANDLE:
❌ {% if condition %} - Conditional blocks ❌ {% for item %} - Loop constructs ❌ Complex Jinja2 logic
WHY: Conditionals and loops belong in TEMPLATES, not markdown.
Example - Using in Markdown: Welcome to {{ page.metadata.product_name }} version {{ page.metadata.version }}.
Connect to {{ page.metadata.api_url }}/users
Example - Escaping Syntax: Use {{/* page.title */}} to display the page title.
This renders as: Use {{ page.title }} to display the page title.
Example - Using Conditionals in Templates: <!-- templates/page.html --> <article> {% if page.metadata.beta %} <div class="beta-notice">Beta Feature</div> {% endif %}
{{ content }} <!-- Markdown with {{ vars }} renders here -->
</article>
KEY FEATURE: Code blocks stay literal naturally!
Since this plugin only processes text tokens (not code tokens), code blocks and inline code automatically preserve their content:
Use `{{ page.title }}` to show the title. ← Stays literal in output
```python
# This {{ var }} stays literal too!
print("{{ page.title }}")
```
This is the RIGHT architectural approach:
- Single-pass parsing (fast!)
- Natural code block handling (no escaping needed!)
- Clear separation: content (markdown) vs logic (templates)
Methods 4
update_context
Update the rendering context (for parser reuse).
update_context
def update_context(self, context: dict[str, Any]) -> None
Update the rendering context (for parser reuse).
Parameters 1
context |
dict[str, Any] |
New context dict with variables (page, site, config, etc.) |
preprocess
Handle escaped syntax {{/* ... */}} before parsing.
preprocess
def preprocess(self, text: str) -> str
Handle escaped syntax {{/* ... */}} before parsing.
Parameters 1
text |
str |
Returns
str
substitute_variables
Substitute {{ variable }} expressions in text nodes.
substitute_variables
def substitute_variables(self, text: str) -> str
Substitute {{ variable }} expressions in text nodes.
Parameters 1
text |
str |
Returns
str
restore_placeholders
Restore placeholders to HTML-escaped template syntax.
This uses HTML entities …
restore_placeholders
def restore_placeholders(self, html: str) -> str
Restore placeholders to HTML-escaped template syntax.
This uses HTML entities to prevent Jinja2 from processing the restored template syntax. The browser will render {{ as {{ in the final output.
This is the correct long-term solution because:
- Jinja2 won't see {{ so it won't try to template it
- The browser renders entities as literal {{ for users to see
- No timing issues or re-processing concerns
- Works for documentation examples, code snippets, etc.
Parameters 1
html |
str |
HTML output from Mistune |
Returns
HTML with placeholders restored as HTML entitiesstr
—
Internal Methods 4
__init__
Initialize with rendering context.
__init__
def __init__(self, context: dict[str, Any])
Initialize with rendering context.
Parameters 1
context |
dict[str, Any] |
Dict with variables (page, site, config, etc.) |
__call__
Register the plugin with Mistune.
__call__
def __call__(self, md: Any) -> None
Register the plugin with Mistune.
Parameters 1
md |
Any |
_substitute_variables
Legacy method combining preprocess and substitution.
Kept for backward compatib…
_substitute_variables
def _substitute_variables(self, text: str) -> str
Legacy method combining preprocess and substitution. Kept for backward compatibility if needed, but splitting usage is preferred.
Parameters 1
text |
str |
Returns
str
_eval_expression
Safely evaluate a simple expression like 'page.metadata.title'.
Supports dot n…
_eval_expression
def _eval_expression(self, expr: str) -> Any
Safely evaluate a simple expression like 'page.metadata.title'.
Supports dot notation for accessing nested attributes/dict keys.
SECURITY: Blocks access to private/dunder attributes to prevent:
- {{ page.class.bases }}
- {{ config.init.globals }}
- {{ page._private_field }}
Parameters 1
expr |
str |
Expression to evaluate (e.g., 'page.metadata.title') |
Returns
Evaluated resultAny
—