Name | starlette-async-jinja JSON |
Version |
1.12.6
JSON |
| download |
home_page | None |
Summary | None |
upload_time | 2025-07-16 06:38:56 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.13 |
license | BSD-3-Clause |
keywords |
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# starlette-async-jinja
[](https://github.com/lesleslie/crackerjack)
[](https://www.python.org/downloads/)
An asynchronous Jinja2 template integration for Starlette and FastAPI, built on top of the `jinja2-async-environment` package.
## Features
- **Fully async template rendering** - Load templates and render them asynchronously
- **Seamless framework integration** - Works with Starlette and FastAPI request/response cycles
- **Template fragments** - Render specific blocks from templates
- **Template partials** - Include sub-templates with their own context using `render_block`
- **Fast JSON responses** - Enhanced JSON responses using `msgspec` for faster serialization
- **Context processors** - Add global context to all templates
- **Performance optimizations** - Context processor caching, fragment caching, and memory pooling
- **Configurable caching** - Fine-tune cache sizes and TTL for optimal performance
## Installation
```bash
pip install starlette-async-jinja
```
## Requirements
- Python 3.13+
- Starlette
- Jinja2
- jinja2-async-environment
- anyio
- msgspec
## Basic Usage
### Starlette Example
```python
from starlette.applications import Starlette
from starlette.routing import Route
from anyio import Path as AsyncPath
from starlette_async_jinja import AsyncJinja2Templates
# Initialize templates with an async path
templates = AsyncJinja2Templates(directory=AsyncPath("templates"))
async def homepage(request):
return await templates.TemplateResponse(
request, "index.html", {"message": "Hello, world!"}
)
# Or using the alias
async def about(request):
return await templates.render_template(
request, "about.html", {"message": "About page"}
)
# Define routes
app = Starlette(routes=[Route("/", homepage), Route("/about", about)])
```
### FastAPI Example
```python
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from anyio import Path as AsyncPath
from starlette_async_jinja import AsyncJinja2Templates, JsonResponse
app = FastAPI()
# Initialize templates with an async path
templates = AsyncJinja2Templates(directory=AsyncPath("templates"))
@app.get("/", response_class=HTMLResponse)
async def homepage(request: Request):
"""Render the homepage template."""
return await templates.TemplateResponse(
request,
"index.html",
{
"title": "FastAPI with Async Jinja2",
"message": "Welcome to FastAPI with async template rendering!",
},
)
@app.get("/about", response_class=HTMLResponse)
async def about(request: Request):
"""Render the about page template."""
return await templates.render_template(
request,
"about.html",
{"title": "About Us", "message": "Learn more about our company"},
)
@app.get("/api/data")
async def get_data():
"""Return JSON data using the optimized JsonResponse."""
return JsonResponse(
{
"items": [
{"id": 1, "name": "Item 1"},
{"id": 2, "name": "Item 2"},
{"id": 3, "name": "Item 3"},
]
}
)
```
## Using Template Partials with `render_block`
The `render_block` feature allows you to render entire template files as reusable components, inspired by [jinja_partials](https://github.com/mikeckennedy/jinja_partials). This is especially useful for creating modular template components.
### Component Templates
**templates/components/alert.html**
```html
<div class="alert alert-{{ type | default('info') }}">
<h4 class="alert-heading">{{ title }}</h4>
<p>{{ message }}</p>
{% if dismissible %}
<button type="button" class="close" data-dismiss="alert">×</button>
{% endif %}
</div>
```
### Using Components in Your Templates
**templates/index.html**
```html
{% extends "base.html" %}
{% block content %}
<div class="container">
<h1>Welcome to {{ site_name }}</h1>
{# Instead of a macro, use render_block #}
{{ render_block('components/alert.html',
type='warning',
title='Attention!',
message='This is an important notice.',
dismissible=True) }}
{% for item in items %}
{# Another component rendered with render_block #}
{{ render_block('components/card.html',
title=item.title,
content=item.description,
image_url=item.image) }}
{% endfor %}
</div>
{% endblock %}
```
### Important Notes on `render_block`
- Each component only receives the variables explicitly passed to it
- `render_block` renders entire template files as components
- The `markup=True` parameter (default) ensures proper HTML escaping
## Context Processors
Context processors allow you to add global context to all templates:
```python
def global_context(request):
return {"site_name": "My Awesome Site", "current_year": 2024}
templates = AsyncJinja2Templates(
directory=AsyncPath("templates"), context_processors=[global_context]
)
# Now all templates will have access to site_name and current_year
```
### Context Processors with FastAPI
```python
from fastapi import FastAPI, Request
from anyio import Path as AsyncPath
from starlette_async_jinja import AsyncJinja2Templates
app = FastAPI()
# Define context processors
def global_context(request):
return {"site_name": "My FastAPI App", "current_year": 2024, "version": "1.0.0"}
def user_context(request):
# In a real app, you might get this from a session or JWT
return {"user": {"name": "Guest User"}}
# Initialize templates with context processors
templates = AsyncJinja2Templates(
directory=AsyncPath("templates"), context_processors=[global_context, user_context]
)
@app.get("/")
async def homepage(request: Request):
# These variables will be automatically available in all templates:
# - site_name
# - current_year
# - version
# - user
return await templates.TemplateResponse(
request, "index.html", {"title": "Home Page"}
)
```
## Using Template Fragments
Fragments allow you to render specific blocks from within a template:
```html
<!-- In your template (page.html) -->
{% block header %}
<h1>Welcome to {{ site_name }}</h1>
{% endblock %}
{% block footer %}
<footer>© {{ year }} {{ company_name }}</footer>
{% endblock %}
```
### Starlette Example
```python
from starlette.responses import HTMLResponse
# In your route handler:
async def render_header(request):
content = await templates.render_fragment(
"page.html", "header", site_name="My Awesome Site"
)
return HTMLResponse(content)
```
### FastAPI Example
```python
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
app = FastAPI()
templates = AsyncJinja2Templates(directory=AsyncPath("templates"))
@app.get("/header", response_class=HTMLResponse)
async def get_header(request: Request):
"""Return just the header fragment."""
content = await templates.render_fragment(
"page.html", "header", site_name="My FastAPI Site"
)
return HTMLResponse(content)
@app.get("/footer", response_class=HTMLResponse)
async def get_footer(request: Request):
"""Return just the footer fragment."""
content = await templates.render_fragment(
"page.html", "footer", year=2024, company_name="My Company"
)
return HTMLResponse(content)
```
## JsonResponse
Enhanced JSON response using `msgspec` for faster serialization:
```python
from starlette_async_jinja import JsonResponse
# Starlette example
async def api_endpoint(request):
data = {"name": "John", "email": "john@example.com"}
return JsonResponse(data)
# FastAPI example
@app.get("/api/user")
async def get_user():
data = {"name": "John", "email": "john@example.com"}
return JsonResponse(data) # Faster than FastAPI's default JSONResponse
```
## Jinja2 Macros Support
Jinja2 macros are fully supported in async templates with the updated `jinja2-async-environment>=0.13`:
```html
<!-- templates/components.html -->
{% macro alert(type, message) %}
<div class="alert alert-{{ type }}">{{ message }}</div>
{% endmacro %}
{% macro button(text, style='primary') %}
<button class="btn btn-{{ style }}">{{ text }}</button>
{% endmacro %}
```
### Using Macros in Templates
Macros work seamlessly with `render_block` when called within templates:
```html
<!-- templates/page.html -->
{% from 'components.html' import alert, button %}
<h1>Welcome!</h1>
{{ alert('info', 'Welcome to our site!') }}
{{ button('Get Started', 'success') }}
```
```python
# This works perfectly with macros inside
content = await templates.render_block("page.html", {})
```
### Direct Macro Access
For advanced use cases, macros can be called directly from template modules:
```python
async def render_macro_component():
template = await templates.env.get_template_async("components.html")
module = await template.make_module_async()
# Call macro directly and await the result
alert_html = await module.alert("warning", "Direct macro call")
return alert_html
```
### Choosing Your Approach
- **Macros**: Traditional Jinja2 components defined within templates, great for simple reusable elements
- **render_block**: Renders entire template files as components, useful for complex reusable template partials
- **render_fragment**: Renders specific named blocks from templates, ideal for partial page updates
## Issues and Limitations
- Only [asynchronous template loaders](https://github.com/lesleslie/jinja2-async-environment/blob/main/jinja2_async_environment/loaders.py) are fully supported
- The Jinja bytecodecache requires an asynchronous Redis backend
## API Reference
### AsyncJinja2Templates
```python
templates = AsyncJinja2Templates(
directory=AsyncPath("templates"),
context_processors=[global_context],
# Performance optimization options
context_cache_size=128, # Context processor cache size
context_cache_ttl=300.0, # Context cache TTL (seconds)
fragment_cache_size=64, # Fragment block cache size
fragment_cache_ttl=600.0, # Fragment cache TTL (seconds)
context_pool_size=10, # Context object pool size
fragment_stringio_threshold=1024, # StringIO threshold for large fragments
# Standard Jinja2 environment options
autoescape=True,
**env_options,
)
```
#### Methods
- `async TemplateResponse(request, name, context={}, status_code=200, headers=None, media_type=None, background=None)` - Render a template to a response
- `async render_template(request, name, context={}, status_code=200, headers=None, media_type=None, background=None)` - Alias for TemplateResponse
- `async render_fragment(template_name, block_name, **kwargs)` - Render a specific block from a template
- `async render_block(template_name, markup=True, **data)` - Render a template as a partial with optional markup escaping (default: True)
- `async get_template_async(name)` - Get a template by name
### JsonResponse
Enhanced JSON response using `msgspec` for faster serialization.
### BlockNotFoundError
Exception raised when attempting to render a template block that doesn't exist.
## Performance Optimizations
This package includes several built-in performance optimizations:
### Context Processor Caching
Context processors are automatically cached based on request properties to avoid recomputation:
```python
def expensive_context_processor(request):
# This will only run once per unique request path/method combination
# and be cached for subsequent requests
return {
"expensive_data": fetch_expensive_data(),
"computed_value": perform_complex_calculation(),
}
templates = AsyncJinja2Templates(
directory=AsyncPath("templates"),
context_processors=[expensive_context_processor],
context_cache_size=128, # Number of cache entries
context_cache_ttl=300.0, # Cache for 5 minutes
)
```
### Fragment Rendering Optimizations
Fragment rendering includes several optimizations:
- **Block function caching** - Compiled block functions are cached to avoid re-extraction
- **Context object pooling** - Context dictionaries are reused to reduce memory allocations
- **Adaptive string building** - Large fragments use StringIO for better performance
```python
# Fragments are automatically optimized
content = await templates.render_fragment(
"components/card.html",
"card_block",
title="Product Name",
description="Product description...",
)
```
### Configuration Options
```python
templates = AsyncJinja2Templates(
directory=AsyncPath("templates"),
# Context processor caching
context_cache_size=128, # Max cached context entries
context_cache_ttl=300.0, # Cache TTL in seconds
# Fragment rendering optimizations
fragment_cache_size=64, # Max cached block functions
fragment_cache_ttl=600.0, # Block cache TTL in seconds
context_pool_size=10, # Context object pool size
fragment_stringio_threshold=1024, # Use StringIO for fragments > 1KB
)
```
### Performance Benefits
- **20-40%** faster template rendering with context processors
- **30-50%** faster repeated fragment rendering
- **10-20%** reduction in memory allocations
- **15-25%** faster rendering of large template fragments
## Advanced Usage
### With Redis Bytecode Caching
For production environments, you can use Redis for bytecode caching:
```python
from anyio import Path as AsyncPath
from starlette_async_jinja import AsyncJinja2Templates
from jinja2_async_environment.bccache import AsyncRedisBytecodeCache
import redis.asyncio as redis
# Create a Redis client
redis_client = redis.Redis(host="localhost", port=6379, db=0)
# Set up bytecode caching
bytecode_cache = AsyncRedisBytecodeCache(redis_client, prefix="jinja2_")
# Create templates with caching
templates = AsyncJinja2Templates(
directory=AsyncPath("templates"), bytecode_cache=bytecode_cache
)
```
### Using Different Loaders
You can use different loader types from `jinja2-async-environment`:
```python
from anyio import Path as AsyncPath
from starlette_async_jinja import AsyncJinja2Templates
from jinja2_async_environment.loaders import (
AsyncFileSystemLoader,
AsyncPackageLoader,
AsyncChoiceLoader,
)
# Load templates from filesystem
fs_loader = AsyncFileSystemLoader("templates")
# Load templates from a Python package
package_loader = AsyncPackageLoader("your_package", "templates")
# Create a loader that tries multiple sources
choice_loader = AsyncChoiceLoader(
[
fs_loader, # First try the filesystem
package_loader, # Then try the package
]
)
# Create templates with the choice loader
templates = AsyncJinja2Templates(
directory=AsyncPath("templates"), # This is still required
loader=choice_loader, # But we override the loader
)
```
## Type Annotations
This package is fully typed with Python's type annotations and is compatible with static type checkers like mypy and pyright.
## Acknowledgements
- [jinja_partials](https://github.com/mikeckennedy/jinja_partials)
- [jinja2-fragments](https://github.com/sponsfreixes/jinja2-fragments)
- [jinja2-async-environment](https://github.com/lesleslie/jinja2-async-environment)
## License
BSD-3-Clause
Raw data
{
"_id": null,
"home_page": null,
"name": "starlette-async-jinja",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.13",
"maintainer_email": null,
"keywords": null,
"author": null,
"author_email": "lesleslie <les@wedgwoodwebworks.com>",
"download_url": "https://files.pythonhosted.org/packages/c0/d2/80931f9ac07969f267ad4c23b39690df871f4b307280e4624a4a57e90577/starlette_async_jinja-1.12.6.tar.gz",
"platform": null,
"description": "# starlette-async-jinja\n\n[](https://github.com/lesleslie/crackerjack)\n[](https://www.python.org/downloads/)\n\nAn asynchronous Jinja2 template integration for Starlette and FastAPI, built on top of the `jinja2-async-environment` package.\n\n## Features\n\n- **Fully async template rendering** - Load templates and render them asynchronously\n- **Seamless framework integration** - Works with Starlette and FastAPI request/response cycles\n- **Template fragments** - Render specific blocks from templates\n- **Template partials** - Include sub-templates with their own context using `render_block`\n- **Fast JSON responses** - Enhanced JSON responses using `msgspec` for faster serialization\n- **Context processors** - Add global context to all templates\n- **Performance optimizations** - Context processor caching, fragment caching, and memory pooling\n- **Configurable caching** - Fine-tune cache sizes and TTL for optimal performance\n\n## Installation\n\n```bash\npip install starlette-async-jinja\n```\n\n## Requirements\n\n- Python 3.13+\n- Starlette\n- Jinja2\n- jinja2-async-environment\n- anyio\n- msgspec\n\n## Basic Usage\n\n### Starlette Example\n\n```python\nfrom starlette.applications import Starlette\nfrom starlette.routing import Route\nfrom anyio import Path as AsyncPath\nfrom starlette_async_jinja import AsyncJinja2Templates\n\n# Initialize templates with an async path\ntemplates = AsyncJinja2Templates(directory=AsyncPath(\"templates\"))\n\n\nasync def homepage(request):\n return await templates.TemplateResponse(\n request, \"index.html\", {\"message\": \"Hello, world!\"}\n )\n\n\n# Or using the alias\nasync def about(request):\n return await templates.render_template(\n request, \"about.html\", {\"message\": \"About page\"}\n )\n\n\n# Define routes\napp = Starlette(routes=[Route(\"/\", homepage), Route(\"/about\", about)])\n```\n\n### FastAPI Example\n\n```python\nfrom fastapi import FastAPI, Request\nfrom fastapi.responses import HTMLResponse\nfrom anyio import Path as AsyncPath\nfrom starlette_async_jinja import AsyncJinja2Templates, JsonResponse\n\napp = FastAPI()\n\n# Initialize templates with an async path\ntemplates = AsyncJinja2Templates(directory=AsyncPath(\"templates\"))\n\n\n@app.get(\"/\", response_class=HTMLResponse)\nasync def homepage(request: Request):\n \"\"\"Render the homepage template.\"\"\"\n return await templates.TemplateResponse(\n request,\n \"index.html\",\n {\n \"title\": \"FastAPI with Async Jinja2\",\n \"message\": \"Welcome to FastAPI with async template rendering!\",\n },\n )\n\n\n@app.get(\"/about\", response_class=HTMLResponse)\nasync def about(request: Request):\n \"\"\"Render the about page template.\"\"\"\n return await templates.render_template(\n request,\n \"about.html\",\n {\"title\": \"About Us\", \"message\": \"Learn more about our company\"},\n )\n\n\n@app.get(\"/api/data\")\nasync def get_data():\n \"\"\"Return JSON data using the optimized JsonResponse.\"\"\"\n return JsonResponse(\n {\n \"items\": [\n {\"id\": 1, \"name\": \"Item 1\"},\n {\"id\": 2, \"name\": \"Item 2\"},\n {\"id\": 3, \"name\": \"Item 3\"},\n ]\n }\n )\n```\n\n## Using Template Partials with `render_block`\n\nThe `render_block` feature allows you to render entire template files as reusable components, inspired by [jinja_partials](https://github.com/mikeckennedy/jinja_partials). This is especially useful for creating modular template components.\n\n### Component Templates\n\n**templates/components/alert.html**\n\n```html\n<div class=\"alert alert-{{ type | default('info') }}\">\n <h4 class=\"alert-heading\">{{ title }}</h4>\n <p>{{ message }}</p>\n {% if dismissible %}\n <button type=\"button\" class=\"close\" data-dismiss=\"alert\">\u00d7</button>\n {% endif %}\n</div>\n```\n\n### Using Components in Your Templates\n\n**templates/index.html**\n\n```html\n{% extends \"base.html\" %}\n\n{% block content %}\n<div class=\"container\">\n <h1>Welcome to {{ site_name }}</h1>\n\n {# Instead of a macro, use render_block #}\n {{ render_block('components/alert.html',\n type='warning',\n title='Attention!',\n message='This is an important notice.',\n dismissible=True) }}\n\n {% for item in items %}\n {# Another component rendered with render_block #}\n {{ render_block('components/card.html',\n title=item.title,\n content=item.description,\n image_url=item.image) }}\n {% endfor %}\n</div>\n{% endblock %}\n```\n\n### Important Notes on `render_block`\n\n- Each component only receives the variables explicitly passed to it\n- `render_block` renders entire template files as components\n- The `markup=True` parameter (default) ensures proper HTML escaping\n\n## Context Processors\n\nContext processors allow you to add global context to all templates:\n\n```python\ndef global_context(request):\n return {\"site_name\": \"My Awesome Site\", \"current_year\": 2024}\n\n\ntemplates = AsyncJinja2Templates(\n directory=AsyncPath(\"templates\"), context_processors=[global_context]\n)\n\n# Now all templates will have access to site_name and current_year\n```\n\n### Context Processors with FastAPI\n\n```python\nfrom fastapi import FastAPI, Request\nfrom anyio import Path as AsyncPath\nfrom starlette_async_jinja import AsyncJinja2Templates\n\napp = FastAPI()\n\n\n# Define context processors\ndef global_context(request):\n return {\"site_name\": \"My FastAPI App\", \"current_year\": 2024, \"version\": \"1.0.0\"}\n\n\ndef user_context(request):\n # In a real app, you might get this from a session or JWT\n return {\"user\": {\"name\": \"Guest User\"}}\n\n\n# Initialize templates with context processors\ntemplates = AsyncJinja2Templates(\n directory=AsyncPath(\"templates\"), context_processors=[global_context, user_context]\n)\n\n\n@app.get(\"/\")\nasync def homepage(request: Request):\n # These variables will be automatically available in all templates:\n # - site_name\n # - current_year\n # - version\n # - user\n return await templates.TemplateResponse(\n request, \"index.html\", {\"title\": \"Home Page\"}\n )\n```\n\n## Using Template Fragments\n\nFragments allow you to render specific blocks from within a template:\n\n```html\n<!-- In your template (page.html) -->\n{% block header %}\n <h1>Welcome to {{ site_name }}</h1>\n{% endblock %}\n\n{% block footer %}\n <footer>\u00a9 {{ year }} {{ company_name }}</footer>\n{% endblock %}\n```\n\n### Starlette Example\n\n```python\nfrom starlette.responses import HTMLResponse\n\n\n# In your route handler:\nasync def render_header(request):\n content = await templates.render_fragment(\n \"page.html\", \"header\", site_name=\"My Awesome Site\"\n )\n return HTMLResponse(content)\n```\n\n### FastAPI Example\n\n```python\nfrom fastapi import FastAPI, Request\nfrom fastapi.responses import HTMLResponse\n\napp = FastAPI()\ntemplates = AsyncJinja2Templates(directory=AsyncPath(\"templates\"))\n\n\n@app.get(\"/header\", response_class=HTMLResponse)\nasync def get_header(request: Request):\n \"\"\"Return just the header fragment.\"\"\"\n content = await templates.render_fragment(\n \"page.html\", \"header\", site_name=\"My FastAPI Site\"\n )\n return HTMLResponse(content)\n\n\n@app.get(\"/footer\", response_class=HTMLResponse)\nasync def get_footer(request: Request):\n \"\"\"Return just the footer fragment.\"\"\"\n content = await templates.render_fragment(\n \"page.html\", \"footer\", year=2024, company_name=\"My Company\"\n )\n return HTMLResponse(content)\n```\n\n## JsonResponse\n\nEnhanced JSON response using `msgspec` for faster serialization:\n\n```python\nfrom starlette_async_jinja import JsonResponse\n\n\n# Starlette example\nasync def api_endpoint(request):\n data = {\"name\": \"John\", \"email\": \"john@example.com\"}\n return JsonResponse(data)\n\n\n# FastAPI example\n@app.get(\"/api/user\")\nasync def get_user():\n data = {\"name\": \"John\", \"email\": \"john@example.com\"}\n return JsonResponse(data) # Faster than FastAPI's default JSONResponse\n```\n\n## Jinja2 Macros Support\n\nJinja2 macros are fully supported in async templates with the updated `jinja2-async-environment>=0.13`:\n\n```html\n<!-- templates/components.html -->\n{% macro alert(type, message) %}\n<div class=\"alert alert-{{ type }}\">{{ message }}</div>\n{% endmacro %}\n\n{% macro button(text, style='primary') %}\n<button class=\"btn btn-{{ style }}\">{{ text }}</button>\n{% endmacro %}\n```\n\n### Using Macros in Templates\n\nMacros work seamlessly with `render_block` when called within templates:\n\n```html\n<!-- templates/page.html -->\n{% from 'components.html' import alert, button %}\n\n<h1>Welcome!</h1>\n{{ alert('info', 'Welcome to our site!') }}\n{{ button('Get Started', 'success') }}\n```\n\n```python\n# This works perfectly with macros inside\ncontent = await templates.render_block(\"page.html\", {})\n```\n\n### Direct Macro Access\n\nFor advanced use cases, macros can be called directly from template modules:\n\n```python\nasync def render_macro_component():\n template = await templates.env.get_template_async(\"components.html\")\n module = await template.make_module_async()\n\n # Call macro directly and await the result\n alert_html = await module.alert(\"warning\", \"Direct macro call\")\n return alert_html\n```\n\n### Choosing Your Approach\n\n- **Macros**: Traditional Jinja2 components defined within templates, great for simple reusable elements\n- **render_block**: Renders entire template files as components, useful for complex reusable template partials\n- **render_fragment**: Renders specific named blocks from templates, ideal for partial page updates\n\n## Issues and Limitations\n\n- Only [asynchronous template loaders](https://github.com/lesleslie/jinja2-async-environment/blob/main/jinja2_async_environment/loaders.py) are fully supported\n- The Jinja bytecodecache requires an asynchronous Redis backend\n\n## API Reference\n\n### AsyncJinja2Templates\n\n```python\ntemplates = AsyncJinja2Templates(\n directory=AsyncPath(\"templates\"),\n context_processors=[global_context],\n # Performance optimization options\n context_cache_size=128, # Context processor cache size\n context_cache_ttl=300.0, # Context cache TTL (seconds)\n fragment_cache_size=64, # Fragment block cache size\n fragment_cache_ttl=600.0, # Fragment cache TTL (seconds)\n context_pool_size=10, # Context object pool size\n fragment_stringio_threshold=1024, # StringIO threshold for large fragments\n # Standard Jinja2 environment options\n autoescape=True,\n **env_options,\n)\n```\n\n#### Methods\n\n- `async TemplateResponse(request, name, context={}, status_code=200, headers=None, media_type=None, background=None)` - Render a template to a response\n- `async render_template(request, name, context={}, status_code=200, headers=None, media_type=None, background=None)` - Alias for TemplateResponse\n- `async render_fragment(template_name, block_name, **kwargs)` - Render a specific block from a template\n- `async render_block(template_name, markup=True, **data)` - Render a template as a partial with optional markup escaping (default: True)\n- `async get_template_async(name)` - Get a template by name\n\n### JsonResponse\n\nEnhanced JSON response using `msgspec` for faster serialization.\n\n### BlockNotFoundError\n\nException raised when attempting to render a template block that doesn't exist.\n\n## Performance Optimizations\n\nThis package includes several built-in performance optimizations:\n\n### Context Processor Caching\n\nContext processors are automatically cached based on request properties to avoid recomputation:\n\n```python\ndef expensive_context_processor(request):\n # This will only run once per unique request path/method combination\n # and be cached for subsequent requests\n return {\n \"expensive_data\": fetch_expensive_data(),\n \"computed_value\": perform_complex_calculation(),\n }\n\n\ntemplates = AsyncJinja2Templates(\n directory=AsyncPath(\"templates\"),\n context_processors=[expensive_context_processor],\n context_cache_size=128, # Number of cache entries\n context_cache_ttl=300.0, # Cache for 5 minutes\n)\n```\n\n### Fragment Rendering Optimizations\n\nFragment rendering includes several optimizations:\n\n- **Block function caching** - Compiled block functions are cached to avoid re-extraction\n- **Context object pooling** - Context dictionaries are reused to reduce memory allocations\n- **Adaptive string building** - Large fragments use StringIO for better performance\n\n```python\n# Fragments are automatically optimized\ncontent = await templates.render_fragment(\n \"components/card.html\",\n \"card_block\",\n title=\"Product Name\",\n description=\"Product description...\",\n)\n```\n\n### Configuration Options\n\n```python\ntemplates = AsyncJinja2Templates(\n directory=AsyncPath(\"templates\"),\n # Context processor caching\n context_cache_size=128, # Max cached context entries\n context_cache_ttl=300.0, # Cache TTL in seconds\n # Fragment rendering optimizations\n fragment_cache_size=64, # Max cached block functions\n fragment_cache_ttl=600.0, # Block cache TTL in seconds\n context_pool_size=10, # Context object pool size\n fragment_stringio_threshold=1024, # Use StringIO for fragments > 1KB\n)\n```\n\n### Performance Benefits\n\n- **20-40%** faster template rendering with context processors\n- **30-50%** faster repeated fragment rendering\n- **10-20%** reduction in memory allocations\n- **15-25%** faster rendering of large template fragments\n\n## Advanced Usage\n\n### With Redis Bytecode Caching\n\nFor production environments, you can use Redis for bytecode caching:\n\n```python\nfrom anyio import Path as AsyncPath\nfrom starlette_async_jinja import AsyncJinja2Templates\nfrom jinja2_async_environment.bccache import AsyncRedisBytecodeCache\nimport redis.asyncio as redis\n\n# Create a Redis client\nredis_client = redis.Redis(host=\"localhost\", port=6379, db=0)\n\n# Set up bytecode caching\nbytecode_cache = AsyncRedisBytecodeCache(redis_client, prefix=\"jinja2_\")\n\n# Create templates with caching\ntemplates = AsyncJinja2Templates(\n directory=AsyncPath(\"templates\"), bytecode_cache=bytecode_cache\n)\n```\n\n### Using Different Loaders\n\nYou can use different loader types from `jinja2-async-environment`:\n\n```python\nfrom anyio import Path as AsyncPath\nfrom starlette_async_jinja import AsyncJinja2Templates\nfrom jinja2_async_environment.loaders import (\n AsyncFileSystemLoader,\n AsyncPackageLoader,\n AsyncChoiceLoader,\n)\n\n# Load templates from filesystem\nfs_loader = AsyncFileSystemLoader(\"templates\")\n\n# Load templates from a Python package\npackage_loader = AsyncPackageLoader(\"your_package\", \"templates\")\n\n# Create a loader that tries multiple sources\nchoice_loader = AsyncChoiceLoader(\n [\n fs_loader, # First try the filesystem\n package_loader, # Then try the package\n ]\n)\n\n# Create templates with the choice loader\ntemplates = AsyncJinja2Templates(\n directory=AsyncPath(\"templates\"), # This is still required\n loader=choice_loader, # But we override the loader\n)\n```\n\n## Type Annotations\n\nThis package is fully typed with Python's type annotations and is compatible with static type checkers like mypy and pyright.\n\n## Acknowledgements\n\n- [jinja_partials](https://github.com/mikeckennedy/jinja_partials)\n- [jinja2-fragments](https://github.com/sponsfreixes/jinja2-fragments)\n- [jinja2-async-environment](https://github.com/lesleslie/jinja2-async-environment)\n\n## License\n\nBSD-3-Clause\n",
"bugtrack_url": null,
"license": "BSD-3-Clause",
"summary": null,
"version": "1.12.6",
"project_urls": {
"Documentation": "https://github.com/lesleslie/starlette-async-jinja",
"Homepage": "https://github.com/lesleslie/starlette-async-jinja",
"Repository": "https://github.com/lesleslie/starlette-async-jinja"
},
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "a2a8b8a31194ba13f23974053bdd2652c2297aab1f50607594a63a9e728a7f77",
"md5": "6c0d49fcf8572f803667b9a693898ce8",
"sha256": "ca14989dae72f7884e6d3d93593e8e040d8cd09f4c35b6ce8e88ad605b3ebd0c"
},
"downloads": -1,
"filename": "starlette_async_jinja-1.12.6-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6c0d49fcf8572f803667b9a693898ce8",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.13",
"size": 10575,
"upload_time": "2025-07-16T06:38:54",
"upload_time_iso_8601": "2025-07-16T06:38:54.637409Z",
"url": "https://files.pythonhosted.org/packages/a2/a8/b8a31194ba13f23974053bdd2652c2297aab1f50607594a63a9e728a7f77/starlette_async_jinja-1.12.6-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "c0d280931f9ac07969f267ad4c23b39690df871f4b307280e4624a4a57e90577",
"md5": "95f093750d4fdf46e8b6f604b8917bb1",
"sha256": "ff02f27752605d8a2a052b2d9a3698878d5b86b75dcd21ccb14e9902955ed103"
},
"downloads": -1,
"filename": "starlette_async_jinja-1.12.6.tar.gz",
"has_sig": false,
"md5_digest": "95f093750d4fdf46e8b6f604b8917bb1",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.13",
"size": 1210047,
"upload_time": "2025-07-16T06:38:56",
"upload_time_iso_8601": "2025-07-16T06:38:56.480548Z",
"url": "https://files.pythonhosted.org/packages/c0/d2/80931f9ac07969f267ad4c23b39690df871f4b307280e4624a4a57e90577/starlette_async_jinja-1.12.6.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-16 06:38:56",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "lesleslie",
"github_project": "starlette-async-jinja",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "starlette-async-jinja"
}