CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-quart

A Python ASGI web framework with the same API as Flask

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

templates.mddocs/

Template System

Async Jinja2 template rendering with streaming support, Flask-compatible template context, and comprehensive template environment customization.

Capabilities

Template Rendering Functions

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
    """

Template Environment

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."""

Usage Examples

Basic Template Rendering

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')

Template with Request Context

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')

Template String Rendering

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'
    )

Streaming Templates

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()}"

Custom Template Functions and Filters

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>

Error Handling in Templates

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

docs

context.md

core-application.md

helpers.md

index.md

request-response.md

signals.md

templates.md

testing.md

websocket.md

tile.json