A Python ASGI web framework with the same API as Flask
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Async Jinja2 template rendering with streaming support, Flask-compatible template context, and comprehensive template environment customization.
Async template rendering with full Jinja2 support and streaming capabilities for large templates.
async def render_template(template_name_or_list: str | list[str], **context) -> str:
"""
Render template with given context.
Args:
template_name_or_list: Template name or list of template names (first found is used)
**context: Template context variables
Returns:
Rendered template as string
"""
async def render_template_string(source: str, **context) -> str:
"""
Render template from string source.
Args:
source: Template source code
**context: Template context variables
Returns:
Rendered template as string
"""
async def stream_template(template_name_or_list: str | list[str], **context):
"""
Stream template rendering for large templates.
Args:
template_name_or_list: Template name or list of template names
**context: Template context variables
Returns:
AsyncIterator yielding template chunks
"""
async def stream_template_string(source: str, **context):
"""
Stream template rendering from string source.
Args:
source: Template source code
**context: Template context variables
Returns:
AsyncIterator yielding template chunks
"""Quart-specific Jinja2 environment with async support and Flask-compatible context.
class Environment:
"""
Quart-specific Jinja2 environment with async template support.
Provides Flask-compatible template context with additional async capabilities
and integration with Quart's request/response cycle.
"""
def get_template(self, name: str | list[str]):
"""Get template by name."""
def from_string(self, source: str):
"""Create template from string source."""
def select_template(self, names: list[str]):
"""Select first available template from list."""from quart import Quart, render_template, request
app = Quart(__name__)
@app.route('/')
async def index():
# Simple template rendering
return await render_template('index.html',
title='Welcome',
message='Hello, Quart!')
@app.route('/user/<username>')
async def user_profile(username):
# Template with dynamic data
user_data = await get_user_data(username)
return await render_template('user.html',
user=user_data,
current_user=await get_current_user())
@app.route('/dashboard')
async def dashboard():
# Template with multiple data sources
stats = await get_dashboard_stats()
recent_activity = await get_recent_activity()
notifications = await get_notifications()
return await render_template('dashboard.html',
stats=stats,
activity=recent_activity,
notifications=notifications,
page_title='Dashboard')from quart import Quart, render_template, request, g
app = Quart(__name__)
@app.before_request
async def before_request():
# Set up global template context
g.current_time = datetime.now()
g.user_agent = request.headers.get('User-Agent', '')
@app.context_processor
async def inject_template_vars():
# Add variables to all templates
return {
'app_name': 'My Quart App',
'version': '1.0.0',
'current_time': g.current_time,
'is_mobile': 'Mobile' in g.user_agent
}
@app.route('/page')
async def page():
# Template automatically has access to injected variables
return await render_template('page.html',
content='Page content here')from quart import Quart, render_template_string
app = Quart(__name__)
@app.route('/dynamic')
async def dynamic_template():
# Render template from string (useful for dynamic templates)
template_source = """
<h1>{{ title }}</h1>
<p>Current time: {{ current_time }}</p>
{% for item in items %}
<li>{{ item.name }}: {{ item.value }}</li>
{% endfor %}
"""
return await render_template_string(
template_source,
title='Dynamic Template',
current_time=datetime.now(),
items=[
{'name': 'Item 1', 'value': 'Value 1'},
{'name': 'Item 2', 'value': 'Value 2'}
]
)
@app.route('/email/preview')
async def email_preview():
# Generate email template preview
email_template = await get_email_template('welcome')
user_data = await get_sample_user_data()
return await render_template_string(
email_template.content,
user=user_data,
company_name='My Company',
unsubscribe_url='https://example.com/unsubscribe'
)from quart import Quart, Response, stream_template
import asyncio
app = Quart(__name__)
@app.route('/large-report')
async def large_report():
# Stream large template for better performance
async def generate():
async for chunk in stream_template('large_report.html',
data=await get_large_dataset(),
title='Annual Report'):
yield chunk
return Response(generate(), mimetype='text/html')
@app.route('/live-data')
async def live_data():
# Stream template with real-time data updates
async def generate():
template_start = """
<html><head><title>Live Data</title></head><body>
<h1>Live Data Stream</h1>
<div id="data">
"""
yield template_start
# Stream data updates
for i in range(100):
data_chunk = f"<p>Data point {i}: {await get_live_data_point()}</p>\n"
yield data_chunk
await asyncio.sleep(0.1) # Simulate real-time updates
template_end = """
</div>
<script>
// Auto-scroll to bottom
window.scrollTo(0, document.body.scrollHeight);
</script>
</body></html>
"""
yield template_end
return Response(generate(), mimetype='text/html')
async def get_large_dataset():
# Simulate large dataset
return [{'id': i, 'value': f'Item {i}'} for i in range(10000)]
async def get_live_data_point():
# Simulate live data
return f"Value at {datetime.now()}"from quart import Quart, render_template
from markupsafe import Markup
import json
app = Quart(__name__)
@app.template_filter('jsonify')
def jsonify_filter(value):
"""Custom filter to convert Python objects to JSON."""
return Markup(json.dumps(value, indent=2))
@app.template_filter('currency')
def currency_filter(value):
"""Format value as currency."""
try:
return f"${float(value):.2f}"
except (ValueError, TypeError):
return "$0.00"
@app.template_global()
def get_menu_items():
"""Global function available in all templates."""
return [
{'name': 'Home', 'url': '/'},
{'name': 'About', 'url': '/about'},
{'name': 'Contact', 'url': '/contact'}
]
@app.template_test('even')
def is_even(value):
"""Custom test for even numbers."""
try:
return int(value) % 2 == 0
except (ValueError, TypeError):
return False
@app.route('/demo')
async def template_demo():
return await render_template('demo.html',
data={'items': [1, 2, 3, 4, 5]},
price=29.99)Example demo.html template using custom functions:
<!DOCTYPE html>
<html>
<head>
<title>Template Demo</title>
</head>
<body>
<nav>
<ul>
{% for item in get_menu_items() %}
<li><a href="{{ item.url }}">{{ item.name }}</a></li>
{% endfor %}
</ul>
</nav>
<h1>Demo Page</h1>
<p>Price: {{ price | currency }}</p>
<h2>Data (JSON format):</h2>
<pre>{{ data | jsonify }}</pre>
<h2>Numbers:</h2>
{% for item in data.items %}
<p>{{ item }} is {% if item is even %}even{% else %}odd{% endif %}</p>
{% endfor %}
</body>
</html>from quart import Quart, render_template, abort
from jinja2 import TemplateNotFound, TemplateSyntaxError
app = Quart(__name__)
@app.route('/safe-render/<template_name>')
async def safe_render(template_name):
try:
# Safely render user-specified template
safe_templates = ['page1.html', 'page2.html', 'page3.html']
if template_name not in safe_templates:
abort(404)
return await render_template(f'safe/{template_name}',
content='Safe content')
except TemplateNotFound:
# Handle missing template
return await render_template('error.html',
error='Template not found',
code=404), 404
@app.errorhandler(TemplateSyntaxError)
async def handle_template_error(error):
# Handle template syntax errors
if app.debug:
return f"Template Error: {error}", 500
else:
return await render_template('error.html',
error='Template rendering failed',
code=500), 500
@app.route('/fallback-template')
async def fallback_template():
# Try multiple templates, use first found
template_options = [
'custom_page.html',
'default_page.html',
'fallback.html'
]
return await render_template(template_options,
title='Fallback Demo',
message='This uses template fallback')Install with Tessl CLI
npx tessl i tessl/pypi-quart