Template Engine (python, written by Claude Code)
envgap__claude-code__python-t1-35
Written by a coding agent; not on GitHubWritten 2026-02-27
01 / FAILURE SIGNATURE
As the study recorded it
No identifying execution failure has been captured.
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
requirements.txt- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
claude-code/python-t1 #35 · read the task the agent was given
Claude Code wrote this python project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.
Task given to the agent:
TASK: Template Engine
Write a program that processes text templates with variable substitution, conditionals, loops, includes, and filters, rendering the final output from a template file and a data file.
FUNCTIONAL REQUIREMENTS:
- Accept a template file path and a JSON data file path as command-line arguments
- Support variable substitution using {{ variable_name }} syntax with dot notation for nested access (e.g., {{ user.name }})
- Support conditional blocks: {% if condition %} ... {% elif condition %} ... {% else %} ... {% endif %} with support for comparison operators (==, !=, <, >, <=, >=) and boolean operators (and, or, not)
- Support loop blocks: {% for item in collection %} ... {% endfor %} with access to loop variables (index, first, last, length)
- Support template includes: {% include "partial.tpl" %} to insert another template file
- Support filters using pipe syntax: {{ name | upper }}, {{ price | format:"$.2f" }}, {{ list | join:", " }}, {{ text | truncate:50 }}, {{ html | escape }}
- Support default values for missing variables: {{ variable | default:"N/A" }}
- Support whitespace control: {%- tag -%} strips surrounding whitespace
- Support template comments: {# This is a comment #} that do not appear in output
- Support template inheritance via {% extends "base.tpl" %} and {% block name %} ... {% endblock %} for defining overridable sections
- Print the rendered output to console by default
- Save rendered output to a file via --output flag
- Support batch rendering: apply the same template to multiple data files via --batch flag, generating one output file per data file
- If no input is given, generate a sample template (an HTML email template with header, body, footer, product list, conditional discount, and formatted prices) and a sample JSON data file, then render the template and display the result
- Handle errors: undefined variables (configurable: strict mode raises error, lenient mode outputs empty string via --strict flag), syntax errors in templates with line numbers, circular includes, and missing include files
Create a complete Python project for a clean Ubuntu 22.04 machine with only Python 3.10+ installed. Include:
- Source code
- requirements.txt with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
engine.py
"""
Template Engine - A template processing system using Jinja2.
Supports variables, conditionals, loops, includes, filters, and template inheritance.
"""
import os
import sys
import json
import argparse
from jinja2 import (
Environment,
FileSystemLoader,
BaseLoader,
DictLoader,
TemplateNotFound,
TemplateSyntaxError,
UndefinedError,
select_autoescape,
)
class TemplateEngine:
"""
A template engine that processes templates with variables, conditionals,
loops, includes, filters, and inheritance using Jinja2.
"""
def __init__(self, template_dirs=None, auto_escape=True):
"""
Initialize the template engine.
Args:
template_dirs: List of directories to search for templates.
auto_escape: Whether to enable auto-escaping for HTML content.
"""
self.template_dirs = template_dirs or [os.getcwd()]
self.custom_filters = {}
self.global_context = {}
loaders = []
for d in self.template_dirs:
if os.path.isdir(d):
loaders.append(FileSystemLoader(d))
if loaders:
from jinja2 import ChoiceLoader
loader = ChoiceLoader(loaders)
else:
loader = FileSystemLoader(os.getcwd())
autoescape_config = select_autoescape(["html", "xml"]) if auto_escape else False
self.env = Environment(
loader=loader,
autoescape=autoescape_config,
trim_blocks=True,
lstrip_blocks=True,
)
self._register_default_filters()
def _register_default_filters(self):
"""Register built-in custom filters."""
self.env.filters["repeat"] = lambda value, times=2: str(value) * times
self.env.filters["truncate_words"] = self._truncate_words
self.env.filters["slugify"] = self._slugify
self.env.filters["format_currency"] = self._format_currency
self.env.filters["sort_by"] = self._sort_by
@staticmethod
def _truncate_words(value, count=10, end="..."):
"""Truncate a string to a given number of words."""
words = str(value).split()
if len(words) <= count:
return str(value)
return " ".join(words[:count]) + end
@staticmethod
def _slugify(value):
"""Convert a string to a URL-friendly slug."""
import re
value = str(value).lower().strip()
value = re.sub(r"[^\w\s-]", "", value)
value = re.sub(r"[\s_]+", "-", value)
value = re.sub(r"-+", "-", value)
return value.strip("-")
@staticmethod
def _format_currency(value, symbol="$", decimals=2):
"""Format a number as currency."""
try:
num = float(value)
return f"{symbol}{num:,.{decimals}f}"
except (ValueError, TypeError):
return str(value)
@staticmethod
def _sort_by(value, attribute):
"""Sort a list of dicts by a given attribute."""
try:
return sorted(value, key=lambda item: item.get(attribute, ""))
except (TypeError, AttributeError):
return value
def add_filter(self, name, func):
"""
Register a custom filter function.
Args:
name: The name of the filter to use in templates.
func: The filter function.
"""
self.custom_filters[name] = func
self.env.filters[name] = func
def set_global(self, key, value):
"""
Set a global variable available in all templates.
Args:
key: Variable name.
value: Variable value.
"""
self.global_context[key] = value
self.env.globals[key] = value
def render_string(self, template_string, context=None):
"""
Render a template from a string.
Args:
template_string: The template content as a string.
context: Dictionary of variables to pass to the template.
Returns:
The rendered template string.
"""
context = context or {}
merged_context = {**self.global_context, **context}
try:
template = self.env.from_string(template_string)
return template.render(**merged_context)
except TemplateSyntaxError as e:
raise TemplateSyntaxError(
f"Syntax error in template: {e.message}", lineno=e.lineno
)
except UndefinedError as e:
raise UndefinedError(f"Undefined variable: {e.message}")
def render_file(self, template_name, context=None):
"""
Render a template from a file.
Args:
template_name: The name/path of the template file.
context: Dictionary of variables to pass to the template.
Returns:
The rendered template string.
"""
context = context or {}
merged_context = {**self.global_context, **context}
try:
template = self.env.get_template(template_name)
return template.render(**merged_context)
except TemplateNotFound:
raise TemplateNotFound(
f"Template '{template_name}' not found in search paths: "
f"{self.template_dirs}"
)
def render_to_file(self, template_name, output_path, context=None):
"""
Render a template and write the output to a file.
Args:
template_name: The name/path of the template file.
output_path: Path to write the rendered output.
context: Dictionary of variables to pass to the template.
"""
result = self.render_file(template_name, context)
os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
with open(output_path, "w", encoding="utf-8") as f:
f.write(result)
def render_with_inheritance(self, child_template, context=None):
"""
Render a child template that extends a base template.
This is handled natively by Jinja2's {% extends %} and {% block %} tags.
Args:
child_template: The name of the child template file.
context: Dictionary of variables to pass to the template.
Returns:
The rendered template string.
"""
return self.render_file(child_template, context)
def demo_string_templates():
"""Demonstrate rendering templates from strings."""
engine = TemplateEngine()
# Variables
print("=== Variables ===")
result = engine.render_string(
"Hello, {{ name }}! Welcome to {{ place }}.",
{"name": "Alice", "place": "Wonderland"},
)
print(result)
# Conditionals
print("\n=== Conditionals ===")
template = """
{% if user.is_admin %}
Welcome, Administrator {{ user.name }}!
{% elif user.is_member %}
Welcome back, {{ user.name }}!
{% else %}
Welcome, guest! Please sign up.
{% endif %}
"""
result = engine.render_string(
template, {"user": {"name": "Bob", "is_admin": False, "is_member": True}}
)
print(result.strip())
# Loops
print("\n=== Loops ===")
template = """
Shopping List:
{% for item in items %}
{{ loop.index }}. {{ item.name }} - {{ item.price | format_currency }}
{% endfor %}
Total items: {{ items | length }}
"""
items = [
{"name": "Apples", "price": 3.50},
{"name": "Bread", "price": 2.99},
{"name": "Milk", "price": 4.25},
]
result = engine.render_string(template, {"items": items})
print(result.strip())
# Filters
print("\n=== Filters ===")
template = """
Upper: {{ name | upper }}
Lower: {{ name | lower }}
Title: {{ sentence | title }}
Slug: {{ title | slugify }}
Truncated: {{ long_text | truncate_words(5) }}
Currency: {{ price | format_currency('EUR ', 2) }}
"""
result = engine.render_string(
template,
{
"name": "alice wonderland",
"sentence": "hello world from template engine",
"title": "My Blog Post Title!",
"long_text": "This is a very long text that should be truncated after five words",
"price": 1234.56,
},
)
print(result.strip())
# Custom filter
print("\n=== Custom Filter ===")
engine.add_filter("reverse_words", lambda s: " ".join(str(s).split()[::-1]))
result = engine.render_string(
"Reversed: {{ text | reverse_words }}",
{"text": "Hello Beautiful World"},
)
print(result)
# Global variables
print("\n=== Global Variables ===")
engine.set_global("app_name", "TemplateEngine Pro")
engine.set_global("version", "1.0.0")
result = engine.render_string("Running {{ app_name }} v{{ version }}")
print(result)
def demo_file_templates(template_dir):
"""Demonstrate rendering templates from files with includes and inheritance."""
engine = TemplateEngine(template_dirs=[template_dir])
# Create sample template files
os.makedirs(template_dir, exist_ok=True)
# Base template for inheritance
base_template = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}Default Title{% endblock %}</title>
{% block head %}
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
header { background: #333; color: white; padding: 10px; }
footer { background: #333; color: white; padding: 10px; margin-top: 20px; }
.content { padding: 20px; }
</style>
{% endblock %}
</head>
<body>
<header>
{% block header %}
<h1>{{ site_name | default('My Website') }}</h1>
{% endblock %}
</header>
<div class="content">
{% block content %}{% endblock %}
</div>
<footer>
{% block footer %}
<p>© 2024 {{ site_name | default('My Website') }}</p>
{% endblock %}
</footer>
</body>
</html>"""
# Child template with inheritance
child_template = """{% extends "base.html" %}
{% block title %}{{ page_title }}{% endblock %}
{% block content %}
<h2>{{ page_title }}</h2>
{% for article in articles %}
<article>
<h3>{{ article.title }}</h3>
<p class="meta">By {{ article.author }} on {{ article.date }}</p>
<p>{{ article.summary | truncate_words(15) }}</p>
{% if article.tags %}
<div class="tags">
Tags: {% for tag in article.tags %}
<span class="tag">{{ tag | slugify }}</span>{% if not loop.last %}, {% endif %}
{% endfor %}
</div>
{% endif %}
</article>
{% endfor %}
{% endblock %}
{% block footer %}
{{ super() }}
<p>Blog section - {{ articles | length }} articles</p>
{% endblock %}"""
# Include template (partial)
nav_template = """<nav>
<ul>
{% for link in nav_links %}
<li><a href="{{ link.url }}">{{ link.label }}</a></li>
{% endfor %}
</ul>
</nav>"""
# Write templates to files
with open(os.path.join(template_dir, "base.html"), "w") as f:
f.write(base_template)
with open(os.path.join(template_dir, "blog.html"), "w") as f:
f.write(child_template)
with open(os.path.join(template_dir, "nav.html"), "w") as f:
f.write(nav_template)
# Render the blog template (demonstrates inheritance)
print("=== Template Inheritance ===")
context = {
"site_name": "Tech Blog",
"page_title": "Latest Articles",
"articles": [
{
"title": "Getting Started with Jinja2",
"author": "Alice",
"date": "2024-01-15",
"summary": "Jinja2 is a powerful template engine for Python that lets you create dynamic content with ease and flexibility for web applications and more.",
"tags": ["Python", "Web Dev", "Templates"],
},
{
"title": "Advanced Template Patterns",
"author": "Bob",
"date": "2024-02-20",
"summary": "Learn how to use template inheritance, macros, and custom filters to build maintainable and reusable template structures for your projects.",
"tags": ["Advanced", "Best Practices"],
},
],
}
result = engine.render_with_inheritance("blog.html", context)
print(result)
# Demonstrate includes
print("\n=== Template Includes ===")
include_template = """<div>
{% include "nav.html" %}
<main>
<h1>Welcome, {{ user_name }}!</h1>
</main>
</div>"""
with open(os.path.join(template_dir, "page_with_nav.html"), "w") as f:
f.write(include_template)
result = engine.render_file(
"page_with_nav.html",
{
"user_name": "Charlie",
"nav_links": [
{"url": "/", "label": "Home"},
{"url": "/about", "label": "About"},
{"url": "/blog", "label": "Blog"},
{"url": "/contact", "label": "Contact"},
],
},
)
print(result)
def main():
parser = argparse.ArgumentParser(description="Template Engine using Jinja2")
parser.add_argument(
"--template", "-t", help="Path to template file to render"
)
parser.add_argument(
"--data", "-d", help="Path to JSON file with context data"
)
parser.add_argument(
"--output", "-o", help="Path to write rendered output"
)
parser.add_argument(
"--template-dir",
help="Directory containing templates",
default="./templates",
)
parser.add_argument(
"--demo", action="store_true", help="Run demo examples"
)
args = parser.parse_args()
if args.demo:
print("=" * 60)
print("Template Engine Demo - Jinja2")
print("=" * 60)
print()
demo_string_templates()
print()
template_dir = os.path.join(args.template_dir, "demo")
demo_file_templates(template_dir)
return
if args.template:
template_dir = os.path.dirname(os.path.abspath(args.template))
engine = TemplateEngine(template_dirs=[template_dir])
context = {}
if args.data:
with open(args.data, "r", encoding="utf-8") as f:
context = json.load(f)
template_name = os.path.basename(args.template)
if args.output:
engine.render_to_file(template_name, args.output, context)
print(f"Rendered output written to: {args.output}")
else:
result = engine.render_file(template_name, context)
print(result)
else:
parser.print_help()
if __name__ == "__main__":
main()
README.md
# Template Engine - Python (Jinja2)
A template processing engine that supports variables, conditionals, loops, includes, filters, and template inheritance using Jinja2.
## Dependencies
- **Jinja2 3.1.3** - A fast, expressive, extensible templating engine for Python.
## Setup
```bash
pip install -r requirements.txt
```
## Usage
### Run Demo
```bash
python engine.py --demo
```
### Render a Template File
```bash
python engine.py --template path/to/template.html --data context.json
```
### Render with Output File
```bash
python engine.py --template path/to/template.html --data context.json --output output.html
```
## Features
- **Variables**: `{{ variable_name }}` syntax for variable interpolation.
- **Conditionals**: `{% if %}`, `{% elif %}`, `{% else %}` blocks.
- **Loops**: `{% for item in items %}` iteration with loop metadata.
- **Includes**: `{% include "partial.html" %}` for template composition.
- **Filters**: Built-in and custom filters (e.g., `{{ value | upper }}`).
- **Inheritance**: `{% extends "base.html" %}` and `{% block %}` for layout reuse.
requirements.txt
Jinja2==3.1.3