The Rupy framework now supports server-side template rendering using the Handlebars template engine. This feature allows you to create dynamic HTML pages (or any text-based content) by combining templates with data from your Python handlers.
Template rendering is built into Rupy - no additional installation required!
Create a template file in the ./template directory (default location):
template/hello.tpl:
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<h1>{{greeting}}, {{name}}!</h1>
<p>{{message}}</p>
</body>
</html>from rupy import Rupy, Request
app = Rupy()
@app.template("/", template="hello.tpl")
def index(request: Request) -> dict:
"""Handler returns a dict that becomes the template context."""
return {
"title": "Welcome",
"greeting": "Hello",
"name": "World",
"message": "Welcome to Rupy template rendering!"
}
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8000)python your_app.py
curl http://127.0.0.1:8000/Decorator to register a template-based route handler.
Parameters:
path(str): URL path pattern (e.g.,"/","/user/<username>")template(str): Template filename relative to template directory (e.g.,"index.tpl")content_type(str, optional): Response content type. Defaults to"text/html"
Returns:
The decorated function must return a dict that will be used as the template context.
Example:
@app.template("/user/<username>", template="user.tpl")
def user_profile(request: Request, username: str) -> dict:
return {
"username": username,
"user_id": 12345
}Configure the directory where template files are located.
Parameters:
directory(str): Path to template directory
Default: "./template"
Example:
app = Rupy()
app.set_template_directory("./templates")Get the current template directory path.
Returns:
str: Current template directory path
Example:
current_dir = app.get_template_directory()
print(f"Templates are in: {current_dir}")Rupy uses the Handlebars template engine. Here are some common patterns:
Variables are HTML-escaped by default:
Template routes support dynamic parameters just like regular routes:
@app.template("/post/<post_id>", template="post.tpl")
def show_post(request: Request, post_id: str) -> dict:
return {
"post_id": post_id,
"title": f"Post {post_id}",
"content": "Post content here..."
}You can use templates for any text-based format:
@app.template("/api/data", template="data.json", content_type="application/json")
def json_template(request: Request) -> dict:
return {
"status": "success",
"count": 42
}template/data.json:
{
"status": "{{status}}",
"count": {{count}},
"timestamp": "{{timestamp}}"
}app = Rupy()
app.set_template_directory("./my_templates")
@app.template("/", template="home.tpl")
def home(request: Request) -> dict:
return {"message": "Hello"}You can configure multiple template directories for flexible template lookup. Templates are searched in the order directories were added:
app = Rupy()
app.set_template_directory("./templates") # Primary directory
app.add_template_directory("./shared_templates") # Fallback directory
app.add_template_directory("./common_templates") # Second fallback
# Get all configured directories
dirs = app.get_template_directories()
print(f"Template directories: {dirs}")
# Remove a directory if needed
app.remove_template_directory("./common_templates")How it works:
- When rendering a template, Rupy searches each directory in order
- The first matching template file is used
- This allows for template override patterns and shared template libraries
The Template class allows you to render templates programmatically without creating a route. This is useful for generating emails, reports, or other dynamic content:
from rupy import Rupy, Template
app = Rupy()
app.set_template_directory("./templates")
# Create a template instance
email_template = Template(app, "email.tpl")
# Render with context data
def send_welcome_email(user_email: str, user_name: str):
rendered = email_template.render({
"email": user_email,
"name": user_name,
"subject": "Welcome!",
"message": "Thanks for signing up!"
})
# Send the rendered email...
return renderedTemplate Class API:
-
Template(app, template_name)- Create a template instanceapp: Rupy application instancetemplate_name: Template filename (e.g., "email.tpl")
-
template.render(context)- Render the templatecontext: Dictionary with template variables- Returns: Rendered string
Example Use Cases:
- Email Generation: Render email templates dynamically
- Report Generation: Create PDF or text reports
- Data Export: Generate CSV, JSON, or XML from templates
- Batch Processing: Render multiple templates in a loop
from rupy import Rupy, Request, Template
app = Rupy()
@app.get("/send-report")
def send_report(request: Request) -> str:
# Render report template programmatically
report = Template(app, "report.tpl")
rendered = report.render({
"title": "Monthly Report",
"data": "Sales: $10,000"
})
# Could send via email, save to file, etc.
return renderedIf a template file doesn't exist, Rupy returns a 500 error with details:
Template rendering error: Failed to read template file './template/missing.tpl': No such file or directory
If a template has invalid Handlebars syntax, you'll get a parse error:
Template rendering error: Failed to parse template: ...
Template handlers must return a dict:
@app.template("/bad", template="test.tpl")
def bad_handler(request: Request) -> str:
return "This will error!" # Must return dict!Error: Template handler must return a dict
- Organize Templates: Keep templates in a dedicated directory
- Consistent Naming: Use
.tplor.htmlextension for templates - Return Type: Always return a dict from template handlers
- Variable Names: Use clear, descriptive variable names in both Python and templates
- Content Type: Set appropriate content type for non-HTML templates
- Multiple Directories: Use primary directory for app-specific templates and fallback directories for shared templates
- Template Class: Use
Templateclass for programmatic rendering (emails, reports, etc.)
examples/template_example.py- Basic template decorator usageexamples/template_class_example.py- Template class and multiple directories
- XSS Protection: Handlebars automatically escapes HTML by default
- Path Traversal: Template directory is set at application level, not from user input
- File Access: Only files within the configured template directory can be accessed
- Verify template file exists in one of the configured directories
- Check file name and extension match exactly
- Use
app.get_template_directories()to see all search paths - Templates are searched in the order directories were added
- Ensure variable names in template match dict keys exactly
- Check for typos in variable names
- Verify handler is returning the dict correctly
- Specify
content_typeparameter if not using HTML - Ensure template syntax matches the content type (e.g., valid JSON for JSON templates)
- First directory has highest priority
- Use
app.get_template_directories()to check search order - Templates in earlier directories override those in later ones