Snowflake-Labs/jinjamap
Python
Captured source
source ↗Snowflake-Labs/jinjamap
Language: Python
License: Apache-2.0
Stars: 2
Forks: 0
Open issues: 0
Created: 2026-01-27T06:59:52Z
Pushed: 2026-01-28T16:56:26Z
Default branch: main
Fork: no
Archived: no
README:
jinjamap
Map rendered Jinja2 output lines back to original template lines. Essential for debugging SQL templates and tracking errors to their source in complex template projects.
Install
pip install jinjamap
Or install from source:
pip install -e .
Usage
from jinjamap import render_templates_with_mapping
templates = {
"query.sql": "SELECT * FROM {{ table }} WHERE id = {{ user_id }}"
}
result = render_templates_with_mapping(
templates,
{"table": "users", "user_id": 42}
)
print(result.rendered_files["query.sql"])
# SELECT * FROM users WHERE id = 42
for m in result.source_mappings["query.sql"]:
print(f"Output line {m.output_line} → Template line {m.template_line}")With Custom Environment
from jinja2 import Environment
from jinjamap import render_templates_with_mapping
env = Environment()
env.filters['upper'] = str.upper
env.globals['limit'] = 100
templates = {
"query.sql": "SELECT * FROM {{ table | upper }} LIMIT {{ limit }}"
}
result = render_templates_with_mapping(templates, {"table": "users"}, environment=env)Multiple Templates
templates = {
"users.sql": "SELECT * FROM {{ table1 }}",
"orders.sql": "SELECT * FROM {{ table2 }}"
}
result = render_templates_with_mapping(
templates,
{"table1": "users", "table2": "orders"}
)
print(result.rendered_files["users.sql"])
print(result.source_mappings["users.sql"])API
render_templates_with_mapping(templates, variables, environment=None, flat_mode=True)
Args:
templates: dict[str, str]- Dict mapping filenames to template stringsvariables: dict[str, Any]- Variables to pass to templatesenvironment: Environment | None- Optional Jinja2 Environmentflat_mode: bool- If True, omittemplate_filefrom mappings (default)
Returns: MultiTemplateResult with:
rendered_files: dict[str, str]- Rendered content by filenamesource_mappings: dict[str, list[SourceMapping]]- Mappings by filenameget_template_line(filename, output_line) → int | Noneget_template_file(filename, output_line) → str | None
SourceMapping
output_line: int- Line in rendered output (0-indexed)template_line: int- Line in template (0-indexed)template_file: str | None- Template filename (None whenflat_mode=True)
Flat Mode
flat_mode=True(default): Omittemplate_filefrom mappingsflat_mode=False: Includetemplate_file(for templates with includes/imports)
How It Works
The problem: when Jinja2 renders a template, there's no built-in way to know which output line came from which template line. Jinja2 itself doesn't provide source mapping functionality.
Alternative approaches like embedding custom Python functions in templates to track line numbers are too slow for large templates. This marker-based approach is up to 100x faster.
The solution: inject invisible markers before rendering, track them through the render, then remove them.
Process:
1. Analyze: Use Jinja2's lexer to find safe injection points (lines without {{ }} or {% %} blocks) 2. Mark: Insert comment markers with unique IDs at those lines 3. Render: Jinja2 processes the marked template normally 4. Track: Scan rendered output for marker positions 5. Clean: Remove all markers and build the line mappings
Example:
Original template (line 0):
SELECT * FROM {{ table }}
After marking:
-- __MARKER_0_0__query.sql
SELECT * FROM {{ table }}
After render:
-- __MARKER_0_0__query.sql
SELECT * FROM users
After cleanup:
SELECT * FROM users
Mapping: [SourceMapping(output_line=0, template_line=0)]The markers use a correlation ID to ensure they're unique and don't collide with actual template content.
License
Copyright (c) Snowflake Inc. All rights reserved. Licensed under the Apache 2.0 license.
Notability
notability 1.0/10Low stars, trivial repo.